Provide a test for variable existence in psql
authorAndrew Dunstan <andrew@dunslane.net>
Thu, 21 Sep 2017 23:02:23 +0000 (19:02 -0400)
committerAndrew Dunstan <andrew@dunslane.net>
Thu, 21 Sep 2017 23:02:23 +0000 (19:02 -0400)
"\if :{?variable_name}" will be translated to "\if TRUE" if the variable
exists and "\if FALSE" otherwise. Thus it will be possible to execute code
conditionally on the existence of the variable, regardless of its value.

Fabien Coelho, with some review by Robins Tharakan and some light text
editing by me.

Discussion: https://postgr.es/m/alpine.DEB.2.20.1708260835520.3627@lancre

doc/src/sgml/ref/psql-ref.sgml
src/bin/psql/psqlscanslash.l
src/fe_utils/psqlscan.l
src/include/fe_utils/psqlscan_int.h
src/test/regress/expected/psql.out
src/test/regress/sql/psql.sql

index 60bafa81754b9c02b7514dbd0d7c0bfe24059694..e7a3e17c67b4fcb8e3cf1366c7c569ba1016a264 100644 (file)
@@ -783,6 +783,10 @@ testdb=&gt;
     The forms <literal>:'<replaceable>variable_name</>'</literal> and
     <literal>:"<replaceable>variable_name</>"</literal> described there
     work as well.
+    The <literal>:{?<replaceable>variable_name</>}</> syntax allows
+    testing whether a variable is defined. It is substituted by
+    TRUE or FALSE.
+    Escaping the colon with a backslash protects it from substitution.
     </para>
 
     <para>
@@ -3938,6 +3942,12 @@ testdb=&gt; <userinput>INSERT INTO my_table VALUES (:'content');</userinput>
     can escape a colon with a backslash to protect it from substitution.
     </para>
 
+    <para>
+    The <literal>:{?<replaceable>name</>}</> special syntax returns TRUE
+    or FALSE depending on whether the variable exists or not, and is thus
+    always substituted, unless the colon is backslash-escaped.
+    </para>
+
     <para>
     The colon syntax for variables is standard <acronym>SQL</acronym> for
     embedded query languages, such as <application>ECPG</application>.
index db7a1b9eead9c2ebe24e8e3370dc66a514094109..9a53cb3e02baacf97b9e42a33e62e2f91b3e52da 100644 (file)
@@ -281,6 +281,10 @@ other          .
                    unquoted_option_chars = 0;
                }
 
+:\{\?{variable_char}+\}    {
+                   psqlscan_test_variable(cur_state, yytext, yyleng);
+               }
+
 :'{variable_char}* {
                    /* Throw back everything but the colon */
                    yyless(1);
@@ -295,6 +299,20 @@ other          .
                    ECHO;
                }
 
+:\{\?{variable_char}*  {
+                   /* Throw back everything but the colon */
+                   yyless(1);
+                   unquoted_option_chars++;
+                   ECHO;
+               }
+
+:\{        {
+                   /* Throw back everything but the colon */
+                   yyless(1);
+                   unquoted_option_chars++;
+                   ECHO;
+               }
+
 {other}            {
                    unquoted_option_chars++;
                    ECHO;
index 27689d72da866a99e2d0c98a4ef6522766830a15..4375142a0072b718e024bfbe80ac5b4195beec37 100644 (file)
@@ -745,9 +745,13 @@ other          .
                                             PQUOTE_SQL_IDENT);
                }
 
+:\{\?{variable_char}+\}    {
+                   psqlscan_test_variable(cur_state, yytext, yyleng);
+               }
+
    /*
     * These rules just avoid the need for scanner backup if one of the
-    * two rules above fails to match completely.
+    * three rules above fails to match completely.
     */
 
 :'{variable_char}* {
@@ -762,6 +766,17 @@ other          .
                    ECHO;
                }
 
+:\{\?{variable_char}*  {
+                   /* Throw back everything but the colon */
+                   yyless(1);
+                   ECHO;
+               }
+:\{    {
+                   /* Throw back everything but the colon */
+                   yyless(1);
+                   ECHO;
+               }
+
    /*
     * Back to backend-compatible rules.
     */
@@ -1442,3 +1457,28 @@ psqlscan_escape_variable(PsqlScanState state, const char *txt, int len,
        psqlscan_emit(state, txt, len);
    }
 }
+
+void
+psqlscan_test_variable(PsqlScanState state, const char *txt, int len)
+{
+   char    *varname;
+   char    *value;
+
+   varname = psqlscan_extract_substring(state, txt + 3, len - 4);
+   if (state->callbacks->get_variable)
+       value = state->callbacks->get_variable(varname, PQUOTE_PLAIN,
+                                              state->cb_passthrough);
+   else
+       value = NULL;
+   free(varname);
+
+   if (value != NULL)
+   {
+       psqlscan_emit(state, "TRUE", 4);
+       free(value);
+   }
+   else
+   {
+       psqlscan_emit(state, "FALSE", 5);
+   }
+}
index c70ff29f4ef5d80265e96e9728ca5075f1c090c6..e9b351756b94fbf4ac96a2045fd0f64f7ff6e4ac 100644 (file)
@@ -142,5 +142,7 @@ extern char *psqlscan_extract_substring(PsqlScanState state,
 extern void psqlscan_escape_variable(PsqlScanState state,
                         const char *txt, int len,
                         PsqlScanQuoteType quote);
+extern void psqlscan_test_variable(PsqlScanState state,
+                                  const char *txt, int len);
 
 #endif                         /* PSQLSCAN_INT_H */
index 836d8510fd2cd3733d64a0e9a387899f4dd4e313..3818cfea7e4895f764799c968542c663d3e6a213 100644 (file)
@@ -3014,6 +3014,32 @@ bar 'bar' "bar"
    \echo 'should print #8-1'
 should print #8-1
 \endif
+-- :{?...} defined variable test
+\set i 1
+\if :{?i}
+  \echo '#9-1 ok, variable i is defined'
+#9-1 ok, variable i is defined
+\else
+  \echo 'should not print #9-2'
+\endif
+\if :{?no_such_variable}
+  \echo 'should not print #10-1'
+\else
+  \echo '#10-2 ok, variable no_such_variable is not defined'
+#10-2 ok, variable no_such_variable is not defined
+\endif
+SELECT :{?i} AS i_is_defined;
+ i_is_defined 
+--------------
+ t
+(1 row)
+
+SELECT NOT :{?no_such_var} AS no_such_var_is_not_defined;
+ no_such_var_is_not_defined 
+----------------------------
+ t
+(1 row)
+
 -- SHOW_CONTEXT
 \set SHOW_CONTEXT never
 do $$
index ddae1bf1e7e95d7e383ef54c91fe0f27dc3a6c03..b45da9bb8deeafcc28266d736bf24e984346b364 100644 (file)
@@ -572,6 +572,24 @@ select \if false \\ (bogus \else \\ 42 \endif \\ forty_two;
    \echo 'should print #8-1'
 \endif
 
+-- :{?...} defined variable test
+\set i 1
+\if :{?i}
+  \echo '#9-1 ok, variable i is defined'
+\else
+  \echo 'should not print #9-2'
+\endif
+
+\if :{?no_such_variable}
+  \echo 'should not print #10-1'
+\else
+  \echo '#10-2 ok, variable no_such_variable is not defined'
+\endif
+
+SELECT :{?i} AS i_is_defined;
+
+SELECT NOT :{?no_such_var} AS no_such_var_is_not_defined;
+
 -- SHOW_CONTEXT
 
 \set SHOW_CONTEXT never