psql: Add more meta-commands able to use the extended protocol
authorMichael Paquier <[email protected]>
Thu, 22 Aug 2024 07:25:57 +0000 (16:25 +0900)
committerMichael Paquier <[email protected]>
Thu, 22 Aug 2024 07:25:57 +0000 (16:25 +0900)
Currently, only unnamed prepared statement are supported by psql with
the meta-command \bind.  With only this command, it is not possible to
test named statement creation, execution or close through the extended
protocol.

This commit introduces three additional commands:
* \parse creates a prepared statement using the extended protocol,
acting as a wrapper of libpq's PQsendPrepare().
* \bind_named binds and executes an existing prepared statement using
the extended protocol, for PQsendQueryPrepared().
* \close closes an existing prepared statement using the extended
protocol, for PQsendClosePrepared().

This is going to be useful to add regression tests for the extended
query protocol, and I have some plans for that on separate threads.
Note that \bind relies on PQsendQueryParams().

The code of psql is refactored so as bind_flag is replaced by an enum in
_psqlSettings that tracks the type of libpq routine to execute, based on
the meta-command involved, with the default being PQsendQuery().  This
refactoring piece has been written by me, while Anthonin has implemented
the rest.

Author: Anthonin Bonnefoy, Michael Paquier
Reviewed-by: Aleksander Alekseev, Jelte Fennema-Nio
Discussion: https://postgr.es/m/CAO6_XqpSq0Q0kQcVLCbtagY94V2GxNP3zCnR6WnOM8WqXPK4nw@mail.gmail.com

doc/src/sgml/ref/psql-ref.sgml
src/bin/psql/command.c
src/bin/psql/common.c
src/bin/psql/help.c
src/bin/psql/settings.h
src/bin/psql/tab-complete.c
src/test/regress/expected/psql.out
src/test/regress/sql/psql.sql
src/tools/pgindent/typedefs.list

index 07419a3b92e4eddce29fefd6a625110a9d1e406d..3fd9959ed168caf386acce840015894c929ffeec 100644 (file)
@@ -917,6 +917,36 @@ INSERT INTO tbl1 VALUES ($1, $2) \bind 'first value' 'second value' \g
        </listitem>
       </varlistentry>
 
+      <varlistentry id="app-psql-meta-command-bind-named">
+       <term><literal>\bind_named</literal> <replaceable class="parameter">statement_name</replaceable> [ <replaceable class="parameter">parameter</replaceable> ] ... </term>
+
+       <listitem>
+        <para>
+         <literal>\bind_named</literal> is equivalent to <literal>\bind</literal>,
+         except that it takes the name of an existing prepared statement as
+         first parameter. An empty string denotes the unnamed prepared
+         statement.
+        </para>
+
+        <para>
+         Example:
+<programlisting>
+INSERT INTO tbls1 VALUES ($1, $2) \parse stmt1
+\bind_named stmt1 'first value' 'second value' \g
+</programlisting>
+        </para>
+
+        <para>
+         This command causes the extended query protocol (see
+         <xref linkend="protocol-query-concepts"/>) to be used, unlike normal
+         <application>psql</application> operation, which uses the simple
+         query protocol. So this command can be useful to test the extended
+         query protocol from <application>psql</application>.
+        </para>
+
+       </listitem>
+      </varlistentry>
+
       <varlistentry id="app-psql-meta-command-c-lc">
         <term><literal>\c</literal> or <literal>\connect [ -reuse-previous=<replaceable class="parameter">on|off</replaceable> ] [ <replaceable class="parameter">dbname</replaceable> [ <replaceable class="parameter">username</replaceable> ] [ <replaceable class="parameter">host</replaceable> ] [ <replaceable class="parameter">port</replaceable> ] | <replaceable class="parameter">conninfo</replaceable> ]</literal></term>
         <listitem>
@@ -1038,6 +1068,35 @@ INSERT INTO tbl1 VALUES ($1, $2) \bind 'first value' 'second value' \g
         </listitem>
       </varlistentry>
 
