Skip to content

[pull] master from postgres:master #993

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions doc/src/sgml/ddl.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,16 @@ CREATE TABLE people (
<varname>tableoid</varname>.
</para>
</listitem>
<listitem>
<para>
A virtual generated column cannot have a user-defined type, and the
generation expression of a virtual generated column must not reference
user-defined functions or types, that is, it can only use built-in
functions or types. This applies also indirectly, such as for functions
or types that underlie operators or casts. (This restriction does not
exist for stored generated columns.)
</para>
</listitem>
<listitem>
<para>
A generated column cannot have a column default or an identity definition.
Expand Down
16 changes: 8 additions & 8 deletions doc/src/sgml/ref/alter_table.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -460,16 +460,16 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
<para>
This form adds a new constraint to a table using the same constraint
syntax as <link linkend="sql-createtable"><command>CREATE TABLE</command></link>, plus the option <literal>NOT
VALID</literal>, which is currently only allowed for foreign key,
<literal>CHECK</literal> constraints and not-null constraints.
VALID</literal>, which is currently only allowed for foreign-key,
<literal>CHECK</literal>, and not-null constraints.
</para>

<para>
Normally, this form will cause a scan of the table to verify that all
existing rows in the table satisfy the new constraint. But if
the <literal>NOT VALID</literal> option is used, this
potentially-lengthy scan is skipped. The constraint will still be
enforced against subsequent inserts or updates (that is, they'll fail
applied against subsequent inserts or updates (that is, they'll fail
unless there is a matching row in the referenced table, in the case
of foreign keys, or they'll fail unless the new row matches the
specified check condition). But the
Expand Down Expand Up @@ -591,7 +591,7 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
This form validates a foreign key, check, or not-null constraint that was
previously created as <literal>NOT VALID</literal>, by scanning the
table to ensure there are no rows for which the constraint is not
satisfied. If the constraint is not enforced, an error is thrown.
satisfied. If the constraint was set to <literal>NOT ENFORCED</literal>, an error is thrown.
Nothing happens if the constraint is already marked valid.
(See <xref linkend="sql-altertable-notes"/> below for an explanation
of the usefulness of this command.)
Expand Down Expand Up @@ -1466,11 +1466,11 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</para>

<para>
Adding an enforced <literal>CHECK</literal> or <literal>NOT NULL</literal>
Adding a <literal>CHECK</literal> or <literal>NOT NULL</literal>
constraint requires scanning the table to verify that existing rows meet the
constraint, but does not require a table rewrite. If a <literal>CHECK</literal>
constraint is added as <literal>NOT ENFORCED</literal>, the validation will
not be performed.
constraint is added as <literal>NOT ENFORCED</literal>, no verification will
be performed.
</para>

<para>
Expand All @@ -1485,7 +1485,7 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</para>

<para>
Scanning a large table to verify a new foreign key or check constraint
Scanning a large table to verify new foreign-key, check, or not-null constraints
can take a long time, and other updates to the table are locked out
until the <command>ALTER TABLE ADD CONSTRAINT</command> command is
committed. The main purpose of the <literal>NOT VALID</literal>
Expand Down
9 changes: 9 additions & 0 deletions doc/src/sgml/ref/create_table.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,15 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
not other generated columns. Any functions and operators used must be
immutable. References to other tables are not allowed.
</para>

<para>
A virtual generated column cannot have a user-defined type, and the
generation expression of a virtual generated column must not reference
user-defined functions or types, that is, it can only use built-in
functions or types. This applies also indirectly, such as for functions
or types that underlie operators or casts. (This restriction does not
exist for stored generated columns.)
</para>
</listitem>
</varlistentry>

Expand Down
59 changes: 29 additions & 30 deletions doc/src/sgml/ref/pg_createsubscriber.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -169,36 +169,6 @@ PostgreSQL documentation
</listitem>
</varlistentry>

<varlistentry>
<term><option>-R <replaceable class="parameter">objtype</replaceable></option></term>
<term><option>--remove=<replaceable class="parameter">objtype</replaceable></option></term>
<listitem>
<para>
Remove all objects of the specified type from specified databases on the
target server.
</para>
<para>
<itemizedlist>
<listitem>
<para>
<literal>publications</literal>:
The <literal>FOR ALL TABLES</literal> publications established for this
subscriber are always removed; specifying this object type causes all
other publications replicated from the source server to be dropped as
well.
</para>
</listitem>
</itemizedlist>
</para>
<para>
The objects selected to be dropped are individually logged, including during
a <option>--dry-run</option>. There is no opportunity to affect or stop the
dropping of the selected objects, so consider taking a backup of them
using <application>pg_dump</application>.
</para>
</listitem>
</varlistentry>

<varlistentry>
<term><option>-s <replaceable class="parameter">dir</replaceable></option></term>
<term><option>--socketdir=<replaceable class="parameter">dir</replaceable></option></term>
Expand Down Expand Up @@ -259,6 +229,35 @@ PostgreSQL documentation
</listitem>
</varlistentry>

<varlistentry>
<term><option>--clean=<replaceable class="parameter">objtype</replaceable></option></term>
<listitem>
<para>
Drop all objects of the specified type from specified databases on the
target server.
</para>
<para>
<itemizedlist>
<listitem>
<para>
<literal>publications</literal>:
The <literal>FOR ALL TABLES</literal> publications established for this
subscriber are always dropped; specifying this object type causes all
other publications replicated from the source server to be dropped as
well.
</para>
</listitem>
</itemizedlist>
</para>
<para>
The objects selected to be dropped are individually logged, including during
a <option>--dry-run</option>. There is no opportunity to affect or stop the
dropping of the selected objects, so consider taking a backup of them
using <application>pg_dump</application>.
</para>
</listitem>
</varlistentry>

<varlistentry>
<term><option>--config-file=<replaceable class="parameter">filename</replaceable></option></term>
<listitem>
Expand Down
93 changes: 93 additions & 0 deletions src/backend/catalog/heap.c
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,15 @@ CheckAttributeType(const char *attname,
flags);
}

/*
* For consistency with check_virtual_generated_security().
*/
if ((flags & CHKATYPE_IS_VIRTUAL) && atttypid >= FirstUnpinnedObjectId)
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("virtual generated column \"%s\" cannot have a user-defined type", attname),
errdetail("Virtual generated columns that make use of user-defined types are not yet supported."));

