00000040  62 65 6e 63 68 2e 73 67  6d 6c 0d 0a 69 6e 64 65

Hmmm. "0d 0a" looks bad.

I've meddled into "/etc/mime.types" to change "text/x-diff" to "text/plain" used by some other mailers. Here is the v6 version again, size is 38424 and md5sum is 63d79c0d5a93294f002edc640a3f525b.

--
Fabien.
diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml
index 3dd492c..c203c41 100644
--- a/doc/src/sgml/ref/pgbench.sgml
+++ b/doc/src/sgml/ref/pgbench.sgml
@@ -895,6 +895,21 @@ pgbench <optional> <replaceable>options</replaceable> 
</optional> <replaceable>d
   </para>
 
   <variablelist>
+   <varlistentry>
+    <term><literal>\if</literal> <replaceable 
class="parameter">expression</replaceable></term>
+    <term><literal>\elif</literal> <replaceable 
class="parameter">expression</replaceable></term>
+    <term><literal>\else</literal></term>
+    <term><literal>\endif</literal></term>
+    <listitem>
+     <para> 
+      This group of commands implements nestable conditional blocks,
+      similarly to <literal>psql</literal>'s <xref 
linkend="psql-metacommand-if"/>.
+      Conditional expressions are identical to those with 
<literal>\set</literal>,
+      with non-zero values interpreted as true.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry id='pgbench-metacommand-set'>
     <term>
      <literal>\set <replaceable>varname</replaceable> 
<replaceable>expression</replaceable></literal>
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index fce7e3a..e88f782 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -2148,7 +2148,7 @@ hello 10
       </varlistentry>
 
 
-      <varlistentry>
+      <varlistentry id="psql-metacommand-if">
         <term><literal>\if</literal> <replaceable 
class="parameter">expression</replaceable></term>
         <term><literal>\elif</literal> <replaceable 
class="parameter">expression</replaceable></term>
         <term><literal>\else</literal></term>
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 31ea6ca..b5ebefc 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -32,6 +32,7 @@
 #endif                                                 /* ! WIN32 */
 
 #include "postgres_fe.h"
+#include "fe_utils/conditional.h"
 
 #include "getopt_long.h"
 #include "libpq-fe.h"
@@ -274,6 +275,9 @@ typedef enum
         * and we enter the CSTATE_SLEEP state to wait for it to expire. Other
         * meta-commands are executed immediately.
         *
+        * CSTATE_SKIP_COMMAND for conditional branches which are not executed,
+        * quickly skip commands that do not need any evaluation.
+        *
         * CSTATE_WAIT_RESULT waits until we get a result set back from the 
server
         * for the current command.
         *
@@ -283,6 +287,7 @@ typedef enum
         * command counter, and loops back to CSTATE_START_COMMAND state.
         */
        CSTATE_START_COMMAND,
+       CSTATE_SKIP_COMMAND,
        CSTATE_WAIT_RESULT,
        CSTATE_SLEEP,
        CSTATE_END_COMMAND,
@@ -312,6 +317,7 @@ typedef struct
        PGconn     *con;                        /* connection handle to DB */
        int                     id;                             /* client No. */
        ConnectionStateEnum state;      /* state machine's current state. */
+       ConditionalStack cstack;        /* enclosing conditionals state */
 
        int                     use_file;               /* index in sql_script 
for this client */
        int                     command;                /* command number in 
script */
@@ -400,7 +406,11 @@ typedef enum MetaCommand
        META_SET,                                       /* \set */
        META_SETSHELL,                          /* \setshell */
        META_SHELL,                                     /* \shell */
-       META_SLEEP                                      /* \sleep */
+       META_SLEEP,                                     /* \sleep */
+       META_IF,                                        /* \if */
+       META_ELIF,                                      /* \elif */
+       META_ELSE,                                      /* \else */
+       META_ENDIF                                      /* \endif */
 } MetaCommand;
 
 typedef enum QueryMode
@@ -1587,6 +1597,7 @@ setBoolValue(PgBenchValue *pv, bool bval)
        pv->type = PGBT_BOOLEAN;
        pv->u.bval = bval;
 }
+
 /* assign an integer value */
 static void
 setIntValue(PgBenchValue *pv, int64 ival)
@@ -2295,6 +2306,14 @@ getMetaCommand(const char *cmd)
                mc = META_SHELL;
        else if (pg_strcasecmp(cmd, "sleep") == 0)
                mc = META_SLEEP;
+       else if (pg_strcasecmp(cmd, "if") == 0)
+               mc = META_IF;
+       else if (pg_strcasecmp(cmd, "elif") == 0)
+               mc = META_ELIF;
+       else if (pg_strcasecmp(cmd, "else") == 0)
+               mc = META_ELSE;
+       else if (pg_strcasecmp(cmd, "endif") == 0)
+               mc = META_ENDIF;
        else
                mc = META_NONE;
        return mc;
@@ -2416,11 +2435,11 @@ preparedStatementName(char *buffer, int file, int state)
 }
 
 static void
-commandFailed(CState *st, const char *message)
+commandFailed(CState *st, const char *cmd, const char *message)
 {
        fprintf(stderr,
-                       "client %d aborted in command %d of script %d; %s\n",
-                       st->id, st->command, st->use_file, message);
+                       "client %d aborted in command %d (%s) of script %d; 
%s\n",
+                       st->id, st->command, cmd, st->use_file, message);
 }
 
 /* return a script number with a weighted choice. */
@@ -2608,6 +2627,8 @@ doCustom(TState *thread, CState *st, StatsData *agg)
                                        st->state = CSTATE_START_THROTTLE;
                                else
                                        st->state = CSTATE_START_TX;
