This is an automated email from the ASF dual-hosted git repository.

reshke pushed a commit to branch REL_2_STABLE
in repository https://gitbox.apache.org/repos/asf/cloudberry.git

commit c55112bb29a1e1dc96bfbeeed52bedc190ddc41e
Author: Nathan Bossart <[email protected]>
AuthorDate: Mon Aug 11 09:00:00 2025 -0500

    Restrict psql meta-commands in plain-text dumps.
    
    A malicious server could inject psql meta-commands into plain-text
    dump output (i.e., scripts created with pg_dump --format=plain,
    pg_dumpall, or pg_restore --file) that are run at restore time on
    the machine running psql.  To fix, introduce a new "restricted"
    mode in psql that blocks all meta-commands (except for \unrestrict
    to exit the mode), and teach pg_dump, pg_dumpall, and pg_restore to
    use this mode in plain-text dumps.
    
    While at it, encourage users to only restore dumps generated from
    trusted servers or to inspect it beforehand, since restoring causes
    the destination to execute arbitrary code of the source superusers'
    choice.  However, the client running the dump and restore needn't
    trust the source or destination superusers.
    
    Reported-by: Martin Rakhmanov
    Reported-by: Matthieu Denais <[email protected]>
    Reported-by: RyotaK <[email protected]>
    Suggested-by: Tom Lane <[email protected]>
    Reviewed-by: Noah Misch <[email protected]>
    Reviewed-by: Michael Paquier <[email protected]>
    Reviewed-by: Peter Eisentraut <[email protected]>
    Security: CVE-2025-8714
    Backpatch-through: 13
---
 doc/src/sgml/ref/pg_dump.sgml        | 35 ++++++++++++++
 doc/src/sgml/ref/pg_dumpall.sgml     | 30 ++++++++++++
 doc/src/sgml/ref/pg_restore.sgml     | 34 +++++++++++++
 doc/src/sgml/ref/pgupgrade.sgml      |  8 +++
 doc/src/sgml/ref/psql-ref.sgml       | 36 ++++++++++++++
 src/bin/pg_dump/dumputils.c          | 38 +++++++++++++++
 src/bin/pg_dump/dumputils.h          |  2 +
 src/bin/pg_dump/pg_backup.h          |  4 ++
 src/bin/pg_dump/pg_backup_archiver.c | 32 +++++++++++-
 src/bin/pg_dump/pg_dump.c            | 21 ++++++++
 src/bin/pg_dump/pg_dumpall.c         | 41 ++++++++++++++++
 src/bin/pg_dump/pg_restore.c         | 31 ++++++++++++
 src/bin/pg_dump/t/002_pg_dump.pl     | 19 ++++++--
 src/bin/pg_upgrade/test.sh           |  2 +
 src/bin/psql/command.c               | 94 +++++++++++++++++++++++++++++++++++-
 src/bin/psql/help.c                  |  4 ++
 src/bin/psql/tab-complete.c          |  4 +-
 src/test/regress/expected/psql.out   |  2 +
 src/test/regress/sql/psql.sql        |  2 +
 19 files changed, 429 insertions(+), 10 deletions(-)

diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml
index 4bf68d3fbd6..d3113d76a07 100644
--- a/doc/src/sgml/ref/pg_dump.sgml
+++ b/doc/src/sgml/ref/pg_dump.sgml
@@ -92,6 +92,18 @@ PostgreSQL documentation
    light of the limitations listed below.
   </para>
 
+  <warning>
+   <para>
+    Restoring a dump causes the destination to execute arbitrary code of the
+    source superusers' choice.  Partial dumps and partial restores do not limit
+    that.  If the source superusers are not trusted, the dumped SQL statements
+    must be inspected before restoring.  Non-plain-text dumps can be inspected
+    by using <application>pg_restore</application>'s <option>--file</option>
+    option.  Note that the client running the dump and restore need not trust
+    the source or destination superusers.
+   </para>
+  </warning>
+
  </refsect1>
 
  <refsect1 id="pg-dump-options">
@@ -1014,6 +1026,29 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>--restrict-key=<replaceable 
class="parameter">restrict_key</replaceable></option></term>
+      <listitem>
+       <para>
+        Use the provided string as the <application>psql</application>
+        <command>\restrict</command> key in the dump output.  This can only be
+        specified for plain-text dumps, i.e., when <option>--format</option> is
+        set to <literal>plain</literal> or the <option>--format</option> option
+        is omitted.  If no restrict key is specified,
+        <application>pg_dump</application> will generate a random one as
+        needed.  Keys may contain only alphanumeric characters.
+       </para>
+       <para>
+        This option is primarily intended for testing purposes and other
+        scenarios that require repeatable output (e.g., comparing dump files).
+        It is not recommended for general use, as a malicious server with
+        advance knowledge of the key may be able to inject arbitrary code that
+        will be executed on the machine that runs
+        <application>psql</application> with the dump output.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>--rows-per-insert=<replaceable 
class="parameter">nrows</replaceable></option></term>
       <listitem>
