Hello Heikki,
As soon as we add more functions, the way they are documented needs to be
reworked too; we'll need to add a table in the manual to list them.
Here is a v8 with "abs", "min", "max", "random", "gaussian" et
"exponential".
[...]
There is no real doc, WIP...
Here is a v9 with a doc. The implicit typing of expressions is improved.
I also added two debug functions which allow to show intermediate integer
(idebug) or double (ddebug).
\set i idebug(random(1, 10))
will print the random value and assign it to i.
I updated the defaut scripts, which seems to speed up meta command
evaluations. The initial version does less than 2 million evals per
second:
sh> cat before.sql
\set naccounts 100000 * :scale
\setrandom aid 1 :naccounts
sh> ./pgbench -T 3 -P 1 -f before.sql
[...]
tps = 1899004.793098 (excluding connections establishing)
The updated version does more than 3 million evals per second:
sh> cat after.sql
\set aid random(1, 100000 * :scale)
sh> ./pgbench -T 3 -P 1 -f after.sql
[...]
tps = 3143168.813690 (excluding connections establishing)
Suggestion:
A possibility would be to remove altogether the \setrandom stuff as the
functionality is available through \set, maybe with an error message to
advise about using \set with one of the random functions. That would
remove about 200 ugly locs...
Another option would be to translate the setrandom stuff into a \set
expression, that would maybe save 100 locs for the eval, but keep and
expand a little the "manual" parser part.
--
Fabien.
diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml
index 2517a3a..73b7caf 100644
--- a/doc/src/sgml/ref/pgbench.sgml
+++ b/doc/src/sgml/ref/pgbench.sgml
@@ -758,17 +758,20 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
Sets variable <replaceable>varname</> to an integer value calculated
from <replaceable>expression</>.
The expression may contain integer constants such as <literal>5432</>,
- references to variables <literal>:</><replaceable>variablename</>,
+ double constants such as <literal>3.14156</>,
+ references to integer variables <literal>:</><replaceable>variablename</>,
and expressions composed of unary (<literal>-</>) or binary operators
(<literal>+</>, <literal>-</>, <literal>*</>, <literal>/</>, <literal>%</>)
- with their usual associativity, and parentheses.
+ with their usual associativity, integer function calls and parentheses.
+ <xref linkend="functions-pgbench-func-table"> shows the available
+ functions.
</para>
<para>
Examples:
<programlisting>
\set ntellers 10 * :scale
-\set aid (1021 * :aid) % (100000 * :scale) + 1
+\set aid (1021 * random(1, 100000 * :scale)) % (100000 * :scale) + 1
</programlisting></para>
</listitem>
</varlistentry>
@@ -918,18 +921,110 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
</varlistentry>
</variablelist>
+ <!-- list pgbench functions in alphabetical order -->
+ <table id="functions-pgbench-func-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 or double absolute value</>
+ <entry><literal>abs(-17)</></>
+ <entry><literal>17</></>
+ </row>
+ <row>
+ <entry><literal><function>ddebug(<replaceable>x</>)</></></>
+ <entry>double</>
+ <entry>stderr print for debug and return argument</>
+ <entry><literal>ddebug(5432.1)</></>
+ <entry><literal>5432.1</></>
+ </row>
+ <row>
+ <entry><literal><function>double(<replaceable>i</>)</></></>
+ <entry>double</>
+ <entry>evaluate as int and cast to double</>
+ <entry><literal>double(5432)</></>
+ <entry><literal>5432.0</></>
+ </row>
+ <row>
+ <entry><literal><function>exporand(<replaceable>i</>, <replaceable>j</>, <replaceable>t</>)</></></>
+ <entry>integer</>
+ <entry>exponentially distributed random integer in the bounds, see below</>
+ <entry><literal>exporand(1, 10, 3.0)</></>
+ <entry>int between <literal>1</> and <literal>10</></>
+ </row>
+ <row>
+ <entry><literal><function>idebug(<replaceable>i</>)</></></>
+ <entry>integer</>
+ <entry>stderr print for debug and return argument</>
+ <entry><literal>idebug(5432)</></>
+ <entry><literal>5432</></>
+ </row>
+ <row>
+ <entry><literal><function>int(<replaceable>x</>)</></></>
+ <entry>integer</>
+ <entry>evaluate as double and cast to int</>
+ <entry><literal>int(5.4 + 3.8)</></>
+ <entry><literal>9</></>
+ </row>
+ <row>
+ <entry><literal><function>gaussrand(<replaceable>i</>, <replaceable>j</>, <replaceable>t</>)</></></>
+ <entry>integer</>
+ <entry>gaussian distributed random integer in the bounds, see below</>
+ <entry><literal>gaussrand(1, 10, 2.5)</></>
+ <entry>int between <literal>1</> and <literal>10</></>
+ </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>
+ <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>random(<replaceable>i</>, <replaceable>j</>)</></></>
+ <entry>integer</>
+ <entry>uniformly distributed random integer in the bounds</>
+ <entry><literal>random(1, 10)</></>
+ <entry>int between <literal>1</> and <literal>10</></>
+ </row>
+ <row>
+ <entry><literal><function>sqrt(<replaceable>x</>)</></></>
+ <entry>double</>
+ <entry>square root</>
+ <entry><literal>sqrt(2.0)</></>
+ <entry><literal>1.414213562</></>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
<para>
As an example, the full definition of the built-in TPC-B-like
transaction is:
<programlisting>
-\set nbranches :scale
-\set ntellers 10 * :scale
-\set naccounts 100000 * :scale
-\setrandom aid 1 :naccounts
-\setrandom bid 1 :nbranches
-\setrandom tid 1 :ntellers
-\setrandom delta -5000 5000
+\set aid random(1, 100000 * :scale)
+\set bid random(1, 1 * :scale)
+\set tid random(1, 10 * :scale)
+\set delta random(-5000, 5000)
BEGIN;
UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
diff --git a/src/bin/pgbench/exprparse.y b/src/bin/pgbench/exprparse.y
index e68631e..97bb559 100644
--- a/src/bin/pgbench/exprparse.y
+++ b/src/bin/pgbench/exprparse.y
@@ -16,10 +16,14 @@
PgBenchExpr *expr_parse_result;
+static PgBenchExprList *make_elist(PgBenchExpr *exp, PgBenchExprList *list);
static PgBenchExpr *make_integer_constant(int64 ival);
+static PgBenchExpr *make_double_constant(double dval);
static PgBenchExpr *make_variable(char *varname);
static PgBenchExpr *make_op(char operator, PgBenchExpr *lexpr,
PgBenchExpr *rexpr);
+static int find_func(const char * fname);
+static PgBenchExpr *make_func(const int fnumber, PgBenchExprList *args);
%}
@@ -29,15 +33,19 @@ static PgBenchExpr *make_op(char operator, PgBenchExpr *lexpr,
%union
{
int64 ival;
+ double dval;
char *str;
PgBenchExpr *expr;
+ PgBenchExprList *elist;
}
+%type <elist> elist
%type <expr> expr
-%type <ival> INTEGER
-%type <str> VARIABLE
+%type <ival> INTEGER function
+%type <dval> DOUBLE
+%type <str> VARIABLE FUNCTION
-%token INTEGER VARIABLE
+%token INTEGER DOUBLE VARIABLE FUNCTION
%token CHAR_ERROR /* never used, will raise a syntax error */
/* Precedence: lowest to highest */
@@ -49,6 +57,10 @@ static PgBenchExpr *make_op(char operator, PgBenchExpr *lexpr,
result: expr { expr_parse_result = $1; }
+elist: 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); }
@@ -58,7 +70,12 @@ expr: '(' expr ')' { $$ = $2; }
| expr '/' expr { $$ = make_op('/', $1, $3); }
| expr '%' expr { $$ = make_op('%', $1, $3); }
| INTEGER { $$ = make_integer_constant($1); }
+ | DOUBLE { $$ = make_double_constant($1); }
| VARIABLE { $$ = make_variable($1); }
+ | function '(' elist ')'{ $$ = make_func($1, $3); }
+ ;
+
+function: FUNCTION { $$ = find_func($1); pg_free($1); }
;
%%
@@ -74,6 +91,16 @@ make_integer_constant(int64 ival)
}
static PgBenchExpr *
+make_double_constant(double dval)
+{
+ PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
+
+ expr->etype = ENODE_DOUBLE_CONSTANT;
+ expr->u.double_constant.dval = dval;
+ return expr;
+}
+
+static PgBenchExpr *
make_variable(char *varname)
{
PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
@@ -95,4 +122,93 @@ make_op(char operator, PgBenchExpr *lexpr, PgBenchExpr *rexpr)
return expr;
}
+static struct {
+ char * fname;
+ int nargs;
+ PgBenchFunction tag;
+} PGBENCH_FUNCTIONS[] = {
+ { "abs", 1, PGBENCH_ABS },
+ { "sqrt", 1, PGBENCH_SQRT },
+ { "int", 1, PGBENCH_INT },
+ { "double", 1, PGBENCH_DOUBLE },
+ { "min", -1, PGBENCH_MIN },
+ { "max", -1, PGBENCH_MAX },
+ { "random", 2, PGBENCH_RANDOM },
+ { "gaussrand", 3, PGBENCH_GAUSSRAND },
+ { "exporand", 3, PGBENCH_EXPORAND },
+ { "idebug", 1, PGBENCH_IDEBUG },
+ { "ddebug", 1, PGBENCH_DDEBUG }
+};
+
+static int
+find_func(const char * fname)
+{
+ int nfunctions = sizeof(PGBENCH_FUNCTIONS) / sizeof(PGBENCH_FUNCTIONS[0]);
+ int i;
+
+ for (i = 0; i < nfunctions; i++)
+ if (pg_strcasecmp(fname, PGBENCH_FUNCTIONS[i].fname) == 0)
+ return i;
+
+ expr_yyerror_more("unexpected function name", fname);
+
+ /* not reached */
+ return -1;
+}
+
+static PgBenchExprList *
+make_elist(PgBenchExpr *expr, PgBenchExprList *list)
+{
+ PgBenchExprList *cons = pg_malloc(sizeof(PgBenchExprList));
+ cons->expr = expr;
+ cons->next = list;
+ return cons;
+}
+
+static PgBenchExprList *
+reverse_elist(PgBenchExprList *list)
+{
+ PgBenchExprList *cur = list, *prec = NULL, *next = NULL;
+
+ while (cur != NULL)
+ {
+ next = cur->next;
+ cur->next = prec;
+ prec = cur;
+ cur = next;
+ }
+
+ return prec;
+}
+
+static int
+elist_length(PgBenchExprList *list)
+{
+ int len = 0;
+
+ for (; list != NULL; list = list->next)
+ len++;
+
+ return len;
+}
+
+static PgBenchExpr *
+make_func(const int fnumber, PgBenchExprList *args)
+{
+ PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
+
+ Assert(fnumber >= 0);
+
+ if (PGBENCH_FUNCTIONS[fnumber].nargs != -1 &&
+ PGBENCH_FUNCTIONS[fnumber].nargs != elist_length(args))
+ expr_yyerror_more("unexpected number of arguments",
+ PGBENCH_FUNCTIONS[fnumber].fname);
+
+ expr->etype = ENODE_FUNCTION;
+ expr->u.function.function = PGBENCH_FUNCTIONS[fnumber].tag;
+ expr->u.function.args = reverse_elist(args);
+
+ return expr;
+}
+
#include "exprscan.c"
diff --git a/src/bin/pgbench/exprscan.l b/src/bin/pgbench/exprscan.l
index 5331ab7..bf8aa0f 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,19 @@ space [ \t\r\f]
yylval.ival = strtoint64(yytext);
return INTEGER;
}
+[0-9]+\.[0-9]+ {
+ yycol += yyleng;
+ yylval.dval = atof(yytext);
+ return DOUBLE;
+ }
+[a-zA-Z]+ {
+ 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 +83,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,15 +112,14 @@ expr_scanner_init(const char *str, const char *source,
expr_command = (char *) cmd;
expr_col = (int) ecol;
- /*
- * Might be left over after error
- */
+ /* reset column count for this scan */
+ yycol = 0;
+
+ /* Might be left over after error */
if (YY_CURRENT_BUFFER)
yy_delete_buffer(YY_CURRENT_BUFFER);
- /*
- * Make a scan buffer with special termination needed by flex.
- */
+ /* Make a scan buffer with special termination needed by flex. */
scanbuflen = slen;
scanbuf = pg_malloc(slen + 2);
memcpy(scanbuf, str, slen);
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index e839fa3..2f4dc03 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -300,13 +300,10 @@ static int debug = 0; /* debug flag */
/* default scenario */
static char *tpc_b = {
- "\\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"
@@ -318,13 +315,10 @@ static char *tpc_b = {
/* -N case */
static char *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"
@@ -334,8 +328,7 @@ static char *simple_update = {
/* -S case */
static char *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"
};
@@ -883,6 +876,114 @@ getQueryParams(CState *st, const Command *command, const char **params)
params[i] = getVariable(st, command->argv[i + 1]);
}
+static bool evalInt(TState *, CState *, PgBenchExpr *, int64 *);
+
+static bool
+evalDouble(TState *thread, CState *st, PgBenchExpr *expr, double *retval)
+{
+ switch (expr->etype)
+ {
+ case ENODE_DOUBLE_CONSTANT:
+ {
+ *retval = expr->u.double_constant.dval;
+ return true;
+ }
+ case ENODE_OPERATOR:
+ {
+ double lval, rval;
+
+ if (!evalDouble(thread, st, expr->u.operator.lexpr, &lval))
+ return false;
+ if (!evalDouble(thread, 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 '/':
+ *retval = lval / rval;
+ return true;
+
+ case '%':
+ default: /* *MUST* be an int operator */
+ {
+ int64 ival;
+ if (!evalInt(thread, st, expr, &ival))
+ return false;
+ *retval = (double) ival;
+ return true;
+ }
+ }
+ }
+ case ENODE_FUNCTION:
+ {
+ PgBenchFunction func = expr->u.function.function;
+ PgBenchExprList *args = expr->u.function.args;
+
+ switch (func)
+ {
+ case PGBENCH_ABS: /* also an integer function */
+ case PGBENCH_SQRT:
+ case PGBENCH_DDEBUG:
+ {
+ double arg;
+
+ if (!evalDouble(thread, st, args->expr, &arg))
+ return false;
+
+ if (func == PGBENCH_ABS)
+ *retval = arg >= 0.0? arg: -arg;
+ else if (func == PGBENCH_SQRT)
+ *retval = sqrt(arg);
+ else /* ddebug */
+ {
+ fprintf(stderr, "ddebug(script=%d,command=%d): %f\n",
+ st->use_file, st->state+1, arg);
+ *retval = arg;
+ }
+
+ return true;
+ }
+ case PGBENCH_DOUBLE:
+ {
+ int64 ival;
+ if (!evalInt(thread, st, args->expr, &ival))
+ return false;
+ *retval = (double) ival;
+ return true;
+ }
+ default: /* *MUST* be an integer function */
+ {
+ int64 ival;
+ if (!evalInt(thread, st, expr, &ival))
+ return false;
+ *retval = (double) ival;
+ return true;
+ }
+ }
+ }
+ default: /* *MUST* be an integer expression */
+ {
+ int64 ival;
+ if (!evalInt(thread, st, expr, &ival))
+ return false;
+ *retval = (double) ival;
+ return true;
+ }
+ }
+}
+
/*
* Recursive evaluation of an expression in a pgbench script
* using the current state of variables.
@@ -890,7 +991,7 @@ getQueryParams(CState *st, const Command *command, const char **params)
* the value itself is returned through the retval pointer.
*/
static bool
-evaluateExpr(CState *st, PgBenchExpr *expr, int64 *retval)
+evalInt(TState *thread, CState *st, PgBenchExpr *expr, int64 *retval)
{
switch (expr->etype)
{
@@ -900,6 +1001,12 @@ evaluateExpr(CState *st, PgBenchExpr *expr, int64 *retval)
return true;
}
+ case ENODE_DOUBLE_CONSTANT:
+ {
+ *retval = (int64) expr->u.double_constant.dval;
+ return true;
+ }
+
case ENODE_VARIABLE:
{
char *var;
@@ -919,9 +1026,9 @@ evaluateExpr(CState *st, PgBenchExpr *expr, int64 *retval)
int64 lval;
int64 rval;
- if (!evaluateExpr(st, expr->u.operator.lexpr, &lval))
+ if (!evalInt(thread, st, expr->u.operator.lexpr, &lval))
return false;
- if (!evaluateExpr(st, expr->u.operator.rexpr, &rval))
+ if (!evalInt(thread, st, expr->u.operator.rexpr, &rval))
return false;
switch (expr->u.operator.operator)
{
@@ -960,8 +1067,133 @@ evaluateExpr(CState *st, PgBenchExpr *expr, int64 *retval)
return false;
}
- default:
- break;
+ case ENODE_FUNCTION:
+ {
+ PgBenchFunction func = expr->u.function.function;
+ PgBenchExprList *args = expr->u.function.args;
+
+ switch (func)
+ {
+ case PGBENCH_RANDOM:
+ case PGBENCH_GAUSSRAND:
+ case PGBENCH_EXPORAND:
+ {
+ int64 arg1, arg2;
+
+ if (!evalInt(thread, st, args->expr, &arg1))
+ return false;
+ if (!evalInt(thread, st, args->next->expr, &arg2))
+ return false;
+
+ /* check random range */
+ if (arg1 > arg2)
+ {
+ fprintf(stderr, "empty range given to random\n");
+ st->ecnt++;
+ return false;
+ }
+ else if (arg2 - arg1 < 0 || (arg2 - arg1) + 1 < 0)
+ {
+ /* prevent int overflows in random functions */
+ fprintf(stderr, "random range is too large\n");
+ st->ecnt++;
+ return false;
+ }
+
+ if (func == PGBENCH_RANDOM)
+ *retval = getrand(thread, arg1, arg2);
+ else /* gaussian & exponential */
+ {
+ double threshold;
+ if (!evalDouble(thread, st, args->next->next->expr,
+ &threshold))
+ return false;
+ if (func == PGBENCH_GAUSSRAND)
+ *retval = getGaussianRand(thread, arg1, arg2, threshold);
+ else /* exponential */
+ *retval = getExponentialRand(thread, arg1, arg2, threshold);
+ }
+
+ return true;
+ }
+ case PGBENCH_IDEBUG: /* unary functions */
+ case PGBENCH_ABS: /* both an int & double function */
+ {
+ int64 arg;
+
+ if (!evalInt(thread, st, args->expr, &arg))
+ return false;
+
+ if (func == PGBENCH_ABS)
+ *retval = arg >= 0? arg: -arg;
+ else /* PGBENCH_IDEBUG */
+ {
+ fprintf(stderr, "idebug(script=%d,command=%d): "
+ INT64_FORMAT "\n", st->use_file, st->state+1, arg);
+ *retval = arg;
+ }
+
+ return true;
+ }
+ case PGBENCH_MIN: /* n-ary, at least one argument */
+ case PGBENCH_MAX:
+ {
+ int64 val = -1;
+ bool first = true;
+ while (args != NULL)
+ {
+ int64 arg;
+
+ if (!evalInt(thread, st, args->expr, &arg))
+ return false;
+
+ if (first)
+ val = arg;
+ else if (func == PGBENCH_MIN)
+ val = val < arg? val: arg;
+ else if (func == PGBENCH_MAX)
+ val = val > arg? val: arg;
+
+ args = args->next;
+ first = false;
+ }
+
+ *retval = val;
+ return true;
+ }
+ case PGBENCH_INT: /* eval as double & cast to int */
+ {
+ double arg;
+
+ if (!evalDouble(thread, st, args->expr, &arg))
+ return false;
+
+ *retval = (int64) arg;
+ return true;
+ }
+ default:
+ {
+ double arg;
+
+ if (!evalDouble(thread, st, expr, &arg))
+ return false;
+
+ *retval = (int64) arg;
+ return true;
+ }
+ }
+ }
+
+ default: /* *MUST* be a double function */
+ {
+ double arg;
+
+ if (!evalDouble(thread, st, expr, &arg))
+ return false;
+
+ *retval = (int64) arg;
+ return true;
+ }
}
fprintf(stderr, "bad expression\n");
@@ -1609,7 +1841,7 @@ top:
PgBenchExpr *expr = commands[st->state]->expr;
int64 result;
- if (!evaluateExpr(st, expr, &result))
+ if (!evalInt(thread, st, expr, &result))
{
st->ecnt++;
return true;
diff --git a/src/bin/pgbench/pgbench.h b/src/bin/pgbench/pgbench.h
index 42e2aae..af7abcb 100644
--- a/src/bin/pgbench/pgbench.h
+++ b/src/bin/pgbench/pgbench.h
@@ -14,11 +14,30 @@
typedef enum PgBenchExprType
{
ENODE_INTEGER_CONSTANT,
+ ENODE_DOUBLE_CONSTANT,
ENODE_VARIABLE,
- ENODE_OPERATOR
+ ENODE_OPERATOR,
+ ENODE_FUNCTION
} PgBenchExprType;
+typedef enum PgBenchFunction
+{
+ PGBENCH_NONE,
+ PGBENCH_INT,
+ PGBENCH_DOUBLE,
+ PGBENCH_IDEBUG,
+ PGBENCH_DDEBUG,
+ PGBENCH_ABS,
+ PGBENCH_SQRT,
+ PGBENCH_MIN,
+ PGBENCH_MAX,
+ PGBENCH_RANDOM,
+ PGBENCH_GAUSSRAND,
+ PGBENCH_EXPORAND
+} PgBenchFunction;
+
typedef struct PgBenchExpr PgBenchExpr;
+typedef struct PgBenchExprList PgBenchExprList;
struct PgBenchExpr
{
@@ -31,6 +50,10 @@ struct PgBenchExpr
} integer_constant;
struct
{
+ double dval;
+ } double_constant;
+ struct
+ {
char *varname;
} variable;
struct
@@ -39,14 +62,25 @@ struct PgBenchExpr
PgBenchExpr *lexpr;
PgBenchExpr *rexpr;
} operator;
+ struct
+ {
+ PgBenchFunction function;
+ PgBenchExprList *args;
+ } function;
} u;
};
+struct PgBenchExprList {
+ PgBenchExpr *expr;
+ PgBenchExprList *next;
+};
+
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);
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers