Commit d57c7a7c authored by Andrew Dunstan's avatar Andrew Dunstan

Provide a test for variable existence in psql

"\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
parent 71480501
...@@ -783,6 +783,10 @@ testdb=> ...@@ -783,6 +783,10 @@ testdb=>
The forms <literal>:'<replaceable>variable_name</>'</literal> and The forms <literal>:'<replaceable>variable_name</>'</literal> and
<literal>:"<replaceable>variable_name</>"</literal> described there <literal>:"<replaceable>variable_name</>"</literal> described there
work as well. 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>
<para> <para>
...@@ -3938,6 +3942,12 @@ testdb=&gt; <userinput>INSERT INTO my_table VALUES (:'content');</userinput> ...@@ -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. can escape a colon with a backslash to protect it from substitution.
</para> </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> <para>
The colon syntax for variables is standard <acronym>SQL</acronym> for The colon syntax for variables is standard <acronym>SQL</acronym> for
embedded query languages, such as <application>ECPG</application>. embedded query languages, such as <application>ECPG</application>.
......
...@@ -281,6 +281,10 @@ other . ...@@ -281,6 +281,10 @@ other .
unquoted_option_chars = 0; unquoted_option_chars = 0;
} }
:\{\?{variable_char}+\} {
psqlscan_test_variable(cur_state, yytext, yyleng);
}
:'{variable_char}* { :'{variable_char}* {
/* Throw back everything but the colon */ /* Throw back everything but the colon */
yyless(1); yyless(1);
...@@ -295,6 +299,20 @@ other . ...@@ -295,6 +299,20 @@ other .
ECHO; 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} { {other} {
unquoted_option_chars++; unquoted_option_chars++;
ECHO; ECHO;
......
...@@ -745,9 +745,13 @@ other . ...@@ -745,9 +745,13 @@ other .
PQUOTE_SQL_IDENT); 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 * 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}* { :'{variable_char}* {
...@@ -762,6 +766,17 @@ other . ...@@ -762,6 +766,17 @@ other .
ECHO; 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. * Back to backend-compatible rules.
*/ */
...@@ -1442,3 +1457,28 @@ psqlscan_escape_variable(PsqlScanState state, const char *txt, int len, ...@@ -1442,3 +1457,28 @@ psqlscan_escape_variable(PsqlScanState state, const char *txt, int len,
psqlscan_emit(state, txt, 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);
}
}
...@@ -142,5 +142,7 @@ extern char *psqlscan_extract_substring(PsqlScanState state, ...@@ -142,5 +142,7 @@ extern char *psqlscan_extract_substring(PsqlScanState state,
extern void psqlscan_escape_variable(PsqlScanState state, extern void psqlscan_escape_variable(PsqlScanState state,
const char *txt, int len, const char *txt, int len,
PsqlScanQuoteType quote); PsqlScanQuoteType quote);
extern void psqlscan_test_variable(PsqlScanState state,
const char *txt, int len);
#endif /* PSQLSCAN_INT_H */ #endif /* PSQLSCAN_INT_H */
...@@ -3014,6 +3014,32 @@ bar 'bar' "bar" ...@@ -3014,6 +3014,32 @@ bar 'bar' "bar"
\echo 'should print #8-1' \echo 'should print #8-1'
should print #8-1 should print #8-1
\endif \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 -- SHOW_CONTEXT
\set SHOW_CONTEXT never \set SHOW_CONTEXT never
do $$ do $$
......
...@@ -572,6 +572,24 @@ select \if false \\ (bogus \else \\ 42 \endif \\ forty_two; ...@@ -572,6 +572,24 @@ select \if false \\ (bogus \else \\ 42 \endif \\ forty_two;
\echo 'should print #8-1' \echo 'should print #8-1'
\endif \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 -- SHOW_CONTEXT
\set SHOW_CONTEXT never \set SHOW_CONTEXT never
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment