diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml
index 2517a3a..b816673 100644
--- a/doc/src/sgml/ref/pgbench.sgml
+++ b/doc/src/sgml/ref/pgbench.sgml
@@ -696,11 +696,16 @@ pgbench options> dbname>
- The format of a script file is one SQL command per line; multiline
- SQL commands are not supported. Empty lines and lines beginning with
- --> are ignored. Script file lines can also be
- meta commands>, which are interpreted by pgbench>
- itself, as described below.
+ The format of a script file is composed of lines which are each either
+ one SQL command or one meta command> interpreted by
+ pgbench> itself, as described below.
+ Meta-commands can be spread over multiple lines using backslash
+ (\>) continuations, in which case the set of continuated
+ lines is considered as just one line.
+ SQL commands may be spead over several lines and must be
+ ;>-terminated, and may contain simple dollar-quoted strings
+ over multiple lines.
+ Empty lines and lines beginning with --> are ignored.
@@ -768,7 +773,9 @@ pgbench options> dbname>
Examples:
\set ntellers 10 * :scale
-\set aid (1021 * :aid) % (100000 * :scale) + 1
+-- update an already defined aid:
+\set aid \
+ (1021 * :aid) % (100000 * :scale) + 1
@@ -931,11 +938,15 @@ pgbench options> dbname>
\setrandom tid 1 :ntellers
\setrandom delta -5000 5000
BEGIN;
-UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
+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);
+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;
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 95be62c..c10fb29 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -2430,7 +2430,10 @@ process_commands(char *buf, const char *source, const int lineno)
}
/*
- * Read a line from fd, and return it in a malloc'd buffer.
+ * Read a possibly \-continuated (for backslash commands) or ;-terminated
+ * (for SQL statements) lines from fd, and return it in a malloc'd buffer.
+ * Also handle possible $$-quotes.
+ *
* Return NULL at EOF.
*
* The buffer will typically be larger than necessary, but we don't care
@@ -2443,6 +2446,13 @@ read_line_from_file(FILE *fd)
char *buf;
size_t buflen = BUFSIZ;
size_t used = 0;
+ bool is_sql_statement = false;
+ bool is_backslash_command = false;
+ /* simplistic $$-quoting handling */
+ int ddquote_start = 0;
+ int ddquote_length = 0;
+ int ddquote_end = 0; /* where the previous $$-quote ended */
+ char *ddquote_string = NULL;
buf = (char *) palloc(buflen);
buf[0] = '\0';
@@ -2451,13 +2461,130 @@ read_line_from_file(FILE *fd)
{
size_t thislen = strlen(tmpbuf);
+ /* coldly skip comments and empty lines */
+ {
+ int i = 0;
+
+ while (i < thislen && isspace(tmpbuf[i]))
+ i++;
+
+ if (tmpbuf[i] == '\0') /* blank */
+ continue;
+
+ if (tmpbuf[i] == '-' && tmpbuf[i+1] == '-') /* comment */
+ continue;
+ }
+
/* Append tmpbuf to whatever we had already */
memcpy(buf + used, tmpbuf, thislen + 1);
used += thislen;
- /* Done if we collected a newline */
- if (thislen > 0 && tmpbuf[thislen - 1] == '\n')
- break;
+ /* Determined what the current line is */
+ if (!is_backslash_command && !is_sql_statement)
+ {
+ int i = 0;
+
+ while (i < thislen && isspace(tmpbuf[i]))
+ i++;
+
+ if (tmpbuf[i] == '\\')
+ is_backslash_command = true;
+ else if (tmpbuf[i] != '\0')
+ is_sql_statement = true;
+ }
+
+ /* Handle simple $$-quoting, may not work if several quotes on a line.
+ */
+ if (is_sql_statement && ddquote_string != NULL)
+ {
+ char * found = strstr(buf + ddquote_start + ddquote_length,
+ ddquote_string);
+ if (found != NULL)
+ {
+ pg_free(ddquote_string);
+ ddquote_string = NULL;
+ ddquote_end = found + ddquote_length - buf;
+ }
+ }
+
+ /* Is there a starting $$-quote?
+ */
+ if (is_sql_statement && ddquote_string == NULL)
+ {
+ int i = ddquote_end;
+
+ /* \$[a-zA-Z0-9]*\$ pattern */
+ while (i < thislen - 1)
+ {
+ if (buf[i] == '$') /* starting '$' */
+ {
+ int j = i+1;
+ while (j < thislen && isalnum(buf[j]))
+ j++; /* eat alnum characters */
+ if (buf[j] == '$') /* final '$' */
+ {
+ ddquote_start = i;
+ ddquote_length = j-i+1;
+ ddquote_string = pg_malloc(ddquote_length + 1);
+ strncpy(ddquote_string, buf+i, ddquote_length);
+ ddquote_string[ddquote_length] = '\0';
+ break;
+ }
+ }
+ i++;
+ }
+ }
+
+ /* If we collected a newline */
+ if (used > 0 && buf[used - 1] == '\n')
+ {
+ if (is_backslash_command)
+ {
+ /* Handle simple \-continuations */
+ if (used >= 2 && buf[used - 2] == '\\')
+ {
+ buf[used - 2] = '\0';
+ used -= 2;
+ }
+ else if (used >= 3 && buf[used - 2] == '\r' &&
+ buf[used - 3] == '\\')
+ {
+ buf[used - 3] = '\0';
+ used -= 3;
+ }
+ else
+ /* Else we are done */
+ break;
+ }
+ else if (is_sql_statement)
+ {
+ if (ddquote_string == NULL)
+ {
+ /* look for a terminating ";" */
+ int i = 2;
+
+ /* backward skip blanks */
+ while (used-i >= 0 && isspace(buf[used-i]))
+ i++;
+
+ if (used-i >= 0 && buf[used-i] == ';')
+ break;
+ }
+
+ /* scratch newline because process_commands approach to
+ * parsing is simplistic and expects just one line.
+ */
+ if (buf[used-1] == '\n')
+ buf[used-1] = ' ';
+ if (used >= 2 && buf[used-2] == '\r')
+ {
+ buf[used-2] = ' ';
+ buf[used-1] = '\0';
+ used --;
+ }
+ }
+ /* else it was a blank line */
+ }
/* Else, enlarge buf to ensure we can append next bufferload */
buflen += BUFSIZ;