On Thu, Jan 26, 2017 at 4:06 PM, Corey Huinker <corey.huin...@gmail.com>
wrote:

>
>
> On Thu, Jan 26, 2017 at 3:55 PM, Fabien COELHO <coe...@cri.ensmp.fr>
> wrote:
>
>>
>> Hello Daniel,
>>
>> A comment about control flow and variables: in branches that are not
>>> taken, variables are expanded nonetheless, in a way that can be surprising.
>>> Case in point:
>>>
>>> \set var 'ab''cd'
>>> -- select :var;
>>> \if false
>>>  select :var ;
>>> \else
>>>  select 1;
>>> \endif
>>>
>>> To avoid that kind of trouble, would it make sense not to expand
>>> variables in untaken branches?
>>>
>>
>> Hmmm. This case is somehow contrived (for a string, :'var' could be used,
>> in which case escaping would avoid the hazard), but I think that what you
>> suggest is a better behavior, if easy to implement.
>>
>> --
>> Fabien.
>>
>
> Good question, Daniel. Variable expansion seems to be done via
> psql_get_variable which is invoked via callback, which means that I might
> have to move branch_block_active into PsqlSettings. That's slightly
> different because the existing boolean is scoped with MainLoop(), but
> there's no way to get to a new MainLoop unless you're in an executing
> branch, and no way to legally exit a MainLoop with an unbalanced if-endif
> state. In short, I think it's better behavior. It does prevent someone from
> setting a variable to '\endif' and expecting that to work, like this:
>
> select case
>      when random() < 0.5 then '\endif'
>      else E'\else\n\echo fooled you\n\endif'
> end as contrived_metaprogramming
> \gset
>
> \if false
>   :contrived_metaprogramming
>
>
> I'm sure someone will argue that preventing that is a good thing. Unless
> someone sees a good reason not to move that PsqlSettings, I'll make that
> change.
>
>
And here it is
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 640fe12..fcf265b 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -2035,6 +2035,78 @@ hello 10
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><literal>\if</literal> <replaceable 
class="parameter">expr</replaceable></term>
+        <term><literal>\elif</literal> <replaceable 
class="parameter">expr</replaceable></term>
+        <term><literal>\else</literal></term>
+        <term><literal>\endif</literal></term>
+        <listitem>
+        <para>
+        This group of commands implements nestable conditional blocks, like
+        this:
+        </para>
+<programlisting>
+SELECT
+    EXISTS(SELECT 1 FROM customer) as has_customers,
+    EXISTS(SELECT 1 FROM employee) as has_employees
+\gset
+\if :has_users
+    SELECT * FROM customer ORDER BY creation_date LIMIT 5;
+\elif :has_employees
+    \echo 'no customers found'
+    SELECT * FROM employee ORDER BY creation_date LIMIT 5;
+\else
+    \if yes
+        \echo 'No customers or employees'
+    \else
+        \echo 'this should never print'
+    \endif
+\endif
+</programlisting>
+        <para>
+        Conditional blocks must begin with a <command>\if</command> and end
+        with an <command>\endif</command>, and the pairs must be found in
+        the same source file. If an EOF is reached on the main file or an
+        <command>\include</command>-ed file before all 
+        <command>\if</command>-<command>\endif</command> are matched, then
+        psql will raise an error.
+        </para>
+        <para>
+        The <command>\if</command> and <command>\elif</command> commands
+        read the rest of the line and evaluate it as a boolean expression.
+        Currently, expressions are limited to a single unquoted string
+        which is evaluated like other options booleans, so the valid values
+        are any unabiguous case insensitive matches for one of:
+        <literal>true</literal>, <literal>false</literal>, 
<literal>1</literal>,
+        <literal>0</literal>, <literal>on</literal>, <literal>off</literal>,
+        <literal>yes</literal>, <literal>no</literal>.  So 
+        <literal>t</literal>, <literal>T</literal>, and <literal>tR</literal>
+        will all match <literal>true</literal>.
+        </para>
+        <para>
+        Queries within a false branch of a conditional block will not be
+        sent to the server.
+        </para>
+        <para>
+        Non-conditional <command>\</command>-commands within a false branch
+        of a conditional block will not be evaluated for correctness. The
+        command will be ignored along with all remaining input to the end
+        of the line.
+        </para>
+        <para>
+        Expressions on <command>\if</command> and <command>\elif</command>
+        commands within a false branch of a conditional block will not be
+        evaluated.
+        </para>
+        <para>
+        A conditional block can at most one <command>\else</command> command.
+        </para>
+        <para>
+        The <command>\elif</command> command cannot follow the
+        <command>\else</command> command.
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\ir</literal> or <literal>\include_relative</literal> 
<replaceable class="parameter">filename</replaceable></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 0c164a3..8235015 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -49,6 +49,7 @@
 #include "psqlscanslash.h"
 #include "settings.h"
 #include "variables.h"