diff --git a/doc/src/sgml/ref/pg_dumpall.sgml b/doc/src/sgml/ref/pg_dumpall.sgml
index ae632f739cd..c025c4cf27f 100644
--- a/doc/src/sgml/ref/pg_dumpall.sgml
+++ b/doc/src/sgml/ref/pg_dumpall.sgml
@@ -65,6 +65,16 @@ PostgreSQL documentation
   linkend="libpq-pgpass"/> for more information.
   </para>
 
+  <warning>
+   <para>
+    Restoring a dump causes the destination to execute arbitrary code of the
+    source superusers' choice.  Partial dumps and partial restores do not limit
+    that.  If the source superusers are not trusted, the dumped SQL statements
+    must be inspected before restoring.  Note that the client running the dump
+    and restore need not trust the source or destination superusers.
+   </para>
+  </warning>
+
  </refsect1>
 
  <refsect1>
@@ -542,6 +552,26 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>--restrict-key=<replaceable 
class="parameter">restrict_key</replaceable></option></term>
+      <listitem>
+       <para>
+        Use the provided string as the <application>psql</application>
+        <command>\restrict</command> key in the dump output.  If no restrict
+        key is specified, <application>pg_dumpall</application> will generate a
+        random one as needed.  Keys may contain only alphanumeric characters.
+       </para>
+       <para>
+        This option is primarily intended for testing purposes and other
+        scenarios that require repeatable output (e.g., comparing dump files).
+        It is not recommended for general use, as a malicious server with
+        advance knowledge of the key may be able to inject arbitrary code that
+        will be executed on the machine that runs
+        <application>psql</application> with the dump output.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>--rows-per-insert=<replaceable 
class="parameter">nrows</replaceable></option></term>
       <listitem>
diff --git a/doc/src/sgml/ref/pg_restore.sgml b/doc/src/sgml/ref/pg_restore.sgml
index 1b56a4afb36..40f136670cb 100644
--- a/doc/src/sgml/ref/pg_restore.sgml
+++ b/doc/src/sgml/ref/pg_restore.sgml
@@ -68,6 +68,18 @@ PostgreSQL documentation
    <application>pg_restore</application> will not be able to load the data
    using <command>COPY</command> statements.
   </para>
+
+  <warning>
+   <para>
+    Restoring a dump causes the destination to execute arbitrary code of the
+    source superusers' choice.  Partial dumps and partial restores do not limit
+    that.  If the source superusers are not trusted, the dumped SQL statements
+    must be inspected before restoring.  Non-plain-text dumps can be inspected
+    by using <application>pg_restore</application>'s <option>--file</option>
+    option.  Note that the client running the dump and restore need not trust
+    the source or destination superusers.
+   </para>
+  </warning>
  </refsect1>
 
  <refsect1 id="app-pgrestore-options">
@@ -660,6 +672,28 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>--restrict-key=<replaceable 
class="parameter">restrict_key</replaceable></option></term>
+      <listitem>
+       <para>
+        Use the provided string as the <application>psql</application>
+        <command>\restrict</command> key in the dump output.  This can only be
+        specified for SQL script output, i.e., when the <option>--file</option>
+        option is used.  If no restrict key is specified,
+        <application>pg_restore</application> will generate a random one as
+        needed.  Keys may contain only alphanumeric characters.
+       </para>
+       <para>
+        This option is primarily intended for testing purposes and other
+        scenarios that require repeatable output (e.g., comparing dump files).
+        It is not recommended for general use, as a malicious server with
+        advance knowledge of the key may be able to inject arbitrary code that
+        will be executed on the machine that runs
+        <application>psql</application> with the dump output.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
        <term><option>--section=<replaceable 
class="parameter">sectionname</replaceable></option></term>
        <listitem>
diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 6069063b481..81cd4135eba 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -70,6 +70,14 @@ PostgreSQL documentation
    pg_upgrade supports upgrades from 9.2.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot 
and beta releases.
   </para>
+
+  <warning>
+   <para>
+    Upgrading a cluster causes the destination to execute arbitrary code of the
+    source superusers' choice.  Ensure that the source superusers are trusted
+    before upgrading.
+   </para>
+  </warning>
  </refsect1>
 
  <refsect1>
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 4fb777a3b83..f7c1ccad02e 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -3201,6 +3201,24 @@ lo_import 152801
       </varlistentry>
 
 