/*
* This might not be strictly invalid per SQL standard, but it is pretty
* useless, and it cannot be dumped, so we must disallow it.
Expand Down Expand Up @@ -3215,6 +3224,86 @@ check_nested_generated(ParseState *pstate, Node *node)
check_nested_generated_walker(node, pstate);
}

/*
* Check security of virtual generated column expression.
*
* Just like selecting from a view is exploitable (CVE-2024-7348), selecting
* from a table with virtual generated columns is exploitable. Users who are
* concerned about this can avoid selecting from views, but telling them to
* avoid selecting from tables is less practical.
*
* To address this, this restricts generation expressions for virtual
* generated columns are restricted to using built-in functions and types. We
* assume that built-in functions and types cannot be exploited for this
* purpose. Note the overall security also requires that all functions in use
* a immutable. (For example, there are some built-in non-immutable functions
* that can run arbitrary SQL.) The immutability is checked elsewhere, since
* that is a property that needs to hold independent of security
* considerations.
*
* In the future, this could be expanded by some new mechanism to declare
* other functions and types as safe or trusted for this purpose, but that is
* to be designed.
*/

/*
* Callback for check_functions_in_node() that determines whether a function
* is user-defined.
*/
static bool
contains_user_functions_checker(Oid func_id, void *context)
{
return (func_id >= FirstUnpinnedObjectId);
}

/*
* Checks for all the things we don't want in the generation expressions of
* virtual generated columns for security reasons. Errors out if it finds
* one.
*/
static bool
check_virtual_generated_security_walker(Node *node, void *context)
{
ParseState *pstate = context;

if (node == NULL)
return false;

if (!IsA(node, List))
{
if (check_functions_in_node(node, contains_user_functions_checker, NULL))
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("generation expression uses user-defined function"),
errdetail("Virtual generated columns that make use of user-defined functions are not yet supported."),
parser_errposition(pstate, exprLocation(node)));

/*
* check_functions_in_node() doesn't check some node types (see
* comment there). We handle CoerceToDomain and MinMaxExpr by
* checking for built-in types. The other listed node types cannot
* call user-definable SQL-visible functions.
*
* We furthermore need this type check to handle built-in, immutable
* polymorphic functions such as array_eq().
*/
if (exprType(node) >= FirstUnpinnedObjectId)
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("generation expression uses user-defined type"),
errdetail("Virtual generated columns that make use of user-defined types are not yet supported."),
parser_errposition(pstate, exprLocation(node)));
}

return expression_tree_walker(node, check_virtual_generated_security_walker, context);
}

static void
check_virtual_generated_security(ParseState *pstate, Node *node)
{
check_virtual_generated_security_walker(node, pstate);
}

/*
* Take a raw default and convert it to a cooked format ready for
* storage.
Expand Down Expand Up @@ -3254,6 +3343,10 @@ cookDefault(ParseState *pstate,
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("generation expression is not immutable")));

/* Check security of expressions for virtual generated column */
if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
check_virtual_generated_security(pstate, expr);
}
else
{
Expand Down
34 changes: 17 additions & 17 deletions src/bin/pg_basebackup/pg_createsubscriber.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ struct CreateSubscriberOptions
SimpleStringList replslot_names; /* list of replication slot names */
int recovery_timeout; /* stop recovery after this time */
bool all_dbs; /* all option */
SimpleStringList objecttypes_to_remove; /* list of object types to remove */
SimpleStringList objecttypes_to_clean; /* list of object types to cleanup */
};

/* per-database publication/subscription info */
Expand All @@ -71,8 +71,8 @@ struct LogicalRepInfos
{
struct LogicalRepInfo *dbinfo;
bool two_phase; /* enable-two-phase option */
bits32 objecttypes_to_remove; /* flags indicating which object types
* to remove on subscriber */
bits32 objecttypes_to_clean; /* flags indicating which object types
* to clean up on subscriber */
};

