Bonjour Michaël,
Patch v3 is also a rebase.
This has rotten for half a year, so I am marking it as returned with
feedback. There have been comments from Alvaro and Andres as well...
Attached a v4. I'm resurrecting this small patch, after "\aset" has been
added to pgbench (9d8ef988).
Alvaro's feedback was about the lack of "pg_strndup" availability, but he
concluded that it would seldom be used if available, so it was not worth
the effort to add it for the sake of this small patch.
About arguments for this patch:
First, this syntax is already available in "psql", and I think that
keeping pgbench/psql in sync is better for users' ease of mind. That is
the initial (weak) argument about which Andres objected.
Second, it is on the path to move pgbench expression as a front-end util
that can be used by psql, which is still a project of mine, although I
have not started much on that yet. For that pgbench expressions must be
able to do what psql can do before merging, including this test. Other
things needed before this is stated are a working free on expression trees
(trivial), merging variable management to some extent (at least the same
API, possibly the same implementation would save quite a few lines of
code), having string values, support for :'var' and :"var" string escapes…
no very big deals, but some work anyway.
Third, I have a practical pgbench-specific use case, which motivates the
resurrection right now: I'd like to be able to run a benchmark with a mix
of SELECT, UPDATE, DELETE and INSERT commands, that is expected in a
normal functioning database system.
For INSERT, I think I have a few ideas for possible and simple solutions,
but it still need some thoughts so this is for later. The key issue is
how to handle a varying number of rows.
Under DELETE, some SELECT and UPDATE scripts may fail because the data are
not there anymore, hence the ability to check whether a variable is empty
comes handy:
Low probability delete script:
\set uid random(...)
DELETE FROM Operations WHERE oid IN (... :uid) \;
DELETE FROM Accounts WHERE aid IN (... :uid) \;
DELETE FROM Client WHERE ... :uid;
Parallel running update script:
-- a pseudo random client arrives
\set uname random(...)
SELECT uid FROM Client WHERE uname = :uname::TEXT \aset
-- remainder must be skipped if no client was found
\if :{?uid}
SELECT SUM(abalance) FROM Account WHERE uid = :uid ...
-- if the balance is right, withdrawing is possible...
...
\else
-- skip silently, the client has left!
\endif
--
Fabien.
diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml
index 58a2aa3bf2..1f375c4167 100644
--- a/doc/src/sgml/ref/pgbench.sgml
+++ b/doc/src/sgml/ref/pgbench.sgml
@@ -1133,6 +1133,8 @@ SELECT 4 AS four \; SELECT 5 AS five \aset
integer constants such as <literal>5432</literal>,
double constants such as <literal>3.14159</literal>,
references to variables <literal>:</literal><replaceable>variablename</replaceable>,
+ testing whether a variable exists
+ <literal>:{?</literal><replaceable>variablename</replaceable><literal>}</literal>,
<link linkend="pgbench-builtin-operators">operators</link>
with their usual SQL precedence and associativity,
<link linkend="pgbench-builtin-functions">function calls</link>,
diff --git a/src/bin/pgbench/exprparse.y b/src/bin/pgbench/exprparse.y
index 85d61caa9f..dc6d419e50 100644
--- a/src/bin/pgbench/exprparse.y
+++ b/src/bin/pgbench/exprparse.y
@@ -28,6 +28,7 @@ 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_variable_exists(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);
@@ -59,10 +60,10 @@ static PgBenchExpr *make_case(yyscan_t yyscanner, PgBenchExprList *when_then_lis
%type <ival> INTEGER_CONST function
%type <dval> DOUBLE_CONST
%type <bval> BOOLEAN_CONST
-%type <str> VARIABLE FUNCTION
+%type <str> VARIABLE VAREXISTS FUNCTION
%token NULL_CONST INTEGER_CONST MAXINT_PLUS_ONE_CONST DOUBLE_CONST
-%token BOOLEAN_CONST VARIABLE FUNCTION
+%token BOOLEAN_CONST VARIABLE VAREXISTS 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
@@ -144,6 +145,7 @@ expr: '(' expr ')' { $$ = $2; }
| DOUBLE_CONST { $$ = make_double_constant($1); }
/* misc */
| VARIABLE { $$ = make_variable($1); }
+ | VAREXISTS { $$ = make_variable_exists($1); }
| function '(' elist ')' { $$ = make_func(yyscanner, $1, $3); }
| case_control { $$ = $1; }
;
@@ -216,6 +218,16 @@ make_variable(char *varname)
}
/* binary operators */
+static PgBenchExpr *
+make_variable_exists(char *varname)
+{
+ PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
+
+ expr->etype = ENODE_VAREXISTS;
+ expr->u.variable.varname = varname;
+ return expr;
+}
+
static PgBenchExpr *
make_op(yyscan_t yyscanner, const char *operator,
PgBenchExpr *lexpr, PgBenchExpr *rexpr)
diff --git a/src/bin/pgbench/exprscan.l b/src/bin/pgbench/exprscan.l
index 430bff38a6..254bebf300 100644
--- a/src/bin/pgbench/exprscan.l
+++ b/src/bin/pgbench/exprscan.l
@@ -204,6 +204,13 @@ notnull [Nn][Oo][Tt][Nn][Uu][Ll][Ll]
*/
return MAXINT_PLUS_ONE_CONST;
}
+:\{\?{alnum}+\} {
+ /* no pg_strndup? */
+ yylval->str = pg_strdup(yytext + 3);
+ /* scratch final '}' */
+ yylval->str[strlen(yylval->str)-1] = '\0';
+ return VAREXISTS;
+ }
{digit}+ {
if (!strtoint64(yytext, true, &yylval->ival))
expr_yyerror_more(yyscanner, "bigint constant overflow",
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index e99af80167..8cc9e57d9d 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -2451,6 +2451,10 @@ evaluateExpr(CState *st, PgBenchExpr *expr, PgBenchValue *retval)
return true;
}
+ case ENODE_VAREXISTS:
+ setBoolValue(retval, lookupVariable(st, expr->u.variable.varname) != NULL);
+ return true;
+
case ENODE_FUNCTION:
return evalFunc(st,
expr->u.function.function,
diff --git a/src/bin/pgbench/pgbench.h b/src/bin/pgbench/pgbench.h
index fb2c34f512..18575ea2b6 100644
--- a/src/bin/pgbench/pgbench.h
+++ b/src/bin/pgbench/pgbench.h
@@ -58,6 +58,7 @@ typedef enum PgBenchExprType
{
ENODE_CONSTANT,
ENODE_VARIABLE,
+ ENODE_VAREXISTS,
ENODE_FUNCTION
} PgBenchExprType;
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index e85728c379..1a75df0c5f 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -459,6 +459,8 @@ pgbench(
qr{command=98.: int 5432\b}, # :random_seed
qr{command=99.: int -9223372036854775808\b}, # min int
qr{command=100.: int 9223372036854775807\b}, # max int
+ qr{command=101.: boolean false\b}, # var exists
+ qr{command=102.: boolean true\b},
],
'pgbench expressions',
{
@@ -586,6 +588,9 @@ SELECT :v0, :v1, :v2, :v3;
-- minint constant parsing
\set min debug(-9223372036854775808)
\set max debug(-(:min + 1))
+-- test variable existence
+\set no debug(:{?no_such_variable})
+\set yes debug(:{?cs})
}
});