+      <varlistentry>
+        <term><literal>\restrict <replaceable 
class="parameter">restrict_key</replaceable></literal></term>
+        <listitem>
+        <para>
+        Enter "restricted" mode with the provided key.  In this mode, the only
+        allowed meta-command is <command>\unrestrict</command>, to exit
+        restricted mode.  The key may contain only alphanumeric characters.
+        </para>
+        <para>
+        This command is primarily intended for use in plain-text dumps
+        generated by <application>pg_dump</application>,
+        <application>pg_dumpall</application>, and
+        <application>pg_restore</application>, but it may be useful elsewhere.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
       <varlistentry>
         <term><literal>\s [ <replaceable 
class="parameter">filename</replaceable> ]</literal></term>
         <listitem>
@@ -3375,6 +3393,24 @@ testdb=&gt; <userinput>\setenv LESS -imx4F</userinput>
       </varlistentry>
 
 
+      <varlistentry>
+        <term><literal>\unrestrict <replaceable 
class="parameter">restrict_key</replaceable></literal></term>
+        <listitem>
+        <para>
+        Exit "restricted" mode (i.e., where all other meta-commands are
+        blocked), provided the specified key matches the one given to
+        <command>\restrict</command> when restricted mode was entered.
+        </para>
+        <para>
+        This command is primarily intended for use in plain-text dumps
+        generated by <application>pg_dump</application>,
+        <application>pg_dumpall</application>, and
+        <application>pg_restore</application>, but it may be useful elsewhere.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
       <varlistentry>
         <term><literal>\unset <replaceable 
class="parameter">name</replaceable></literal></term>
 
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index 1385d9fa8f1..ebb3654b725 100644
--- a/src/bin/pg_dump/dumputils.c
+++ b/src/bin/pg_dump/dumputils.c
@@ -19,6 +19,7 @@
 #include "dumputils.h"
 #include "fe_utils/string_utils.h"
 
+static const char restrict_chars[] = 
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
 
 static bool parseAclItem(const char *item, const char *type,
                                                 const char *name, const char 
*subname, int remoteVersion,
@@ -1071,3 +1072,40 @@ makeAlterConfigCommand(PGconn *conn, const char 
*configitem,
 
        pg_free(mine);
 }
+
+/*
+ * Generates a valid restrict key (i.e., an alphanumeric string) for use with
+ * psql's \restrict and \unrestrict meta-commands.  For safety, the value is
+ * chosen at random.
+ */
+char *
+generate_restrict_key(void)
+{
+       uint8           buf[64];
+       char       *ret = palloc(sizeof(buf));
+
+       if (!pg_strong_random(buf, sizeof(buf)))
+               return NULL;
+
+       for (int i = 0; i < sizeof(buf) - 1; i++)
+       {
+               uint8           idx = buf[i] % strlen(restrict_chars);
+
+               ret[i] = restrict_chars[idx];
+       }
+       ret[sizeof(buf) - 1] = '\0';
+
+       return ret;
+}
+
+/*
+ * Checks that a given restrict key (intended for use with psql's \restrict and
+ * \unrestrict meta-commands) contains only alphanumeric characters.
+ */
+bool
+valid_restrict_key(const char *restrict_key)
+{
+       return restrict_key != NULL &&
+               restrict_key[0] != '\0' &&
+               strspn(restrict_key, restrict_chars) == strlen(restrict_key);
+}
diff --git a/src/bin/pg_dump/dumputils.h b/src/bin/pg_dump/dumputils.h
index 8e7728150cc..2dc6b38992b 100644
--- a/src/bin/pg_dump/dumputils.h
+++ b/src/bin/pg_dump/dumputils.h
@@ -70,5 +70,7 @@ extern void makeAlterConfigCommand(PGconn *conn, const char 
*configitem,
 extern char *escape_backslashes(const char *src, bool quotes_too);
 extern char *escape_fmtopts_string(const char *src);
 extern char *custom_fmtopts_string(const char *src);
+extern char *generate_restrict_key(void);
+extern bool valid_restrict_key(const char *restrict_key);
 
 #endif                                                 /* DUMPUTILS_H */
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index e7f8140fb24..5f414e18686 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -136,6 +136,8 @@ typedef struct _restoreOptions
        int                     enable_row_security;
        int                     sequence_data;  /* dump sequence data even in 
schema-only mode */
        int                     binary_upgrade;
+
+       char       *restrict_key;
 } RestoreOptions;
 
 typedef struct _dumpOptions
@@ -182,6 +184,8 @@ typedef struct _dumpOptions
 
        int                     sequence_data;  /* dump sequence data even in 
schema-only mode */
        int                     do_nothing;
+
+       char       *restrict_key;
 } DumpOptions;
 
 
diff --git a/src/bin/pg_dump/pg_backup_archiver.c 
b/src/bin/pg_dump/pg_backup_archiver.c
index 591264869f1..8ca50fee958 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -209,6 +209,7 @@ dumpOptionsFromRestoreOptions(RestoreOptions *ropt)
        dopt->include_everything = ropt->include_everything;
        dopt->enable_row_security = ropt->enable_row_security;
        dopt->sequence_data = ropt->sequence_data;
+       dopt->restrict_key = ropt->restrict_key ? pg_strdup(ropt->restrict_key) 
: NULL;
 
        return dopt;
 }
@@ -465,6 +466,17 @@ RestoreArchive(Archive *AHX)
 
        ahprintf(AH, "--\n-- Apache Cloudberry database dump\n--\n\n");
 
+       /*
+        * If generating plain-text output, enter restricted mode to block any
+        * unexpected psql meta-commands.  A malicious source might try to 
inject
+        * a variety of things via bogus responses to queries.  While we cannot
+        * prevent such sources from affecting the destination at restore time, 
we
+        * can block psql meta-commands so that the client machine that runs 
psql
+        * with the dump output remains unaffected.
+        */
+       if (ropt->restrict_key)
+               ahprintf(AH, "\\restrict %s\n\n", ropt->restrict_key);
+
        if (AH->archiveRemoteVersion)
                ahprintf(AH, "-- Dumped from database version %s\n",
                                 AH->archiveRemoteVersion);
@@ -746,6 +758,14 @@ RestoreArchive(Archive *AHX)
 
        ahprintf(AH, "--\n-- Apache Cloudberry database dump complete\n--\n\n");
 
+       /*
+        * If generating plain-text output, exit restricted mode at the very end
+        * of the script. This is not pro forma; in particular, pg_dumpall
+        * requires this when transitioning from one database to another.
+        */
+       if (ropt->restrict_key)
+               ahprintf(AH, "\\unrestrict %s\n\n", ropt->restrict_key);
+
        /*
         * Clean up & we're done.
         */
@@ -3282,11 +3302,21 @@ _reconnectToDB(ArchiveHandle *AH, const char *dbname)
        else
        {
                PQExpBufferData connectbuf;
+               RestoreOptions *ropt = AH->public.ropt;
+
+               /*
+                * We must temporarily exit restricted mode for \connect, etc.
+                * Anything added between this line and the following \restrict 
must
+                * be careful to avoid any possible meta-command injection 
vectors.
+                */
+               ahprintf(AH, "\\unrestrict %s\n", ropt->restrict_key);
 
                initPQExpBuffer(&connectbuf);
                appendPsqlMetaConnect(&connectbuf, dbname);
-               ahprintf(AH, "%s\n", connectbuf.data);
+               ahprintf(AH, "%s", connectbuf.data);
                termPQExpBuffer(&connectbuf);
+
+               ahprintf(AH, "\\restrict %s\n\n", ropt->restrict_key);
        }
 
        /*
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 625cc45fc2f..a484f693b43 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -544,6 +544,7 @@ main(int argc, char **argv)
                {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
                {"rows-per-insert", required_argument, NULL, 10},
                {"include-foreign-data", required_argument, NULL, 11},
+               {"restrict-key", required_argument, NULL, 25},
 
                /* START MPP ADDITION */
 
@@ -805,6 +806,10 @@ main(int argc, char **argv)
                                                                                
  optarg);
                                break;
 