static void cleanup_objects_atexit(void);
Expand Down Expand Up @@ -253,13 +253,13 @@ usage(void)
printf(_(" -n, --dry-run dry run, just show what would be done\n"));
printf(_(" -p, --subscriber-port=PORT subscriber port number (default %s)\n"), DEFAULT_SUB_PORT);
printf(_(" -P, --publisher-server=CONNSTR publisher connection string\n"));
printf(_(" -R, --remove=OBJECTTYPE remove all objects of the specified type from specified\n"
" databases on the subscriber; accepts: \"%s\"\n"), "publications");
printf(_(" -s, --socketdir=DIR socket directory to use (default current dir.)\n"));
printf(_(" -t, --recovery-timeout=SECS seconds to wait for recovery to end\n"));
printf(_(" -T, --enable-two-phase enable two-phase commit for all subscriptions\n"));
printf(_(" -U, --subscriber-username=NAME user name for subscriber connection\n"));
printf(_(" -v, --verbose output verbose messages\n"));
printf(_(" --clean=OBJECTTYPE drop all objects of the specified type from specified\n"
" databases on the subscriber; accepts: \"%s\"\n"), "publications");
printf(_(" --config-file=FILENAME use specified main server configuration\n"
" file when running target cluster\n"));
printf(_(" --publication=NAME publication name\n"));
Expand Down Expand Up @@ -1730,7 +1730,7 @@ static void
check_and_drop_publications(PGconn *conn, struct LogicalRepInfo *dbinfo)
{
PGresult *res;
bool drop_all_pubs = dbinfos.objecttypes_to_remove & OBJECTTYPE_PUBLICATIONS;
bool drop_all_pubs = dbinfos.objecttypes_to_clean & OBJECTTYPE_PUBLICATIONS;

Assert(conn != NULL);

Expand Down Expand Up @@ -2026,7 +2026,6 @@ main(int argc, char **argv)
{"dry-run", no_argument, NULL, 'n'},
{"subscriber-port", required_argument, NULL, 'p'},
{"publisher-server", required_argument, NULL, 'P'},
{"remove", required_argument, NULL, 'R'},
{"socketdir", required_argument, NULL, 's'},
{"recovery-timeout", required_argument, NULL, 't'},
{"enable-two-phase", no_argument, NULL, 'T'},
Expand All @@ -2038,6 +2037,7 @@ main(int argc, char **argv)
{"publication", required_argument, NULL, 2},
{"replication-slot", required_argument, NULL, 3},
{"subscription", required_argument, NULL, 4},
{"clean", required_argument, NULL, 5},
{NULL, 0, NULL, 0}
};

Expand Down Expand Up @@ -2109,7 +2109,7 @@ main(int argc, char **argv)

get_restricted_token();

while ((c = getopt_long(argc, argv, "ad:D:np:P:R:s:t:TU:v",
while ((c = getopt_long(argc, argv, "ad:D:np:P:s:t:TU:v",
long_options, &option_index)) != -1)
{
switch (c)
Expand Down Expand Up @@ -2139,12 +2139,6 @@ main(int argc, char **argv)
case 'P':
opt.pub_conninfo_str = pg_strdup(optarg);
break;
case 'R':
if (!simple_string_list_member(&opt.objecttypes_to_remove, optarg))
simple_string_list_append(&opt.objecttypes_to_remove, optarg);
else
pg_fatal("object type \"%s\" specified more than once for -R/--remove", optarg);
break;
case 's':
opt.socket_dir = pg_strdup(optarg);
canonicalize_path(opt.socket_dir);
Expand Down Expand Up @@ -2191,6 +2185,12 @@ main(int argc, char **argv)
else
pg_fatal("subscription \"%s\" specified more than once for --subscription", optarg);
break;
case 5:
if (!simple_string_list_member(&opt.objecttypes_to_clean, optarg))
simple_string_list_append(&opt.objecttypes_to_clean, optarg);
else
pg_fatal("object type \"%s\" specified more than once for --clean", optarg);
break;
default:
/* getopt_long already emitted a complaint */
pg_log_error_hint("Try \"%s --help\" for more information.", progname);
Expand Down Expand Up @@ -2334,13 +2334,13 @@ main(int argc, char **argv)
}

/* Verify the object types specified for removal from the subscriber */
for (SimpleStringListCell *cell = opt.objecttypes_to_remove.head; cell; cell = cell->next)
for (SimpleStringListCell *cell = opt.objecttypes_to_clean.head; cell; cell = cell->next)
{
if (pg_strcasecmp(cell->val, "publications") == 0)
dbinfos.objecttypes_to_remove |= OBJECTTYPE_PUBLICATIONS;
dbinfos.objecttypes_to_clean |= OBJECTTYPE_PUBLICATIONS;
else
{
pg_log_error("invalid object type \"%s\" specified for -R/--remove", cell->val);
pg_log_error("invalid object type \"%s\" specified for --clean", cell->val);
pg_log_error_hint("The valid value is: \"%s\"", "publications");
exit(1);
}
Expand Down
Loading