v38 is a simple rebase, trying to keep up-to-date with Tom's work.
v39 is yet another rebase: 42 is in sight!
--
Fabien.
diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml
index c6d1454..4ceddae 100644
--- a/doc/src/sgml/ref/pgbench.sgml
+++ b/doc/src/sgml/ref/pgbench.sgml
@@ -815,9 +815,10 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
<listitem>
<para>
- Sets variable <replaceable>varname</> to an integer value calculated
+ Sets variable <replaceable>varname</> to a value calculated
from <replaceable>expression</>.
The expression may contain integer constants such as <literal>5432</>,
+ double constants such as <literal>3.14159</>,
references to variables <literal>:</><replaceable>variablename</>,
unary operators (<literal>+</>, <literal>-</>) and binary operators
(<literal>+</>, <literal>-</>, <literal>*</>, <literal>/</>,
@@ -830,7 +831,7 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
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>
@@ -850,66 +851,35 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
</para>
<para>
- By default, or when <literal>uniform</> is specified, all values in the
- range are drawn with equal probability. Specifying <literal>gaussian</>
- or <literal>exponential</> options modifies this behavior; each
- requires a mandatory parameter which determines the precise shape of the
- distribution.
- </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>\setrandom n 1 10</> or <literal>\setrandom n 1 10 uniform</>
+ is equivalent to <literal>\set n random(1, 10)</> and uses a uniform
+ distribution.
+ </para>
+ </listitem>
- <para>
- For a Gaussian distribution, the interval is mapped onto a standard
- normal distribution (the classical bell-shaped Gaussian curve) truncated
- at <literal>-parameter</> on the left and <literal>+parameter</>
- on the right.
- Values in the middle of the interval are more likely to be drawn.
- To be precise, if <literal>PHI(x)</> is the cumulative distribution
- function of the standard normal distribution, with mean <literal>mu</>
- defined as <literal>(max + min) / 2.0</>, with
-<literallayout>
- f(x) = PHI(2.0 * parameter * (x - mu) / (max - min + 1)) /
- (2.0 * PHI(parameter) - 1.0)
-</literallayout>
- then value <replaceable>i</> between <replaceable>min</> and
- <replaceable>max</> inclusive is drawn with probability:
- <literal>f(i + 0.5) - f(i - 0.5)</>.
- Intuitively, the larger <replaceable>parameter</>, the more
- frequently values close to the middle of the interval are drawn, and the
- less frequently values close to the <replaceable>min</> and
- <replaceable>max</> bounds. About 67% of values are drawn from the
- middle <literal>1.0 / parameter</>, that is a relative
- <literal>0.5 / parameter</> around the mean, and 95% in the middle
- <literal>2.0 / parameter</>, that is a relative
- <literal>1.0 / parameter</> around the mean; for instance, if
- <replaceable>parameter</> is 4.0, 67% of values are drawn from the
- middle quarter (1.0 / 4.0) of the interval (i.e. from
- <literal>3.0 / 8.0</> to <literal>5.0 / 8.0</>) and 95% from
- the middle half (<literal>2.0 / 4.0</>) of the interval (second and
- third quartiles). The minimum <replaceable>parameter</> is 2.0 for
- performance of the Box-Muller transform.
- </para>
+ <listitem>
+ <para>
+ <literal>\setrandom n 1 10 exponential 3.0</> is equivalent to
+ <literal>\set n random_exponential(1, 10, 3.0)</> and uses an
+ exponential distribution.
+ </para>
+ </listitem>
- <para>
- For an exponential distribution, <replaceable>parameter</>
- controls the distribution by truncating a quickly-decreasing
- exponential distribution at <replaceable>parameter</>, and then
- projecting onto integers between the bounds.
- To be precise, with
-<literallayout>
-f(x) = exp(-parameter * (x - min) / (max - min + 1)) / (1.0 - exp(-parameter))
-</literallayout>
- Then value <replaceable>i</> between <replaceable>min</> and
- <replaceable>max</> inclusive is drawn with probability:
- <literal>f(x) - f(x + 1)</>.
- Intuitively, the larger <replaceable>parameter</>, the more
- frequently values close to <replaceable>min</> are accessed, and the
- less frequently values close to <replaceable>max</> are accessed.
- The closer to 0 <replaceable>parameter</>, the flatter (more uniform)
- the access distribution.
- A crude approximation of the distribution is that the most frequent 1%
- values in the range, close to <replaceable>min</>, are drawn
- <replaceable>parameter</>% of the time.
- <replaceable>parameter</> value must be strictly positive.
+ <listitem>
+ <para>
+ <literal>\setrandom n 1 10 gaussian 2.0</> is equivalent to
+ <literal>\set n random_gaussian(1, 10, 2.0)</>, and uses a gaussian
+ distribution.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ See the documentation of these functions below for further information
+ about the precise shape of these distributions, depending on the value
+ of the parameter.
</para>
<para>
@@ -990,34 +960,6 @@ f(x) = exp(-parameter * (x - min) / (max - min + 1)) / (1.0 - exp(-parameter))
</listitem>
</varlistentry>
</variablelist>
-
- <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
-BEGIN;
-UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
-SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
-UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;
-UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;
-INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);
-END;
-</programlisting>
-
- This script allows each iteration of the transaction to reference
- different, randomly-chosen rows. (This example also shows why it's
- important for each client session to have its own variables —
- otherwise they'd not be independently touching different rows.)
- </para>
-
</refsect2>
<refsect2 id="pgbench-builtin-functions">
@@ -1046,7 +988,7 @@ END;
<row>
<entry><literal><function>abs(<replaceable>a</>)</></></>
<entry>same as <replaceable>a</></>
- <entry>integer value</>
+ <entry>integer or double absolute value</>
<entry><literal>abs(-17)</></>
<entry><literal>17</></>
</row>
@@ -1054,8 +996,22 @@ END;
<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</></>
+ <entry><literal>debug(5432.1)</></>
+ <entry><literal>5432.1</></>
+ </row>
+ <row>
+ <entry><literal><function>double(<replaceable>i</>)</></></>
+ <entry>double</>
+ <entry>cast to double</>
+ <entry><literal>double(5432)</></>
+ <entry><literal>5432.0</></>
+ </row>
+ <row>
+ <entry><literal><function>int(<replaceable>x</>)</></></>
+ <entry>integer</>
+ <entry>cast to int</>
+ <entry><literal>int(5.4 + 3.8)</></>
+ <entry><literal>9</></>
</row>
<row>
<entry><literal><function>max(<replaceable>i</> [, <replaceable>...</> ] )</></></>
@@ -1071,9 +1027,143 @@ END;
<entry><literal>min(5, 4, 3, 2)</></>
<entry><literal>2</></>
</row>
+ <row>
+ <entry><literal><function>pi()</></></>
+ <entry>double</>
+ <entry>value of the PI constant</>
+ <entry><literal>pi()</></>
+ <entry><literal>3.14159265358979323846</></>
+ </row>
+ <row>
+ <entry><literal><function>random(<replaceable>lb</>, <replaceable>ub</>)</></></>
+ <entry>integer</>
+ <entry>uniformly-distributed random integer in <literal>[lb, ub]</></>
+ <entry><literal>random(1, 10)</></>
+ <entry>an integer between <literal>1</> and <literal>10</></>
+ </row>
+ <row>
+ <entry><literal><function>random_exponential(<replaceable>lb</>, <replaceable>ub</>, <replaceable>parameter</>)</></></>
+ <entry>integer</>
+ <entry>exponentially-distributed random integer in <literal>[lb, ub]</>,
+ see below</>
+ <entry><literal>random_exponential(1, 10, 3.0)</></>
+ <entry>an integer between <literal>1</> and <literal>10</></>
+ </row>
+ <row>
+ <entry><literal><function>random_gaussian(<replaceable>lb</>, <replaceable>ub</>, <replaceable>parameter</>)</></></>
+ <entry>integer</>
+ <entry>gaussian-distributed random integer in <literal>[lb, ub]</>,
+ see below</>
+ <entry><literal>random_gaussian(1, 10, 2.5)</></>
+ <entry>an integer 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>
+ The <literal>random</> function generates values using a uniform
+ distribution, that is all the values are drawn within the specified
+ range with equal probability. The <literal>random_exponential</> and
+ <literal>random_gaussian</> functions require an additional double
+ parameter which determines the precise shape of the distribution.
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ For an exponential distribution, <replaceable>parameter</>
+ controls the distribution by truncating a quickly-decreasing
+ exponential distribution at <replaceable>parameter</>, and then
+ projecting onto integers between the bounds.
+ To be precise, with
+<literallayout>
+f(x) = exp(-parameter * (x - min) / (max - min + 1)) / (1 - exp(-parameter))
+</literallayout>
+ Then value <replaceable>i</> between <replaceable>min</> and
+ <replaceable>max</> inclusive is drawn with probability:
+ <literal>f(x) - f(x + 1)</>.
+ </para>
+
+ <para>
+ Intuitively, the larger the <replaceable>parameter</>, the more
+ frequently values close to <replaceable>min</> are accessed, and the
+ less frequently values close to <replaceable>max</> are accessed.
+ The closer to 0 <replaceable>parameter</> is, the flatter (more
+ uniform) the access distribution.
+ A crude approximation of the distribution is that the most frequent 1%
+ values in the range, close to <replaceable>min</>, are drawn
+ <replaceable>parameter</>% of the time.
+ The <replaceable>parameter</> value must be strictly positive.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ For a Gaussian distribution, the interval is mapped onto a standard
+ normal distribution (the classical bell-shaped Gaussian curve) truncated
+ at <literal>-parameter</> on the left and <literal>+parameter</>
+ on the right.
+ Values in the middle of the interval are more likely to be drawn.
+ To be precise, if <literal>PHI(x)</> is the cumulative distribution
+ function of the standard normal distribution, with mean <literal>mu</>
+ defined as <literal>(max + min) / 2.0</>, with
+<literallayout>
+ f(x) = PHI(2.0 * parameter * (x - mu) / (max - min + 1)) /
+ (2.0 * PHI(parameter) - 1)
+</literallayout>
+ then value <replaceable>i</> between <replaceable>min</> and
+ <replaceable>max</> inclusive is drawn with probability:
+ <literal>f(i + 0.5) - f(i - 0.5)</>.
+ Intuitively, the larger the <replaceable>parameter</>, the more
+ frequently values close to the middle of the interval are drawn, and the
+ less frequently values close to the <replaceable>min</> and
+ <replaceable>max</> bounds. About 67% of values are drawn from the
+ middle <literal>1.0 / parameter</>, that is a relative
+ <literal>0.5 / parameter</> around the mean, and 95% in the middle
+ <literal>2.0 / parameter</>, that is a relative
+ <literal>1.0 / parameter</> around the mean; for instance, if
+ <replaceable>parameter</> is 4.0, 67% of values are drawn from the
+ middle quarter (1.0 / 4.0) of the interval (i.e. from
+ <literal>3.0 / 8.0</> to <literal>5.0 / 8.0</>) and 95% from
+ the middle half (<literal>2.0 / 4.0</>) of the interval (second and third
+ quartiles). The minimum <replaceable>parameter</> is 2.0 for performance
+ of the Box-Muller transform.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ As an example, the full definition of the built-in TPC-B-like
+ transaction is:
+
+<programlisting>
+\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;
+UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;
+UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;
+INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);
+END;
+</programlisting>
+
+ This script allows each iteration of the transaction to reference
+ different, randomly-chosen rows. (This example also shows why it's
+ important for each client session to have its own variables —
+ otherwise they'd not be independently touching different rows.)
+ </para>
+
</refsect2>
<refsect2>
@@ -1223,13 +1313,10 @@ tps = 618.764555 (including connections establishing)
tps = 622.977698 (excluding connections establishing)
script statistics:
- statement latencies in milliseconds:
- 0.004386 \set nbranches 1 * :scale
- 0.001343 \set ntellers 10 * :scale
- 0.001212 \set naccounts 100000 * :scale
- 0.001310 \setrandom aid 1 :naccounts
- 0.001073 \setrandom bid 1 :nbranches
- 0.001005 \setrandom tid 1 :ntellers
- 0.001078 \setrandom delta -5000 5000
+ 0.002522 \set aid random(1, 100000 * :scale)
+ 0.005459 \set bid random(1, 1 * :scale)
+ 0.002348 \set tid random(1, 10 * :scale)
+ 0.001078 \set delta random(-5000, 5000)
0.326152 BEGIN;
0.603376 UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
0.454643 SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
diff --git a/src/bin/pgbench/exprparse.y b/src/bin/pgbench/exprparse.y
index 5d0d972..d7ae005 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);
@@ -37,6 +38,7 @@ static PgBenchExpr *make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList *
%union
{
int64 ival;
+ double dval;
char *str;
PgBenchExpr *expr;
PgBenchExprList *elist;
@@ -45,9 +47,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 '+' '-'
@@ -73,6 +76,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); }
;
@@ -87,8 +91,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;
}
@@ -153,6 +169,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 d069c5b..3965369 100644
--- a/src/bin/pgbench/exprscan.l
+++ b/src/bin/pgbench/exprscan.l
@@ -124,6 +124,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 4196b0e..93c72e0 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,
@@ -380,14 +373,12 @@ static void addScript(ParsedScript script);
static void *threadRun(void *arg);
static void setalarm(int seconds);
-
/* callback functions for our flex lexer */
static const PsqlScanCallbacks pgbench_callbacks = {
NULL, /* don't need get_variable functionality */
pgbench_error
};
-
static void
usage(void)
{
@@ -446,6 +437,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 +560,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 +582,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 +1028,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(CState *st, 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(CState *st, 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 +1091,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 +1113,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;
-
- 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)
+ double ld, rd;
+
+ if (!coerceToDouble(st, lval, &ld) ||
+ !coerceToDouble(st, rval, &rd))
+ return false;
+
+ switch (func)
+ {
+ case PGBENCH_ADD:
+ setDoubleValue(retval, ld + rd);
+ return true;
+
+ 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(st, lval, &li) ||
+ !coerceToInt(st, 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): " INT64_FORMAT "\n",
- st->use_file, st->state + 1, iargs[0]);
+ fprintf(stderr, "debug(script=%d,command=%d): ",
+ st->use_file, st->state+1);
- *retval = iargs[0];
+ 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);
+
+ if (!coerceToDouble(st, &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);
+
+ if (!coerceToInt(st, &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(st, &vargs[0], &extremum))
+ return false;
+
for (i = 1; i < nargs; i++)
{
- int64 ival = iargs[i];
+ int64 ival;
+
+ if (!coerceToInt(st, &vargs[i], &ival))
+ return false;
if (func == PGBENCH_MIN)
extremum = extremum < ival ? extremum : ival;
@@ -1140,13 +1320,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(st, &vargs[0], &imin) ||
+ !coerceToInt(st, &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(st, &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 +1408,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 +1428,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 +1939,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 +2084,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 a9db9c5..bd3afbe 100644
--- a/src/bin/pgbench/pgbench.h
+++ b/src/bin/pgbench/pgbench.h
@@ -22,10 +22,31 @@
*/
#define yyscan_t void *
+/*
+ * 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;
@@ -42,6 +63,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;
@@ -53,10 +81,7 @@ struct PgBenchExpr
PgBenchExprType etype;
union
{
- struct
- {
- int64 ival;
- } integer_constant;
+ PgBenchValue constant;
struct
{
char *varname;
diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml
index 4ceddae..c5399ff 100644
--- a/doc/src/sgml/ref/pgbench.sgml
+++ b/doc/src/sgml/ref/pgbench.sgml
@@ -838,60 +838,6 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
<varlistentry>
<term>
- <literal>\setrandom <replaceable>varname</> <replaceable>min</> <replaceable>max</> [ uniform | { gaussian | exponential } <replaceable>parameter</> ]</literal>
- </term>
-
- <listitem>
- <para>
- Sets variable <replaceable>varname</> to a random integer value
- between the limits <replaceable>min</> and <replaceable>max</> inclusive.
- Each limit can be either an integer constant or a
- <literal>:</><replaceable>variablename</> reference to a variable
- having an integer value.
- </para>
-
- <para>
- <itemizedlist>
- <listitem>
- <para>
- <literal>\setrandom n 1 10</> or <literal>\setrandom n 1 10 uniform</>
- is equivalent to <literal>\set n random(1, 10)</> and uses a uniform
- distribution.
- </para>
- </listitem>
-
- <listitem>
- <para>
- <literal>\setrandom n 1 10 exponential 3.0</> is equivalent to
- <literal>\set n random_exponential(1, 10, 3.0)</> and uses an
- exponential distribution.
- </para>
- </listitem>
-
- <listitem>
- <para>
- <literal>\setrandom n 1 10 gaussian 2.0</> is equivalent to
- <literal>\set n random_gaussian(1, 10, 2.0)</>, and uses a gaussian
- distribution.
- </para>
- </listitem>
- </itemizedlist>
-
- See the documentation of these functions below for further information
- about the precise shape of these distributions, depending on the value
- of the parameter.
- </para>
-
- <para>
- Example:
-<programlisting>
-\setrandom aid 1 :naccounts gaussian 5.0
-</programlisting></para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
<literal>\sleep <replaceable>number</> [ us | ms | s ]</literal>
</term>
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 93c72e0..21c5507 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -1939,148 +1939,7 @@ 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;
- int64 min,
- max;
- double parameter = 0;
- char res[64];
-
- if (*argv[2] == ':')
- {
- if ((var = getVariable(st, argv[2] + 1)) == NULL)
- {
- fprintf(stderr, "%s: undefined variable \"%s\"\n",
- argv[0], argv[2]);
- st->ecnt++;
- return true;
- }
- min = strtoint64(var);
- }
- else
- min = strtoint64(argv[2]);
-
- if (*argv[3] == ':')
- {
- if ((var = getVariable(st, argv[3] + 1)) == NULL)
- {
- fprintf(stderr, "%s: undefined variable \"%s\"\n",
- argv[0], argv[3]);
- st->ecnt++;
- return true;
- }
- max = strtoint64(var);
- }
- else
- max = strtoint64(argv[3]);
-
- if (max < min)
- {
- fprintf(stderr, "%s: \\setrandom maximum is less than minimum\n",
- argv[0]);
- st->ecnt++;
- return true;
- }
-
- /*
- * Generate random number functions need to be able to subtract
- * max from min and add one to the result without overflowing.
- * Since we know max > min, we can detect overflow just by
- * checking for a negative result. But we must check both that the
- * subtraction doesn't overflow, and that adding one to the result
- * doesn't overflow either.
- */
- if (max - min < 0 || (max - min) + 1 < 0)
- {
- fprintf(stderr, "%s: \\setrandom range is too large\n",
- argv[0]);
- st->ecnt++;
- return true;
- }
-
- if (argc == 4 || /* uniform without or with "uniform" keyword */
- (argc == 5 && pg_strcasecmp(argv[4], "uniform") == 0))
- {
-#ifdef DEBUG
- printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
-#endif
- snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
- }
- else if (argc == 6 &&
- ((pg_strcasecmp(argv[4], "gaussian") == 0) ||
- (pg_strcasecmp(argv[4], "exponential") == 0)))
- {
- if (*argv[5] == ':')
- {
- if ((var = getVariable(st, argv[5] + 1)) == NULL)
- {
- fprintf(stderr, "%s: invalid parameter: \"%s\"\n",
- argv[0], argv[5]);
- st->ecnt++;
- return true;
- }
- parameter = strtod(var, NULL);
- }
- else
- parameter = strtod(argv[5], NULL);
-
- if (pg_strcasecmp(argv[4], "gaussian") == 0)
- {
- if (parameter < MIN_GAUSSIAN_PARAM)
- {
- fprintf(stderr, "gaussian parameter must be at least %f (not \"%s\")\n", MIN_GAUSSIAN_PARAM, argv[5]);
- st->ecnt++;
- return true;
- }
-#ifdef DEBUG
- printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n",
- min, max,
- getGaussianRand(thread, min, max, parameter));
-#endif
- snprintf(res, sizeof(res), INT64_FORMAT,
- getGaussianRand(thread, min, max, parameter));
- }
- else if (pg_strcasecmp(argv[4], "exponential") == 0)
- {
- if (parameter <= 0.0)
- {
- fprintf(stderr,
- "exponential parameter must be greater than zero (not \"%s\")\n",
- argv[5]);
- st->ecnt++;
- return true;
- }
-#ifdef DEBUG
- printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n",
- min, max,
- getExponentialRand(thread, min, max, parameter));
-#endif
- snprintf(res, sizeof(res), INT64_FORMAT,
- getExponentialRand(thread, min, max, parameter));
- }
- }
- else /* this means an error somewhere in the parsing phase... */
- {
- fprintf(stderr, "%s: invalid arguments for \\setrandom\n",
- argv[0]);
- st->ecnt++;
- return true;
- }
-
- if (!putVariable(st, argv[0], argv[1], res))
- {
- st->ecnt++;
- return true;
- }
-
- st->listen = true;
- }
- else if (pg_strcasecmp(argv[0], "set") == 0)
+ if (pg_strcasecmp(argv[0], "set") == 0)
{
char res[64];
PgBenchExpr *expr = commands[st->state]->expr;
@@ -2880,39 +2739,9 @@ process_backslash_command(PsqlScanState sstate, const char *source)
if (pg_strcasecmp(my_command->argv[0], "setrandom") == 0)
{
- /*--------
- * parsing:
- * \setrandom variable min max [uniform]
- * \setrandom variable min max (gaussian|exponential) parameter
- */
-
- if (my_command->argc < 4)
- syntax_error(source, lineno, my_command->line, my_command->argv[0],
- "missing arguments", NULL, -1);
-
- if (my_command->argc == 4 || /* uniform without/with "uniform"
- * keyword */
- (my_command->argc == 5 &&
- pg_strcasecmp(my_command->argv[4], "uniform") == 0))
- {
- /* nothing to do */
- }
- else if ( /* argc >= 5 */
- (pg_strcasecmp(my_command->argv[4], "gaussian") == 0) ||
- (pg_strcasecmp(my_command->argv[4], "exponential") == 0))
- {
- if (my_command->argc < 6)
- syntax_error(source, lineno, my_command->line, my_command->argv[0],
- "missing parameter", NULL, -1);
- else if (my_command->argc > 6)
- syntax_error(source, lineno, my_command->line, my_command->argv[0],
- "too many arguments", NULL,
- offsets[6] - start_offset);
- }
- else /* unrecognized distribution argument */
- syntax_error(source, lineno, my_command->line, my_command->argv[0],
- "unexpected argument", my_command->argv[4],
- offsets[4] - start_offset);
+ syntax_error(source, lineno, my_command->line, my_command->argv[0],
+ "\\setrandom is not supported anymore, "
+ "use \\set with the random() function\n", NULL, -1);
}
else if (pg_strcasecmp(my_command->argv[0], "sleep") == 0)
{
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers