LCOV - code coverage report
Current view: top level - src/bin/pgbench - exprparse.y (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 114 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 12 0
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              :                                                                 *expr_parse_result_p = $1;
      84              :                                                                 (void) yynerrs; /* suppress compiler warning */
      85              :                                                         }
      86              : 
      87              : elist:                                          { $$ = NULL; }
      88              :         | expr                                  { $$ = make_elist($1, NULL); }
      89              :         | elist ',' expr                { $$ = make_elist($3, $1); }
      90              :         ;
      91              : 
      92              : expr: '(' expr ')'                      { $$ = $2; }
      93              :         | '+' expr %prec UNARY  { $$ = $2; }
      94              :         /* unary minus "-x" implemented as "0 - x" */
      95              :         | '-' 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              :                                                         { $$ = make_integer_constant(PG_INT64_MIN); }
     100              :         /* binary ones complement "~x" implemented as 0xffff... xor x" */
     101              :         | '~' expr                              { $$ = make_op(yyscanner, "#",
     102              :                                                                                    make_integer_constant(~INT64CONST(0)), $2); }
     103              :         | NOT_OP expr                   { $$ = make_uop(yyscanner, "!not", $2); }
     104              :         | expr '+' expr                 { $$ = make_op(yyscanner, "+", $1, $3); }
     105              :         | expr '-' expr                 { $$ = make_op(yyscanner, "-", $1, $3); }
     106              :         | expr '*' expr                 { $$ = make_op(yyscanner, "*", $1, $3); }
     107              :         | expr '/' expr                 { $$ = make_op(yyscanner, "/", $1, $3); }
     108              :         | expr '%' expr                 { $$ = make_op(yyscanner, "mod", $1, $3); }
     109              :         | expr '<' expr                      { $$ = make_op(yyscanner, "<", $1, $3); }
     110              :         | expr LE_OP expr               { $$ = make_op(yyscanner, "<=", $1, $3); }
     111              :         | expr '>' expr                      { $$ = make_op(yyscanner, "<", $3, $1); }
     112              :         | expr GE_OP expr               { $$ = make_op(yyscanner, "<=", $3, $1); }
     113              :         | expr '=' expr                 { $$ = make_op(yyscanner, "=", $1, $3); }
     114              :         | expr NE_OP expr               { $$ = make_op(yyscanner, "<>", $1, $3); }
     115              :         | expr '&' expr                     { $$ = make_op(yyscanner, "&", $1, $3); }
     116              :         | expr '|' expr                 { $$ = make_op(yyscanner, "|", $1, $3); }
     117              :         | expr '#' expr                 { $$ = make_op(yyscanner, "#", $1, $3); }
     118              :         | expr LS_OP expr               { $$ = make_op(yyscanner, "<<", $1, $3); }
     119              :         | expr RS_OP expr               { $$ = make_op(yyscanner, ">>", $1, $3); }
     120              :         | expr AND_OP expr              { $$ = make_op(yyscanner, "!and", $1, $3); }
     121              :         | expr OR_OP expr               { $$ = make_op(yyscanner, "!or", $1, $3); }
     122              :         /* IS variants */
     123              :         | expr ISNULL_OP                { $$ = make_op(yyscanner, "!is", $1, make_null_constant()); }
     124              :         | expr NOTNULL_OP               {
     125              :                                                                 $$ = make_uop(yyscanner, "!not",
     126              :                                                                                           make_op(yyscanner, "!is", $1, make_null_constant()));
     127              :                                                         }
     128              :         | expr IS_OP NULL_CONST { $$ = make_op(yyscanner, "!is", $1, make_null_constant()); }
     129              :         | expr IS_OP NOT_OP NULL_CONST
     130              :                                                         {
     131              :                                                                 $$ = make_uop(yyscanner, "!not",
     132              :                                                                                           make_op(yyscanner, "!is", $1, make_null_constant()));
     133              :                                                         }
     134              :         | expr IS_OP BOOLEAN_CONST
     135              :                                                         {
     136              :                                                                 $$ = make_op(yyscanner, "!is", $1, make_boolean_constant($3));
     137              :                                                         }
     138              :         | expr IS_OP NOT_OP BOOLEAN_CONST
     139              :                                                         {
     140              :                                                                 $$ = make_uop(yyscanner, "!not",
     141              :                                                                                           make_op(yyscanner, "!is", $1, make_boolean_constant($4)));
     142              :                                                         }
     143              :         /* constants */
     144              :         | NULL_CONST                    { $$ = make_null_constant(); }
     145              :         | BOOLEAN_CONST                 { $$ = make_boolean_constant($1); }
     146              :         | INTEGER_CONST                 { $$ = make_integer_constant($1); }
     147              :         | DOUBLE_CONST                  { $$ = make_double_constant($1); }
     148              :         /* misc */
     149              :         | VARIABLE                              { $$ = make_variable($1); }
     150              :         | function '(' elist ')' { $$ = make_func(yyscanner, $1, $3); }
     151              :         | case_control                  { $$ = $1; }
     152              :         ;
     153              : 
     154              : when_then_list:
     155              :           when_then_list WHEN_KW expr THEN_KW expr { $$ = make_elist($5, make_elist($3, $1)); }
     156              :         | WHEN_KW expr THEN_KW expr { $$ = make_elist($4, make_elist($2, NULL)); }
     157              : 
     158              : case_control:
     159              :           CASE_KW when_then_list END_KW { $$ = make_case(yyscanner, $2, make_null_constant()); }
     160              :         | CASE_KW when_then_list ELSE_KW expr END_KW { $$ = make_case(yyscanner, $2, $4); }
     161              : 
     162              : function: FUNCTION                      { $$ = find_func(yyscanner, $1); pg_free($1); }
     163              :         ;
     164              : 
     165              : %%
     166              : 
     167              : static PgBenchExpr *
     168            0 : make_null_constant(void)
     169              : {
     170            0 :         PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
     171              : 
     172            0 :         expr->etype = ENODE_CONSTANT;
     173            0 :         expr->u.constant.type = PGBT_NULL;
     174            0 :         expr->u.constant.u.ival = 0;
     175            0 :         return expr;
     176            0 : }
     177              : 
     178              : static PgBenchExpr *
     179            0 : make_integer_constant(int64 ival)
     180              : {
     181            0 :         PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
     182              : 
     183            0 :         expr->etype = ENODE_CONSTANT;
     184            0 :         expr->u.constant.type = PGBT_INT;
     185            0 :         expr->u.constant.u.ival = ival;
     186            0 :         return expr;
     187            0 : }
     188              : 
     189              : static PgBenchExpr *
     190            0 : make_double_constant(double dval)
     191              : {
     192            0 :         PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
     193              : 
     194            0 :         expr->etype = ENODE_CONSTANT;
     195            0 :         expr->u.constant.type = PGBT_DOUBLE;
     196            0 :         expr->u.constant.u.dval = dval;
     197            0 :         return expr;
     198            0 : }
     199              : 
     200              : static PgBenchExpr *
     201            0 : make_boolean_constant(bool bval)
     202              : {
     203            0 :         PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
     204              : 
     205            0 :         expr->etype = ENODE_CONSTANT;
     206            0 :         expr->u.constant.type = PGBT_BOOLEAN;
     207            0 :         expr->u.constant.u.bval = bval;
     208            0 :         return expr;
     209            0 : }
     210              : 
     211              : static PgBenchExpr *
     212            0 : make_variable(char *varname)
     213              : {
     214            0 :         PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
     215              : 
     216            0 :         expr->etype = ENODE_VARIABLE;
     217            0 :         expr->u.variable.varname = varname;
     218            0 :         return expr;
     219            0 : }
     220              : 
     221              : /* binary operators */
     222              : static PgBenchExpr *
     223            0 : make_op(yyscan_t yyscanner, const char *operator,
     224              :                 PgBenchExpr *lexpr, PgBenchExpr *rexpr)
     225              : {
     226            0 :         return make_func(yyscanner, find_func(yyscanner, operator),
     227            0 :                                          make_elist(rexpr, make_elist(lexpr, NULL)));
     228              : }
     229              : 
     230              : /* unary operator */
     231              : static PgBenchExpr *
     232            0 : make_uop(yyscan_t yyscanner, const char *operator, PgBenchExpr *expr)
     233              : {
     234            0 :         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            0 : find_func(yyscan_t yyscanner, const char *fname)
     394              : {
     395            0 :         int                     i = 0;
     396              : 
     397            0 :         while (PGBENCH_FUNCTIONS[i].fname)
     398              :         {
     399            0 :                 if (pg_strcasecmp(fname, PGBENCH_FUNCTIONS[i].fname) == 0)
     400            0 :                         return i;
     401            0 :                 i++;
     402              :         }
     403              : 
     404            0 :         expr_yyerror_more(yyscanner, "unexpected function name", fname);
     405              : 
     406              :         /* not reached */
     407              :         return -1;
     408            0 : }
     409              : 
     410              : /* Expression linked list builder */
     411              : static PgBenchExprList *
     412            0 : make_elist(PgBenchExpr *expr, PgBenchExprList *list)
     413              : {
     414            0 :         PgBenchExprLink *cons;
     415              : 
     416            0 :         if (list == NULL)
     417              :         {
     418            0 :                 list = pg_malloc(sizeof(PgBenchExprList));
     419            0 :                 list->head = NULL;
     420            0 :                 list->tail = NULL;
     421            0 :         }
     422              : 
     423            0 :         cons = pg_malloc(sizeof(PgBenchExprLink));
     424            0 :         cons->expr = expr;
     425            0 :         cons->next = NULL;
     426              : 
     427            0 :         if (list->head == NULL)
     428            0 :                 list->head = cons;
     429              :         else
     430            0 :                 list->tail->next = cons;
     431              : 
     432            0 :         list->tail = cons;
     433              : 
     434            0 :         return list;
     435            0 : }
     436              : 
     437              : /* Return the length of an expression list */
     438              : static int
     439            0 : elist_length(PgBenchExprList *list)
     440              : {
     441            0 :         PgBenchExprLink *link = list != NULL ? list->head : NULL;
     442            0 :         int                     len = 0;
     443              : 
     444            0 :         for (; link != NULL; link = link->next)
     445            0 :                 len++;
     446              : 
     447            0 :         return len;
     448            0 : }
     449              : 
     450              : /* Build function call expression */
     451              : static PgBenchExpr *
     452            0 : make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList *args)
     453              : {
     454            0 :         int                     len = elist_length(args);
     455              : 
     456            0 :         PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
     457              : 
     458            0 :         Assert(fnumber >= 0);
     459              : 
     460              :         /* validate arguments number including few special cases */
     461            0 :         switch (PGBENCH_FUNCTIONS[fnumber].nargs)
     462              :         {
     463              :                         /* check at least one arg for least & greatest */
     464              :                 case PGBENCH_NARGS_VARIABLE:
     465            0 :                         if (len == 0)
     466            0 :                                 expr_yyerror_more(yyscanner, "at least one argument expected",
     467            0 :                                                                   PGBENCH_FUNCTIONS[fnumber].fname);
     468            0 :                         break;
     469              : 
     470              :                         /* case (when ... then ...)+ (else ...)? end */
     471              :                 case PGBENCH_NARGS_CASE:
     472              :                         /* 'else' branch is always present, but could be a NULL-constant */
     473            0 :                         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            0 :                         break;
     478              : 
     479              :                         /* hash functions with optional seed argument */
     480              :                 case PGBENCH_NARGS_HASH:
     481            0 :                         if (len < 1 || len > 2)
     482            0 :                                 expr_yyerror_more(yyscanner, "unexpected number of arguments",
     483            0 :                                                                   PGBENCH_FUNCTIONS[fnumber].fname);
     484              : 
     485            0 :                         if (len == 1)
     486              :                         {
     487            0 :                                 PgBenchExpr *var = make_variable("default_seed");
     488              : 
     489            0 :                                 args = make_elist(var, args);
     490            0 :                         }
     491            0 :                         break;
     492              : 
     493              :                         /* pseudorandom permutation function with optional seed argument */
     494              :                 case PGBENCH_NARGS_PERMUTE:
     495            0 :                         if (len < 2 || len > 3)
     496            0 :                                 expr_yyerror_more(yyscanner, "unexpected number of arguments",
     497            0 :                                                                   PGBENCH_FUNCTIONS[fnumber].fname);
     498              : 
     499            0 :                         if (len == 2)
     500              :                         {
     501            0 :                                 PgBenchExpr *var = make_variable("default_seed");
     502              : 
     503            0 :                                 args = make_elist(var, args);
     504            0 :                         }
     505            0 :                         break;
     506              : 
     507              :                         /* common case: positive arguments number */
     508              :                 default:
     509            0 :                         Assert(PGBENCH_FUNCTIONS[fnumber].nargs >= 0);
     510              : 
     511            0 :                         if (PGBENCH_FUNCTIONS[fnumber].nargs != len)
     512            0 :                                 expr_yyerror_more(yyscanner, "unexpected number of arguments",
     513            0 :                                                                   PGBENCH_FUNCTIONS[fnumber].fname);
     514            0 :         }
     515              : 
     516            0 :         expr->etype = ENODE_FUNCTION;
     517            0 :         expr->u.function.function = PGBENCH_FUNCTIONS[fnumber].tag;
     518              : 
     519              :         /* only the link is used, the head/tail is not useful anymore */
     520            0 :         expr->u.function.args = args != NULL ? args->head : NULL;
     521            0 :         if (args)
     522            0 :                 pg_free(args);
     523              : 
     524            0 :         return expr;
     525            0 : }
     526              : 
     527              : static PgBenchExpr *
     528            0 : make_case(yyscan_t yyscanner, PgBenchExprList *when_then_list, PgBenchExpr *else_part)
     529              : {
     530            0 :         return make_func(yyscanner,
     531            0 :                                          find_func(yyscanner, "!case_end"),
     532            0 :                                          make_elist(else_part, when_then_list));
     533              : }
        

Generated by: LCOV version 2.3.2-1