mirror of
https://github.com/postgres/postgres.git
synced 2025-05-17 00:03:56 -04:00
Hashing function is useful for simulating real-world workload in test like WEB workload, as an example - YCSB benchmarks. Author: Ildar Musin with minor editorization by me Reviewed by: Fabien Coelho, me Discussion: https://www.postgresql.org/message-id/flat/0e8bd39e-dfcd-2879-f88f-272799ad7ef2@postgrespro.ru
532 lines
13 KiB
Plaintext
532 lines
13 KiB
Plaintext
%{
|
|
/*-------------------------------------------------------------------------
|
|
*
|
|
* exprparse.y
|
|
* bison grammar for a simple expression syntax
|
|
*
|
|
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* src/bin/pgbench/exprparse.y
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres_fe.h"
|
|
|
|
#include "pgbench.h"
|
|
|
|
#define PGBENCH_NARGS_VARIABLE (-1)
|
|
#define PGBENCH_NARGS_CASE (-2)
|
|
#define PGBENCH_NARGS_HASH (-3)
|
|
|
|
PgBenchExpr *expr_parse_result;
|
|
|
|
static PgBenchExprList *make_elist(PgBenchExpr *exp, PgBenchExprList *list);
|
|
static PgBenchExpr *make_null_constant(void);
|
|
static PgBenchExpr *make_boolean_constant(bool bval);
|
|
static PgBenchExpr *make_integer_constant(int64 ival);
|
|
static PgBenchExpr *make_double_constant(double dval);
|
|
static PgBenchExpr *make_variable(char *varname);
|
|
static PgBenchExpr *make_op(yyscan_t yyscanner, const char *operator,
|
|
PgBenchExpr *lexpr, PgBenchExpr *rexpr);
|
|
static PgBenchExpr *make_uop(yyscan_t yyscanner, const char *operator, PgBenchExpr *expr);
|
|
static int find_func(yyscan_t yyscanner, const char *fname);
|
|
static PgBenchExpr *make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList *args);
|
|
static PgBenchExpr *make_case(yyscan_t yyscanner, PgBenchExprList *when_then_list, PgBenchExpr *else_part);
|
|
|
|
%}
|
|
|
|
%pure-parser
|
|
%expect 0
|
|
%name-prefix="expr_yy"
|
|
|
|
%parse-param {yyscan_t yyscanner}
|
|
%lex-param {yyscan_t yyscanner}
|
|
|
|
%union
|
|
{
|
|
int64 ival;
|
|
double dval;
|
|
bool bval;
|
|
char *str;
|
|
PgBenchExpr *expr;
|
|
PgBenchExprList *elist;
|
|
}
|
|
|
|
%type <elist> elist when_then_list
|
|
%type <expr> expr case_control
|
|
%type <ival> INTEGER_CONST function
|
|
%type <dval> DOUBLE_CONST
|
|
%type <bval> BOOLEAN_CONST
|
|
%type <str> VARIABLE FUNCTION
|
|
|
|
%token NULL_CONST INTEGER_CONST DOUBLE_CONST BOOLEAN_CONST VARIABLE FUNCTION
|
|
%token AND_OP OR_OP NOT_OP NE_OP LE_OP GE_OP LS_OP RS_OP IS_OP
|
|
%token CASE_KW WHEN_KW THEN_KW ELSE_KW END_KW
|
|
|
|
/* Precedence: lowest to highest, taken from postgres SQL parser */
|
|
%left OR_OP
|
|
%left AND_OP
|
|
%right NOT_OP
|
|
%nonassoc IS_OP ISNULL_OP NOTNULL_OP
|
|
%nonassoc '<' '>' '=' LE_OP GE_OP NE_OP
|
|
%left '|' '#' '&' LS_OP RS_OP '~'
|
|
%left '+' '-'
|
|
%left '*' '/' '%'
|
|
%right UNARY
|
|
|
|
%%
|
|
|
|
result: expr { expr_parse_result = $1; }
|
|
|
|
elist: { $$ = NULL; }
|
|
| expr { $$ = make_elist($1, NULL); }
|
|
| elist ',' expr { $$ = make_elist($3, $1); }
|
|
;
|
|
|
|
expr: '(' expr ')' { $$ = $2; }
|
|
| '+' expr %prec UNARY { $$ = $2; }
|
|
/* unary minus "-x" implemented as "0 - x" */
|
|
| '-' expr %prec UNARY { $$ = make_op(yyscanner, "-",
|
|
make_integer_constant(0), $2); }
|
|
/* binary ones complement "~x" implemented as 0xffff... xor x" */
|
|
| '~' expr { $$ = make_op(yyscanner, "#",
|
|
make_integer_constant(~INT64CONST(0)), $2); }
|
|
| NOT_OP expr { $$ = make_uop(yyscanner, "!not", $2); }
|
|
| expr '+' expr { $$ = make_op(yyscanner, "+", $1, $3); }
|
|
| expr '-' expr { $$ = make_op(yyscanner, "-", $1, $3); }
|
|
| expr '*' expr { $$ = make_op(yyscanner, "*", $1, $3); }
|
|
| expr '/' expr { $$ = make_op(yyscanner, "/", $1, $3); }
|
|
| expr '%' expr { $$ = make_op(yyscanner, "mod", $1, $3); }
|
|
| expr '<' expr { $$ = make_op(yyscanner, "<", $1, $3); }
|
|
| expr LE_OP expr { $$ = make_op(yyscanner, "<=", $1, $3); }
|
|
| expr '>' expr { $$ = make_op(yyscanner, "<", $3, $1); }
|
|
| expr GE_OP expr { $$ = make_op(yyscanner, "<=", $3, $1); }
|
|
| expr '=' expr { $$ = make_op(yyscanner, "=", $1, $3); }
|
|
| expr NE_OP expr { $$ = make_op(yyscanner, "<>", $1, $3); }
|
|
| expr '&' expr { $$ = make_op(yyscanner, "&", $1, $3); }
|
|
| expr '|' expr { $$ = make_op(yyscanner, "|", $1, $3); }
|
|
| expr '#' expr { $$ = make_op(yyscanner, "#", $1, $3); }
|
|
| expr LS_OP expr { $$ = make_op(yyscanner, "<<", $1, $3); }
|
|
| expr RS_OP expr { $$ = make_op(yyscanner, ">>", $1, $3); }
|
|
| expr AND_OP expr { $$ = make_op(yyscanner, "!and", $1, $3); }
|
|
| expr OR_OP expr { $$ = make_op(yyscanner, "!or", $1, $3); }
|
|
/* IS variants */
|
|
| expr ISNULL_OP { $$ = make_op(yyscanner, "!is", $1, make_null_constant()); }
|
|
| expr NOTNULL_OP {
|
|
$$ = make_uop(yyscanner, "!not",
|
|
make_op(yyscanner, "!is", $1, make_null_constant()));
|
|
}
|
|
| expr IS_OP NULL_CONST { $$ = make_op(yyscanner, "!is", $1, make_null_constant()); }
|
|
| expr IS_OP NOT_OP NULL_CONST
|
|
{
|
|
$$ = make_uop(yyscanner, "!not",
|
|
make_op(yyscanner, "!is", $1, make_null_constant()));
|
|
}
|
|
| expr IS_OP BOOLEAN_CONST
|
|
{
|
|
$$ = make_op(yyscanner, "!is", $1, make_boolean_constant($3));
|
|
}
|
|
| expr IS_OP NOT_OP BOOLEAN_CONST
|
|
{
|
|
$$ = make_uop(yyscanner, "!not",
|
|
make_op(yyscanner, "!is", $1, make_boolean_constant($4)));
|
|
}
|
|
/* constants */
|
|
| NULL_CONST { $$ = make_null_constant(); }
|
|
| BOOLEAN_CONST { $$ = make_boolean_constant($1); }
|
|
| INTEGER_CONST { $$ = make_integer_constant($1); }
|
|
| DOUBLE_CONST { $$ = make_double_constant($1); }
|
|
/* misc */
|
|
| VARIABLE { $$ = make_variable($1); }
|
|
| function '(' elist ')' { $$ = make_func(yyscanner, $1, $3); }
|
|
| case_control { $$ = $1; }
|
|
;
|
|
|
|
when_then_list:
|
|
when_then_list WHEN_KW expr THEN_KW expr { $$ = make_elist($5, make_elist($3, $1)); }
|
|
| WHEN_KW expr THEN_KW expr { $$ = make_elist($4, make_elist($2, NULL)); }
|
|
|
|
case_control:
|
|
CASE_KW when_then_list END_KW { $$ = make_case(yyscanner, $2, make_null_constant()); }
|
|
| CASE_KW when_then_list ELSE_KW expr END_KW { $$ = make_case(yyscanner, $2, $4); }
|
|
|
|
function: FUNCTION { $$ = find_func(yyscanner, $1); pg_free($1); }
|
|
;
|
|
|
|
%%
|
|
|
|
static PgBenchExpr *
|
|
make_null_constant(void)
|
|
{
|
|
PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
|
|
|
|
expr->etype = ENODE_CONSTANT;
|
|
expr->u.constant.type = PGBT_NULL;
|
|
expr->u.constant.u.ival = 0;
|
|
return expr;
|
|
}
|
|
|
|
static PgBenchExpr *
|
|
make_integer_constant(int64 ival)
|
|
{
|
|
PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
|
|
|
|
expr->etype = ENODE_CONSTANT;
|
|
expr->u.constant.type = PGBT_INT;
|
|
expr->u.constant.u.ival = ival;
|
|
return expr;
|
|
}
|
|
|
|
static PgBenchExpr *
|
|
make_double_constant(double dval)
|
|
{
|
|
PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
|
|
|
|
expr->etype = ENODE_CONSTANT;
|
|
expr->u.constant.type = PGBT_DOUBLE;
|
|
expr->u.constant.u.dval = dval;
|
|
return expr;
|
|
}
|
|
|
|
static PgBenchExpr *
|
|
make_boolean_constant(bool bval)
|
|
{
|
|
PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
|
|
|
|
expr->etype = ENODE_CONSTANT;
|
|
expr->u.constant.type = PGBT_BOOLEAN;
|
|
expr->u.constant.u.bval = bval;
|
|
return expr;
|
|
}
|
|
|
|
static PgBenchExpr *
|
|
make_variable(char *varname)
|
|
{
|
|
PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
|
|
|
|
expr->etype = ENODE_VARIABLE;
|
|
expr->u.variable.varname = varname;
|
|
return expr;
|
|
}
|
|
|
|
/* binary operators */
|
|
static PgBenchExpr *
|
|
make_op(yyscan_t yyscanner, const char *operator,
|
|
PgBenchExpr *lexpr, PgBenchExpr *rexpr)
|
|
{
|
|
return make_func(yyscanner, find_func(yyscanner, operator),
|
|
make_elist(rexpr, make_elist(lexpr, NULL)));
|
|
}
|
|
|
|
/* unary operator */
|
|
static PgBenchExpr *
|
|
make_uop(yyscan_t yyscanner, const char *operator, PgBenchExpr *expr)
|
|
{
|
|
return make_func(yyscanner, find_func(yyscanner, operator), make_elist(expr, NULL));
|
|
}
|
|
|
|
/*
|
|
* List of available functions:
|
|
* - fname: function name, "!..." for special internal functions
|
|
* - nargs: number of arguments. Special cases:
|
|
* - PGBENCH_NARGS_VARIABLE is a special value for least & greatest
|
|
* meaning #args >= 1;
|
|
* - PGBENCH_NARGS_CASE is for the "CASE WHEN ..." function, which
|
|
* has #args >= 3 and odd;
|
|
* - PGBENCH_NARGS_HASH is for hash functions, which have one required
|
|
* and one optional argument;
|
|
* - tag: function identifier from PgBenchFunction enum
|
|
*/
|
|
static const struct
|
|
{
|
|
const 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
|
|
},
|
|
{
|
|
"mod", 2, PGBENCH_MOD
|
|
},
|
|
/* actual functions */
|
|
{
|
|
"abs", 1, PGBENCH_ABS
|
|
},
|
|
{
|
|
"least", PGBENCH_NARGS_VARIABLE, PGBENCH_LEAST
|
|
},
|
|
{
|
|
"greatest", PGBENCH_NARGS_VARIABLE, PGBENCH_GREATEST
|
|
},
|
|
{
|
|
"debug", 1, PGBENCH_DEBUG
|
|
},
|
|
{
|
|
"pi", 0, PGBENCH_PI
|
|
},
|
|
{
|
|
"sqrt", 1, PGBENCH_SQRT
|
|
},
|
|
{
|
|
"ln", 1, PGBENCH_LN
|
|
},
|
|
{
|
|
"exp", 1, PGBENCH_EXP
|
|
},
|
|
{
|
|
"int", 1, PGBENCH_INT
|
|
},
|
|
{
|
|
"double", 1, PGBENCH_DOUBLE
|
|
},
|
|
{
|
|
"random", 2, PGBENCH_RANDOM
|
|
},
|
|
{
|
|
"random_gaussian", 3, PGBENCH_RANDOM_GAUSSIAN
|
|
},
|
|
{
|
|
"random_exponential", 3, PGBENCH_RANDOM_EXPONENTIAL
|
|
},
|
|
{
|
|
"random_zipfian", 3, PGBENCH_RANDOM_ZIPFIAN
|
|
},
|
|
{
|
|
"pow", 2, PGBENCH_POW
|
|
},
|
|
{
|
|
"power", 2, PGBENCH_POW
|
|
},
|
|
/* logical operators */
|
|
{
|
|
"!and", 2, PGBENCH_AND
|
|
},
|
|
{
|
|
"!or", 2, PGBENCH_OR
|
|
},
|
|
{
|
|
"!not", 1, PGBENCH_NOT
|
|
},
|
|
/* bitwise integer operators */
|
|
{
|
|
"&", 2, PGBENCH_BITAND
|
|
},
|
|
{
|
|
"|", 2, PGBENCH_BITOR
|
|
},
|
|
{
|
|
"#", 2, PGBENCH_BITXOR
|
|
},
|
|
{
|
|
"<<", 2, PGBENCH_LSHIFT
|
|
},
|
|
{
|
|
">>", 2, PGBENCH_RSHIFT
|
|
},
|
|
/* comparison operators */
|
|
{
|
|
"=", 2, PGBENCH_EQ
|
|
},
|
|
{
|
|
"<>", 2, PGBENCH_NE
|
|
},
|
|
{
|
|
"<=", 2, PGBENCH_LE
|
|
},
|
|
{
|
|
"<", 2, PGBENCH_LT
|
|
},
|
|
{
|
|
"!is", 2, PGBENCH_IS
|
|
},
|
|
/* "case when ... then ... else ... end" construction */
|
|
{
|
|
"!case_end", PGBENCH_NARGS_CASE, PGBENCH_CASE
|
|
},
|
|
{
|
|
"hash", PGBENCH_NARGS_HASH, PGBENCH_HASH_MURMUR2
|
|
},
|
|
{
|
|
"hash_murmur2", PGBENCH_NARGS_HASH, PGBENCH_HASH_MURMUR2
|
|
},
|
|
{
|
|
"hash_fnv1a", PGBENCH_NARGS_HASH, PGBENCH_HASH_FNV1A
|
|
},
|
|
{
|
|
"hash", PGBENCH_NARGS_HASH, PGBENCH_HASH_MURMUR2
|
|
},
|
|
{
|
|
"hash_murmur2", PGBENCH_NARGS_HASH, PGBENCH_HASH_MURMUR2
|
|
},
|
|
{
|
|
"hash_fnv1a", PGBENCH_NARGS_HASH, PGBENCH_HASH_FNV1A
|
|
},
|
|
/* 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(yyscan_t yyscanner, 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(yyscanner, "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(yyscan_t yyscanner, int fnumber, PgBenchExprList *args)
|
|
{
|
|
int len = elist_length(args);
|
|
|
|
PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
|
|
|
|
Assert(fnumber >= 0);
|
|
|
|
/* validate arguments number including few special cases */
|
|
switch (PGBENCH_FUNCTIONS[fnumber].nargs)
|
|
{
|
|
/* check at least one arg for least & greatest */
|
|
case PGBENCH_NARGS_VARIABLE:
|
|
if (len == 0)
|
|
expr_yyerror_more(yyscanner, "at least one argument expected",
|
|
PGBENCH_FUNCTIONS[fnumber].fname);
|
|
break;
|
|
|
|
/* case (when ... then ...)+ (else ...)? end */
|
|
case PGBENCH_NARGS_CASE:
|
|
/* 'else' branch is always present, but could be a NULL-constant */
|
|
if (len < 3 || len % 2 != 1)
|
|
expr_yyerror_more(yyscanner,
|
|
"odd and >= 3 number of arguments expected",
|
|
"case control structure");
|
|
break;
|
|
|
|
/* hash functions with optional seed argument */
|
|
case PGBENCH_NARGS_HASH:
|
|
if (len > 2)
|
|
expr_yyerror_more(yyscanner, "unexpected number of arguments",
|
|
PGBENCH_FUNCTIONS[fnumber].fname);
|
|
|
|
if (len == 1)
|
|
{
|
|
PgBenchExpr *var = make_variable("default_seed");
|
|
args = make_elist(var, args);
|
|
}
|
|
break;
|
|
|
|
/* common case: positive arguments number */
|
|
default:
|
|
Assert(PGBENCH_FUNCTIONS[fnumber].nargs >= 0);
|
|
|
|
if (PGBENCH_FUNCTIONS[fnumber].nargs != len)
|
|
expr_yyerror_more(yyscanner, "unexpected number of arguments",
|
|
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;
|
|
}
|
|
|
|
static PgBenchExpr *
|
|
make_case(yyscan_t yyscanner, PgBenchExprList *when_then_list, PgBenchExpr *else_part)
|
|
{
|
|
return make_func(yyscanner,
|
|
find_func(yyscanner, "!case_end"),
|
|
make_elist(else_part, when_then_list));
|
|
}
|
|
|
|
/*
|
|
* exprscan.l is compiled as part of exprparse.y. Currently, this is
|
|
* unavoidable because exprparse does not create a .h file to export
|
|
* its token symbols. If these files ever grow large enough to be
|
|
* worth compiling separately, that could be fixed; but for now it
|
|
* seems like useless complication.
|
|
*/
|
|
|
|
/* First, get rid of "#define yyscan_t" from pgbench.h */
|
|
#undef yyscan_t
|
|
/* ... and the yylval macro, which flex will have its own definition for */
|
|
#undef yylval
|
|
|
|
#include "exprscan.c"
|