From e984ef5861df4bc9733b36271d05763e82de7c04 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 30 Mar 2017 12:59:11 -0400 Subject: Support \if ... \elif ... \else ... \endif in psql scripting. This patch adds nestable conditional blocks to psql. The control structure feature per se is complete, but the boolean expressions understood by \if and \elif are pretty primitive; basically, after variable substitution and backtick expansion, the result has to be "true" or "false" or one of the other standard spellings of a boolean value. But that's enough for many purposes, since you can always do the heavy lifting on the server side; and we can extend it later. Along the way, pay down some of the technical debt that had built up around psql/command.c: * Refactor exec_command() into a function per command, instead of being a 1500-line monstrosity. This makes the file noticeably longer because of repetitive function header/trailer overhead, but it seems much more readable. * Teach psql_get_variable() and psqlscanslash.l to suppress variable substitution and backtick expansion on the basis of the conditional stack state, thereby allowing removal of the OT_NO_EVAL kluge. * Fix the no-doubt-once-expedient hack of sometimes silently substituting mainloop.c's previous_buf for query_buf when calling HandleSlashCmds. (It's a bit remarkable that commands like \r worked at all with that.) Recall of a previous query is now done explicitly in the slash commands where that should happen. Corey Huinker, reviewed by Fabien Coelho, further hacking by me Discussion: https://postgr.es/m/CADkLM=c94OSRTnat=LX0ivNq4pxDNeoomFfYvBKM5N_xfmLtAA@mail.gmail.com --- doc/src/sgml/ref/psql-ref.sgml | 92 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) (limited to 'doc/src') diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 2a9c4120205..b51b11baa35 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -2063,6 +2063,95 @@ hello 10 + + \if expression + \elif expression + \else + \endif + + + This group of commands implements nestable conditional blocks. + A conditional block must begin with an \if and end + with an \endif. In between there may be any number + of \elif clauses, which may optionally be followed + by a single \else clause. Ordinary queries and + other types of backslash commands may (and usually do) appear between + the commands forming a conditional block. + + + The \if and \elif commands read + their argument(s) and evaluate them as a boolean expression. If the + expression yields true then processing continues + normally; otherwise, lines are skipped until a + matching \elif, \else, + or \endif is reached. Once + an \if or \elif test has + succeeded, the arguments of later \elif commands in + the same block are not evaluated but are treated as false. Lines + following an \else are processed only if no earlier + matching \if or \elif succeeded. + + + The expression argument + of an \if or \elif command + is subject to variable interpolation and backquote expansion, just + like any other backslash command argument. After that it is evaluated + like the value of an on/off option variable. So a valid value + is any unambiguous case-insensitive match for one of: + true, false, 1, + 0, on, off, + yes, no. For example, + t, T, and tR + will all be considered to be true. + + + Expressions that do not properly evaluate to true or false will + generate a warning and be treated as false. + + + Lines being skipped are parsed normally to identify queries and + backslash commands, but queries are not sent to the server, and + backslash commands other than conditionals + (\if, \elif, + \else, \endif) are + ignored. Conditional commands are checked only for valid nesting. + Variable references in skipped lines are not expanded, and backquote + expansion is not performed either. + + + All the backslash commands of a given conditional block must appear in + the same source file. If EOF is reached on the main input file or an + \include-ed file before all local + \if-blocks have been closed, + then psql will raise an error. + + + Here is an example: + + +-- check for the existence of two separate records in the database and store +-- the results in separate psql variables +SELECT + EXISTS(SELECT 1 FROM customer WHERE customer_id = 123) as is_customer, + EXISTS(SELECT 1 FROM employee WHERE employee_id = 456) as is_employee +\gset +\if :is_customer + SELECT * FROM customer WHERE customer_id = 123; +\elif :is_employee + \echo 'is not a customer but is an employee' + SELECT * FROM employee WHERE employee_id = 456; +\else + \if yes + \echo 'not a customer or employee' + \else + \echo 'this will never print' + \endif +\endif + + + + + \l[+] or \list[+] [ pattern ] @@ -3715,7 +3804,8 @@ testdb=> INSERT INTO my_table VALUES (:'content'); In prompt 1 normally =, - but ^ if in single-line mode, + but @ if the session is in an inactive branch of a + conditional block, or ^ if in single-line mode, or ! if the session is disconnected from the database (which can happen if \connect fails). In prompt 2 %R is replaced by a character that -- cgit v1.2.3