diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bin/pgbench/exprparse.y | 43 | ||||
-rw-r--r-- | src/bin/pgbench/exprscan.l | 5 | ||||
-rw-r--r-- | src/bin/pgbench/pgbench.c | 450 | ||||
-rw-r--r-- | src/bin/pgbench/pgbench.h | 35 |
4 files changed, 439 insertions, 94 deletions
diff --git a/src/bin/pgbench/exprparse.y b/src/bin/pgbench/exprparse.y index 4948ff3b813..877244852dd 100644 --- a/src/bin/pgbench/exprparse.y +++ b/src/bin/pgbench/exprparse.y @@ -20,6 +20,7 @@ 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(yyscan_t yyscanner, const char *operator, PgBenchExpr *lexpr, PgBenchExpr *rexpr); @@ -38,6 +39,7 @@ static PgBenchExpr *make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList * %union { int64 ival; + double dval; char *str; PgBenchExpr *expr; PgBenchExprList *elist; @@ -46,9 +48,10 @@ static PgBenchExpr *make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList * %type <elist> elist %type <expr> expr %type <ival> INTEGER function +%type <dval> DOUBLE %type <str> VARIABLE FUNCTION -%token INTEGER VARIABLE FUNCTION +%token INTEGER DOUBLE VARIABLE FUNCTION /* Precedence: lowest to highest */ %left '+' '-' @@ -74,6 +77,7 @@ expr: '(' expr ')' { $$ = $2; } | expr '/' expr { $$ = make_op(yyscanner, "/", $1, $3); } | expr '%' expr { $$ = make_op(yyscanner, "%", $1, $3); } | INTEGER { $$ = make_integer_constant($1); } + | DOUBLE { $$ = make_double_constant($1); } | VARIABLE { $$ = make_variable($1); } | function '(' elist ')' { $$ = make_func(yyscanner, $1, $3); } ; @@ -88,8 +92,20 @@ 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; +} + +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; } @@ -154,6 +170,27 @@ static const struct { "debug", 1, PGBENCH_DEBUG }, + { + "pi", 0, PGBENCH_PI + }, + { + "sqrt", 1, PGBENCH_SQRT + }, + { + "int", 1, PGBENCH_INT + }, + { + "double", 1, PGBENCH_DOUBLE + }, + { + "random", 2, PGBENCH_RANDOM + }, + { + "random_gaussian", 3, PGBENCH_RANDOM_GAUSSIAN + }, + { + "random_exponential", 3, PGBENCH_RANDOM_EXPONENTIAL + }, /* keep as last array element */ { NULL, 0, 0 diff --git a/src/bin/pgbench/exprscan.l b/src/bin/pgbench/exprscan.l index 7120836f840..d8b706a99bc 100644 --- a/src/bin/pgbench/exprscan.l +++ b/src/bin/pgbench/exprscan.l @@ -125,6 +125,11 @@ newline [\n] yylval->ival = strtoint64(yytext); return INTEGER; } +{digit}+(\.{digit}*)?([eE][-+]?{digit}+)? { + yycolumn += yyleng; + yylval->dval = atof(yytext); + return DOUBLE; + } {alpha}{alnum}* { yylval->str = pg_strdup(yytext); return FUNCTION; diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c index 4196b0e94b0..a2df4df20d9 100644 --- a/src/bin/pgbench/pgbench.c +++ b/src/bin/pgbench/pgbench.c @@ -328,13 +328,10 @@ static const BuiltinScript builtin_script[] = { "tpcb-like", "<builtin: TPC-B (sort of)>", - "\\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" @@ -346,13 +343,10 @@ static const BuiltinScript builtin_script[] = { "simple-update", "<builtin: 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" @@ -362,15 +356,14 @@ static const BuiltinScript builtin_script[] = { "select-only", "<builtin: 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 bool evaluateExpr(CState *st, PgBenchExpr *expr, int64 *retval); +static bool evaluateExpr(TState *, CState *, PgBenchExpr *, PgBenchValue *); static void doLog(TState *thread, CState *st, instr_time *now, StatsData *agg, bool skipped, double latency, double lag); static void processXactStats(TState *thread, CState *st, instr_time *now, @@ -446,6 +439,33 @@ usage(void) progname, progname); } +/* return whether str matches "^\s*[-+]?[0-9]+$" */ +static bool +is_an_int(const char *str) +{ + const char *ptr = str; + + /* skip leading spaces; cast is consistent with strtoint64 */ + while (*ptr && isspace((unsigned char) *ptr)) + ptr++; + + /* skip sign */ + if (*ptr == '+' || *ptr == '-') + ptr++; + + /* at least one digit */ + if (*ptr && !isdigit((unsigned char) *ptr)) + return false; + + /* eat all digits */ + while (*ptr && isdigit((unsigned char) *ptr)) + ptr++; + + /* must have reached end of string */ + return *ptr == '\0'; +} + + /* * strtoint64 -- convert a string to 64-bit integer * @@ -542,6 +562,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] */ @@ -563,6 +584,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 @@ -1006,6 +1030,62 @@ getQueryParams(CState *st, const Command *command, const char **params) params[i] = getVariable(st, command->argv[i + 1]); } +/* get a value as an int, tell if there is a problem */ +static bool +coerceToInt(PgBenchValue *pval, int64 *ival) +{ + if (pval->type == PGBT_INT) + { + *ival = pval->u.ival; + return true; + } + else + { + double dval = pval->u.dval; + Assert(pval->type == PGBT_DOUBLE); + if (dval < INT64_MIN || INT64_MAX < dval) + { + fprintf(stderr, "double to int overflow for %f\n", dval); + return false; + } + *ival = (int64) dval; + return true; + } +} + +/* get a value as a double, or tell if there is a problem */ +static bool +coerceToDouble(PgBenchValue *pval, double *dval) +{ + if (pval->type == PGBT_DOUBLE) + { + *dval = pval->u.dval; + return true; + } + else + { + Assert(pval->type == PGBT_INT); + *dval = (double) pval->u.ival; + return true; + } +} + +/* assign an integer value */ +static void +setIntValue(PgBenchValue *pv, int64 ival) +{ + pv->type = PGBT_INT; + pv->u.ival = ival; +} + +/* assign a double value */ +static void +setDoubleValue(PgBenchValue *pv, double dval) +{ + pv->type = PGBT_DOUBLE; + pv->u.dval = dval; +} + /* maximum number of function arguments */ #define MAX_FARGS 16 @@ -1013,16 +1093,16 @@ getQueryParams(CState *st, const Command *command, const char **params) * Recursive evaluation of functions */ static bool -evalFunc(CState *st, - PgBenchFunction func, PgBenchExprLink *args, int64 *retval) +evalFunc(TState *thread, CState *st, + PgBenchFunction func, PgBenchExprLink *args, PgBenchValue *retval) { /* evaluate all function arguments */ - int nargs = 0; - int64 iargs[MAX_FARGS]; + int nargs = 0; + PgBenchValue vargs[MAX_FARGS]; PgBenchExprLink *l = args; for (nargs = 0; nargs < MAX_FARGS && l != NULL; nargs++, l = l->next) - if (!evaluateExpr(st, l->expr, &iargs[nargs])) + if (!evaluateExpr(thread, st, l->expr, &vargs[nargs])) return false; if (l != NULL) @@ -1035,104 +1115,206 @@ evalFunc(CState *st, /* then evaluate function */ switch (func) { + /* overloaded operators */ case PGBENCH_ADD: case PGBENCH_SUB: case PGBENCH_MUL: case PGBENCH_DIV: case PGBENCH_MOD: { - int64 lval = iargs[0], - rval = iargs[1]; - + PgBenchValue *lval = &vargs[0], + *rval = &vargs[1]; Assert(nargs == 2); - switch (func) + /* overloaded type management, double if some double */ + if ((lval->type == PGBT_DOUBLE || + rval->type == PGBT_DOUBLE) && func != PGBENCH_MOD) { - case PGBENCH_ADD: - *retval = lval + rval; - return true; + double ld, rd; - case PGBENCH_SUB: - *retval = lval - rval; - return true; + if (!coerceToDouble(lval, &ld) || + !coerceToDouble(rval, &rd)) + return false; - case PGBENCH_MUL: - *retval = lval * rval; - return true; + switch (func) + { + case PGBENCH_ADD: + setDoubleValue(retval, ld + rd); + 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) + case PGBENCH_SUB: + setDoubleValue(retval, ld - rd); + return true; + + case PGBENCH_MUL: + setDoubleValue(retval, ld * rd); + return true; + + case PGBENCH_DIV: + setDoubleValue(retval, ld / rd); + return true; + + default: + /* cannot get here */ + Assert(0); + } + } + else /* we have integer operands, or % */ + { + int64 li, ri; + + if (!coerceToInt(lval, &li) || + !coerceToInt(rval, &ri)) + return false; + + switch (func) + { + case PGBENCH_ADD: + setIntValue(retval, li + ri); + return true; + + case PGBENCH_SUB: + setIntValue(retval, li - ri); + return true; + + case PGBENCH_MUL: + setIntValue(retval, li * ri); + return true; + + case PGBENCH_DIV: + case PGBENCH_MOD: + if (ri == 0) + { + fprintf(stderr, "division by zero\n"); + return false; + } + /* special handling of -1 divisor */ + if (ri == -1) { - /* overflow check (needed for INT64_MIN) */ - if (lval == PG_INT64_MIN) + if (func == PGBENCH_DIV) { - fprintf(stderr, "bigint out of range\n"); - return false; + /* overflow check (needed for INT64_MIN) */ + if (li == PG_INT64_MIN) + { + fprintf(stderr, "bigint out of range\n"); + return false; + } + else + setIntValue(retval, - li); } else - *retval = -lval; + setIntValue(retval, 0); + return true; } - else - *retval = 0; + /* else divisor is not -1 */ + if (func == PGBENCH_DIV) + setIntValue(retval, li / ri); + else /* func == PGBENCH_MOD */ + setIntValue(retval, li % ri); + 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); + default: + /* cannot get here */ + Assert(0); + } } } + /* no arguments */ + case PGBENCH_PI: + setDoubleValue(retval, M_PI); + return true; + + /* 1 overloaded argument */ case PGBENCH_ABS: { + PgBenchValue *varg = &vargs[0]; Assert(nargs == 1); - if (iargs[0] < 0) - *retval = -iargs[0]; + if (varg->type == PGBT_INT) + { + int64 i = varg->u.ival; + setIntValue(retval, i < 0 ? -i : i); + } else - *retval = iargs[0]; + { + double d = varg->u.dval; + Assert(varg->type == PGBT_DOUBLE); + setDoubleValue(retval, d < 0.0 ? -d: d); + } return true; } case PGBENCH_DEBUG: { + PgBenchValue *varg = &vargs[0]; + Assert(nargs == 1); + + fprintf(stderr, "debug(script=%d,command=%d): ", + st->use_file, st->state+1); + + if (varg->type == PGBT_INT) + fprintf(stderr, "int "INT64_FORMAT"\n", varg->u.ival); + else + { + Assert(varg->type == PGBT_DOUBLE); + fprintf(stderr, "double %f\n", varg->u.dval); + } + + *retval = *varg; + + return true; + } + + /* 1 double argument */ + case PGBENCH_DOUBLE: + case PGBENCH_SQRT: + { + double dval; Assert(nargs == 1); - fprintf(stderr, "debug(script=%d,command=%d): " INT64_FORMAT "\n", - st->use_file, st->state + 1, iargs[0]); + if (!coerceToDouble(&vargs[0], &dval)) + return false; + + if (func == PGBENCH_SQRT) + dval = sqrt(dval); + + setDoubleValue(retval, dval); + return true; + } + + /* 1 int argument */ + case PGBENCH_INT: + { + int64 ival; + Assert(nargs == 1); - *retval = iargs[0]; + if (!coerceToInt(&vargs[0], &ival)) + return false; + setIntValue(retval, ival); return true; } + /* variable number of int arguments */ case PGBENCH_MIN: case PGBENCH_MAX: { - int64 extremum = iargs[0]; + int64 extremum; int i; - Assert(nargs >= 1); + if (!coerceToInt(&vargs[0], &extremum)) + return false; + for (i = 1; i < nargs; i++) { - int64 ival = iargs[i]; + int64 ival; + + if (!coerceToInt(&vargs[i], &ival)) + return false; if (func == PGBENCH_MIN) extremum = extremum < ival ? extremum : ival; @@ -1140,13 +1322,84 @@ evalFunc(CState *st, extremum = extremum > ival ? extremum : ival; } - *retval = extremum; + setIntValue(retval, extremum); return true; } + /* random functions */ + case PGBENCH_RANDOM: + case PGBENCH_RANDOM_EXPONENTIAL: + case PGBENCH_RANDOM_GAUSSIAN: + { + int64 imin, imax; + Assert(nargs >= 2); + + if (!coerceToInt(&vargs[0], &imin) || + !coerceToInt(&vargs[1], &imax)) + return false; + + /* check random range */ + if (imin > imax) + { + fprintf(stderr, "empty range given to random\n"); + return false; + } + else if (imax - imin < 0 || (imax - imin) + 1 < 0) + { + /* prevent int overflows in random functions */ + fprintf(stderr, "random range is too large\n"); + return false; + } + + if (func == PGBENCH_RANDOM) + { + Assert(nargs == 2); + setIntValue(retval, getrand(thread, imin, imax)); + } + else /* gaussian & exponential */ + { + double param; + Assert(nargs == 3); + + if (!coerceToDouble(&vargs[2], ¶m)) + return false; + + if (func == PGBENCH_RANDOM_GAUSSIAN) + { + if (param < MIN_GAUSSIAN_PARAM) + { + fprintf(stderr, + "gaussian parameter must be at least %f " + "(not %f)\n", MIN_GAUSSIAN_PARAM, param); + return false; + } + + setIntValue(retval, + getGaussianRand(thread, imin, imax, param)); + } + else /* exponential */ + { + if (param <= 0.0) + { + fprintf(stderr, + "exponential parameter must be greater than zero" + " (got %f)\n", param); + return false; + } + + setIntValue(retval, + getExponentialRand(thread, imin, imax, param)); + } + } + + return true; + } + default: - fprintf(stderr, "unexpected function tag: %d\n", func); - exit(1); + /* cannot get here */ + Assert(0); + /* dead code to avoid a compiler warning */ + return false; } } @@ -1157,13 +1410,13 @@ evalFunc(CState *st, * the value itself is returned through the retval pointer. */ static bool -evaluateExpr(CState *st, PgBenchExpr *expr, int64 *retval) +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; } @@ -1177,24 +1430,39 @@ evaluateExpr(CState *st, PgBenchExpr *expr, int64 *retval) expr->u.variable.varname); return false; } - *retval = strtoint64(var); + + if (is_an_int(var)) + { + setIntValue(retval, strtoint64(var)); + } + else /* type should be double */ + { + double dv; + if (sscanf(var, "%lf", &dv) != 1) + { + fprintf(stderr, + "malformed variable \"%s\" value: \"%s\"\n", + expr->u.variable.varname, var); + return false; + } + setDoubleValue(retval, dv); + } + return true; } case ENODE_FUNCTION: - return evalFunc(st, + return evalFunc(thread, st, expr->u.function.function, expr->u.function.args, retval); default: + /* internal error which should never occur */ fprintf(stderr, "unexpected enode type in evaluation: %d\n", expr->etype); exit(1); } - - fprintf(stderr, "bad expression\n"); - return false; } /* @@ -1673,6 +1941,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; @@ -1814,15 +2086,21 @@ top: { char res[64]; PgBenchExpr *expr = commands[st->state]->expr; - int64 result; + 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, result.u.ival); + else + { + Assert(result.type == PGBT_DOUBLE); + sprintf(res, "%.18e", result.u.dval); + } if (!putVariable(st, argv[0], argv[1], res)) { diff --git a/src/bin/pgbench/pgbench.h b/src/bin/pgbench/pgbench.h index 46350aace10..7dcb67f5203 100644 --- a/src/bin/pgbench/pgbench.h +++ b/src/bin/pgbench/pgbench.h @@ -28,10 +28,31 @@ */ union YYSTYPE; +/* + * Variable types used in parser. + */ +typedef enum +{ + PGBT_INT, + PGBT_DOUBLE + /* add other types here */ +} PgBenchValueType; + +typedef struct +{ + PgBenchValueType type; + union + { + int64 ival; + double dval; + /* add other types here */ + } u; +} PgBenchValue; + /* Types of expression nodes */ typedef enum PgBenchExprType { - ENODE_INTEGER_CONSTANT, + ENODE_CONSTANT, ENODE_VARIABLE, ENODE_FUNCTION } PgBenchExprType; @@ -48,6 +69,13 @@ typedef enum PgBenchFunction PGBENCH_ABS, PGBENCH_MIN, PGBENCH_MAX, + PGBENCH_INT, + PGBENCH_DOUBLE, + PGBENCH_PI, + PGBENCH_SQRT, + PGBENCH_RANDOM, + PGBENCH_RANDOM_GAUSSIAN, + PGBENCH_RANDOM_EXPONENTIAL } PgBenchFunction; typedef struct PgBenchExpr PgBenchExpr; @@ -59,10 +87,7 @@ struct PgBenchExpr PgBenchExprType etype; union { - struct - { - int64 ival; - } integer_constant; + PgBenchValue constant; struct { char *varname; |