diff options
Diffstat (limited to 'src/bin/pgbench/exprparse.y')
-rw-r--r-- | src/bin/pgbench/exprparse.y | 161 |
1 files changed, 146 insertions, 15 deletions
diff --git a/src/bin/pgbench/exprparse.y b/src/bin/pgbench/exprparse.y index 06ee04b6873..cac4d5e4f44 100644 --- a/src/bin/pgbench/exprparse.y +++ b/src/bin/pgbench/exprparse.y @@ -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; } |