+      <varlistentry id="app-psql-meta-command-close">
+       <term><literal>\close</literal> <replaceable class="parameter">prepared_statement_name</replaceable></term>
+
+       <listitem>
+        <para>
+         Closes the specified prepared statement. An empty string denotes the
+         unnamed prepared statement. If no prepared statement exists with this
+         name, the operation is a no-op.
+        </para>
+
+        <para>
+         Example:
+<programlisting>
+SELECT $1 \parse stmt1
+\close stmt1
+</programlisting>
+        </para>
+
+        <para>
+         This command causes the extended query protocol to be used,
+         unlike normal <application>psql</application> operation, which
+         uses the simple query protocol. So this command can be useful
+         to test the extended query protocol from
+         <application>psql</application>.
+        </para>
+
+       </listitem>
+      </varlistentry>
+
       <varlistentry id="app-psql-meta-commands-copy">
         <term><literal>\copy { <replaceable class="parameter">table</replaceable> [ ( <replaceable class="parameter">column_list</replaceable> ) ] }
         <literal>from</literal>
@@ -2780,6 +2839,37 @@ lo_import 152801
         </listitem>
       </varlistentry>
 
+      <varlistentry id="app-psql-meta-command-parse">
+        <term><literal>\parse <replaceable class="parameter">statement_name</replaceable></literal></term>
+        <listitem>
+        <para>
+         Creates a prepared statement from the current query buffer, based on
+         the name of a destination prepared-statement object. An empty string
+         denotes the unnamed prepared statement.
+        </para>
+
+        <para>
+         Example:
+<programlisting>
+SELECT $1 \parse stmt1
+</programlisting>
+        </para>
+
+        <para>
+         This command causes the extended query protocol to be used, unlike
+         normal <application>psql</application> operation, which uses the
+         simple query protocol. A
+         <xref linkend="protocol-message-formats-Parse"/>
+         message will be issued by this command so it can be useful to
+         test the extended query protocol from
+         <application>psql</application>. This command affects only the next
+         query executed; all subsequent queries will use the simple query
+         protocol by default.
+        </para>
+
+        </listitem>
+      </varlistentry>
+
       <varlistentry id="app-psql-meta-command-password">
         <term><literal>\password [ <replaceable class="parameter">username</replaceable> ]</literal></term>
         <listitem>
index 180781ecd05250c4aaf405884b76ccb63ebee9ae..4dfc7b2d8577404a5f3646746970c38133e3c916 100644 (file)
@@ -64,10 +64,14 @@ static backslashResult exec_command(const char *cmd,
                                    PQExpBuffer previous_buf);
 static backslashResult exec_command_a(PsqlScanState scan_state, bool active_branch);
 static backslashResult exec_command_bind(PsqlScanState scan_state, bool active_branch);
+static backslashResult exec_command_bind_named(PsqlScanState scan_state, bool active_branch,
+                                              const char *cmd);
 static backslashResult exec_command_C(PsqlScanState scan_state, bool active_branch);
 static backslashResult exec_command_connect(PsqlScanState scan_state, bool active_branch);
 static backslashResult exec_command_cd(PsqlScanState scan_state, bool active_branch,
                                       const char *cmd);
+static backslashResult exec_command_close(PsqlScanState scan_state, bool active_branch,
+                                         const char *cmd);
 static backslashResult exec_command_conninfo(PsqlScanState scan_state, bool active_branch);
 static backslashResult exec_command_copy(PsqlScanState scan_state, bool active_branch);
 static backslashResult exec_command_copyright(PsqlScanState scan_state, bool active_branch);