+                       case 25:
+                               dopt.restrict_key = pg_strdup(optarg);
+                               break;
+
                        default:
                                fprintf(stderr, _("Try \"%s --help\" for more 
information.\n"), progname);
                                exit_nicely(1);
@@ -873,8 +878,22 @@ main(int argc, char **argv)
 
        /* archiveFormat specific setup */
        if (archiveFormat == archNull)
+       {
                plainText = 1;
 
+               /*
+                * If you don't provide a restrict key, one will be appointed 
for you.
+                */
+               if (!dopt.restrict_key)
+                       dopt.restrict_key = generate_restrict_key();
+               if (!dopt.restrict_key)
+                       fatal("could not generate restrict key");
+               if (!valid_restrict_key(dopt.restrict_key))
+                       fatal("invalid restrict key");
+       }
+       else if (dopt.restrict_key)
+               fatal("option --restrict-key can only be used with 
--format=plain");
+
        /* Custom and directory formats are compressed by default, others not */
        if (compressLevel == -1)
        {
@@ -1201,6 +1220,7 @@ main(int argc, char **argv)
        ropt->enable_row_security = dopt.enable_row_security;
        ropt->sequence_data = dopt.sequence_data;
        ropt->binary_upgrade = dopt.binary_upgrade;
+       ropt->restrict_key = dopt.restrict_key ? pg_strdup(dopt.restrict_key) : 
NULL;
 
        if (compressLevel == -1)
                ropt->compression = 0;
@@ -1298,6 +1318,7 @@ help(const char *progname)
        printf(_("  --no-unlogged-table-data     do not dump unlogged table 
data\n"));
        printf(_("  --on-conflict-do-nothing     add ON CONFLICT DO NOTHING to 
INSERT commands\n"));
        printf(_("  --quote-all-identifiers      quote all identifiers, even if 
not key words\n"));
+       printf(_("  --restrict-key=RESTRICT_KEY  use provided string as psql 
\\restrict key\n"));
        printf(_("  --rows-per-insert=NROWS      number of rows per INSERT; 
implies --inserts\n"));
        printf(_("  --section=SECTION            dump named section (pre-data, 
data, or post-data)\n"));
        printf(_("  --serializable-deferrable    wait until the dump can run 
without anomalies\n"));
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 7f2ff68a955..1684a2b87ca 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -109,6 +109,8 @@ static char *filename = NULL;
 static SimpleStringList database_exclude_patterns = {NULL, NULL};
 static SimpleStringList database_exclude_names = {NULL, NULL};
 
+static char *restrict_key;
+
 #define exit_nicely(code) exit(code)
 
 int
@@ -166,6 +168,7 @@ main(int argc, char *argv[])
                {"no-unlogged-table-data", no_argument, 
&no_unlogged_table_data, 1},
                {"on-conflict-do-nothing", no_argument, 
&on_conflict_do_nothing, 1},
                {"rows-per-insert", required_argument, NULL, 7},
+               {"restrict-key", required_argument, NULL, 9},
 
                /* START MPP ADDITION */
                {"gp-syntax", no_argument, NULL, 1000},
@@ -370,6 +373,11 @@ main(int argc, char *argv[])
                                appendPQExpBufferStr(pgdumpopts, " 
--rows-per-insert ");
                                appendShellString(pgdumpopts, optarg);
                                break;
+                       case 9:
+                               restrict_key = pg_strdup(optarg);
+                               appendPQExpBufferStr(pgdumpopts, " 
--restrict-key ");
+                               appendShellString(pgdumpopts, optarg);
+                               break;
 
                                /* START MPP ADDITION */
                        case 1000:
@@ -500,6 +508,22 @@ main(int argc, char *argv[])
        if (roles_only)
                appendPQExpBufferStr(pgdumpopts, " --roles-only");
 
+       /*
+        * If you don't provide a restrict key, one will be appointed for you.
+        */
+       if (!restrict_key)
+               restrict_key = generate_restrict_key();
+       if (!restrict_key)
+       {
+               pg_log_error("could not generate restrict key");
+               exit_nicely(1);
+       }
+       if (!valid_restrict_key(restrict_key))
+       {
+               pg_log_error("invalid restrict key");
+               exit_nicely(1);
+       }
+
        /*
         * If there was a database specified on the command line, use that,
         * otherwise try to connect to database "postgres", and failing that
@@ -598,6 +622,16 @@ main(int argc, char *argv[])
        if (verbose)
                dumpTimestamp("Started on");
 
+       /*
+        * Enter restricted mode to block any unexpected psql meta-commands.  A
+        * malicious source might try to inject a variety of things via bogus
+        * responses to queries.  While we cannot prevent such sources from
+        * affecting the destination at restore time, we can block psql
+        * meta-commands so that the client machine that runs psql with the dump
+        * output remains unaffected.
+        */
+       fprintf(OPF, "\\restrict %s\n\n", restrict_key);
+
        /*
         * We used to emit \connect postgres here, but that served no purpose
         * other than to break things for installations without a postgres
@@ -685,6 +719,12 @@ main(int argc, char *argv[])
                        dumpTablespaces(conn);
        }
 
+       /*
+        * Exit restricted mode just before dumping the databases.  pg_dump will
+        * handle entering restricted mode again as appropriate.
+        */
+       fprintf(OPF, "\\unrestrict %s\n\n", restrict_key);
+
        if (!globals_only && !roles_only && !tablespaces_only)
                dumpDatabases(conn);
 
@@ -753,6 +793,7 @@ help(void)
        printf(_("  --no-unlogged-table-data     do not dump unlogged table 
data\n"));
        printf(_("  --on-conflict-do-nothing     add ON CONFLICT DO NOTHING to 
INSERT commands\n"));
        printf(_("  --quote-all-identifiers      quote all identifiers, even if 
not key words\n"));
+       printf(_("  --restrict-key=RESTRICT_KEY  use provided string as psql 
\\restrict key\n"));
        printf(_("  --rows-per-insert=NROWS      number of rows per INSERT; 
implies --inserts\n"));
        printf(_("  --use-set-session-authorization\n"
                         "                               use SET SESSION 
AUTHORIZATION commands instead of\n"
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index 2d03735c7d2..e492643a394 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -122,6 +122,7 @@ main(int argc, char **argv)
                {"no-publications", no_argument, &no_publications, 1},
                {"no-security-labels", no_argument, &no_security_labels, 1},
                {"no-subscriptions", no_argument, &no_subscriptions, 1},
+               {"restrict-key", required_argument, NULL, 6},
 
                /* GPDB */
                {"binary-upgrade", no_argument, &binary_upgrade, 1},
@@ -285,6 +286,10 @@ main(int argc, char **argv)
                                set_dump_section(optarg, &(opts->dumpSections));
                                break;
 
+                       case 6:
+                               opts->restrict_key = pg_strdup(optarg);
+                               break;
+
                        default:
                                fprintf(stderr, _("Try \"%s --help\" for more 
information.\n"), progname);
                                exit_nicely(1);
@@ -324,8 +329,33 @@ main(int argc, char **argv)
                                        progname);
                        exit_nicely(1);
                }
+
+               if (opts->restrict_key)
+               {
+                       pg_log_error("options -d/--dbname and --restrict-key 
cannot be used together");
+                       exit_nicely(1);
+               }
+
                opts->useDB = 1;
        }
+       else
+       {
+               /*
+                * If you don't provide a restrict key, one will be appointed 
for you.
+                */
+               if (!opts->restrict_key)
+                       opts->restrict_key = generate_restrict_key();
+               if (!opts->restrict_key)
+               {
+                       pg_log_error("could not generate restrict key");
+                       exit_nicely(1);
+               }
+               if (!valid_restrict_key(opts->restrict_key))
+               {
+                       pg_log_error("invalid restrict key");
+                       exit_nicely(1);
+               }
+       }
 
        if (opts->dataOnly && opts->schemaOnly)
        {
@@ -509,6 +539,7 @@ usage(const char *progname)
        printf(_("  --no-security-labels         do not restore security 
labels\n"));
        printf(_("  --no-subscriptions           do not restore 
subscriptions\n"));
        printf(_("  --no-tablespaces             do not restore tablespace 
assignments\n"));
+       printf(_("  --restrict-key=RESTRICT_KEY  use provided string as psql 
\\restrict key\n"));
        printf(_("  --section=SECTION            restore named section 
(pre-data, data, or post-data)\n"));
        printf(_("  --strict-names               require table and/or schema 
include patterns to\n"
                         "                               match at least one 
entity each\n"));
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index d669bd9b09f..1a6ab11a3ed 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -425,6 +425,16 @@ my %full_runs = (
 
 # This is where the actual tests are defined.
 my %tests = (
+       'restrict' => {
+               all_runs => 1,
+               regexp => qr/^\\restrict [a-zA-Z0-9]+$/m,
+       },
+
+       'unrestrict' => {
+               all_runs => 1,
+               regexp => qr/^\\unrestrict [a-zA-Z0-9]+$/m,
+       },
+
        'ALTER DEFAULT PRIVILEGES FOR ROLE regress_dump_test_role GRANT' => {
                create_order => 14,
                create_sql   => 'ALTER DEFAULT PRIVILEGES
@@ -2993,7 +3003,6 @@ my %tests = (
        },
 
        'ALTER TABLE measurement PRIMARY KEY' => {
-               all_runs     => 1,
                catch_all    => 'CREATE ... commands',
                create_order => 93,
                create_sql =>
@@ -3030,7 +3039,6 @@ my %tests = (
        },
 
        'ALTER INDEX ... ATTACH PARTITION (primary key)' => {
-               all_runs  => 1,
                catch_all => 'CREATE ... commands',
                regexp    => qr/^
                \QALTER INDEX dump_test.measurement_pkey ATTACH PARTITION 
dump_test_second_schema.measurement_y2006m2_pkey\E
@@ -4003,9 +4011,10 @@ foreach my $run (sort keys %pgdump_runs)
                        next;
                }
 
-               # Run the test listed as a like, unless it is specifically noted
-               # as an unlike (generally due to an explicit exclusion or 
similar).
-               if ($tests{$test}->{like}->{$test_key}
+               # Run the test if all_runs is set or if listed as a like, 
unless it is
+               # specifically noted as an unlike (generally due to an explicit
+               # exclusion or similar).
+               if (($tests{$test}->{like}->{$test_key} || 
$tests{$test}->{all_runs})
                        && !defined($tests{$test}->{unlike}->{$test_key}))
                {
                        if (!ok($output_file =~ $tests{$test}->{regexp},
diff --git a/src/bin/pg_upgrade/test.sh b/src/bin/pg_upgrade/test.sh
index 32d186d8974..3891b0543d3 100644
--- a/src/bin/pg_upgrade/test.sh
+++ b/src/bin/pg_upgrade/test.sh
@@ -196,6 +196,7 @@ if "$MAKE" -C "$oldsrc" installcheck-parallel; then
        fi
 
        pg_dumpall $extra_dump_options --no-sync \
+               --restrict-key=test \
                -f "$temp_root"/dump1.sql || pg_dumpall1_status=$?
 
        if [ "$newsrc" != "$oldsrc" ]; then
@@ -256,6 +257,7 @@ esac
 pg_ctl start -l "$logdir/postmaster2.log" -o "$POSTMASTER_OPTS" -w
 
 pg_dumpall $extra_dump_options --no-sync \
+       --restrict-key=test \
        -f "$temp_root"/dump2.sql || pg_dumpall2_status=$?
 pg_ctl -m fast stop
 
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 269925342be..b00fb1197f5 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -119,6 +119,8 @@ static backslashResult exec_command_pset(PsqlScanState 
scan_state, bool active_b
 static backslashResult exec_command_quit(PsqlScanState scan_state, bool 
active_branch);
 static backslashResult exec_command_reset(PsqlScanState scan_state, bool 
active_branch,
                                                                                
  PQExpBuffer query_buf);
+static backslashResult exec_command_restrict(PsqlScanState scan_state, bool 
active_branch,
+                                                                               
         const char *cmd);
 static backslashResult exec_command_s(PsqlScanState scan_state, bool 
active_branch);
 static backslashResult exec_command_set(PsqlScanState scan_state, bool 
active_branch);
 static backslashResult exec_command_setenv(PsqlScanState scan_state, bool 
active_branch,
@@ -128,6 +130,8 @@ static backslashResult exec_command_sf_sv(PsqlScanState 
scan_state, bool active_
 static backslashResult exec_command_t(PsqlScanState scan_state, bool 
active_branch);
 static backslashResult exec_command_T(PsqlScanState scan_state, bool 
active_branch);
 static backslashResult exec_command_timing(PsqlScanState scan_state, bool 
active_branch);
+static backslashResult exec_command_unrestrict(PsqlScanState scan_state, bool 
active_branch,
+                                                                               
           const char *cmd);
 static backslashResult exec_command_unset(PsqlScanState scan_state, bool 
active_branch,
                                                                                
  const char *cmd);
 static backslashResult exec_command_write(PsqlScanState scan_state, bool 
active_branch,
@@ -176,6 +180,8 @@ static char *pset_value_string(const char *param, 
printQueryOpt *popt);
 static void checkWin32Codepage(void);
 #endif
 
+static bool restricted;
+static char *restrict_key;
 
 
 /*----------
@@ -221,8 +227,19 @@ HandleSlashCmds(PsqlScanState scan_state,
        /* Parse off the command name */
        cmd = psql_scan_slash_command(scan_state);
 
-       /* And try to execute it */
-       status = exec_command(cmd, scan_state, cstack, query_buf, previous_buf);
+       /*
+        * And try to execute it.
+        *
+        * If we are in "restricted" mode, the only allowable backslash command 
is
+        * \unrestrict (to exit restricted mode).
+        */
+       if (restricted && strcmp(cmd, "unrestrict") != 0)
+       {
+               pg_log_error("backslash commands are restricted; only 
\\unrestrict is allowed");
+               status = PSQL_CMD_ERROR;
+       }
+       else
+               status = exec_command(cmd, scan_state, cstack, query_buf, 
previous_buf);
 
        if (status == PSQL_CMD_UNKNOWN)
        {
@@ -379,6 +396,8 @@ exec_command(const char *cmd,
                status = exec_command_quit(scan_state, active_branch);
        else if (strcmp(cmd, "r") == 0 || strcmp(cmd, "reset") == 0)
                status = exec_command_reset(scan_state, active_branch, 
query_buf);
+       else if (strcmp(cmd, "restrict") == 0)
+               status = exec_command_restrict(scan_state, active_branch, cmd);
        else if (strcmp(cmd, "s") == 0)
                status = exec_command_s(scan_state, active_branch);
        else if (strcmp(cmd, "set") == 0)
@@ -395,6 +414,8 @@ exec_command(const char *cmd,
                status = exec_command_T(scan_state, active_branch);
        else if (strcmp(cmd, "timing") == 0)
                status = exec_command_timing(scan_state, active_branch);
+       else if (strcmp(cmd, "unrestrict") == 0)
+               status = exec_command_unrestrict(scan_state, active_branch, 
cmd);
        else if (strcmp(cmd, "unset") == 0)
                status = exec_command_unset(scan_state, active_branch, cmd);
        else if (strcmp(cmd, "w") == 0 || strcmp(cmd, "write") == 0)
@@ -2252,6 +2273,35 @@ exec_command_reset(PsqlScanState scan_state, bool 
active_branch,
        return PSQL_CMD_SKIP_LINE;
 }
 
+/*
+ * \restrict -- enter "restricted mode" with the provided key
+ */
+static backslashResult
+exec_command_restrict(PsqlScanState scan_state, bool active_branch,
+                                         const char *cmd)
+{
+       if (active_branch)
+       {
+               char       *opt;
+
+               Assert(!restricted);
+
+               opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true);
+               if (opt == NULL || opt[0] == '\0')
+               {
+                       pg_log_error("\\%s: missing required argument", cmd);
+                       return PSQL_CMD_ERROR;
+               }
+
+               restrict_key = pstrdup(opt);
+               restricted = true;
+       }
+       else
+               ignore_slash_options(scan_state);
+
+       return PSQL_CMD_SKIP_LINE;
+}
+
 /*
  * \s -- save history in a file or show it on the screen
  */
@@ -2554,6 +2604,46 @@ exec_command_timing(PsqlScanState scan_state, bool 
active_branch)
        return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
 }
 
+/*
+ * \unrestrict -- exit "restricted mode" if provided key matches
+ */
+static backslashResult
+exec_command_unrestrict(PsqlScanState scan_state, bool active_branch,
+                                               const char *cmd)
+{
+       if (active_branch)
+       {
+               char       *opt;
+
+               opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true);
+               if (opt == NULL || opt[0] == '\0')
+               {
+                       pg_log_error("\\%s: missing required argument", cmd);
+                       return PSQL_CMD_ERROR;
+               }
+
+               if (!restricted)
+               {
+                       pg_log_error("\\%s: not currently in restricted mode", 
cmd);
+                       return PSQL_CMD_ERROR;
+               }
+               else if (strcmp(opt, restrict_key) == 0)
+               {
+                       pfree(restrict_key);
+                       restricted = false;
+               }
+               else
+               {
+                       pg_log_error("\\%s: wrong key", cmd);
+                       return PSQL_CMD_ERROR;
+               }
+       }
+       else
+               ignore_slash_options(scan_state);
+
+       return PSQL_CMD_SKIP_LINE;
+}
+
 /*
  * \unset -- unset variable
  */
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 5ebb566c3b9..73d1dca4d09 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -182,6 +182,10 @@ slashUsage(unsigned short int pager)
        fprintf(output, _("  \\gset [PREFIX]         execute query and store 
results in psql variables\n"));
        fprintf(output, _("  \\gx [(OPTIONS)] [FILE] as \\g, but forces 
expanded output mode\n"));
        fprintf(output, _("  \\q                     quit psql\n"));
+       fprintf(output, _("  \\restrict RESTRICT_KEY\n"
+                                         "                         enter 
restricted mode with provided key\n"));
+       fprintf(output, _("  \\unrestrict RESTRICT_KEY\n"
+                                         "                         exit 
restricted mode if key matches\n"));
        fprintf(output, _("  \\watch [SEC]           execute query every SEC 
seconds\n"));
        fprintf(output, "\n");
 
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 1bdb618da93..dc401f503b1 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1603,10 +1603,10 @@ psql_completion(const char *text, int start, int end)
                "\\o",
                "\\p", "\\password", "\\prompt", "\\pset",
                "\\q", "\\qecho",
-               "\\r",
+               "\\r", "\\restrict",
                "\\s", "\\set", "\\setenv", "\\sf", "\\sv",
                "\\t", "\\T", "\\timing",
-               "\\unset",
+               "\\unrestrict", "\\unset",
                "\\x",
                "\\w", "\\warn", "\\watch",
                "\\z",
diff --git a/src/test/regress/expected/psql.out 
b/src/test/regress/expected/psql.out
index 2f06a903b57..800e2761083 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4581,6 +4581,7 @@ invalid command \lo
        \pset arg1 arg2
        \q
        \reset
+       \restrict test
        \s arg1
        \set arg1 arg2 arg3 arg4 arg5 arg6 arg7
        \setenv arg1 arg2
@@ -4589,6 +4590,7 @@ invalid command \lo
        \t arg1
        \T arg1
        \timing arg1
+       \unrestrict not_valid
        \unset arg1
        \w arg1
        \watch arg1
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index 467d0b42143..36a68595d5e 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -987,6 +987,7 @@ select \if false \\ (bogus \else \\ 42 \endif \\ forty_two;
        \pset arg1 arg2
        \q
        \reset
+       \restrict test
        \s arg1
        \set arg1 arg2 arg3 arg4 arg5 arg6 arg7
        \setenv arg1 arg2
@@ -995,6 +996,7 @@ select \if false \\ (bogus \else \\ 42 \endif \\ forty_two;
        \t arg1
        \T arg1
        \timing arg1
+       \unrestrict not_valid
        \unset arg1
        \w arg1
        \watch arg1


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to