LCOV - code coverage report
Current view: top level - src/bin/pgbench - exprparse.y (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 99.3 % 149 148
Test Date: 2026-03-03 16:15:26 Functions: 100.0 % 12 12
Legend: Lines:     hit not hit

            Line data    Source code
       1              : %{
       2              : /*-------------------------------------------------------------------------
       3              :  *
       4              :  * exprparse.y
       5              :  *    bison grammar for a simple expression syntax
       6              :  *
       7              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       8              :  * Portions Copyright (c) 1994, Regents of the University of California
       9              :  *
      10              :  * src/bin/pgbench/exprparse.y
      11              :  *
      12              :  *-------------------------------------------------------------------------
      13              :  */
      14              : 
      15              : #include "postgres_fe.h"
      16              : 
      17              : #include "pgbench.h"
      18              : 
      19              : #define PGBENCH_NARGS_VARIABLE  (-1)
      20              : #define PGBENCH_NARGS_CASE      (-2)
      21              : #define PGBENCH_NARGS_HASH      (-3)
      22              : #define PGBENCH_NARGS_PERMUTE   (-4)
      23              : 
      24              : static PgBenchExprList *make_elist(PgBenchExpr *expr, PgBenchExprList *list);
      25              : static PgBenchExpr *make_null_constant(void);
      26              : static PgBenchExpr *make_boolean_constant(bool bval);
      27              : static PgBenchExpr *make_integer_constant(int64 ival);
      28              : static PgBenchExpr *make_double_constant(double dval);
      29              : static PgBenchExpr *make_variable(char *varname);
      30              : static PgBenchExpr *make_op(yyscan_t yyscanner, const char *operator,
      31              :                             PgBenchExpr *lexpr, PgBenchExpr *rexpr);
      32              : static PgBenchExpr *make_uop(yyscan_t yyscanner, const char *operator, PgBenchExpr *expr);
      33              : static int  find_func(yyscan_t yyscanner, const char *fname);
      34              : static PgBenchExpr *make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList *args);
      35              : static PgBenchExpr *make_case(yyscan_t yyscanner, PgBenchExprList *when_then_list, PgBenchExpr *else_part);
      36              : 
      37              : %}
      38              : 
      39              : %pure-parser
      40              : %expect 0
      41              : %name-prefix="expr_yy"
      42              : 
      43              : %parse-param {PgBenchExpr **expr_parse_result_p}
      44              : %parse-param {yyscan_t yyscanner}
      45              : %lex-param   {yyscan_t yyscanner}
      46              : 
      47              : %union
      48              : {
      49              :     int64       ival;
      50              :     double      dval;
      51              :     bool        bval;
      52              :     char       *str;
      53              :     PgBenchExpr *expr;
      54              :     PgBenchExprList *elist;
      55              : }
      56              : 
      57              : %type <elist> elist when_then_list
      58              : %type <expr> expr case_control
      59              : %type <ival> INTEGER_CONST function
      60              : %type <dval> DOUBLE_CONST
      61              : %type <bval> BOOLEAN_CONST
      62              : %type <str> VARIABLE FUNCTION
      63              : 
      64              : %token NULL_CONST INTEGER_CONST MAXINT_PLUS_ONE_CONST DOUBLE_CONST
      65              : %token BOOLEAN_CONST VARIABLE FUNCTION
      66              : %token AND_OP OR_OP NOT_OP NE_OP LE_OP GE_OP LS_OP RS_OP IS_OP
      67              : %token CASE_KW WHEN_KW THEN_KW ELSE_KW END_KW
      68              : 
      69              : /* Precedence: lowest to highest, taken from postgres SQL parser */
      70              : %left   OR_OP
      71              : %left   AND_OP
      72              : %right  NOT_OP
      73              : %nonassoc IS_OP ISNULL_OP NOTNULL_OP
      74              : %nonassoc '<' '>' '=' LE_OP GE_OP NE_OP
      75              : %left   '|' '#' '&' LS_OP RS_OP '~'
      76              : %left   '+' '-'
      77              : %left   '*' '/' '%'
      78              : %right  UNARY
      79              : 
      80              : %%
      81              : 
      82              : result: expr                {
      83          380 :                                 *expr_parse_result_p = $1;
      84              :                                 (void) yynerrs; /* suppress compiler warning */
      85              :                             }
      86              : 
      87            5 : elist:                      { $$ = NULL; }
      88          393 :     | expr                  { $$ = make_elist($1, NULL); }
      89          343 :     | elist ',' expr        { $$ = make_elist($3, $1); }
      90              :     ;
      91              : 
      92           52 : expr: '(' expr ')'          { $$ = $2; }
      93            2 :     | '+' expr %prec UNARY  { $$ = $2; }
      94              :     /* unary minus "-x" implemented as "0 - x" */
      95           46 :     | '-' expr %prec UNARY  { $$ = make_op(yyscanner, "-",
      96              :                                            make_integer_constant(0), $2); }
      97              :     /* special PG_INT64_MIN handling, only after a unary minus */
      98              :     | '-' MAXINT_PLUS_ONE_CONST %prec UNARY
      99            1 :                             { $$ = make_integer_constant(PG_INT64_MIN); }
     100              :     /* binary ones complement "~x" implemented as 0xffff... xor x" */
     101            2 :     | '~' expr              { $$ = make_op(yyscanner, "#",
     102              :                                            make_integer_constant(~INT64CONST(0)), $2); }
     103           12 :     | NOT_OP expr           { $$ = make_uop(yyscanner, "!not", $2); }
     104           48 :     | expr '+' expr         { $$ = make_op(yyscanner, "+", $1, $3); }
     105           18 :     | expr '-' expr         { $$ = make_op(yyscanner, "-", $1, $3); }
     106          204 :     | expr '*' expr         { $$ = make_op(yyscanner, "*", $1, $3); }
     107           13 :     | expr '/' expr         { $$ = make_op(yyscanner, "/", $1, $3); }
     108            2 :     | expr '%' expr         { $$ = make_op(yyscanner, "mod", $1, $3); }
     109           10 :     | expr '<' expr          { $$ = make_op(yyscanner, "<", $1, $3); }
     110            4 :     | expr LE_OP expr       { $$ = make_op(yyscanner, "<=", $1, $3); }
     111            6 :     | expr '>' expr          { $$ = make_op(yyscanner, "<", $3, $1); }
     112            3 :     | expr GE_OP expr       { $$ = make_op(yyscanner, "<=", $3, $1); }
     113           32 :     | expr '=' expr         { $$ = make_op(yyscanner, "=", $1, $3); }
     114            8 :     | expr NE_OP expr       { $$ = make_op(yyscanner, "<>", $1, $3); }
     115            1 :     | expr '&' expr         { $$ = make_op(yyscanner, "&", $1, $3); }
     116            2 :     | expr '|' expr         { $$ = make_op(yyscanner, "|", $1, $3); }
     117            1 :     | expr '#' expr         { $$ = make_op(yyscanner, "#", $1, $3); }
     118            7 :     | expr LS_OP expr       { $$ = make_op(yyscanner, "<<", $1, $3); }
     119            1 :     | expr RS_OP expr       { $$ = make_op(yyscanner, ">>", $1, $3); }
     120           44 :     | expr AND_OP expr      { $$ = make_op(yyscanner, "!and", $1, $3); }
     121            5 :     | expr OR_OP expr       { $$ = make_op(yyscanner, "!or", $1, $3); }
     122              :     /* IS variants */
     123            1 :     | expr ISNULL_OP        { $$ = make_op(yyscanner, "!is", $1, make_null_constant()); }
     124              :     | expr NOTNULL_OP       {
     125            2 :                                 $$ = make_uop(yyscanner, "!not",
     126            1 :                                               make_op(yyscanner, "!is", $1, make_null_constant()));
     127              :                             }
     128            4 :     | expr IS_OP NULL_CONST { $$ = make_op(yyscanner, "!is", $1, make_null_constant()); }
     129              :     | expr IS_OP NOT_OP NULL_CONST
     130              :                             {
     131            4 :                                 $$ = make_uop(yyscanner, "!not",
     132            2 :                                               make_op(yyscanner, "!is", $1, make_null_constant()));
     133              :                             }
     134              :     | expr IS_OP BOOLEAN_CONST
     135              :                             {
     136            1 :                                 $$ = make_op(yyscanner, "!is", $1, make_boolean_constant($3));
     137              :                             }
     138              :     | expr IS_OP NOT_OP BOOLEAN_CONST
     139              :                             {
     140            1 :                                 $$ = make_uop(yyscanner, "!not",
     141            1 :                                               make_op(yyscanner, "!is", $1, make_boolean_constant($4)));
     142              :                             }
     143              :     /* constants */
     144            6 :     | NULL_CONST            { $$ = make_null_constant(); }
     145           31 :     | BOOLEAN_CONST         { $$ = make_boolean_constant($1); }
     146          794 :     | INTEGER_CONST         { $$ = make_integer_constant($1); }
     147           61 :     | DOUBLE_CONST          { $$ = make_double_constant($1); }
     148              :     /* misc */
     149          272 :     | VARIABLE              { $$ = make_variable($1); }
     150          398 :     | function '(' elist ')' { $$ = make_func(yyscanner, $1, $3); }
     151           14 :     | case_control          { $$ = $1; }
     152              :     ;
     153              : 
     154              : when_then_list:
     155            2 :       when_then_list WHEN_KW expr THEN_KW expr { $$ = make_elist($5, make_elist($3, $1)); }
     156           14 :     | WHEN_KW expr THEN_KW expr { $$ = make_elist($4, make_elist($2, NULL)); }
     157              : 
     158              : case_control:
     159            4 :       CASE_KW when_then_list END_KW { $$ = make_case(yyscanner, $2, make_null_constant()); }
     160           10 :     | CASE_KW when_then_list ELSE_KW expr END_KW { $$ = make_case(yyscanner, $2, $4); }
     161              : 
     162          399 : function: FUNCTION          { $$ = find_func(yyscanner, $1); pg_free($1); }
     163              :     ;
     164              : 
     165              : %%
     166              : 
     167              : static PgBenchExpr *
     168           18 : make_null_constant(void)
     169              : {
     170           18 :     PgBenchExpr *expr = pg_malloc_object(PgBenchExpr);
     171              : 
     172           18 :     expr->etype = ENODE_CONSTANT;
     173           18 :     expr->u.constant.type = PGBT_NULL;
     174           18 :     expr->u.constant.u.ival = 0;
     175           18 :     return expr;
     176              : }
     177              : 
     178              : static PgBenchExpr *
     179          843 : make_integer_constant(int64 ival)
     180              : {
     181          843 :     PgBenchExpr *expr = pg_malloc_object(PgBenchExpr);
     182              : 
     183          843 :     expr->etype = ENODE_CONSTANT;
     184          843 :     expr->u.constant.type = PGBT_INT;
     185          843 :     expr->u.constant.u.ival = ival;
     186          843 :     return expr;
     187              : }
     188              : 
     189              : static PgBenchExpr *
     190           61 : make_double_constant(double dval)
     191              : {
     192           61 :     PgBenchExpr *expr = pg_malloc_object(PgBenchExpr);
     193              : 
     194           61 :     expr->etype = ENODE_CONSTANT;
     195           61 :     expr->u.constant.type = PGBT_DOUBLE;
     196           61 :     expr->u.constant.u.dval = dval;
     197           61 :     return expr;
     198              : }
     199              : 
     200              : static PgBenchExpr *
     201           33 : make_boolean_constant(bool bval)
     202              : {
     203           33 :     PgBenchExpr *expr = pg_malloc_object(PgBenchExpr);
     204              : 
     205           33 :     expr->etype = ENODE_CONSTANT;
     206           33 :     expr->u.constant.type = PGBT_BOOLEAN;
     207           33 :     expr->u.constant.u.bval = bval;
     208           33 :     return expr;
     209              : }
     210              : 
     211              : static PgBenchExpr *
     212          309 : make_variable(char *varname)
     213              : {
     214          309 :     PgBenchExpr *expr = pg_malloc_object(PgBenchExpr);
     215              : 
     216          309 :     expr->etype = ENODE_VARIABLE;
     217          309 :     expr->u.variable.varname = varname;
     218          309 :     return expr;
     219              : }
     220              : 
     221              : /* binary operators */
     222              : static PgBenchExpr *
     223          467 : make_op(yyscan_t yyscanner, const char *operator,
     224              :         PgBenchExpr *lexpr, PgBenchExpr *rexpr)
     225              : {
     226          467 :     return make_func(yyscanner, find_func(yyscanner, operator),
     227              :                      make_elist(rexpr, make_elist(lexpr, NULL)));
     228              : }
     229              : 
     230              : /* unary operator */
     231              : static PgBenchExpr *
     232           16 : make_uop(yyscan_t yyscanner, const char *operator, PgBenchExpr *expr)
     233              : {
     234           16 :     return make_func(yyscanner, find_func(yyscanner, operator), make_elist(expr, NULL));
     235              : }
     236              : 
     237              : /*
     238              :  * List of available functions:
     239              :  * - fname: function name, "!..." for special internal functions
     240              :  * - nargs: number of arguments. Special cases:
     241              :  *          - PGBENCH_NARGS_VARIABLE is a special value for least & greatest
     242              :  *            meaning #args >= 1;
     243              :  *          - PGBENCH_NARGS_CASE is for the "CASE WHEN ..." function, which
     244              :  *            has #args >= 3 and odd;
     245              :  *          - PGBENCH_NARGS_HASH is for hash functions, which have one required
     246              :  *            and one optional argument;
     247              :  * - tag: function identifier from PgBenchFunction enum
     248              :  */
     249              : static const struct
     250              : {
     251              :     const char *fname;
     252              :     int         nargs;
     253              :     PgBenchFunction tag;
     254              : }           PGBENCH_FUNCTIONS[] =
     255              : 
     256              : {
     257              :     /* parsed as operators, executed as functions */
     258              :     {
     259              :         "+", 2, PGBENCH_ADD
     260              :     },
     261              :     {
     262              :         "-", 2, PGBENCH_SUB
     263              :     },
     264              :     {
     265              :         "*", 2, PGBENCH_MUL
     266              :     },
     267              :     {
     268              :         "/", 2, PGBENCH_DIV
     269              :     },
     270              :     {
     271              :         "mod", 2, PGBENCH_MOD
     272              :     },
     273              :     /* actual functions */
     274              :     {
     275              :         "abs", 1, PGBENCH_ABS
     276              :     },
     277              :     {
     278              :         "least", PGBENCH_NARGS_VARIABLE, PGBENCH_LEAST
     279              :     },
     280              :     {
     281              :         "greatest", PGBENCH_NARGS_VARIABLE, PGBENCH_GREATEST
     282              :     },
     283              :     {
     284              :         "debug", 1, PGBENCH_DEBUG
     285              :     },
     286              :     {
     287              :         "pi", 0, PGBENCH_PI
     288              :     },
     289              :     {
     290              :         "sqrt", 1, PGBENCH_SQRT
     291              :     },
     292              :     {
     293              :         "ln", 1, PGBENCH_LN
     294              :     },
     295              :     {
     296              :         "exp", 1, PGBENCH_EXP
     297              :     },
     298              :     {
     299              :         "int", 1, PGBENCH_INT
     300              :     },
     301              :     {
     302              :         "double", 1, PGBENCH_DOUBLE
     303              :     },
     304              :     {
     305              :         "random", 2, PGBENCH_RANDOM
     306              :     },
     307              :     {
     308              :         "random_gaussian", 3, PGBENCH_RANDOM_GAUSSIAN
     309              :     },
     310              :     {
     311              :         "random_exponential", 3, PGBENCH_RANDOM_EXPONENTIAL
     312              :     },
     313              :     {
     314              :         "random_zipfian", 3, PGBENCH_RANDOM_ZIPFIAN
     315              :     },
     316              :     {
     317              :         "pow", 2, PGBENCH_POW
     318              :     },
     319              :     {
     320              :         "power", 2, PGBENCH_POW
     321              :     },
     322              :     /* logical operators */
     323              :     {
     324              :         "!and", 2, PGBENCH_AND
     325              :     },
     326              :     {
     327              :         "!or", 2, PGBENCH_OR
     328              :     },
     329              :     {
     330              :         "!not", 1, PGBENCH_NOT
     331              :     },
     332              :     /* bitwise integer operators */
     333              :     {
     334              :         "&", 2, PGBENCH_BITAND
     335              :     },
     336              :     {
     337              :         "|", 2, PGBENCH_BITOR
     338              :     },
     339              :     {
     340              :         "#", 2, PGBENCH_BITXOR
     341              :     },
     342              :     {
     343              :         "<<", 2, PGBENCH_LSHIFT
     344              :     },
     345              :     {
     346              :         ">>", 2, PGBENCH_RSHIFT
     347              :     },
     348              :     /* comparison operators */
     349              :     {
     350              :         "=", 2, PGBENCH_EQ
     351              :     },
     352              :     {
     353              :         "<>", 2, PGBENCH_NE
     354              :     },
     355              :     {
     356              :         "<=", 2, PGBENCH_LE
     357              :     },
     358              :     {
     359              :         "<", 2, PGBENCH_LT
     360              :     },
     361              :     {
     362              :         "!is", 2, PGBENCH_IS
     363              :     },
     364              :     /* "case when ... then ... else ... end" construction */
     365              :     {
     366              :         "!case_end", PGBENCH_NARGS_CASE, PGBENCH_CASE
     367              :     },
     368              :     {
     369              :         "hash", PGBENCH_NARGS_HASH, PGBENCH_HASH_MURMUR2
     370              :     },
     371              :     {
     372              :         "hash_murmur2", PGBENCH_NARGS_HASH, PGBENCH_HASH_MURMUR2
     373              :     },
     374              :     {
     375              :         "hash_fnv1a", PGBENCH_NARGS_HASH, PGBENCH_HASH_FNV1A
     376              :     },
     377              :     {
     378              :         "permute", PGBENCH_NARGS_PERMUTE, PGBENCH_PERMUTE
     379              :     },
     380              :     /* keep as last array element */
     381              :     {
     382              :         NULL, 0, 0
     383              :     }
     384              : };
     385              : 
     386              : /*
     387              :  * Find a function from its name
     388              :  *
     389              :  * return the index of the function from the PGBENCH_FUNCTIONS array
     390              :  * or fail if the function is unknown.
     391              :  */
     392              : static int
     393          896 : find_func(yyscan_t yyscanner, const char *fname)
     394              : {
     395          896 :     int         i = 0;
     396              : 
     397        12490 :     while (PGBENCH_FUNCTIONS[i].fname)
     398              :     {
     399        12489 :         if (pg_strcasecmp(fname, PGBENCH_FUNCTIONS[i].fname) == 0)
     400          895 :             return i;
     401        11594 :         i++;
     402              :     }
     403              : 
     404            1 :     expr_yyerror_more(yyscanner, "unexpected function name", fname);
     405              : 
     406              :     /* not reached */
     407              :     return -1;
     408              : }
     409              : 
     410              : /* Expression linked list builder */
     411              : static PgBenchExprList *
     412         1769 : make_elist(PgBenchExpr *expr, PgBenchExprList *list)
     413              : {
     414              :     PgBenchExprLink *cons;
     415              : 
     416         1769 :     if (list == NULL)
     417              :     {
     418          890 :         list = pg_malloc_object(PgBenchExprList);
     419          890 :         list->head = NULL;
     420          890 :         list->tail = NULL;
     421              :     }
     422              : 
     423         1769 :     cons = pg_malloc_object(PgBenchExprLink);
     424         1769 :     cons->expr = expr;
     425         1769 :     cons->next = NULL;
     426              : 
     427         1769 :     if (list->head == NULL)
     428          890 :         list->head = cons;
     429              :     else
     430          879 :         list->tail->next = cons;
     431              : 
     432         1769 :     list->tail = cons;
     433              : 
     434         1769 :     return list;
     435              : }
     436              : 
     437              : /* Return the length of an expression list */
     438              : static int
     439          895 : elist_length(PgBenchExprList *list)
     440              : {
     441          895 :     PgBenchExprLink *link = list != NULL ? list->head : NULL;
     442          895 :     int         len = 0;
     443              : 
     444         2627 :     for (; link != NULL; link = link->next)
     445         1732 :         len++;
     446              : 
     447          895 :     return len;
     448              : }
     449              : 
     450              : /* Build function call expression */
     451              : static PgBenchExpr *
     452          895 : make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList *args)
     453              : {
     454          895 :     int         len = elist_length(args);
     455              : 
     456          895 :     PgBenchExpr *expr = pg_malloc_object(PgBenchExpr);
     457              : 
     458              :     Assert(fnumber >= 0);
     459              : 
     460              :     /* validate arguments number including few special cases */
     461          895 :     switch (PGBENCH_FUNCTIONS[fnumber].nargs)
     462              :     {
     463              :             /* check at least one arg for least & greatest */
     464            8 :         case PGBENCH_NARGS_VARIABLE:
     465            8 :             if (len == 0)
     466            3 :                 expr_yyerror_more(yyscanner, "at least one argument expected",
     467            3 :                                   PGBENCH_FUNCTIONS[fnumber].fname);
     468            5 :             break;
     469              : 
     470              :             /* case (when ... then ...)+ (else ...)? end */
     471           14 :         case PGBENCH_NARGS_CASE:
     472              :             /* 'else' branch is always present, but could be a NULL-constant */
     473           14 :             if (len < 3 || len % 2 != 1)
     474            0 :                 expr_yyerror_more(yyscanner,
     475              :                                   "odd and >= 3 number of arguments expected",
     476              :                                   "case control structure");
     477           14 :             break;
     478              : 
     479              :             /* hash functions with optional seed argument */
     480            8 :         case PGBENCH_NARGS_HASH:
     481            8 :             if (len < 1 || len > 2)
     482            2 :                 expr_yyerror_more(yyscanner, "unexpected number of arguments",
     483            2 :                                   PGBENCH_FUNCTIONS[fnumber].fname);
     484              : 
     485            6 :             if (len == 1)
     486              :             {
     487            2 :                 PgBenchExpr *var = make_variable("default_seed");
     488              : 
     489            2 :                 args = make_elist(var, args);
     490              :             }
     491            6 :             break;
     492              : 
     493              :             /* pseudorandom permutation function with optional seed argument */
     494           48 :         case PGBENCH_NARGS_PERMUTE:
     495           48 :             if (len < 2 || len > 3)
     496            2 :                 expr_yyerror_more(yyscanner, "unexpected number of arguments",
     497            2 :                                   PGBENCH_FUNCTIONS[fnumber].fname);
     498              : 
     499           46 :             if (len == 2)
     500              :             {
     501           35 :                 PgBenchExpr *var = make_variable("default_seed");
     502              : 
     503           35 :                 args = make_elist(var, args);
     504              :             }
     505           46 :             break;
     506              : 
     507              :             /* common case: positive arguments number */
     508          817 :         default:
     509              :             Assert(PGBENCH_FUNCTIONS[fnumber].nargs >= 0);
     510              : 
     511          817 :             if (PGBENCH_FUNCTIONS[fnumber].nargs != len)
     512            1 :                 expr_yyerror_more(yyscanner, "unexpected number of arguments",
     513            1 :                                   PGBENCH_FUNCTIONS[fnumber].fname);
     514              :     }
     515              : 
     516          887 :     expr->etype = ENODE_FUNCTION;
     517          887 :     expr->u.function.function = PGBENCH_FUNCTIONS[fnumber].tag;
     518              : 
     519              :     /* only the link is used, the head/tail is not useful anymore */
     520          887 :     expr->u.function.args = args != NULL ? args->head : NULL;
     521          887 :     if (args)
     522          886 :         pg_free(args);
     523              : 
     524          887 :     return expr;
     525              : }
     526              : 
     527              : static PgBenchExpr *
     528           14 : make_case(yyscan_t yyscanner, PgBenchExprList *when_then_list, PgBenchExpr *else_part)
     529              : {
     530           14 :     return make_func(yyscanner,
     531              :                      find_func(yyscanner, "!case_end"),
     532              :                      make_elist(else_part, when_then_list));
     533              : }
        

Generated by: LCOV version 2.0-1