mirror of
https://github.com/postgres/postgres.git
synced 2025-06-26 12:21:12 +03:00
Extend pgbench's expression syntax to support a few built-in functions.
Fabien Coelho, reviewed mostly by Michael Paquier and me, but also by Heikki Linnakangas, BeomYong Lee, Kyotaro Horiguchi, Oleksander Shulgin, and Álvaro Herrera.
This commit is contained in:
@ -786,7 +786,7 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<variablelist>
|
<variablelist>
|
||||||
<varlistentry>
|
<varlistentry id='pgbench-metacommand-set'>
|
||||||
<term>
|
<term>
|
||||||
<literal>\set <replaceable>varname</> <replaceable>expression</></literal>
|
<literal>\set <replaceable>varname</> <replaceable>expression</></literal>
|
||||||
</term>
|
</term>
|
||||||
@ -798,8 +798,10 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
|
|||||||
The expression may contain integer constants such as <literal>5432</>,
|
The expression may contain integer constants such as <literal>5432</>,
|
||||||
references to variables <literal>:</><replaceable>variablename</>,
|
references to variables <literal>:</><replaceable>variablename</>,
|
||||||
and expressions composed of unary (<literal>-</>) or binary operators
|
and expressions composed of unary (<literal>-</>) or binary operators
|
||||||
(<literal>+</>, <literal>-</>, <literal>*</>, <literal>/</>, <literal>%</>)
|
(<literal>+</>, <literal>-</>, <literal>*</>, <literal>/</>,
|
||||||
with their usual associativity, and parentheses.
|
<literal>%</>) with their usual associativity,
|
||||||
|
<link linkend="pgbench-builtin-functions">function calls</>, and
|
||||||
|
parentheses.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -994,6 +996,62 @@ END;
|
|||||||
|
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
|
||||||
|
<refsect2 id="pgbench-builtin-functions">
|
||||||
|
<title>Built-In Functions</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The following functions are built into <application>pgbench</> and
|
||||||
|
may be used in conjunction with
|
||||||
|
<link linkend="pgbench-metacommand-set"><literal>\set</literal></link>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<!-- list pgbench functions in alphabetical order -->
|
||||||
|
<table>
|
||||||
|
<title>pgbench Functions</title>
|
||||||
|
<tgroup cols="5">
|
||||||
|
<thead>
|
||||||
|
<row>
|
||||||
|
<entry>Function</entry>
|
||||||
|
<entry>Return Type</entry>
|
||||||
|
<entry>Description</entry>
|
||||||
|
<entry>Example</entry>
|
||||||
|
<entry>Result</entry>
|
||||||
|
</row>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<row>
|
||||||
|
<entry><literal><function>abs(<replaceable>a</>)</></></>
|
||||||
|
<entry>same as <replaceable>a</></>
|
||||||
|
<entry>integer value</>
|
||||||
|
<entry><literal>abs(-17)</></>
|
||||||
|
<entry><literal>17</></>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal><function>debug(<replaceable>a</>)</></></>
|
||||||
|
<entry>same as <replaceable>a</> </>
|
||||||
|
<entry>print to <systemitem>stderr</systemitem> the given argument</>
|
||||||
|
<entry><literal>debug(5432)</></>
|
||||||
|
<entry><literal>5432</></>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal><function>max(<replaceable>i</> [, <replaceable>...</> ] )</></></>
|
||||||
|
<entry>integer</>
|
||||||
|
<entry>maximum value</>
|
||||||
|
<entry><literal>max(5, 4, 3, 2)</></>
|
||||||
|
<entry><literal>5</></>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry><literal><function>min(<replaceable>i</> [, <replaceable>...</> ] )</></></>
|
||||||
|
<entry>integer</>
|
||||||
|
<entry>minimum value</>
|
||||||
|
<entry><literal>min(5, 4, 3, 2)</></>
|
||||||
|
<entry><literal>2</></>
|
||||||
|
</row>
|
||||||
|
</tbody>
|
||||||
|
</tgroup>
|
||||||
|
</table>
|
||||||
|
</refsect2>
|
||||||
|
|
||||||
<refsect2>
|
<refsect2>
|
||||||
<title>Per-Transaction Logging</title>
|
<title>Per-Transaction Logging</title>
|
||||||
|
|
||||||
|
@ -16,10 +16,13 @@
|
|||||||
|
|
||||||
PgBenchExpr *expr_parse_result;
|
PgBenchExpr *expr_parse_result;
|
||||||
|
|
||||||
|
static PgBenchExprList *make_elist(PgBenchExpr *exp, PgBenchExprList *list);
|
||||||
static PgBenchExpr *make_integer_constant(int64 ival);
|
static PgBenchExpr *make_integer_constant(int64 ival);
|
||||||
static PgBenchExpr *make_variable(char *varname);
|
static PgBenchExpr *make_variable(char *varname);
|
||||||
static PgBenchExpr *make_op(char operator, PgBenchExpr *lexpr,
|
static PgBenchExpr *make_op(const char *operator, PgBenchExpr *lexpr,
|
||||||
PgBenchExpr *rexpr);
|
PgBenchExpr *rexpr);
|
||||||
|
static int find_func(const char *fname);
|
||||||
|
static PgBenchExpr *make_func(const int fnumber, PgBenchExprList *args);
|
||||||
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
@ -31,13 +34,15 @@ static PgBenchExpr *make_op(char operator, PgBenchExpr *lexpr,
|
|||||||
int64 ival;
|
int64 ival;
|
||||||
char *str;
|
char *str;
|
||||||
PgBenchExpr *expr;
|
PgBenchExpr *expr;
|
||||||
|
PgBenchExprList *elist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%type <elist> elist
|
||||||
%type <expr> expr
|
%type <expr> expr
|
||||||
%type <ival> INTEGER
|
%type <ival> INTEGER function
|
||||||
%type <str> VARIABLE
|
%type <str> VARIABLE FUNCTION
|
||||||
|
|
||||||
%token INTEGER VARIABLE
|
%token INTEGER VARIABLE FUNCTION
|
||||||
%token CHAR_ERROR /* never used, will raise a syntax error */
|
%token CHAR_ERROR /* never used, will raise a syntax error */
|
||||||
|
|
||||||
/* Precedence: lowest to highest */
|
/* Precedence: lowest to highest */
|
||||||
@ -49,16 +54,25 @@ static PgBenchExpr *make_op(char operator, PgBenchExpr *lexpr,
|
|||||||
|
|
||||||
result: expr { expr_parse_result = $1; }
|
result: expr { expr_parse_result = $1; }
|
||||||
|
|
||||||
|
elist: { $$ = NULL; }
|
||||||
|
| expr { $$ = make_elist($1, NULL); }
|
||||||
|
| elist ',' expr { $$ = make_elist($3, $1); }
|
||||||
|
;
|
||||||
|
|
||||||
expr: '(' expr ')' { $$ = $2; }
|
expr: '(' expr ')' { $$ = $2; }
|
||||||
| '+' expr %prec UMINUS { $$ = $2; }
|
| '+' expr %prec UMINUS { $$ = $2; }
|
||||||
| '-' expr %prec UMINUS { $$ = make_op('-', make_integer_constant(0), $2); }
|
| '-' expr %prec UMINUS { $$ = make_op("-", make_integer_constant(0), $2); }
|
||||||
| expr '+' expr { $$ = make_op('+', $1, $3); }
|
| expr '+' expr { $$ = make_op("+", $1, $3); }
|
||||||
| expr '-' expr { $$ = make_op('-', $1, $3); }
|
| expr '-' expr { $$ = make_op("-", $1, $3); }
|
||||||
| expr '*' expr { $$ = make_op('*', $1, $3); }
|
| expr '*' expr { $$ = make_op("*", $1, $3); }
|
||||||
| expr '/' expr { $$ = make_op('/', $1, $3); }
|
| expr '/' expr { $$ = make_op("/", $1, $3); }
|
||||||
| expr '%' expr { $$ = make_op('%', $1, $3); }
|
| expr '%' expr { $$ = make_op("%", $1, $3); }
|
||||||
| INTEGER { $$ = make_integer_constant($1); }
|
| INTEGER { $$ = make_integer_constant($1); }
|
||||||
| VARIABLE { $$ = make_variable($1); }
|
| VARIABLE { $$ = make_variable($1); }
|
||||||
|
| function '(' elist ')'{ $$ = make_func($1, $3); }
|
||||||
|
;
|
||||||
|
|
||||||
|
function: FUNCTION { $$ = find_func($1); pg_free($1); }
|
||||||
;
|
;
|
||||||
|
|
||||||
%%
|
%%
|
||||||
@ -84,14 +98,131 @@ make_variable(char *varname)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PgBenchExpr *
|
static PgBenchExpr *
|
||||||
make_op(char operator, PgBenchExpr *lexpr, PgBenchExpr *rexpr)
|
make_op(const char *operator, PgBenchExpr *lexpr, PgBenchExpr *rexpr)
|
||||||
|
{
|
||||||
|
return make_func(find_func(operator),
|
||||||
|
make_elist(rexpr, make_elist(lexpr, NULL)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* List of available functions:
|
||||||
|
* - fname: function name
|
||||||
|
* - nargs: number of arguments
|
||||||
|
* -1 is a special value for min & max meaning #args >= 1
|
||||||
|
* - tag: function identifier from PgBenchFunction enum
|
||||||
|
*/
|
||||||
|
static struct
|
||||||
|
{
|
||||||
|
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 },
|
||||||
|
{ "%", 2, PGBENCH_MOD },
|
||||||
|
/* actual functions */
|
||||||
|
{ "abs", 1, PGBENCH_ABS },
|
||||||
|
{ "min", -1, PGBENCH_MIN },
|
||||||
|
{ "max", -1, PGBENCH_MAX },
|
||||||
|
{ "debug", 1, PGBENCH_DEBUG },
|
||||||
|
/* 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(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("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(const int fnumber, PgBenchExprList *args)
|
||||||
{
|
{
|
||||||
PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
|
PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
|
||||||
|
|
||||||
expr->etype = ENODE_OPERATOR;
|
Assert(fnumber >= 0);
|
||||||
expr->u.operator.operator = operator;
|
|
||||||
expr->u.operator.lexpr = lexpr;
|
if (PGBENCH_FUNCTIONS[fnumber].nargs >= 0 &&
|
||||||
expr->u.operator.rexpr = rexpr;
|
PGBENCH_FUNCTIONS[fnumber].nargs != elist_length(args))
|
||||||
|
expr_yyerror_more("unexpected number of arguments",
|
||||||
|
PGBENCH_FUNCTIONS[fnumber].fname);
|
||||||
|
|
||||||
|
/* check at least one arg for min & max */
|
||||||
|
if (PGBENCH_FUNCTIONS[fnumber].nargs == -1 &&
|
||||||
|
elist_length(args) == 0)
|
||||||
|
expr_yyerror_more("at least one argument expected",
|
||||||
|
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;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@ space [ \t\r\f]
|
|||||||
"%" { yycol += yyleng; return '%'; }
|
"%" { yycol += yyleng; return '%'; }
|
||||||
"(" { yycol += yyleng; return '('; }
|
"(" { yycol += yyleng; return '('; }
|
||||||
")" { yycol += yyleng; return ')'; }
|
")" { yycol += yyleng; return ')'; }
|
||||||
|
"," { yycol += yyleng; return ','; }
|
||||||
|
|
||||||
:[a-zA-Z0-9_]+ {
|
:[a-zA-Z0-9_]+ {
|
||||||
yycol += yyleng;
|
yycol += yyleng;
|
||||||
@ -57,8 +58,14 @@ space [ \t\r\f]
|
|||||||
yylval.ival = strtoint64(yytext);
|
yylval.ival = strtoint64(yytext);
|
||||||
return INTEGER;
|
return INTEGER;
|
||||||
}
|
}
|
||||||
|
[a-zA-Z0-9_]+ {
|
||||||
|
yycol += yyleng;
|
||||||
|
yylval.str = pg_strdup(yytext);
|
||||||
|
return FUNCTION;
|
||||||
|
}
|
||||||
|
|
||||||
[\n] { yycol = 0; yyline++; }
|
[\n] { yycol = 0; yyline++; }
|
||||||
|
|
||||||
{space}+ { yycol += yyleng; /* ignore */ }
|
{space}+ { yycol += yyleng; /* ignore */ }
|
||||||
|
|
||||||
. {
|
. {
|
||||||
@ -71,10 +78,16 @@ space [ \t\r\f]
|
|||||||
%%
|
%%
|
||||||
|
|
||||||
void
|
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,
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -94,6 +107,9 @@ expr_scanner_init(const char *str, const char *source,
|
|||||||
expr_command = (char *) cmd;
|
expr_command = (char *) cmd;
|
||||||
expr_col = (int) ecol;
|
expr_col = (int) ecol;
|
||||||
|
|
||||||
|
/* reset column count for this scan */
|
||||||
|
yycol = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Might be left over after error
|
* Might be left over after error
|
||||||
*/
|
*/
|
||||||
|
@ -372,6 +372,8 @@ static void doLog(TState *thread, CState *st, instr_time *now,
|
|||||||
StatsData *agg, bool skipped, double latency, double lag);
|
StatsData *agg, bool skipped, double latency, double lag);
|
||||||
|
|
||||||
|
|
||||||
|
static bool evaluateExpr(CState *, PgBenchExpr *, int64 *);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
usage(void)
|
usage(void)
|
||||||
{
|
{
|
||||||
@ -990,6 +992,150 @@ getQueryParams(CState *st, const Command *command, const char **params)
|
|||||||
params[i] = getVariable(st, command->argv[i + 1]);
|
params[i] = getVariable(st, command->argv[i + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* maximum number of function arguments */
|
||||||
|
#define MAX_FARGS 16
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Recursive evaluation of functions
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
evalFunc(CState *st,
|
||||||
|
PgBenchFunction func, PgBenchExprLink *args, int64 *retval)
|
||||||
|
{
|
||||||
|
/* evaluate all function arguments */
|
||||||
|
int nargs = 0;
|
||||||
|
int64 iargs[MAX_FARGS];
|
||||||
|
PgBenchExprLink *l = args;
|
||||||
|
|
||||||
|
for (nargs = 0; nargs < MAX_FARGS && l != NULL; nargs++, l = l->next)
|
||||||
|
if (!evaluateExpr(st, l->expr, &iargs[nargs]))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (l != NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"too many function arguments, maximum is %d\n", MAX_FARGS);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* then evaluate function */
|
||||||
|
switch (func)
|
||||||
|
{
|
||||||
|
case PGBENCH_ADD:
|
||||||
|
case PGBENCH_SUB:
|
||||||
|
case PGBENCH_MUL:
|
||||||
|
case PGBENCH_DIV:
|
||||||
|
case PGBENCH_MOD:
|
||||||
|
{
|
||||||
|
int64 lval = iargs[0],
|
||||||
|
rval = iargs[1];
|
||||||
|
|
||||||
|
Assert(nargs == 2);
|
||||||
|
|
||||||
|
switch (func)
|
||||||
|
{
|
||||||
|
case PGBENCH_ADD:
|
||||||
|
*retval = lval + rval;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case PGBENCH_SUB:
|
||||||
|
*retval = lval - rval;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case PGBENCH_MUL:
|
||||||
|
*retval = lval * rval;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case PGBENCH_DIV:
|
||||||
|
case PGBENCH_MOD:
|
||||||
|
if (rval == 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "division by zero\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* special handling of -1 divisor */
|
||||||
|
if (rval == -1)
|
||||||
|
{
|
||||||
|
if (func == PGBENCH_DIV)
|
||||||
|
{
|
||||||
|
/* overflow check (needed for INT64_MIN) */
|
||||||
|
if (lval == PG_INT64_MIN)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "bigint out of range\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*retval = -lval;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*retval = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/* divisor is not -1 */
|
||||||
|
if (func == PGBENCH_DIV)
|
||||||
|
*retval = lval / rval;
|
||||||
|
else /* func == PGBENCH_MOD */
|
||||||
|
*retval = lval % rval;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* cannot get here */
|
||||||
|
Assert(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case PGBENCH_ABS:
|
||||||
|
{
|
||||||
|
Assert(nargs == 1);
|
||||||
|
|
||||||
|
if (iargs[0] < 0)
|
||||||
|
*retval = -iargs[0];
|
||||||
|
else
|
||||||
|
*retval = iargs[0];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PGBENCH_DEBUG:
|
||||||
|
{
|
||||||
|
Assert(nargs == 1);
|
||||||
|
|
||||||
|
fprintf(stderr, "debug(script=%d,command=%d): " INT64_FORMAT "\n",
|
||||||
|
st->use_file, st->state + 1, iargs[0]);
|
||||||
|
|
||||||
|
*retval = iargs[0];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PGBENCH_MIN:
|
||||||
|
case PGBENCH_MAX:
|
||||||
|
{
|
||||||
|
int64 extremum = iargs[0];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
Assert(nargs >= 1);
|
||||||
|
|
||||||
|
for (i = 1; i < nargs; i++)
|
||||||
|
{
|
||||||
|
int64 ival = iargs[i];
|
||||||
|
|
||||||
|
if (func == PGBENCH_MIN)
|
||||||
|
extremum = extremum < ival ? extremum : ival;
|
||||||
|
else if (func == PGBENCH_MAX)
|
||||||
|
extremum = extremum > ival ? extremum : ival;
|
||||||
|
}
|
||||||
|
|
||||||
|
*retval = extremum;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "unexpected function tag: %d\n", func);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Recursive evaluation of an expression in a pgbench script
|
* Recursive evaluation of an expression in a pgbench script
|
||||||
* using the current state of variables.
|
* using the current state of variables.
|
||||||
@ -1021,86 +1167,16 @@ evaluateExpr(CState *st, PgBenchExpr *expr, int64 *retval)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case ENODE_OPERATOR:
|
case ENODE_FUNCTION:
|
||||||
{
|
return evalFunc(st,
|
||||||
int64 lval;
|
expr->u.function.function,
|
||||||
int64 rval;
|
expr->u.function.args,
|
||||||
|
retval);
|
||||||
if (!evaluateExpr(st, expr->u.operator.lexpr, &lval))
|
|
||||||
return false;
|
|
||||||
if (!evaluateExpr(st, expr->u.operator.rexpr, &rval))
|
|
||||||
return false;
|
|
||||||
switch (expr->u.operator.operator)
|
|
||||||
{
|
|
||||||
case '+':
|
|
||||||
*retval = lval + rval;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case '-':
|
|
||||||
*retval = lval - rval;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case '*':
|
|
||||||
*retval = lval * rval;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case '/':
|
|
||||||
if (rval == 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "division by zero\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* INT64_MIN / -1 is problematic, since the result
|
|
||||||
* can't be represented on a two's-complement machine.
|
|
||||||
* Some machines produce INT64_MIN, some produce zero,
|
|
||||||
* some throw an exception. We can dodge the problem
|
|
||||||
* by recognizing that division by -1 is the same as
|
|
||||||
* negation.
|
|
||||||
*/
|
|
||||||
if (rval == -1)
|
|
||||||
{
|
|
||||||
*retval = -lval;
|
|
||||||
|
|
||||||
/* overflow check (needed for INT64_MIN) */
|
|
||||||
if (lval == PG_INT64_MIN)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "bigint out of range\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
*retval = lval / rval;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case '%':
|
|
||||||
if (rval == 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "division by zero\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Some machines throw a floating-point exception for
|
|
||||||
* INT64_MIN % -1. Dodge that problem by noting that
|
|
||||||
* any value modulo -1 is 0.
|
|
||||||
*/
|
|
||||||
if (rval == -1)
|
|
||||||
*retval = 0;
|
|
||||||
else
|
|
||||||
*retval = lval % rval;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "bad operator\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
fprintf(stderr, "unexpected enode type in evaluation: %d\n",
|
||||||
|
expr->etype);
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "bad expression\n");
|
fprintf(stderr, "bad expression\n");
|
||||||
@ -1710,6 +1786,7 @@ top:
|
|||||||
st->ecnt++;
|
st->ecnt++;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
sprintf(res, INT64_FORMAT, result);
|
sprintf(res, INT64_FORMAT, result);
|
||||||
|
|
||||||
if (!putVariable(st, argv[0], argv[1], res))
|
if (!putVariable(st, argv[0], argv[1], res))
|
||||||
|
@ -11,14 +11,31 @@
|
|||||||
#ifndef PGBENCH_H
|
#ifndef PGBENCH_H
|
||||||
#define PGBENCH_H
|
#define PGBENCH_H
|
||||||
|
|
||||||
|
/* Types of expression nodes */
|
||||||
typedef enum PgBenchExprType
|
typedef enum PgBenchExprType
|
||||||
{
|
{
|
||||||
ENODE_INTEGER_CONSTANT,
|
ENODE_INTEGER_CONSTANT,
|
||||||
ENODE_VARIABLE,
|
ENODE_VARIABLE,
|
||||||
ENODE_OPERATOR
|
ENODE_FUNCTION
|
||||||
} PgBenchExprType;
|
} PgBenchExprType;
|
||||||
|
|
||||||
|
/* List of operators and callable functions */
|
||||||
|
typedef enum PgBenchFunction
|
||||||
|
{
|
||||||
|
PGBENCH_ADD,
|
||||||
|
PGBENCH_SUB,
|
||||||
|
PGBENCH_MUL,
|
||||||
|
PGBENCH_DIV,
|
||||||
|
PGBENCH_MOD,
|
||||||
|
PGBENCH_DEBUG,
|
||||||
|
PGBENCH_ABS,
|
||||||
|
PGBENCH_MIN,
|
||||||
|
PGBENCH_MAX,
|
||||||
|
} PgBenchFunction;
|
||||||
|
|
||||||
typedef struct PgBenchExpr PgBenchExpr;
|
typedef struct PgBenchExpr PgBenchExpr;
|
||||||
|
typedef struct PgBenchExprLink PgBenchExprLink;
|
||||||
|
typedef struct PgBenchExprList PgBenchExprList;
|
||||||
|
|
||||||
struct PgBenchExpr
|
struct PgBenchExpr
|
||||||
{
|
{
|
||||||
@ -35,18 +52,31 @@ struct PgBenchExpr
|
|||||||
} variable;
|
} variable;
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
char operator;
|
PgBenchFunction function;
|
||||||
PgBenchExpr *lexpr;
|
PgBenchExprLink *args;
|
||||||
PgBenchExpr *rexpr;
|
} function;
|
||||||
} operator;
|
|
||||||
} u;
|
} u;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* List of expression nodes */
|
||||||
|
struct PgBenchExprLink
|
||||||
|
{
|
||||||
|
PgBenchExpr *expr;
|
||||||
|
PgBenchExprLink *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PgBenchExprList
|
||||||
|
{
|
||||||
|
PgBenchExprLink *head;
|
||||||
|
PgBenchExprLink *tail;
|
||||||
|
};
|
||||||
|
|
||||||
extern PgBenchExpr *expr_parse_result;
|
extern PgBenchExpr *expr_parse_result;
|
||||||
|
|
||||||
extern int expr_yyparse(void);
|
extern int expr_yyparse(void);
|
||||||
extern int expr_yylex(void);
|
extern int expr_yylex(void);
|
||||||
extern void expr_yyerror(const char *str);
|
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,
|
extern void expr_scanner_init(const char *str, const char *source,
|
||||||
const int lineno, const char *line,
|
const int lineno, const char *line,
|
||||||
const char *cmd, const int ecol);
|
const char *cmd, const int ecol);
|
||||||
|
Reference in New Issue
Block a user