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