+                               /* check consistency */
+                               Assert(conditional_stack_empty(st->cstack));
                                break;
 
                                /*
@@ -2773,7 +2794,7 @@ doCustom(TState *thread, CState *st, StatsData *agg)
                                {
                                        if (!sendCommand(st, command))
                                        {
-                                               commandFailed(st, "SQL command 
send failed");
+                                               commandFailed(st, "SQL", "SQL 
command send failed");
                                                st->state = CSTATE_ABORTED;
                                        }
                                        else
@@ -2806,7 +2827,7 @@ doCustom(TState *thread, CState *st, StatsData *agg)
 
                                                if (!evaluateSleep(st, argc, 
argv, &usec))
                                                {
-                                                       commandFailed(st, 
"execution of meta-command 'sleep' failed");
+                                                       commandFailed(st, 
"sleep", "execution of meta-command failed");
                                                        st->state = 
CSTATE_ABORTED;
                                                        break;
                                                }
@@ -2817,77 +2838,209 @@ doCustom(TState *thread, CState *st, StatsData *agg)
                                                st->state = CSTATE_SLEEP;
                                                break;
                                        }
-                                       else
+                                       else if (command->meta == META_SET ||
+                                                        command->meta == 
META_IF ||
+                                                        command->meta == 
META_ELIF)
                                        {
+                                               /* backslash commands with an 
expression to evaluate */
+                                               PgBenchExpr *expr = 
command->expr;
+                                               PgBenchValue result;
+
+                                               if (command->meta == META_ELIF 
&&
+                                                       
conditional_stack_peek(st->cstack) == IFSTATE_TRUE)
+                                               {
+                                                       /* elif after executed 
block, skip eval and wait for endif */
+                                                       
conditional_stack_poke(st->cstack, IFSTATE_IGNORED);
+                                                       goto 
move_to_end_command;
+                                               }
+
+                                               if (!evaluateExpr(thread, st, 
expr, &result))
+                                               {
+                                                       commandFailed(st, 
argv[0], "evaluation of meta-command failed");
+                                                       st->state = 
CSTATE_ABORTED;
+                                                       break;
+                                               }
+
                                                if (command->meta == META_SET)
                                                {
-                                                       PgBenchExpr *expr = 
command->expr;
-                                                       PgBenchValue result;
-
-                                                       if 
(!evaluateExpr(thread, st, expr, &result))
-                                                       {
-                                                               
commandFailed(st, "evaluation of meta-command 'set' failed");
-                                                               st->state = 
CSTATE_ABORTED;
-                                                               break;
-                                                       }
-
                                                        if 
(!putVariableValue(st, argv[0], argv[1], &result))
                                                        {
-                                                               
commandFailed(st, "assignment of meta-command 'set' failed");
+                                                               
commandFailed(st, "set", "assignment of meta-command failed");
                                                                st->state = 
CSTATE_ABORTED;
                                                                break;
                                                        }
                                                }
-                                               else if (command->meta == 
META_SETSHELL)
+                                               else /* if and elif evaluated 
cases */
                                                {
-                                                       bool            ret = 
runShellCommand(st, argv[1], argv + 2, argc - 2);
+                                                       bool cond = 
valueTruth(&result);
 
-                                                       if (timer_exceeded) /* 
timeout */
+                                                       /* execute or not 
depending on evaluated condition */
+                                                       if (command->meta == 
META_IF)
                                                        {
-                                                               st->state = 
CSTATE_FINISHED;
-                                                               break;
-                                                       }
-                                                       else if (!ret)  /* on 
error */
-                                                       {
-                                                               
commandFailed(st, "execution of meta-command 'setshell' failed");
-                                                               st->state = 
CSTATE_ABORTED;
-                                                               break;
+                                                               
conditional_stack_push(st->cstack, cond ? IFSTATE_TRUE : IFSTATE_FALSE);
                                                        }
-                                                       else
+                                                       else /* elif */
                                                        {
-                                                               /* succeeded */
+                                                               /* we should 
get here only if the "elif" needed evaluation */
+                                                               
Assert(conditional_stack_peek(st->cstack) == IFSTATE_FALSE);
+                                                               
conditional_stack_poke(st->cstack, cond ? IFSTATE_TRUE : IFSTATE_FALSE);
                                                        }
                                                }