@@ -116,6 +120,8 @@ static backslashResult exec_command_lo(PsqlScanState scan_state, bool active_bra
 static backslashResult exec_command_out(PsqlScanState scan_state, bool active_branch);
 static backslashResult exec_command_print(PsqlScanState scan_state, bool active_branch,
                                          PQExpBuffer query_buf, PQExpBuffer previous_buf);
+static backslashResult exec_command_parse(PsqlScanState scan_state, bool active_branch,
+                                         const char *cmd);
 static backslashResult exec_command_password(PsqlScanState scan_state, bool active_branch);
 static backslashResult exec_command_prompt(PsqlScanState scan_state, bool active_branch,
                                           const char *cmd);
@@ -312,12 +318,16 @@ exec_command(const char *cmd,
        status = exec_command_a(scan_state, active_branch);
    else if (strcmp(cmd, "bind") == 0)
        status = exec_command_bind(scan_state, active_branch);
+   else if (strcmp(cmd, "bind_named") == 0)
+       status = exec_command_bind_named(scan_state, active_branch, cmd);
    else if (strcmp(cmd, "C") == 0)
        status = exec_command_C(scan_state, active_branch);
    else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0)
        status = exec_command_connect(scan_state, active_branch);
    else if (strcmp(cmd, "cd") == 0)
        status = exec_command_cd(scan_state, active_branch, cmd);
+   else if (strcmp(cmd, "close") == 0)
+       status = exec_command_close(scan_state, active_branch, cmd);
    else if (strcmp(cmd, "conninfo") == 0)
        status = exec_command_conninfo(scan_state, active_branch);
    else if (pg_strcasecmp(cmd, "copy") == 0)
@@ -379,6 +389,8 @@ exec_command(const char *cmd,
    else if (strcmp(cmd, "p") == 0 || strcmp(cmd, "print") == 0)
        status = exec_command_print(scan_state, active_branch,
                                    query_buf, previous_buf);
+   else if (strcmp(cmd, "parse") == 0)
+       status = exec_command_parse(scan_state, active_branch, cmd);
    else if (strcmp(cmd, "password") == 0)
        status = exec_command_password(scan_state, active_branch);
    else if (strcmp(cmd, "prompt") == 0)
@@ -472,6 +484,7 @@ exec_command_bind(PsqlScanState scan_state, bool active_branch)
        int         nalloc = 0;
 
        pset.bind_params = NULL;
+       pset.stmtName = NULL;
 
        while ((opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false)))
        {
@@ -485,7 +498,57 @@ exec_command_bind(PsqlScanState scan_state, bool active_branch)
        }
 
        pset.bind_nparams = nparams;
-       pset.bind_flag = true;
+       pset.send_mode = PSQL_SEND_EXTENDED_QUERY_PARAMS;
+   }
+   else
+       ignore_slash_options(scan_state);
+
+   return status;
+}
+
+/*
+ * \bind_named -- set query parameters for an existing prepared statement
+ */
+static backslashResult
+exec_command_bind_named(PsqlScanState scan_state, bool active_branch,
+                       const char *cmd)
+{
+   backslashResult status = PSQL_CMD_SKIP_LINE;
+
+   if (active_branch)
+   {
+       char       *opt;
+       int         nparams = 0;
+       int         nalloc = 0;
+
+       pset.bind_params = NULL;
+       pset.stmtName = NULL;
+
+       /* get the mandatory prepared statement name */
+       opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false);
+       if (!opt)
+       {
+           pg_log_error("\\%s: missing required argument", cmd);
+           status = PSQL_CMD_ERROR;
+       }
+       else
+       {
+           pset.stmtName = opt;
+           pset.send_mode = PSQL_SEND_EXTENDED_QUERY_PREPARED;
+
+           /* set of parameters */
+           while ((opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false)))
+           {
+               nparams++;
+               if (nparams > nalloc)
+               {
+                   nalloc = nalloc ? nalloc * 2 : 1;
+                   pset.bind_params = pg_realloc_array(pset.bind_params, char *, nalloc);
+               }
+               pset.bind_params[nparams - 1] = opt;
+           }
+           pset.bind_nparams = nparams;
+       }
    }
    else
        ignore_slash_options(scan_state);
