summaryrefslogtreecommitdiff
path: root/src/bin/pgbench/exprparse.y
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/pgbench/exprparse.y')
-rw-r--r--src/bin/pgbench/exprparse.y161
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;
}