-                                               else if (command->meta == 
META_SHELL)
+                                       }
+                                       else if (command->meta == META_ELSE)
+                                       {
+                                               switch 
(conditional_stack_peek(st->cstack))
+                                               {
+                                                       case IFSTATE_TRUE:
+                                                               
conditional_stack_poke(st->cstack, IFSTATE_ELSE_FALSE);
+                                                               break;
+                                                       case IFSTATE_FALSE: /* 
inconsistent if active */
+                                                       case IFSTATE_IGNORED: 
/* inconsistent if active */
+                                                       case IFSTATE_NONE: /* 
else without if */
+                                                       case IFSTATE_ELSE_TRUE: 
/* else after else */
+                                                       case 
IFSTATE_ELSE_FALSE: /* else after else */
+                                                       default:
+                                                               /* dead code if 
conditional check is ok */
+                                                               Assert(false);
+                                               }
+                                               goto move_to_end_command;
+                                       }
+                                       else if (command->meta == META_ENDIF)
+                                       {
+                                               
Assert(!conditional_stack_empty(st->cstack));
+                                               
conditional_stack_pop(st->cstack);
+                                               goto move_to_end_command;
+                                       }
+                                       else if (command->meta == META_SETSHELL)
+                                       {
+                                               bool            ret = 
runShellCommand(st, argv[1], argv + 2, argc - 2);
+
+                                               if (timer_exceeded) /* timeout 
*/
+                                               {
+                                                       st->state = 
CSTATE_FINISHED;
+                                                       break;
+                                               }
+                                               else if (!ret)  /* on error */
+                                               {
+                                                       commandFailed(st, 
"setshell", "execution of meta-command failed");
+                                                       st->state = 
CSTATE_ABORTED;
+                                                       break;
+                                               }
+                                               else
+                                               {
+                                                       /* succeeded */
+                                               }
+                                       }
+                                       else if (command->meta == META_SHELL)
+                                       {
+                                               bool            ret = 
runShellCommand(st, NULL, argv + 1, argc - 1);
+
+                                               if (timer_exceeded) /* timeout 
*/
+                                               {
+                                                       st->state = 
CSTATE_FINISHED;
+                                                       break;
+                                               }
+                                               else if (!ret)  /* on error */
                                                {
-                                                       bool            ret = 
runShellCommand(st, NULL, argv + 1, argc - 1);
+                                                       commandFailed(st, 
"shell", "execution of meta-command failed");
+                                                       st->state = 
CSTATE_ABORTED;
+                                                       break;
+                                               }
+                                               else
+                                               {
+                                                       /* succeeded */
+                                               }
+                                       }
+
+                                       move_to_end_command:
+                                       /*
+                                        * executing the expression or shell 
command might
+                                        * take a non-negligible amount of 
time, so reset
+                                        * 'now'
+                                        */
+                                       INSTR_TIME_SET_ZERO(now);
+
+                                       st->state = CSTATE_END_COMMAND;
+                               }
+                               break;
+
+                               /*
+                                * non executed conditional branch
+                                */
+                       case CSTATE_SKIP_COMMAND:
+                               Assert(!conditional_active(st->cstack));
+                               /* quickly skip commands until something to 
do... */
+                               while (true)
+                               {
+                                       command = 
sql_script[st->use_file].commands[st->command];
+
+                                       /* cannot reach end of script in that 
state */
+                                       Assert(command != NULL);
 
-                                                       if (timer_exceeded) /* 
timeout */
+                                       /* if this is conditional related, 
update conditional state */
+                                       if (command->type == META_COMMAND &&
+                                               (command->meta == META_IF ||
+                                                command->meta == META_ELIF ||
+                                                command->meta == META_ELSE ||
+                                                command->meta == META_ENDIF))
+                                       {
+                                               switch 
(conditional_stack_peek(st->cstack))
+                                               {
+                                               case IFSTATE_FALSE:
+                                                       if (command->meta == 
META_IF || command->meta == META_ELIF)
                                                        {
-                                                               st->state = 
CSTATE_FINISHED;
-                                                               break;
+                                                               /* we must 
evaluate the condition */
+                                                               st->state = 
CSTATE_START_COMMAND;
                                                        }
-                                                       else if (!ret)  /* on 
error */
+                                                       else if (command->meta 
== META_ELSE)
                                                        {
-                                                               
commandFailed(st, "execution of meta-command 'shell' failed");
-                                                               st->state = 
CSTATE_ABORTED;
-                                                               break;
+                                                               /* we must 
execute next command */
+                                                               
conditional_stack_poke(st->cstack, IFSTATE_ELSE_TRUE);
+                                                               st->state = 
CSTATE_START_COMMAND;
+                                                               st->command++;
                                                        }
-                                                       else
+                                                       else if (command->meta 
== META_ENDIF)
                                                        {
-                                                               /* succeeded */
+                                                               
Assert(!conditional_stack_empty(st->cstack));
+                                                               
conditional_stack_pop(st->cstack);
+                                                               if 
(conditional_active(st->cstack))
+                                                                       
st->state = CSTATE_START_COMMAND;
+                                                               /* else state 
remains in CSTATE_SKIP_COMMAND */
+                                                               st->command++;
                                                        }
-                                               }
+                                                       break;
 
-                                               /*
-                                                * executing the expression or 
shell command might
-                                                * take a non-negligible amount 
of time, so reset
-                                                * 'now'
-                                                */
-                                               INSTR_TIME_SET_ZERO(now);
+                                               case IFSTATE_IGNORED:
+                                               case IFSTATE_ELSE_FALSE:
+                                                       if (command->meta == 
META_IF)
+                                                               
conditional_stack_push(st->cstack, IFSTATE_IGNORED);
+                                                       else if (command->meta 
== META_ENDIF)
+                                                       {
+                                                               
Assert(!conditional_stack_empty(st->cstack));
+                                                               
conditional_stack_pop(st->cstack);
+                                                               if 
(conditional_active(st->cstack))
+                                                                       
st->state = CSTATE_START_COMMAND;
+                                                       }
+                                                       /* could detect "else" 
& "elif" after "else" */
+                                                       st->command++;
+                                                       break;
 
-                                               st->state = CSTATE_END_COMMAND;
+                                               case IFSTATE_NONE:
+                                               case IFSTATE_TRUE:
+                                               case IFSTATE_ELSE_TRUE:
+                                               default:
+                                                       /* inconsistent if 
inactive, unreachable dead code */
+                                                       Assert(false);
+                                               }
                                        }
+                                       else
+                                       {
+                                               /* skip and consider next */
+                                               st->command++;
+                                       }
+
+                                       if (st->state != CSTATE_SKIP_COMMAND)
+                                               break;
                                }
                                break;
 
