v4, rebase (after pgbench moved to src) and use of the recently added syntax_error function.v5 which adds a forgotten header declaration for a new function, so as to avoid a warning.
While playing around for adding other functions I noticed that with the parsing rules I set unknown function were detected quite late in the process, so that the error messages was imprecise.
v6 fixes this by checking function names ealier. -- Fabien.
diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml index a808546..d09b2bf 100644 --- a/doc/src/sgml/ref/pgbench.sgml +++ b/doc/src/sgml/ref/pgbench.sgml @@ -762,7 +762,7 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</> references to variables <literal>:</><replaceable>variablename</>, and expressions composed of unary (<literal>-</>) or binary operators (<literal>+</>, <literal>-</>, <literal>*</>, <literal>/</>, <literal>%</>) - with their usual associativity, and parentheses. + with their usual associativity, function <literal>abs</> and parentheses. </para> <para> diff --git a/src/bin/pgbench/exprparse.y b/src/bin/pgbench/exprparse.y index e68631e..035322d 100644 --- a/src/bin/pgbench/exprparse.y +++ b/src/bin/pgbench/exprparse.y @@ -20,6 +20,8 @@ static PgBenchExpr *make_integer_constant(int64 ival); static PgBenchExpr *make_variable(char *varname); static PgBenchExpr *make_op(char operator, PgBenchExpr *lexpr, PgBenchExpr *rexpr); +static int find_func(const char * fname); +static PgBenchExpr *make_func(const int fnumber, PgBenchExpr *arg1); %} @@ -34,10 +36,10 @@ static PgBenchExpr *make_op(char operator, PgBenchExpr *lexpr, } %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 */ @@ -59,6 +61,10 @@ expr: '(' expr ')' { $$ = $2; } | expr '%' expr { $$ = make_op('%', $1, $3); } | INTEGER { $$ = make_integer_constant($1); } | VARIABLE { $$ = make_variable($1); } + | function '(' expr ')' { $$ = make_func($1, $3); } + ; + +function: FUNCTION { $$ = find_func($1); pg_free($1); } ; %% @@ -95,4 +101,41 @@ make_op(char operator, PgBenchExpr *lexpr, PgBenchExpr *rexpr) return expr; } +static struct { + char * fname; + int nargs; + PgBenchFunction tag; +} PGBENCH_FUNCTIONS[] = { + { "abs", 1, PGBENCH_ABS } +}; + +static int +find_func(const char * fname) +{ + int nfunctions = sizeof(PGBENCH_FUNCTIONS) / sizeof(PGBENCH_FUNCTIONS[0]); + int i; + + for (i = 0; i < nfunctions; i++) + if (pg_strcasecmp(fname, PGBENCH_FUNCTIONS[i].fname) == 0) + return i; + + expr_yyerror_more("unexpected function name", fname); + + /* not reached */ + return -1; +} + +static PgBenchExpr * +make_func(const int fnumber, PgBenchExpr *arg1) +{ + PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr)); + + Assert(fnumber >= 0); + + expr->etype = ENODE_FUNCTION; + expr->u.function.function = PGBENCH_FUNCTIONS[fnumber].tag; + expr->u.function.arg1 = arg1; + return expr; +} + #include "exprscan.c" diff --git a/src/bin/pgbench/exprscan.l b/src/bin/pgbench/exprscan.l index 5331ab7..0e07bfe 100644 --- a/src/bin/pgbench/exprscan.l +++ b/src/bin/pgbench/exprscan.l @@ -57,6 +57,11 @@ space [ \t\r\f] yylval.ival = strtoint64(yytext); return INTEGER; } +[a-zA-Z]+ { + yycol += yyleng; + yylval.str = pg_strdup(yytext); + return FUNCTION; + } [\n] { yycol = 0; yyline++; } {space}+ { yycol += yyleng; /* ignore */ } @@ -71,10 +76,16 @@ space [ \t\r\f] %% void -yyerror(const char *message) +expr_yyerror_more(const char *message, const char *more) { syntax_error(expr_source, expr_lineno, expr_full_line, expr_command, - message, NULL, expr_col + yycol); + message, more, expr_col + yycol); +} + +void +yyerror(const char *message) +{ + expr_yyerror_more(message, NULL); } /* diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c index 06a4dfd..5084381 100644 --- a/src/bin/pgbench/pgbench.c +++ b/src/bin/pgbench/pgbench.c @@ -956,6 +956,26 @@ evaluateExpr(CState *st, PgBenchExpr *expr, int64 *retval) return false; } + case ENODE_FUNCTION: + { + switch (expr->u.function.function) + { + case PGBENCH_ABS: + { + int64 arg1; + if (!evaluateExpr(st, expr->u.function.arg1, &arg1)) + return false; + + *retval = arg1 > 0? arg1: -arg1; + return true; + } + default: + fprintf(stderr, "bad function (%d)\n", + expr->u.function.function); + return false; + } + } + default: break; } diff --git a/src/bin/pgbench/pgbench.h b/src/bin/pgbench/pgbench.h index a3db6b9..5062ef5 100644 --- a/src/bin/pgbench/pgbench.h +++ b/src/bin/pgbench/pgbench.h @@ -15,11 +15,18 @@ typedef enum PgBenchExprType { ENODE_INTEGER_CONSTANT, ENODE_VARIABLE, - ENODE_OPERATOR + ENODE_OPERATOR, + ENODE_FUNCTION } PgBenchExprType; typedef struct PgBenchExpr PgBenchExpr; +typedef enum PgBenchFunction +{ + PGBENCH_NONE, + PGBENCH_ABS +} PgBenchFunction; + struct PgBenchExpr { PgBenchExprType etype; @@ -39,6 +46,11 @@ struct PgBenchExpr PgBenchExpr *lexpr; PgBenchExpr *rexpr; } operator; + struct + { + PgBenchFunction function; + PgBenchExpr *arg1; + } function; } u; }; @@ -47,6 +59,7 @@ extern PgBenchExpr *expr_parse_result; extern int expr_yyparse(void); extern int expr_yylex(void); extern void expr_yyerror(const char *str); +extern void expr_yyerror_more(const char *str, const char *more); extern void expr_scanner_init(const char *str, const char *source, const int lineno, const char *line, const char *cmd, const int ecol);
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers