diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml
index ade1b53..9d5eb32 100644
--- a/doc/src/sgml/ref/pgbench.sgml
+++ b/doc/src/sgml/ref/pgbench.sgml
@@ -798,8 +798,11 @@ pgbench options dbname
The expression may contain integer constants such as 5432,
references to variables :variablename,
and expressions composed of unary (-) or binary operators
- (+, -, *, /, %)
- with their usual associativity, and parentheses.
+ (+, -, *, /,
+ %) with their usual associativity, function calls and
+ parentheses.
+ shows the available
+ functions.
@@ -965,6 +968,52 @@ f(x) = exp(-parameter * (x - min) / (max - min + 1)) / (1.0 - exp(-parameter))
+
+
+ PgBench Functions
+
+
+
+ Function
+ Return Type
+ Description
+ Example
+ Result
+
+
+
+
+ abs(a)
+ same as a
+ integer value
+ abs(-17)
+ 17
+
+
+ debug(a)
+ same asa
+ print to stderr the given argument
+ debug(5432)
+ 5432
+
+
+ max(i [, ... ] )
+ integer
+ maximum value
+ max(5, 4, 3, 2)
+ 5
+
+
+ min(i [, ... ] )
+ integer
+ minimum value
+ min(5, 4, 3, 2)
+ 2
+
+
+
+
+
As an example, the full definition of the built-in TPC-B-like
transaction is:
diff --git a/src/bin/pgbench/exprparse.y b/src/bin/pgbench/exprparse.y
index 06ee04b..93c6173 100644
--- a/src/bin/pgbench/exprparse.y
+++ b/src/bin/pgbench/exprparse.y
@@ -16,10 +16,13 @@
PgBenchExpr *expr_parse_result;
+static PgBenchExprList *make_elist(PgBenchExpr *exp, PgBenchExprList *list);
static PgBenchExpr *make_integer_constant(int64 ival);
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);
+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;
char *str;
PgBenchExpr *expr;
+ PgBenchExprList *elist;
}
+%type elist
%type expr
-%type INTEGER
-%type VARIABLE
+%type INTEGER function
+%type VARIABLE FUNCTION
-%token INTEGER VARIABLE
+%token INTEGER VARIABLE FUNCTION
%token CHAR_ERROR /* never used, will raise a syntax error */
/* Precedence: lowest to highest */
@@ -49,16 +54,25 @@ static PgBenchExpr *make_op(char operator, PgBenchExpr *lexpr,
result: expr { expr_parse_result = $1; }
+elist: { $$ = NULL; }
+ | expr { $$ = make_elist($1, NULL); }
+ | elist ',' expr { $$ = make_elist($3, $1); }
+ ;
+
expr: '(' expr ')' { $$ = $2; }
| '+' expr %prec UMINUS { $$ = $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 %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); }
| INTEGER { $$ = make_integer_constant($1); }
| VARIABLE { $$ = make_variable($1); }
+ | function '(' elist ')'{ $$ = make_func($1, $3); }
+ ;
+
+function: FUNCTION { $$ = find_func($1); pg_free($1); }
;
%%
@@ -68,8 +82,9 @@ make_integer_constant(int64 ival)
{
PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
- expr->etype = ENODE_INTEGER_CONSTANT;
- expr->u.integer_constant.ival = ival;
+ expr->etype = ENODE_CONSTANT;
+ expr->u.constant.type = PGBT_INT;
+ expr->u.constant.u.ival = ival;
return expr;
}
@@ -84,14 +99,128 @@ make_variable(char *varname)
}
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));
- expr->etype = ENODE_OPERATOR;
- expr->u.operator.operator = operator;
- expr->u.operator.lexpr = lexpr;
- expr->u.operator.rexpr = rexpr;
+ Assert(fnumber >= 0);
+
+ if ((PGBENCH_FUNCTIONS[fnumber].nargs >= 0 &&
+ PGBENCH_FUNCTIONS[fnumber].nargs != elist_length(args)) ||
+ /* check at least one arg for min & max */
+ (PGBENCH_FUNCTIONS[fnumber].nargs == -1 &&
+ elist_length(args) == 0))
+ expr_yyerror_more("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;
}
diff --git a/src/bin/pgbench/exprscan.l b/src/bin/pgbench/exprscan.l
index f1c4c7e..7e851f0 100644
--- a/src/bin/pgbench/exprscan.l
+++ b/src/bin/pgbench/exprscan.l
@@ -46,6 +46,7 @@ space [ \t\r\f]
"%" { yycol += yyleng; return '%'; }
"(" { yycol += yyleng; return '('; }
")" { yycol += yyleng; return ')'; }
+"," { yycol += yyleng; return ','; }
:[a-zA-Z0-9_]+ {
yycol += yyleng;
@@ -57,8 +58,14 @@ space [ \t\r\f]
yylval.ival = strtoint64(yytext);
return INTEGER;
}
+[a-zA-Z0-9_]+ {
+ yycol += yyleng;
+ yylval.str = pg_strdup(yytext);
+ return FUNCTION;
+ }
+
+[\n] { yycol = 0; yyline++; /* never occurs, input on one line */ }
-[\n] { yycol = 0; yyline++; }
{space}+ { yycol += yyleng; /* ignore */ }
. {
@@ -71,10 +78,16 @@ space [ \t\r\f]
%%
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,
- 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_col = (int) ecol;
+ /* reset column count for this scan */
+ yycol = 0;
+
/*
* Might be left over after error
*/
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 42a4e6b..be70804 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -321,13 +321,10 @@ static struct
{
"tpcb-like",
"",
- "\\set nbranches " CppAsString2(nbranches) " * :scale\n"
- "\\set ntellers " CppAsString2(ntellers) " * :scale\n"
- "\\set naccounts " CppAsString2(naccounts) " * :scale\n"
- "\\setrandom aid 1 :naccounts\n"
- "\\setrandom bid 1 :nbranches\n"
- "\\setrandom tid 1 :ntellers\n"
- "\\setrandom delta -5000 5000\n"
+ "\\set aid random(1, " CppAsString2(naccounts) " * :scale)\n"
+ "\\set bid random(1, " CppAsString2(nbranches) " * :scale)\n"
+ "\\set tid random(1, " CppAsString2(ntellers) " * :scale)\n"
+ "\\set delta random(-5000, 5000)\n"
"BEGIN;\n"
"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
@@ -339,13 +336,10 @@ static struct
{
"simple-update",
"",
- "\\set nbranches " CppAsString2(nbranches) " * :scale\n"
- "\\set ntellers " CppAsString2(ntellers) " * :scale\n"
- "\\set naccounts " CppAsString2(naccounts) " * :scale\n"
- "\\setrandom aid 1 :naccounts\n"
- "\\setrandom bid 1 :nbranches\n"
- "\\setrandom tid 1 :ntellers\n"
- "\\setrandom delta -5000 5000\n"
+ "\\set aid random(1, " CppAsString2(naccounts) " * :scale)\n"
+ "\\set bid random(1, " CppAsString2(nbranches) " * :scale)\n"
+ "\\set tid random(1, " CppAsString2(ntellers) " * :scale)\n"
+ "\\set delta random(-5000, 5000)\n"
"BEGIN;\n"
"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
@@ -355,13 +349,11 @@ static struct
{
"select-only",
"",
- "\\set naccounts " CppAsString2(naccounts) " * :scale\n"
- "\\setrandom aid 1 :naccounts\n"
+ "\\set aid random(1, " CppAsString2(naccounts) " * :scale)\n"
"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
}
};
-
/* Function prototypes */
static void setalarm(int seconds);
static void *threadRun(void *arg);
@@ -372,6 +364,8 @@ static void doLog(TState *thread, CState *st, instr_time *now,
StatsData *agg, bool skipped, double latency, double lag);
+static bool evaluateExpr(TState *, CState *, PgBenchExpr *, PgBenchValue *);
+
static void
usage(void)
{
@@ -526,6 +520,7 @@ getExponentialRand(TState *thread, int64 min, int64 max, double parameter)
uniform,
rand;
+ /* abort if wrong parameter, but must really be checked beforehand */
Assert(parameter > 0.0);
cut = exp(-parameter);
/* erand in [0, 1), uniform in (0, 1] */
@@ -547,6 +542,9 @@ getGaussianRand(TState *thread, int64 min, int64 max, double parameter)
double stdev;
double rand;
+ /* abort if parameter is too low, but must really be checked beforehand */
+ Assert(parameter >= MIN_GAUSSIAN_PARAM);
+
/*
* Get user specified random number from this loop, with -parameter <
* stdev <= parameter
@@ -991,22 +989,189 @@ getQueryParams(CState *st, const Command *command, const char **params)
}
/*
- * Recursive evaluation of an expression in a pgbench script
- * using the current state of variables.
- * Returns whether the evaluation was ok,
- * the value itself is returned through the retval pointer.
+ * Recursive evaluation of int or double expressions
+ *
+ * Note that currently only integer variables are available, with values
+ * stored as text.
*/
+
+static int64
+coerceToInt(PgBenchValue *pval)
+{
+ if (pval->type == PGBT_INT)
+ return pval->u.ival;
+ /* stop on internal error */
+ fprintf(stderr, "unexpected value type %d\n", pval->type);
+ exit(1);
+ return 0;
+}
+
+static void
+setIntValue(PgBenchValue *pv, int64 ival)
+{
+ pv->type = PGBT_INT;
+ pv->u.ival = ival;
+}
+
static bool
-evaluateExpr(CState *st, PgBenchExpr *expr, int64 *retval)
+evalFunc(TState *thread, CState *st,
+ PgBenchFunction func, PgBenchExprLink *args, PgBenchValue *retval)
+{
+ switch (func)
+ {
+ case PGBENCH_ADD:
+ case PGBENCH_SUB:
+ case PGBENCH_MUL:
+ case PGBENCH_DIV:
+ case PGBENCH_MOD:
+ {
+ PgBenchValue lval, rval;
+
+ if (!args || !args->next || args->next->next)
+ /* two arguments only */
+ return false;
+
+ if (!evaluateExpr(thread, st, args->expr, &lval))
+ return false;
+
+ if (!evaluateExpr(thread, st, args->next->expr, &rval))
+ return false;
+
+ if (lval.type == PGBT_INT && rval.type == PGBT_INT)
+ {
+ switch (func)
+ {
+ case PGBENCH_ADD:
+ setIntValue(retval, coerceToInt(&lval) + coerceToInt(&rval));
+ return true;
+
+ case PGBENCH_SUB:
+ setIntValue(retval, coerceToInt(&lval) - coerceToInt(&rval));
+ return true;
+
+ case PGBENCH_MUL:
+ setIntValue(retval, coerceToInt(&lval) * coerceToInt(&rval));
+ return true;
+
+ case PGBENCH_DIV:
+ case PGBENCH_MOD:
+ if (coerceToInt(&rval) == 0)
+ {
+ fprintf(stderr, "division by zero\n");
+ return false;
+ }
+ if (coerceToInt(&rval) == -1)
+ {
+ setIntValue(retval, 0);
+ return true;
+ }
+ if (func == PGBENCH_DIV)
+ setIntValue(retval, coerceToInt(&lval) / coerceToInt(&rval));
+ else
+ setIntValue(retval, coerceToInt(&lval) % coerceToInt(&rval));
+ return true;
+
+ default:
+ /* cannot get here */
+ Assert(0);
+ }
+ }
+ else
+ {
+ /* cannot get here */
+ Assert(0);
+ }
+ }
+
+ case PGBENCH_ABS:
+ {
+ PgBenchValue arg;
+
+ if (!evaluateExpr(thread, st, args->expr, &arg))
+ return false;
+
+ if (arg.type == PGBT_INT)
+ {
+ int64 i = coerceToInt(&arg);
+ setIntValue(retval, i < 0? -i: i);
+ }
+
+ return true;
+ }
+
+ case PGBENCH_DEBUG:
+ {
+ if (!evaluateExpr(thread, st, args->expr, retval))
+ return false;
+
+ fprintf(stderr, "debug(script=%d,command=%d): ",
+ st->use_file, st->state+1);
+
+ if (retval->type == PGBT_INT)
+ fprintf(stderr, "int " INT64_FORMAT "\n", retval->u.ival);
+ else
+ {
+ /* internal error */
+ fprintf(stderr, "unexpected value type %d\n", retval->type);
+ return false;
+ }
+
+ return true;
+ }
+
+ case PGBENCH_MIN:
+ case PGBENCH_MAX:
+ {
+ int64 val = -1;
+ bool first = true;
+ while (args != NULL)
+ {
+ PgBenchValue arg;
+
+ if (!evaluateExpr(thread, st, args->expr, &arg))
+ return false;
+
+ if (first)
+ val = coerceToInt(&arg);
+ else
+ {
+ int64 i = coerceToInt(&arg);
+ if (func == PGBENCH_MIN)
+ val = val < i? val: i;
+ else if (func == PGBENCH_MAX)
+ val = val > i? val: i;
+ }
+
+ args = args->next;
+ first = false;
+ }
+
+ setIntValue(retval, val);
+ return true;
+ }
+
+ default:
+ fprintf(stderr, "unexpected function tag: %d\n", func);
+ exit(1);
+ }
+}
+
+/*
+ * Recursive evaluation of an expression in a pgbench script using the current
+ * state of variables.
+ * Returns whether the evaluation was ok, the value itself is returned through
+ * the retval pointer.
+ */
+static bool
+evaluateExpr(TState *thread, CState *st, PgBenchExpr *expr, PgBenchValue *retval)
{
switch (expr->etype)
{
- case ENODE_INTEGER_CONSTANT:
+ case ENODE_CONSTANT:
{
- *retval = expr->u.integer_constant.ival;
+ *retval = expr->u.constant;
return true;
}
-
case ENODE_VARIABLE:
{
char *var;
@@ -1017,90 +1182,22 @@ evaluateExpr(CState *st, PgBenchExpr *expr, int64 *retval)
expr->u.variable.varname);
return false;
}
- *retval = strtoint64(var);
+
+ setIntValue(retval, strtoint64(var));
+
return true;
}
- case ENODE_OPERATOR:
- {
- int64 lval;
- int64 rval;
-
- 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;
- }
+ case ENODE_FUNCTION:
+ return evalFunc(thread, st,
+ expr->u.function.function,
+ expr->u.function.args,
+ retval);
default:
- break;
+ fprintf(stderr, "unexpected enode type in evaluation: %d\n",
+ expr->etype);
+ exit(1);
}
fprintf(stderr, "bad expression\n");
@@ -1562,6 +1659,10 @@ top:
fprintf(stderr, "\n");
}
+ /*
+ * Note: this section could be removed, as the same functionnality
+ * is available through \set xxx random_gaussian(...)
+ */
if (pg_strcasecmp(argv[0], "setrandom") == 0)
{
char *var;
@@ -1702,15 +1803,20 @@ top:
else if (pg_strcasecmp(argv[0], "set") == 0)
{
char res[64];
- PgBenchExpr *expr = commands[st->state]->expr;
- int64 result;
+ PgBenchExpr *expr = commands[st->state]->expr;
+ PgBenchValue result;
- if (!evaluateExpr(st, expr, &result))
+ if (!evaluateExpr(thread, st, expr, &result))
{
st->ecnt++;
return true;
}
- sprintf(res, INT64_FORMAT, result);
+
+ if (result.type == PGBT_INT)
+ sprintf(res, INT64_FORMAT, coerceToInt(&result));
+ else
+ /* cannot happend if evaluateExpr above returned ok */
+ Assert(0);
if (!putVariable(st, argv[0], argv[1], res))
{
diff --git a/src/bin/pgbench/pgbench.h b/src/bin/pgbench/pgbench.h
index 5bb2480..1f7251a 100644
--- a/src/bin/pgbench/pgbench.h
+++ b/src/bin/pgbench/pgbench.h
@@ -11,42 +11,95 @@
#ifndef PGBENCH_H
#define PGBENCH_H
+/*
+ * Variable types used in parser.
+ */
+typedef enum
+{
+ PGBT_INT
+ // add other types here
+} PgBenchValueType;
+
+typedef struct
+{
+ PgBenchValueType type;
+ union
+ {
+ int64 ival;
+ // add other types here
+ } u;
+} PgBenchValue;
+
+/* Types of expression nodes */
typedef enum PgBenchExprType
{
- ENODE_INTEGER_CONSTANT,
+ ENODE_CONSTANT,
ENODE_VARIABLE,
- ENODE_OPERATOR
+ ENODE_FUNCTION
} 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 PgBenchExprLink PgBenchExprLink;
+typedef struct PgBenchExprList PgBenchExprList;
+/*
+ * Basic representation of an expression parsed. This can be used as
+ * different things by the parser as defined by PgBenchExprType:
+ * - ENODE_CONSTANT, constant integer or double value
+ * - ENODE_VARIABLE, variable result of \set or \setrandom
+ * - ENODE_FUNCTION, in-core functions and operators
+ */
struct PgBenchExpr
{
PgBenchExprType etype;
union
{
- struct
- {
- int64 ival;
- } integer_constant;
+ PgBenchValue constant;
struct
{
char *varname;
} variable;
struct
{
- char operator;
- PgBenchExpr *lexpr;
- PgBenchExpr *rexpr;
- } operator;
+ PgBenchFunction function;
+ PgBenchExprLink *args;
+ } function;
} u;
};
+/* List of expression nodes */
+struct PgBenchExprLink
+{
+ PgBenchExpr *expr;
+ PgBenchExprLink *next;
+};
+
+struct PgBenchExprList
+{
+ PgBenchExprLink *head;
+ PgBenchExprLink *tail;
+};
+
extern PgBenchExpr *expr_parse_result;
extern int expr_yyparse(void);
extern int expr_yylex(void);
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,
const int lineno, const char *line,
const char *cmd, const int ecol);