@@ -643,6 +706,38 @@ exec_command_cd(PsqlScanState scan_state, bool active_branch, const char *cmd)
    return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
 }
 
+/*
+ * \close -- close a previously prepared statement
+ */
+static backslashResult
+exec_command_close(PsqlScanState scan_state, bool active_branch, const char *cmd)
+{
+   backslashResult status = PSQL_CMD_SKIP_LINE;
+
+   if (active_branch)
+   {
+       char       *opt = psql_scan_slash_option(scan_state,
+                                                OT_NORMAL, NULL, false);
+
+       pset.stmtName = NULL;
+       if (!opt)
+       {
+           pg_log_error("\\%s: missing required argument", cmd);
+           status = PSQL_CMD_ERROR;
+       }
+       else
+       {
+           pset.stmtName = opt;
+           pset.send_mode = PSQL_SEND_EXTENDED_CLOSE;
+           status = PSQL_CMD_SEND;
+       }
+   }
+   else
+       ignore_slash_options(scan_state);
+
+   return status;
+}
+
 /*
  * \conninfo -- display information about the current connection
  */
@@ -2096,6 +2191,39 @@ exec_command_print(PsqlScanState scan_state, bool active_branch,
    return PSQL_CMD_SKIP_LINE;
 }
 
+/*
+ * \parse -- parse query
+ */
+static backslashResult
+exec_command_parse(PsqlScanState scan_state, bool active_branch,
+                  const char *cmd)
+{
+   backslashResult status = PSQL_CMD_SKIP_LINE;
+
+   if (active_branch)
+   {
+       char       *opt = psql_scan_slash_option(scan_state,
+                                                OT_NORMAL, NULL, false);
+
+       pset.stmtName = NULL;
+       if (!opt)
+       {
+           pg_log_error("\\%s: missing required argument", cmd);
+           status = PSQL_CMD_ERROR;
+       }
+       else
+       {
+           pset.stmtName = opt;
+           pset.send_mode = PSQL_SEND_EXTENDED_PARSE;
+           status = PSQL_CMD_SEND;
+       }
+   }
+   else
+       ignore_slash_options(scan_state);
+
+   return status;
+}
+
 /*
  * \password -- set user password
  */
index fe8e049c4c1a243973d9be560ec001b78cce99aa..be265aa05a4ca64696e508d0334f5a7bce505a23 100644 (file)
@@ -1274,15 +1274,28 @@ sendquery_cleanup:
        pset.gsavepopt = NULL;
    }
 
-   /* clean up after \bind */
-   if (pset.bind_flag)
+   /* clean up after extended protocol queries */
+   switch (pset.send_mode)
    {
-       for (i = 0; i < pset.bind_nparams; i++)
-           free(pset.bind_params[i]);
-       free(pset.bind_params);
-       pset.bind_params = NULL;
-       pset.bind_flag = false;
+       case PSQL_SEND_EXTENDED_CLOSE:  /* \close */
+           free(pset.stmtName);
+           break;
+       case PSQL_SEND_EXTENDED_PARSE:  /* \parse */
+           free(pset.stmtName);
+           break;
+       case PSQL_SEND_EXTENDED_QUERY_PARAMS:   /* \bind */
+       case PSQL_SEND_EXTENDED_QUERY_PREPARED: /* \bind_named */
+           for (i = 0; i < pset.bind_nparams; i++)
+               free(pset.bind_params[i]);
+           free(pset.bind_params);
+           free(pset.stmtName);
+           pset.bind_params = NULL;
+           break;
+       case PSQL_SEND_QUERY:
+           break;
    }
+   pset.stmtName = NULL;
+   pset.send_mode = PSQL_SEND_QUERY;
 
    /* reset \gset trigger */
    if (pset.gset_prefix)
