mirror of
https://github.com/postgres/postgres.git
synced 2025-04-25 21:42:33 +03:00
pgbench: Support double constants and functions.
The new functions are pi(), random(), random_exponential(), random_gaussian(), and sqrt(). I was worried that this would be slower than before, but, if anything, it actually turns out to be slightly faster, because we now express the built-in pgbench scripts using fewer lines; each \setrandom can be merged into a subsequent \set. Fabien Coelho
This commit is contained in:
parent
9bd61311bd
commit
86c43f4e22
@ -815,9 +815,10 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
|
|||||||
|
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Sets variable <replaceable>varname</> to an integer value calculated
|
Sets variable <replaceable>varname</> to a value calculated
|
||||||
from <replaceable>expression</>.
|
from <replaceable>expression</>.
|
||||||
The expression may contain integer constants such as <literal>5432</>,
|
The expression may contain integer constants such as <literal>5432</>,
|
||||||
|
double constants such as <literal>3.14159</>,
|
||||||
references to variables <literal>:</><replaceable>variablename</>,
|
references to variables <literal>:</><replaceable>variablename</>,
|
||||||
unary operators (<literal>+</>, <literal>-</>) and binary operators
|
unary operators (<literal>+</>, <literal>-</>) and binary operators
|
||||||
(<literal>+</>, <literal>-</>, <literal>*</>, <literal>/</>,
|
(<literal>+</>, <literal>-</>, <literal>*</>, <literal>/</>,
|
||||||
@ -830,7 +831,7 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
|
|||||||
Examples:
|
Examples:
|
||||||
<programlisting>
|
<programlisting>
|
||||||
\set ntellers 10 * :scale
|
\set ntellers 10 * :scale
|
||||||
\set aid (1021 * :aid) % (100000 * :scale) + 1
|
\set aid (1021 * random(1, 100000 * :scale)) % (100000 * :scale) + 1
|
||||||
</programlisting></para>
|
</programlisting></para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -850,66 +851,35 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
By default, or when <literal>uniform</> is specified, all values in the
|
<itemizedlist>
|
||||||
range are drawn with equal probability. Specifying <literal>gaussian</>
|
<listitem>
|
||||||
or <literal>exponential</> options modifies this behavior; each
|
<para>
|
||||||
requires a mandatory parameter which determines the precise shape of the
|
<literal>\setrandom n 1 10</> or <literal>\setrandom n 1 10 uniform</>
|
||||||
distribution.
|
is equivalent to <literal>\set n random(1, 10)</> and uses a uniform
|
||||||
</para>
|
distribution.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
|
||||||
<para>
|
<listitem>
|
||||||
For a Gaussian distribution, the interval is mapped onto a standard
|
<para>
|
||||||
normal distribution (the classical bell-shaped Gaussian curve) truncated
|
<literal>\setrandom n 1 10 exponential 3.0</> is equivalent to
|
||||||
at <literal>-parameter</> on the left and <literal>+parameter</>
|
<literal>\set n random_exponential(1, 10, 3.0)</> and uses an
|
||||||
on the right.
|
exponential distribution.
|
||||||
Values in the middle of the interval are more likely to be drawn.
|
</para>
|
||||||
To be precise, if <literal>PHI(x)</> is the cumulative distribution
|
</listitem>
|
||||||
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>
|
|
||||||
|
|
||||||
<para>
|
<listitem>
|
||||||
For an exponential distribution, <replaceable>parameter</>
|
<para>
|
||||||
controls the distribution by truncating a quickly-decreasing
|
<literal>\setrandom n 1 10 gaussian 2.0</> is equivalent to
|
||||||
exponential distribution at <replaceable>parameter</>, and then
|
<literal>\set n random_gaussian(1, 10, 2.0)</>, and uses a gaussian
|
||||||
projecting onto integers between the bounds.
|
distribution.
|
||||||
To be precise, with
|
</para>
|
||||||
<literallayout>
|
</listitem>
|
||||||
f(x) = exp(-parameter * (x - min) / (max - min + 1)) / (1.0 - exp(-parameter))
|
</itemizedlist>
|
||||||
</literallayout>
|
|
||||||
Then value <replaceable>i</> between <replaceable>min</> and
|
See the documentation of these functions below for further information
|
||||||
<replaceable>max</> inclusive is drawn with probability:
|
about the precise shape of these distributions, depending on the value
|
||||||
<literal>f(x) - f(x + 1)</>.
|
of the parameter.
|
||||||
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.
|
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -990,34 +960,6 @@ f(x) = exp(-parameter * (x - min) / (max - min + 1)) / (1.0 - exp(-parameter))
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
</variablelist>
|
</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>
|
||||||
|
|
||||||
<refsect2 id="pgbench-builtin-functions">
|
<refsect2 id="pgbench-builtin-functions">
|
||||||
@ -1046,7 +988,7 @@ END;
|
|||||||
<row>
|
<row>
|
||||||
<entry><literal><function>abs(<replaceable>a</>)</></></>
|
<entry><literal><function>abs(<replaceable>a</>)</></></>
|
||||||
<entry>same as <replaceable>a</></>
|
<entry>same as <replaceable>a</></>
|
||||||
<entry>integer value</>
|
<entry>integer or double absolute value</>
|
||||||
<entry><literal>abs(-17)</></>
|
<entry><literal>abs(-17)</></>
|
||||||
<entry><literal>17</></>
|
<entry><literal>17</></>
|
||||||
</row>
|
</row>
|
||||||
@ -1054,8 +996,22 @@ END;
|
|||||||
<entry><literal><function>debug(<replaceable>a</>)</></></>
|
<entry><literal><function>debug(<replaceable>a</>)</></></>
|
||||||
<entry>same as <replaceable>a</> </>
|
<entry>same as <replaceable>a</> </>
|
||||||
<entry>print to <systemitem>stderr</systemitem> the given argument</>
|
<entry>print to <systemitem>stderr</systemitem> the given argument</>
|
||||||
<entry><literal>debug(5432)</></>
|
<entry><literal>debug(5432.1)</></>
|
||||||
<entry><literal>5432</></>
|
<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>
|
||||||
<row>
|
<row>
|
||||||
<entry><literal><function>max(<replaceable>i</> [, <replaceable>...</> ] )</></></>
|
<entry><literal><function>max(<replaceable>i</> [, <replaceable>...</> ] )</></></>
|
||||||
@ -1071,9 +1027,143 @@ END;
|
|||||||
<entry><literal>min(5, 4, 3, 2)</></>
|
<entry><literal>min(5, 4, 3, 2)</></>
|
||||||
<entry><literal>2</></>
|
<entry><literal>2</></>
|
||||||
</row>
|
</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>
|
</tbody>
|
||||||
</tgroup>
|
</tgroup>
|
||||||
</table>
|
</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>
|
||||||
|
|
||||||
<refsect2>
|
<refsect2>
|
||||||
@ -1223,13 +1313,10 @@ tps = 618.764555 (including connections establishing)
|
|||||||
tps = 622.977698 (excluding connections establishing)
|
tps = 622.977698 (excluding connections establishing)
|
||||||
script statistics:
|
script statistics:
|
||||||
- statement latencies in milliseconds:
|
- statement latencies in milliseconds:
|
||||||
0.004386 \set nbranches 1 * :scale
|
0.002522 \set aid random(1, 100000 * :scale)
|
||||||
0.001343 \set ntellers 10 * :scale
|
0.005459 \set bid random(1, 1 * :scale)
|
||||||
0.001212 \set naccounts 100000 * :scale
|
0.002348 \set tid random(1, 10 * :scale)
|
||||||
0.001310 \setrandom aid 1 :naccounts
|
0.001078 \set delta random(-5000, 5000)
|
||||||
0.001073 \setrandom bid 1 :nbranches
|
|
||||||
0.001005 \setrandom tid 1 :ntellers
|
|
||||||
0.001078 \setrandom delta -5000 5000
|
|
||||||
0.326152 BEGIN;
|
0.326152 BEGIN;
|
||||||
0.603376 UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
|
0.603376 UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
|
||||||
0.454643 SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
|
0.454643 SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
|
||||||
|
@ -20,6 +20,7 @@ PgBenchExpr *expr_parse_result;
|
|||||||
|
|
||||||
static PgBenchExprList *make_elist(PgBenchExpr *exp, PgBenchExprList *list);
|
static PgBenchExprList *make_elist(PgBenchExpr *exp, PgBenchExprList *list);
|
||||||
static PgBenchExpr *make_integer_constant(int64 ival);
|
static PgBenchExpr *make_integer_constant(int64 ival);
|
||||||
|
static PgBenchExpr *make_double_constant(double dval);
|
||||||
static PgBenchExpr *make_variable(char *varname);
|
static PgBenchExpr *make_variable(char *varname);
|
||||||
static PgBenchExpr *make_op(yyscan_t yyscanner, const char *operator,
|
static PgBenchExpr *make_op(yyscan_t yyscanner, const char *operator,
|
||||||
PgBenchExpr *lexpr, PgBenchExpr *rexpr);
|
PgBenchExpr *lexpr, PgBenchExpr *rexpr);
|
||||||
@ -38,6 +39,7 @@ static PgBenchExpr *make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList *
|
|||||||
%union
|
%union
|
||||||
{
|
{
|
||||||
int64 ival;
|
int64 ival;
|
||||||
|
double dval;
|
||||||
char *str;
|
char *str;
|
||||||
PgBenchExpr *expr;
|
PgBenchExpr *expr;
|
||||||
PgBenchExprList *elist;
|
PgBenchExprList *elist;
|
||||||
@ -46,9 +48,10 @@ static PgBenchExpr *make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList *
|
|||||||
%type <elist> elist
|
%type <elist> elist
|
||||||
%type <expr> expr
|
%type <expr> expr
|
||||||
%type <ival> INTEGER function
|
%type <ival> INTEGER function
|
||||||
|
%type <dval> DOUBLE
|
||||||
%type <str> VARIABLE FUNCTION
|
%type <str> VARIABLE FUNCTION
|
||||||
|
|
||||||
%token INTEGER VARIABLE FUNCTION
|
%token INTEGER DOUBLE VARIABLE FUNCTION
|
||||||
|
|
||||||
/* Precedence: lowest to highest */
|
/* Precedence: lowest to highest */
|
||||||
%left '+' '-'
|
%left '+' '-'
|
||||||
@ -74,6 +77,7 @@ expr: '(' expr ')' { $$ = $2; }
|
|||||||
| expr '/' expr { $$ = make_op(yyscanner, "/", $1, $3); }
|
| expr '/' expr { $$ = make_op(yyscanner, "/", $1, $3); }
|
||||||
| expr '%' expr { $$ = make_op(yyscanner, "%", $1, $3); }
|
| expr '%' expr { $$ = make_op(yyscanner, "%", $1, $3); }
|
||||||
| INTEGER { $$ = make_integer_constant($1); }
|
| INTEGER { $$ = make_integer_constant($1); }
|
||||||
|
| DOUBLE { $$ = make_double_constant($1); }
|
||||||
| VARIABLE { $$ = make_variable($1); }
|
| VARIABLE { $$ = make_variable($1); }
|
||||||
| function '(' elist ')' { $$ = make_func(yyscanner, $1, $3); }
|
| function '(' elist ')' { $$ = make_func(yyscanner, $1, $3); }
|
||||||
;
|
;
|
||||||
@ -88,8 +92,20 @@ make_integer_constant(int64 ival)
|
|||||||
{
|
{
|
||||||
PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
|
PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
|
||||||
|
|
||||||
expr->etype = ENODE_INTEGER_CONSTANT;
|
expr->etype = ENODE_CONSTANT;
|
||||||
expr->u.integer_constant.ival = ival;
|
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;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,6 +170,27 @@ static const struct
|
|||||||
{
|
{
|
||||||
"debug", 1, PGBENCH_DEBUG
|
"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 */
|
/* keep as last array element */
|
||||||
{
|
{
|
||||||
NULL, 0, 0
|
NULL, 0, 0
|
||||||
|
@ -125,6 +125,11 @@ newline [\n]
|
|||||||
yylval->ival = strtoint64(yytext);
|
yylval->ival = strtoint64(yytext);
|
||||||
return INTEGER;
|
return INTEGER;
|
||||||
}
|
}
|
||||||
|
{digit}+(\.{digit}*)?([eE][-+]?{digit}+)? {
|
||||||
|
yycolumn += yyleng;
|
||||||
|
yylval->dval = atof(yytext);
|
||||||
|
return DOUBLE;
|
||||||
|
}
|
||||||
{alpha}{alnum}* {
|
{alpha}{alnum}* {
|
||||||
yylval->str = pg_strdup(yytext);
|
yylval->str = pg_strdup(yytext);
|
||||||
return FUNCTION;
|
return FUNCTION;
|
||||||
|
@ -328,13 +328,10 @@ static const BuiltinScript builtin_script[] =
|
|||||||
{
|
{
|
||||||
"tpcb-like",
|
"tpcb-like",
|
||||||
"<builtin: TPC-B (sort of)>",
|
"<builtin: TPC-B (sort of)>",
|
||||||
"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
|
"\\set aid random(1, " CppAsString2(naccounts) " * :scale)\n"
|
||||||
"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
|
"\\set bid random(1, " CppAsString2(nbranches) " * :scale)\n"
|
||||||
"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
|
"\\set tid random(1, " CppAsString2(ntellers) " * :scale)\n"
|
||||||
"\\setrandom aid 1 :naccounts\n"
|
"\\set delta random(-5000, 5000)\n"
|
||||||
"\\setrandom bid 1 :nbranches\n"
|
|
||||||
"\\setrandom tid 1 :ntellers\n"
|
|
||||||
"\\setrandom delta -5000 5000\n"
|
|
||||||
"BEGIN;\n"
|
"BEGIN;\n"
|
||||||
"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
|
"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
|
||||||
"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
|
"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
|
||||||
@ -346,13 +343,10 @@ static const BuiltinScript builtin_script[] =
|
|||||||
{
|
{
|
||||||
"simple-update",
|
"simple-update",
|
||||||
"<builtin: simple update>",
|
"<builtin: simple update>",
|
||||||
"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
|
"\\set aid random(1, " CppAsString2(naccounts) " * :scale)\n"
|
||||||
"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
|
"\\set bid random(1, " CppAsString2(nbranches) " * :scale)\n"
|
||||||
"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
|
"\\set tid random(1, " CppAsString2(ntellers) " * :scale)\n"
|
||||||
"\\setrandom aid 1 :naccounts\n"
|
"\\set delta random(-5000, 5000)\n"
|
||||||
"\\setrandom bid 1 :nbranches\n"
|
|
||||||
"\\setrandom tid 1 :ntellers\n"
|
|
||||||
"\\setrandom delta -5000 5000\n"
|
|
||||||
"BEGIN;\n"
|
"BEGIN;\n"
|
||||||
"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
|
"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
|
||||||
"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
|
"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
|
||||||
@ -362,15 +356,14 @@ static const BuiltinScript builtin_script[] =
|
|||||||
{
|
{
|
||||||
"select-only",
|
"select-only",
|
||||||
"<builtin: select only>",
|
"<builtin: select only>",
|
||||||
"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
|
"\\set aid random(1, " CppAsString2(naccounts) " * :scale)\n"
|
||||||
"\\setrandom aid 1 :naccounts\n"
|
|
||||||
"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
|
"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Function prototypes */
|
/* 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,
|
static void doLog(TState *thread, CState *st, instr_time *now,
|
||||||
StatsData *agg, bool skipped, double latency, double lag);
|
StatsData *agg, bool skipped, double latency, double lag);
|
||||||
static void processXactStats(TState *thread, CState *st, instr_time *now,
|
static void processXactStats(TState *thread, CState *st, instr_time *now,
|
||||||
@ -446,6 +439,33 @@ usage(void)
|
|||||||
progname, progname);
|
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
|
* strtoint64 -- convert a string to 64-bit integer
|
||||||
*
|
*
|
||||||
@ -542,6 +562,7 @@ getExponentialRand(TState *thread, int64 min, int64 max, double parameter)
|
|||||||
uniform,
|
uniform,
|
||||||
rand;
|
rand;
|
||||||
|
|
||||||
|
/* abort if wrong parameter, but must really be checked beforehand */
|
||||||
Assert(parameter > 0.0);
|
Assert(parameter > 0.0);
|
||||||
cut = exp(-parameter);
|
cut = exp(-parameter);
|
||||||
/* erand in [0, 1), uniform in (0, 1] */
|
/* erand in [0, 1), uniform in (0, 1] */
|
||||||
@ -563,6 +584,9 @@ getGaussianRand(TState *thread, int64 min, int64 max, double parameter)
|
|||||||
double stdev;
|
double stdev;
|
||||||
double rand;
|
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 <
|
* Get user specified random number from this loop, with -parameter <
|
||||||
* stdev <= parameter
|
* stdev <= parameter
|
||||||
@ -1006,6 +1030,62 @@ getQueryParams(CState *st, const Command *command, const char **params)
|
|||||||
params[i] = getVariable(st, command->argv[i + 1]);
|
params[i] = getVariable(st, command->argv[i + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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 */
|
/* maximum number of function arguments */
|
||||||
#define MAX_FARGS 16
|
#define MAX_FARGS 16
|
||||||
|
|
||||||
@ -1013,16 +1093,16 @@ getQueryParams(CState *st, const Command *command, const char **params)
|
|||||||
* Recursive evaluation of functions
|
* Recursive evaluation of functions
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
evalFunc(CState *st,
|
evalFunc(TState *thread, CState *st,
|
||||||
PgBenchFunction func, PgBenchExprLink *args, int64 *retval)
|
PgBenchFunction func, PgBenchExprLink *args, PgBenchValue *retval)
|
||||||
{
|
{
|
||||||
/* evaluate all function arguments */
|
/* evaluate all function arguments */
|
||||||
int nargs = 0;
|
int nargs = 0;
|
||||||
int64 iargs[MAX_FARGS];
|
PgBenchValue vargs[MAX_FARGS];
|
||||||
PgBenchExprLink *l = args;
|
PgBenchExprLink *l = args;
|
||||||
|
|
||||||
for (nargs = 0; nargs < MAX_FARGS && l != NULL; nargs++, l = l->next)
|
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;
|
return false;
|
||||||
|
|
||||||
if (l != NULL)
|
if (l != NULL)
|
||||||
@ -1035,104 +1115,206 @@ evalFunc(CState *st,
|
|||||||
/* then evaluate function */
|
/* then evaluate function */
|
||||||
switch (func)
|
switch (func)
|
||||||
{
|
{
|
||||||
|
/* overloaded operators */
|
||||||
case PGBENCH_ADD:
|
case PGBENCH_ADD:
|
||||||
case PGBENCH_SUB:
|
case PGBENCH_SUB:
|
||||||
case PGBENCH_MUL:
|
case PGBENCH_MUL:
|
||||||
case PGBENCH_DIV:
|
case PGBENCH_DIV:
|
||||||
case PGBENCH_MOD:
|
case PGBENCH_MOD:
|
||||||
{
|
{
|
||||||
int64 lval = iargs[0],
|
PgBenchValue *lval = &vargs[0],
|
||||||
rval = iargs[1];
|
*rval = &vargs[1];
|
||||||
|
|
||||||
Assert(nargs == 2);
|
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:
|
double ld, rd;
|
||||||
*retval = lval + rval;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case PGBENCH_SUB:
|
if (!coerceToDouble(lval, &ld) ||
|
||||||
*retval = lval - rval;
|
!coerceToDouble(rval, &rd))
|
||||||
return true;
|
return false;
|
||||||
|
|
||||||
case PGBENCH_MUL:
|
switch (func)
|
||||||
*retval = lval * rval;
|
{
|
||||||
return true;
|
case PGBENCH_ADD:
|
||||||
|
setDoubleValue(retval, ld + rd);
|
||||||
|
return true;
|
||||||
|
|
||||||
case PGBENCH_DIV:
|
case PGBENCH_SUB:
|
||||||
case PGBENCH_MOD:
|
setDoubleValue(retval, ld - rd);
|
||||||
if (rval == 0)
|
return true;
|
||||||
{
|
|
||||||
fprintf(stderr, "division by zero\n");
|
case PGBENCH_MUL:
|
||||||
return false;
|
setDoubleValue(retval, ld * rd);
|
||||||
}
|
return true;
|
||||||
/* special handling of -1 divisor */
|
|
||||||
if (rval == -1)
|
case PGBENCH_DIV:
|
||||||
{
|
setDoubleValue(retval, ld / rd);
|
||||||
if (func == PGBENCH_DIV)
|
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)
|
||||||
{
|
{
|
||||||
/* overflow check (needed for INT64_MIN) */
|
fprintf(stderr, "division by zero\n");
|
||||||
if (lval == PG_INT64_MIN)
|
return false;
|
||||||
|
}
|
||||||
|
/* special handling of -1 divisor */
|
||||||
|
if (ri == -1)
|
||||||
|
{
|
||||||
|
if (func == PGBENCH_DIV)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "bigint out of range\n");
|
/* overflow check (needed for INT64_MIN) */
|
||||||
return false;
|
if (li == PG_INT64_MIN)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "bigint out of range\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
setIntValue(retval, - li);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
*retval = -lval;
|
setIntValue(retval, 0);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else
|
/* else divisor is not -1 */
|
||||||
*retval = 0;
|
if (func == PGBENCH_DIV)
|
||||||
return true;
|
setIntValue(retval, li / ri);
|
||||||
}
|
else /* func == PGBENCH_MOD */
|
||||||
/* divisor is not -1 */
|
setIntValue(retval, li % ri);
|
||||||
if (func == PGBENCH_DIV)
|
|
||||||
*retval = lval / rval;
|
|
||||||
else /* func == PGBENCH_MOD */
|
|
||||||
*retval = lval % rval;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
return true;
|
||||||
/* 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:
|
case PGBENCH_ABS:
|
||||||
{
|
{
|
||||||
|
PgBenchValue *varg = &vargs[0];
|
||||||
Assert(nargs == 1);
|
Assert(nargs == 1);
|
||||||
|
|
||||||
if (iargs[0] < 0)
|
if (varg->type == PGBT_INT)
|
||||||
*retval = -iargs[0];
|
{
|
||||||
|
int64 i = varg->u.ival;
|
||||||
|
setIntValue(retval, i < 0 ? -i : i);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
*retval = iargs[0];
|
{
|
||||||
|
double d = varg->u.dval;
|
||||||
|
Assert(varg->type == PGBT_DOUBLE);
|
||||||
|
setDoubleValue(retval, d < 0.0 ? -d: d);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case PGBENCH_DEBUG:
|
case PGBENCH_DEBUG:
|
||||||
{
|
{
|
||||||
|
PgBenchValue *varg = &vargs[0];
|
||||||
Assert(nargs == 1);
|
Assert(nargs == 1);
|
||||||
|
|
||||||
fprintf(stderr, "debug(script=%d,command=%d): " INT64_FORMAT "\n",
|
fprintf(stderr, "debug(script=%d,command=%d): ",
|
||||||
st->use_file, st->state + 1, iargs[0]);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 1 double argument */
|
||||||
|
case PGBENCH_DOUBLE:
|
||||||
|
case PGBENCH_SQRT:
|
||||||
|
{
|
||||||
|
double dval;
|
||||||
|
Assert(nargs == 1);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (!coerceToInt(&vargs[0], &ival))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
setIntValue(retval, ival);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* variable number of int arguments */
|
||||||
case PGBENCH_MIN:
|
case PGBENCH_MIN:
|
||||||
case PGBENCH_MAX:
|
case PGBENCH_MAX:
|
||||||
{
|
{
|
||||||
int64 extremum = iargs[0];
|
int64 extremum;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
Assert(nargs >= 1);
|
Assert(nargs >= 1);
|
||||||
|
|
||||||
|
if (!coerceToInt(&vargs[0], &extremum))
|
||||||
|
return false;
|
||||||
|
|
||||||
for (i = 1; i < nargs; i++)
|
for (i = 1; i < nargs; i++)
|
||||||
{
|
{
|
||||||
int64 ival = iargs[i];
|
int64 ival;
|
||||||
|
|
||||||
|
if (!coerceToInt(&vargs[i], &ival))
|
||||||
|
return false;
|
||||||
|
|
||||||
if (func == PGBENCH_MIN)
|
if (func == PGBENCH_MIN)
|
||||||
extremum = extremum < ival ? extremum : ival;
|
extremum = extremum < ival ? extremum : ival;
|
||||||
@ -1140,13 +1322,84 @@ evalFunc(CState *st,
|
|||||||
extremum = extremum > ival ? extremum : ival;
|
extremum = extremum > ival ? extremum : ival;
|
||||||
}
|
}
|
||||||
|
|
||||||
*retval = extremum;
|
setIntValue(retval, extremum);
|
||||||
return true;
|
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:
|
default:
|
||||||
fprintf(stderr, "unexpected function tag: %d\n", func);
|
/* cannot get here */
|
||||||
exit(1);
|
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.
|
* the value itself is returned through the retval pointer.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
evaluateExpr(CState *st, PgBenchExpr *expr, int64 *retval)
|
evaluateExpr(TState *thread, CState *st, PgBenchExpr *expr, PgBenchValue *retval)
|
||||||
{
|
{
|
||||||
switch (expr->etype)
|
switch (expr->etype)
|
||||||
{
|
{
|
||||||
case ENODE_INTEGER_CONSTANT:
|
case ENODE_CONSTANT:
|
||||||
{
|
{
|
||||||
*retval = expr->u.integer_constant.ival;
|
*retval = expr->u.constant;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1177,24 +1430,39 @@ evaluateExpr(CState *st, PgBenchExpr *expr, int64 *retval)
|
|||||||
expr->u.variable.varname);
|
expr->u.variable.varname);
|
||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case ENODE_FUNCTION:
|
case ENODE_FUNCTION:
|
||||||
return evalFunc(st,
|
return evalFunc(thread, st,
|
||||||
expr->u.function.function,
|
expr->u.function.function,
|
||||||
expr->u.function.args,
|
expr->u.function.args,
|
||||||
retval);
|
retval);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
/* internal error which should never occur */
|
||||||
fprintf(stderr, "unexpected enode type in evaluation: %d\n",
|
fprintf(stderr, "unexpected enode type in evaluation: %d\n",
|
||||||
expr->etype);
|
expr->etype);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "bad expression\n");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1673,6 +1941,10 @@ top:
|
|||||||
fprintf(stderr, "\n");
|
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)
|
if (pg_strcasecmp(argv[0], "setrandom") == 0)
|
||||||
{
|
{
|
||||||
char *var;
|
char *var;
|
||||||
@ -1814,15 +2086,21 @@ top:
|
|||||||
{
|
{
|
||||||
char res[64];
|
char res[64];
|
||||||
PgBenchExpr *expr = commands[st->state]->expr;
|
PgBenchExpr *expr = commands[st->state]->expr;
|
||||||
int64 result;
|
PgBenchValue result;
|
||||||
|
|
||||||
if (!evaluateExpr(st, expr, &result))
|
if (!evaluateExpr(thread, st, expr, &result))
|
||||||
{
|
{
|
||||||
st->ecnt++;
|
st->ecnt++;
|
||||||
return true;
|
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))
|
if (!putVariable(st, argv[0], argv[1], res))
|
||||||
{
|
{
|
||||||
|
@ -28,10 +28,31 @@
|
|||||||
*/
|
*/
|
||||||
union YYSTYPE;
|
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 */
|
/* Types of expression nodes */
|
||||||
typedef enum PgBenchExprType
|
typedef enum PgBenchExprType
|
||||||
{
|
{
|
||||||
ENODE_INTEGER_CONSTANT,
|
ENODE_CONSTANT,
|
||||||
ENODE_VARIABLE,
|
ENODE_VARIABLE,
|
||||||
ENODE_FUNCTION
|
ENODE_FUNCTION
|
||||||
} PgBenchExprType;
|
} PgBenchExprType;
|
||||||
@ -48,6 +69,13 @@ typedef enum PgBenchFunction
|
|||||||
PGBENCH_ABS,
|
PGBENCH_ABS,
|
||||||
PGBENCH_MIN,
|
PGBENCH_MIN,
|
||||||
PGBENCH_MAX,
|
PGBENCH_MAX,
|
||||||
|
PGBENCH_INT,
|
||||||
|
PGBENCH_DOUBLE,
|
||||||
|
PGBENCH_PI,
|
||||||
|
PGBENCH_SQRT,
|
||||||
|
PGBENCH_RANDOM,
|
||||||
|
PGBENCH_RANDOM_GAUSSIAN,
|
||||||
|
PGBENCH_RANDOM_EXPONENTIAL
|
||||||
} PgBenchFunction;
|
} PgBenchFunction;
|
||||||
|
|
||||||
typedef struct PgBenchExpr PgBenchExpr;
|
typedef struct PgBenchExpr PgBenchExpr;
|
||||||
@ -59,10 +87,7 @@ struct PgBenchExpr
|
|||||||
PgBenchExprType etype;
|
PgBenchExprType etype;
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
struct
|
PgBenchValue constant;
|
||||||
{
|
|
||||||
int64 ival;
|
|
||||||
} integer_constant;
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
char *varname;
|
char *varname;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user