Removing \cset from pgbench
Hello
In reply to /messages/by-id/alpine.DEB.2.21.1901102211350.27692@lancre
wherein Fabien wrote:
I'm not very happy with the resulting syntax, but IMO the feature is useful.
My initial design was to copy PL/pgSQL "into" with some "\into" orthogonal
to \; and ;, but the implementation was not especially nice and I was told
to use psql's \gset approach, which I did.If we do not provide \cset, then combined queries and getting results are
not orthogonal, although from a performance testing point of view an
application could do both, and the point is to allow pgbench to test the
performance impact of doing that.
We very briefly discussed this topic at FOSDEM pgday. My feeling on the
general opinion is that there's appreciation for \gset in general, but
that people feel that \cset is too much cruft to take for not enough
additional added value (compared to great value delivered by \gset).
What I'm going to do now is to write a patch to remove the \cset part of
the commit and post it, intending to push at some point next week.
If somebody has grown really fond of \cset, they can work on a patch to
implement it properly, which it isn't now.
Thanks
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Hola Alvaro,
In reply to /messages/by-id/alpine.DEB.2.21.1901102211350.27692@lancre
wherein Fabien wrote:I'm not very happy with the resulting syntax, but IMO the feature is useful.
My initial design was to copy PL/pgSQL "into" with some "\into" orthogonal
to \; and ;, but the implementation was not especially nice and I was told
to use psql's \gset approach, which I did.If we do not provide \cset, then combined queries and getting results are
not orthogonal, although from a performance testing point of view an
application could do both, and the point is to allow pgbench to test the
performance impact of doing that.We very briefly discussed this topic at FOSDEM pgday. My feeling on the
general opinion is that there's appreciation for \gset in general, but
that people feel that \cset is too much cruft to take for not enough
additional added value (compared to great value delivered by \gset).What I'm going to do now is to write a patch to remove the \cset part of
the commit and post it, intending to push at some point next week.
If somebody has grown really fond of \cset, they can work on a patch to
implement it properly, which it isn't now.
My usless 0.02ᅵ:
I'm willing to implement it properly. Do you have any advice?
For me the issue comes from the fact that postgres silently ignores empty
queries, i.e. on:
SELECT 1 \; \; SELECT 2;
Three queries are sent, the middle one empty, but two results returned
(PGRES_TUPLES_OK, PGRES_TUPLES_OK) instead of (PGRES_TUPLES_OK,
PGRES_EMPTY_QUERY, PGRES_TUPLES_OK). However, on:
;
You do have an PGRES_EMPTY_QUERY result. How to deal cleanly and simply
with that?
Removing the "skip empty query" optimizations would remove the "it does
not work with empty queries" documentation warning that I understood Tom
complains about. It may have a little impact on "psql" implementation to
keep the "show the last non-empty result" behavior on combined queries.
Providing non-orthogonal features (eg combined queries cannot use some
options, such as -M in pgbench) is as much substandard as awkward
optimizations like the above.
An alternative is to detect whether a query is empty, but that complicates
the lexing phase to detect "\; <spaces and comments only> \;" and adjust
the query count to match variable setting to their queries: one version of
the patch kept the position of embedded semi-colons so as to be able to
check that only spaces appear (comments are removed by the lexer). Because
this was seen as awkward code working around awkward protocol behavior for
an unlikely corner case, it was removed in the reviewing process and the
limitation was documented instead.
--
Fabien.
Hola �lvaro,
What I'm going to do now is to write a patch to remove the \cset part of
the commit and post it, intending to push at some point next week.
Per your request, here is a patch which removes \cset from pgbench. Sigh.
--
Fabien.
Attachments:
pgbench-remove-cset-1.patchtext/x-diff; name=pgbench-remove-cset-1.patchDownload
diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml
index 24833f46bc..b45e2f0896 100644
--- a/doc/src/sgml/ref/pgbench.sgml
+++ b/doc/src/sgml/ref/pgbench.sgml
@@ -963,48 +963,6 @@ pgbench <optional> <replaceable>options</replaceable> </optional> <replaceable>d
</para>
<variablelist>
- <varlistentry id='pgbench-metacommand-cset'>
- <term>
- <literal>\cset [<replaceable>prefix</replaceable>]</literal>
- </term>
-
- <listitem>
- <para>
- This command may be used to end SQL queries, replacing an embedded
- semicolon (<literal>\;</literal>) within a compound SQL command.
- </para>
-
- <para>
- When this command is used, the preceding SQL query is expected to
- return one row, the columns of which are stored into variables named after
- column names, and prefixed with <replaceable>prefix</replaceable> if provided.
- </para>
-
- <para>
- The following example sends four queries as one compound SQL command,
- inducing one message sent at the protocol level.
- The result of the first query is stored into variable <replaceable>one</replaceable>,
- the results of the third query are stored into variables <replaceable>z_three</replaceable>
- and <replaceable>z_four</replaceable>,
- whereas the results of the other queries are discarded.
-<programlisting>
--- compound of four queries
-SELECT 1 AS one \cset
-SELECT 2 AS two \;
-SELECT 3 AS three, 4 AS four \cset z_
-SELECT 5;
-</programlisting>
- </para>
-
- <note>
- <para>
- <literal>\cset</literal> does not work when empty SQL queries appear
- within a compound SQL command.
- </para>
- </note>
- </listitem>
- </varlistentry>
-
<varlistentry id='pgbench-metacommand-gset'>
<term>
<literal>\gset [<replaceable>prefix</replaceable>]</literal>
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 4789ab92ee..43976d1d5d 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -490,7 +490,6 @@ typedef enum MetaCommand
META_SETSHELL, /* \setshell */
META_SHELL, /* \shell */
META_SLEEP, /* \sleep */
- META_CSET, /* \cset */
META_GSET, /* \gset */
META_IF, /* \if */
META_ELIF, /* \elif */
@@ -522,7 +521,7 @@ static const char *QUERYMODE[] = {"simple", "extended", "prepared"};
* string itself. For SQL commands, after post-processing
* argv[0] is the same as 'lines' with variables substituted.
* nqueries In a multi-command SQL line, the number of queries.
- * varprefix SQL commands terminated with \gset or \cset have this set
+ * varprefix SQL commands terminated with \gset have this set
* to a non NULL value. If nonempty, it's used to prefix the
* variable name that receives the value.
* varprefix_max Allocated size of the varprefix array.
@@ -2614,8 +2613,6 @@ getMetaCommand(const char *cmd)
mc = META_ELSE;
else if (pg_strcasecmp(cmd, "endif") == 0)
mc = META_ENDIF;
- else if (pg_strcasecmp(cmd, "cset") == 0)
- mc = META_CSET;
else if (pg_strcasecmp(cmd, "gset") == 0)
mc = META_GSET;
else
@@ -4293,30 +4290,11 @@ free_command(Command *command)
}
/*
* It should also free expr recursively, but this is currently not needed
- * as only \{g,c}set commands (which do not have an expression) are freed.
+ * as only gset commands (which do not have an expression) are freed.
*/
pg_free(command);
}
-/*
- * append "more" text to current compound command which had been interrupted
- * by \cset.
- */
-static void
-append_sql_command(Command *my_command, char *more, int numqueries)
-{
- Assert(my_command->type == SQL_COMMAND && my_command->lines.len > 0);
-
- more = skip_sql_comments(more);
- if (more == NULL)
- return;
-
- /* append command text, embedding a ';' in place of the \cset */
- appendPQExpBuffer(&my_command->lines, ";\n%s", more);
-
- my_command->nqueries += numqueries;
-}
-
/*
* Once an SQL command is fully parsed, possibly by accumulating several
* parts, complete other fields of the Command structure.
@@ -4549,7 +4527,7 @@ process_backslash_command(PsqlScanState sstate, const char *source)
syntax_error(source, lineno, my_command->first_line, my_command->argv[0],
"unexpected argument", NULL, -1);
}
- else if (my_command->meta == META_CSET || my_command->meta == META_GSET)
+ else if (my_command->meta == META_GSET)
{
if (my_command->argc > 2)
syntax_error(source, lineno, my_command->first_line, my_command->argv[0],
@@ -4637,7 +4615,6 @@ ParseScript(const char *script, const char *desc, int weight)
PQExpBufferData line_buf;
int alloc_num;
int index;
- bool saw_cset = false;
int lineno;
int start_offset;
@@ -4683,22 +4660,12 @@ ParseScript(const char *script, const char *desc, int weight)
semicolons = psql_scan_get_escaped_semicolons(sstate);
- if (saw_cset)
- {
- /* the previous multi-line command ended with \cset */
- append_sql_command(ps.commands[index - 1], line_buf.data,
- semicolons + 1);
- saw_cset = false;
- }
- else
- {
- /* If we collected a new SQL command, process that */
- command = create_sql_command(&line_buf, desc, semicolons + 1);
+ /* If we collected a new SQL command, process that */
+ command = create_sql_command(&line_buf, desc, semicolons + 1);
- /* store new command */
- if (command)
- ps.commands[index++] = command;
- }
+ /* store new command */
+ if (command)
+ ps.commands[index++] = command;
/* If we reached a backslash, process that */
if (sr == PSCAN_BACKSLASH)
@@ -4708,49 +4675,42 @@ ParseScript(const char *script, const char *desc, int weight)
if (command)
{
/*
- * If this is gset/cset, merge into the preceding command. (We
+ * If this is gset, merge into the preceding command. (We
* don't use a command slot in this case).
*/
- if (command->meta == META_CSET ||
- command->meta == META_GSET)
+ if (command->meta == META_GSET)
{
int cindex;
Command *cmd;
- /*
- * If \cset is seen, set flag to leave the command pending
- * for the next iteration to process.
- */
- saw_cset = command->meta == META_CSET;
-
if (index == 0)
syntax_error(desc, lineno, NULL, NULL,
- "\\gset/cset cannot start a script",
+ "\\gset cannot start a script",
NULL, -1);
cmd = ps.commands[index - 1];
if (cmd->type != SQL_COMMAND)
syntax_error(desc, lineno, NULL, NULL,
- "\\gset/cset must follow a SQL command",
+ "\\gset must follow a SQL command",
cmd->first_line, -1);
- /* this {g,c}set applies to the previous query */
+ /* this gset applies to the previous query */
cindex = cmd->nqueries - 1;
/*
- * now that we know there's a {g,c}set in this command,
+ * now that we know there's a gset in this command,
* allocate space for the variable name prefix array.
*/
allocate_command_varprefix(cmd, cmd->nqueries);
/*
- * Don't allow the previous command be a gset/cset; that
+ * Don't allow the previous command be a gset; that
* would make no sense.
*/
if (cmd->varprefix[cindex] != NULL)
syntax_error(desc, lineno, NULL, NULL,
- "\\gset/cset cannot follow one another",
+ "\\gset cannot follow one another",
NULL, -1);
/* get variable prefix */
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index 45888dc12e..1ccc803ba3 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -538,22 +538,17 @@ pgbench(
}
});
-# working \gset and \cset
+# working \gset
pgbench(
'-t 1', 0,
- [ qr{type: .*/001_pgbench_gset_and_cset}, qr{processed: 1/1} ],
+ [ qr{type: .*/001_pgbench_gset}, qr{processed: 1/1} ],
[ qr{command=3.: int 0\b},
qr{command=5.: int 1\b},
qr{command=6.: int 2\b},
qr{command=8.: int 3\b},
- qr{command=9.: int 4\b},
- qr{command=10.: int 5\b},
- qr{command=12.: int 6\b},
- qr{command=13.: int 7\b},
- qr{command=14.: int 8\b},
- qr{command=16.: int 9\b} ],
- 'pgbench gset and cset commands',
- { '001_pgbench_gset_and_cset' => q{-- test gset and cset
+ qr{command=10.: int 4\b} ],
+ 'pgbench gset command',
+ { '001_pgbench_gset' => q{-- test gset
-- no columns
SELECT \gset
-- one value
@@ -563,21 +558,12 @@ SELECT 0 AS i0 \gset
SELECT 1 AS i1, 2 AS i2 \gset
\set i debug(:i1)
\set i debug(:i2)
--- cset & gset to follow
-SELECT :i2 + 1 AS i3, :i2 * :i2 AS i4 \cset
- SELECT 5 AS i5 \gset
-\set i debug(:i3)
-\set i debug(:i4)
-\set i debug(:i5)
-- with prefix
-SELECT 6 AS i6, 7 AS i7 \cset x_
- SELECT 8 AS i8 \gset y_
-\set i debug(:x_i6)
-\set i debug(:x_i7)
-\set i debug(:y_i8)
+SELECT 3 AS i3 \gset x_
+\set i debug(:x_i3)
-- overwrite existing variable
-SELECT 0 AS i9, 9 AS i9 \gset
-\set i debug(:i9)
+SELECT 0 AS i4, 4 AS i4 \gset
+\set i debug(:i4)
} });
# trigger many expression errors
@@ -772,20 +758,17 @@ SELECT LEAST(}.join(', ', (':i') x 256).q{)}
[ 'bad boolean', 2,
[qr{malformed variable.*trueXXX}], q{\set b :badtrue or true} ],
- # GSET & CSET
+ # GSET
[ 'gset no row', 2,
[qr{expected one row, got 0\b}], q{SELECT WHERE FALSE \gset} ],
- [ 'cset no row', 2,
- [qr{expected one row, got 0\b}], q{SELECT WHERE FALSE \cset
-SELECT 1 AS i\gset}, 1 ],
- [ 'gset alone', 1, [qr{gset/cset cannot start a script}], q{\gset} ],
+ [ 'gset alone', 1, [qr{gset cannot start a script}], q{\gset} ],
[ 'gset no SQL', 1,
- [qr{gset/cset must follow a SQL command}], q{\set i +1
+ [qr{gset must follow a SQL command}], q{\set i +1
\gset} ],
[ 'gset too many arguments', 1,
[qr{too many arguments}], q{SELECT 1 \gset a b} ],
[ 'gset after gset', 1,
- [qr{gset/cset cannot follow one another}], q{SELECT 1 AS i \gset
+ [qr{gset cannot follow one another}], q{SELECT 1 AS i \gset
\gset} ],
[ 'gset non SELECT', 2,
[qr{expected one row, got 0}],
What I'm going to do now is to write a patch to remove the \cset part of
the commit and post it, intending to push at some point next week.Per your request, here is a patch which removes \cset from pgbench. Sigh.
Again, only the removal is a little deeper. This lifts the constraint
about not using empty queries in a compound statement, at the price of
some added logic to detect the last result.
--
Fabien.
Attachments:
pgbench-remove-cset-2.patchtext/x-diff; name=pgbench-remove-cset-2.patchDownload
diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml
index 24833f46bc..a148138402 100644
--- a/doc/src/sgml/ref/pgbench.sgml
+++ b/doc/src/sgml/ref/pgbench.sgml
@@ -963,48 +963,6 @@ pgbench <optional> <replaceable>options</replaceable> </optional> <replaceable>d
</para>
<variablelist>
- <varlistentry id='pgbench-metacommand-cset'>
- <term>
- <literal>\cset [<replaceable>prefix</replaceable>]</literal>
- </term>
-
- <listitem>
- <para>
- This command may be used to end SQL queries, replacing an embedded
- semicolon (<literal>\;</literal>) within a compound SQL command.
- </para>
-
- <para>
- When this command is used, the preceding SQL query is expected to
- return one row, the columns of which are stored into variables named after
- column names, and prefixed with <replaceable>prefix</replaceable> if provided.
- </para>
-
- <para>
- The following example sends four queries as one compound SQL command,
- inducing one message sent at the protocol level.
- The result of the first query is stored into variable <replaceable>one</replaceable>,
- the results of the third query are stored into variables <replaceable>z_three</replaceable>
- and <replaceable>z_four</replaceable>,
- whereas the results of the other queries are discarded.
-<programlisting>
--- compound of four queries
-SELECT 1 AS one \cset
-SELECT 2 AS two \;
-SELECT 3 AS three, 4 AS four \cset z_
-SELECT 5;
-</programlisting>
- </para>
-
- <note>
- <para>
- <literal>\cset</literal> does not work when empty SQL queries appear
- within a compound SQL command.
- </para>
- </note>
- </listitem>
- </varlistentry>
-
<varlistentry id='pgbench-metacommand-gset'>
<term>
<literal>\gset [<replaceable>prefix</replaceable>]</literal>
@@ -1038,13 +996,6 @@ SELECT 1 \;
SELECT 2 AS two, 3 AS three \gset p_
</programlisting>
</para>
-
- <note>
- <para>
- <literal>\gset</literal> does not work when empty SQL queries appear
- within a compound SQL command.
- </para>
- </note>
</listitem>
</varlistentry>
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 4789ab92ee..545c4416f1 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -490,7 +490,6 @@ typedef enum MetaCommand
META_SETSHELL, /* \setshell */
META_SHELL, /* \shell */
META_SLEEP, /* \sleep */
- META_CSET, /* \cset */
META_GSET, /* \gset */
META_IF, /* \if */
META_ELIF, /* \elif */
@@ -521,11 +520,9 @@ static const char *QUERYMODE[] = {"simple", "extended", "prepared"};
* argv Command arguments, the first of which is the command or SQL
* string itself. For SQL commands, after post-processing
* argv[0] is the same as 'lines' with variables substituted.
- * nqueries In a multi-command SQL line, the number of queries.
- * varprefix SQL commands terminated with \gset or \cset have this set
+ * varprefix SQL commands terminated with \gset have this set
* to a non NULL value. If nonempty, it's used to prefix the
* variable name that receives the value.
- * varprefix_max Allocated size of the varprefix array.
* expr Parsed expression, if needed.
* stats Time spent in this command.
*/
@@ -537,9 +534,7 @@ typedef struct Command
MetaCommand meta;
int argc;
char *argv[MAX_ARGS];
- int nqueries;
- char **varprefix;
- int varprefix_max;
+ char *varprefix;
PgBenchExpr *expr;
SimpleStats stats;
} Command;
@@ -619,7 +614,6 @@ static void doLog(TState *thread, CState *st,
static void processXactStats(TState *thread, CState *st, instr_time *now,
bool skipped, StatsData *agg);
static void pgbench_error(const char *fmt,...) pg_attribute_printf(1, 2);
-static void allocate_command_varprefix(Command *cmd, int totalqueries);
static void addScript(ParsedScript script);
static void *threadRun(void *arg);
static void finishCon(CState *st);
@@ -2614,8 +2608,6 @@ getMetaCommand(const char *cmd)
mc = META_ELSE;
else if (pg_strcasecmp(cmd, "endif") == 0)
mc = META_ENDIF;
- else if (pg_strcasecmp(cmd, "cset") == 0)
- mc = META_CSET;
else if (pg_strcasecmp(cmd, "gset") == 0)
mc = META_GSET;
else
@@ -2848,24 +2840,30 @@ sendCommand(CState *st, Command *command)
/*
* Process query response from the backend.
*
- * If varprefix is not NULL, it's the array of variable prefix names where to
- * store the results.
+ * If varprefix is not NULL, it's the variable name prefix where to store
+ * the results of the *last* command.
*
* Returns true if everything is A-OK, false if any error occurs.
*/
static bool
-readCommandResponse(CState *st, char **varprefix)
+readCommandResponse(CState *st, char *varprefix)
{
PGresult *res;
int qrynum = 0;
- while ((res = PQgetResult(st->con)) != NULL)
+ res = PQgetResult(st->con);
+
+ while (res != NULL)
{
+ /* look now at the next result to know whether it is the last */
+ PGresult *next_res = PQgetResult(st->con);
+ bool is_last = (next_res == NULL);
+
switch (PQresultStatus(res))
{
case PGRES_COMMAND_OK: /* non-SELECT commands */
case PGRES_EMPTY_QUERY: /* may be used for testing no-op overhead */
- if (varprefix && varprefix[qrynum] != NULL)
+ if (is_last && varprefix != NULL)
{
fprintf(stderr,
"client %d script %d command %d query %d: expected one row, got %d\n",
@@ -2876,7 +2874,7 @@ readCommandResponse(CState *st, char **varprefix)
break;
case PGRES_TUPLES_OK:
- if (varprefix && varprefix[qrynum] != NULL)
+ if (is_last && varprefix != NULL)
{
if (PQntuples(res) != 1)
{
@@ -2895,9 +2893,9 @@ readCommandResponse(CState *st, char **varprefix)
char *varname = PQfname(res, fld);
/* allocate varname only if necessary, freed below */
- if (*varprefix[qrynum] != '\0')
+ if (*varprefix != '\0')
varname =
- psprintf("%s%s", varprefix[qrynum], varname);
+ psprintf("%s%s", varprefix, varname);
/* store result as a string */
if (!putVariable(st, "gset", varname,
@@ -2914,7 +2912,7 @@ readCommandResponse(CState *st, char **varprefix)
return false;
}
- if (*varprefix[qrynum] != '\0')
+ if (*varprefix != '\0')
pg_free(varname);
}
}
@@ -2935,6 +2933,7 @@ readCommandResponse(CState *st, char **varprefix)
PQclear(res);
qrynum++;
+ res = next_res;
}
if (qrynum == 0)
@@ -4248,7 +4247,7 @@ skip_sql_comments(char *sql_command)
* struct.
*/
static Command *
-create_sql_command(PQExpBuffer buf, const char *source, int numqueries)
+create_sql_command(PQExpBuffer buf, const char *source)
{
Command *my_command;
char *p = skip_sql_comments(buf->data);
@@ -4265,9 +4264,7 @@ create_sql_command(PQExpBuffer buf, const char *source, int numqueries)
my_command->meta = META_NONE;
my_command->argc = 0;
memset(my_command->argv, 0, sizeof(my_command->argv));
- my_command->nqueries = numqueries;
my_command->varprefix = NULL; /* allocated later, if needed */
- my_command->varprefix_max = 0;
my_command->expr = NULL;
initSimpleStats(&my_command->stats);
@@ -4284,39 +4281,14 @@ free_command(Command *command)
for (int i = 0; i < command->argc; i++)
pg_free(command->argv[i]);
if (command->varprefix)
- {
- for (int i = 0; i < command->varprefix_max; i++)
- if (command->varprefix[i] &&
- command->varprefix[i][0] != '\0') /* see ParseScript */
- pg_free(command->varprefix[i]);
pg_free(command->varprefix);
- }
/*
* It should also free expr recursively, but this is currently not needed
- * as only \{g,c}set commands (which do not have an expression) are freed.
+ * as only gset commands (which do not have an expression) are freed.
*/
pg_free(command);
}
-/*
- * append "more" text to current compound command which had been interrupted
- * by \cset.
- */
-static void
-append_sql_command(Command *my_command, char *more, int numqueries)
-{
- Assert(my_command->type == SQL_COMMAND && my_command->lines.len > 0);
-
- more = skip_sql_comments(more);
- if (more == NULL)
- return;
-
- /* append command text, embedding a ';' in place of the \cset */
- appendPQExpBuffer(&my_command->lines, ";\n%s", more);
-
- my_command->nqueries += numqueries;
-}
-
/*
* Once an SQL command is fully parsed, possibly by accumulating several
* parts, complete other fields of the Command structure.
@@ -4350,35 +4322,6 @@ postprocess_sql_command(Command *my_command)
}
}
-/*
- * Determine the command's varprefix size needed and allocate memory for it
- */
-static void
-allocate_command_varprefix(Command *cmd, int totalqueries)
-{
- int new_max;
-
- /* sufficient space already allocated? */
- if (totalqueries <= cmd->varprefix_max)
- return;
-
- /* determine the new array size */
- new_max = Max(cmd->varprefix_max, 2);
- while (new_max < totalqueries)
- new_max *= 2;
-
- /* enlarge the array, zero-initializing the allocated space */
- if (cmd->varprefix == NULL)
- cmd->varprefix = pg_malloc0(sizeof(char *) * new_max);
- else
- {
- cmd->varprefix = pg_realloc(cmd->varprefix, sizeof(char *) * new_max);
- memset(cmd->varprefix + cmd->varprefix_max, 0,
- sizeof(char *) * (new_max - cmd->varprefix_max));
- }
- cmd->varprefix_max = new_max;
-}
-
/*
* Parse a backslash command; return a Command struct, or NULL if comment
*
@@ -4549,7 +4492,7 @@ process_backslash_command(PsqlScanState sstate, const char *source)
syntax_error(source, lineno, my_command->first_line, my_command->argv[0],
"unexpected argument", NULL, -1);
}
- else if (my_command->meta == META_CSET || my_command->meta == META_GSET)
+ else if (my_command->meta == META_GSET)
{
if (my_command->argc > 2)
syntax_error(source, lineno, my_command->first_line, my_command->argv[0],
@@ -4637,7 +4580,6 @@ ParseScript(const char *script, const char *desc, int weight)
PQExpBufferData line_buf;
int alloc_num;
int index;
- bool saw_cset = false;
int lineno;
int start_offset;
@@ -4674,31 +4616,18 @@ ParseScript(const char *script, const char *desc, int weight)
PsqlScanResult sr;
promptStatus_t prompt;
Command *command = NULL;
- int semicolons;
resetPQExpBuffer(&line_buf);
lineno = expr_scanner_get_lineno(sstate, start_offset);
sr = psql_scan(sstate, &line_buf, &prompt);
- semicolons = psql_scan_get_escaped_semicolons(sstate);
+ /* If we collected a new SQL command, process that */
+ command = create_sql_command(&line_buf, desc);
- if (saw_cset)
- {
- /* the previous multi-line command ended with \cset */
- append_sql_command(ps.commands[index - 1], line_buf.data,
- semicolons + 1);
- saw_cset = false;
- }
- else
- {
- /* If we collected a new SQL command, process that */
- command = create_sql_command(&line_buf, desc, semicolons + 1);
-
- /* store new command */
- if (command)
- ps.commands[index++] = command;
- }
+ /* store new command */
+ if (command)
+ ps.commands[index++] = command;
/* If we reached a backslash, process that */
if (sr == PSCAN_BACKSLASH)
@@ -4708,56 +4637,39 @@ ParseScript(const char *script, const char *desc, int weight)
if (command)
{
/*
- * If this is gset/cset, merge into the preceding command. (We
+ * If this is gset, merge into the preceding command. (We
* don't use a command slot in this case).
*/
- if (command->meta == META_CSET ||
- command->meta == META_GSET)
+ if (command->meta == META_GSET)
{
- int cindex;
Command *cmd;
- /*
- * If \cset is seen, set flag to leave the command pending
- * for the next iteration to process.
- */
- saw_cset = command->meta == META_CSET;
-
if (index == 0)
syntax_error(desc, lineno, NULL, NULL,
- "\\gset/cset cannot start a script",
+ "\\gset cannot start a script",
NULL, -1);
cmd = ps.commands[index - 1];
if (cmd->type != SQL_COMMAND)
syntax_error(desc, lineno, NULL, NULL,
- "\\gset/cset must follow a SQL command",
+ "\\gset must follow a SQL command",
cmd->first_line, -1);
- /* this {g,c}set applies to the previous query */
- cindex = cmd->nqueries - 1;
-
/*
- * now that we know there's a {g,c}set in this command,
- * allocate space for the variable name prefix array.
- */
- allocate_command_varprefix(cmd, cmd->nqueries);
-
- /*
- * Don't allow the previous command be a gset/cset; that
+ * Don't allow the previous command be a gset; that
* would make no sense.
*/
- if (cmd->varprefix[cindex] != NULL)
+ if (cmd->varprefix != NULL)
syntax_error(desc, lineno, NULL, NULL,
- "\\gset/cset cannot follow one another",
+ "\\gset cannot follow one another",
NULL, -1);
/* get variable prefix */
if (command->argc <= 1 || command->argv[1][0] == '\0')
- cmd->varprefix[cindex] = ""; /* avoid strdup */
+ cmd->varprefix = pg_strdup("");
else
- cmd->varprefix[cindex] = pg_strdup(command->argv[1]);
+ cmd->varprefix = pg_strdup(command->argv[1]);
/* cleanup unused command */
free_command(command);
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index 45888dc12e..e7e0042790 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -538,22 +538,18 @@ pgbench(
}
});
-# working \gset and \cset
+# working \gset
pgbench(
'-t 1', 0,
- [ qr{type: .*/001_pgbench_gset_and_cset}, qr{processed: 1/1} ],
+ [ qr{type: .*/001_pgbench_gset}, qr{processed: 1/1} ],
[ qr{command=3.: int 0\b},
qr{command=5.: int 1\b},
qr{command=6.: int 2\b},
qr{command=8.: int 3\b},
- qr{command=9.: int 4\b},
- qr{command=10.: int 5\b},
- qr{command=12.: int 6\b},
- qr{command=13.: int 7\b},
- qr{command=14.: int 8\b},
- qr{command=16.: int 9\b} ],
- 'pgbench gset and cset commands',
- { '001_pgbench_gset_and_cset' => q{-- test gset and cset
+ qr{command=10.: int 4\b},
+ qr{command=12.: int 5\b} ],
+ 'pgbench gset command',
+ { '001_pgbench_gset' => q{-- test gset
-- no columns
SELECT \gset
-- one value
@@ -563,21 +559,15 @@ SELECT 0 AS i0 \gset
SELECT 1 AS i1, 2 AS i2 \gset
\set i debug(:i1)
\set i debug(:i2)
--- cset & gset to follow
-SELECT :i2 + 1 AS i3, :i2 * :i2 AS i4 \cset
- SELECT 5 AS i5 \gset
-\set i debug(:i3)
-\set i debug(:i4)
-\set i debug(:i5)
-- with prefix
-SELECT 6 AS i6, 7 AS i7 \cset x_
- SELECT 8 AS i8 \gset y_
-\set i debug(:x_i6)
-\set i debug(:x_i7)
-\set i debug(:y_i8)
+SELECT 3 AS i3 \gset x_
+\set i debug(:x_i3)
-- overwrite existing variable
-SELECT 0 AS i9, 9 AS i9 \gset
-\set i debug(:i9)
+SELECT 0 AS i4, 4 AS i4 \gset
+\set i debug(:i4)
+-- work on the last SQL command under \;
+\; \; SELECT 0 AS i5 \; SELECT 5 AS i5 \; \; \gset
+\set i debug(:i5)
} });
# trigger many expression errors
@@ -772,20 +762,17 @@ SELECT LEAST(}.join(', ', (':i') x 256).q{)}
[ 'bad boolean', 2,
[qr{malformed variable.*trueXXX}], q{\set b :badtrue or true} ],
- # GSET & CSET
+ # GSET
[ 'gset no row', 2,
[qr{expected one row, got 0\b}], q{SELECT WHERE FALSE \gset} ],
- [ 'cset no row', 2,
- [qr{expected one row, got 0\b}], q{SELECT WHERE FALSE \cset
-SELECT 1 AS i\gset}, 1 ],
- [ 'gset alone', 1, [qr{gset/cset cannot start a script}], q{\gset} ],
+ [ 'gset alone', 1, [qr{gset cannot start a script}], q{\gset} ],
[ 'gset no SQL', 1,
- [qr{gset/cset must follow a SQL command}], q{\set i +1
+ [qr{gset must follow a SQL command}], q{\set i +1
\gset} ],
[ 'gset too many arguments', 1,
[qr{too many arguments}], q{SELECT 1 \gset a b} ],
[ 'gset after gset', 1,
- [qr{gset/cset cannot follow one another}], q{SELECT 1 AS i \gset
+ [qr{gset cannot follow one another}], q{SELECT 1 AS i \gset
\gset} ],
[ 'gset non SELECT', 2,
[qr{expected one row, got 0}],
diff --git a/src/fe_utils/psqlscan.l b/src/fe_utils/psqlscan.l
index 321744cddb..b31527b94f 100644
--- a/src/fe_utils/psqlscan.l
+++ b/src/fe_utils/psqlscan.l
@@ -693,15 +693,8 @@ other .
* substitution. We want these before {self}, also.
*/
-"\\"; {
- /* Count semicolons in compound commands */
- cur_state->escaped_semicolons++;
- /* Force a semicolon into the query buffer */
- psqlscan_emit(cur_state, yytext + 1, 1);
- }
-
-"\\": {
- /* Force a colon into the query buffer */
+"\\"[;:] {
+ /* Force a semi-colon or colon into the query buffer */
psqlscan_emit(cur_state, yytext + 1, 1);
}
@@ -1072,9 +1065,6 @@ psql_scan(PsqlScanState state,
/* Set current output target */
state->output_buf = query_buf;
- /* Reset number of escaped semicolons seen */
- state->escaped_semicolons = 0;
-
/* Set input source */
if (state->buffer_stack != NULL)
yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
@@ -1218,16 +1208,6 @@ psql_scan_reset(PsqlScanState state)
state->dolqstart = NULL;
}
-/*
- * Return the number of escaped semicolons in the lexed string seen by the
- * previous psql_scan call.
- */
-int
-psql_scan_get_escaped_semicolons(PsqlScanState state)
-{
- return state->escaped_semicolons;
-}
-
/*
* Reselect this lexer (psqlscan.l) after using another one.
*
diff --git a/src/include/fe_utils/psqlscan.h b/src/include/fe_utils/psqlscan.h
index d6fef9ff77..1cf5b2e7fa 100644
--- a/src/include/fe_utils/psqlscan.h
+++ b/src/include/fe_utils/psqlscan.h
@@ -90,8 +90,6 @@ extern PsqlScanResult psql_scan(PsqlScanState state,
extern void psql_scan_reset(PsqlScanState state);
-extern int psql_scan_get_escaped_semicolons(PsqlScanState state);
-
extern void psql_scan_reselect_sql_lexer(PsqlScanState state);
extern bool psql_scan_in_quote(PsqlScanState state);
diff --git a/src/include/fe_utils/psqlscan_int.h b/src/include/fe_utils/psqlscan_int.h
index 752cc9406a..42a738f422 100644
--- a/src/include/fe_utils/psqlscan_int.h
+++ b/src/include/fe_utils/psqlscan_int.h
@@ -112,7 +112,6 @@ typedef struct PsqlScanStateData
int start_state; /* yylex's starting/finishing state */
int paren_depth; /* depth of nesting in parentheses */
int xcdepth; /* depth of nesting in slash-star comments */
- int escaped_semicolons; /* number of embedded (\;) semicolons */
char *dolqstart; /* current $foo$ quote start string */
/*
On 2019-Mar-23, Fabien COELHO wrote:
Per your request, here is a patch which removes \cset from pgbench. Sigh.
Again, only the removal is a little deeper. This lifts the constraint about
not using empty queries in a compound statement, at the price of some added
logic to detect the last result.
Thank you very much! I've pushed this.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services