@@ -2900,7 +3053,7 @@ doCustom(TState *thread, CState *st, StatsData *agg)
                                        fprintf(stderr, "client %d 
receiving\n", st->id);
                                if (!PQconsumeInput(st->con))
                                {                               /* there's 
something wrong */
-                                       commandFailed(st, "perhaps the backend 
died while processing");
+                                       commandFailed(st, "SQL", "perhaps the 
backend died while processing");
                                        st->state = CSTATE_ABORTED;
                                        break;
                                }
@@ -2922,7 +3075,7 @@ doCustom(TState *thread, CState *st, StatsData *agg)
                                                st->state = CSTATE_END_COMMAND;
                                                break;
                                        default:
-                                               commandFailed(st, 
PQerrorMessage(st->con));
+                                               commandFailed(st, "SQL", 
PQerrorMessage(st->con));
                                                PQclear(res);
                                                st->state = CSTATE_ABORTED;
                                                break;
@@ -2966,9 +3119,10 @@ doCustom(TState *thread, CState *st, StatsData *agg)
                                                                         
INSTR_TIME_GET_DOUBLE(st->stmt_begin));
                                }
 
-                               /* Go ahead with next command */
+                               /* Go ahead with next command, to be executed 
or skipped */
                                st->command++;
-                               st->state = CSTATE_START_COMMAND;
+                               st->state = conditional_active(st->cstack) ?
+                                       CSTATE_START_COMMAND : 
CSTATE_SKIP_COMMAND;
                                break;
 
                                /*
@@ -2979,6 +3133,13 @@ doCustom(TState *thread, CState *st, StatsData *agg)
                                /* transaction finished: calculate latency and 
do log */
                                processXactStats(thread, st, &now, false, agg);
 
+                               /* conditional stack must be empty */
+                               if (!conditional_stack_empty(st->cstack))
+                               {
+                                       fprintf(stderr, "end of script reached 
within a conditional, missing \\endif\n");
+                                       exit(1);
+                               }
+
                                if (is_connect)
                                {
                                        PQfinish(st->con);
@@ -3799,19 +3960,25 @@ process_backslash_command(PsqlScanState sstate, const 
char *source)
        /* ... and convert it to enum form */
        my_command->meta = getMetaCommand(my_command->argv[0]);
 
-       if (my_command->meta == META_SET)
+       if (my_command->meta == META_SET ||
+               my_command->meta == META_IF ||
+               my_command->meta == META_ELIF)
        {
-               /* For \set, collect var name, then lex the expression. */
                yyscan_t        yyscanner;
 
-               if (!expr_lex_one_word(sstate, &word_buf, &word_offset))
-                       syntax_error(source, lineno, my_command->line, 
my_command->argv[0],
-                                                "missing argument", NULL, -1);
+               /* For \set, collect var name */
+               if (my_command->meta == META_SET)
+               {
+                       if (!expr_lex_one_word(sstate, &word_buf, &word_offset))
+                               syntax_error(source, lineno, my_command->line, 
my_command->argv[0],
+                                                        "missing argument", 
NULL, -1);
 
-               offsets[j] = word_offset;
-               my_command->argv[j++] = pg_strdup(word_buf.data);
-               my_command->argc++;
+                       offsets[j] = word_offset;
+                       my_command->argv[j++] = pg_strdup(word_buf.data);
+                       my_command->argc++;
+               }
 
+               /* then for all parse the expression */
                yyscanner = expr_scanner_init(sstate, source, lineno, 
start_offset,
                                                                          
my_command->argv[0]);
 
@@ -3907,6 +4074,12 @@ process_backslash_command(PsqlScanState sstate, const 
char *source)
                        syntax_error(source, lineno, my_command->line, 
my_command->argv[0],
                                                 "missing command", NULL, -1);
        }
+       else if (my_command->meta == META_ELSE || my_command->meta == 
META_ENDIF)
+       {
+               if (my_command->argc != 1)
+                       syntax_error(source, lineno, my_command->line, 
my_command->argv[0],
+                                                "unexpected argument", NULL, 
-1);
+       }
        else
        {
                /* my_command->meta == META_NONE */
@@ -3919,6 +4092,62 @@ process_backslash_command(PsqlScanState sstate, const 
char *source)
        return my_command;
 }
 
+static void
+ConditionError(const char *desc, int cmdn, const char *msg)
+{
+       fprintf(stderr,
+                       "condition error in script \"%s\" command %d: %s\n",
+                       desc, cmdn, msg);
+       exit(1);
+}
+
+/*
+ * Partial evaluation of conditionals before recording and running the script.
+ */
+static void
+CheckConditional(ParsedScript ps)
+{
+       /* statically check conditional structure */
+       ConditionalStack cs = conditional_stack_create();
+       int i;
+       for (i = 0 ; ps.commands[i] != NULL ; i++)
+       {
+               Command *cmd = ps.commands[i];
+               if (cmd->type == META_COMMAND)
+               {
+                       switch (cmd->meta)
+                       {
+                       case META_IF:
+                               conditional_stack_push(cs, IFSTATE_FALSE);
+                               break;
+                       case META_ELIF:
+                               if (conditional_stack_empty(cs))
+                                       ConditionError(ps.desc, i+1, "\\elif 
without matching \\if");
+                               if (conditional_stack_peek(cs) == 
IFSTATE_ELSE_FALSE)
+                                       ConditionError(ps.desc, i+1, "\\elif 
after \\else");
+                               break;
+                       case META_ELSE:
+                               if (conditional_stack_empty(cs))
+                                       ConditionError(ps.desc, i+1, "\\else 
without matching \\if");
+                               if (conditional_stack_peek(cs) == 
IFSTATE_ELSE_FALSE)
+                                       ConditionError(ps.desc, i+1, "\\else 
after \\else");
+                               conditional_stack_poke(cs, IFSTATE_ELSE_FALSE);
+                               break;
+                       case META_ENDIF:
+                               if (!conditional_stack_pop(cs))
+                                       ConditionError(ps.desc, i+1, "\\endif 
without matching \\if");
+                               break;
+                       default:
+                               /* ignore anything else... */
+                               break;
+                       }
+               }
+       }
+       if (!conditional_stack_empty(cs))
+               ConditionError(ps.desc, i+1, "\\if without matching \\endif");
+       conditional_stack_destroy(cs);
+}
+
 /*
  * Parse a script (either the contents of a file, or a built-in script)
  * and add it to the list of scripts.
@@ -4204,6 +4433,8 @@ addScript(ParsedScript script)
                exit(1);
        }
 
+       CheckConditional(script);
+
        sql_script[num_scripts] = script;
        num_scripts++;
 }
@@ -4950,6 +5181,12 @@ main(int argc, char **argv)
                }
        }
 
+       /* other CState initializations */
+       for (i = 0; i < nclients; i++)
+       {
+               state[i].cstack = conditional_stack_create();
+       }
+
        if (debug)
        {
                if (duration <= 0)
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl 
b/src/bin/pgbench/t/001_pgbench_with_server.pl
index a8b2962..1d3d134 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -259,6 +259,12 @@ pgbench(
                qr{command=46.: int 46\b},
                qr{command=47.: boolean true\b},
                qr{command=48.: boolean true\b},
+               qr{command=60.: int 60\b},
+               qr{command=69.: int 69\b},
+               qr{command=78.: int 78\b},
+               qr{command=81.: int 81\b},
+               qr{command=88.: int 88\b},
+               qr{command=90.: int 0\b},
        ],
        'pgbench expressions',
        {   '001_pgbench_expressions' => q{-- integer functions
@@ -338,6 +344,41 @@ pgbench(
 \set v2 5432
 \set v3 -54.21E-2
 SELECT :v0, :v1, :v2, :v3;
+-- if tests
+\set nope 0
+\if 1 > 0
+\set id debug(60)
+\elif 0
+\set nope 1
+\else
+\set nope 1
+\endif
+\if 1 < 0
+\set nope 1
+\elif 1 > 0
+\set ie debug(69)
+\else
+\set nope 1
+\endif
+\if 1 < 0
+\set nope 1
+\elif 1 < 0
+\set nope 1
+\else
+\set if debug(78)
+\endif
+\if 1 = 1
+\set ig debug(81)
+\elif 0
+\set nope 1
+\endif
+\if 1 = 0
+\set nope 1
+\elif 1 <> 0
+\set ih debug(88)
+\endif
+-- must be zero if false branches where skipped
+\set nope debug(:nope)
 } });
 
 =head
@@ -391,7 +432,7 @@ SELECT LEAST(:i, :i, :i, :i, :i, :i, :i, :i, :i, :i, :i);
 
        # SHELL
        [   'shell bad command',               0,
-               [qr{meta-command 'shell' failed}], q{\shell no-such-command} ],
+               [qr{\(shell\) .* meta-command failed}], q{\shell 
no-such-command} ],
        [   'shell undefined variable', 0,
                [qr{undefined variable ":nosuchvariable"}],
                q{-- undefined variable in shell
diff --git a/src/bin/pgbench/t/002_pgbench_no_server.pl 
b/src/bin/pgbench/t/002_pgbench_no_server.pl
index 6ea55f8..80c5aed 100644
--- a/src/bin/pgbench/t/002_pgbench_no_server.pl
+++ b/src/bin/pgbench/t/002_pgbench_no_server.pl
@@ -8,6 +8,16 @@ use warnings;
 use TestLib;
 use Test::More;
 
+# create a directory for scripts
+my $testname = $0;
+$testname =~ s,.*/,,;
+$testname =~ s/\.pl$//;
+
+my $testdir = "$TestLib::tmp_check/t_${testname}_stuff";
+mkdir $testdir
+       or
+       BAIL_OUT("could not create test directory \"${testdir}\": $!");
+
 # invoke pgbench
 sub pgbench
 {
@@ -17,6 +27,28 @@ sub pgbench
                $stat, $out, $err, $name);
 }
 
+# invoke pgbench with scripts
+sub pgbench_scripts
+{
+       my ($opts, $stat, $out, $err, $name, $files) = @_;
+       my @cmd = ('pgbench', split /\s+/, $opts);
+       my @filenames = ();
+       if (defined $files)
+       {
+               for my $fn (sort keys %$files)
+               {
+                       my $filename = $testdir . '/' . $fn;
+                       # cleanup file weight if any
+                       $filename =~ s/\@\d+$//;
+                       # cleanup from prior runs
+                       unlink $filename;
+                       append_to_file($filename, $$files{$fn});
+                       push @cmd, '-f', $filename;
+               }
+       }
+       command_checks_all(\@cmd, $stat, $out, $err, $name);
+}
+
 #
 # Option various errors
 #
@@ -125,4 +157,24 @@ pgbench(
                qr{simple-update},              qr{select-only} ],
        'pgbench builtin list');
 
+my @script_tests = (
+       # name, err, { file => contents }
+       [ 'missing endif', [qr{\\if without matching \\endif}], 
{'if-noendif.sql' => '\if 1'} ],
+       [ 'missing if on elif', [qr{\\elif without matching \\if}], 
{'elif-noif.sql' => '\elif 1'} ],
+       [ 'missing if on else', [qr{\\else without matching \\if}], 
{'else-noif.sql' => '\else'} ],
+       [ 'missing if on endif', [qr{\\endif without matching \\if}], 
{'endif-noif.sql' => '\endif'} ],
+       [ 'elif after else', [qr{\\elif after \\else}], {'else-elif.sql' => 
"\\if 1\n\\else\n\\elif 0\n\\endif"} ],
+       [ 'else after else', [qr{\\else after \\else}], {'else-else.sql' => 
"\\if 1\n\\else\n\\else\n\\endif"} ],
+       [ 'if syntax error', [qr{syntax error in command "if"}], {'if-bad.sql' 
=> "\\if\n\\endif\n"} ],
+       [ 'elif syntax error', [qr{syntax error in command "elif"}], 
{'elif-bad.sql' => "\\if 0\n\\elif +\n\\endif\n"} ],
+       [ 'else syntax error', [qr{unexpected argument in command "else"}], 
{'else-bad.sql' => "\\if 0\n\\else BAD\n\\endif\n"} ],
+       [ 'endif syntax error', [qr{unexpected argument in command "endif"}], 
{'endif-bad.sql' => "\\if 0\n\\endif BAD\n"} ],
+);
+
+for my $t (@script_tests)
+{
+       my ($name, $err, $files) = @$t;
+       pgbench_scripts('', 1, [qr{^$}], $err, 'pgbench option error: ' . 
$name, $files);
+}
+
 done_testing();
diff --git a/src/bin/psql/Makefile b/src/bin/psql/Makefile
index c8eb2f9..b3166ec 100644
--- a/src/bin/psql/Makefile
+++ b/src/bin/psql/Makefile
@@ -21,7 +21,7 @@ REFDOCDIR= $(top_srcdir)/doc/src/sgml/ref
 override CPPFLAGS := -I. -I$(srcdir) -I$(libpq_srcdir) $(CPPFLAGS)
 override LDFLAGS := -L$(top_builddir)/src/fe_utils -lpgfeutils $(libpq_pgport) 
$(LDFLAGS)
 
-OBJS=  command.o common.o conditional.o copy.o crosstabview.o \
+OBJS=  command.o common.o copy.o crosstabview.o \
        describe.o help.o input.o large_obj.o mainloop.o \
        prompt.o psqlscanslash.o sql_help.o startup.o stringutils.o \
        tab-complete.o variables.o \
diff --git a/src/bin/psql/command.h b/src/bin/psql/command.h
index 95ad02d..29a8edd 100644
--- a/src/bin/psql/command.h
+++ b/src/bin/psql/command.h
@@ -10,7 +10,7 @@
 
 #include "fe_utils/print.h"
 #include "fe_utils/psqlscan.h"
-#include "conditional.h"
+#include "fe_utils/conditional.h"
 
 
 typedef enum _backslashResult
diff --git a/src/bin/psql/conditional.c b/src/bin/psql/conditional.c
deleted file mode 100644
index cebf8c7..0000000
--- a/src/bin/psql/conditional.c
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * psql - the PostgreSQL interactive terminal
- *
- * Copyright (c) 2000-2018, PostgreSQL Global Development Group
- *
- * src/bin/psql/conditional.c
- */
-#include "postgres_fe.h"
-
-#include "conditional.h"
-
-/*
- * create stack
- */
-ConditionalStack
-conditional_stack_create(void)
-{
-       ConditionalStack cstack = pg_malloc(sizeof(ConditionalStackData));
-
-       cstack->head = NULL;
-       return cstack;
-}
-
-/*
- * destroy stack
- */
-void
-conditional_stack_destroy(ConditionalStack cstack)
-{
-       while (conditional_stack_pop(cstack))
-               continue;
-       free(cstack);
-}
-
-/*
- * Create a new conditional branch.
- */
-void
-conditional_stack_push(ConditionalStack cstack, ifState new_state)
-{
-       IfStackElem *p = (IfStackElem *) pg_malloc(sizeof(IfStackElem));
-
-       p->if_state = new_state;
-       p->query_len = -1;
-       p->paren_depth = -1;
-       p->next = cstack->head;
-       cstack->head = p;
-}
-
-/*
- * Destroy the topmost conditional branch.
- * Returns false if there was no branch to end.
- */
-bool
-conditional_stack_pop(ConditionalStack cstack)
-{
-       IfStackElem *p = cstack->head;
-
-       if (!p)
-               return false;
-       cstack->head = cstack->head->next;
-       free(p);
-       return true;
-}
-
-/*
- * Fetch the current state of the top of the stack.
- */
-ifState
-conditional_stack_peek(ConditionalStack cstack)
-{
-       if (conditional_stack_empty(cstack))
-               return IFSTATE_NONE;
-       return cstack->head->if_state;
-}
-
-/*
- * Change the state of the topmost branch.
- * Returns false if there was no branch state to set.
- */
-bool
-conditional_stack_poke(ConditionalStack cstack, ifState new_state)
-{
-       if (conditional_stack_empty(cstack))
-               return false;
-       cstack->head->if_state = new_state;
-       return true;
-}
-
-/*
- * True if there are no active \if-blocks.
- */
-bool
-conditional_stack_empty(ConditionalStack cstack)
-{
-       return cstack->head == NULL;
-}
-
-/*
- * True if we should execute commands normally; that is, the current
- * conditional branch is active, or there is no open \if block.
- */
-bool
-conditional_active(ConditionalStack cstack)
-{
-       ifState         s = conditional_stack_peek(cstack);
-
-       return s == IFSTATE_NONE || s == IFSTATE_TRUE || s == IFSTATE_ELSE_TRUE;
-}
-
-/*
- * Save current query buffer length in topmost stack entry.
- */
-void
-conditional_stack_set_query_len(ConditionalStack cstack, int len)
-{
-       Assert(!conditional_stack_empty(cstack));
-       cstack->head->query_len = len;
-}
-
-/*
- * Fetch last-recorded query buffer length from topmost stack entry.
- * Will return -1 if no stack or it was never saved.
- */
-int
-conditional_stack_get_query_len(ConditionalStack cstack)
-{
-       if (conditional_stack_empty(cstack))
-               return -1;
-       return cstack->head->query_len;
-}
-
-/*
- * Save current parenthesis nesting depth in topmost stack entry.
- */
-void
-conditional_stack_set_paren_depth(ConditionalStack cstack, int depth)
-{
-       Assert(!conditional_stack_empty(cstack));
-       cstack->head->paren_depth = depth;
-}
-
-/*
- * Fetch last-recorded parenthesis nesting depth from topmost stack entry.
- * Will return -1 if no stack or it was never saved.
- */
-int
-conditional_stack_get_paren_depth(ConditionalStack cstack)
-{
-       if (conditional_stack_empty(cstack))
-               return -1;
-       return cstack->head->paren_depth;
-}
diff --git a/src/bin/psql/conditional.h b/src/bin/psql/conditional.h
deleted file mode 100644
index 565875a..0000000
--- a/src/bin/psql/conditional.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * psql - the PostgreSQL interactive terminal
- *
- * Copyright (c) 2000-2018, PostgreSQL Global Development Group
- *
- * src/bin/psql/conditional.h
- */
-#ifndef CONDITIONAL_H
-#define CONDITIONAL_H
-
-/*
- * Possible states of a single level of \if block.
- */
-typedef enum ifState
-{
-       IFSTATE_NONE = 0,                       /* not currently in an \if 
block */
-       IFSTATE_TRUE,                           /* currently in an \if or \elif 
that is true
-                                                                * and all 
parent branches (if any) are true */
-       IFSTATE_FALSE,                          /* currently in an \if or \elif 
that is false
-                                                                * but no true 
branch has yet been seen, and
-                                                                * all parent 
branches (if any) are true */
-       IFSTATE_IGNORED,                        /* currently in an \elif that 
follows a true
-                                                                * branch, or 
the whole \if is a child of a
-                                                                * false parent 
branch */
-       IFSTATE_ELSE_TRUE,                      /* currently in an \else that 
is true and all
-                                                                * parent 
branches (if any) are true */
-       IFSTATE_ELSE_FALSE                      /* currently in an \else that 
is false or
-                                                                * ignored */
-} ifState;
-
-/*
- * The state of nested \ifs is stored in a stack.
- *
- * query_len is used to determine what accumulated text to throw away at the
- * end of an inactive branch.  (We could, perhaps, teach the lexer to not add
- * stuff to the query buffer in the first place when inside an inactive branch;
- * but that would be very invasive.)  We also need to save and restore the
- * lexer's parenthesis nesting depth when throwing away text.  (We don't need
- * to save and restore any of its other state, such as comment nesting depth,
- * because a backslash command could never appear inside a comment or SQL
- * literal.)
- */
-typedef struct IfStackElem
-{
-       ifState         if_state;               /* current state, see enum 
above */
-       int                     query_len;              /* length of query_buf 
at last branch start */
-       int                     paren_depth;    /* parenthesis depth at last 
branch start */
-       struct IfStackElem *next;       /* next surrounding \if, if any */
-} IfStackElem;
-
-typedef struct ConditionalStackData
-{
-       IfStackElem *head;
-}                      ConditionalStackData;
-
-typedef struct ConditionalStackData *ConditionalStack;
-
-
-extern ConditionalStack conditional_stack_create(void);
-
-extern void conditional_stack_destroy(ConditionalStack cstack);
-
-extern void conditional_stack_push(ConditionalStack cstack, ifState new_state);
-
-extern bool conditional_stack_pop(ConditionalStack cstack);
-
-extern ifState conditional_stack_peek(ConditionalStack cstack);
-
-extern bool conditional_stack_poke(ConditionalStack cstack, ifState new_state);
-
-extern bool conditional_stack_empty(ConditionalStack cstack);
-
-extern bool conditional_active(ConditionalStack cstack);
-
-extern void conditional_stack_set_query_len(ConditionalStack cstack, int len);
-
-extern int     conditional_stack_get_query_len(ConditionalStack cstack);
-
-extern void conditional_stack_set_paren_depth(ConditionalStack cstack, int 
depth);
-
-extern int     conditional_stack_get_paren_depth(ConditionalStack cstack);
-
-#endif                                                 /* CONDITIONAL_H */
diff --git a/src/bin/psql/prompt.h b/src/bin/psql/prompt.h
index 2354b8f..3a84565 100644
--- a/src/bin/psql/prompt.h
+++ b/src/bin/psql/prompt.h
@@ -10,7 +10,7 @@
 
 /* enum promptStatus_t is now defined by psqlscan.h */
 #include "fe_utils/psqlscan.h"
-#include "conditional.h"
+#include "fe_utils/conditional.h"
 
 char      *get_prompt(promptStatus_t status, ConditionalStack cstack);
 
diff --git a/src/bin/psql/psqlscanslash.l b/src/bin/psql/psqlscanslash.l
index 5e0bd0e..34df35e 100644
--- a/src/bin/psql/psqlscanslash.l
+++ b/src/bin/psql/psqlscanslash.l
@@ -19,7 +19,7 @@
 #include "postgres_fe.h"
 
 #include "psqlscanslash.h"
-#include "conditional.h"
+#include "fe_utils/conditional.h"
 
 #include "libpq-fe.h"
 }
diff --git a/src/fe_utils/Makefile b/src/fe_utils/Makefile
index 3f4ba8b..5362cff 100644
--- a/src/fe_utils/Makefile
+++ b/src/fe_utils/Makefile
@@ -19,7 +19,7 @@ include $(top_builddir)/src/Makefile.global
 
 override CPPFLAGS := -DFRONTEND -I$(libpq_srcdir) $(CPPFLAGS)
 
-OBJS = mbprint.o print.o psqlscan.o simple_list.o string_utils.o
+OBJS = mbprint.o print.o psqlscan.o simple_list.o string_utils.o conditional.o
 
 all: libpgfeutils.a
 
diff --git a/src/fe_utils/conditional.c b/src/fe_utils/conditional.c
new file mode 100644
index 0000000..ac6487d
--- /dev/null
+++ b/src/fe_utils/conditional.c
@@ -0,0 +1,174 @@
+/*
+ * psql - the PostgreSQL interactive terminal
+ *
+ * Copyright (c) 2000-2018, PostgreSQL Global Development Group
+ *
+ * src/bin/psql/conditional.c
+ */
+#include "postgres_fe.h"
+
+#include "fe_utils/conditional.h"
+
+/*
+ * create stack
+ */
+ConditionalStack
+conditional_stack_create(void)
+{
+       ConditionalStack cstack = pg_malloc(sizeof(ConditionalStackData));
+
+       cstack->head = NULL;
+       return cstack;
+}
+
+/*
+ * destroy stack
+ */
+void
+conditional_stack_destroy(ConditionalStack cstack)
+{
+       while (conditional_stack_pop(cstack))
+               continue;
+       free(cstack);
+}
+
+/*
+ * Create a new conditional branch.
+ */
+void
+conditional_stack_push(ConditionalStack cstack, ifState new_state)
+{
+       IfStackElem *p = (IfStackElem *) pg_malloc(sizeof(IfStackElem));
+
+       p->if_state = new_state;
+       p->query_len = -1;
+       p->paren_depth = -1;
+       p->next = cstack->head;
+       cstack->head = p;
+}
+
+/*
+ * Destroy the topmost conditional branch.
+ * Returns false if there was no branch to end.
+ */
+bool
+conditional_stack_pop(ConditionalStack cstack)
+{
+       IfStackElem *p = cstack->head;
+
+       if (!p)
+               return false;
+       cstack->head = cstack->head->next;
+       free(p);
+       return true;
+}
+
+/*
+ * Returns current stack depth, for debugging purposes.
+ */
+int
+conditional_stack_depth(ConditionalStack cstack)
+{
+       if (cstack == NULL)
+               return -1;
+       else
+       {
+               IfStackElem     *p = cstack->head;
+               int                     depth = 0;
+               while (p != NULL)
+               {
+                       depth++;
+                       p = p->next;
+               }
+               return depth;
+       }
+}
+
+/*
+ * Fetch the current state of the top of the stack.
+ */
+ifState
+conditional_stack_peek(ConditionalStack cstack)
+{
+       if (conditional_stack_empty(cstack))
+               return IFSTATE_NONE;
+       return cstack->head->if_state;
+}
+
+/*
+ * Change the state of the topmost branch.
+ * Returns false if there was no branch state to set.
+ */
+bool
+conditional_stack_poke(ConditionalStack cstack, ifState new_state)
+{
+       if (conditional_stack_empty(cstack))
+               return false;
+       cstack->head->if_state = new_state;
+       return true;
+}
+
+/*
+ * True if there are no active \if-blocks.
+ */
+bool
+conditional_stack_empty(ConditionalStack cstack)
+{
+       return cstack->head == NULL;
+}
+
+/*
+ * True if we should execute commands normally; that is, the current
+ * conditional branch is active, or there is no open \if block.
+ */
+bool
+conditional_active(ConditionalStack cstack)
+{
+       ifState         s = conditional_stack_peek(cstack);
+
+       return s == IFSTATE_NONE || s == IFSTATE_TRUE || s == IFSTATE_ELSE_TRUE;
+}
+
+/*
+ * Save current query buffer length in topmost stack entry.
+ */
+void
+conditional_stack_set_query_len(ConditionalStack cstack, int len)
+{
+       Assert(!conditional_stack_empty(cstack));
+       cstack->head->query_len = len;
+}
+
+/*
+ * Fetch last-recorded query buffer length from topmost stack entry.
+ * Will return -1 if no stack or it was never saved.
+ */
+int
+conditional_stack_get_query_len(ConditionalStack cstack)
+{
+       if (conditional_stack_empty(cstack))
+               return -1;
+       return cstack->head->query_len;
+}
+
+/*
+ * Save current parenthesis nesting depth in topmost stack entry.
+ */
+void
+conditional_stack_set_paren_depth(ConditionalStack cstack, int depth)
+{
+       Assert(!conditional_stack_empty(cstack));
+       cstack->head->paren_depth = depth;
+}
+
+/*
+ * Fetch last-recorded parenthesis nesting depth from topmost stack entry.
+ * Will return -1 if no stack or it was never saved.
+ */
+int
+conditional_stack_get_paren_depth(ConditionalStack cstack)
+{
+       if (conditional_stack_empty(cstack))
+               return -1;
+       return cstack->head->paren_depth;
+}
diff --git a/src/include/fe_utils/conditional.h 
b/src/include/fe_utils/conditional.h
new file mode 100644
index 0000000..55206b8
--- /dev/null
+++ b/src/include/fe_utils/conditional.h
@@ -0,0 +1,85 @@
+/*
+ * psql - the PostgreSQL interactive terminal
+ *
+ * Copyright (c) 2000-2018, PostgreSQL Global Development Group
+ *
+ * src/bin/psql/conditional.h
+ */
+#ifndef CONDITIONAL_H
+#define CONDITIONAL_H
+
+/*
+ * Possible states of a single level of \if block.
+ */
+typedef enum ifState
+{
+       IFSTATE_NONE = 0,                       /* not currently in an \if 
block */
+       IFSTATE_TRUE,                           /* currently in an \if or \elif 
that is true
+                                                                * and all 
parent branches (if any) are true */
+       IFSTATE_FALSE,                          /* currently in an \if or \elif 
that is false
+                                                                * but no true 
branch has yet been seen, and
+                                                                * all parent 
branches (if any) are true */
+       IFSTATE_IGNORED,                        /* currently in an \elif that 
follows a true
+                                                                * branch, or 
the whole \if is a child of a
+                                                                * false parent 
branch */
+       IFSTATE_ELSE_TRUE,                      /* currently in an \else that 
is true and all
+                                                                * parent 
branches (if any) are true */
+       IFSTATE_ELSE_FALSE                      /* currently in an \else that 
is false or
+                                                                * ignored */
+} ifState;
+
+/*
+ * The state of nested \ifs is stored in a stack.
+ *
+ * query_len is used to determine what accumulated text to throw away at the
+ * end of an inactive branch.  (We could, perhaps, teach the lexer to not add
+ * stuff to the query buffer in the first place when inside an inactive branch;
+ * but that would be very invasive.)  We also need to save and restore the
+ * lexer's parenthesis nesting depth when throwing away text.  (We don't need
+ * to save and restore any of its other state, such as comment nesting depth,
+ * because a backslash command could never appear inside a comment or SQL
+ * literal.)
+ */
+typedef struct IfStackElem
+{
+       ifState         if_state;               /* current state, see enum 
above */
+       int                     query_len;              /* length of query_buf 
at last branch start */
+       int                     paren_depth;    /* parenthesis depth at last 
branch start */
+       struct IfStackElem *next;       /* next surrounding \if, if any */
+} IfStackElem;
+
+typedef struct ConditionalStackData
+{
+       IfStackElem *head;
+}                      ConditionalStackData;
+
+typedef struct ConditionalStackData *ConditionalStack;
+
+
+extern ConditionalStack conditional_stack_create(void);
+
+extern void conditional_stack_destroy(ConditionalStack cstack);
+
+extern int conditional_stack_depth(ConditionalStack cstack);
+
+extern void conditional_stack_push(ConditionalStack cstack, ifState new_state);
+
+extern bool conditional_stack_pop(ConditionalStack cstack);
+
+extern ifState conditional_stack_peek(ConditionalStack cstack);
+
+extern bool conditional_stack_poke(ConditionalStack cstack, ifState new_state);
+
+extern bool conditional_stack_empty(ConditionalStack cstack);
+
+extern bool conditional_active(ConditionalStack cstack);
+
+extern void conditional_stack_set_query_len(ConditionalStack cstack, int len);
+
+extern int     conditional_stack_get_query_len(ConditionalStack cstack);
+
+extern void conditional_stack_set_paren_depth(ConditionalStack cstack, int 
depth);
+
+extern int     conditional_stack_get_paren_depth(ConditionalStack cstack);
+
+#endif                                                 /* CONDITIONAL_H */

Reply via email to