+#include "fe_utils/psqlscan_int.h"
 
 /*
  * Editable database object types.
@@ -132,7 +133,7 @@ HandleSlashCmds(PsqlScanState scan_state,
                status = PSQL_CMD_ERROR;
        }
 
-       if (status != PSQL_CMD_ERROR)
+       if (status != PSQL_CMD_ERROR && pset.active_branch)
        {
                /* eat any remaining arguments after a valid command */
                /* note we suppress evaluation of backticks here */
@@ -194,6 +195,68 @@ read_connect_arg(PsqlScanState scan_state)
        return result;
 }
 
+/*
+ * Read and interpret argument as a boolean expression.
+ * Return true if a boolean value was successfully parsed.
+ */
+static bool
+read_boolean_expression(PsqlScanState scan_state, char *action,
+                                               bool *result)
+{
+       bool    success = false;
+       char    *value = psql_scan_slash_option(scan_state,
+                                                                               
        OT_NORMAL, NULL, false);
+       /*
+        * placeholder code until ParseVariableBool() ads error detection
+        * once that patch is in place, use that instead
+        */
+       if (value)
+       {
+               size_t          len;
+
+               if (value == NULL)
+                       return false;                   /* not set -> assume 
"off" */
+
+               len = strlen(value);
+
+               if ((pg_strncasecmp(value, "true", len) == 0) ||
+                       (pg_strncasecmp(value, "yes", len) == 0) ||
+                       (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0) 
||
+                       (pg_strcasecmp(value, "1") == 0))
+               {
+                       success = true;
+                       *result = true;
+               }
+               else if ((pg_strncasecmp(value, "false", len) == 0) ||
+                                (pg_strncasecmp(value, "no", len) == 0) ||
+                                (pg_strncasecmp(value, "off", (len > 2 ? len : 
2)) == 0) ||
+                                (pg_strcasecmp(value, "0") == 0))
+               {
+                       success = true;
+                       *result = false;
+               }
+               else
+               {
+                       psql_error("\\%s: invalid boolean expression: %s\n",
+                                               action, value);
+               }
+               free(value);
+       }
+       else
+       {
+               psql_error("\\%s: no expression given\n",action);
+       }
+       return success;
+}
+
+static bool
+is_branching_command(const char *cmd)
+{
+       return ((strcmp(cmd, "if") == 0 || \
+                       strcmp(cmd, "elif") == 0 || \
+                       strcmp(cmd, "else") == 0 || \
+                       strcmp(cmd, "endif") == 0));
+}
 
 /*
  * Subroutine to actually try to execute a backslash command.
@@ -207,6 +270,14 @@ exec_command(const char *cmd,
                                                                 * failed */
        backslashResult status = PSQL_CMD_SKIP_LINE;
 
