Commit bc7fa0c1 authored by Teodor Sigaev's avatar Teodor Sigaev

Improve scripting language in pgbench

Added:
 - variable now might contain integer, double, boolean and null values
 - functions ln, exp
 - logical AND/OR/NOT
 - bitwise AND/OR/NOT/XOR
 - bit right/left shift
 - comparison operators
 - IS [NOT] (NULL|TRUE|FALSE)
 - conditional choice (in form of when/case/then)

New operations and functions allow to implement more complicated test scenario.

Author: Fabien Coelho with minor editorization by me
Reviewed-By: Pavel Stehule, Jeevan Ladhe, me
Discussion: https://www.postgresql.org/message-id/flat/alpine.DEB.2.10.1604030742390.31618@sto
parent 63008b19
......@@ -904,14 +904,32 @@ pgbench <optional> <replaceable>options</replaceable> </optional> <replaceable>d
<para>
Sets variable <replaceable>varname</replaceable> to a value calculated
from <replaceable>expression</replaceable>.
The expression may contain integer constants such as <literal>5432</literal>,
The expression may contain the <literal>NULL</literal> constant,
boolean constants <literal>TRUE</literal> and <literal>FALSE</literal>,
integer constants such as <literal>5432</literal>,
double constants such as <literal>3.14159</literal>,
references to variables <literal>:</literal><replaceable>variablename</replaceable>,
unary operators (<literal>+</literal>, <literal>-</literal>) and binary operators
(<literal>+</literal>, <literal>-</literal>, <literal>*</literal>, <literal>/</literal>,
<literal>%</literal>) with their usual precedence and associativity,
<link linkend="pgbench-builtin-functions">function calls</link>, and
parentheses.
<link linkend="pgbench-builtin-operators">operators</link>
with their usual SQL precedence and associativity,
<link linkend="pgbench-builtin-functions">function calls</link>,
SQL <link linkend="functions-case"><token>CASE</token> generic conditional
expressions</link> and parentheses.
</para>
<para>
Functions and most operators return <literal>NULL</literal> on
<literal>NULL</literal> input.
</para>
<para>
For conditional purposes, non zero numerical values are
<literal>TRUE</literal>, zero numerical values and <literal>NULL</literal>
are <literal>FALSE</literal>.
</para>
<para>
When no final <token>ELSE</token> clause is provided to a
<token>CASE</token>, the default value is <literal>NULL</literal>.
</para>
<para>
......@@ -920,6 +938,7 @@ pgbench <optional> <replaceable>options</replaceable> </optional> <replaceable>d
\set ntellers 10 * :scale
\set aid (1021 * random(1, 100000 * :scale)) % \
(100000 * :scale) + 1
\set divx CASE WHEN :x &lt;&gt; 0 THEN :y/:x ELSE NULL END
</programlisting></para>
</listitem>
</varlistentry>
......@@ -996,6 +1015,177 @@ pgbench <optional> <replaceable>options</replaceable> </optional> <replaceable>d
</variablelist>
</refsect2>
<refsect2 id="pgbench-builtin-operators">
<title>Built-In Operators</title>
<para>
The arithmetic, bitwise, comparison and logical operators listed in
<xref linkend="pgbench-operators"/> are built into <application>pgbench</application>
and may be used in expressions appearing in
<link linkend="pgbench-metacommand-set"><literal>\set</literal></link>.
</para>
<table id="pgbench-operators">
<title>pgbench Operators by increasing precedence</title>
<tgroup cols="4">
<thead>
<row>
<entry>Operator</entry>
<entry>Description</entry>
<entry>Example</entry>
<entry>Result</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>OR</literal></entry>
<entry>logical or</entry>
<entry><literal>5 or 0</literal></entry>
<entry><literal>TRUE</literal></entry>
</row>
<row>
<entry><literal>AND</literal></entry>
<entry>logical and</entry>
<entry><literal>3 and 0</literal></entry>
<entry><literal>FALSE</literal></entry>
</row>
<row>
<entry><literal>NOT</literal></entry>
<entry>logical not</entry>
<entry><literal>not false</literal></entry>
<entry><literal>TRUE</literal></entry>
</row>
<row>
<entry><literal>IS [NOT] (NULL|TRUE|FALSE)</literal></entry>
<entry>value tests</entry>
<entry><literal>1 is null</literal></entry>
<entry><literal>FALSE</literal></entry>
</row>
<row>
<entry><literal>ISNULL|NOTNULL</literal></entry>
<entry>null tests</entry>
<entry><literal>1 notnull</literal></entry>
<entry><literal>TRUE</literal></entry>
</row>
<row>
<entry><literal>=</literal></entry>
<entry>is equal</entry>
<entry><literal>5 = 4</literal></entry>
<entry><literal>FALSE</literal></entry>
</row>
<row>
<entry><literal>&lt;&gt;</literal></entry>
<entry>is not equal</entry>
<entry><literal>5 &lt;&gt; 4</literal></entry>
<entry><literal>TRUE</literal></entry>
</row>
<row>
<entry><literal>!=</literal></entry>
<entry>is not equal</entry>
<entry><literal>5 != 5</literal></entry>
<entry><literal>FALSE</literal></entry>
</row>
<row>
<entry><literal>&lt;</literal></entry>
<entry>lower than</entry>
<entry><literal>5 &lt; 4</literal></entry>
<entry><literal>FALSE</literal></entry>
</row>
<row>
<entry><literal>&lt;=</literal></entry>
<entry>lower or equal</entry>
<entry><literal>5 &lt;= 4</literal></entry>
<entry><literal>FALSE</literal></entry>
</row>
<row>
<entry><literal>&gt;</literal></entry>
<entry>greater than</entry>
<entry><literal>5 &gt; 4</literal></entry>
<entry><literal>TRUE</literal></entry>
</row>
<row>
<entry><literal>&gt;=</literal></entry>
<entry>greater or equal</entry>
<entry><literal>5 &gt;= 4</literal></entry>
<entry><literal>TRUE</literal></entry>
</row>
<row>
<entry><literal>|</literal></entry>
<entry>integer bitwise OR</entry>
<entry><literal>1 | 2</literal></entry>
<entry><literal>3</literal></entry>
</row>
<row>
<entry><literal>#</literal></entry>
<entry>integer bitwise XOR</entry>
<entry><literal>1 # 3</literal></entry>
<entry><literal>2</literal></entry>
</row>
<row>
<entry><literal>&amp;</literal></entry>
<entry>integer bitwise AND</entry>
<entry><literal>1 &amp; 3</literal></entry>
<entry><literal>1</literal></entry>
</row>
<row>
<entry><literal>~</literal></entry>
<entry>integer bitwise NOT</entry>
<entry><literal>~ 1</literal></entry>
<entry><literal>-2</literal></entry>
</row>
<row>
<entry><literal>&lt;&lt;</literal></entry>
<entry>integer bitwise shift left</entry>
<entry><literal>1 &lt;&lt; 2</literal></entry>
<entry><literal>4</literal></entry>
</row>
<row>
<entry><literal>&gt;&gt;</literal></entry>
<entry>integer bitwise shift right</entry>
<entry><literal>8 &gt;&gt; 2</literal></entry>
<entry><literal>2</literal></entry>
</row>
<row>
<entry><literal>+</literal></entry>
<entry>addition</entry>
<entry><literal>5 + 4</literal></entry>
<entry><literal>9</literal></entry>
</row>
<row>
<entry><literal>-</literal></entry>
<entry>substraction</entry>
<entry><literal>3 - 2.0</literal></entry>
<entry><literal>1.0</literal></entry>
</row>
<row>
<entry><literal>*</literal></entry>
<entry>multiplication</entry>
<entry><literal>5 * 4</literal></entry>
<entry><literal>20</literal></entry>
</row>
<row>
<entry><literal>/</literal></entry>
<entry>division (integer truncates the results)</entry>
<entry><literal>5 / 3</literal></entry>
<entry><literal>1</literal></entry>
</row>
<row>
<entry><literal>%</literal></entry>
<entry>modulo</entry>
<entry><literal>3 % 2</literal></entry>
<entry><literal>1</literal></entry>
</row>
<row>
<entry><literal>-</literal></entry>
<entry>opposite</entry>
<entry><literal>- 2.0</literal></entry>
<entry><literal>-2.0</literal></entry>
</row>
</tbody>
</tgroup>
</table>
</refsect2>
<refsect2 id="pgbench-builtin-functions">
<title>Built-In Functions</title>
......@@ -1041,6 +1231,13 @@ pgbench <optional> <replaceable>options</replaceable> </optional> <replaceable>d
<entry><literal>double(5432)</literal></entry>
<entry><literal>5432.0</literal></entry>
</row>
<row>
<entry><literal><function>exp(<replaceable>x</replaceable>)</function></literal></entry>
<entry>double</entry>
<entry>exponential</entry>
<entry><literal>exp(1.0)</literal></entry>
<entry><literal>2.718281828459045</literal></entry>
</row>
<row>
<entry><literal><function>greatest(<replaceable>a</replaceable> [, <replaceable>...</replaceable> ] )</function></literal></entry>
<entry>double if any <replaceable>a</replaceable> is double, else integer</entry>
......@@ -1062,6 +1259,20 @@ pgbench <optional> <replaceable>options</replaceable> </optional> <replaceable>d
<entry><literal>least(5, 4, 3, 2.1)</literal></entry>
<entry><literal>2.1</literal></entry>
</row>
<row>
<entry><literal><function>ln(<replaceable>x</replaceable>)</function></literal></entry>
<entry>double</entry>
<entry>natural logarithm</entry>
<entry><literal>ln(2.718281828459045)</literal></entry>
<entry><literal>1.0</literal></entry>
</row>
<row>
<entry><literal><function>mod(<replaceable>i</replaceable>, <replaceable>bj</replaceable>)</function></literal></entry>
<entry>integer</entry>
<entry>modulo</entry>
<entry><literal>mod(54, 32)</literal></entry>
<entry><literal>22</literal></entry>
</row>
<row>
<entry><literal><function>pi()</function></literal></entry>
<entry>double</entry>
......
......@@ -19,13 +19,17 @@
PgBenchExpr *expr_parse_result;
static PgBenchExprList *make_elist(PgBenchExpr *exp, PgBenchExprList *list);
static PgBenchExpr *make_null_constant(void);
static PgBenchExpr *make_boolean_constant(bool bval);
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);
static PgBenchExpr *make_uop(yyscan_t yyscanner, const char *operator, PgBenchExpr *expr);
static int find_func(yyscan_t yyscanner, const char *fname);
static PgBenchExpr *make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList *args);
static PgBenchExpr *make_case(yyscan_t yyscanner, PgBenchExprList *when_then_list, PgBenchExpr *else_part);
%}
......@@ -40,53 +44,126 @@ static PgBenchExpr *make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList *
{
int64 ival;
double dval;
bool bval;
char *str;
PgBenchExpr *expr;
PgBenchExprList *elist;
}
%type <elist> elist
%type <expr> expr
%type <elist> elist when_then_list
%type <expr> expr case_control
%type <ival> INTEGER_CONST function
%type <dval> DOUBLE_CONST
%type <bval> BOOLEAN_CONST
%type <str> VARIABLE FUNCTION
%token INTEGER_CONST DOUBLE_CONST VARIABLE FUNCTION
/* Precedence: lowest to highest */
%token NULL_CONST INTEGER_CONST DOUBLE_CONST BOOLEAN_CONST VARIABLE FUNCTION
%token AND_OP OR_OP NOT_OP NE_OP LE_OP GE_OP LS_OP RS_OP IS_OP
%token CASE_KW WHEN_KW THEN_KW ELSE_KW END_KW
/* Precedence: lowest to highest, taken from postgres SQL parser */
%left OR_OP
%left AND_OP
%right NOT_OP
%nonassoc IS_OP ISNULL_OP NOTNULL_OP
%nonassoc '<' '>' '=' LE_OP GE_OP NE_OP
%left '|' '#' '&' LS_OP RS_OP '~'
%left '+' '-'
%left '*' '/' '%'
%right UMINUS
%right UNARY
%%
result: expr { expr_parse_result = $1; }
elist: { $$ = NULL; }
| expr { $$ = make_elist($1, NULL); }
elist: { $$ = NULL; }
| expr { $$ = make_elist($1, NULL); }
| elist ',' expr { $$ = make_elist($3, $1); }
;
expr: '(' expr ')' { $$ = $2; }
| '+' expr %prec UMINUS { $$ = $2; }
| '-' expr %prec UMINUS { $$ = make_op(yyscanner, "-",
| '+' expr %prec UNARY { $$ = $2; }
/* unary minus "-x" implemented as "0 - x" */
| '-' expr %prec UNARY { $$ = make_op(yyscanner, "-",
make_integer_constant(0), $2); }
/* binary ones complement "~x" implemented as 0xffff... xor x" */
| '~' expr { $$ = make_op(yyscanner, "#",
make_integer_constant(~INT64CONST(0)), $2); }
| NOT_OP expr { $$ = make_uop(yyscanner, "!not", $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); }
| expr '%' expr { $$ = make_op(yyscanner, "%", $1, $3); }
| expr '%' expr { $$ = make_op(yyscanner, "mod", $1, $3); }
| expr '<' expr { $$ = make_op(yyscanner, "<", $1, $3); }
| expr LE_OP expr { $$ = make_op(yyscanner, "<=", $1, $3); }
| expr '>' expr { $$ = make_op(yyscanner, "<", $3, $1); }
| expr GE_OP expr { $$ = make_op(yyscanner, "<=", $3, $1); }
| expr '=' expr { $$ = make_op(yyscanner, "=", $1, $3); }
| expr NE_OP 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); }
| expr LS_OP expr { $$ = make_op(yyscanner, "<<", $1, $3); }
| expr RS_OP expr { $$ = make_op(yyscanner, ">>", $1, $3); }
| expr AND_OP expr { $$ = make_op(yyscanner, "!and", $1, $3); }
| expr OR_OP expr { $$ = make_op(yyscanner, "!or", $1, $3); }
/* IS variants */
| expr ISNULL_OP { $$ = make_op(yyscanner, "!is", $1, make_null_constant()); }
| expr NOTNULL_OP {
$$ = make_uop(yyscanner, "!not",
make_op(yyscanner, "!is", $1, make_null_constant()));
}
| expr IS_OP NULL_CONST { $$ = make_op(yyscanner, "!is", $1, make_null_constant()); }
| expr IS_OP NOT_OP NULL_CONST
{
$$ = make_uop(yyscanner, "!not",
make_op(yyscanner, "!is", $1, make_null_constant()));
}
| expr IS_OP BOOLEAN_CONST
{
$$ = make_op(yyscanner, "!is", $1, make_boolean_constant($3));
}
| expr IS_OP NOT_OP BOOLEAN_CONST
{
$$ = make_uop(yyscanner, "!not",
make_op(yyscanner, "!is", $1, make_boolean_constant($4)));
}
/* constants */
| NULL_CONST { $$ = make_null_constant(); }
| BOOLEAN_CONST { $$ = make_boolean_constant($1); }
| INTEGER_CONST { $$ = make_integer_constant($1); }
| DOUBLE_CONST { $$ = make_double_constant($1); }
| VARIABLE { $$ = make_variable($1); }
/* misc */
| VARIABLE { $$ = make_variable($1); }
| function '(' elist ')' { $$ = make_func(yyscanner, $1, $3); }
| case_control { $$ = $1; }
;
when_then_list:
when_then_list WHEN_KW expr THEN_KW expr { $$ = make_elist($5, make_elist($3, $1)); }
| WHEN_KW expr THEN_KW expr { $$ = make_elist($4, make_elist($2, NULL)); }
case_control:
CASE_KW when_then_list END_KW { $$ = make_case(yyscanner, $2, make_null_constant()); }
| CASE_KW when_then_list ELSE_KW expr END_KW { $$ = make_case(yyscanner, $2, $4); }
function: FUNCTION { $$ = find_func(yyscanner, $1); pg_free($1); }
;
%%
static PgBenchExpr *
make_null_constant(void)
{
PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
expr->etype = ENODE_CONSTANT;
expr->u.constant.type = PGBT_NULL;
expr->u.constant.u.ival = 0;
return expr;
}
static PgBenchExpr *
make_integer_constant(int64 ival)
{
......@@ -109,6 +186,17 @@ make_double_constant(double dval)
return expr;
}
static PgBenchExpr *
make_boolean_constant(bool bval)
{
PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
expr->etype = ENODE_CONSTANT;
expr->u.constant.type = PGBT_BOOLEAN;
expr->u.constant.u.bval = bval;
return expr;
}
static PgBenchExpr *
make_variable(char *varname)
{
......@@ -119,6 +207,7 @@ make_variable(char *varname)
return expr;
}
/* binary operators */
static PgBenchExpr *
make_op(yyscan_t yyscanner, const char *operator,
PgBenchExpr *lexpr, PgBenchExpr *rexpr)
......@@ -127,11 +216,19 @@ make_op(yyscan_t yyscanner, const char *operator,
make_elist(rexpr, make_elist(lexpr, NULL)));
}
/* unary operator */
static PgBenchExpr *
make_uop(yyscan_t yyscanner, const char *operator, PgBenchExpr *expr)
{
return make_func(yyscanner, find_func(yyscanner, operator), make_elist(expr, NULL));
}
/*
* List of available functions:
* - fname: function name
* - fname: function name, "!..." for special internal functions
* - nargs: number of arguments
* -1 is a special value for least & greatest meaning #args >= 1
* -2 is for the "CASE WHEN ..." function, which has #args >= 3 and odd
* - tag: function identifier from PgBenchFunction enum
*/
static const struct
......@@ -155,7 +252,7 @@ static const struct
"/", 2, PGBENCH_DIV
},
{
"%", 2, PGBENCH_MOD
"mod", 2, PGBENCH_MOD
},
/* actual functions */
{
......@@ -176,6 +273,12 @@ static const struct
{
"sqrt", 1, PGBENCH_SQRT
},
{
"ln", 1, PGBENCH_LN
},
{
"exp", 1, PGBENCH_EXP
},
{
"int", 1, PGBENCH_INT
},
......@@ -200,6 +303,52 @@ static const struct
{
"power", 2, PGBENCH_POW
},
/* logical operators */
{
"!and", 2, PGBENCH_AND
},
{
"!or", 2, PGBENCH_OR
},
{
"!not", 1, PGBENCH_NOT
},
/* bitwise integer operators */
{
"&", 2, PGBENCH_BITAND
},
{
"|", 2, PGBENCH_BITOR
},
{
"#", 2, PGBENCH_BITXOR
},
{
"<<", 2, PGBENCH_LSHIFT
},
{
">>", 2, PGBENCH_RSHIFT
},
/* comparison operators */
{
"=", 2, PGBENCH_EQ
},
{
"<>", 2, PGBENCH_NE
},
{
"<=", 2, PGBENCH_LE
},
{
"<", 2, PGBENCH_LT
},
{
"!is", 2, PGBENCH_IS
},
/* "case when ... then ... else ... end" construction */
{
"!case_end", -2, PGBENCH_CASE
},
/* keep as last array element */
{
NULL, 0, 0
......@@ -288,6 +437,16 @@ make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList *args)
elist_length(args) == 0)
expr_yyerror_more(yyscanner, "at least one argument expected",
PGBENCH_FUNCTIONS[fnumber].fname);
/* special case: case (when ... then ...)+ (else ...)? end */
if (PGBENCH_FUNCTIONS[fnumber].nargs == -2)
{
int len = elist_length(args);
/* 'else' branch is always present, but could be a NULL-constant */
if (len < 3 || len % 2 != 1)
expr_yyerror_more(yyscanner, "odd and >= 3 number of arguments expected",
"case control structure");
}
expr->etype = ENODE_FUNCTION;
expr->u.function.function = PGBENCH_FUNCTIONS[fnumber].tag;
......@@ -300,6 +459,14 @@ make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList *args)
return expr;
}
static PgBenchExpr *
make_case(yyscan_t yyscanner, PgBenchExprList *when_then_list, PgBenchExpr *else_part)
{
return make_func(yyscanner,
find_func(yyscanner, "!case_end"),
make_elist(else_part, when_then_list));
}
/*
* exprscan.l is compiled as part of exprparse.y. Currently, this is
* unavoidable because exprparse does not create a .h file to export
......
......@@ -71,6 +71,22 @@ newline [\n]
/* Line continuation marker */
continuation \\{newline}
/* case insensitive keywords */
and [Aa][Nn][Dd]
or [Oo][Rr]
not [Nn][Oo][Tt]
case [Cc][Aa][Ss][Ee]
when [Ww][Hh][Ee][Nn]
then [Tt][Hh][Ee][Nn]
else [Ee][Ll][Ss][Ee]
end [Ee][Nn][Dd]
true [Tt][Rr][Uu][Ee]
false [Ff][Aa][Ll][Ss][Ee]
null [Nn][Uu][Ll][Ll]
is [Ii][Ss]
isnull [Ii][Ss][Nn][Uu][Ll][Ll]
notnull [Nn][Oo][Tt][Nn][Uu][Ll][Ll]
/* Exclusive states */
%x EXPR
......@@ -129,15 +145,52 @@ continuation \\{newline}
"-" { return '-'; }
"*" { return '*'; }
"/" { return '/'; }
"%" { return '%'; }
"%" { return '%'; } /* C version, also in Pg SQL */
"=" { return '='; }
"<>" { return NE_OP; }
"!=" { return NE_OP; } /* C version, also in Pg SQL */
"<=" { return LE_OP; }
">=" { return GE_OP; }
"<<" { return LS_OP; }
">>" { return RS_OP; }
"<" { return '<'; }
">" { return '>'; }
"|" { return '|'; }
"&" { return '&'; }
"#" { return '#'; }
"~" { return '~'; }
"(" { return '('; }
")" { return ')'; }
"," { return ','; }
{and} { return AND_OP; }
{or} { return OR_OP; }
{not} { return NOT_OP; }
{is} { return IS_OP; }
{isnull} { return ISNULL_OP; }
{notnull} { return NOTNULL_OP; }
{case} { return CASE_KW; }
{when} { return WHEN_KW; }
{then} { return THEN_KW; }
{else} { return ELSE_KW; }
{end} { return END_KW; }
:{alnum}+ {
yylval->str = pg_strdup(yytext + 1);
return VARIABLE;
}
{null} { return NULL_CONST; }
{true} {
yylval->bval = true;
return BOOLEAN_CONST;
}
{false} {
yylval->bval = false;
return BOOLEAN_CONST;
}
{digit}+ {
yylval->ival = strtoint64(yytext);
return INTEGER_CONST;
......
This diff is collapsed.
......@@ -33,8 +33,11 @@ union YYSTYPE;
*/
typedef enum
{
PGBT_NO_VALUE,
PGBT_NULL,
PGBT_INT,
PGBT_DOUBLE
PGBT_DOUBLE,
PGBT_BOOLEAN
/* add other types here */
} PgBenchValueType;
......@@ -45,6 +48,7 @@ typedef struct
{
int64 ival;
double dval;
bool bval;
/* add other types here */
} u;
} PgBenchValue;
......@@ -73,11 +77,27 @@ typedef enum PgBenchFunction
PGBENCH_DOUBLE,
PGBENCH_PI,
PGBENCH_SQRT,
PGBENCH_LN,
PGBENCH_EXP,
PGBENCH_RANDOM,
PGBENCH_RANDOM_GAUSSIAN,
PGBENCH_RANDOM_EXPONENTIAL,
PGBENCH_RANDOM_ZIPFIAN,
PGBENCH_POW
PGBENCH_POW,
PGBENCH_AND,
PGBENCH_OR,
PGBENCH_NOT,
PGBENCH_BITAND,
PGBENCH_BITOR,
PGBENCH_BITXOR,
PGBENCH_LSHIFT,
PGBENCH_RSHIFT,
PGBENCH_EQ,
PGBENCH_NE,
PGBENCH_LE,
PGBENCH_LT,
PGBENCH_IS,
PGBENCH_CASE
} PgBenchFunction;
typedef struct PgBenchExpr PgBenchExpr;
......
......@@ -211,10 +211,13 @@ COMMIT;
# test expressions
pgbench(
'-t 1 -Dfoo=-10.1 -Dbla=false -Di=+3 -Dminint=-9223372036854775808',
'-t 1 -Dfoo=-10.1 -Dbla=false -Di=+3 -Dminint=-9223372036854775808 -Dn=null -Dt=t -Df=of -Dd=1.0',
0,
[ qr{type: .*/001_pgbench_expressions}, qr{processed: 1/1} ],
[ qr{command=4.: int 4\b},
[ qr{command=1.: int 1\d\b},
qr{command=2.: int 1\d\d\b},
qr{command=3.: int 1\d\d\d\b},
qr{command=4.: int 4\b},
qr{command=5.: int 5\b},
qr{command=6.: int 6\b},
qr{command=7.: int 7\b},
......@@ -223,51 +226,61 @@ pgbench(
qr{command=10.: int 10\b},
qr{command=11.: int 11\b},
qr{command=12.: int 12\b},
qr{command=13.: double 13\b},
qr{command=14.: double 14\b},
qr{command=15.: double 15\b},
qr{command=16.: double 16\b},
qr{command=17.: double 17\b},
qr{command=18.: double 18\b},
qr{command=19.: double 19\b},
qr{command=20.: double 20\b},
qr{command=21.: int 9223372036854775807\b},
qr{command=23.: int [1-9]\b},
qr{command=24.: double -27\b},
qr{command=25.: double 1024\b},
qr{command=26.: double 1\b},
qr{command=27.: double 1\b},
qr{command=28.: double -0.125\b},
qr{command=29.: double -0.125\b},
qr{command=30.: double -0.00032\b},
qr{command=31.: double 8.50705917302346e\+0?37\b},
qr{command=32.: double 1e\+0?30\b},
qr{command=18.: int 9223372036854775807\b},
qr{command=20.: int [1-9]\b},
qr{command=21.: double -27\b},
qr{command=22.: double 1024\b},
qr{command=23.: double 1\b},
qr{command=24.: double 1\b},
qr{command=25.: double -0.125\b},
qr{command=26.: double -0.125\b},
qr{command=27.: double -0.00032\b},
qr{command=28.: double 8.50705917302346e\+0?37\b},
qr{command=29.: double 1e\+30\b},
qr{command=30.: boolean false\b},
qr{command=31.: boolean true\b},
qr{command=32.: int 32\b},
qr{command=33.: int 33\b},
qr{command=34.: double 34\b},
qr{command=35.: int 35\b},
qr{command=36.: int 36\b},
qr{command=37.: double 37\b},
qr{command=38.: int 38\b},
qr{command=39.: int 39\b},
qr{command=40.: boolean true\b},
qr{command=41.: null\b},
qr{command=42.: null\b},
qr{command=43.: boolean true\b},
qr{command=44.: boolean true\b},
qr{command=45.: boolean true\b},
qr{command=46.: int 46\b},
qr{command=47.: boolean true\b},
qr{command=48.: boolean true\b},
],
'pgbench expressions',
{ '001_pgbench_expressions' => q{-- integer functions
\set i1 debug(random(1, 100))
\set i2 debug(random_exponential(1, 100, 10.0))
\set i3 debug(random_gaussian(1, 100, 10.0))
\set i1 debug(random(10, 19))
\set i2 debug(random_exponential(100, 199, 10.0))
\set i3 debug(random_gaussian(1000, 1999, 10.0))
\set i4 debug(abs(-4))
\set i5 debug(greatest(5, 4, 3, 2))
\set i6 debug(11 + least(-5, -4, -3, -2))
\set i7 debug(int(7.3))
-- integer operators
\set i8 debug(17 / 5 + 5)
\set i9 debug(- (3 * 4 - 3) / -1 + 3 % -1)
-- integer arithmetic and bit-wise operators
\set i8 debug(17 / (4|1) + ( 4 + (7 >> 2)))
\set i9 debug(- (3 * 4 - (-(~ 1) + -(~ 0))) / -1 + 3 % -1)
\set ia debug(10 + (0 + 0 * 0 - 0 / 1))
\set ib debug(:ia + :scale)
\set ic debug(64 % 13)
-- double functions
\set d1 debug(sqrt(3.0) * abs(-0.8E1))
\set d2 debug(double(1 + 1) * 7)
\set ic debug(64 % (((2 + 1 * 2 + (1 # 2) | 4 * (2 & 11)) - (1 << 2)) + 2))
-- double functions and operators
\set d1 debug(sqrt(+1.5 * 2.0) * abs(-0.8E1))
\set d2 debug(double(1 + 1) * (-75.0 / :foo))
\set pi debug(pi() * 4.9)
\set d4 debug(greatest(4, 2, -1.17) * 4.0)
\set d4 debug(greatest(4, 2, -1.17) * 4.0 * Ln(Exp(1.0)))
\set d5 debug(least(-5.18, .0E0, 1.0/0) * -3.3)
-- double operators
\set d6 debug((0.5 * 12.1 - 0.05) * (31.0 / 10))
\set d7 debug(11.1 + 7.9)
\set d8 debug(:foo * -2)
-- forced overflow
\set maxint debug(:minint - 1)
-- reset a variable
......@@ -284,8 +297,55 @@ pgbench(
\set powernegd2 debug(power(-5.0,-5.0))
\set powerov debug(pow(9223372036854775807, 2))
\set powerov2 debug(pow(10,30))
-- comparisons and logical operations
\set c0 debug(1.0 = 0.0 and 1.0 != 0.0)
\set c1 debug(0 = 1 Or 1.0 = 1)
\set c4 debug(case when 0 < 1 then 32 else 0 end)
\set c5 debug(case when true then 33 else 0 end)
\set c6 debug(case when false THEN -1 when 1 = 1 then 13 + 19 + 2.0 end )
\set c7 debug(case when (1 > 0) and (1 >= 0) and (0 < 1) and (0 <= 1) and (0 != 1) and (0 = 0) and (0 <> 1) then 35 else 0 end)
\set c8 debug(CASE \
WHEN (1.0 > 0.0) AND (1.0 >= 0.0) AND (0.0 < 1.0) AND (0.0 <= 1.0) AND \
(0.0 != 1.0) AND (0.0 = 0.0) AND (0.0 <> 1.0) AND (0.0 = 0.0) \
THEN 36 \
ELSE 0 \
END)
\set c9 debug(CASE WHEN NOT FALSE THEN 3 * 12.3333334 END)
\set ca debug(case when false then 0 when 1-1 <> 0 then 1 else 38 end)
\set cb debug(10 + mod(13 * 7 + 12, 13) - mod(-19 * 11 - 17, 19))
\set cc debug(NOT (0 > 1) AND (1 <= 1) AND NOT (0 >= 1) AND (0 < 1) AND \
NOT (false and true) AND (false OR TRUE) AND (NOT :f) AND (NOT FALSE) AND \
NOT (NOT TRUE))
-- NULL value and associated operators
\set n0 debug(NULL + NULL * exp(NULL))
\set n1 debug(:n0)
\set n2 debug(NOT (:n0 IS NOT NULL OR :d1 IS NULL))
\set n3 debug(:n0 IS NULL AND :d1 IS NOT NULL AND :d1 NOTNULL)
\set n4 debug(:n0 ISNULL AND NOT :n0 IS TRUE AND :n0 IS NOT FALSE)
\set n5 debug(CASE WHEN :n IS NULL THEN 46 ELSE NULL END)
-- use a variables of all types
\set n6 debug(:n IS NULL AND NOT :f AND :t)
-- conditional truth
\set cs debug(CASE WHEN 1 THEN TRUE END AND CASE WHEN 1.0 THEN TRUE END AND CASE WHEN :n THEN NULL ELSE TRUE END)
-- lazy evaluation
\set zy 0
\set yz debug(case when :zy = 0 then -1 else (1 / :zy) end)
\set yz debug(case when :zy = 0 or (1 / :zy) < 0 then -1 else (1 / :zy) end)
\set yz debug(case when :zy > 0 and (1 / :zy) < 0 then (1 / :zy) else 1 end)
-- substitute variables of all possible types
\set v0 NULL
\set v1 TRUE
\set v2 5432
\set v3 -54.21E-2
SELECT :v0, :v1, :v2, :v3;
} });
=head
} });
=cut
# backslash commands
pgbench(
'-t 1', 0,
......@@ -404,8 +464,42 @@ SELECT LEAST(:i, :i, :i, :i, :i, :i, :i, :i, :i, :i, :i);
q{\set i random_zipfian(0, 10, 1000000)} ],
[ 'set non numeric value', 0,
[qr{malformed variable "foo" value: "bla"}], q{\set i :foo + 1} ],
[ 'set no expression', 1, [qr{syntax error}], q{\set i} ],
[ 'set missing argument', 1, [qr{missing argument}i], q{\set} ],
[ 'set no expression',
1,
[qr{syntax error}],
q{\set i} ],
[ 'set missing argument',
1,
[qr{missing argument}i],
q{\set} ],
[ 'set not a bool',
0,
[ qr{cannot coerce double to boolean} ],
q{\set b NOT 0.0} ],
[ 'set not an int',
0,
[ qr{cannot coerce boolean to int} ],
q{\set i TRUE + 2} ],
[ 'set not an double',
0,
[ qr{cannot coerce boolean to double} ],
q{\set d ln(TRUE)} ],
[ 'set case error',
1,
[ qr{syntax error in command "set"} ],
q{\set i CASE TRUE THEN 1 ELSE 0 END} ],
[ 'set random error',
0,
[ qr{cannot coerce boolean to int} ],
q{\set b random(FALSE, TRUE)} ],
[ 'set number of args mismatch',
1,
[ qr{unexpected number of arguments} ],
q{\set d ln(1.0, 2.0))} ],
[ 'set at least one arg',
1,
[ qr{at least one argument expected} ],
q{\set i greatest())} ],
# SETSHELL
[ 'setshell not an int', 0,
......@@ -427,7 +521,10 @@ SELECT LEAST(:i, :i, :i, :i, :i, :i, :i, :i, :i, :i, :i);
# MISC
[ 'misc invalid backslash command', 1,
[qr{invalid command .* "nosuchcommand"}], q{\nosuchcommand} ],
[ 'misc empty script', 1, [qr{empty command list for script}], q{} ],);
[ 'misc empty script', 1, [qr{empty command list for script}], q{} ],
[ 'bad boolean', 0, [qr{malformed variable.*trueXXX}], q{\set b :badtrue or true} ],
);
for my $e (@errors)
{
......@@ -435,7 +532,7 @@ for my $e (@errors)
my $n = '001_pgbench_error_' . $name;
$n =~ s/ /_/g;
pgbench(
'-n -t 1 -Dfoo=bla -M prepared',
'-n -t 1 -Dfoo=bla -Dnull=null -Dtrue=true -Done=1 -Dzero=0.0 -Dbadtrue=trueXXX -M prepared',
$status,
[ $status ? qr{^$} : qr{processed: 0/1} ],
$re,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment