1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-25 01:02:05 +03:00

Extend pgbench's expression syntax to support a few built-in functions.

Fabien Coelho, reviewed mostly by Michael Paquier and me, but also by
Heikki Linnakangas, BeomYong Lee, Kyotaro Horiguchi, Oleksander
Shulgin, and Álvaro Herrera.
This commit is contained in:
Robert Haas
2016-03-01 13:04:09 -05:00
parent bd6cf3f237
commit 7e137f846d
5 changed files with 415 additions and 103 deletions

View File

@ -16,10 +16,13 @@
PgBenchExpr *expr_parse_result;
static PgBenchExprList *make_elist(PgBenchExpr *exp, PgBenchExprList *list);
static PgBenchExpr *make_integer_constant(int64 ival);
static PgBenchExpr *make_variable(char *varname);
static PgBenchExpr *make_op(char operator, PgBenchExpr *lexpr,
static PgBenchExpr *make_op(const char *operator, PgBenchExpr *lexpr,
PgBenchExpr *rexpr);
static int find_func(const char *fname);
static PgBenchExpr *make_func(const int fnumber, PgBenchExprList *args);
%}
@ -31,13 +34,15 @@ static PgBenchExpr *make_op(char operator, PgBenchExpr *lexpr,
int64 ival;
char *str;
PgBenchExpr *expr;
PgBenchExprList *elist;
}
%type <elist> elist
%type <expr> expr
%type <ival> INTEGER
%type <str> VARIABLE
%type <ival> INTEGER function
%type <str> VARIABLE FUNCTION
%token INTEGER VARIABLE
%token INTEGER VARIABLE FUNCTION
%token CHAR_ERROR /* never used, will raise a syntax error */
/* Precedence: lowest to highest */
@ -49,16 +54,25 @@ static PgBenchExpr *make_op(char operator, PgBenchExpr *lexpr,
result: expr { expr_parse_result = $1; }
elist: { $$ = NULL; }
| expr { $$ = make_elist($1, NULL); }
| elist ',' expr { $$ = make_elist($3, $1); }
;
expr: '(' expr ')' { $$ = $2; }
| '+' expr %prec UMINUS { $$ = $2; }
| '-' expr %prec UMINUS { $$ = make_op('-', make_integer_constant(0), $2); }
| expr '+' expr { $$ = make_op('+', $1, $3); }
| expr '-' expr { $$ = make_op('-', $1, $3); }
| expr '*' expr { $$ = make_op('*', $1, $3); }
| expr '/' expr { $$ = make_op('/', $1, $3); }
| expr '%' expr { $$ = make_op('%', $1, $3); }
| '-' expr %prec UMINUS { $$ = make_op("-", make_integer_constant(0), $2); }
| expr '+' expr { $$ = make_op("+", $1, $3); }
| expr '-' expr { $$ = make_op("-", $1, $3); }
| expr '*' expr { $$ = make_op("*", $1, $3); }
| expr '/' expr { $$ = make_op("/", $1, $3); }
| expr '%' expr { $$ = make_op("%", $1, $3); }
| INTEGER { $$ = make_integer_constant($1); }
| VARIABLE { $$ = make_variable($1); }
| function '(' elist ')'{ $$ = make_func($1, $3); }
;
function: FUNCTION { $$ = find_func($1); pg_free($1); }
;
%%
@ -84,14 +98,131 @@ make_variable(char *varname)
}
static PgBenchExpr *
make_op(char operator, PgBenchExpr *lexpr, PgBenchExpr *rexpr)
make_op(const char *operator, PgBenchExpr *lexpr, PgBenchExpr *rexpr)
{
return make_func(find_func(operator),
make_elist(rexpr, make_elist(lexpr, NULL)));
}
/*
* List of available functions:
* - fname: function name
* - nargs: number of arguments
* -1 is a special value for min & max meaning #args >= 1
* - tag: function identifier from PgBenchFunction enum
*/
static struct
{
char * fname;
int nargs;
PgBenchFunction tag;
} PGBENCH_FUNCTIONS[] = {
/* parsed as operators, executed as functions */
{ "+", 2, PGBENCH_ADD },
{ "-", 2, PGBENCH_SUB },
{ "*", 2, PGBENCH_MUL },
{ "/", 2, PGBENCH_DIV },
{ "%", 2, PGBENCH_MOD },
/* actual functions */
{ "abs", 1, PGBENCH_ABS },
{ "min", -1, PGBENCH_MIN },
{ "max", -1, PGBENCH_MAX },
{ "debug", 1, PGBENCH_DEBUG },
/* keep as last array element */
{ NULL, 0, 0 }
};
/*
* Find a function from its name
*
* return the index of the function from the PGBENCH_FUNCTIONS array
* or fail if the function is unknown.
*/
static int
find_func(const char * fname)
{
int i = 0;
while (PGBENCH_FUNCTIONS[i].fname)
{
if (pg_strcasecmp(fname, PGBENCH_FUNCTIONS[i].fname) == 0)
return i;
i++;
}
expr_yyerror_more("unexpected function name", fname);
/* not reached */
return -1;
}
/* Expression linked list builder */
static PgBenchExprList *
make_elist(PgBenchExpr *expr, PgBenchExprList *list)
{
PgBenchExprLink * cons;
if (list == NULL)
{
list = pg_malloc(sizeof(PgBenchExprList));
list->head = NULL;
list->tail = NULL;
}
cons = pg_malloc(sizeof(PgBenchExprLink));
cons->expr = expr;
cons->next = NULL;
if (list->head == NULL)
list->head = cons;
else
list->tail->next = cons;
list->tail = cons;
return list;
}
/* Return the length of an expression list */
static int
elist_length(PgBenchExprList *list)
{
PgBenchExprLink *link = list != NULL? list->head: NULL;
int len = 0;
for (; link != NULL; link = link->next)
len++;
return len;
}
/* Build function call expression */
static PgBenchExpr *
make_func(const int fnumber, PgBenchExprList *args)
{
PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
expr->etype = ENODE_OPERATOR;
expr->u.operator.operator = operator;
expr->u.operator.lexpr = lexpr;
expr->u.operator.rexpr = rexpr;
Assert(fnumber >= 0);
if (PGBENCH_FUNCTIONS[fnumber].nargs >= 0 &&
PGBENCH_FUNCTIONS[fnumber].nargs != elist_length(args))
expr_yyerror_more("unexpected number of arguments",
PGBENCH_FUNCTIONS[fnumber].fname);
/* check at least one arg for min & max */
if (PGBENCH_FUNCTIONS[fnumber].nargs == -1 &&
elist_length(args) == 0)
expr_yyerror_more("at least one argument expected",
PGBENCH_FUNCTIONS[fnumber].fname);
expr->etype = ENODE_FUNCTION;
expr->u.function.function = PGBENCH_FUNCTIONS[fnumber].tag;
/* only the link is used, the head/tail is not useful anymore */
expr->u.function.args = args != NULL? args->head: NULL;
if (args)
pg_free(args);
return expr;
}