+       if (!pset.active_branch && !is_branching_command(cmd) )
+       {
+               /* Continue with an empty buffer as if the command were never 
read */
+               resetPQExpBuffer(query_buf);
+               psql_scan_reset(scan_state);
+               return status;
+       }
+
        /*
         * \a -- toggle field alignment This makes little sense but we keep it
         * around.
@@ -1000,6 +1071,141 @@ exec_command(const char *cmd,
                }
        }
 
+       else if (strcmp(cmd, "if") == 0)
+       {
+               ifState new_if_state = IFSTATE_IGNORED;
+               /*
+                * only evaluate the expression for truth if the underlying
+                * branch is active
+                */
+               if (pset.active_branch)
+               {
+                       bool if_true = false;
+                       success = read_boolean_expression(scan_state, "if", 
&if_true);
+                       if (success)
+                       {
+                               if (if_true)
+                                       new_if_state = IFSTATE_TRUE;
+                               else
+                                       new_if_state = IFSTATE_FALSE;
+                       }
+               }
+               if (success)
+               {
+                       psqlscan_branch_push(scan_state,new_if_state);
+                       pset.active_branch = psqlscan_branch_active(scan_state);
+               }
+               psql_scan_reset(scan_state);
+       }
+
+       else if (strcmp(cmd, "elif") == 0)
+       {
+               if (psqlscan_branch_empty(scan_state))
+               {
+                       psql_error("encountered un-matched \\elif\n");
+                       success = false;
+               }
+               else
+               {
+                       bool elif_true = false;
+                       switch (psqlscan_branch_get_state(scan_state))
+                       {
+                               case IFSTATE_IGNORED:
+                                       /*
+                                        * inactive branch, do nothing:
+                                        * either if-endif already had a true 
block,
+                                        * or whole parent block is false.
+                                        */
+                                       break;
+                               case IFSTATE_TRUE:
+                                       /*
+                                        * just finished true section of active 
branch
+                                        * do not evaluate expression, just skip
+                                        */
+                                       psqlscan_branch_set_state(scan_state, 
IFSTATE_IGNORED);
+                                       pset.active_branch = false;
+                                       break;
+                               case IFSTATE_FALSE:
+                                       /*
+                                        * have not yet found a true block in 
this if-endif,
+                                        * determine if this section is true or 
not.
+                                        */
+                                       /* switch variable expansion back on 
just for this line */
+                                       pset.active_branch = true;
+                                       success = 
read_boolean_expression(scan_state, "elif",
+                                                                               
                                &elif_true);
+                                       pset.active_branch = false;
+                                       if (success)
+                                       {
+                                               if (elif_true)
+                                               {
+                                                       
psqlscan_branch_set_state(scan_state, IFSTATE_TRUE);
+                                                       pset.active_branch = 
true;
+                                               }
+                                       }
+                                       break;
+                               case IFSTATE_ELSE_TRUE:
+                               case IFSTATE_ELSE_FALSE:
+                                       psql_error("encountered \\elif after 
\\else\n");
+                                       success = false;
+                                       break;
+                               default:
+                                       break;
+                       }
+               }
+               psql_scan_reset(scan_state);
+       }
+
+       else if (strcmp(cmd, "else") == 0)
+       {
+               if (psqlscan_branch_empty(scan_state))
+               {
+                       psql_error("encountered un-matched \\else\n");
+                       success = false;
+               }
+               else
+               {
+                       switch (psqlscan_branch_get_state(scan_state))
+                       {
+                               case IFSTATE_TRUE:
+                                       /* just finished true section of active 
branch */
+                               case IFSTATE_IGNORED:
+                                       /* whole branch was inactive */
+                                       psqlscan_branch_set_state(scan_state, 
IFSTATE_ELSE_FALSE);
+                                       pset.active_branch = false;
+                                       break;
+                               case IFSTATE_FALSE:
+                                       /* just finished true section of active 
branch */
+                                       psqlscan_branch_set_state(scan_state, 
IFSTATE_ELSE_TRUE);
+                                       pset.active_branch = true;
+                                       break;
+                               case IFSTATE_ELSE_TRUE:
+                               case IFSTATE_ELSE_FALSE:
+                                       psql_error("encountered \\else after 
\\else\n");
+                                       success = false;
+                                       break;
+                               default:
+                                       break;
+                       }
+               }
+               psql_scan_reset(scan_state);
+       }
+
+       else if (strcmp(cmd, "endif") == 0)
+       {
+               if (psqlscan_branch_empty(scan_state))
+               {
+                       psql_error("encountered un-matched \\endif\n");
+                       success = false;
+               }
+               else
+               {
+                       psqlscan_branch_end_state(scan_state);
+                       pset.active_branch = psqlscan_branch_active(scan_state);
+               }
+               psql_scan_reset(scan_state);
+       }
+
        /* \l is list databases */
        else if (strcmp(cmd, "l") == 0 || strcmp(cmd, "list") == 0 ||
                         strcmp(cmd, "l+") == 0 || strcmp(cmd, "list+") == 0)
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index e1b04de..a526008 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -126,6 +126,10 @@ psql_get_variable(const char *varname, bool escape, bool 
as_ident)
        char       *result;
        const char *value;
 
+       /* do not expand variables the branch is inactive */
+       if (!pset.active_branch)
+               return NULL;
+
        value = GetVariable(pset.vars, varname);
        if (!value)
                return NULL;
diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c
index bb306a4..b8d4887 100644
--- a/src/bin/psql/mainloop.c
+++ b/src/bin/psql/mainloop.c
@@ -15,7 +15,7 @@
 #include "settings.h"
 
 #include "mb/pg_wchar.h"
-
+#include "fe_utils/psqlscan_int.h"
 
 /* callback functions for our flex lexer */
 const PsqlScanCallbacks psqlscan_callbacks = {
@@ -23,7 +23,6 @@ const PsqlScanCallbacks psqlscan_callbacks = {
        psql_error
 };
 
-
 /*
  * Main processing loop for reading lines of input
  *     and sending them to the backend.
@@ -51,6 +50,9 @@ MainLoop(FILE *source)
        volatile int count_eof = 0;
        volatile bool die_on_error = false;
 
+       /* only needed at the end to detect unbalanced ifs in scan_state */
+       bool if_endifs_balanced = true;
+
        /* Save the prior command source */
        FILE       *prev_cmd_source;
        bool            prev_cmd_interactive;
@@ -285,21 +287,28 @@ MainLoop(FILE *source)
                        if (scan_result == PSCAN_SEMICOLON ||
                                (scan_result == PSCAN_EOL && pset.singleline))
                        {
-                               /*
-                                * Save query in history.  We use history_buf 
to accumulate
-                                * multi-line queries into a single history 
entry.
-                                */
-                               if (pset.cur_cmd_interactive && 
!line_saved_in_history)
+                               if (pset.active_branch)
                                {
-                                       pg_append_history(line, history_buf);
-                                       pg_send_history(history_buf);
-                                       line_saved_in_history = true;
+                                       /*
+                                        * Save query in history.  We use 
history_buf to accumulate
+                                        * multi-line queries into a single 
history entry.
+                                        */
+                                       if (pset.cur_cmd_interactive && 
!line_saved_in_history)
+                                       {
+                                               pg_append_history(line, 
history_buf);
+                                               pg_send_history(history_buf);
+                                               line_saved_in_history = true;
+                                       }
+
+                                       /* execute query */
+                                       success = SendQuery(query_buf->data);
                                }
+                               else
+                                       success = true;
 
-                               /* execute query */
-                               success = SendQuery(query_buf->data);
                                slashCmdStatus = success ? PSQL_CMD_SEND : 
PSQL_CMD_ERROR;
                                pset.stmt_lineno = 1;
+                               slashCmdStatus = success ? PSQL_CMD_SEND : 
PSQL_CMD_ERROR;
 
                                /* transfer query to previous_buf by 
pointer-swapping */
                                {
@@ -358,15 +367,21 @@ MainLoop(FILE *source)
 
                                if (slashCmdStatus == PSQL_CMD_SEND)
                                {
-                                       success = SendQuery(query_buf->data);
-
-                                       /* transfer query to previous_buf by 
pointer-swapping */
+                                       if (pset.active_branch)
                                        {
-                                               PQExpBuffer swap_buf = 
previous_buf;
+                                               success = 
SendQuery(query_buf->data);
 
-                                               previous_buf = query_buf;
-                                               query_buf = swap_buf;
+                                               /* transfer query to 
previous_buf by pointer-swapping */
+                                               {
+                                                       PQExpBuffer swap_buf = 
previous_buf;
+
+                                                       previous_buf = 
query_buf;
+                                                       query_buf = swap_buf;
+                                               }
                                        }
+                                       else
+                                               success = true;
+
                                        resetPQExpBuffer(query_buf);
 
                                        /* flush any paren nesting info after 
forced send */
@@ -425,12 +440,17 @@ MainLoop(FILE *source)
        if (query_buf->len > 0 && !pset.cur_cmd_interactive &&
                successResult == EXIT_SUCCESS)
        {
-               /* save query in history */
-               if (pset.cur_cmd_interactive)
-                       pg_send_history(history_buf);
+               if (pset.active_branch)
+               {
+                       /* save query in history */
+                       if (pset.cur_cmd_interactive)
+                               pg_send_history(history_buf);
 
-               /* execute query */
-               success = SendQuery(query_buf->data);
+                       /* execute query */
+                       success = SendQuery(query_buf->data);
+               }
+               else
+                       success = true;
 
                if (!success && die_on_error)
                        successResult = EXIT_USER;
@@ -451,11 +471,17 @@ MainLoop(FILE *source)
        destroyPQExpBuffer(previous_buf);
        destroyPQExpBuffer(history_buf);
 
+       if (slashCmdStatus != PSQL_CMD_TERMINATE)
+               if_endifs_balanced = psqlscan_branch_empty(scan_state);
+
        psql_scan_destroy(scan_state);
 
        pset.cur_cmd_source = prev_cmd_source;
        pset.cur_cmd_interactive = prev_cmd_interactive;
        pset.lineno = prev_lineno;
 
+       if (! if_endifs_balanced )
+               psql_error("found EOF before closing \\endif(s)\n");
+
        return successResult;
 }      /* MainLoop() */
diff --git a/src/bin/psql/mainloop.h b/src/bin/psql/mainloop.h
index 228a5e0..47f4c32 100644
--- a/src/bin/psql/mainloop.h
+++ b/src/bin/psql/mainloop.h
@@ -14,4 +14,5 @@ extern const PsqlScanCallbacks psqlscan_callbacks;
 
 extern int     MainLoop(FILE *source);
 
+
 #endif   /* MAINLOOP_H */
diff --git a/src/bin/psql/settings.h b/src/bin/psql/settings.h
index 4c7c3b1..15d5599 100644
--- a/src/bin/psql/settings.h
+++ b/src/bin/psql/settings.h
@@ -114,6 +114,9 @@ typedef struct _psqlSettings
 
        VariableSpace vars;                     /* "shell variable" repository 
*/
 
+       bool            active_branch;  /* true if session is not in an \if 
branch
+                                                                * or the 
current branch is true */
+
        /*
         * The remaining fields are set by assign hooks associated with entries 
in
         * "vars".  They should not be set directly except by those hook
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index 85aac4a..b03ded4 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -128,6 +128,8 @@ main(int argc, char *argv[])
        setvbuf(stderr, NULL, _IONBF, 0);
 #endif
 
+       pset.active_branch = true;
+
        pset.progname = get_progname(argv[0]);
 
        pset.db = NULL;
diff --git a/src/fe_utils/psqlscan.l b/src/fe_utils/psqlscan.l
index 1b29341..a09d404 100644
--- a/src/fe_utils/psqlscan.l
+++ b/src/fe_utils/psqlscan.l
@@ -904,6 +904,8 @@ psql_scan_create(const PsqlScanCallbacks *callbacks)
 
        psql_scan_reset(state);
 
+       state->branch_stack = NULL;
+
        return state;
 }
 
@@ -919,6 +921,13 @@ psql_scan_destroy(PsqlScanState state)
 
        yylex_destroy(state->scanner);
 
+       while (state->branch_stack != NULL)
+       {
+               IfStackElem *p = state->branch_stack;
+               state->branch_stack = state->branch_stack->next;
+               free(p);
+       }
+
        free(state);
 }
 
@@ -1426,3 +1435,88 @@ psqlscan_escape_variable(PsqlScanState state, const char 
*txt, int len,
                psqlscan_emit(state, txt, len);
        }
 }
+
+/*
+ * psqlscan_branch_empty
+ *
+ * True if there are no active \if-structures
+ */
+bool
+psqlscan_branch_empty(PsqlScanState state)
+{
+       return (state->branch_stack == NULL);
+}
+
+/*
+ * Fetch the current state of the top of the stack
+ */
+ifState
+psqlscan_branch_get_state(PsqlScanState state)
+{
+       if (psqlscan_branch_empty(state))
+               return IFSTATE_NONE;
+       return state->branch_stack->if_state;
+}
+
+/*
+ * psqlscan_branch_active
+ *
+ * True if the current \if-block (if any) is true and queries/commands
+ * should be executed.
+ */
+bool
+psqlscan_branch_active(PsqlScanState state)
+{
+       ifState s = psqlscan_branch_get_state(state);
+       return ((s == IFSTATE_NONE) ||
+                       (s == IFSTATE_TRUE) ||
+                       (s == IFSTATE_ELSE_TRUE));
+}
+
+
+/*
+ * psqlscan_branch_push
+ *
+ * Create a new \if branch.
+ */
+bool
+psqlscan_branch_push(PsqlScanState state, ifState new_state)
+{
+       IfStackElem *p = pg_malloc0(sizeof(IfStackElem));
+       p->if_state = new_state;
+       p->next = state->branch_stack;
+       state->branch_stack = p;
+       return true;
+}
+
+/*
+ * psqlscan_branch_set_state
+ *
+ * Change the state of the topmost branch.
+ * Returns false if there was branch state to set.
+ */
+bool
+psqlscan_branch_set_state(PsqlScanState state, ifState new_state)
+{
+       if (psqlscan_branch_empty(state))
+               return false;
+       state->branch_stack->if_state = new_state;
+       return true;
+}
+
+/*
+ * psqlscan_branch_end_state
+ *
+ * Destroy the topmost branch because and \endif was encountered.
+ * Returns false if there was no branch to end.
+ */
+bool
+psqlscan_branch_end_state(PsqlScanState state)
+{
+       IfStackElem *p = state->branch_stack;
+       if (!p)
+               return false;
+       state->branch_stack = state->branch_stack->next;
+       free(p);
+       return true;
+}
diff --git a/src/include/fe_utils/psqlscan_int.h 
b/src/include/fe_utils/psqlscan_int.h
index 0fddc7a..a24a5ac 100644
--- a/src/include/fe_utils/psqlscan_int.h
+++ b/src/include/fe_utils/psqlscan_int.h
@@ -75,6 +75,30 @@ typedef struct StackElem
        struct StackElem *next;
 } StackElem;
 
+typedef enum _ifState
+{
+       IFSTATE_NONE = 0,       /* Not currently in an \if block */
+       IFSTATE_TRUE,           /* currently in an \if or \elif which is true
+                                                * and all parent branches (if 
any) are true */
+       IFSTATE_FALSE,          /* currently in an \if or \elif which is false
+                                                * but no true branch has yet 
been seen,
+                                                * and all parent branches (if 
any) are true */
+       IFSTATE_IGNORED,        /* currently in an \elif which follows a true 
\if
+                                                * or the whole \if is a child 
of a false parent */
+       IFSTATE_ELSE_TRUE,      /* currently in an \else which is true
+                                                * and all parent branches (if 
any) are true */
+       IFSTATE_ELSE_FALSE      /* currently in an \else which is false or 
ignored */
+} ifState;
+
+/*
+ * The state of nested ifs is stored in a stack.
+ */
+typedef struct IfStackElem
+{
+       ifState         if_state;
+       struct IfStackElem *next;
+} IfStackElem;
+
 /*
  * All working state of the lexer must be stored in PsqlScanStateData
  * between calls.  This allows us to have multiple open lexer operations,
@@ -118,6 +142,11 @@ typedef struct PsqlScanStateData
         * Callback functions provided by the program making use of the lexer.
         */
        const PsqlScanCallbacks *callbacks;
+
+       /*
+        * \if branch state variables
+        */
+       IfStackElem *branch_stack;
 } PsqlScanStateData;
 
 
@@ -141,4 +170,21 @@ extern void psqlscan_escape_variable(PsqlScanState state,
                                                 const char *txt, int len,
                                                 bool as_ident);
 
+/*
+ * branching commands
+ */
+extern bool psqlscan_branch_empty(PsqlScanState state);
+
+extern bool psqlscan_branch_active(PsqlScanState state);
+
+extern ifState psqlscan_branch_get_state(PsqlScanState state);
+
+extern bool psqlscan_branch_push(PsqlScanState state,
+                                                                       ifState 
new_state);
+
+extern bool psqlscan_branch_set_state(PsqlScanState state,
+                                                                       ifState 
new_state);
+
+extern bool psqlscan_branch_end_state(PsqlScanState state);
+
 #endif   /* PSQLSCAN_INT_H */
diff --git a/src/test/regress/expected/psql.out 
b/src/test/regress/expected/psql.out
index 464436a..5cc1130 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -2686,6 +2686,78 @@ deallocate q;
 \pset format aligned
 \pset expanded off
 \pset border 1
+\if true
+       \if 1
+               \if yes
+                       \if on
+                               \echo 'all true'
+all true
+                       \else
+                               \echo 'should not print #1-1'
+                       \endif
+               \else
+                       \echo 'should not print #1-2'
+               \endif
+       \else
+               \echo 'should not print #1-3'
+       \endif
+\else
+       \echo 'should not print #1-4'
+\endif
+\if false
+       \echo 'should not print #2-1'
+\elif 0
+       \echo 'should not print #2-2'
+\elif no
+       \echo 'should not print #2-3'
+\elif off
+       \echo 'should not print #2-4'
+\else
+       \echo 'all false'
+all false
+\endif
+\if true
+       \echo 'first thing true'
+first thing true
+\else
+       \echo 'should not print #3-1'
+\endif
+\if false
+       \echo 'should not print #4-1'
+\elif true
+       \echo 'second thing true'
+second thing true
+\else
+       \echo 'should not print #5-1'
+\endif
+\set var 'ab''cd'
+-- select :var;
+\if false
+  select :var ;
+\else
+  select 1;
+ ?column? 
+----------
+        1
+(1 row)
+
+\endif
+\endif
+encountered un-matched \endif
+\else
+encountered un-matched \else
+\elif
+encountered un-matched \elif
+\if true
+\else
+\else
+encountered \else after \else
+\endif
+\if false
+\else
+\elif
+encountered \elif after \else
+\endif
 -- SHOW_CONTEXT
 \set SHOW_CONTEXT never
 do $$
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index 900aa7e..8793aa2 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -357,6 +357,74 @@ deallocate q;
 \pset expanded off
 \pset border 1
 
+\if true
+       \if 1
+               \if yes
+                       \if on
+                               \echo 'all true'
+                       \else
+                               \echo 'should not print #1-1'
+                       \endif
+               \else
+                       \echo 'should not print #1-2'
+               \endif
+       \else
+               \echo 'should not print #1-3'
+       \endif
+\else
+       \echo 'should not print #1-4'
+\endif
+
+\if false
+       \echo 'should not print #2-1'
+\elif 0
+       \echo 'should not print #2-2'
+\elif no
+       \echo 'should not print #2-3'
+\elif off
+       \echo 'should not print #2-4'
+\else
+       \echo 'all false'
+\endif
+
+\if true
+       \echo 'first thing true'
+\else
+       \echo 'should not print #3-1'
+\endif
+
+\if false
+       \echo 'should not print #4-1'
+\elif true
+       \echo 'second thing true'
+\else
+       \echo 'should not print #5-1'
+\endif
+
+\set var 'ab''cd'
+-- select :var;
+\if false
+  select :var ;
+\else
+  select 1;
+\endif
+
+\endif
+
+\else
+
+\elif
+
+\if true
+\else
+\else
+\endif
+
+\if false
+\else
+\elif
+\endif
+
 -- SHOW_CONTEXT
 
 \set SHOW_CONTEXT never
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to