diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index ebb7a1173f9..012c8ce8c94 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -3397,6 +3397,11 @@ testdb=> \setenv LESS -imx4F
pg_dumpall, and
pg_restore, but it may be useful elsewhere.
+
+ Unlike most other meta-commands, the entire remainder of the line is
+ always taken to be the argument of \unrestrict, and
+ neither variable interpolation nor backquote expansion are performed.
+
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 5d95bdb5f6f..3202aa3db8f 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -2285,6 +2285,12 @@ exec_command_restrict(PsqlScanState scan_state, bool active_branch,
Assert(!restricted);
+ /*
+ * Unlike \unrestrict, this argument may safely undergo backquote and
+ * variable expansion: HandleSlashCmds() rejects \restrict in
+ * restricted mode before its argument is scanned, so we only get here
+ * when the input could execute such things anyway.
+ */
opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true);
if (opt == NULL || opt[0] == '\0')
{
@@ -2613,14 +2619,23 @@ exec_command_unrestrict(PsqlScanState scan_state, bool active_branch,
if (active_branch)
{
char *opt;
+ size_t len;
- opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true);
+ opt = psql_scan_slash_option(scan_state, OT_WHOLE_LINE, NULL, true);
if (opt == NULL || opt[0] == '\0')
{
pg_log_error("\\%s: missing required argument", cmd);
return PSQL_CMD_ERROR;
}
+ /* strip any trailing spaces and semicolons */
+ len = strlen(opt);
+ while (len > 0 &&
+ (opt[len - 1] == ';' ||
+ (isascii((unsigned char) opt[len - 1]) &&
+ isspace((unsigned char) opt[len - 1]))))
+ opt[--len] = '\0';
+
if (!restricted)
{
pg_log_error("\\%s: not currently in restricted mode", cmd);
@@ -2638,7 +2653,7 @@ exec_command_unrestrict(PsqlScanState scan_state, bool active_branch,
}
}
else
- ignore_slash_options(scan_state);
+ ignore_slash_whole_line(scan_state);
return PSQL_CMD_SKIP_LINE;
}