@@ -1456,7 +1469,7 @@ ExecQueryAndProcessResults(const char *query,
                           const printQueryOpt *opt, FILE *printQueryFout)
 {
    bool        timing = pset.timing;
-   bool        success;
+   bool        success = false;
    bool        return_early = false;
    instr_time  before,
                after;
@@ -1469,10 +1482,32 @@ ExecQueryAndProcessResults(const char *query,
    else
        INSTR_TIME_SET_ZERO(before);
 
-   if (pset.bind_flag)
-       success = PQsendQueryParams(pset.db, query, pset.bind_nparams, NULL, (const char *const *) pset.bind_params, NULL, NULL, 0);
-   else
-       success = PQsendQuery(pset.db, query);
+   switch (pset.send_mode)
+   {
+       case PSQL_SEND_EXTENDED_CLOSE:
+           success = PQsendClosePrepared(pset.db, pset.stmtName);
+           break;
+       case PSQL_SEND_EXTENDED_PARSE:
+           success = PQsendPrepare(pset.db, pset.stmtName, query, 0, NULL);
+           break;
+       case PSQL_SEND_EXTENDED_QUERY_PARAMS:
+           Assert(pset.stmtName == NULL);
+           success = PQsendQueryParams(pset.db, query,
+                                       pset.bind_nparams, NULL,
+                                       (const char *const *) pset.bind_params,
+                                       NULL, NULL, 0);
+           break;
+       case PSQL_SEND_EXTENDED_QUERY_PREPARED:
+           Assert(pset.stmtName != NULL);
+           success = PQsendQueryPrepared(pset.db, pset.stmtName,
+                                         pset.bind_nparams,
+                                         (const char *const *) pset.bind_params,
+                                         NULL, NULL, 0);
+           break;
+       case PSQL_SEND_QUERY:
+           success = PQsendQuery(pset.db, query);
+           break;
+   }
 
    if (!success)
    {
index 6f58a110748064a5b12a3dcb0c97a33e2f0a7b0b..19d20c587819e8077854d2f1ba796fa91d46b7cb 100644 (file)
@@ -165,6 +165,9 @@ slashUsage(unsigned short int pager)
 
    HELP0("General\n");
    HELP0("  \\bind [PARAM]...       set query parameters\n");
+   HELP0("  \\bind_named STMT_NAME [PARAM]...\n"
+         "                         set query parameters for an existing prepared statement\n");
+   HELP0("  \\close STMT_NAME       close an existing prepared statement\n");
    HELP0("  \\copyright             show PostgreSQL usage and distribution terms\n");
    HELP0("  \\crosstabview [COLUMNS] execute query and display result in crosstab\n");
    HELP0("  \\errverbose            show most recent error message at maximum verbosity\n");
@@ -312,6 +315,7 @@ slashUsage(unsigned short int pager)
              "                         connect to new database (currently no connection)\n");
    HELP0("  \\conninfo              display information about current connection\n");
    HELP0("  \\encoding [ENCODING]   show or set client encoding\n");
+   HELP0("  \\parse STMT_NAME       create a prepared statement\n");
    HELP0("  \\password [USERNAME]   securely change the password for a user\n");
    HELP0("\n");
 
index 505f99d8e47de1dd0646fe6cfe203e027ab4fc8f..a22de8ef78e0a6b3e9f38ae1c638a058fc267544 100644 (file)
@@ -62,6 +62,15 @@ typedef enum
    PSQL_COMP_CASE_LOWER,
 } PSQL_COMP_CASE;
 
+typedef enum
+{
+   PSQL_SEND_QUERY,
+   PSQL_SEND_EXTENDED_CLOSE,
+   PSQL_SEND_EXTENDED_PARSE,
+   PSQL_SEND_EXTENDED_QUERY_PARAMS,
+   PSQL_SEND_EXTENDED_QUERY_PREPARED,
+} PSQL_SEND_MODE;
+
 typedef enum
 {
    hctl_none = 0,
@@ -96,10 +105,12 @@ typedef struct _psqlSettings
    char       *gset_prefix;    /* one-shot prefix argument for \gset */
    bool        gdesc_flag;     /* one-shot request to describe query result */
    bool        gexec_flag;     /* one-shot request to execute query result */
-   bool        bind_flag;      /* one-shot request to use extended query
-                                * protocol */
+   PSQL_SEND_MODE send_mode;   /* one-shot request to send query with normal
+                                * or extended query protocol */
    int         bind_nparams;   /* number of parameters */
    char      **bind_params;    /* parameters for extended query protocol call */
+   char       *stmtName;       /* prepared statement name used for extended
+                                * query protocol commands */
    bool        crosstab_flag;  /* one-shot request to crosstab result */
    char       *ctv_args[4];    /* \crosstabview arguments */
 
index 024469474da7ce2bc6fc1b451d6adcdcc35865f4..0d25981253e61e1c1cb692da662a568c0e65647a 100644 (file)
@@ -1713,8 +1713,8 @@ psql_completion(const char *text, int start, int end)
    /* psql's backslash commands. */
    static const char *const backslash_commands[] = {
        "\\a",
-       "\\bind",
-       "\\connect", "\\conninfo", "\\C", "\\cd", "\\copy",
+       "\\bind", "\\bind_named",
+       "\\connect", "\\conninfo", "\\C", "\\cd", "\\close", "\\copy",
        "\\copyright", "\\crosstabview",
        "\\d", "\\da", "\\dA", "\\dAc", "\\dAf", "\\dAo", "\\dAp",
        "\\db", "\\dc", "\\dconfig", "\\dC", "\\dd", "\\ddp", "\\dD",
@@ -1731,7 +1731,7 @@ psql_completion(const char *text, int start, int end)
        "\\if", "\\include", "\\include_relative", "\\ir",
        "\\list", "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
        "\\out",
-       "\\password", "\\print", "\\prompt", "\\pset",
+       "\\parse", "\\password", "\\print", "\\prompt", "\\pset",
        "\\qecho", "\\quit",
        "\\reset",
        "\\s", "\\set", "\\setenv", "\\sf", "\\sv",
index 3bbe4c5f974d6fd70888e307ccbb63f2cbb2a144..6aeb7cb96369233fc24ae79eb552855727bb45e5 100644 (file)
@@ -98,6 +98,53 @@ two | 2
    1 |   2
 (1 row)
 
+-- \parse (extended query protocol)
+\parse
+\parse: missing required argument
+SELECT 1 \parse ''
+SELECT 2 \parse stmt1
+SELECT $1 \parse stmt2
+SELECT $1, $2 \parse stmt3
+-- \bind_named (extended query protocol)
+\bind_named
+\bind_named: missing required argument
+\bind_named '' \g
+ ?column? 
+----------
+        1
+(1 row)
+
+\bind_named stmt1 \g
+ ?column? 
+----------
+        2
+(1 row)
+
+\bind_named stmt2 'foo' \g
+ ?column? 
+----------
+ foo
+(1 row)
+
+\bind_named stmt3 'foo' 'bar' \g
+ ?column? | ?column? 
+----------+----------
+ foo      | bar
+(1 row)
+
+-- \close (extended query protocol)
+\close
+\close: missing required argument
+\close ''
+\close stmt2
+\close stmt2
+SELECT name, statement FROM pg_prepared_statements ORDER BY name;
+ name  |   statement    
+-------+----------------
+ stmt1 | SELECT 2 
+ stmt3 | SELECT $1, $2 
+(2 rows)
+
 -- \bind (extended query protocol)
 SELECT 1 \bind \g
  ?column? 
@@ -129,6 +176,11 @@ ERROR:  cannot insert multiple commands into a prepared statement
 -- bind error
 SELECT $1, $2 \bind 'foo' \g
 ERROR:  bind message supplies 1 parameters, but prepared statement "" requires 2
+-- bind_named error
+\bind_named stmt2 'baz' \g
+ERROR:  prepared statement "stmt2" does not exist
+\bind_named stmt3 'baz' \g
+ERROR:  bind message supplies 1 parameters, but prepared statement "stmt3" requires 2
 -- \gset
 select 10 as test01, 20 as test02, 'Hello' as test03 \gset pref01_
 \echo :pref01_test01 :pref01_test02 :pref01_test03
@@ -4507,9 +4559,11 @@ bar 'bar' "bar"
    \pset fieldsep | `nosuchcommand` :foo :'foo' :"foo"
    \a
    SELECT $1 \bind 1 \g
+   \bind_named stmt1 1 2 \g
    \C arg1
    \c arg1 arg2 arg3 arg4
    \cd arg1
+   \close stmt1
    \conninfo
    \copy arg1 arg2 arg3 arg4 arg5 arg6
    \copyright
@@ -4538,6 +4592,7 @@ invalid command \lo
    \lo_list
    \o arg1
    \p
+   SELECT 1 \parse
    \password arg1
    \prompt arg1 arg2
    \pset arg1 arg2
index 3b3c6f6e2944d17bff47db81b5000e7153935a2d..0a2f8b469226570301bfa53fd590bc4e4a1e4f33 100644 (file)
@@ -45,8 +45,28 @@ SELECT 1 as one, 2 as two \g (format=csv csv_fieldsep='\t')
 SELECT 1 as one, 2 as two \gx (title='foo bar')
 \g
 
--- \bind (extended query protocol)
+-- \parse (extended query protocol)
+\parse
+SELECT 1 \parse ''
+SELECT 2 \parse stmt1
+SELECT $1 \parse stmt2
+SELECT $1, $2 \parse stmt3
+
+-- \bind_named (extended query protocol)
+\bind_named
+\bind_named '' \g
+\bind_named stmt1 \g
+\bind_named stmt2 'foo' \g
+\bind_named stmt3 'foo' 'bar' \g
+
+-- \close (extended query protocol)
+\close
+\close ''
+\close stmt2
+\close stmt2
+SELECT name, statement FROM pg_prepared_statements ORDER BY name;
 
+-- \bind (extended query protocol)
 SELECT 1 \bind \g
 SELECT $1 \bind 'foo' \g
 SELECT $1, $2 \bind 'foo' 'bar' \g
@@ -58,6 +78,9 @@ SELECT foo \bind \g
 SELECT 1 \; SELECT 2 \bind \g
 -- bind error
 SELECT $1, $2 \bind 'foo' \g
+-- bind_named error
+\bind_named stmt2 'baz' \g
+\bind_named stmt3 'baz' \g
 
 -- \gset
 
@@ -990,9 +1013,11 @@ select \if false \\ (bogus \else \\ 42 \endif \\ forty_two;
    \pset fieldsep | `nosuchcommand` :foo :'foo' :"foo"
    \a
    SELECT $1 \bind 1 \g
+   \bind_named stmt1 1 2 \g
    \C arg1
    \c arg1 arg2 arg3 arg4
    \cd arg1
+   \close stmt1
    \conninfo
    \copy arg1 arg2 arg3 arg4 arg5 arg6
    \copyright
@@ -1020,6 +1045,7 @@ select \if false \\ (bogus \else \\ 42 \endif \\ forty_two;
    \lo_list
    \o arg1
    \p
+   SELECT 1 \parse
    \password arg1
    \prompt arg1 arg2
    \pset arg1 arg2
index 6d424c89186cfb17b3b63b72dd7eefa3bd32204f..3f3a8f2634bab3e6d794e4bb506094906fe6a4ce 100644 (file)
@@ -1958,6 +1958,7 @@ PSQL_COMP_CASE
 PSQL_ECHO
 PSQL_ECHO_HIDDEN
 PSQL_ERROR_ROLLBACK
+PSQL_SEND_MODE
 PTEntryArray
 PTIterationArray
 PTOKEN_PRIVILEGES