public inbox for [email protected]
help / color / mirror / Atom feedRe: ALTER TABLE: warn when actions do not recurse to partitions
19+ messages / 4 participants
[nested] [flat]
* Re: ALTER TABLE: warn when actions do not recurse to partitions
@ 2026-01-13 11:16 Jim Jones <[email protected]>
2026-01-14 00:52 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
0 siblings, 1 reply; 19+ messages in thread
From: Jim Jones @ 2026-01-13 11:16 UTC (permalink / raw)
To: Chao Li <[email protected]>; David G. Johnston <[email protected]>; +Cc: Greg Sabino Mullane <[email protected]>; Postgres hackers <[email protected]>
Hi Chao
On 13/01/2026 05:02, Chao Li wrote:
>
> PSA v3:
>
> * Rephrased the notice message as David's suggestion.
> * Removed partition count from notice message.
> * If a partitioned table doesn't have any partition, then suppress the
> message.
I've been playing with this patch, and it seems to work as expected -
I'm surprised it didn't break any existing tests :). Do you plan to
extend this patch to other subcommands mentioned in your initial post,
such as SET STATISTICS?
Thanks for the patch
Best, Jim
== tests ==
CREATE TABLE m (a int NOT NULL, b int) PARTITION BY RANGE (a);
CREATE TABLE m_p1 PARTITION OF m FOR VALUES FROM (1) TO (10);
CREATE TABLE m_p2 PARTITION OF m FOR VALUES FROM (10) TO (20);
CREATE UNIQUE INDEX m_idx ON m(a);
CREATE UNIQUE INDEX m_p1_idx ON m_p1(a);
CREATE UNIQUE INDEX m_p2_idx ON m_p2(a);
CREATE TABLE
CREATE TABLE
CREATE TABLE
CREATE INDEX
CREATE INDEX
CREATE INDEX
-- issue a NOTICE (m has partitions)
ALTER TABLE m REPLICA IDENTITY NOTHING;
ALTER TABLE m REPLICA IDENTITY FULL;
ALTER TABLE m REPLICA IDENTITY DEFAULT;
ALTER TABLE m REPLICA IDENTITY USING INDEX m_idx;
NOTICE: present partitions not affected
HINT: partitions may be modified individually using separate commands
ALTER TABLE
NOTICE: present partitions not affected
HINT: partitions may be modified individually using separate commands
ALTER TABLE
NOTICE: present partitions not affected
HINT: partitions may be modified individually using separate commands
ALTER TABLE
NOTICE: present partitions not affected
HINT: partitions may be modified individually using separate commands
ALTER TABLE
-- does not issue a NOTICE (with ONLY: no recursion into partitions)
ALTER TABLE ONLY m REPLICA IDENTITY NOTHING;
ALTER TABLE ONLY m REPLICA IDENTITY FULL;
ALTER TABLE ONLY m REPLICA IDENTITY DEFAULT;
ALTER TABLE ONLY m REPLICA IDENTITY USING INDEX m_idx;
ALTER TABLE
ALTER TABLE
ALTER TABLE
ALTER TABLE
-- does not issue a NOTICE (p1 has no partitions)
ALTER TABLE m_p1 REPLICA IDENTITY NOTHING;
ALTER TABLE m_p1 REPLICA IDENTITY FULL;
ALTER TABLE m_p1 REPLICA IDENTITY DEFAULT;
ALTER TABLE m_p1 REPLICA IDENTITY USING INDEX m_p1_idx;
ALTER TABLE
ALTER TABLE
ALTER TABLE
ALTER TABLE
-- does not issue a NOTICE (m no longer has partitions)
DROP TABLE m_p1, m_p2;
DROP TABLE
ALTER TABLE m REPLICA IDENTITY NOTHING;
ALTER TABLE m REPLICA IDENTITY FULL;
ALTER TABLE m REPLICA IDENTITY DEFAULT;
ALTER TABLE m REPLICA IDENTITY USING INDEX m_idx;
ALTER TABLE
ALTER TABLE
ALTER TABLE
ALTER TABLE
^ permalink raw reply [nested|flat] 19+ messages in thread
* Re: ALTER TABLE: warn when actions do not recurse to partitions
2026-01-13 11:16 Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
@ 2026-01-14 00:52 ` Chao Li <[email protected]>
2026-01-22 05:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
0 siblings, 1 reply; 19+ messages in thread
From: Chao Li @ 2026-01-14 00:52 UTC (permalink / raw)
To: Jim Jones <[email protected]>; +Cc: David G. Johnston <[email protected]>; Greg Sabino Mullane <[email protected]>; Postgres hackers <[email protected]>
> On Jan 13, 2026, at 19:16, Jim Jones <[email protected]> wrote:
>
> Hi Chao
>
> On 13/01/2026 05:02, Chao Li wrote:
>>
>> PSA v3:
>>
>> * Rephrased the notice message as David's suggestion.
>> * Removed partition count from notice message.
>> * If a partitioned table doesn't have any partition, then suppress the
>> message.
>
> I've been playing with this patch, and it seems to work as expected -
> I'm surprised it didn't break any existing tests :). Do you plan to
> extend this patch to other subcommands mentioned in your initial post,
> such as SET STATISTICS?
>
> Thanks for the patch
>
> Best, Jim
>
Hi Jim,
Thanks for your testing. Yes, I plan to add the notice to other sub-commands as needed. I only updated REPLICA IDENTITY first to call for feedback. As you see, David has suggested the great wording for the notice message. Once the change on REPLICA IDENTITY is reviewed, it’s easy to extend to other sub-commands.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
^ permalink raw reply [nested|flat] 19+ messages in thread
* Re: ALTER TABLE: warn when actions do not recurse to partitions
2026-01-13 11:16 Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-14 00:52 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
@ 2026-01-22 05:45 ` Chao Li <[email protected]>
2026-01-22 19:27 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
0 siblings, 1 reply; 19+ messages in thread
From: Chao Li @ 2026-01-22 05:45 UTC (permalink / raw)
To: Jim Jones <[email protected]>; +Cc: David G. Johnston <[email protected]>; Greg Sabino Mullane <[email protected]>; Postgres hackers <[email protected]>
On Jan 14, 2026, at 08:52, Chao Li <[email protected]> wrote:
On Jan 13, 2026, at 19:16, Jim Jones <[email protected]> wrote:
Hi Chao
On 13/01/2026 05:02, Chao Li wrote:
PSA v3:
* Rephrased the notice message as David's suggestion.
* Removed partition count from notice message.
* If a partitioned table doesn't have any partition, then suppress the
message.
I've been playing with this patch, and it seems to work as expected -
I'm surprised it didn't break any existing tests :). Do you plan to
extend this patch to other subcommands mentioned in your initial post,
such as SET STATISTICS?
Thanks for the patch
Best, Jim
Hi Jim,
Thanks for your testing. Yes, I plan to add the notice to other
sub-commands as needed. I only updated REPLICA IDENTITY first to call for
feedback. As you see, David has suggested the great wording for the notice
message. Once the change on REPLICA IDENTITY is reviewed, it’s easy to
extend to other sub-commands.
PFA v4.
I’ve extended the NOTICE to cover all sub-commands for which ONLY has no
effect and where actions on a partitioned table do not propagate to its
partitions:
- ALTER COLUMN SET/RESET attribute_option
- ALTER COLUMN SET COMPRESSION
- ENABLE/DISABLE RULE
- ENABLE/DISABLE/FORCE/NO FORCE ROW LEVEL SECURITY
- REPLICA IDENTITY
- OWNER TO
- SET TABLESPACE
- SET SCHEMA
RENAME is intentionally excluded. Using ONLY (or not) has no effect
for RENAME, since relation names are independent by nature and there is no
expectation of recursion.
OF / NOT OF are also excluded. Using ONLY has no effect for these commands,
as they apply only to the partitioned table itself and not to its
partitions.
One thing worth noting: following David’s suggestion, I removed the action
name from the NOTICE message in v2. However, I later realized that we do
need to include the action name, because an ALTER TABLE command may contain
multiple sub-commands, and the NOTICE would otherwise be ambiguous.
In v4, I reuse alter_table_type_to_string() to construct the action name,
consistent with what ATSimplePermissions() does. The NOTICE message itself
also follows the same style as messages emitted by ATSimplePermissions().
For example, when an ALTER TABLE contains multiple sub-commands, the output
now looks like:
```
evantest=# alter table p_test replica identity full, alter column username
set (n_distinct = 0.1);
NOTICE: ALTER action REPLICA IDENTITY on relation "p_test" does not affect
present partitions
HINT: partitions may be modified individually, or specify ONLY to suppress
this message
NOTICE: ALTER action ALTER COLUMN ... SET on relation "p_test" does not
affect present partitions
HINT: partitions may be modified individually, or specify ONLY to suppress
this message
ALTER TABLE
```
Regression tests have been updated, and a few new tests have been added. v4
should now be ready for review.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
Attachments:
[application/octet-stream] v4-0001-ALTER-TABLE-emit-NOTICE-when-ONLY-is-omitted-but-.patch (32.9K, 3-v4-0001-ALTER-TABLE-emit-NOTICE-when-ONLY-is-omitted-but-.patch)
download | inline diff:
From 3b89bafbe5101eecec84a79b87344ce5bdc338a2 Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <[email protected]>
Date: Mon, 12 Jan 2026 16:56:58 +0800
Subject: [PATCH v4] ALTER TABLE: emit NOTICE when ONLY is omitted but no
partition recursion occurs
Some ALTER TABLE sub-commands do not recurse to partitions even when ONLY
is not specified on a partitioned table. This can be surprising, since
the default expectation is that commands propagate to all partitions.
Emit a NOTICE in such cases to make the behavior explicit, and advise how
to suppress the message or modify partitions individually.
Affected sub-commands include:
- ALTER COLUMN SET/RESET attribute_option
- ALTER COLUMN SET COMPRESSION
- ENABLE/DISABLE RULE
- ENABLE/DISABLE/FORCE/NO FORCE ROW LEVEL SECURITY
- REPLICA IDENTITY
- OWNER TO
- SET TABLESPACE
- SET SCHEMA
RENAME is intentionally excluded. Using ONLY (or not) has no effect for
RENAME, since relation names are independent by nature and there is no
expectation of recursion.
OF / NOT OF are also excluded. Using ONLY has no effect for these
commands, as they apply only to the partitioned table itself and not to
its partitions.
Regression tests are updated to use ONLY where appropriate.
Author: Chao Li <[email protected]>
Reviewed-by: David G. Johnston <[email protected]>
Reviewed-by: Greg Sabino Mullane <[email protected]>
Discussion: https://postgr.es/m/CAEoWx2=SLga-xH09Cq_PAvsHhQHrBK+V0vF821JKgzS=Bm0haA@mail.gmail.com
---
src/backend/commands/tablecmds.c | 72 +++++++++++++++++--
src/include/nodes/parsenodes.h | 1 +
src/test/regress/expected/alter_table.out | 68 +++++++++++++++++-
src/test/regress/expected/cluster.out | 2 +-
src/test/regress/expected/merge.out | 4 +-
src/test/regress/expected/partition_merge.out | 2 +-
src/test/regress/expected/partition_split.out | 2 +-
src/test/regress/expected/privileges.out | 2 +-
src/test/regress/expected/rowsecurity.out | 4 +-
src/test/regress/expected/tablespace.out | 4 +-
src/test/regress/expected/update.out | 2 +-
src/test/regress/expected/vacuum.out | 6 +-
src/test/regress/sql/alter_table.sql | 51 ++++++++++++-
src/test/regress/sql/cluster.sql | 2 +-
src/test/regress/sql/merge.sql | 4 +-
src/test/regress/sql/partition_merge.sql | 2 +-
src/test/regress/sql/partition_split.sql | 2 +-
src/test/regress/sql/privileges.sql | 2 +-
src/test/regress/sql/rowsecurity.sql | 4 +-
src/test/regress/sql/tablespace.sql | 4 +-
src/test/regress/sql/update.sql | 2 +-
src/test/regress/sql/vacuum.sql | 6 +-
22 files changed, 212 insertions(+), 36 deletions(-)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index f976c0e5c7e..ab8849406da 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -745,6 +745,7 @@ static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation
static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
Relation rel, PartitionCmd *cmd,
AlterTableUtilityContext *context);
+static void EmitPartitionNoRecurseNotice(AlterTableType cmdtype, Relation rel, bool recurse, bool recursing);
/* ----------------------------------------------------------------
* DefineRelation
@@ -5056,6 +5057,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATT_MATVIEW | ATT_FOREIGN_TABLE);
/* This command never recurses */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ EmitPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing);
break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
ATSimplePermissions(cmd->subtype, rel,
@@ -5071,6 +5074,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ EmitPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing);
break;
case AT_DropColumn: /* DROP COLUMN */
ATSimplePermissions(cmd->subtype, rel,
@@ -5138,6 +5143,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ EmitPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing);
break;
case AT_ClusterOn: /* CLUSTER ON */
case AT_DropCluster: /* SET WITHOUT CLUSTER */
@@ -5181,6 +5188,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
pass = AT_PASS_MISC; /* doesn't actually matter */
+ /* Emit a notice if needed */
+ EmitPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing);
break;
case AT_SetRelOptions: /* SET (...) */
case AT_ResetRelOptions: /* RESET (...) */
@@ -5190,6 +5199,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATT_MATVIEW | ATT_INDEX);
/* This command never recurses */
/* No command-specific prep needed */
+ /* It will check for partitioned table at exec time */
pass = AT_PASS_MISC;
break;
case AT_AddInherit: /* INHERIT */
@@ -5227,8 +5237,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATSimplePermissions(cmd->subtype, rel,
ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
pass = AT_PASS_MISC;
- /* This command never recurses */
- /* No command-specific prep needed */
+ /* This command doesn't recurse to partitions, so notice if needed */
+ EmitPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing);
break;
case AT_EnableTrig: /* ENABLE TRIGGER variants */
case AT_EnableAlwaysTrig:
@@ -5249,17 +5259,30 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
case AT_EnableAlwaysRule:
case AT_EnableReplicaRule:
case AT_DisableRule:
- case AT_AddOf: /* OF */
- case AT_DropOf: /* NOT OF */
- case AT_EnableRowSecurity:
+ case AT_EnableRowSecurity: /* ENABLE/DISABLE ROW SECURITY variants */
case AT_DisableRowSecurity:
- case AT_ForceRowSecurity:
+ case AT_ForceRowSecurity: /* FORCE/NO FORCE ROW SECURITY variants */
case AT_NoForceRowSecurity:
ATSimplePermissions(cmd->subtype, rel,
ATT_TABLE | ATT_PARTITIONED_TABLE);
/* These commands never recurse */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ EmitPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing);
+ break;
+ case AT_AddOf: /* OF */
+ case AT_DropOf: /* NOT OF */
+ ATSimplePermissions(cmd->subtype, rel,
+ ATT_TABLE | ATT_PARTITIONED_TABLE);
+ /* These commands never recurse */
+ /* No command-specific prep needed */
+
+ /*
+ * They only work on partitioned tables but child partitions, thus
+ * no need to emit a notice
+ */
+ pass = AT_PASS_MISC;
break;
case AT_GenericOptions:
ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
@@ -6752,6 +6775,8 @@ alter_table_type_to_string(AlterTableType cmdtype)
return "ALTER COLUMN ... DROP IDENTITY";
case AT_ReAddStatistics:
return NULL; /* not real grammar */
+ case AT_SetSchema:
+ return "SET SCHEMA";
}
return NULL;
@@ -18996,6 +19021,13 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
rel = relation_open(relid, NoLock);
+ /*
+ * SET SCHEMA doesn't recurse to children, emit a notice if ONLY is not
+ * specified. As this action doesn't go through ATPrepCmd, we have to emit
+ * the notice here.
+ */
+ EmitPartitionNoRecurseNotice(AT_SetSchema, rel, stmt->relation->inh, false);
+
oldNspOid = RelationGetNamespace(rel);
/* If it's an owned sequence, disallow moving it by itself. */
@@ -23340,3 +23372,31 @@ ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
/* Restore the userid and security context. */
SetUserIdAndSecContext(save_userid, save_sec_context);
}
+
+/*
+ * When ONLY is not specified with a partitioned table, it is expected that the
+ * command recurses to all partitions. However, some sub-commands do not recurse.
+ * In such cases, emit a NOTICE to make this behavior explicit to the user.
+ */
+static void
+EmitPartitionNoRecurseNotice(AlterTableType cmdtype, Relation rel, bool recurse, bool recursing)
+{
+ /* Only emit the notice at the top level of recursion */
+ if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && recurse && !recursing)
+ {
+ PartitionDesc pd = RelationGetPartitionDesc(rel, true);
+ int nparts = pd->nparts;
+ const char *action_str;
+
+ /* Emit a notice only if there are partitions */
+ if (nparts == 0)
+ return;
+
+ action_str = alter_table_type_to_string(cmdtype);
+ ereport(NOTICE,
+ errmsg("ALTER action %s on relation \"%s\" does not affect present partitions",
+ action_str,
+ RelationGetRelationName(rel)),
+ errhint("partitions may be modified individually, or specify ONLY to suppress this message"));
+ }
+}
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 646d6ced763..c61cc3c0eaf 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2509,6 +2509,7 @@ typedef enum AlterTableType
AT_SetIdentity, /* SET identity column options */
AT_DropIdentity, /* DROP IDENTITY */
AT_ReAddStatistics, /* internal to commands/tablecmds.c */
+ AT_SetSchema, /* SET SCHEMA */
} AlterTableType;
typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index ac1a7345d0f..916761f4d0f 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -4572,8 +4572,74 @@ SELECT * FROM list_parted;
---
(0 rows)
+CREATE TABLE list_parted4 (a int, b text) PARTITION BY LIST (a);
+CREATE TABLE list_parted4_1 PARTITION OF list_parted4 FOR VALUES IN (1);
+-- set column attribute on partitioned table should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+NOTICE: ALTER action ALTER COLUMN ... SET on relation "list_parted4" does not affect present partitions
+HINT: partitions may be modified individually, or specify ONLY to suppress this message
+ALTER TABLE list_parted4 ALTER COLUMN b RESET (n_distinct);
+NOTICE: ALTER action ALTER COLUMN ... RESET on relation "list_parted4" does not affect present partitions
+HINT: partitions may be modified individually, or specify ONLY to suppress this message
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b RESET (n_distinct);
+-- enable/disable rules on partitioned tables should get a notice
+CREATE RULE list_parted4_rule AS ON INSERT TO list_parted4 DO INSTEAD NOTHING;
+ALTER TABLE list_parted4 DISABLE RULE list_parted4_rule;
+NOTICE: ALTER action DISABLE RULE on relation "list_parted4" does not affect present partitions
+HINT: partitions may be modified individually, or specify ONLY to suppress this message
+ALTER TABLE ONLY list_parted4 DISABLE RULE list_parted4_rule;
+ALTER TABLE list_parted4 ENABLE RULE list_parted4_rule;
+NOTICE: ALTER action ENABLE RULE on relation "list_parted4" does not affect present partitions
+HINT: partitions may be modified individually, or specify ONLY to suppress this message
+ALTER TABLE ONLY list_parted4 ENABLE RULE list_parted4_rule;
+DROP RULE list_parted4_rule ON list_parted4;
+-- enable/disable row level security on partitioned tables should get a notice
+ALTER TABLE list_parted4 ENABLE ROW LEVEL SECURITY;
+NOTICE: ALTER action ENABLE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: partitions may be modified individually, or specify ONLY to suppress this message
+ALTER TABLE ONLY list_parted4 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 DISABLE ROW LEVEL SECURITY;
+NOTICE: ALTER action DISABLE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: partitions may be modified individually, or specify ONLY to suppress this message
+ALTER TABLE ONLY list_parted4 DISABLE ROW LEVEL SECURITY;
+-- force/no force row level security on partitioned tables should get a notice
+ALTER TABLE list_parted4 FORCE ROW LEVEL SECURITY;
+NOTICE: ALTER action FORCE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: partitions may be modified individually, or specify ONLY to suppress this message
+ALTER TABLE ONLY list_parted4 FORCE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 NO FORCE ROW LEVEL SECURITY;
+NOTICE: ALTER action NO FORCE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: partitions may be modified individually, or specify ONLY to suppress this message
+ALTER TABLE ONLY list_parted4 NO FORCE ROW LEVEL SECURITY;
+-- set replica identity on partitioned tables should get a notice
+ALTER TABLE list_parted4 REPLICA IDENTITY FULL;
+NOTICE: ALTER action REPLICA IDENTITY on relation "list_parted4" does not affect present partitions
+HINT: partitions may be modified individually, or specify ONLY to suppress this message
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY FULL;
+ALTER TABLE list_parted4 REPLICA IDENTITY NOTHING;
+NOTICE: ALTER action REPLICA IDENTITY on relation "list_parted4" does not affect present partitions
+HINT: partitions may be modified individually, or specify ONLY to suppress this message
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY NOTHING;
+-- set compression on partitioned tables should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+NOTICE: ALTER action ALTER COLUMN ... SET COMPRESSION on relation "list_parted4" does not affect present partitions
+HINT: partitions may be modified individually, or specify ONLY to suppress this message
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+-- set owner on partitioned tables should get a notice
+ALTER TABLE list_parted4 OWNER TO regress_alter_table_user1;
+NOTICE: ALTER action OWNER TO on relation "list_parted4" does not affect present partitions
+HINT: partitions may be modified individually, or specify ONLY to suppress this message
+ALTER TABLE ONLY list_parted4 OWNER TO regress_alter_table_user1;
+-- set schema on partitioned tables should get a notice
+CREATE SCHEMA alter_table_test_schema;
+ALTER TABLE list_parted4 SET SCHEMA alter_table_test_schema;
+NOTICE: ALTER action SET SCHEMA on relation "list_parted4" does not affect present partitions
+HINT: partitions may be modified individually, or specify ONLY to suppress this message
+ALTER TABLE ONLY alter_table_test_schema.list_parted4 SET SCHEMA public;
+DROP SCHEMA alter_table_test_schema CASCADE;
-- cleanup
-DROP TABLE list_parted, list_parted2, range_parted, list_parted3;
+DROP TABLE list_parted, list_parted2, range_parted, list_parted3, list_parted4;
DROP TABLE fail_def_part;
DROP TABLE hash_parted;
-- more tests for certain multi-level partitioning scenarios
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 4d40a6809ab..6ea8c99d092 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -507,7 +507,7 @@ SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
RESET SESSION AUTHORIZATION;
-ALTER TABLE ptnowner OWNER TO regress_ptnowner;
+ALTER TABLE ONLY ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
SELECT oid, relname, relfilenode FROM pg_partition_tree('ptnowner') AS tree
JOIN pg_class AS c ON c.oid=tree.relid;
diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out
index 9cb1d87066a..bd6aac72336 100644
--- a/src/test/regress/expected/merge.out
+++ b/src/test/regress/expected/merge.out
@@ -2298,8 +2298,8 @@ SELECT * FROM pa_target ORDER BY tid, val;
ROLLBACK;
-- test RLS enforcement
BEGIN;
-ALTER TABLE pa_target ENABLE ROW LEVEL SECURITY;
-ALTER TABLE pa_target FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target FORCE ROW LEVEL SECURITY;
CREATE POLICY pa_target_pol ON pa_target USING (tid != 0);
MERGE INTO pa_target t
USING pa_source s
diff --git a/src/test/regress/expected/partition_merge.out b/src/test/regress/expected/partition_merge.out
index 925fe4f570a..f629fc7f3e5 100644
--- a/src/test/regress/expected/partition_merge.out
+++ b/src/test/regress/expected/partition_merge.out
@@ -810,7 +810,7 @@ SET SESSION AUTHORIZATION regress_partition_merge_bob;
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
ERROR: must be owner of table t
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_merge_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_merge_bob;
SET SESSION AUTHORIZATION regress_partition_merge_bob;
-- ERROR: must be owner of table tp_0_1
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
diff --git a/src/test/regress/expected/partition_split.out b/src/test/regress/expected/partition_split.out
index 4004efe0dac..421fa36ec0f 100644
--- a/src/test/regress/expected/partition_split.out
+++ b/src/test/regress/expected/partition_split.out
@@ -1372,7 +1372,7 @@ ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); --error
ERROR: must be owner of table t
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_split_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_split_bob;
SET SESSION AUTHORIZATION regress_partition_split_bob;
ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
(PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index daafaa94fde..8687e50fd50 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -1882,7 +1882,7 @@ SELECT brin_summarize_range('sro_brin', 0);
DROP TABLE sro_tab;
-- Check with a partitioned table
CREATE TABLE sro_ptab (a int) PARTITION BY RANGE (a);
-ALTER TABLE sro_ptab OWNER TO regress_sro_user;
+ALTER TABLE ONLY sro_ptab OWNER TO regress_sro_user;
CREATE TABLE sro_part PARTITION OF sro_ptab FOR VALUES FROM (1) TO (10);
ALTER TABLE sro_part OWNER TO regress_sro_user;
INSERT INTO sro_ptab VALUES (1), (2), (3);
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index c958ef4d70a..d751de84920 100644
--- a/src/test/regress/expected/rowsecurity.out
+++ b/src/test/regress/expected/rowsecurity.out
@@ -1188,7 +1188,7 @@ INSERT INTO part_document VALUES
( 8, 55, 2, 'regress_rls_carol', 'great satire'),
( 9, 11, 1, 'regress_rls_dave', 'awesome science fiction'),
(10, 99, 2, 'regress_rls_dave', 'awesome technology book');
-ALTER TABLE part_document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY part_document ENABLE ROW LEVEL SECURITY;
-- Create policy on parent
-- user's security level must be higher than or equal to document's
CREATE POLICY pp1 ON part_document AS PERMISSIVE
@@ -4821,7 +4821,7 @@ CREATE TABLE rls_ptbl (a int) PARTITION BY RANGE (a);
CREATE TABLE rls_part PARTITION OF rls_ptbl FOR VALUES FROM (-100) TO (100);
INSERT INTO rls_ptbl SELECT x/10 FROM generate_series(1, 100) x;
ANALYZE rls_ptbl, rls_part;
-ALTER TABLE rls_ptbl ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY rls_ptbl ENABLE ROW LEVEL SECURITY;
ALTER TABLE rls_part ENABLE ROW LEVEL SECURITY;
GRANT SELECT ON rls_ptbl TO regress_rls_alice;
GRANT SELECT ON rls_part TO regress_rls_alice;
diff --git a/src/test/regress/expected/tablespace.out b/src/test/regress/expected/tablespace.out
index a90e39e5738..eef9de7d9ca 100644
--- a/src/test/regress/expected/tablespace.out
+++ b/src/test/regress/expected/tablespace.out
@@ -290,13 +290,13 @@ CREATE TABLE testschema.part_2 PARTITION OF testschema.part FOR VALUES IN (2);
SET default_tablespace TO pg_global;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
ERROR: only shared relations can be placed in pg_global tablespace
-ALTER TABLE testschema.part SET TABLESPACE regress_tblspace;
+ALTER TABLE ONLY testschema.part SET TABLESPACE regress_tblspace;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
CREATE TABLE testschema.part_4 PARTITION OF testschema.part FOR VALUES IN (4)
TABLESPACE pg_default;
CREATE TABLE testschema.part_56 PARTITION OF testschema.part FOR VALUES IN (5, 6)
PARTITION BY LIST (a);
-ALTER TABLE testschema.part SET TABLESPACE pg_default;
+ALTER TABLE ONLY testschema.part SET TABLESPACE pg_default;
CREATE TABLE testschema.part_78 PARTITION OF testschema.part FOR VALUES IN (7, 8)
PARTITION BY LIST (a);
ERROR: only shared relations can be placed in pg_global tablespace
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index eef2bac1cbf..584b40598eb 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -607,7 +607,7 @@ DROP TRIGGER trig_d15_20 ON part_d_15_20;
DROP FUNCTION func_parted_mod_b();
-- RLS policies with update-row-movement
-----------------------------------------
-ALTER TABLE range_parted ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY range_parted ENABLE ROW LEVEL SECURITY;
CREATE USER regress_range_parted_user;
GRANT ALL ON range_parted, mintab TO regress_range_parted_user;
CREATE POLICY seeall ON range_parted AS PERMISSIVE FOR SELECT USING (true);
diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out
index d4696bc3325..f22bb2127e1 100644
--- a/src/test/regress/expected/vacuum.out
+++ b/src/test/regress/expected/vacuum.out
@@ -614,7 +614,7 @@ VACUUM (ANALYZE) vacowned_part2;
WARNING: permission denied to vacuum "vacowned_part2", skipping it
RESET ROLE;
-- Partitioned table and one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO regress_vacuum;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
@@ -634,7 +634,7 @@ VACUUM (ANALYZE) vacowned_part2;
WARNING: permission denied to vacuum "vacowned_part2", skipping it
RESET ROLE;
-- Only one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO CURRENT_USER;
+ALTER TABLE ONLY vacowned_parted OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
WARNING: permission denied to vacuum "vacowned_parted", skipping it
@@ -656,7 +656,7 @@ VACUUM (ANALYZE) vacowned_part2;
WARNING: permission denied to vacuum "vacowned_part2", skipping it
RESET ROLE;
-- Only partitioned table owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 417202430a5..cea18d27ae3 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2900,8 +2900,57 @@ ALTER TABLE list_parted2 ALTER COLUMN b TYPE text;
ALTER TABLE list_parted DROP COLUMN b;
SELECT * FROM list_parted;
+CREATE TABLE list_parted4 (a int, b text) PARTITION BY LIST (a);
+CREATE TABLE list_parted4_1 PARTITION OF list_parted4 FOR VALUES IN (1);
+
+-- set column attribute on partitioned table should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+ALTER TABLE list_parted4 ALTER COLUMN b RESET (n_distinct);
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b RESET (n_distinct);
+
+-- enable/disable rules on partitioned tables should get a notice
+CREATE RULE list_parted4_rule AS ON INSERT TO list_parted4 DO INSTEAD NOTHING;
+ALTER TABLE list_parted4 DISABLE RULE list_parted4_rule;
+ALTER TABLE ONLY list_parted4 DISABLE RULE list_parted4_rule;
+ALTER TABLE list_parted4 ENABLE RULE list_parted4_rule;
+ALTER TABLE ONLY list_parted4 ENABLE RULE list_parted4_rule;
+DROP RULE list_parted4_rule ON list_parted4;
+
+-- enable/disable row level security on partitioned tables should get a notice
+ALTER TABLE list_parted4 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 DISABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 DISABLE ROW LEVEL SECURITY;
+
+-- force/no force row level security on partitioned tables should get a notice
+ALTER TABLE list_parted4 FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 FORCE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 NO FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 NO FORCE ROW LEVEL SECURITY;
+
+-- set replica identity on partitioned tables should get a notice
+ALTER TABLE list_parted4 REPLICA IDENTITY FULL;
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY FULL;
+ALTER TABLE list_parted4 REPLICA IDENTITY NOTHING;
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY NOTHING;
+
+-- set compression on partitioned tables should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+
+-- set owner on partitioned tables should get a notice
+ALTER TABLE list_parted4 OWNER TO regress_alter_table_user1;
+ALTER TABLE ONLY list_parted4 OWNER TO regress_alter_table_user1;
+
+-- set schema on partitioned tables should get a notice
+CREATE SCHEMA alter_table_test_schema;
+ALTER TABLE list_parted4 SET SCHEMA alter_table_test_schema;
+ALTER TABLE ONLY alter_table_test_schema.list_parted4 SET SCHEMA public;
+DROP SCHEMA alter_table_test_schema CASCADE;
+
-- cleanup
-DROP TABLE list_parted, list_parted2, range_parted, list_parted3;
+DROP TABLE list_parted, list_parted2, range_parted, list_parted3, list_parted4;
DROP TABLE fail_def_part;
DROP TABLE hash_parted;
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index b7115f86104..06cdb311b97 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -241,7 +241,7 @@ ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
RESET SESSION AUTHORIZATION;
-ALTER TABLE ptnowner OWNER TO regress_ptnowner;
+ALTER TABLE ONLY ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
SELECT oid, relname, relfilenode FROM pg_partition_tree('ptnowner') AS tree
JOIN pg_class AS c ON c.oid=tree.relid;
diff --git a/src/test/regress/sql/merge.sql b/src/test/regress/sql/merge.sql
index 2660b19f238..2bffcf7aa98 100644
--- a/src/test/regress/sql/merge.sql
+++ b/src/test/regress/sql/merge.sql
@@ -1422,8 +1422,8 @@ ROLLBACK;
-- test RLS enforcement
BEGIN;
-ALTER TABLE pa_target ENABLE ROW LEVEL SECURITY;
-ALTER TABLE pa_target FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target FORCE ROW LEVEL SECURITY;
CREATE POLICY pa_target_pol ON pa_target USING (tid != 0);
MERGE INTO pa_target t
USING pa_source s
diff --git a/src/test/regress/sql/partition_merge.sql b/src/test/regress/sql/partition_merge.sql
index a211fee2ad1..73bffb530dd 100644
--- a/src/test/regress/sql/partition_merge.sql
+++ b/src/test/regress/sql/partition_merge.sql
@@ -571,7 +571,7 @@ SET SESSION AUTHORIZATION regress_partition_merge_bob;
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_merge_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_merge_bob;
SET SESSION AUTHORIZATION regress_partition_merge_bob;
-- ERROR: must be owner of table tp_0_1
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
diff --git a/src/test/regress/sql/partition_split.sql b/src/test/regress/sql/partition_split.sql
index 37c6d730840..f4f9c7886e4 100644
--- a/src/test/regress/sql/partition_split.sql
+++ b/src/test/regress/sql/partition_split.sql
@@ -960,7 +960,7 @@ ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); --error
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_split_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_split_bob;
SET SESSION AUTHORIZATION regress_partition_split_bob;
ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
(PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index 96eff1104d2..55f043fd049 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -1212,7 +1212,7 @@ SELECT brin_summarize_range('sro_brin', 0);
DROP TABLE sro_tab;
-- Check with a partitioned table
CREATE TABLE sro_ptab (a int) PARTITION BY RANGE (a);
-ALTER TABLE sro_ptab OWNER TO regress_sro_user;
+ALTER TABLE ONLY sro_ptab OWNER TO regress_sro_user;
CREATE TABLE sro_part PARTITION OF sro_ptab FOR VALUES FROM (1) TO (10);
ALTER TABLE sro_part OWNER TO regress_sro_user;
INSERT INTO sro_ptab VALUES (1), (2), (3);
diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql
index 5d923c5ca3b..0d70d260dfd 100644
--- a/src/test/regress/sql/rowsecurity.sql
+++ b/src/test/regress/sql/rowsecurity.sql
@@ -483,7 +483,7 @@ INSERT INTO part_document VALUES
( 9, 11, 1, 'regress_rls_dave', 'awesome science fiction'),
(10, 99, 2, 'regress_rls_dave', 'awesome technology book');
-ALTER TABLE part_document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY part_document ENABLE ROW LEVEL SECURITY;
-- Create policy on parent
-- user's security level must be higher than or equal to document's
@@ -2358,7 +2358,7 @@ CREATE TABLE rls_part PARTITION OF rls_ptbl FOR VALUES FROM (-100) TO (100);
INSERT INTO rls_ptbl SELECT x/10 FROM generate_series(1, 100) x;
ANALYZE rls_ptbl, rls_part;
-ALTER TABLE rls_ptbl ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY rls_ptbl ENABLE ROW LEVEL SECURITY;
ALTER TABLE rls_part ENABLE ROW LEVEL SECURITY;
GRANT SELECT ON rls_ptbl TO regress_rls_alice;
GRANT SELECT ON rls_part TO regress_rls_alice;
diff --git a/src/test/regress/sql/tablespace.sql b/src/test/regress/sql/tablespace.sql
index dfe3db096e2..c86ec261ed5 100644
--- a/src/test/regress/sql/tablespace.sql
+++ b/src/test/regress/sql/tablespace.sql
@@ -195,13 +195,13 @@ SET default_tablespace TO regress_tblspace;
CREATE TABLE testschema.part_2 PARTITION OF testschema.part FOR VALUES IN (2);
SET default_tablespace TO pg_global;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
-ALTER TABLE testschema.part SET TABLESPACE regress_tblspace;
+ALTER TABLE ONLY testschema.part SET TABLESPACE regress_tblspace;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
CREATE TABLE testschema.part_4 PARTITION OF testschema.part FOR VALUES IN (4)
TABLESPACE pg_default;
CREATE TABLE testschema.part_56 PARTITION OF testschema.part FOR VALUES IN (5, 6)
PARTITION BY LIST (a);
-ALTER TABLE testschema.part SET TABLESPACE pg_default;
+ALTER TABLE ONLY testschema.part SET TABLESPACE pg_default;
CREATE TABLE testschema.part_78 PARTITION OF testschema.part FOR VALUES IN (7, 8)
PARTITION BY LIST (a);
CREATE TABLE testschema.part_910 PARTITION OF testschema.part FOR VALUES IN (9, 10)
diff --git a/src/test/regress/sql/update.sql b/src/test/regress/sql/update.sql
index 8b4707eb9c3..56b1edb00ce 100644
--- a/src/test/regress/sql/update.sql
+++ b/src/test/regress/sql/update.sql
@@ -341,7 +341,7 @@ DROP FUNCTION func_parted_mod_b();
-- RLS policies with update-row-movement
-----------------------------------------
-ALTER TABLE range_parted ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY range_parted ENABLE ROW LEVEL SECURITY;
CREATE USER regress_range_parted_user;
GRANT ALL ON range_parted, mintab TO regress_range_parted_user;
CREATE POLICY seeall ON range_parted AS PERMISSIVE FOR SELECT USING (true);
diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql
index 247b8e23b23..acff5824ebb 100644
--- a/src/test/regress/sql/vacuum.sql
+++ b/src/test/regress/sql/vacuum.sql
@@ -452,7 +452,7 @@ VACUUM (ANALYZE) vacowned_part1;
VACUUM (ANALYZE) vacowned_part2;
RESET ROLE;
-- Partitioned table and one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO regress_vacuum;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
@@ -466,7 +466,7 @@ VACUUM (ANALYZE) vacowned_part1;
VACUUM (ANALYZE) vacowned_part2;
RESET ROLE;
-- Only one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO CURRENT_USER;
+ALTER TABLE ONLY vacowned_parted OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
VACUUM vacowned_part1;
@@ -479,7 +479,7 @@ VACUUM (ANALYZE) vacowned_part1;
VACUUM (ANALYZE) vacowned_part2;
RESET ROLE;
-- Only partitioned table owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
--
2.50.1 (Apple Git-155)
^ permalink raw reply [nested|flat] 19+ messages in thread
* Re: ALTER TABLE: warn when actions do not recurse to partitions
2026-01-13 11:16 Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-14 00:52 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 05:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
@ 2026-01-22 19:27 ` Jim Jones <[email protected]>
2026-01-23 00:11 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
0 siblings, 1 reply; 19+ messages in thread
From: Jim Jones @ 2026-01-22 19:27 UTC (permalink / raw)
To: Chao Li <[email protected]>; +Cc: David G. Johnston <[email protected]>; Greg Sabino Mullane <[email protected]>; Postgres hackers <[email protected]>
Hi Chao
On 22/01/2026 06:45, Chao Li wrote:
> evantest=# alter table p_test replica identity full, alter column
> username set (n_distinct = 0.1);
> NOTICE: ALTER action REPLICA IDENTITY on relation "p_test" does not
> affect present partitions
> HINT: partitions may be modified individually, or specify ONLY to
> suppress this message
> NOTICE: ALTER action ALTER COLUMN ... SET on relation "p_test" does not
> affect present partitions
> HINT: partitions may be modified individually, or specify ONLY to
> suppress this message
> ALTER TABLE
One could argue that encapsulating all conditions in
EmitPartitionNoRecurseNotice(), meaning it is called all the time, is
slightly inefficient, but the impact is really negligible in this case -
and it is how it is done in similar functions in tablecmds.c :) The code
LGTM.
One small thing:
errhint is supposed to be capitalised - see Error Message Style Guide[1]
"Detail and hint messages: Use complete sentences, and end each with a
period. Capitalize the first word of sentences. Put two spaces after the
period if another sentence follows (for English text; might be
inappropriate in other languages)."
ereport(NOTICE,
errmsg("ALTER action %s on relation \"%s\" does not affect present
partitions",
action_str,
RelationGetRelationName(rel)),
errhint("partitions may be modified individually, or specify ONLY to
suppress this message"));
What about this?
HINT: To update partitions, apply the command to each one individually,
or specify ONLY to suppress this message.
I'll test the newly covered subcomands tomorrow.
Best, Jim
1 - https://www.postgresql.org/docs/current/error-style-guide.html
^ permalink raw reply [nested|flat] 19+ messages in thread
* Re: ALTER TABLE: warn when actions do not recurse to partitions
2026-01-13 11:16 Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-14 00:52 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 05:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 19:27 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
@ 2026-01-23 00:11 ` Chao Li <[email protected]>
2026-01-23 09:57 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
0 siblings, 1 reply; 19+ messages in thread
From: Chao Li @ 2026-01-23 00:11 UTC (permalink / raw)
To: Jim Jones <[email protected]>; +Cc: David G. Johnston <[email protected]>; Greg Sabino Mullane <[email protected]>; Postgres hackers <[email protected]>
> On Jan 23, 2026, at 03:27, Jim Jones <[email protected]> wrote:
>
> Hi Chao
>
> On 22/01/2026 06:45, Chao Li wrote:
>> evantest=# alter table p_test replica identity full, alter column
>> username set (n_distinct = 0.1);
>> NOTICE: ALTER action REPLICA IDENTITY on relation "p_test" does not
>> affect present partitions
>> HINT: partitions may be modified individually, or specify ONLY to
>> suppress this message
>> NOTICE: ALTER action ALTER COLUMN ... SET on relation "p_test" does not
>> affect present partitions
>> HINT: partitions may be modified individually, or specify ONLY to
>> suppress this message
>> ALTER TABLE
>
>
> One could argue that encapsulating all conditions in
> EmitPartitionNoRecurseNotice(), meaning it is called all the time, is
> slightly inefficient, but the impact is really negligible in this case -
> and it is how it is done in similar functions in tablecmds.c :) The code
> LGTM.
Hi Jim, thanks a lot for the review.
>
> One small thing:
>
> errhint is supposed to be capitalised - see Error Message Style Guide[1]
Thanks for the info, I wasn’t aware of that. When I wrote the code, I searched “errhint” over the source tree, and didn’t find a standard to follow.
>
> "Detail and hint messages: Use complete sentences, and end each with a
> period. Capitalize the first word of sentences. Put two spaces after the
> period if another sentence follows (for English text; might be
> inappropriate in other languages)."
>
> ereport(NOTICE,
> errmsg("ALTER action %s on relation \"%s\" does not affect present
> partitions",
> action_str,
> RelationGetRelationName(rel)),
> errhint("partitions may be modified individually, or specify ONLY to
> suppress this message"));
>
> What about this?
>
> HINT: To update partitions, apply the command to each one individually,
> or specify ONLY to suppress this message.
Looks good. I will integrate your edit to the next version.
>
> I'll test the newly covered subcomands tomorrow.
Thanks again for testing. I will wait to see the test results and address all issues together in next version.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
^ permalink raw reply [nested|flat] 19+ messages in thread
* Re: ALTER TABLE: warn when actions do not recurse to partitions
2026-01-13 11:16 Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-14 00:52 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 05:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 19:27 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-23 00:11 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
@ 2026-01-23 09:57 ` Jim Jones <[email protected]>
2026-03-09 06:46 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
0 siblings, 1 reply; 19+ messages in thread
From: Jim Jones @ 2026-01-23 09:57 UTC (permalink / raw)
To: Chao Li <[email protected]>; +Cc: David G. Johnston <[email protected]>; Greg Sabino Mullane <[email protected]>; Postgres hackers <[email protected]>
On 23/01/2026 01:11, Chao Li wrote:
> I will wait to see the test results and address all issues together in next version.
While testing some edge cases I found out that the NOTICE is being
emitted too early in the code path, e.g.
postgres=# ALTER TABLE m ALTER COLUMN b SET COMPRESSION pglz;
NOTICE: ALTER action ALTER COLUMN ... SET COMPRESSION on relation "m"
does not affect present partitions
HINT: partitions may be modified individually, or specify ONLY to
suppress this message
ERROR: column data type integer does not support compression
I'd argue that emitting only the ERROR message in this case would be the
right approach. What about moving the EmitPartitionNoRecurseNotice()
call to ATExecCmd, right **after** the changes were successfully
executed? For instance, in the case I mentioned above, you could explore:
@@ -5446,6 +5475,8 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
address = ATExecSetCompression(rel, cmd->name,
cmd->def,
lockmode);
+ /* Emit notice after validation passes */
+ EmitPartitionNoRecurseNotice(cmd->subtype, rel,
cmd->recurse, false);
break;
Not sure if cmd->recurse is propagated in this code path. If not, you
might need to do it manually, e.g.
@@ -4936,6 +4937,14 @@ ATPrepCmd(List **wqueue, Relation rel,
AlterTableCmd *cmd,
*/
cmd = copyObject(cmd);
+ if (recurse)
+ cmd->recurse = true;
+
I'm not saying it should be exactly this way, but it sounds more
reasonable to me to emit the NOTICE only if we know that the command is
going to be successfully executed (or was successfully executed).
This patch touches a lot of regression tests, but mostly to add the
keyword ONLY to the ALTER TABLE statements, to avoid the NOTICE message,
so that's ok.
Thanks!
Best, Jim
^ permalink raw reply [nested|flat] 19+ messages in thread
* Re: ALTER TABLE: warn when actions do not recurse to partitions
2026-01-13 11:16 Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-14 00:52 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 05:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 19:27 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-23 00:11 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-23 09:57 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
@ 2026-03-09 06:46 ` Chao Li <[email protected]>
2026-03-10 15:32 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
0 siblings, 1 reply; 19+ messages in thread
From: Chao Li @ 2026-03-09 06:46 UTC (permalink / raw)
To: Jim Jones <[email protected]>; +Cc: David G. Johnston <[email protected]>; Greg Sabino Mullane <[email protected]>; Postgres hackers <[email protected]>
> On Jan 23, 2026, at 17:57, Jim Jones <[email protected]> wrote:
>
>
>
> On 23/01/2026 01:11, Chao Li wrote:
>> I will wait to see the test results and address all issues together in next version.
>
> While testing some edge cases I found out that the NOTICE is being
> emitted too early in the code path, e.g.
Hi Jim, thank you very much for your review and test.
>
> postgres=# ALTER TABLE m ALTER COLUMN b SET COMPRESSION pglz;
> NOTICE: ALTER action ALTER COLUMN ... SET COMPRESSION on relation "m"
> does not affect present partitions
> HINT: partitions may be modified individually, or specify ONLY to
> suppress this message
> ERROR: column data type integer does not support compression
>
> I'd argue that emitting only the ERROR message in this case would be the
> right approach. What about moving the EmitPartitionNoRecurseNotice()
> call to ATExecCmd, right **after** the changes were successfully
> executed? For instance, in the case I mentioned above, you could explore:
>
> @@ -5446,6 +5475,8 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
> case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
> address = ATExecSetCompression(rel, cmd->name,
> cmd->def,
>
> lockmode);
> + /* Emit notice after validation passes */
> + EmitPartitionNoRecurseNotice(cmd->subtype, rel,
> cmd->recurse, false);
> break;
>
> Not sure if cmd->recurse is propagated in this code path. If not, you
> might need to do it manually, e.g.
>
> @@ -4936,6 +4937,14 @@ ATPrepCmd(List **wqueue, Relation rel,
> AlterTableCmd *cmd,
> */
> cmd = copyObject(cmd);
>
> + if (recurse)
> + cmd->recurse = true;
> +
>
> I'm not saying it should be exactly this way, but it sounds more
> reasonable to me to emit the NOTICE only if we know that the command is
> going to be successfully executed (or was successfully executed).
>
I agree with you that the NOTICE should only be emitted when the action succeeds.
Actually, there was another known issue in v4. Since an ALTER TABLE command may contain multiple sub-commands, the NOTICE and HINT could be repeated multiple times, and the HINT was completely duplicate.
I had tried to keep the patch simple because I was worried that a larger refactoring might make it harder for the patch to move forward. But now it looks like I have to some refactoring, though I still to limit the refactoring as minimum as possible.
I hesitated to move EmitPartitionNoRecurseNotice to ATExecCmd. Because ATExecCmd lacks info about recursing, and do cmd->recurse = true; only for notice seems not the right way.
After some investigation, I decided to borrow the idea from 1d92e0c2cc4789255c630d8776bbe85ca9ebc27f, which caches the message first and emits it later. With that approach, in v5, the NOTICE is shown only when the sub-command succeeds, duplicated NOTICE are filtered, and the HINT is shown only once at the end.
In v5, I also updated the HINT message to better comply with the error style guide: capitalize the first letter and end it with a period.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
Attachments:
[application/octet-stream] v5-0001-ALTER-TABLE-emit-NOTICE-when-ONLY-is-omitted-but-.patch (38.1K, 2-v5-0001-ALTER-TABLE-emit-NOTICE-when-ONLY-is-omitted-but-.patch)
download | inline diff:
From 8c092365dc18245e5aa6deb39a4b819341e0a40e Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <[email protected]>
Date: Mon, 12 Jan 2026 16:56:58 +0800
Subject: [PATCH v5] ALTER TABLE: emit NOTICE when ONLY is omitted but no
partition recursion occurs
Some ALTER TABLE sub-commands do not recurse to partitions even when ONLY
is not specified on a partitioned table. This can be surprising, since
the default expectation is that commands propagate to all partitions.
Emit a NOTICE in such cases to make the behavior explicit, and advise how
to suppress the message or modify partitions individually.
Affected sub-commands include:
- ALTER COLUMN SET/RESET attribute_option
- ALTER COLUMN SET COMPRESSION
- ENABLE/DISABLE RULE
- ENABLE/DISABLE/FORCE/NO FORCE ROW LEVEL SECURITY
- REPLICA IDENTITY
- OWNER TO
- SET TABLESPACE
- SET SCHEMA
RENAME is intentionally excluded. Using ONLY (or not) has no effect for
RENAME, since relation names are independent by nature and there is no
expectation of recursion.
OF / NOT OF are also excluded. Using ONLY has no effect for these
commands, as they apply only to the partitioned table itself and not to
its partitions.
Regression tests are updated to use ONLY where appropriate.
Author: Chao Li <[email protected]>
Reviewed-by: David G. Johnston <[email protected]>
Reviewed-by: Greg Sabino Mullane <[email protected]>
Reviewed-by: Jim Jones <[email protected]>
Discussion: https://postgr.es/m/CAEoWx2=SLga-xH09Cq_PAvsHhQHrBK+V0vF821JKgzS=Bm0haA@mail.gmail.com
---
src/backend/commands/tablecmds.c | 143 ++++++++++++++++--
src/include/nodes/parsenodes.h | 1 +
src/test/regress/expected/alter_table.out | 68 ++++++++-
src/test/regress/expected/cluster.out | 2 +-
src/test/regress/expected/merge.out | 4 +-
src/test/regress/expected/partition_merge.out | 2 +-
src/test/regress/expected/partition_split.out | 2 +-
src/test/regress/expected/privileges.out | 2 +-
src/test/regress/expected/rowsecurity.out | 4 +-
src/test/regress/expected/tablespace.out | 4 +-
src/test/regress/expected/update.out | 2 +-
src/test/regress/expected/vacuum.out | 6 +-
src/test/regress/sql/alter_table.sql | 51 ++++++-
src/test/regress/sql/cluster.sql | 2 +-
src/test/regress/sql/merge.sql | 4 +-
src/test/regress/sql/partition_merge.sql | 2 +-
src/test/regress/sql/partition_split.sql | 2 +-
src/test/regress/sql/privileges.sql | 2 +-
src/test/regress/sql/rowsecurity.sql | 4 +-
src/test/regress/sql/tablespace.sql | 4 +-
src/test/regress/sql/update.sql | 2 +-
src/test/regress/sql/vacuum.sql | 6 +-
22 files changed, 276 insertions(+), 43 deletions(-)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 85242dcc245..2df39cca52f 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -358,6 +358,11 @@ typedef enum addFkConstraintSides
addFkBothSides,
} addFkConstraintSides;
+typedef struct partitionNoRecurseNotice
+{
+ List *notices;
+} partitionNoRecurseNotice;
+
/*
* Partition tables are expected to be dropped when the parent partitioned
* table gets dropped. Hence for partitioning we use AUTO dependency.
@@ -461,7 +466,8 @@ static void ATController(AlterTableStmt *parsetree,
AlterTableUtilityContext *context);
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
bool recurse, bool recursing, LOCKMODE lockmode,
- AlterTableUtilityContext *context);
+ AlterTableUtilityContext *context,
+ partitionNoRecurseNotice * postNotice);
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
AlterTableUtilityContext *context);
static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
@@ -745,6 +751,10 @@ static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation
static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
Relation rel, PartitionCmd *cmd,
AlterTableUtilityContext *context);
+static void CollectPartitionNoRecurseNotice(AlterTableType cmdtype, Relation rel,
+ bool recurse, bool recursing,
+ partitionNoRecurseNotice * postNotice);
+static void EmitPartitionNoRecurseNotice(partitionNoRecurseNotice * postNotice);
/* ----------------------------------------------------------------
* DefineRelation
@@ -4886,13 +4896,14 @@ ATController(AlterTableStmt *parsetree,
{
List *wqueue = NIL;
ListCell *lcmd;
+ partitionNoRecurseNotice postNotice = {0};
/* Phase 1: preliminary examination of commands, create work queue */
foreach(lcmd, cmds)
{
AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
- ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
+ ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context, &postNotice);
}
/* Close the relation, but keep lock until commit */
@@ -4903,6 +4914,9 @@ ATController(AlterTableStmt *parsetree,
/* Phase 3: scan/rewrite tables as needed, and run afterStmts */
ATRewriteTables(parsetree, &wqueue, lockmode, context);
+
+ /* Emit post-notice for partitions that were not recursed into. */
+ EmitPartitionNoRecurseNotice(&postNotice);
}
/*
@@ -4917,7 +4931,8 @@ ATController(AlterTableStmt *parsetree,
static void
ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
bool recurse, bool recursing, LOCKMODE lockmode,
- AlterTableUtilityContext *context)
+ AlterTableUtilityContext *context,
+ partitionNoRecurseNotice * postNotice)
{
AlteredTableInfo *tab;
AlterTablePass pass = AT_PASS_UNSET;
@@ -5066,6 +5081,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATT_MATVIEW | ATT_FOREIGN_TABLE);
/* This command never recurses */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, postNotice);
break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
ATSimplePermissions(cmd->subtype, rel,
@@ -5081,6 +5098,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, postNotice);
break;
case AT_DropColumn: /* DROP COLUMN */
ATSimplePermissions(cmd->subtype, rel,
@@ -5148,6 +5167,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, postNotice);
break;
case AT_ClusterOn: /* CLUSTER ON */
case AT_DropCluster: /* SET WITHOUT CLUSTER */
@@ -5191,6 +5212,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
pass = AT_PASS_MISC; /* doesn't actually matter */
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, postNotice);
break;
case AT_SetRelOptions: /* SET (...) */
case AT_ResetRelOptions: /* RESET (...) */
@@ -5200,6 +5223,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATT_MATVIEW | ATT_INDEX);
/* This command never recurses */
/* No command-specific prep needed */
+ /* It will check for partitioned table at exec time */
pass = AT_PASS_MISC;
break;
case AT_AddInherit: /* INHERIT */
@@ -5237,8 +5261,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATSimplePermissions(cmd->subtype, rel,
ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
pass = AT_PASS_MISC;
- /* This command never recurses */
- /* No command-specific prep needed */
+ /* This command doesn't recurse to partitions, so notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, postNotice);
break;
case AT_EnableTrig: /* ENABLE TRIGGER variants */
case AT_EnableAlwaysTrig:
@@ -5259,17 +5283,30 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
case AT_EnableAlwaysRule:
case AT_EnableReplicaRule:
case AT_DisableRule:
- case AT_AddOf: /* OF */
- case AT_DropOf: /* NOT OF */
- case AT_EnableRowSecurity:
+ case AT_EnableRowSecurity: /* ENABLE/DISABLE ROW SECURITY variants */
case AT_DisableRowSecurity:
- case AT_ForceRowSecurity:
+ case AT_ForceRowSecurity: /* FORCE/NO FORCE ROW SECURITY variants */
case AT_NoForceRowSecurity:
ATSimplePermissions(cmd->subtype, rel,
ATT_TABLE | ATT_PARTITIONED_TABLE);
/* These commands never recurse */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, postNotice);
+ break;
+ case AT_AddOf: /* OF */
+ case AT_DropOf: /* NOT OF */
+ ATSimplePermissions(cmd->subtype, rel,
+ ATT_TABLE | ATT_PARTITIONED_TABLE);
+ /* These commands never recurse */
+ /* No command-specific prep needed */
+
+ /*
+ * They only work on partitioned tables but child partitions, thus
+ * no need to emit a notice
+ */
+ pass = AT_PASS_MISC;
break;
case AT_GenericOptions:
ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
@@ -6762,6 +6799,8 @@ alter_table_type_to_string(AlterTableType cmdtype)
return "ALTER COLUMN ... DROP IDENTITY";
case AT_ReAddStatistics:
return NULL; /* not real grammar */
+ case AT_SetSchema:
+ return "SET SCHEMA";
}
return NULL;
@@ -6883,7 +6922,7 @@ ATSimpleRecursion(List **wqueue, Relation rel,
/* find_all_inheritors already got lock */
childrel = relation_open(childrelid, NoLock);
CheckAlterTableIsSafe(childrel);
- ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
+ ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context, NULL);
relation_close(childrel, NoLock);
}
}
@@ -6946,7 +6985,7 @@ ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
childrel = relation_open(childrelid, lockmode);
CheckAlterTableIsSafe(childrel);
- ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
+ ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context, NULL);
relation_close(childrel, NoLock);
}
}
@@ -9592,7 +9631,7 @@ ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
newcmd->recurse = true;
newcmd->def = (Node *) nnconstr;
- ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
+ ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context, NULL);
}
}
@@ -14673,7 +14712,7 @@ ATPrepAlterColumnType(List **wqueue,
errdetail("USING expression contains a whole-row table reference.")));
pfree(attmap);
}
- ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
+ ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context, NULL);
relation_close(childrel, NoLock);
}
}
@@ -18977,6 +19016,7 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
RangeVar *newrv;
ObjectAddresses *objsMoved;
ObjectAddress myself;
+ partitionNoRecurseNotice postNotice = {0};
relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
stmt->missing_ok ? RVR_MISSING_OK : 0,
@@ -18993,6 +19033,13 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
rel = relation_open(relid, NoLock);
+ /*
+ * SET SCHEMA doesn't recurse to children, emit a notice if ONLY is not
+ * specified. As this action doesn't go through ATPrepCmd, we have to emit
+ * the notice here.
+ */
+ CollectPartitionNoRecurseNotice(AT_SetSchema, rel, stmt->relation->inh, false, &postNotice);
+
oldNspOid = RelationGetNamespace(rel);
/* If it's an owned sequence, disallow moving it by itself. */
@@ -19030,6 +19077,8 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
/* close rel, but keep lock until commit */
relation_close(rel, NoLock);
+ EmitPartitionNoRecurseNotice(&postNotice);
+
return myself;
}
@@ -23381,3 +23430,71 @@ ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
/* Restore the userid and security context. */
SetUserIdAndSecContext(save_userid, save_sec_context);
}
+
+/*
+ * When ONLY is not specified with a partitioned table, it is expected that the
+ * command recurses to all partitions. However, some sub-commands do not recurse.
+ * In such cases, emit a NOTICE to make this behavior explicit to the user.
+ */
+static void
+CollectPartitionNoRecurseNotice(AlterTableType cmdtype, Relation rel, bool recurse, bool recursing,
+ partitionNoRecurseNotice * postNotice)
+{
+ if (postNotice == NULL)
+ return;
+
+ /* Only emit the notice at the top level of recursion */
+ if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && recurse && !recursing)
+ {
+ PartitionDesc pd = RelationGetPartitionDesc(rel, true);
+ int nparts = pd->nparts;
+ const char *action_str;
+ char *notice_msg;
+ const ListCell *cell;
+ bool found = false;
+
+ /* Emit a notice only if there are partitions */
+ if (nparts == 0)
+ return;
+
+ action_str = alter_table_type_to_string(cmdtype);
+ notice_msg = psprintf(_("ALTER action %s on relation \"%s\" does not affect present partitions"),
+ action_str,
+ RelationGetRelationName(rel));
+
+ foreach(cell, postNotice->notices)
+ {
+ if (strcmp((char *) lfirst(cell), notice_msg) == 0)
+ {
+ pfree(notice_msg);
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ postNotice->notices = lappend(postNotice->notices, notice_msg);
+ }
+}
+
+static void
+EmitPartitionNoRecurseNotice(partitionNoRecurseNotice * postNotice)
+{
+ ListCell *cell;
+ int len;
+ int i = 0;
+
+ len = list_length(postNotice->notices);
+ foreach(cell, postNotice->notices)
+ {
+ char *notice_msg = (char *) lfirst(cell);
+
+ /* Only emit the hint for the last notice */
+ i++;
+ ereport(NOTICE, errmsg("%s", notice_msg),
+ (i == len) ?
+ errhint("Partitions may be modified individually, or specify ONLY to suppress this message.")
+ : 0);
+ pfree(notice_msg);
+ }
+ list_free(postNotice->notices);
+}
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ff41943a6db..1a18aebe995 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2511,6 +2511,7 @@ typedef enum AlterTableType
AT_SetIdentity, /* SET identity column options */
AT_DropIdentity, /* DROP IDENTITY */
AT_ReAddStatistics, /* internal to commands/tablecmds.c */
+ AT_SetSchema, /* SET SCHEMA */
} AlterTableType;
typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 5998c670aa3..5d090460002 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -4582,8 +4582,74 @@ SELECT * FROM list_parted;
---
(0 rows)
+CREATE TABLE list_parted4 (a int, b text) PARTITION BY LIST (a);
+CREATE TABLE list_parted4_1 PARTITION OF list_parted4 FOR VALUES IN (1);
+-- set column attribute on partitioned table should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+NOTICE: ALTER action ALTER COLUMN ... SET on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE list_parted4 ALTER COLUMN b RESET (n_distinct);
+NOTICE: ALTER action ALTER COLUMN ... RESET on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b RESET (n_distinct);
+-- enable/disable rules on partitioned tables should get a notice
+CREATE RULE list_parted4_rule AS ON INSERT TO list_parted4 DO INSTEAD NOTHING;
+ALTER TABLE list_parted4 DISABLE RULE list_parted4_rule;
+NOTICE: ALTER action DISABLE RULE on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 DISABLE RULE list_parted4_rule;
+ALTER TABLE list_parted4 ENABLE RULE list_parted4_rule;
+NOTICE: ALTER action ENABLE RULE on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ENABLE RULE list_parted4_rule;
+DROP RULE list_parted4_rule ON list_parted4;
+-- enable/disable row level security on partitioned tables should get a notice
+ALTER TABLE list_parted4 ENABLE ROW LEVEL SECURITY;
+NOTICE: ALTER action ENABLE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 DISABLE ROW LEVEL SECURITY;
+NOTICE: ALTER action DISABLE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 DISABLE ROW LEVEL SECURITY;
+-- force/no force row level security on partitioned tables should get a notice
+ALTER TABLE list_parted4 FORCE ROW LEVEL SECURITY;
+NOTICE: ALTER action FORCE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 FORCE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 NO FORCE ROW LEVEL SECURITY;
+NOTICE: ALTER action NO FORCE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 NO FORCE ROW LEVEL SECURITY;
+-- set replica identity on partitioned tables should get a notice
+ALTER TABLE list_parted4 REPLICA IDENTITY FULL;
+NOTICE: ALTER action REPLICA IDENTITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY FULL;
+ALTER TABLE list_parted4 REPLICA IDENTITY NOTHING;
+NOTICE: ALTER action REPLICA IDENTITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY NOTHING;
+-- set compression on partitioned tables should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+NOTICE: ALTER action ALTER COLUMN ... SET COMPRESSION on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+-- set owner on partitioned tables should get a notice
+ALTER TABLE list_parted4 OWNER TO regress_alter_table_user1;
+NOTICE: ALTER action OWNER TO on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 OWNER TO regress_alter_table_user1;
+-- set schema on partitioned tables should get a notice
+CREATE SCHEMA alter_table_test_schema;
+ALTER TABLE list_parted4 SET SCHEMA alter_table_test_schema;
+NOTICE: ALTER action SET SCHEMA on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY alter_table_test_schema.list_parted4 SET SCHEMA public;
+DROP SCHEMA alter_table_test_schema CASCADE;
-- cleanup
-DROP TABLE list_parted, list_parted2, range_parted, list_parted3;
+DROP TABLE list_parted, list_parted2, range_parted, list_parted3, list_parted4;
DROP TABLE fail_def_part;
DROP TABLE hash_parted;
-- more tests for certain multi-level partitioning scenarios
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 4d40a6809ab..6ea8c99d092 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -507,7 +507,7 @@ SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
RESET SESSION AUTHORIZATION;
-ALTER TABLE ptnowner OWNER TO regress_ptnowner;
+ALTER TABLE ONLY ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
SELECT oid, relname, relfilenode FROM pg_partition_tree('ptnowner') AS tree
JOIN pg_class AS c ON c.oid=tree.relid;
diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out
index 9cb1d87066a..bd6aac72336 100644
--- a/src/test/regress/expected/merge.out
+++ b/src/test/regress/expected/merge.out
@@ -2298,8 +2298,8 @@ SELECT * FROM pa_target ORDER BY tid, val;
ROLLBACK;
-- test RLS enforcement
BEGIN;
-ALTER TABLE pa_target ENABLE ROW LEVEL SECURITY;
-ALTER TABLE pa_target FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target FORCE ROW LEVEL SECURITY;
CREATE POLICY pa_target_pol ON pa_target USING (tid != 0);
MERGE INTO pa_target t
USING pa_source s
diff --git a/src/test/regress/expected/partition_merge.out b/src/test/regress/expected/partition_merge.out
index 925fe4f570a..f629fc7f3e5 100644
--- a/src/test/regress/expected/partition_merge.out
+++ b/src/test/regress/expected/partition_merge.out
@@ -810,7 +810,7 @@ SET SESSION AUTHORIZATION regress_partition_merge_bob;
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
ERROR: must be owner of table t
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_merge_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_merge_bob;
SET SESSION AUTHORIZATION regress_partition_merge_bob;
-- ERROR: must be owner of table tp_0_1
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
diff --git a/src/test/regress/expected/partition_split.out b/src/test/regress/expected/partition_split.out
index 4004efe0dac..421fa36ec0f 100644
--- a/src/test/regress/expected/partition_split.out
+++ b/src/test/regress/expected/partition_split.out
@@ -1372,7 +1372,7 @@ ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); --error
ERROR: must be owner of table t
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_split_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_split_bob;
SET SESSION AUTHORIZATION regress_partition_split_bob;
ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
(PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 7bc274566c3..877a1214963 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -1908,7 +1908,7 @@ SELECT brin_summarize_range('sro_brin', 0);
DROP TABLE sro_tab;
-- Check with a partitioned table
CREATE TABLE sro_ptab (a int) PARTITION BY RANGE (a);
-ALTER TABLE sro_ptab OWNER TO regress_sro_user;
+ALTER TABLE ONLY sro_ptab OWNER TO regress_sro_user;
CREATE TABLE sro_part PARTITION OF sro_ptab FOR VALUES FROM (1) TO (10);
ALTER TABLE sro_part OWNER TO regress_sro_user;
INSERT INTO sro_ptab VALUES (1), (2), (3);
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index 07d93e7def1..639c0065b0c 100644
--- a/src/test/regress/expected/rowsecurity.out
+++ b/src/test/regress/expected/rowsecurity.out
@@ -1233,7 +1233,7 @@ INSERT INTO part_document VALUES
( 8, 55, 2, 'regress_rls_carol', 'great satire'),
( 9, 11, 1, 'regress_rls_dave', 'awesome science fiction'),
(10, 99, 2, 'regress_rls_dave', 'awesome technology book');
-ALTER TABLE part_document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY part_document ENABLE ROW LEVEL SECURITY;
-- Create policy on parent
-- user's security level must be higher than or equal to document's
CREATE POLICY pp1 ON part_document AS PERMISSIVE
@@ -4914,7 +4914,7 @@ CREATE TABLE rls_ptbl (a int) PARTITION BY RANGE (a);
CREATE TABLE rls_part PARTITION OF rls_ptbl FOR VALUES FROM (-100) TO (100);
INSERT INTO rls_ptbl SELECT x/10 FROM generate_series(1, 100) x;
ANALYZE rls_ptbl, rls_part;
-ALTER TABLE rls_ptbl ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY rls_ptbl ENABLE ROW LEVEL SECURITY;
ALTER TABLE rls_part ENABLE ROW LEVEL SECURITY;
GRANT SELECT ON rls_ptbl TO regress_rls_alice;
GRANT SELECT ON rls_part TO regress_rls_alice;
diff --git a/src/test/regress/expected/tablespace.out b/src/test/regress/expected/tablespace.out
index 185880a3217..bf437a92746 100644
--- a/src/test/regress/expected/tablespace.out
+++ b/src/test/regress/expected/tablespace.out
@@ -290,13 +290,13 @@ CREATE TABLE testschema.part_2 PARTITION OF testschema.part FOR VALUES IN (2);
SET default_tablespace TO pg_global;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
ERROR: only shared relations can be placed in pg_global tablespace
-ALTER TABLE testschema.part SET TABLESPACE regress_tblspace;
+ALTER TABLE ONLY testschema.part SET TABLESPACE regress_tblspace;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
CREATE TABLE testschema.part_4 PARTITION OF testschema.part FOR VALUES IN (4)
TABLESPACE pg_default;
CREATE TABLE testschema.part_56 PARTITION OF testschema.part FOR VALUES IN (5, 6)
PARTITION BY LIST (a);
-ALTER TABLE testschema.part SET TABLESPACE pg_default;
+ALTER TABLE ONLY testschema.part SET TABLESPACE pg_default;
CREATE TABLE testschema.part_78 PARTITION OF testschema.part FOR VALUES IN (7, 8)
PARTITION BY LIST (a);
ERROR: only shared relations can be placed in pg_global tablespace
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index eef2bac1cbf..584b40598eb 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -607,7 +607,7 @@ DROP TRIGGER trig_d15_20 ON part_d_15_20;
DROP FUNCTION func_parted_mod_b();
-- RLS policies with update-row-movement
-----------------------------------------
-ALTER TABLE range_parted ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY range_parted ENABLE ROW LEVEL SECURITY;
CREATE USER regress_range_parted_user;
GRANT ALL ON range_parted, mintab TO regress_range_parted_user;
CREATE POLICY seeall ON range_parted AS PERMISSIVE FOR SELECT USING (true);
diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out
index d4696bc3325..f22bb2127e1 100644
--- a/src/test/regress/expected/vacuum.out
+++ b/src/test/regress/expected/vacuum.out
@@ -614,7 +614,7 @@ VACUUM (ANALYZE) vacowned_part2;
WARNING: permission denied to vacuum "vacowned_part2", skipping it
RESET ROLE;
-- Partitioned table and one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO regress_vacuum;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
@@ -634,7 +634,7 @@ VACUUM (ANALYZE) vacowned_part2;
WARNING: permission denied to vacuum "vacowned_part2", skipping it
RESET ROLE;
-- Only one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO CURRENT_USER;
+ALTER TABLE ONLY vacowned_parted OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
WARNING: permission denied to vacuum "vacowned_parted", skipping it
@@ -656,7 +656,7 @@ VACUUM (ANALYZE) vacowned_part2;
WARNING: permission denied to vacuum "vacowned_part2", skipping it
RESET ROLE;
-- Only partitioned table owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index d6b6381ae5c..83cc7f86736 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2908,8 +2908,57 @@ ALTER TABLE list_parted2 ALTER COLUMN b TYPE text;
ALTER TABLE list_parted DROP COLUMN b;
SELECT * FROM list_parted;
+CREATE TABLE list_parted4 (a int, b text) PARTITION BY LIST (a);
+CREATE TABLE list_parted4_1 PARTITION OF list_parted4 FOR VALUES IN (1);
+
+-- set column attribute on partitioned table should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+ALTER TABLE list_parted4 ALTER COLUMN b RESET (n_distinct);
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b RESET (n_distinct);
+
+-- enable/disable rules on partitioned tables should get a notice
+CREATE RULE list_parted4_rule AS ON INSERT TO list_parted4 DO INSTEAD NOTHING;
+ALTER TABLE list_parted4 DISABLE RULE list_parted4_rule;
+ALTER TABLE ONLY list_parted4 DISABLE RULE list_parted4_rule;
+ALTER TABLE list_parted4 ENABLE RULE list_parted4_rule;
+ALTER TABLE ONLY list_parted4 ENABLE RULE list_parted4_rule;
+DROP RULE list_parted4_rule ON list_parted4;
+
+-- enable/disable row level security on partitioned tables should get a notice
+ALTER TABLE list_parted4 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 DISABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 DISABLE ROW LEVEL SECURITY;
+
+-- force/no force row level security on partitioned tables should get a notice
+ALTER TABLE list_parted4 FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 FORCE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 NO FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 NO FORCE ROW LEVEL SECURITY;
+
+-- set replica identity on partitioned tables should get a notice
+ALTER TABLE list_parted4 REPLICA IDENTITY FULL;
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY FULL;
+ALTER TABLE list_parted4 REPLICA IDENTITY NOTHING;
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY NOTHING;
+
+-- set compression on partitioned tables should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+
+-- set owner on partitioned tables should get a notice
+ALTER TABLE list_parted4 OWNER TO regress_alter_table_user1;
+ALTER TABLE ONLY list_parted4 OWNER TO regress_alter_table_user1;
+
+-- set schema on partitioned tables should get a notice
+CREATE SCHEMA alter_table_test_schema;
+ALTER TABLE list_parted4 SET SCHEMA alter_table_test_schema;
+ALTER TABLE ONLY alter_table_test_schema.list_parted4 SET SCHEMA public;
+DROP SCHEMA alter_table_test_schema CASCADE;
+
-- cleanup
-DROP TABLE list_parted, list_parted2, range_parted, list_parted3;
+DROP TABLE list_parted, list_parted2, range_parted, list_parted3, list_parted4;
DROP TABLE fail_def_part;
DROP TABLE hash_parted;
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index b7115f86104..06cdb311b97 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -241,7 +241,7 @@ ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
RESET SESSION AUTHORIZATION;
-ALTER TABLE ptnowner OWNER TO regress_ptnowner;
+ALTER TABLE ONLY ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
SELECT oid, relname, relfilenode FROM pg_partition_tree('ptnowner') AS tree
JOIN pg_class AS c ON c.oid=tree.relid;
diff --git a/src/test/regress/sql/merge.sql b/src/test/regress/sql/merge.sql
index 2660b19f238..2bffcf7aa98 100644
--- a/src/test/regress/sql/merge.sql
+++ b/src/test/regress/sql/merge.sql
@@ -1422,8 +1422,8 @@ ROLLBACK;
-- test RLS enforcement
BEGIN;
-ALTER TABLE pa_target ENABLE ROW LEVEL SECURITY;
-ALTER TABLE pa_target FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target FORCE ROW LEVEL SECURITY;
CREATE POLICY pa_target_pol ON pa_target USING (tid != 0);
MERGE INTO pa_target t
USING pa_source s
diff --git a/src/test/regress/sql/partition_merge.sql b/src/test/regress/sql/partition_merge.sql
index a211fee2ad1..73bffb530dd 100644
--- a/src/test/regress/sql/partition_merge.sql
+++ b/src/test/regress/sql/partition_merge.sql
@@ -571,7 +571,7 @@ SET SESSION AUTHORIZATION regress_partition_merge_bob;
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_merge_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_merge_bob;
SET SESSION AUTHORIZATION regress_partition_merge_bob;
-- ERROR: must be owner of table tp_0_1
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
diff --git a/src/test/regress/sql/partition_split.sql b/src/test/regress/sql/partition_split.sql
index 37c6d730840..f4f9c7886e4 100644
--- a/src/test/regress/sql/partition_split.sql
+++ b/src/test/regress/sql/partition_split.sql
@@ -960,7 +960,7 @@ ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); --error
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_split_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_split_bob;
SET SESSION AUTHORIZATION regress_partition_split_bob;
ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
(PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index 540f73ea9b1..eb73629a81f 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -1230,7 +1230,7 @@ SELECT brin_summarize_range('sro_brin', 0);
DROP TABLE sro_tab;
-- Check with a partitioned table
CREATE TABLE sro_ptab (a int) PARTITION BY RANGE (a);
-ALTER TABLE sro_ptab OWNER TO regress_sro_user;
+ALTER TABLE ONLY sro_ptab OWNER TO regress_sro_user;
CREATE TABLE sro_part PARTITION OF sro_ptab FOR VALUES FROM (1) TO (10);
ALTER TABLE sro_part OWNER TO regress_sro_user;
INSERT INTO sro_ptab VALUES (1), (2), (3);
diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql
index 6b3566271df..1a2e80dade0 100644
--- a/src/test/regress/sql/rowsecurity.sql
+++ b/src/test/regress/sql/rowsecurity.sql
@@ -499,7 +499,7 @@ INSERT INTO part_document VALUES
( 9, 11, 1, 'regress_rls_dave', 'awesome science fiction'),
(10, 99, 2, 'regress_rls_dave', 'awesome technology book');
-ALTER TABLE part_document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY part_document ENABLE ROW LEVEL SECURITY;
-- Create policy on parent
-- user's security level must be higher than or equal to document's
@@ -2414,7 +2414,7 @@ CREATE TABLE rls_part PARTITION OF rls_ptbl FOR VALUES FROM (-100) TO (100);
INSERT INTO rls_ptbl SELECT x/10 FROM generate_series(1, 100) x;
ANALYZE rls_ptbl, rls_part;
-ALTER TABLE rls_ptbl ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY rls_ptbl ENABLE ROW LEVEL SECURITY;
ALTER TABLE rls_part ENABLE ROW LEVEL SECURITY;
GRANT SELECT ON rls_ptbl TO regress_rls_alice;
GRANT SELECT ON rls_part TO regress_rls_alice;
diff --git a/src/test/regress/sql/tablespace.sql b/src/test/regress/sql/tablespace.sql
index c43a59e5957..8eff2914a5b 100644
--- a/src/test/regress/sql/tablespace.sql
+++ b/src/test/regress/sql/tablespace.sql
@@ -195,13 +195,13 @@ SET default_tablespace TO regress_tblspace;
CREATE TABLE testschema.part_2 PARTITION OF testschema.part FOR VALUES IN (2);
SET default_tablespace TO pg_global;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
-ALTER TABLE testschema.part SET TABLESPACE regress_tblspace;
+ALTER TABLE ONLY testschema.part SET TABLESPACE regress_tblspace;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
CREATE TABLE testschema.part_4 PARTITION OF testschema.part FOR VALUES IN (4)
TABLESPACE pg_default;
CREATE TABLE testschema.part_56 PARTITION OF testschema.part FOR VALUES IN (5, 6)
PARTITION BY LIST (a);
-ALTER TABLE testschema.part SET TABLESPACE pg_default;
+ALTER TABLE ONLY testschema.part SET TABLESPACE pg_default;
CREATE TABLE testschema.part_78 PARTITION OF testschema.part FOR VALUES IN (7, 8)
PARTITION BY LIST (a);
CREATE TABLE testschema.part_910 PARTITION OF testschema.part FOR VALUES IN (9, 10)
diff --git a/src/test/regress/sql/update.sql b/src/test/regress/sql/update.sql
index 8b4707eb9c3..56b1edb00ce 100644
--- a/src/test/regress/sql/update.sql
+++ b/src/test/regress/sql/update.sql
@@ -341,7 +341,7 @@ DROP FUNCTION func_parted_mod_b();
-- RLS policies with update-row-movement
-----------------------------------------
-ALTER TABLE range_parted ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY range_parted ENABLE ROW LEVEL SECURITY;
CREATE USER regress_range_parted_user;
GRANT ALL ON range_parted, mintab TO regress_range_parted_user;
CREATE POLICY seeall ON range_parted AS PERMISSIVE FOR SELECT USING (true);
diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql
index 247b8e23b23..acff5824ebb 100644
--- a/src/test/regress/sql/vacuum.sql
+++ b/src/test/regress/sql/vacuum.sql
@@ -452,7 +452,7 @@ VACUUM (ANALYZE) vacowned_part1;
VACUUM (ANALYZE) vacowned_part2;
RESET ROLE;
-- Partitioned table and one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO regress_vacuum;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
@@ -466,7 +466,7 @@ VACUUM (ANALYZE) vacowned_part1;
VACUUM (ANALYZE) vacowned_part2;
RESET ROLE;
-- Only one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO CURRENT_USER;
+ALTER TABLE ONLY vacowned_parted OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
VACUUM vacowned_part1;
@@ -479,7 +479,7 @@ VACUUM (ANALYZE) vacowned_part1;
VACUUM (ANALYZE) vacowned_part2;
RESET ROLE;
-- Only partitioned table owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
--
2.50.1 (Apple Git-155)
^ permalink raw reply [nested|flat] 19+ messages in thread
* Re: ALTER TABLE: warn when actions do not recurse to partitions
2026-01-13 11:16 Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-14 00:52 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 05:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 19:27 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-23 00:11 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-23 09:57 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-03-09 06:46 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
@ 2026-03-10 15:32 ` Greg Sabino Mullane <[email protected]>
2026-03-11 07:04 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
0 siblings, 1 reply; 19+ messages in thread
From: Greg Sabino Mullane @ 2026-03-10 15:32 UTC (permalink / raw)
To: Jim Jones <[email protected]>; +Cc: Chao Li <[email protected]>; David G. Johnston <[email protected]>; Postgres hackers <[email protected]>
Review of v6:
typedef struct partitionNoRecurseNotice
> {
> List *notices;
> } partitionNoRecurseNotice;
Not sure why we need a struct here, rather than just passing the list
around?
Also should be PartitionNoRecurseNotice (CamelCase)
foreach(cell, postNotice->notices)
> {
> if (strcmp((char *) lfirst(cell), notice_msg) == 0)
> {
> pfree(notice_msg);
> found = true;
> break;
> }
> }
This seems a lot of extra work that could be avoided. Since we know each
message is unique to the cmdtype/AlterTableType and the rel/Relation
combination, use those two to drive the duplicate check. Then we can only
build the notice_msg if needed! Perhaps adding two more fields to that
lonely struct above?
partitionNoRecurseNotice * postNotice);
postNotice is a little misleading - maybe pending_notices or just notices?
does not affect present partitions
s/present/existing/g
> CollectPartitionNoRecurseNotice(AT_SetSchema, rel, stmt->relation->inh,
> false, &postNotice);
This hard-coded AT_SetSchema just to return a "SET SCHEMA" later on feels
hacky. Don't have a workaround off the top of my head, just registering my
mild unease. :)
/* Emit a notice only if there are partitions */
> if (nparts == 0)
> return;
It doesn't look like this particular case is tested. Other than that, the
tests look very good.
Cheers,
Greg
^ permalink raw reply [nested|flat] 19+ messages in thread
* Re: ALTER TABLE: warn when actions do not recurse to partitions
2026-01-13 11:16 Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-14 00:52 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 05:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 19:27 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-23 00:11 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-23 09:57 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-03-09 06:46 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-10 15:32 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
@ 2026-03-11 07:04 ` Chao Li <[email protected]>
2026-03-11 16:39 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
0 siblings, 1 reply; 19+ messages in thread
From: Chao Li @ 2026-03-11 07:04 UTC (permalink / raw)
To: Greg Sabino Mullane <[email protected]>; +Cc: Jim Jones <[email protected]>; David G. Johnston <[email protected]>; Postgres hackers <[email protected]>
> On Mar 10, 2026, at 23:32, Greg Sabino Mullane <[email protected]> wrote:
>
> Review of v6:
Thank you very much for the review.
>
> typedef struct partitionNoRecurseNotice
> {
> List *notices;
> } partitionNoRecurseNotice;
> Not sure why we need a struct here, rather than just passing the list around?
Initially I thought there might be a few fields in the structure, but ended up only one List field. Yes, this structure is not needed now. Removed it in v7.
>
> Also should be PartitionNoRecurseNotice (CamelCase)
>
> foreach(cell, postNotice->notices)
> {
> if (strcmp((char *) lfirst(cell), notice_msg) == 0)
> {
> pfree(notice_msg);
> found = true;
> break;
> }
> }
>
> This seems a lot of extra work that could be avoided. Since we know each message is unique to the cmdtype/AlterTableType and the rel/Relation combination, use those two to drive the duplicate check. Then we can only build the notice_msg if needed! Perhaps adding two more fields to that lonely struct above?
Are you concerning that rendering the full message text is the extra work? This is not a hot path, so I don’t think that would be a big deal. Actually, adding two more fields sounds more expensive.
>
> partitionNoRecurseNotice * postNotice);
>
> postNotice is a little misleading - maybe pending_notices or just notices?
Yes, “pending” makes sense. Updated in v7.
>
> does not affect present partitions
>
> s/present/existing/g
> CollectPartitionNoRecurseNotice(AT_SetSchema, rel, stmt->relation->inh, false, &postNotice);
>
> This hard-coded AT_SetSchema just to return a "SET SCHEMA" later on feels hacky. Don't have a workaround off the top of my head, just registering my mild unease. :)
Yes, as SET SCHEMA doesn’t go through the standard ALTER TABLE process: AlterTable() -> ATController() -> ATPrepCmd().
If you get some idea, please let me know.
>
> /* Emit a notice only if there are partitions */
> if (nparts == 0)
> return;
>
> It doesn't look like this particular case is tested. Other than that, the tests look very good.
Good catch. Added a test case to cover that.
PFA v7: addressed Greg’s review comments.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
Attachments:
[application/octet-stream] v7-0001-ALTER-TABLE-emit-NOTICE-when-ONLY-is-omitted-but-.patch (39.5K, 2-v7-0001-ALTER-TABLE-emit-NOTICE-when-ONLY-is-omitted-but-.patch)
download | inline diff:
From e09d8d71015f379ede75dc9fbc280ff45137a6bd Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <[email protected]>
Date: Mon, 12 Jan 2026 16:56:58 +0800
Subject: [PATCH v7] ALTER TABLE: emit NOTICE when ONLY is omitted but no
partition recursion occurs
Some ALTER TABLE sub-commands do not recurse to partitions even when ONLY
is not specified on a partitioned table. This can be surprising, since
the default expectation is that commands propagate to all partitions.
Emit a NOTICE in such cases to make the behavior explicit, and advise how
to suppress the message or modify partitions individually.
Affected sub-commands include:
- ALTER COLUMN SET/RESET attribute_option
- ALTER COLUMN SET COMPRESSION
- ENABLE/DISABLE RULE
- ENABLE/DISABLE/FORCE/NO FORCE ROW LEVEL SECURITY
- REPLICA IDENTITY
- OWNER TO
- SET TABLESPACE
- SET SCHEMA
RENAME is intentionally excluded. Using ONLY (or not) has no effect for
RENAME, since relation names are independent by nature and there is no
expectation of recursion.
OF / NOT OF are also excluded. Using ONLY has no effect for these
commands, as they apply only to the partitioned table itself and not to
its partitions.
Regression tests are updated to use ONLY where appropriate.
Author: Chao Li <[email protected]>
Reviewed-by: David G. Johnston <[email protected]>
Reviewed-by: Greg Sabino Mullane <[email protected]>
Reviewed-by: Jim Jones <[email protected]>
Discussion: https://postgr.es/m/CAEoWx2=SLga-xH09Cq_PAvsHhQHrBK+V0vF821JKgzS=Bm0haA@mail.gmail.com
---
src/backend/commands/tablecmds.c | 138 ++++++++++++++++--
src/include/nodes/parsenodes.h | 1 +
src/test/regress/expected/alter_table.out | 84 ++++++++++-
src/test/regress/expected/cluster.out | 2 +-
src/test/regress/expected/merge.out | 4 +-
src/test/regress/expected/partition_merge.out | 2 +-
src/test/regress/expected/partition_split.out | 2 +-
src/test/regress/expected/privileges.out | 2 +-
src/test/regress/expected/rowsecurity.out | 4 +-
src/test/regress/expected/tablespace.out | 4 +-
src/test/regress/expected/update.out | 2 +-
src/test/regress/expected/vacuum.out | 6 +-
src/test/regress/sql/alter_table.sql | 64 +++++++-
src/test/regress/sql/cluster.sql | 2 +-
src/test/regress/sql/merge.sql | 4 +-
src/test/regress/sql/partition_merge.sql | 2 +-
src/test/regress/sql/partition_split.sql | 2 +-
src/test/regress/sql/privileges.sql | 2 +-
src/test/regress/sql/rowsecurity.sql | 4 +-
src/test/regress/sql/tablespace.sql | 4 +-
src/test/regress/sql/update.sql | 2 +-
src/test/regress/sql/vacuum.sql | 6 +-
22 files changed, 300 insertions(+), 43 deletions(-)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 85242dcc245..1e8ebde43e1 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -461,7 +461,8 @@ static void ATController(AlterTableStmt *parsetree,
AlterTableUtilityContext *context);
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
bool recurse, bool recursing, LOCKMODE lockmode,
- AlterTableUtilityContext *context);
+ AlterTableUtilityContext *context,
+ List **pendingNotice);
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
AlterTableUtilityContext *context);
static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
@@ -745,6 +746,10 @@ static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation
static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
Relation rel, PartitionCmd *cmd,
AlterTableUtilityContext *context);
+static void CollectPartitionNoRecurseNotice(AlterTableType cmdtype, Relation rel,
+ bool recurse, bool recursing,
+ List **pendingNotice);
+static void EmitPartitionNoRecurseNotice(List *pendingNotice);
/* ----------------------------------------------------------------
* DefineRelation
@@ -4886,13 +4891,14 @@ ATController(AlterTableStmt *parsetree,
{
List *wqueue = NIL;
ListCell *lcmd;
+ List *pendingNotice = NIL;
/* Phase 1: preliminary examination of commands, create work queue */
foreach(lcmd, cmds)
{
AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
- ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
+ ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context, &pendingNotice);
}
/* Close the relation, but keep lock until commit */
@@ -4903,6 +4909,9 @@ ATController(AlterTableStmt *parsetree,
/* Phase 3: scan/rewrite tables as needed, and run afterStmts */
ATRewriteTables(parsetree, &wqueue, lockmode, context);
+
+ /* Emit post-notice for partitions that were not recursed into. */
+ EmitPartitionNoRecurseNotice(pendingNotice);
}
/*
@@ -4917,7 +4926,8 @@ ATController(AlterTableStmt *parsetree,
static void
ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
bool recurse, bool recursing, LOCKMODE lockmode,
- AlterTableUtilityContext *context)
+ AlterTableUtilityContext *context,
+ List **pendingNotice)
{
AlteredTableInfo *tab;
AlterTablePass pass = AT_PASS_UNSET;
@@ -5066,6 +5076,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATT_MATVIEW | ATT_FOREIGN_TABLE);
/* This command never recurses */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
ATSimplePermissions(cmd->subtype, rel,
@@ -5081,6 +5093,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_DropColumn: /* DROP COLUMN */
ATSimplePermissions(cmd->subtype, rel,
@@ -5148,6 +5162,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_ClusterOn: /* CLUSTER ON */
case AT_DropCluster: /* SET WITHOUT CLUSTER */
@@ -5191,6 +5207,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
pass = AT_PASS_MISC; /* doesn't actually matter */
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_SetRelOptions: /* SET (...) */
case AT_ResetRelOptions: /* RESET (...) */
@@ -5200,6 +5218,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATT_MATVIEW | ATT_INDEX);
/* This command never recurses */
/* No command-specific prep needed */
+ /* It will check for partitioned table at exec time */
pass = AT_PASS_MISC;
break;
case AT_AddInherit: /* INHERIT */
@@ -5237,8 +5256,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATSimplePermissions(cmd->subtype, rel,
ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
pass = AT_PASS_MISC;
- /* This command never recurses */
- /* No command-specific prep needed */
+ /* This command doesn't recurse to partitions, so notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_EnableTrig: /* ENABLE TRIGGER variants */
case AT_EnableAlwaysTrig:
@@ -5259,17 +5278,30 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
case AT_EnableAlwaysRule:
case AT_EnableReplicaRule:
case AT_DisableRule:
- case AT_AddOf: /* OF */
- case AT_DropOf: /* NOT OF */
- case AT_EnableRowSecurity:
+ case AT_EnableRowSecurity: /* ENABLE/DISABLE ROW SECURITY variants */
case AT_DisableRowSecurity:
- case AT_ForceRowSecurity:
+ case AT_ForceRowSecurity: /* FORCE/NO FORCE ROW SECURITY variants */
case AT_NoForceRowSecurity:
ATSimplePermissions(cmd->subtype, rel,
ATT_TABLE | ATT_PARTITIONED_TABLE);
/* These commands never recurse */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
+ break;
+ case AT_AddOf: /* OF */
+ case AT_DropOf: /* NOT OF */
+ ATSimplePermissions(cmd->subtype, rel,
+ ATT_TABLE | ATT_PARTITIONED_TABLE);
+ /* These commands never recurse */
+ /* No command-specific prep needed */
+
+ /*
+ * They only work on partitioned tables but child partitions, thus
+ * no need to emit a notice
+ */
+ pass = AT_PASS_MISC;
break;
case AT_GenericOptions:
ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
@@ -6762,6 +6794,8 @@ alter_table_type_to_string(AlterTableType cmdtype)
return "ALTER COLUMN ... DROP IDENTITY";
case AT_ReAddStatistics:
return NULL; /* not real grammar */
+ case AT_SetSchema:
+ return "SET SCHEMA";
}
return NULL;
@@ -6883,7 +6917,7 @@ ATSimpleRecursion(List **wqueue, Relation rel,
/* find_all_inheritors already got lock */
childrel = relation_open(childrelid, NoLock);
CheckAlterTableIsSafe(childrel);
- ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
+ ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context, NULL);
relation_close(childrel, NoLock);
}
}
@@ -6946,7 +6980,7 @@ ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
childrel = relation_open(childrelid, lockmode);
CheckAlterTableIsSafe(childrel);
- ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
+ ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context, NULL);
relation_close(childrel, NoLock);
}
}
@@ -9592,7 +9626,7 @@ ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
newcmd->recurse = true;
newcmd->def = (Node *) nnconstr;
- ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
+ ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context, NULL);
}
}
@@ -14673,7 +14707,7 @@ ATPrepAlterColumnType(List **wqueue,
errdetail("USING expression contains a whole-row table reference.")));
pfree(attmap);
}
- ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
+ ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context, NULL);
relation_close(childrel, NoLock);
}
}
@@ -18977,6 +19011,7 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
RangeVar *newrv;
ObjectAddresses *objsMoved;
ObjectAddress myself;
+ List *pendingNotice = NIL;
relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
stmt->missing_ok ? RVR_MISSING_OK : 0,
@@ -18993,6 +19028,13 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
rel = relation_open(relid, NoLock);
+ /*
+ * SET SCHEMA doesn't recurse to children, emit a notice if ONLY is not
+ * specified. As this action doesn't go through ATPrepCmd, we have to emit
+ * the notice here.
+ */
+ CollectPartitionNoRecurseNotice(AT_SetSchema, rel, stmt->relation->inh, false, &pendingNotice);
+
oldNspOid = RelationGetNamespace(rel);
/* If it's an owned sequence, disallow moving it by itself. */
@@ -19030,6 +19072,8 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
/* close rel, but keep lock until commit */
relation_close(rel, NoLock);
+ EmitPartitionNoRecurseNotice(pendingNotice);
+
return myself;
}
@@ -23381,3 +23425,71 @@ ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
/* Restore the userid and security context. */
SetUserIdAndSecContext(save_userid, save_sec_context);
}
+
+/*
+ * When ONLY is not specified with a partitioned table, it is expected that the
+ * command recurses to all partitions. However, some sub-commands do not recurse.
+ * In such cases, emit a NOTICE to make this behavior explicit to the user.
+ */
+static void
+CollectPartitionNoRecurseNotice(AlterTableType cmdtype, Relation rel, bool recurse, bool recursing,
+ List **pendingNotice)
+{
+ if (pendingNotice == NULL)
+ return;
+
+ /* Only emit the notice at the top level of recursion */
+ if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && recurse && !recursing)
+ {
+ PartitionDesc pd = RelationGetPartitionDesc(rel, true);
+ int nparts = pd->nparts;
+ const char *action_str;
+ char *notice_msg;
+ const ListCell *cell;
+ bool found = false;
+
+ /* Emit a notice only if there are partitions */
+ if (nparts == 0)
+ return;
+
+ action_str = alter_table_type_to_string(cmdtype);
+ notice_msg = psprintf(_("ALTER action %s on relation \"%s\" does not affect present partitions"),
+ action_str,
+ RelationGetRelationName(rel));
+
+ foreach(cell, *pendingNotice)
+ {
+ if (strcmp((char *) lfirst(cell), notice_msg) == 0)
+ {
+ pfree(notice_msg);
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ *pendingNotice = lappend(*pendingNotice, notice_msg);
+ }
+}
+
+static void
+EmitPartitionNoRecurseNotice(List *pendingNotice)
+{
+ ListCell *cell;
+ int len;
+ int i = 0;
+
+ len = list_length(pendingNotice);
+ foreach(cell, pendingNotice)
+ {
+ char *notice_msg = (char *) lfirst(cell);
+
+ /* Only emit the hint for the last notice */
+ i++;
+ ereport(NOTICE, errmsg("%s", notice_msg),
+ (i == len) ?
+ errhint("Partitions may be modified individually, or specify ONLY to suppress this message.")
+ : 0);
+ pfree(notice_msg);
+ }
+ list_free(pendingNotice);
+}
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index f3d32ef0188..1267fb59d3f 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2511,6 +2511,7 @@ typedef enum AlterTableType
AT_SetIdentity, /* SET identity column options */
AT_DropIdentity, /* DROP IDENTITY */
AT_ReAddStatistics, /* internal to commands/tablecmds.c */
+ AT_SetSchema, /* SET SCHEMA */
} AlterTableType;
typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 5998c670aa3..bbb34bee938 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -4582,8 +4582,90 @@ SELECT * FROM list_parted;
---
(0 rows)
+CREATE TABLE list_parted4 (a int, b text) PARTITION BY LIST (a);
+CREATE TABLE list_parted4_1 PARTITION OF list_parted4 FOR VALUES IN (1);
+-- set column attribute on partitioned table without ONLY should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+NOTICE: ALTER action ALTER COLUMN ... SET on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE list_parted4 ALTER COLUMN b RESET (n_distinct);
+NOTICE: ALTER action ALTER COLUMN ... RESET on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b RESET (n_distinct);
+-- enable/disable rules on partitioned tables without ONLY should get a notice
+CREATE RULE list_parted4_rule AS ON INSERT TO list_parted4 DO INSTEAD NOTHING;
+ALTER TABLE list_parted4 DISABLE RULE list_parted4_rule;
+NOTICE: ALTER action DISABLE RULE on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 DISABLE RULE list_parted4_rule;
+ALTER TABLE list_parted4 ENABLE RULE list_parted4_rule;
+NOTICE: ALTER action ENABLE RULE on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ENABLE RULE list_parted4_rule;
+DROP RULE list_parted4_rule ON list_parted4;
+-- enable/disable row level security on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 ENABLE ROW LEVEL SECURITY;
+NOTICE: ALTER action ENABLE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 DISABLE ROW LEVEL SECURITY;
+NOTICE: ALTER action DISABLE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 DISABLE ROW LEVEL SECURITY;
+-- force/no force row level security on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 FORCE ROW LEVEL SECURITY;
+NOTICE: ALTER action FORCE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 FORCE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 NO FORCE ROW LEVEL SECURITY;
+NOTICE: ALTER action NO FORCE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 NO FORCE ROW LEVEL SECURITY;
+-- set replica identity on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 REPLICA IDENTITY FULL;
+NOTICE: ALTER action REPLICA IDENTITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY FULL;
+ALTER TABLE list_parted4 REPLICA IDENTITY NOTHING;
+NOTICE: ALTER action REPLICA IDENTITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY NOTHING;
+-- set compression on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+NOTICE: ALTER action ALTER COLUMN ... SET COMPRESSION on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+-- set owner on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 OWNER TO regress_alter_table_user1;
+NOTICE: ALTER action OWNER TO on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 OWNER TO regress_alter_table_user1;
+-- set schema on partitioned tables without ONLY should get a notice
+CREATE SCHEMA alter_table_test_schema;
+ALTER TABLE list_parted4 SET SCHEMA alter_table_test_schema;
+NOTICE: ALTER action SET SCHEMA on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY alter_table_test_schema.list_parted4 SET SCHEMA public;
+DROP SCHEMA alter_table_test_schema CASCADE;
+-- when there are multiple sub-command, notice should not duplicated
+ALTER TABLE list_parted4
+ ALTER COLUMN b SET COMPRESSION pglz,
+ ALTER COLUMN b SET COMPRESSION pglz, -- duplicate, but should not get duplicate notice
+ FORCE ROW LEVEL SECURITY,
+ REPLICA IDENTITY FULL,
+ OWNER TO regress_alter_table_user1;
+NOTICE: ALTER action ALTER COLUMN ... SET COMPRESSION on relation "list_parted4" does not affect present partitions
+NOTICE: ALTER action FORCE ROW SECURITY on relation "list_parted4" does not affect present partitions
+NOTICE: ALTER action REPLICA IDENTITY on relation "list_parted4" does not affect present partitions
+NOTICE: ALTER action OWNER TO on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+-- list_parted5 is a partitioned table that has no partition.
+CREATE TABLE list_parted5 (a int, b text) PARTITION BY LIST (a);
+-- as it has no partition, there should be no notice when altering it without ONLY
+ALTER TABLE list_parted5 FORCE ROW LEVEL SECURITY;
-- cleanup
-DROP TABLE list_parted, list_parted2, range_parted, list_parted3;
+DROP TABLE list_parted, list_parted2, range_parted, list_parted3, list_parted4, list_parted5;
DROP TABLE fail_def_part;
DROP TABLE hash_parted;
-- more tests for certain multi-level partitioning scenarios
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 24b0b1a8fce..67d7ef6509d 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -547,7 +547,7 @@ SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
RESET SESSION AUTHORIZATION;
-ALTER TABLE ptnowner OWNER TO regress_ptnowner;
+ALTER TABLE ONLY ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
SELECT oid, relname, relfilenode FROM pg_partition_tree('ptnowner') AS tree
JOIN pg_class AS c ON c.oid=tree.relid;
diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out
index 9cb1d87066a..bd6aac72336 100644
--- a/src/test/regress/expected/merge.out
+++ b/src/test/regress/expected/merge.out
@@ -2298,8 +2298,8 @@ SELECT * FROM pa_target ORDER BY tid, val;
ROLLBACK;
-- test RLS enforcement
BEGIN;
-ALTER TABLE pa_target ENABLE ROW LEVEL SECURITY;
-ALTER TABLE pa_target FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target FORCE ROW LEVEL SECURITY;
CREATE POLICY pa_target_pol ON pa_target USING (tid != 0);
MERGE INTO pa_target t
USING pa_source s
diff --git a/src/test/regress/expected/partition_merge.out b/src/test/regress/expected/partition_merge.out
index 925fe4f570a..f629fc7f3e5 100644
--- a/src/test/regress/expected/partition_merge.out
+++ b/src/test/regress/expected/partition_merge.out
@@ -810,7 +810,7 @@ SET SESSION AUTHORIZATION regress_partition_merge_bob;
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
ERROR: must be owner of table t
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_merge_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_merge_bob;
SET SESSION AUTHORIZATION regress_partition_merge_bob;
-- ERROR: must be owner of table tp_0_1
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
diff --git a/src/test/regress/expected/partition_split.out b/src/test/regress/expected/partition_split.out
index 4004efe0dac..421fa36ec0f 100644
--- a/src/test/regress/expected/partition_split.out
+++ b/src/test/regress/expected/partition_split.out
@@ -1372,7 +1372,7 @@ ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); --error
ERROR: must be owner of table t
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_split_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_split_bob;
SET SESSION AUTHORIZATION regress_partition_split_bob;
ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
(PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 7bc274566c3..877a1214963 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -1908,7 +1908,7 @@ SELECT brin_summarize_range('sro_brin', 0);
DROP TABLE sro_tab;
-- Check with a partitioned table
CREATE TABLE sro_ptab (a int) PARTITION BY RANGE (a);
-ALTER TABLE sro_ptab OWNER TO regress_sro_user;
+ALTER TABLE ONLY sro_ptab OWNER TO regress_sro_user;
CREATE TABLE sro_part PARTITION OF sro_ptab FOR VALUES FROM (1) TO (10);
ALTER TABLE sro_part OWNER TO regress_sro_user;
INSERT INTO sro_ptab VALUES (1), (2), (3);
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index 07d93e7def1..639c0065b0c 100644
--- a/src/test/regress/expected/rowsecurity.out
+++ b/src/test/regress/expected/rowsecurity.out
@@ -1233,7 +1233,7 @@ INSERT INTO part_document VALUES
( 8, 55, 2, 'regress_rls_carol', 'great satire'),
( 9, 11, 1, 'regress_rls_dave', 'awesome science fiction'),
(10, 99, 2, 'regress_rls_dave', 'awesome technology book');
-ALTER TABLE part_document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY part_document ENABLE ROW LEVEL SECURITY;
-- Create policy on parent
-- user's security level must be higher than or equal to document's
CREATE POLICY pp1 ON part_document AS PERMISSIVE
@@ -4914,7 +4914,7 @@ CREATE TABLE rls_ptbl (a int) PARTITION BY RANGE (a);
CREATE TABLE rls_part PARTITION OF rls_ptbl FOR VALUES FROM (-100) TO (100);
INSERT INTO rls_ptbl SELECT x/10 FROM generate_series(1, 100) x;
ANALYZE rls_ptbl, rls_part;
-ALTER TABLE rls_ptbl ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY rls_ptbl ENABLE ROW LEVEL SECURITY;
ALTER TABLE rls_part ENABLE ROW LEVEL SECURITY;
GRANT SELECT ON rls_ptbl TO regress_rls_alice;
GRANT SELECT ON rls_part TO regress_rls_alice;
diff --git a/src/test/regress/expected/tablespace.out b/src/test/regress/expected/tablespace.out
index 185880a3217..bf437a92746 100644
--- a/src/test/regress/expected/tablespace.out
+++ b/src/test/regress/expected/tablespace.out
@@ -290,13 +290,13 @@ CREATE TABLE testschema.part_2 PARTITION OF testschema.part FOR VALUES IN (2);
SET default_tablespace TO pg_global;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
ERROR: only shared relations can be placed in pg_global tablespace
-ALTER TABLE testschema.part SET TABLESPACE regress_tblspace;
+ALTER TABLE ONLY testschema.part SET TABLESPACE regress_tblspace;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
CREATE TABLE testschema.part_4 PARTITION OF testschema.part FOR VALUES IN (4)
TABLESPACE pg_default;
CREATE TABLE testschema.part_56 PARTITION OF testschema.part FOR VALUES IN (5, 6)
PARTITION BY LIST (a);
-ALTER TABLE testschema.part SET TABLESPACE pg_default;
+ALTER TABLE ONLY testschema.part SET TABLESPACE pg_default;
CREATE TABLE testschema.part_78 PARTITION OF testschema.part FOR VALUES IN (7, 8)
PARTITION BY LIST (a);
ERROR: only shared relations can be placed in pg_global tablespace
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index eef2bac1cbf..584b40598eb 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -607,7 +607,7 @@ DROP TRIGGER trig_d15_20 ON part_d_15_20;
DROP FUNCTION func_parted_mod_b();
-- RLS policies with update-row-movement
-----------------------------------------
-ALTER TABLE range_parted ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY range_parted ENABLE ROW LEVEL SECURITY;
CREATE USER regress_range_parted_user;
GRANT ALL ON range_parted, mintab TO regress_range_parted_user;
CREATE POLICY seeall ON range_parted AS PERMISSIVE FOR SELECT USING (true);
diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out
index d4696bc3325..f22bb2127e1 100644
--- a/src/test/regress/expected/vacuum.out
+++ b/src/test/regress/expected/vacuum.out
@@ -614,7 +614,7 @@ VACUUM (ANALYZE) vacowned_part2;
WARNING: permission denied to vacuum "vacowned_part2", skipping it
RESET ROLE;
-- Partitioned table and one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO regress_vacuum;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
@@ -634,7 +634,7 @@ VACUUM (ANALYZE) vacowned_part2;
WARNING: permission denied to vacuum "vacowned_part2", skipping it
RESET ROLE;
-- Only one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO CURRENT_USER;
+ALTER TABLE ONLY vacowned_parted OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
WARNING: permission denied to vacuum "vacowned_parted", skipping it
@@ -656,7 +656,7 @@ VACUUM (ANALYZE) vacowned_part2;
WARNING: permission denied to vacuum "vacowned_part2", skipping it
RESET ROLE;
-- Only partitioned table owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index d6b6381ae5c..97f641c78da 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2908,8 +2908,70 @@ ALTER TABLE list_parted2 ALTER COLUMN b TYPE text;
ALTER TABLE list_parted DROP COLUMN b;
SELECT * FROM list_parted;
+CREATE TABLE list_parted4 (a int, b text) PARTITION BY LIST (a);
+CREATE TABLE list_parted4_1 PARTITION OF list_parted4 FOR VALUES IN (1);
+
+-- set column attribute on partitioned table without ONLY should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+ALTER TABLE list_parted4 ALTER COLUMN b RESET (n_distinct);
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b RESET (n_distinct);
+
+-- enable/disable rules on partitioned tables without ONLY should get a notice
+CREATE RULE list_parted4_rule AS ON INSERT TO list_parted4 DO INSTEAD NOTHING;
+ALTER TABLE list_parted4 DISABLE RULE list_parted4_rule;
+ALTER TABLE ONLY list_parted4 DISABLE RULE list_parted4_rule;
+ALTER TABLE list_parted4 ENABLE RULE list_parted4_rule;
+ALTER TABLE ONLY list_parted4 ENABLE RULE list_parted4_rule;
+DROP RULE list_parted4_rule ON list_parted4;
+
+-- enable/disable row level security on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 DISABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 DISABLE ROW LEVEL SECURITY;
+
+-- force/no force row level security on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 FORCE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 NO FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 NO FORCE ROW LEVEL SECURITY;
+
+-- set replica identity on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 REPLICA IDENTITY FULL;
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY FULL;
+ALTER TABLE list_parted4 REPLICA IDENTITY NOTHING;
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY NOTHING;
+
+-- set compression on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+
+-- set owner on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 OWNER TO regress_alter_table_user1;
+ALTER TABLE ONLY list_parted4 OWNER TO regress_alter_table_user1;
+
+-- set schema on partitioned tables without ONLY should get a notice
+CREATE SCHEMA alter_table_test_schema;
+ALTER TABLE list_parted4 SET SCHEMA alter_table_test_schema;
+ALTER TABLE ONLY alter_table_test_schema.list_parted4 SET SCHEMA public;
+DROP SCHEMA alter_table_test_schema CASCADE;
+
+-- when there are multiple sub-command, notice should not duplicated
+ALTER TABLE list_parted4
+ ALTER COLUMN b SET COMPRESSION pglz,
+ ALTER COLUMN b SET COMPRESSION pglz, -- duplicate, but should not get duplicate notice
+ FORCE ROW LEVEL SECURITY,
+ REPLICA IDENTITY FULL,
+ OWNER TO regress_alter_table_user1;
+
+-- list_parted5 is a partitioned table that has no partition.
+CREATE TABLE list_parted5 (a int, b text) PARTITION BY LIST (a);
+-- as it has no partition, there should be no notice when altering it without ONLY
+ALTER TABLE list_parted5 FORCE ROW LEVEL SECURITY;
+
-- cleanup
-DROP TABLE list_parted, list_parted2, range_parted, list_parted3;
+DROP TABLE list_parted, list_parted2, range_parted, list_parted3, list_parted4, list_parted5;
DROP TABLE fail_def_part;
DROP TABLE hash_parted;
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index f90c6ec200b..3405e93ab0c 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -260,7 +260,7 @@ ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
RESET SESSION AUTHORIZATION;
-ALTER TABLE ptnowner OWNER TO regress_ptnowner;
+ALTER TABLE ONLY ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
SELECT oid, relname, relfilenode FROM pg_partition_tree('ptnowner') AS tree
JOIN pg_class AS c ON c.oid=tree.relid;
diff --git a/src/test/regress/sql/merge.sql b/src/test/regress/sql/merge.sql
index 2660b19f238..2bffcf7aa98 100644
--- a/src/test/regress/sql/merge.sql
+++ b/src/test/regress/sql/merge.sql
@@ -1422,8 +1422,8 @@ ROLLBACK;
-- test RLS enforcement
BEGIN;
-ALTER TABLE pa_target ENABLE ROW LEVEL SECURITY;
-ALTER TABLE pa_target FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target FORCE ROW LEVEL SECURITY;
CREATE POLICY pa_target_pol ON pa_target USING (tid != 0);
MERGE INTO pa_target t
USING pa_source s
diff --git a/src/test/regress/sql/partition_merge.sql b/src/test/regress/sql/partition_merge.sql
index a211fee2ad1..73bffb530dd 100644
--- a/src/test/regress/sql/partition_merge.sql
+++ b/src/test/regress/sql/partition_merge.sql
@@ -571,7 +571,7 @@ SET SESSION AUTHORIZATION regress_partition_merge_bob;
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_merge_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_merge_bob;
SET SESSION AUTHORIZATION regress_partition_merge_bob;
-- ERROR: must be owner of table tp_0_1
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
diff --git a/src/test/regress/sql/partition_split.sql b/src/test/regress/sql/partition_split.sql
index 37c6d730840..f4f9c7886e4 100644
--- a/src/test/regress/sql/partition_split.sql
+++ b/src/test/regress/sql/partition_split.sql
@@ -960,7 +960,7 @@ ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); --error
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_split_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_split_bob;
SET SESSION AUTHORIZATION regress_partition_split_bob;
ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
(PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index 540f73ea9b1..eb73629a81f 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -1230,7 +1230,7 @@ SELECT brin_summarize_range('sro_brin', 0);
DROP TABLE sro_tab;
-- Check with a partitioned table
CREATE TABLE sro_ptab (a int) PARTITION BY RANGE (a);
-ALTER TABLE sro_ptab OWNER TO regress_sro_user;
+ALTER TABLE ONLY sro_ptab OWNER TO regress_sro_user;
CREATE TABLE sro_part PARTITION OF sro_ptab FOR VALUES FROM (1) TO (10);
ALTER TABLE sro_part OWNER TO regress_sro_user;
INSERT INTO sro_ptab VALUES (1), (2), (3);
diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql
index 6b3566271df..1a2e80dade0 100644
--- a/src/test/regress/sql/rowsecurity.sql
+++ b/src/test/regress/sql/rowsecurity.sql
@@ -499,7 +499,7 @@ INSERT INTO part_document VALUES
( 9, 11, 1, 'regress_rls_dave', 'awesome science fiction'),
(10, 99, 2, 'regress_rls_dave', 'awesome technology book');
-ALTER TABLE part_document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY part_document ENABLE ROW LEVEL SECURITY;
-- Create policy on parent
-- user's security level must be higher than or equal to document's
@@ -2414,7 +2414,7 @@ CREATE TABLE rls_part PARTITION OF rls_ptbl FOR VALUES FROM (-100) TO (100);
INSERT INTO rls_ptbl SELECT x/10 FROM generate_series(1, 100) x;
ANALYZE rls_ptbl, rls_part;
-ALTER TABLE rls_ptbl ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY rls_ptbl ENABLE ROW LEVEL SECURITY;
ALTER TABLE rls_part ENABLE ROW LEVEL SECURITY;
GRANT SELECT ON rls_ptbl TO regress_rls_alice;
GRANT SELECT ON rls_part TO regress_rls_alice;
diff --git a/src/test/regress/sql/tablespace.sql b/src/test/regress/sql/tablespace.sql
index c43a59e5957..8eff2914a5b 100644
--- a/src/test/regress/sql/tablespace.sql
+++ b/src/test/regress/sql/tablespace.sql
@@ -195,13 +195,13 @@ SET default_tablespace TO regress_tblspace;
CREATE TABLE testschema.part_2 PARTITION OF testschema.part FOR VALUES IN (2);
SET default_tablespace TO pg_global;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
-ALTER TABLE testschema.part SET TABLESPACE regress_tblspace;
+ALTER TABLE ONLY testschema.part SET TABLESPACE regress_tblspace;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
CREATE TABLE testschema.part_4 PARTITION OF testschema.part FOR VALUES IN (4)
TABLESPACE pg_default;
CREATE TABLE testschema.part_56 PARTITION OF testschema.part FOR VALUES IN (5, 6)
PARTITION BY LIST (a);
-ALTER TABLE testschema.part SET TABLESPACE pg_default;
+ALTER TABLE ONLY testschema.part SET TABLESPACE pg_default;
CREATE TABLE testschema.part_78 PARTITION OF testschema.part FOR VALUES IN (7, 8)
PARTITION BY LIST (a);
CREATE TABLE testschema.part_910 PARTITION OF testschema.part FOR VALUES IN (9, 10)
diff --git a/src/test/regress/sql/update.sql b/src/test/regress/sql/update.sql
index 8b4707eb9c3..56b1edb00ce 100644
--- a/src/test/regress/sql/update.sql
+++ b/src/test/regress/sql/update.sql
@@ -341,7 +341,7 @@ DROP FUNCTION func_parted_mod_b();
-- RLS policies with update-row-movement
-----------------------------------------
-ALTER TABLE range_parted ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY range_parted ENABLE ROW LEVEL SECURITY;
CREATE USER regress_range_parted_user;
GRANT ALL ON range_parted, mintab TO regress_range_parted_user;
CREATE POLICY seeall ON range_parted AS PERMISSIVE FOR SELECT USING (true);
diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql
index 247b8e23b23..acff5824ebb 100644
--- a/src/test/regress/sql/vacuum.sql
+++ b/src/test/regress/sql/vacuum.sql
@@ -452,7 +452,7 @@ VACUUM (ANALYZE) vacowned_part1;
VACUUM (ANALYZE) vacowned_part2;
RESET ROLE;
-- Partitioned table and one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO regress_vacuum;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
@@ -466,7 +466,7 @@ VACUUM (ANALYZE) vacowned_part1;
VACUUM (ANALYZE) vacowned_part2;
RESET ROLE;
-- Only one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO CURRENT_USER;
+ALTER TABLE ONLY vacowned_parted OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
VACUUM vacowned_part1;
@@ -479,7 +479,7 @@ VACUUM (ANALYZE) vacowned_part1;
VACUUM (ANALYZE) vacowned_part2;
RESET ROLE;
-- Only partitioned table owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
--
2.50.1 (Apple Git-155)
^ permalink raw reply [nested|flat] 19+ messages in thread
* Re: ALTER TABLE: warn when actions do not recurse to partitions
2026-01-13 11:16 Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-14 00:52 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 05:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 19:27 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-23 00:11 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-23 09:57 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-03-09 06:46 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-10 15:32 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-11 07:04 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
@ 2026-03-11 16:39 ` Greg Sabino Mullane <[email protected]>
2026-03-12 01:14 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
0 siblings, 1 reply; 19+ messages in thread
From: Greg Sabino Mullane @ 2026-03-11 16:39 UTC (permalink / raw)
To: Chao Li <[email protected]>; +Cc: Jim Jones <[email protected]>; David G. Johnston <[email protected]>; Postgres hackers <[email protected]>
On Wed, Mar 11, 2026 at 3:05 AM Chao Li <[email protected]> wrote:
> Are you concerning that rendering the full message text is the extra work?
> This is not a hot path, so I don’t think that would be a big deal.
> Actually, adding two more fields sounds more expensive
Well, the recurring creation and freeing of the strings is the part that
seems inefficient. But you don't even need to store the strings at all if
you are tracking the action+rel. In such a case, the final strings can be
created on the fly inside of EmitPartitionNoRecurseNotice, right? Then you
just need a list to store the combos of action+relation.
Yes, as SET SCHEMA doesn’t go through the standard ALTER TABLE process:
> AlterTable() -> ATController() -> ATPrepCmd().
>
> If you get some idea, please let me know.
>
Nothing worth the trouble, to be honest. As you rightly pointed out, this
is not a hot path.
Cheers,
Greg
^ permalink raw reply [nested|flat] 19+ messages in thread
* Re: ALTER TABLE: warn when actions do not recurse to partitions
2026-01-13 11:16 Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-14 00:52 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 05:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 19:27 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-23 00:11 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-23 09:57 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-03-09 06:46 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-10 15:32 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-11 07:04 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-11 16:39 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
@ 2026-03-12 01:14 ` Chao Li <[email protected]>
2026-03-12 02:09 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
0 siblings, 1 reply; 19+ messages in thread
From: Chao Li @ 2026-03-12 01:14 UTC (permalink / raw)
To: Greg Sabino Mullane <[email protected]>; +Cc: Jim Jones <[email protected]>; David G. Johnston <[email protected]>; Postgres hackers <[email protected]>
> On Mar 12, 2026, at 00:39, Greg Sabino Mullane <[email protected]> wrote:
>
> On Wed, Mar 11, 2026 at 3:05 AM Chao Li <[email protected]> wrote:
> Are you concerning that rendering the full message text is the extra work? This is not a hot path, so I don’t think that would be a big deal. Actually, adding two more fields sounds more expensive
>
> Well, the recurring creation and freeing of the strings is the part that seems inefficient. But you don't even need to store the strings at all if you are tracking the action+rel. In such a case, the final strings can be created on the fly inside of EmitPartitionNoRecurseNotice, right? Then you just need a list to store the combos of action+relation.
>
Fully understood your point. My considerations are:
* This is not on a hot path, and that’s a very trivial performance impact.
* I would believe in most of use cases, ALTER TABLE won’t take duplicate sub-commands, thus duplicated messages should rarely happen.
* If we take your approach, actually, we don’t have to store action+relation in the list, only action is okay. But, if we defer building the notice message to EmitPartitionNoRecurseNotice, then we have to leave relation open till EmitPartitionNoRecurseNotice, which feels a worse burden. Looking at ATController(), rel is closed early. An alternative is to store the relation name in a temp variable, which also introduces extra code. As a trade-off, I think building the notice message in CollectPartitionNoRecurseNotice() is an easy implementation.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
^ permalink raw reply [nested|flat] 19+ messages in thread
* Re: ALTER TABLE: warn when actions do not recurse to partitions
2026-01-13 11:16 Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-14 00:52 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 05:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 19:27 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-23 00:11 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-23 09:57 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-03-09 06:46 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-10 15:32 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-11 07:04 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-11 16:39 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-12 01:14 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
@ 2026-03-12 02:09 ` Greg Sabino Mullane <[email protected]>
2026-03-12 06:37 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
0 siblings, 1 reply; 19+ messages in thread
From: Greg Sabino Mullane @ 2026-03-12 02:09 UTC (permalink / raw)
To: Chao Li <[email protected]>; +Cc: Jim Jones <[email protected]>; David G. Johnston <[email protected]>; Postgres hackers <[email protected]>
Okay, that's all fine with me, thank you for the explanations!
One more small idea: lose the bool found = false, and simply return; on a
matching strcmp
Cheers,
Greg
^ permalink raw reply [nested|flat] 19+ messages in thread
* Re: ALTER TABLE: warn when actions do not recurse to partitions
2026-01-13 11:16 Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-14 00:52 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 05:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 19:27 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-23 00:11 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-23 09:57 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-03-09 06:46 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-10 15:32 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-11 07:04 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-11 16:39 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-12 01:14 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-12 02:09 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
@ 2026-03-12 06:37 ` Chao Li <[email protected]>
2026-03-12 12:10 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-12 21:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Zsolt Parragi <[email protected]>
0 siblings, 2 replies; 19+ messages in thread
From: Chao Li @ 2026-03-12 06:37 UTC (permalink / raw)
To: Greg Sabino Mullane <[email protected]>; +Cc: Jim Jones <[email protected]>; David G. Johnston <[email protected]>; Postgres hackers <[email protected]>
> On Mar 12, 2026, at 10:09, Greg Sabino Mullane <[email protected]> wrote:
>
> Okay, that's all fine with me, thank you for the explanations!
>
> One more small idea: lose the bool found = false, and simply return; on a matching strcmp
>
Make sense. PFA v8.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
Attachments:
[application/octet-stream] v8-0001-ALTER-TABLE-emit-NOTICE-when-ONLY-is-omitted-but-.patch (39.5K, 2-v8-0001-ALTER-TABLE-emit-NOTICE-when-ONLY-is-omitted-but-.patch)
download | inline diff:
From 80c265e80316af76b10195c4fdf0129e9516397e Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <[email protected]>
Date: Mon, 12 Jan 2026 16:56:58 +0800
Subject: [PATCH v8] ALTER TABLE: emit NOTICE when ONLY is omitted but no
partition recursion occurs
Some ALTER TABLE sub-commands do not recurse to partitions even when ONLY
is not specified on a partitioned table. This can be surprising, since
the default expectation is that commands propagate to all partitions.
Emit a NOTICE in such cases to make the behavior explicit, and advise how
to suppress the message or modify partitions individually.
Affected sub-commands include:
- ALTER COLUMN SET/RESET attribute_option
- ALTER COLUMN SET COMPRESSION
- ENABLE/DISABLE RULE
- ENABLE/DISABLE/FORCE/NO FORCE ROW LEVEL SECURITY
- REPLICA IDENTITY
- OWNER TO
- SET TABLESPACE
- SET SCHEMA
RENAME is intentionally excluded. Using ONLY (or not) has no effect for
RENAME, since relation names are independent by nature and there is no
expectation of recursion.
OF / NOT OF are also excluded. Using ONLY has no effect for these
commands, as they apply only to the partitioned table itself and not to
its partitions.
Regression tests are updated to use ONLY where appropriate.
Author: Chao Li <[email protected]>
Reviewed-by: David G. Johnston <[email protected]>
Reviewed-by: Greg Sabino Mullane <[email protected]>
Reviewed-by: Jim Jones <[email protected]>
Discussion: https://postgr.es/m/CAEoWx2=SLga-xH09Cq_PAvsHhQHrBK+V0vF821JKgzS=Bm0haA@mail.gmail.com
---
src/backend/commands/tablecmds.c | 136 ++++++++++++++++--
src/include/nodes/parsenodes.h | 1 +
src/test/regress/expected/alter_table.out | 84 ++++++++++-
src/test/regress/expected/cluster.out | 2 +-
src/test/regress/expected/merge.out | 4 +-
src/test/regress/expected/partition_merge.out | 2 +-
src/test/regress/expected/partition_split.out | 2 +-
src/test/regress/expected/privileges.out | 2 +-
src/test/regress/expected/rowsecurity.out | 4 +-
src/test/regress/expected/tablespace.out | 4 +-
src/test/regress/expected/update.out | 2 +-
src/test/regress/expected/vacuum.out | 6 +-
src/test/regress/sql/alter_table.sql | 64 ++++++++-
src/test/regress/sql/cluster.sql | 2 +-
src/test/regress/sql/merge.sql | 4 +-
src/test/regress/sql/partition_merge.sql | 2 +-
src/test/regress/sql/partition_split.sql | 2 +-
src/test/regress/sql/privileges.sql | 2 +-
src/test/regress/sql/rowsecurity.sql | 4 +-
src/test/regress/sql/tablespace.sql | 4 +-
src/test/regress/sql/update.sql | 2 +-
src/test/regress/sql/vacuum.sql | 6 +-
22 files changed, 298 insertions(+), 43 deletions(-)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index a7c32679b22..4001ed41974 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -469,7 +469,8 @@ static void ATController(AlterTableStmt *parsetree,
AlterTableUtilityContext *context);
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
bool recurse, bool recursing, LOCKMODE lockmode,
- AlterTableUtilityContext *context);
+ AlterTableUtilityContext *context,
+ List **pendingNotice);
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
AlterTableUtilityContext *context);
static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
@@ -753,6 +754,10 @@ static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation
static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
Relation rel, PartitionCmd *cmd,
AlterTableUtilityContext *context);
+static void CollectPartitionNoRecurseNotice(AlterTableType cmdtype, Relation rel,
+ bool recurse, bool recursing,
+ List **pendingNotice);
+static void EmitPartitionNoRecurseNotice(List *pendingNotice);
/* ----------------------------------------------------------------
* DefineRelation
@@ -4894,13 +4899,14 @@ ATController(AlterTableStmt *parsetree,
{
List *wqueue = NIL;
ListCell *lcmd;
+ List *pendingNotice = NIL;
/* Phase 1: preliminary examination of commands, create work queue */
foreach(lcmd, cmds)
{
AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
- ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
+ ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context, &pendingNotice);
}
/* Close the relation, but keep lock until commit */
@@ -4911,6 +4917,9 @@ ATController(AlterTableStmt *parsetree,
/* Phase 3: scan/rewrite tables as needed, and run afterStmts */
ATRewriteTables(parsetree, &wqueue, lockmode, context);
+
+ /* Emit post-notice for partitions that were not recursed into. */
+ EmitPartitionNoRecurseNotice(pendingNotice);
}
/*
@@ -4925,7 +4934,8 @@ ATController(AlterTableStmt *parsetree,
static void
ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
bool recurse, bool recursing, LOCKMODE lockmode,
- AlterTableUtilityContext *context)
+ AlterTableUtilityContext *context,
+ List **pendingNotice)
{
AlteredTableInfo *tab;
AlterTablePass pass = AT_PASS_UNSET;
@@ -5074,6 +5084,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATT_MATVIEW | ATT_FOREIGN_TABLE);
/* This command never recurses */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
ATSimplePermissions(cmd->subtype, rel,
@@ -5089,6 +5101,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_DropColumn: /* DROP COLUMN */
ATSimplePermissions(cmd->subtype, rel,
@@ -5156,6 +5170,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_ClusterOn: /* CLUSTER ON */
case AT_DropCluster: /* SET WITHOUT CLUSTER */
@@ -5199,6 +5215,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
pass = AT_PASS_MISC; /* doesn't actually matter */
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_SetRelOptions: /* SET (...) */
case AT_ResetRelOptions: /* RESET (...) */
@@ -5208,6 +5226,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATT_MATVIEW | ATT_INDEX);
/* This command never recurses */
/* No command-specific prep needed */
+ /* It will check for partitioned table at exec time */
pass = AT_PASS_MISC;
break;
case AT_AddInherit: /* INHERIT */
@@ -5245,8 +5264,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATSimplePermissions(cmd->subtype, rel,
ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
pass = AT_PASS_MISC;
- /* This command never recurses */
- /* No command-specific prep needed */
+ /* This command doesn't recurse to partitions, so notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_EnableTrig: /* ENABLE TRIGGER variants */
case AT_EnableAlwaysTrig:
@@ -5267,17 +5286,30 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
case AT_EnableAlwaysRule:
case AT_EnableReplicaRule:
case AT_DisableRule:
- case AT_AddOf: /* OF */
- case AT_DropOf: /* NOT OF */
- case AT_EnableRowSecurity:
+ case AT_EnableRowSecurity: /* ENABLE/DISABLE ROW SECURITY variants */
case AT_DisableRowSecurity:
- case AT_ForceRowSecurity:
+ case AT_ForceRowSecurity: /* FORCE/NO FORCE ROW SECURITY variants */
case AT_NoForceRowSecurity:
ATSimplePermissions(cmd->subtype, rel,
ATT_TABLE | ATT_PARTITIONED_TABLE);
/* These commands never recurse */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
+ break;
+ case AT_AddOf: /* OF */
+ case AT_DropOf: /* NOT OF */
+ ATSimplePermissions(cmd->subtype, rel,
+ ATT_TABLE | ATT_PARTITIONED_TABLE);
+ /* These commands never recurse */
+ /* No command-specific prep needed */
+
+ /*
+ * They only work on partitioned tables but child partitions, thus
+ * no need to emit a notice
+ */
+ pass = AT_PASS_MISC;
break;
case AT_GenericOptions:
ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
@@ -6770,6 +6802,8 @@ alter_table_type_to_string(AlterTableType cmdtype)
return "ALTER COLUMN ... DROP IDENTITY";
case AT_ReAddStatistics:
return NULL; /* not real grammar */
+ case AT_SetSchema:
+ return "SET SCHEMA";
}
return NULL;
@@ -6891,7 +6925,7 @@ ATSimpleRecursion(List **wqueue, Relation rel,
/* find_all_inheritors already got lock */
childrel = relation_open(childrelid, NoLock);
CheckAlterTableIsSafe(childrel);
- ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
+ ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context, NULL);
relation_close(childrel, NoLock);
}
}
@@ -6954,7 +6988,7 @@ ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
childrel = relation_open(childrelid, lockmode);
CheckAlterTableIsSafe(childrel);
- ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
+ ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context, NULL);
relation_close(childrel, NoLock);
}
}
@@ -9600,7 +9634,7 @@ ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
newcmd->recurse = true;
newcmd->def = (Node *) nnconstr;
- ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
+ ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context, NULL);
}
}
@@ -14848,7 +14882,7 @@ ATPrepAlterColumnType(List **wqueue,
errdetail("USING expression contains a whole-row table reference.")));
pfree(attmap);
}
- ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
+ ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context, NULL);
relation_close(childrel, NoLock);
}
}
@@ -19152,6 +19186,7 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
RangeVar *newrv;
ObjectAddresses *objsMoved;
ObjectAddress myself;
+ List *pendingNotice = NIL;
relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
stmt->missing_ok ? RVR_MISSING_OK : 0,
@@ -19168,6 +19203,13 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
rel = relation_open(relid, NoLock);
+ /*
+ * SET SCHEMA doesn't recurse to children, emit a notice if ONLY is not
+ * specified. As this action doesn't go through ATPrepCmd, we have to emit
+ * the notice here.
+ */
+ CollectPartitionNoRecurseNotice(AT_SetSchema, rel, stmt->relation->inh, false, &pendingNotice);
+
oldNspOid = RelationGetNamespace(rel);
/* If it's an owned sequence, disallow moving it by itself. */
@@ -19205,6 +19247,8 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
/* close rel, but keep lock until commit */
relation_close(rel, NoLock);
+ EmitPartitionNoRecurseNotice(pendingNotice);
+
return myself;
}
@@ -23556,3 +23600,69 @@ ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
/* Restore the userid and security context. */
SetUserIdAndSecContext(save_userid, save_sec_context);
}
+
+/*
+ * When ONLY is not specified with a partitioned table, it is expected that the
+ * command recurses to all partitions. However, some sub-commands do not recurse.
+ * In such cases, emit a NOTICE to make this behavior explicit to the user.
+ */
+static void
+CollectPartitionNoRecurseNotice(AlterTableType cmdtype, Relation rel, bool recurse, bool recursing,
+ List **pendingNotice)
+{
+ if (pendingNotice == NULL)
+ return;
+
+ /* Only emit the notice at the top level of recursion */
+ if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && recurse && !recursing)
+ {
+ PartitionDesc pd = RelationGetPartitionDesc(rel, true);
+ int nparts = pd->nparts;
+ const char *action_str;
+ char *notice_msg;
+ const ListCell *cell;
+
+ /* Emit a notice only if there are partitions */
+ if (nparts == 0)
+ return;
+
+ action_str = alter_table_type_to_string(cmdtype);
+ notice_msg = psprintf(_("ALTER action %s on relation \"%s\" does not affect present partitions"),
+ action_str,
+ RelationGetRelationName(rel));
+
+ foreach(cell, *pendingNotice)
+ {
+ if (strcmp((char *) lfirst(cell), notice_msg) == 0)
+ {
+ // Skip the duplicate notice message
+ pfree(notice_msg);
+ return;
+ }
+ }
+ *pendingNotice = lappend(*pendingNotice, notice_msg);
+ }
+}
+
+static void
+EmitPartitionNoRecurseNotice(List *pendingNotice)
+{
+ ListCell *cell;
+ int len;
+ int i = 0;
+
+ len = list_length(pendingNotice);
+ foreach(cell, pendingNotice)
+ {
+ char *notice_msg = (char *) lfirst(cell);
+
+ /* Only emit the hint for the last notice */
+ i++;
+ ereport(NOTICE, errmsg("%s", notice_msg),
+ (i == len) ?
+ errhint("Partitions may be modified individually, or specify ONLY to suppress this message.")
+ : 0);
+ pfree(notice_msg);
+ }
+ list_free(pendingNotice);
+}
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index f3d32ef0188..1267fb59d3f 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2511,6 +2511,7 @@ typedef enum AlterTableType
AT_SetIdentity, /* SET identity column options */
AT_DropIdentity, /* DROP IDENTITY */
AT_ReAddStatistics, /* internal to commands/tablecmds.c */
+ AT_SetSchema, /* SET SCHEMA */
} AlterTableType;
typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 5998c670aa3..bbb34bee938 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -4582,8 +4582,90 @@ SELECT * FROM list_parted;
---
(0 rows)
+CREATE TABLE list_parted4 (a int, b text) PARTITION BY LIST (a);
+CREATE TABLE list_parted4_1 PARTITION OF list_parted4 FOR VALUES IN (1);
+-- set column attribute on partitioned table without ONLY should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+NOTICE: ALTER action ALTER COLUMN ... SET on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE list_parted4 ALTER COLUMN b RESET (n_distinct);
+NOTICE: ALTER action ALTER COLUMN ... RESET on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b RESET (n_distinct);
+-- enable/disable rules on partitioned tables without ONLY should get a notice
+CREATE RULE list_parted4_rule AS ON INSERT TO list_parted4 DO INSTEAD NOTHING;
+ALTER TABLE list_parted4 DISABLE RULE list_parted4_rule;
+NOTICE: ALTER action DISABLE RULE on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 DISABLE RULE list_parted4_rule;
+ALTER TABLE list_parted4 ENABLE RULE list_parted4_rule;
+NOTICE: ALTER action ENABLE RULE on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ENABLE RULE list_parted4_rule;
+DROP RULE list_parted4_rule ON list_parted4;
+-- enable/disable row level security on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 ENABLE ROW LEVEL SECURITY;
+NOTICE: ALTER action ENABLE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 DISABLE ROW LEVEL SECURITY;
+NOTICE: ALTER action DISABLE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 DISABLE ROW LEVEL SECURITY;
+-- force/no force row level security on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 FORCE ROW LEVEL SECURITY;
+NOTICE: ALTER action FORCE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 FORCE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 NO FORCE ROW LEVEL SECURITY;
+NOTICE: ALTER action NO FORCE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 NO FORCE ROW LEVEL SECURITY;
+-- set replica identity on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 REPLICA IDENTITY FULL;
+NOTICE: ALTER action REPLICA IDENTITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY FULL;
+ALTER TABLE list_parted4 REPLICA IDENTITY NOTHING;
+NOTICE: ALTER action REPLICA IDENTITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY NOTHING;
+-- set compression on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+NOTICE: ALTER action ALTER COLUMN ... SET COMPRESSION on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+-- set owner on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 OWNER TO regress_alter_table_user1;
+NOTICE: ALTER action OWNER TO on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 OWNER TO regress_alter_table_user1;
+-- set schema on partitioned tables without ONLY should get a notice
+CREATE SCHEMA alter_table_test_schema;
+ALTER TABLE list_parted4 SET SCHEMA alter_table_test_schema;
+NOTICE: ALTER action SET SCHEMA on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY alter_table_test_schema.list_parted4 SET SCHEMA public;
+DROP SCHEMA alter_table_test_schema CASCADE;
+-- when there are multiple sub-command, notice should not duplicated
+ALTER TABLE list_parted4
+ ALTER COLUMN b SET COMPRESSION pglz,
+ ALTER COLUMN b SET COMPRESSION pglz, -- duplicate, but should not get duplicate notice
+ FORCE ROW LEVEL SECURITY,
+ REPLICA IDENTITY FULL,
+ OWNER TO regress_alter_table_user1;
+NOTICE: ALTER action ALTER COLUMN ... SET COMPRESSION on relation "list_parted4" does not affect present partitions
+NOTICE: ALTER action FORCE ROW SECURITY on relation "list_parted4" does not affect present partitions
+NOTICE: ALTER action REPLICA IDENTITY on relation "list_parted4" does not affect present partitions
+NOTICE: ALTER action OWNER TO on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+-- list_parted5 is a partitioned table that has no partition.
+CREATE TABLE list_parted5 (a int, b text) PARTITION BY LIST (a);
+-- as it has no partition, there should be no notice when altering it without ONLY
+ALTER TABLE list_parted5 FORCE ROW LEVEL SECURITY;
-- cleanup
-DROP TABLE list_parted, list_parted2, range_parted, list_parted3;
+DROP TABLE list_parted, list_parted2, range_parted, list_parted3, list_parted4, list_parted5;
DROP TABLE fail_def_part;
DROP TABLE hash_parted;
-- more tests for certain multi-level partitioning scenarios
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 24b0b1a8fce..67d7ef6509d 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -547,7 +547,7 @@ SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
RESET SESSION AUTHORIZATION;
-ALTER TABLE ptnowner OWNER TO regress_ptnowner;
+ALTER TABLE ONLY ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
SELECT oid, relname, relfilenode FROM pg_partition_tree('ptnowner') AS tree
JOIN pg_class AS c ON c.oid=tree.relid;
diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out
index 9cb1d87066a..bd6aac72336 100644
--- a/src/test/regress/expected/merge.out
+++ b/src/test/regress/expected/merge.out
@@ -2298,8 +2298,8 @@ SELECT * FROM pa_target ORDER BY tid, val;
ROLLBACK;
-- test RLS enforcement
BEGIN;
-ALTER TABLE pa_target ENABLE ROW LEVEL SECURITY;
-ALTER TABLE pa_target FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target FORCE ROW LEVEL SECURITY;
CREATE POLICY pa_target_pol ON pa_target USING (tid != 0);
MERGE INTO pa_target t
USING pa_source s
diff --git a/src/test/regress/expected/partition_merge.out b/src/test/regress/expected/partition_merge.out
index 925fe4f570a..f629fc7f3e5 100644
--- a/src/test/regress/expected/partition_merge.out
+++ b/src/test/regress/expected/partition_merge.out
@@ -810,7 +810,7 @@ SET SESSION AUTHORIZATION regress_partition_merge_bob;
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
ERROR: must be owner of table t
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_merge_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_merge_bob;
SET SESSION AUTHORIZATION regress_partition_merge_bob;
-- ERROR: must be owner of table tp_0_1
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
diff --git a/src/test/regress/expected/partition_split.out b/src/test/regress/expected/partition_split.out
index 4004efe0dac..421fa36ec0f 100644
--- a/src/test/regress/expected/partition_split.out
+++ b/src/test/regress/expected/partition_split.out
@@ -1372,7 +1372,7 @@ ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); --error
ERROR: must be owner of table t
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_split_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_split_bob;
SET SESSION AUTHORIZATION regress_partition_split_bob;
ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
(PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 7bc274566c3..877a1214963 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -1908,7 +1908,7 @@ SELECT brin_summarize_range('sro_brin', 0);
DROP TABLE sro_tab;
-- Check with a partitioned table
CREATE TABLE sro_ptab (a int) PARTITION BY RANGE (a);
-ALTER TABLE sro_ptab OWNER TO regress_sro_user;
+ALTER TABLE ONLY sro_ptab OWNER TO regress_sro_user;
CREATE TABLE sro_part PARTITION OF sro_ptab FOR VALUES FROM (1) TO (10);
ALTER TABLE sro_part OWNER TO regress_sro_user;
INSERT INTO sro_ptab VALUES (1), (2), (3);
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index 07d93e7def1..639c0065b0c 100644
--- a/src/test/regress/expected/rowsecurity.out
+++ b/src/test/regress/expected/rowsecurity.out
@@ -1233,7 +1233,7 @@ INSERT INTO part_document VALUES
( 8, 55, 2, 'regress_rls_carol', 'great satire'),
( 9, 11, 1, 'regress_rls_dave', 'awesome science fiction'),
(10, 99, 2, 'regress_rls_dave', 'awesome technology book');
-ALTER TABLE part_document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY part_document ENABLE ROW LEVEL SECURITY;
-- Create policy on parent
-- user's security level must be higher than or equal to document's
CREATE POLICY pp1 ON part_document AS PERMISSIVE
@@ -4914,7 +4914,7 @@ CREATE TABLE rls_ptbl (a int) PARTITION BY RANGE (a);
CREATE TABLE rls_part PARTITION OF rls_ptbl FOR VALUES FROM (-100) TO (100);
INSERT INTO rls_ptbl SELECT x/10 FROM generate_series(1, 100) x;
ANALYZE rls_ptbl, rls_part;
-ALTER TABLE rls_ptbl ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY rls_ptbl ENABLE ROW LEVEL SECURITY;
ALTER TABLE rls_part ENABLE ROW LEVEL SECURITY;
GRANT SELECT ON rls_ptbl TO regress_rls_alice;
GRANT SELECT ON rls_part TO regress_rls_alice;
diff --git a/src/test/regress/expected/tablespace.out b/src/test/regress/expected/tablespace.out
index 185880a3217..bf437a92746 100644
--- a/src/test/regress/expected/tablespace.out
+++ b/src/test/regress/expected/tablespace.out
@@ -290,13 +290,13 @@ CREATE TABLE testschema.part_2 PARTITION OF testschema.part FOR VALUES IN (2);
SET default_tablespace TO pg_global;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
ERROR: only shared relations can be placed in pg_global tablespace
-ALTER TABLE testschema.part SET TABLESPACE regress_tblspace;
+ALTER TABLE ONLY testschema.part SET TABLESPACE regress_tblspace;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
CREATE TABLE testschema.part_4 PARTITION OF testschema.part FOR VALUES IN (4)
TABLESPACE pg_default;
CREATE TABLE testschema.part_56 PARTITION OF testschema.part FOR VALUES IN (5, 6)
PARTITION BY LIST (a);
-ALTER TABLE testschema.part SET TABLESPACE pg_default;
+ALTER TABLE ONLY testschema.part SET TABLESPACE pg_default;
CREATE TABLE testschema.part_78 PARTITION OF testschema.part FOR VALUES IN (7, 8)
PARTITION BY LIST (a);
ERROR: only shared relations can be placed in pg_global tablespace
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index eef2bac1cbf..584b40598eb 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -607,7 +607,7 @@ DROP TRIGGER trig_d15_20 ON part_d_15_20;
DROP FUNCTION func_parted_mod_b();
-- RLS policies with update-row-movement
-----------------------------------------
-ALTER TABLE range_parted ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY range_parted ENABLE ROW LEVEL SECURITY;
CREATE USER regress_range_parted_user;
GRANT ALL ON range_parted, mintab TO regress_range_parted_user;
CREATE POLICY seeall ON range_parted AS PERMISSIVE FOR SELECT USING (true);
diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out
index d4696bc3325..f22bb2127e1 100644
--- a/src/test/regress/expected/vacuum.out
+++ b/src/test/regress/expected/vacuum.out
@@ -614,7 +614,7 @@ VACUUM (ANALYZE) vacowned_part2;
WARNING: permission denied to vacuum "vacowned_part2", skipping it
RESET ROLE;
-- Partitioned table and one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO regress_vacuum;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
@@ -634,7 +634,7 @@ VACUUM (ANALYZE) vacowned_part2;
WARNING: permission denied to vacuum "vacowned_part2", skipping it
RESET ROLE;
-- Only one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO CURRENT_USER;
+ALTER TABLE ONLY vacowned_parted OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
WARNING: permission denied to vacuum "vacowned_parted", skipping it
@@ -656,7 +656,7 @@ VACUUM (ANALYZE) vacowned_part2;
WARNING: permission denied to vacuum "vacowned_part2", skipping it
RESET ROLE;
-- Only partitioned table owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index d6b6381ae5c..97f641c78da 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2908,8 +2908,70 @@ ALTER TABLE list_parted2 ALTER COLUMN b TYPE text;
ALTER TABLE list_parted DROP COLUMN b;
SELECT * FROM list_parted;
+CREATE TABLE list_parted4 (a int, b text) PARTITION BY LIST (a);
+CREATE TABLE list_parted4_1 PARTITION OF list_parted4 FOR VALUES IN (1);
+
+-- set column attribute on partitioned table without ONLY should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+ALTER TABLE list_parted4 ALTER COLUMN b RESET (n_distinct);
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b RESET (n_distinct);
+
+-- enable/disable rules on partitioned tables without ONLY should get a notice
+CREATE RULE list_parted4_rule AS ON INSERT TO list_parted4 DO INSTEAD NOTHING;
+ALTER TABLE list_parted4 DISABLE RULE list_parted4_rule;
+ALTER TABLE ONLY list_parted4 DISABLE RULE list_parted4_rule;
+ALTER TABLE list_parted4 ENABLE RULE list_parted4_rule;
+ALTER TABLE ONLY list_parted4 ENABLE RULE list_parted4_rule;
+DROP RULE list_parted4_rule ON list_parted4;
+
+-- enable/disable row level security on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 DISABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 DISABLE ROW LEVEL SECURITY;
+
+-- force/no force row level security on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 FORCE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 NO FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 NO FORCE ROW LEVEL SECURITY;
+
+-- set replica identity on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 REPLICA IDENTITY FULL;
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY FULL;
+ALTER TABLE list_parted4 REPLICA IDENTITY NOTHING;
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY NOTHING;
+
+-- set compression on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+
+-- set owner on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 OWNER TO regress_alter_table_user1;
+ALTER TABLE ONLY list_parted4 OWNER TO regress_alter_table_user1;
+
+-- set schema on partitioned tables without ONLY should get a notice
+CREATE SCHEMA alter_table_test_schema;
+ALTER TABLE list_parted4 SET SCHEMA alter_table_test_schema;
+ALTER TABLE ONLY alter_table_test_schema.list_parted4 SET SCHEMA public;
+DROP SCHEMA alter_table_test_schema CASCADE;
+
+-- when there are multiple sub-command, notice should not duplicated
+ALTER TABLE list_parted4
+ ALTER COLUMN b SET COMPRESSION pglz,
+ ALTER COLUMN b SET COMPRESSION pglz, -- duplicate, but should not get duplicate notice
+ FORCE ROW LEVEL SECURITY,
+ REPLICA IDENTITY FULL,
+ OWNER TO regress_alter_table_user1;
+
+-- list_parted5 is a partitioned table that has no partition.
+CREATE TABLE list_parted5 (a int, b text) PARTITION BY LIST (a);
+-- as it has no partition, there should be no notice when altering it without ONLY
+ALTER TABLE list_parted5 FORCE ROW LEVEL SECURITY;
+
-- cleanup
-DROP TABLE list_parted, list_parted2, range_parted, list_parted3;
+DROP TABLE list_parted, list_parted2, range_parted, list_parted3, list_parted4, list_parted5;
DROP TABLE fail_def_part;
DROP TABLE hash_parted;
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index f90c6ec200b..3405e93ab0c 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -260,7 +260,7 @@ ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
RESET SESSION AUTHORIZATION;
-ALTER TABLE ptnowner OWNER TO regress_ptnowner;
+ALTER TABLE ONLY ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
SELECT oid, relname, relfilenode FROM pg_partition_tree('ptnowner') AS tree
JOIN pg_class AS c ON c.oid=tree.relid;
diff --git a/src/test/regress/sql/merge.sql b/src/test/regress/sql/merge.sql
index 2660b19f238..2bffcf7aa98 100644
--- a/src/test/regress/sql/merge.sql
+++ b/src/test/regress/sql/merge.sql
@@ -1422,8 +1422,8 @@ ROLLBACK;
-- test RLS enforcement
BEGIN;
-ALTER TABLE pa_target ENABLE ROW LEVEL SECURITY;
-ALTER TABLE pa_target FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target FORCE ROW LEVEL SECURITY;
CREATE POLICY pa_target_pol ON pa_target USING (tid != 0);
MERGE INTO pa_target t
USING pa_source s
diff --git a/src/test/regress/sql/partition_merge.sql b/src/test/regress/sql/partition_merge.sql
index a211fee2ad1..73bffb530dd 100644
--- a/src/test/regress/sql/partition_merge.sql
+++ b/src/test/regress/sql/partition_merge.sql
@@ -571,7 +571,7 @@ SET SESSION AUTHORIZATION regress_partition_merge_bob;
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_merge_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_merge_bob;
SET SESSION AUTHORIZATION regress_partition_merge_bob;
-- ERROR: must be owner of table tp_0_1
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
diff --git a/src/test/regress/sql/partition_split.sql b/src/test/regress/sql/partition_split.sql
index 37c6d730840..f4f9c7886e4 100644
--- a/src/test/regress/sql/partition_split.sql
+++ b/src/test/regress/sql/partition_split.sql
@@ -960,7 +960,7 @@ ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); --error
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_split_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_split_bob;
SET SESSION AUTHORIZATION regress_partition_split_bob;
ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
(PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index 540f73ea9b1..eb73629a81f 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -1230,7 +1230,7 @@ SELECT brin_summarize_range('sro_brin', 0);
DROP TABLE sro_tab;
-- Check with a partitioned table
CREATE TABLE sro_ptab (a int) PARTITION BY RANGE (a);
-ALTER TABLE sro_ptab OWNER TO regress_sro_user;
+ALTER TABLE ONLY sro_ptab OWNER TO regress_sro_user;
CREATE TABLE sro_part PARTITION OF sro_ptab FOR VALUES FROM (1) TO (10);
ALTER TABLE sro_part OWNER TO regress_sro_user;
INSERT INTO sro_ptab VALUES (1), (2), (3);
diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql
index 6b3566271df..1a2e80dade0 100644
--- a/src/test/regress/sql/rowsecurity.sql
+++ b/src/test/regress/sql/rowsecurity.sql
@@ -499,7 +499,7 @@ INSERT INTO part_document VALUES
( 9, 11, 1, 'regress_rls_dave', 'awesome science fiction'),
(10, 99, 2, 'regress_rls_dave', 'awesome technology book');
-ALTER TABLE part_document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY part_document ENABLE ROW LEVEL SECURITY;
-- Create policy on parent
-- user's security level must be higher than or equal to document's
@@ -2414,7 +2414,7 @@ CREATE TABLE rls_part PARTITION OF rls_ptbl FOR VALUES FROM (-100) TO (100);
INSERT INTO rls_ptbl SELECT x/10 FROM generate_series(1, 100) x;
ANALYZE rls_ptbl, rls_part;
-ALTER TABLE rls_ptbl ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY rls_ptbl ENABLE ROW LEVEL SECURITY;
ALTER TABLE rls_part ENABLE ROW LEVEL SECURITY;
GRANT SELECT ON rls_ptbl TO regress_rls_alice;
GRANT SELECT ON rls_part TO regress_rls_alice;
diff --git a/src/test/regress/sql/tablespace.sql b/src/test/regress/sql/tablespace.sql
index c43a59e5957..8eff2914a5b 100644
--- a/src/test/regress/sql/tablespace.sql
+++ b/src/test/regress/sql/tablespace.sql
@@ -195,13 +195,13 @@ SET default_tablespace TO regress_tblspace;
CREATE TABLE testschema.part_2 PARTITION OF testschema.part FOR VALUES IN (2);
SET default_tablespace TO pg_global;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
-ALTER TABLE testschema.part SET TABLESPACE regress_tblspace;
+ALTER TABLE ONLY testschema.part SET TABLESPACE regress_tblspace;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
CREATE TABLE testschema.part_4 PARTITION OF testschema.part FOR VALUES IN (4)
TABLESPACE pg_default;
CREATE TABLE testschema.part_56 PARTITION OF testschema.part FOR VALUES IN (5, 6)
PARTITION BY LIST (a);
-ALTER TABLE testschema.part SET TABLESPACE pg_default;
+ALTER TABLE ONLY testschema.part SET TABLESPACE pg_default;
CREATE TABLE testschema.part_78 PARTITION OF testschema.part FOR VALUES IN (7, 8)
PARTITION BY LIST (a);
CREATE TABLE testschema.part_910 PARTITION OF testschema.part FOR VALUES IN (9, 10)
diff --git a/src/test/regress/sql/update.sql b/src/test/regress/sql/update.sql
index 8b4707eb9c3..56b1edb00ce 100644
--- a/src/test/regress/sql/update.sql
+++ b/src/test/regress/sql/update.sql
@@ -341,7 +341,7 @@ DROP FUNCTION func_parted_mod_b();
-- RLS policies with update-row-movement
-----------------------------------------
-ALTER TABLE range_parted ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY range_parted ENABLE ROW LEVEL SECURITY;
CREATE USER regress_range_parted_user;
GRANT ALL ON range_parted, mintab TO regress_range_parted_user;
CREATE POLICY seeall ON range_parted AS PERMISSIVE FOR SELECT USING (true);
diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql
index 247b8e23b23..acff5824ebb 100644
--- a/src/test/regress/sql/vacuum.sql
+++ b/src/test/regress/sql/vacuum.sql
@@ -452,7 +452,7 @@ VACUUM (ANALYZE) vacowned_part1;
VACUUM (ANALYZE) vacowned_part2;
RESET ROLE;
-- Partitioned table and one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO regress_vacuum;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
@@ -466,7 +466,7 @@ VACUUM (ANALYZE) vacowned_part1;
VACUUM (ANALYZE) vacowned_part2;
RESET ROLE;
-- Only one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO CURRENT_USER;
+ALTER TABLE ONLY vacowned_parted OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
VACUUM vacowned_part1;
@@ -479,7 +479,7 @@ VACUUM (ANALYZE) vacowned_part1;
VACUUM (ANALYZE) vacowned_part2;
RESET ROLE;
-- Only partitioned table owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
--
2.50.1 (Apple Git-155)
^ permalink raw reply [nested|flat] 19+ messages in thread
* Re: ALTER TABLE: warn when actions do not recurse to partitions
2026-01-13 11:16 Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-14 00:52 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 05:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 19:27 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-23 00:11 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-23 09:57 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-03-09 06:46 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-10 15:32 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-11 07:04 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-11 16:39 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-12 01:14 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-12 02:09 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-12 06:37 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
@ 2026-03-12 12:10 ` Greg Sabino Mullane <[email protected]>
1 sibling, 0 replies; 19+ messages in thread
From: Greg Sabino Mullane @ 2026-03-12 12:10 UTC (permalink / raw)
To: Chao Li <[email protected]>; +Cc: Jim Jones <[email protected]>; David G. Johnston <[email protected]>; Postgres hackers <[email protected]>
Looks good to me!
^ permalink raw reply [nested|flat] 19+ messages in thread
* Re: ALTER TABLE: warn when actions do not recurse to partitions
2026-01-13 11:16 Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-14 00:52 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 05:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 19:27 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-23 00:11 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-23 09:57 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-03-09 06:46 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-10 15:32 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-11 07:04 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-11 16:39 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-12 01:14 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-12 02:09 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-12 06:37 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
@ 2026-03-12 21:45 ` Zsolt Parragi <[email protected]>
2026-03-13 03:16 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
1 sibling, 1 reply; 19+ messages in thread
From: Zsolt Parragi @ 2026-03-12 21:45 UTC (permalink / raw)
To: Chao Li <[email protected]>; +Cc: Greg Sabino Mullane <[email protected]>; Jim Jones <[email protected]>; David G. Johnston <[email protected]>; Postgres hackers <[email protected]>
Hello
> Affected sub-commands include:
> ...
Shouldn't SET ACCESS METHOD be included in this list?
It only changes the catalog entry for the parent, existing partitions
stay as-is. It only sets the access method for future direct child
partitions.
^ permalink raw reply [nested|flat] 19+ messages in thread
* Re: ALTER TABLE: warn when actions do not recurse to partitions
2026-01-13 11:16 Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-14 00:52 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 05:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 19:27 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-23 00:11 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-23 09:57 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-03-09 06:46 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-10 15:32 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-11 07:04 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-11 16:39 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-12 01:14 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-12 02:09 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-12 06:37 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-12 21:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Zsolt Parragi <[email protected]>
@ 2026-03-13 03:16 ` Chao Li <[email protected]>
2026-04-01 08:01 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
0 siblings, 1 reply; 19+ messages in thread
From: Chao Li @ 2026-03-13 03:16 UTC (permalink / raw)
To: Zsolt Parragi <[email protected]>; +Cc: Greg Sabino Mullane <[email protected]>; Jim Jones <[email protected]>; David G. Johnston <[email protected]>; Postgres hackers <[email protected]>
> On Mar 13, 2026, at 05:45, Zsolt Parragi <[email protected]> wrote:
>
> Hello
>
>> Affected sub-commands include:
>> ...
>
> Shouldn't SET ACCESS METHOD be included in this list?
>
> It only changes the catalog entry for the parent, existing partitions
> stay as-is. It only sets the access method for future direct child
> partitions.
Good catch. I missed SET ACCESS METHOD. Thanks a lot.
PFA v9:
* Included SET ACCESS METHOD
* Added a new test case where if the sub-command fails, then the notice message should not show up
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
Attachments:
[application/octet-stream] v9-0001-ALTER-TABLE-emit-NOTICE-when-ONLY-is-omitted-but-.patch (47.7K, 2-v9-0001-ALTER-TABLE-emit-NOTICE-when-ONLY-is-omitted-but-.patch)
download | inline diff:
From 8b90303c65cdee52346e75e11518ea14c56feb36 Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <[email protected]>
Date: Mon, 12 Jan 2026 16:56:58 +0800
Subject: [PATCH v9] ALTER TABLE: emit NOTICE when ONLY is omitted but no
partition recursion occurs
Some ALTER TABLE sub-commands do not recurse to partitions even when ONLY
is not specified on a partitioned table. This can be surprising, since
the default expectation is that commands propagate to all partitions.
Emit a NOTICE in such cases to make the behavior explicit, and advise how
to suppress the message or modify partitions individually.
Affected sub-commands include:
- ALTER COLUMN SET/RESET attribute_option
- ALTER COLUMN SET COMPRESSION
- ENABLE/DISABLE RULE
- ENABLE/DISABLE/FORCE/NO FORCE ROW LEVEL SECURITY
- REPLICA IDENTITY
- OWNER TO
- SET ACCESS METHOD
- SET TABLESPACE
- SET SCHEMA
RENAME is intentionally excluded. Using ONLY (or not) has no effect for
RENAME, since relation names are independent by nature and there is no
expectation of recursion.
OF / NOT OF are also excluded. Using ONLY has no effect for these
commands, as they apply only to the partitioned table itself and not to
its partitions.
Regression tests are updated to use ONLY where appropriate.
Author: Chao Li <[email protected]>
Reviewed-by: David G. Johnston <[email protected]>
Reviewed-by: Greg Sabino Mullane <[email protected]>
Reviewed-by: Jim Jones <[email protected]>
Reviewed-by: Zsolt Parragi <[email protected]>
Discussion: https://postgr.es/m/CAEoWx2=SLga-xH09Cq_PAvsHhQHrBK+V0vF821JKgzS=Bm0haA@mail.gmail.com
---
src/backend/commands/tablecmds.c | 138 ++++++++++++++++--
src/include/nodes/parsenodes.h | 1 +
src/test/regress/expected/alter_table.out | 92 +++++++++++-
src/test/regress/expected/cluster.out | 2 +-
src/test/regress/expected/create_am.out | 16 +-
src/test/regress/expected/merge.out | 4 +-
src/test/regress/expected/partition_merge.out | 2 +-
src/test/regress/expected/partition_split.out | 2 +-
src/test/regress/expected/privileges.out | 2 +-
src/test/regress/expected/rowsecurity.out | 4 +-
src/test/regress/expected/tablespace.out | 4 +-
src/test/regress/expected/update.out | 2 +-
src/test/regress/expected/vacuum.out | 6 +-
src/test/regress/sql/alter_table.sql | 71 ++++++++-
src/test/regress/sql/cluster.sql | 2 +-
src/test/regress/sql/create_am.sql | 16 +-
src/test/regress/sql/merge.sql | 4 +-
src/test/regress/sql/partition_merge.sql | 2 +-
src/test/regress/sql/partition_split.sql | 2 +-
src/test/regress/sql/privileges.sql | 2 +-
src/test/regress/sql/rowsecurity.sql | 4 +-
src/test/regress/sql/tablespace.sql | 4 +-
src/test/regress/sql/update.sql | 2 +-
src/test/regress/sql/vacuum.sql | 6 +-
24 files changed, 331 insertions(+), 59 deletions(-)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index cd6d720386f..76958977290 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -469,7 +469,8 @@ static void ATController(AlterTableStmt *parsetree,
AlterTableUtilityContext *context);
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
bool recurse, bool recursing, LOCKMODE lockmode,
- AlterTableUtilityContext *context);
+ AlterTableUtilityContext *context,
+ List **pendingNotice);
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
AlterTableUtilityContext *context);
static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
@@ -753,6 +754,10 @@ static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation
static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
Relation rel, PartitionCmd *cmd,
AlterTableUtilityContext *context);
+static void CollectPartitionNoRecurseNotice(AlterTableType cmdtype, Relation rel,
+ bool recurse, bool recursing,
+ List **pendingNotice);
+static void EmitPartitionNoRecurseNotice(List *pendingNotice);
/* ----------------------------------------------------------------
* DefineRelation
@@ -4894,13 +4899,14 @@ ATController(AlterTableStmt *parsetree,
{
List *wqueue = NIL;
ListCell *lcmd;
+ List *pendingNotice = NIL;
/* Phase 1: preliminary examination of commands, create work queue */
foreach(lcmd, cmds)
{
AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
- ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
+ ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context, &pendingNotice);
}
/* Close the relation, but keep lock until commit */
@@ -4911,6 +4917,9 @@ ATController(AlterTableStmt *parsetree,
/* Phase 3: scan/rewrite tables as needed, and run afterStmts */
ATRewriteTables(parsetree, &wqueue, lockmode, context);
+
+ /* Emit post-notice for partitions that were not recursed into. */
+ EmitPartitionNoRecurseNotice(pendingNotice);
}
/*
@@ -4925,7 +4934,8 @@ ATController(AlterTableStmt *parsetree,
static void
ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
bool recurse, bool recursing, LOCKMODE lockmode,
- AlterTableUtilityContext *context)
+ AlterTableUtilityContext *context,
+ List **pendingNotice)
{
AlteredTableInfo *tab;
AlterTablePass pass = AT_PASS_UNSET;
@@ -5074,6 +5084,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATT_MATVIEW | ATT_FOREIGN_TABLE);
/* This command never recurses */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
ATSimplePermissions(cmd->subtype, rel,
@@ -5089,6 +5101,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_DropColumn: /* DROP COLUMN */
ATSimplePermissions(cmd->subtype, rel,
@@ -5156,6 +5170,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_ClusterOn: /* CLUSTER ON */
case AT_DropCluster: /* SET WITHOUT CLUSTER */
@@ -5192,6 +5208,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATPrepSetAccessMethod(tab, rel, cmd->name);
pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_SetTableSpace: /* SET TABLESPACE */
ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE |
@@ -5199,6 +5217,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
pass = AT_PASS_MISC; /* doesn't actually matter */
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_SetRelOptions: /* SET (...) */
case AT_ResetRelOptions: /* RESET (...) */
@@ -5208,6 +5228,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATT_MATVIEW | ATT_INDEX);
/* This command never recurses */
/* No command-specific prep needed */
+ /* It will check for partitioned table at exec time */
pass = AT_PASS_MISC;
break;
case AT_AddInherit: /* INHERIT */
@@ -5245,8 +5266,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATSimplePermissions(cmd->subtype, rel,
ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
pass = AT_PASS_MISC;
- /* This command never recurses */
- /* No command-specific prep needed */
+ /* This command doesn't recurse to partitions, so notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_EnableTrig: /* ENABLE TRIGGER variants */
case AT_EnableAlwaysTrig:
@@ -5267,17 +5288,30 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
case AT_EnableAlwaysRule:
case AT_EnableReplicaRule:
case AT_DisableRule:
- case AT_AddOf: /* OF */
- case AT_DropOf: /* NOT OF */
- case AT_EnableRowSecurity:
+ case AT_EnableRowSecurity: /* ENABLE/DISABLE ROW SECURITY variants */
case AT_DisableRowSecurity:
- case AT_ForceRowSecurity:
+ case AT_ForceRowSecurity: /* FORCE/NO FORCE ROW SECURITY variants */
case AT_NoForceRowSecurity:
ATSimplePermissions(cmd->subtype, rel,
ATT_TABLE | ATT_PARTITIONED_TABLE);
/* These commands never recurse */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
+ break;
+ case AT_AddOf: /* OF */
+ case AT_DropOf: /* NOT OF */
+ ATSimplePermissions(cmd->subtype, rel,
+ ATT_TABLE | ATT_PARTITIONED_TABLE);
+ /* These commands never recurse */
+ /* No command-specific prep needed */
+
+ /*
+ * They only work on partitioned tables but child partitions, thus
+ * no need to emit a notice
+ */
+ pass = AT_PASS_MISC;
break;
case AT_GenericOptions:
ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
@@ -6770,6 +6804,8 @@ alter_table_type_to_string(AlterTableType cmdtype)
return "ALTER COLUMN ... DROP IDENTITY";
case AT_ReAddStatistics:
return NULL; /* not real grammar */
+ case AT_SetSchema:
+ return "SET SCHEMA";
}
return NULL;
@@ -6891,7 +6927,7 @@ ATSimpleRecursion(List **wqueue, Relation rel,
/* find_all_inheritors already got lock */
childrel = relation_open(childrelid, NoLock);
CheckAlterTableIsSafe(childrel);
- ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
+ ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context, NULL);
relation_close(childrel, NoLock);
}
}
@@ -6954,7 +6990,7 @@ ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
childrel = relation_open(childrelid, lockmode);
CheckAlterTableIsSafe(childrel);
- ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
+ ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context, NULL);
relation_close(childrel, NoLock);
}
}
@@ -9625,7 +9661,7 @@ ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
newcmd->recurse = true;
newcmd->def = (Node *) nnconstr;
- ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
+ ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context, NULL);
}
}
@@ -14873,7 +14909,7 @@ ATPrepAlterColumnType(List **wqueue,
errdetail("USING expression contains a whole-row table reference.")));
pfree(attmap);
}
- ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
+ ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context, NULL);
relation_close(childrel, NoLock);
}
}
@@ -19177,6 +19213,7 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
RangeVar *newrv;
ObjectAddresses *objsMoved;
ObjectAddress myself;
+ List *pendingNotice = NIL;
relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
stmt->missing_ok ? RVR_MISSING_OK : 0,
@@ -19193,6 +19230,13 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
rel = relation_open(relid, NoLock);
+ /*
+ * SET SCHEMA doesn't recurse to children, emit a notice if ONLY is not
+ * specified. As this action doesn't go through ATPrepCmd, we have to emit
+ * the notice here.
+ */
+ CollectPartitionNoRecurseNotice(AT_SetSchema, rel, stmt->relation->inh, false, &pendingNotice);
+
oldNspOid = RelationGetNamespace(rel);
/* If it's an owned sequence, disallow moving it by itself. */
@@ -19230,6 +19274,8 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
/* close rel, but keep lock until commit */
relation_close(rel, NoLock);
+ EmitPartitionNoRecurseNotice(pendingNotice);
+
return myself;
}
@@ -23581,3 +23627,69 @@ ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
/* Restore the userid and security context. */
SetUserIdAndSecContext(save_userid, save_sec_context);
}
+
+/*
+ * When ONLY is not specified with a partitioned table, it is expected that the
+ * command recurses to all partitions. However, some sub-commands do not recurse.
+ * In such cases, emit a NOTICE to make this behavior explicit to the user.
+ */
+static void
+CollectPartitionNoRecurseNotice(AlterTableType cmdtype, Relation rel, bool recurse, bool recursing,
+ List **pendingNotice)
+{
+ if (pendingNotice == NULL)
+ return;
+
+ /* Only emit the notice at the top level of recursion */
+ if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && recurse && !recursing)
+ {
+ PartitionDesc pd = RelationGetPartitionDesc(rel, true);
+ int nparts = pd->nparts;
+ const char *action_str;
+ char *notice_msg;
+ const ListCell *cell;
+
+ /* Emit a notice only if there are partitions */
+ if (nparts == 0)
+ return;
+
+ action_str = alter_table_type_to_string(cmdtype);
+ notice_msg = psprintf(_("ALTER action %s on relation \"%s\" does not affect present partitions"),
+ action_str,
+ RelationGetRelationName(rel));
+
+ foreach(cell, *pendingNotice)
+ {
+ if (strcmp((char *) lfirst(cell), notice_msg) == 0)
+ {
+ /* Skip the duplicate notice message */
+ pfree(notice_msg);
+ return;
+ }
+ }
+ *pendingNotice = lappend(*pendingNotice, notice_msg);
+ }
+}
+
+static void
+EmitPartitionNoRecurseNotice(List *pendingNotice)
+{
+ ListCell *cell;
+ int len;
+ int i = 0;
+
+ len = list_length(pendingNotice);
+ foreach(cell, pendingNotice)
+ {
+ char *notice_msg = (char *) lfirst(cell);
+
+ /* Only emit the hint for the last notice */
+ i++;
+ ereport(NOTICE, errmsg("%s", notice_msg),
+ (i == len) ?
+ errhint("Partitions may be modified individually, or specify ONLY to suppress this message.")
+ : 0);
+ pfree(notice_msg);
+ }
+ list_free(pendingNotice);
+}
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index f3d32ef0188..1267fb59d3f 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2511,6 +2511,7 @@ typedef enum AlterTableType
AT_SetIdentity, /* SET identity column options */
AT_DropIdentity, /* DROP IDENTITY */
AT_ReAddStatistics, /* internal to commands/tablecmds.c */
+ AT_SetSchema, /* SET SCHEMA */
} AlterTableType;
typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 5998c670aa3..e6d93fd9b41 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -4582,8 +4582,98 @@ SELECT * FROM list_parted;
---
(0 rows)
+CREATE TABLE list_parted4 (a int, b text) PARTITION BY LIST (a);
+CREATE TABLE list_parted4_1 PARTITION OF list_parted4 FOR VALUES IN (1);
+-- set column attribute on partitioned table without ONLY should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+NOTICE: ALTER action ALTER COLUMN ... SET on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE list_parted4 ALTER COLUMN b RESET (n_distinct);
+NOTICE: ALTER action ALTER COLUMN ... RESET on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b RESET (n_distinct);
+-- enable/disable rules on partitioned tables without ONLY should get a notice
+CREATE RULE list_parted4_rule AS ON INSERT TO list_parted4 DO INSTEAD NOTHING;
+ALTER TABLE list_parted4 DISABLE RULE list_parted4_rule;
+NOTICE: ALTER action DISABLE RULE on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 DISABLE RULE list_parted4_rule;
+ALTER TABLE list_parted4 ENABLE RULE list_parted4_rule;
+NOTICE: ALTER action ENABLE RULE on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ENABLE RULE list_parted4_rule;
+DROP RULE list_parted4_rule ON list_parted4;
+-- enable/disable row level security on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 ENABLE ROW LEVEL SECURITY;
+NOTICE: ALTER action ENABLE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 DISABLE ROW LEVEL SECURITY;
+NOTICE: ALTER action DISABLE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 DISABLE ROW LEVEL SECURITY;
+-- force/no force row level security on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 FORCE ROW LEVEL SECURITY;
+NOTICE: ALTER action FORCE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 FORCE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 NO FORCE ROW LEVEL SECURITY;
+NOTICE: ALTER action NO FORCE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 NO FORCE ROW LEVEL SECURITY;
+-- set replica identity on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 REPLICA IDENTITY FULL;
+NOTICE: ALTER action REPLICA IDENTITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY FULL;
+ALTER TABLE list_parted4 REPLICA IDENTITY NOTHING;
+NOTICE: ALTER action REPLICA IDENTITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY NOTHING;
+-- set compression on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+NOTICE: ALTER action ALTER COLUMN ... SET COMPRESSION on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+-- set owner on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 OWNER TO regress_alter_table_user1;
+NOTICE: ALTER action OWNER TO on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 OWNER TO regress_alter_table_user1;
+-- set access method on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 SET ACCESS METHOD heap;
+NOTICE: ALTER action SET ACCESS METHOD on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 SET ACCESS METHOD heap;
+-- set schema on partitioned tables without ONLY should get a notice
+CREATE SCHEMA alter_table_test_schema;
+ALTER TABLE list_parted4 SET SCHEMA alter_table_test_schema;
+NOTICE: ALTER action SET SCHEMA on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY alter_table_test_schema.list_parted4 SET SCHEMA public;
+DROP SCHEMA alter_table_test_schema CASCADE;
+-- when there are multiple sub-command, notice should not duplicated
+ALTER TABLE list_parted4
+ ALTER COLUMN b SET COMPRESSION pglz,
+ ALTER COLUMN b SET COMPRESSION pglz, -- duplicate, but should not get duplicate notice
+ FORCE ROW LEVEL SECURITY,
+ REPLICA IDENTITY FULL,
+ OWNER TO regress_alter_table_user1;
+NOTICE: ALTER action ALTER COLUMN ... SET COMPRESSION on relation "list_parted4" does not affect present partitions
+NOTICE: ALTER action FORCE ROW SECURITY on relation "list_parted4" does not affect present partitions
+NOTICE: ALTER action REPLICA IDENTITY on relation "list_parted4" does not affect present partitions
+NOTICE: ALTER action OWNER TO on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+-- when the sub-command fails, notice should not be printed
+ALTER TABLE list_parted4 SET ACCESS METHOD invalid_am;
+ERROR: access method "invalid_am" does not exist
+-- list_parted5 is a partitioned table that has no partition.
+CREATE TABLE list_parted5 (a int, b text) PARTITION BY LIST (a);
+-- as it has no partition, there should be no notice when altering it without ONLY
+ALTER TABLE list_parted5 FORCE ROW LEVEL SECURITY;
-- cleanup
-DROP TABLE list_parted, list_parted2, range_parted, list_parted3;
+DROP TABLE list_parted, list_parted2, range_parted, list_parted3, list_parted4, list_parted5;
DROP TABLE fail_def_part;
DROP TABLE hash_parted;
-- more tests for certain multi-level partitioning scenarios
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 24b0b1a8fce..67d7ef6509d 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -547,7 +547,7 @@ SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
RESET SESSION AUTHORIZATION;
-ALTER TABLE ptnowner OWNER TO regress_ptnowner;
+ALTER TABLE ONLY ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
SELECT oid, relname, relfilenode FROM pg_partition_tree('ptnowner') AS tree
JOIN pg_class AS c ON c.oid=tree.relid;
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
index c1a95157251..8c428270118 100644
--- a/src/test/regress/expected/create_am.out
+++ b/src/test/regress/expected/create_am.out
@@ -380,7 +380,7 @@ SELECT pg_describe_object(classid, objid, objsubid) AS obj,
(0 rows)
-- New default is set, with dependency added.
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
amname
@@ -401,7 +401,7 @@ SELECT pg_describe_object(classid, objid, objsubid) AS obj,
-- Default is set, with dependency updated.
SET LOCAL default_table_access_method = 'heap2';
-ALTER TABLE am_partitioned SET ACCESS METHOD heap;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
amname
@@ -422,7 +422,7 @@ SELECT pg_describe_object(classid, objid, objsubid) AS obj,
-- Default and AM set in the clause are the same, relam should be set.
SET LOCAL default_table_access_method = 'heap2';
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
amname
@@ -431,7 +431,7 @@ SELECT a.amname FROM pg_class c, pg_am a
(1 row)
-- Reset to default
-ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD DEFAULT;
SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
relam
-------
@@ -453,10 +453,10 @@ SET LOCAL default_table_access_method = 'heap2';
CREATE TABLE am_partitioned_1 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 1);
SET LOCAL default_table_access_method = 'heap';
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
CREATE TABLE am_partitioned_2 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 2);
-ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD DEFAULT;
SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
relam
-------
@@ -466,7 +466,7 @@ SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
CREATE TABLE am_partitioned_3 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 3);
-- Partitioned table with relam at 0
-ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD DEFAULT;
CREATE TABLE am_partitioned_5p PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 5) PARTITION BY hash(y);
-- Partitions of this partitioned table inherit default AM at creation
@@ -474,7 +474,7 @@ CREATE TABLE am_partitioned_5p PARTITION OF am_partitioned
CREATE TABLE am_partitioned_5p1 PARTITION OF am_partitioned_5p
FOR VALUES WITH (MODULUS 10, REMAINDER 1);
-- Partitioned table with relam set.
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
CREATE TABLE am_partitioned_6p PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 6) PARTITION BY hash(y);
-- Partitions of this partitioned table inherit its AM.
diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out
index 9cb1d87066a..bd6aac72336 100644
--- a/src/test/regress/expected/merge.out
+++ b/src/test/regress/expected/merge.out
@@ -2298,8 +2298,8 @@ SELECT * FROM pa_target ORDER BY tid, val;
ROLLBACK;
-- test RLS enforcement
BEGIN;
-ALTER TABLE pa_target ENABLE ROW LEVEL SECURITY;
-ALTER TABLE pa_target FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target FORCE ROW LEVEL SECURITY;
CREATE POLICY pa_target_pol ON pa_target USING (tid != 0);
MERGE INTO pa_target t
USING pa_source s
diff --git a/src/test/regress/expected/partition_merge.out b/src/test/regress/expected/partition_merge.out
index 925fe4f570a..f629fc7f3e5 100644
--- a/src/test/regress/expected/partition_merge.out
+++ b/src/test/regress/expected/partition_merge.out
@@ -810,7 +810,7 @@ SET SESSION AUTHORIZATION regress_partition_merge_bob;
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
ERROR: must be owner of table t
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_merge_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_merge_bob;
SET SESSION AUTHORIZATION regress_partition_merge_bob;
-- ERROR: must be owner of table tp_0_1
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
diff --git a/src/test/regress/expected/partition_split.out b/src/test/regress/expected/partition_split.out
index 4004efe0dac..421fa36ec0f 100644
--- a/src/test/regress/expected/partition_split.out
+++ b/src/test/regress/expected/partition_split.out
@@ -1372,7 +1372,7 @@ ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); --error
ERROR: must be owner of table t
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_split_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_split_bob;
SET SESSION AUTHORIZATION regress_partition_split_bob;
ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
(PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 7bc274566c3..877a1214963 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -1908,7 +1908,7 @@ SELECT brin_summarize_range('sro_brin', 0);
DROP TABLE sro_tab;
-- Check with a partitioned table
CREATE TABLE sro_ptab (a int) PARTITION BY RANGE (a);
-ALTER TABLE sro_ptab OWNER TO regress_sro_user;
+ALTER TABLE ONLY sro_ptab OWNER TO regress_sro_user;
CREATE TABLE sro_part PARTITION OF sro_ptab FOR VALUES FROM (1) TO (10);
ALTER TABLE sro_part OWNER TO regress_sro_user;
INSERT INTO sro_ptab VALUES (1), (2), (3);
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index 07d93e7def1..639c0065b0c 100644
--- a/src/test/regress/expected/rowsecurity.out
+++ b/src/test/regress/expected/rowsecurity.out
@@ -1233,7 +1233,7 @@ INSERT INTO part_document VALUES
( 8, 55, 2, 'regress_rls_carol', 'great satire'),
( 9, 11, 1, 'regress_rls_dave', 'awesome science fiction'),
(10, 99, 2, 'regress_rls_dave', 'awesome technology book');
-ALTER TABLE part_document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY part_document ENABLE ROW LEVEL SECURITY;
-- Create policy on parent
-- user's security level must be higher than or equal to document's
CREATE POLICY pp1 ON part_document AS PERMISSIVE
@@ -4914,7 +4914,7 @@ CREATE TABLE rls_ptbl (a int) PARTITION BY RANGE (a);
CREATE TABLE rls_part PARTITION OF rls_ptbl FOR VALUES FROM (-100) TO (100);
INSERT INTO rls_ptbl SELECT x/10 FROM generate_series(1, 100) x;
ANALYZE rls_ptbl, rls_part;
-ALTER TABLE rls_ptbl ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY rls_ptbl ENABLE ROW LEVEL SECURITY;
ALTER TABLE rls_part ENABLE ROW LEVEL SECURITY;
GRANT SELECT ON rls_ptbl TO regress_rls_alice;
GRANT SELECT ON rls_part TO regress_rls_alice;
diff --git a/src/test/regress/expected/tablespace.out b/src/test/regress/expected/tablespace.out
index 185880a3217..bf437a92746 100644
--- a/src/test/regress/expected/tablespace.out
+++ b/src/test/regress/expected/tablespace.out
@@ -290,13 +290,13 @@ CREATE TABLE testschema.part_2 PARTITION OF testschema.part FOR VALUES IN (2);
SET default_tablespace TO pg_global;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
ERROR: only shared relations can be placed in pg_global tablespace
-ALTER TABLE testschema.part SET TABLESPACE regress_tblspace;
+ALTER TABLE ONLY testschema.part SET TABLESPACE regress_tblspace;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
CREATE TABLE testschema.part_4 PARTITION OF testschema.part FOR VALUES IN (4)
TABLESPACE pg_default;
CREATE TABLE testschema.part_56 PARTITION OF testschema.part FOR VALUES IN (5, 6)
PARTITION BY LIST (a);
-ALTER TABLE testschema.part SET TABLESPACE pg_default;
+ALTER TABLE ONLY testschema.part SET TABLESPACE pg_default;
CREATE TABLE testschema.part_78 PARTITION OF testschema.part FOR VALUES IN (7, 8)
PARTITION BY LIST (a);
ERROR: only shared relations can be placed in pg_global tablespace
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index eef2bac1cbf..584b40598eb 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -607,7 +607,7 @@ DROP TRIGGER trig_d15_20 ON part_d_15_20;
DROP FUNCTION func_parted_mod_b();
-- RLS policies with update-row-movement
-----------------------------------------
-ALTER TABLE range_parted ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY range_parted ENABLE ROW LEVEL SECURITY;
CREATE USER regress_range_parted_user;
GRANT ALL ON range_parted, mintab TO regress_range_parted_user;
CREATE POLICY seeall ON range_parted AS PERMISSIVE FOR SELECT USING (true);
diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out
index d4696bc3325..f22bb2127e1 100644
--- a/src/test/regress/expected/vacuum.out
+++ b/src/test/regress/expected/vacuum.out
@@ -614,7 +614,7 @@ VACUUM (ANALYZE) vacowned_part2;
WARNING: permission denied to vacuum "vacowned_part2", skipping it
RESET ROLE;
-- Partitioned table and one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO regress_vacuum;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
@@ -634,7 +634,7 @@ VACUUM (ANALYZE) vacowned_part2;
WARNING: permission denied to vacuum "vacowned_part2", skipping it
RESET ROLE;
-- Only one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO CURRENT_USER;
+ALTER TABLE ONLY vacowned_parted OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
WARNING: permission denied to vacuum "vacowned_parted", skipping it
@@ -656,7 +656,7 @@ VACUUM (ANALYZE) vacowned_part2;
WARNING: permission denied to vacuum "vacowned_part2", skipping it
RESET ROLE;
-- Only partitioned table owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index d6b6381ae5c..4a08c7ff4e7 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2908,8 +2908,77 @@ ALTER TABLE list_parted2 ALTER COLUMN b TYPE text;
ALTER TABLE list_parted DROP COLUMN b;
SELECT * FROM list_parted;
+CREATE TABLE list_parted4 (a int, b text) PARTITION BY LIST (a);
+CREATE TABLE list_parted4_1 PARTITION OF list_parted4 FOR VALUES IN (1);
+
+-- set column attribute on partitioned table without ONLY should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+ALTER TABLE list_parted4 ALTER COLUMN b RESET (n_distinct);
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b RESET (n_distinct);
+
+-- enable/disable rules on partitioned tables without ONLY should get a notice
+CREATE RULE list_parted4_rule AS ON INSERT TO list_parted4 DO INSTEAD NOTHING;
+ALTER TABLE list_parted4 DISABLE RULE list_parted4_rule;
+ALTER TABLE ONLY list_parted4 DISABLE RULE list_parted4_rule;
+ALTER TABLE list_parted4 ENABLE RULE list_parted4_rule;
+ALTER TABLE ONLY list_parted4 ENABLE RULE list_parted4_rule;
+DROP RULE list_parted4_rule ON list_parted4;
+
+-- enable/disable row level security on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 DISABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 DISABLE ROW LEVEL SECURITY;
+
+-- force/no force row level security on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 FORCE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 NO FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 NO FORCE ROW LEVEL SECURITY;
+
+-- set replica identity on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 REPLICA IDENTITY FULL;
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY FULL;
+ALTER TABLE list_parted4 REPLICA IDENTITY NOTHING;
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY NOTHING;
+
+-- set compression on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+
+-- set owner on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 OWNER TO regress_alter_table_user1;
+ALTER TABLE ONLY list_parted4 OWNER TO regress_alter_table_user1;
+
+-- set access method on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 SET ACCESS METHOD heap;
+ALTER TABLE ONLY list_parted4 SET ACCESS METHOD heap;
+
+-- set schema on partitioned tables without ONLY should get a notice
+CREATE SCHEMA alter_table_test_schema;
+ALTER TABLE list_parted4 SET SCHEMA alter_table_test_schema;
+ALTER TABLE ONLY alter_table_test_schema.list_parted4 SET SCHEMA public;
+DROP SCHEMA alter_table_test_schema CASCADE;
+
+-- when there are multiple sub-command, notice should not duplicated
+ALTER TABLE list_parted4
+ ALTER COLUMN b SET COMPRESSION pglz,
+ ALTER COLUMN b SET COMPRESSION pglz, -- duplicate, but should not get duplicate notice
+ FORCE ROW LEVEL SECURITY,
+ REPLICA IDENTITY FULL,
+ OWNER TO regress_alter_table_user1;
+
+-- when the sub-command fails, notice should not be printed
+ALTER TABLE list_parted4 SET ACCESS METHOD invalid_am;
+
+-- list_parted5 is a partitioned table that has no partition.
+CREATE TABLE list_parted5 (a int, b text) PARTITION BY LIST (a);
+-- as it has no partition, there should be no notice when altering it without ONLY
+ALTER TABLE list_parted5 FORCE ROW LEVEL SECURITY;
+
-- cleanup
-DROP TABLE list_parted, list_parted2, range_parted, list_parted3;
+DROP TABLE list_parted, list_parted2, range_parted, list_parted3, list_parted4, list_parted5;
DROP TABLE fail_def_part;
DROP TABLE hash_parted;
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index f90c6ec200b..3405e93ab0c 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -260,7 +260,7 @@ ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
RESET SESSION AUTHORIZATION;
-ALTER TABLE ptnowner OWNER TO regress_ptnowner;
+ALTER TABLE ONLY ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
SELECT oid, relname, relfilenode FROM pg_partition_tree('ptnowner') AS tree
JOIN pg_class AS c ON c.oid=tree.relid;
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
index 754fe0c694b..4953be0ab3d 100644
--- a/src/test/regress/sql/create_am.sql
+++ b/src/test/regress/sql/create_am.sql
@@ -241,7 +241,7 @@ SELECT pg_describe_object(classid, objid, objsubid) AS obj,
AND pg_am.oid = pg_depend.refobjid
AND pg_depend.objid = 'am_partitioned'::regclass;
-- New default is set, with dependency added.
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
SELECT pg_describe_object(classid, objid, objsubid) AS obj,
@@ -252,7 +252,7 @@ SELECT pg_describe_object(classid, objid, objsubid) AS obj,
AND pg_depend.objid = 'am_partitioned'::regclass;
-- Default is set, with dependency updated.
SET LOCAL default_table_access_method = 'heap2';
-ALTER TABLE am_partitioned SET ACCESS METHOD heap;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
-- Dependency pinned, hence removed.
@@ -264,11 +264,11 @@ SELECT pg_describe_object(classid, objid, objsubid) AS obj,
AND pg_depend.objid = 'am_partitioned'::regclass;
-- Default and AM set in the clause are the same, relam should be set.
SET LOCAL default_table_access_method = 'heap2';
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
-- Reset to default
-ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD DEFAULT;
SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
-- Upon ALTER TABLE SET ACCESS METHOD on a partitioned table, new partitions
-- will inherit the AM set. Existing partitioned are unchanged.
@@ -280,15 +280,15 @@ SET LOCAL default_table_access_method = 'heap2';
CREATE TABLE am_partitioned_1 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 1);
SET LOCAL default_table_access_method = 'heap';
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
CREATE TABLE am_partitioned_2 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 2);
-ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD DEFAULT;
SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
CREATE TABLE am_partitioned_3 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 3);
-- Partitioned table with relam at 0
-ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD DEFAULT;
CREATE TABLE am_partitioned_5p PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 5) PARTITION BY hash(y);
-- Partitions of this partitioned table inherit default AM at creation
@@ -296,7 +296,7 @@ CREATE TABLE am_partitioned_5p PARTITION OF am_partitioned
CREATE TABLE am_partitioned_5p1 PARTITION OF am_partitioned_5p
FOR VALUES WITH (MODULUS 10, REMAINDER 1);
-- Partitioned table with relam set.
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
CREATE TABLE am_partitioned_6p PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 6) PARTITION BY hash(y);
-- Partitions of this partitioned table inherit its AM.
diff --git a/src/test/regress/sql/merge.sql b/src/test/regress/sql/merge.sql
index 2660b19f238..2bffcf7aa98 100644
--- a/src/test/regress/sql/merge.sql
+++ b/src/test/regress/sql/merge.sql
@@ -1422,8 +1422,8 @@ ROLLBACK;
-- test RLS enforcement
BEGIN;
-ALTER TABLE pa_target ENABLE ROW LEVEL SECURITY;
-ALTER TABLE pa_target FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target FORCE ROW LEVEL SECURITY;
CREATE POLICY pa_target_pol ON pa_target USING (tid != 0);
MERGE INTO pa_target t
USING pa_source s
diff --git a/src/test/regress/sql/partition_merge.sql b/src/test/regress/sql/partition_merge.sql
index a211fee2ad1..73bffb530dd 100644
--- a/src/test/regress/sql/partition_merge.sql
+++ b/src/test/regress/sql/partition_merge.sql
@@ -571,7 +571,7 @@ SET SESSION AUTHORIZATION regress_partition_merge_bob;
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_merge_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_merge_bob;
SET SESSION AUTHORIZATION regress_partition_merge_bob;
-- ERROR: must be owner of table tp_0_1
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
diff --git a/src/test/regress/sql/partition_split.sql b/src/test/regress/sql/partition_split.sql
index 37c6d730840..f4f9c7886e4 100644
--- a/src/test/regress/sql/partition_split.sql
+++ b/src/test/regress/sql/partition_split.sql
@@ -960,7 +960,7 @@ ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); --error
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_split_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_split_bob;
SET SESSION AUTHORIZATION regress_partition_split_bob;
ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
(PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index 540f73ea9b1..eb73629a81f 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -1230,7 +1230,7 @@ SELECT brin_summarize_range('sro_brin', 0);
DROP TABLE sro_tab;
-- Check with a partitioned table
CREATE TABLE sro_ptab (a int) PARTITION BY RANGE (a);
-ALTER TABLE sro_ptab OWNER TO regress_sro_user;
+ALTER TABLE ONLY sro_ptab OWNER TO regress_sro_user;
CREATE TABLE sro_part PARTITION OF sro_ptab FOR VALUES FROM (1) TO (10);
ALTER TABLE sro_part OWNER TO regress_sro_user;
INSERT INTO sro_ptab VALUES (1), (2), (3);
diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql
index 6b3566271df..1a2e80dade0 100644
--- a/src/test/regress/sql/rowsecurity.sql
+++ b/src/test/regress/sql/rowsecurity.sql
@@ -499,7 +499,7 @@ INSERT INTO part_document VALUES
( 9, 11, 1, 'regress_rls_dave', 'awesome science fiction'),
(10, 99, 2, 'regress_rls_dave', 'awesome technology book');
-ALTER TABLE part_document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY part_document ENABLE ROW LEVEL SECURITY;
-- Create policy on parent
-- user's security level must be higher than or equal to document's
@@ -2414,7 +2414,7 @@ CREATE TABLE rls_part PARTITION OF rls_ptbl FOR VALUES FROM (-100) TO (100);
INSERT INTO rls_ptbl SELECT x/10 FROM generate_series(1, 100) x;
ANALYZE rls_ptbl, rls_part;
-ALTER TABLE rls_ptbl ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY rls_ptbl ENABLE ROW LEVEL SECURITY;
ALTER TABLE rls_part ENABLE ROW LEVEL SECURITY;
GRANT SELECT ON rls_ptbl TO regress_rls_alice;
GRANT SELECT ON rls_part TO regress_rls_alice;
diff --git a/src/test/regress/sql/tablespace.sql b/src/test/regress/sql/tablespace.sql
index c43a59e5957..8eff2914a5b 100644
--- a/src/test/regress/sql/tablespace.sql
+++ b/src/test/regress/sql/tablespace.sql
@@ -195,13 +195,13 @@ SET default_tablespace TO regress_tblspace;
CREATE TABLE testschema.part_2 PARTITION OF testschema.part FOR VALUES IN (2);
SET default_tablespace TO pg_global;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
-ALTER TABLE testschema.part SET TABLESPACE regress_tblspace;
+ALTER TABLE ONLY testschema.part SET TABLESPACE regress_tblspace;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
CREATE TABLE testschema.part_4 PARTITION OF testschema.part FOR VALUES IN (4)
TABLESPACE pg_default;
CREATE TABLE testschema.part_56 PARTITION OF testschema.part FOR VALUES IN (5, 6)
PARTITION BY LIST (a);
-ALTER TABLE testschema.part SET TABLESPACE pg_default;
+ALTER TABLE ONLY testschema.part SET TABLESPACE pg_default;
CREATE TABLE testschema.part_78 PARTITION OF testschema.part FOR VALUES IN (7, 8)
PARTITION BY LIST (a);
CREATE TABLE testschema.part_910 PARTITION OF testschema.part FOR VALUES IN (9, 10)
diff --git a/src/test/regress/sql/update.sql b/src/test/regress/sql/update.sql
index 8b4707eb9c3..56b1edb00ce 100644
--- a/src/test/regress/sql/update.sql
+++ b/src/test/regress/sql/update.sql
@@ -341,7 +341,7 @@ DROP FUNCTION func_parted_mod_b();
-- RLS policies with update-row-movement
-----------------------------------------
-ALTER TABLE range_parted ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY range_parted ENABLE ROW LEVEL SECURITY;
CREATE USER regress_range_parted_user;
GRANT ALL ON range_parted, mintab TO regress_range_parted_user;
CREATE POLICY seeall ON range_parted AS PERMISSIVE FOR SELECT USING (true);
diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql
index 247b8e23b23..acff5824ebb 100644
--- a/src/test/regress/sql/vacuum.sql
+++ b/src/test/regress/sql/vacuum.sql
@@ -452,7 +452,7 @@ VACUUM (ANALYZE) vacowned_part1;
VACUUM (ANALYZE) vacowned_part2;
RESET ROLE;
-- Partitioned table and one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO regress_vacuum;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
@@ -466,7 +466,7 @@ VACUUM (ANALYZE) vacowned_part1;
VACUUM (ANALYZE) vacowned_part2;
RESET ROLE;
-- Only one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO CURRENT_USER;
+ALTER TABLE ONLY vacowned_parted OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
VACUUM vacowned_part1;
@@ -479,7 +479,7 @@ VACUUM (ANALYZE) vacowned_part1;
VACUUM (ANALYZE) vacowned_part2;
RESET ROLE;
-- Only partitioned table owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
--
2.50.1 (Apple Git-155)
^ permalink raw reply [nested|flat] 19+ messages in thread
* Re: ALTER TABLE: warn when actions do not recurse to partitions
2026-01-13 11:16 Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-14 00:52 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 05:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 19:27 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-23 00:11 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-23 09:57 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-03-09 06:46 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-10 15:32 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-11 07:04 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-11 16:39 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-12 01:14 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-12 02:09 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-12 06:37 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-12 21:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Zsolt Parragi <[email protected]>
2026-03-13 03:16 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
@ 2026-04-01 08:01 ` Chao Li <[email protected]>
2026-04-01 08:03 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
0 siblings, 1 reply; 19+ messages in thread
From: Chao Li @ 2026-04-01 08:01 UTC (permalink / raw)
To: Postgres hackers <[email protected]>; +Cc: Greg Sabino Mullane <[email protected]>; Jim Jones <[email protected]>; David G. Johnston <[email protected]>; Zsolt Parragi <[email protected]>
> On Mar 13, 2026, at 11:16, Chao Li <[email protected]> wrote:
>
>
>
>> On Mar 13, 2026, at 05:45, Zsolt Parragi <[email protected]> wrote:
>>
>> Hello
>>
>>> Affected sub-commands include:
>>> ...
>>
>> Shouldn't SET ACCESS METHOD be included in this list?
>>
>> It only changes the catalog entry for the parent, existing partitions
>> stay as-is. It only sets the access method for future direct child
>> partitions.
>
> Good catch. I missed SET ACCESS METHOD. Thanks a lot.
>
> PFA v9:
> * Included SET ACCESS METHOD
> * Added a new test case where if the sub-command fails, then the notice message should not show up
>
> Best regards,
> --
> Chao Li (Evan)
> HighGo Software Co., Ltd.
> https://www.highgo.com/
>
>
>
>
> <v9-0001-ALTER-TABLE-emit-NOTICE-when-ONLY-is-omitted-but-.patch>
PFA v10 - fixed a test failure.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
^ permalink raw reply [nested|flat] 19+ messages in thread
* Re: ALTER TABLE: warn when actions do not recurse to partitions
2026-01-13 11:16 Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-14 00:52 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 05:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 19:27 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-23 00:11 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-23 09:57 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-03-09 06:46 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-10 15:32 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-11 07:04 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-11 16:39 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-12 01:14 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-12 02:09 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-12 06:37 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-12 21:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Zsolt Parragi <[email protected]>
2026-03-13 03:16 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-04-01 08:01 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
@ 2026-04-01 08:03 ` Chao Li <[email protected]>
2026-05-04 11:24 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
0 siblings, 1 reply; 19+ messages in thread
From: Chao Li @ 2026-04-01 08:03 UTC (permalink / raw)
To: Postgres hackers <[email protected]>; +Cc: Greg Sabino Mullane <[email protected]>; Jim Jones <[email protected]>; David G. Johnston <[email protected]>; Zsolt Parragi <[email protected]>
> On Apr 1, 2026, at 16:01, Chao Li <[email protected]> wrote:
>
>
>
>> On Mar 13, 2026, at 11:16, Chao Li <[email protected]> wrote:
>>
>>
>>
>>> On Mar 13, 2026, at 05:45, Zsolt Parragi <[email protected]> wrote:
>>>
>>> Hello
>>>
>>>> Affected sub-commands include:
>>>> ...
>>>
>>> Shouldn't SET ACCESS METHOD be included in this list?
>>>
>>> It only changes the catalog entry for the parent, existing partitions
>>> stay as-is. It only sets the access method for future direct child
>>> partitions.
>>
>> Good catch. I missed SET ACCESS METHOD. Thanks a lot.
>>
>> PFA v9:
>> * Included SET ACCESS METHOD
>> * Added a new test case where if the sub-command fails, then the notice message should not show up
>>
>> Best regards,
>> --
>> Chao Li (Evan)
>> HighGo Software Co., Ltd.
>> https://www.highgo.com/
>>
>>
>>
>>
>> <v9-0001-ALTER-TABLE-emit-NOTICE-when-ONLY-is-omitted-but-.patch>
>
> PFA v10 - fixed a test failure.
>
Oops, forgot the attachment.
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
Attachments:
[application/octet-stream] v10-0001-ALTER-TABLE-emit-NOTICE-when-ONLY-is-omitted-but.patch (50.9K, 2-v10-0001-ALTER-TABLE-emit-NOTICE-when-ONLY-is-omitted-but.patch)
download | inline diff:
From 269c0b03046a7cd0ec4cd7ed0136b0f8db450ccf Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <[email protected]>
Date: Mon, 12 Jan 2026 16:56:58 +0800
Subject: [PATCH v10] ALTER TABLE: emit NOTICE when ONLY is omitted but no
partition recursion occurs
Some ALTER TABLE sub-commands do not recurse to partitions even when ONLY
is not specified on a partitioned table. This can be surprising, since
the default expectation is that commands propagate to all partitions.
Emit a NOTICE in such cases to make the behavior explicit, and advise how
to suppress the message or modify partitions individually.
Affected sub-commands include:
- ALTER COLUMN SET/RESET attribute_option
- ALTER COLUMN SET COMPRESSION
- ENABLE/DISABLE RULE
- ENABLE/DISABLE/FORCE/NO FORCE ROW LEVEL SECURITY
- REPLICA IDENTITY
- OWNER TO
- SET ACCESS METHOD
- SET TABLESPACE
- SET SCHEMA
RENAME is intentionally excluded. Using ONLY (or not) has no effect for
RENAME, since relation names are independent by nature and there is no
expectation of recursion.
OF / NOT OF are also excluded. Using ONLY has no effect for these
commands, as they apply only to the partitioned table itself and not to
its partitions.
Regression tests are updated to use ONLY where appropriate.
Author: Chao Li <[email protected]>
Reviewed-by: David G. Johnston <[email protected]>
Reviewed-by: Greg Sabino Mullane <[email protected]>
Reviewed-by: Jim Jones <[email protected]>
Reviewed-by: Zsolt Parragi <[email protected]>
Discussion: https://postgr.es/m/CAEoWx2=SLga-xH09Cq_PAvsHhQHrBK+V0vF821JKgzS=Bm0haA@mail.gmail.com
---
src/backend/commands/tablecmds.c | 138 ++++++++++++++++--
src/include/nodes/parsenodes.h | 1 +
src/test/regress/expected/alter_table.out | 92 +++++++++++-
src/test/regress/expected/cluster.out | 2 +-
src/test/regress/expected/create_am.out | 16 +-
src/test/regress/expected/graph_table_rls.out | 6 +-
src/test/regress/expected/merge.out | 4 +-
src/test/regress/expected/partition_merge.out | 2 +-
src/test/regress/expected/partition_split.out | 2 +-
src/test/regress/expected/privileges.out | 2 +-
src/test/regress/expected/rowsecurity.out | 4 +-
src/test/regress/expected/tablespace.out | 4 +-
src/test/regress/expected/update.out | 2 +-
src/test/regress/expected/vacuum.out | 6 +-
src/test/regress/sql/alter_table.sql | 71 ++++++++-
src/test/regress/sql/cluster.sql | 2 +-
src/test/regress/sql/create_am.sql | 16 +-
src/test/regress/sql/graph_table_rls.sql | 6 +-
src/test/regress/sql/merge.sql | 4 +-
src/test/regress/sql/partition_merge.sql | 2 +-
src/test/regress/sql/partition_split.sql | 2 +-
src/test/regress/sql/privileges.sql | 2 +-
src/test/regress/sql/rowsecurity.sql | 4 +-
src/test/regress/sql/tablespace.sql | 4 +-
src/test/regress/sql/update.sql | 2 +-
src/test/regress/sql/vacuum.sql | 6 +-
26 files changed, 337 insertions(+), 65 deletions(-)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 8b4ebc6f226..50a16666bde 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -476,7 +476,8 @@ static void ATController(AlterTableStmt *parsetree,
AlterTableUtilityContext *context);
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
bool recurse, bool recursing, LOCKMODE lockmode,
- AlterTableUtilityContext *context);
+ AlterTableUtilityContext *context,
+ List **pendingNotice);
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
AlterTableUtilityContext *context);
static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
@@ -760,6 +761,10 @@ static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation
static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
Relation rel, PartitionCmd *cmd,
AlterTableUtilityContext *context);
+static void CollectPartitionNoRecurseNotice(AlterTableType cmdtype, Relation rel,
+ bool recurse, bool recursing,
+ List **pendingNotice);
+static void EmitPartitionNoRecurseNotice(List *pendingNotice);
/* ----------------------------------------------------------------
* DefineRelation
@@ -4909,13 +4914,14 @@ ATController(AlterTableStmt *parsetree,
{
List *wqueue = NIL;
ListCell *lcmd;
+ List *pendingNotice = NIL;
/* Phase 1: preliminary examination of commands, create work queue */
foreach(lcmd, cmds)
{
AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
- ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
+ ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context, &pendingNotice);
}
/* Close the relation, but keep lock until commit */
@@ -4926,6 +4932,9 @@ ATController(AlterTableStmt *parsetree,
/* Phase 3: scan/rewrite tables as needed, and run afterStmts */
ATRewriteTables(parsetree, &wqueue, lockmode, context);
+
+ /* Emit post-notice for partitions that were not recursed into. */
+ EmitPartitionNoRecurseNotice(pendingNotice);
}
/*
@@ -4940,7 +4949,8 @@ ATController(AlterTableStmt *parsetree,
static void
ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
bool recurse, bool recursing, LOCKMODE lockmode,
- AlterTableUtilityContext *context)
+ AlterTableUtilityContext *context,
+ List **pendingNotice)
{
AlteredTableInfo *tab;
AlterTablePass pass = AT_PASS_UNSET;
@@ -5089,6 +5099,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATT_MATVIEW | ATT_FOREIGN_TABLE);
/* This command never recurses */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
ATSimplePermissions(cmd->subtype, rel,
@@ -5104,6 +5116,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_DropColumn: /* DROP COLUMN */
ATSimplePermissions(cmd->subtype, rel,
@@ -5171,6 +5185,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_ClusterOn: /* CLUSTER ON */
case AT_DropCluster: /* SET WITHOUT CLUSTER */
@@ -5207,6 +5223,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATPrepSetAccessMethod(tab, rel, cmd->name);
pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_SetTableSpace: /* SET TABLESPACE */
ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE |
@@ -5214,6 +5232,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
pass = AT_PASS_MISC; /* doesn't actually matter */
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_SetRelOptions: /* SET (...) */
case AT_ResetRelOptions: /* RESET (...) */
@@ -5223,6 +5243,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATT_MATVIEW | ATT_INDEX);
/* This command never recurses */
/* No command-specific prep needed */
+ /* It will check for partitioned table at exec time */
pass = AT_PASS_MISC;
break;
case AT_AddInherit: /* INHERIT */
@@ -5260,8 +5281,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATSimplePermissions(cmd->subtype, rel,
ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
pass = AT_PASS_MISC;
- /* This command never recurses */
- /* No command-specific prep needed */
+ /* This command doesn't recurse to partitions, so notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_EnableTrig: /* ENABLE TRIGGER variants */
case AT_EnableAlwaysTrig:
@@ -5282,17 +5303,30 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
case AT_EnableAlwaysRule:
case AT_EnableReplicaRule:
case AT_DisableRule:
- case AT_AddOf: /* OF */
- case AT_DropOf: /* NOT OF */
- case AT_EnableRowSecurity:
+ case AT_EnableRowSecurity: /* ENABLE/DISABLE ROW SECURITY variants */
case AT_DisableRowSecurity:
- case AT_ForceRowSecurity:
+ case AT_ForceRowSecurity: /* FORCE/NO FORCE ROW SECURITY variants */
case AT_NoForceRowSecurity:
ATSimplePermissions(cmd->subtype, rel,
ATT_TABLE | ATT_PARTITIONED_TABLE);
/* These commands never recurse */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
+ break;
+ case AT_AddOf: /* OF */
+ case AT_DropOf: /* NOT OF */
+ ATSimplePermissions(cmd->subtype, rel,
+ ATT_TABLE | ATT_PARTITIONED_TABLE);
+ /* These commands never recurse */
+ /* No command-specific prep needed */
+
+ /*
+ * They only work on partitioned tables but child partitions, thus
+ * no need to emit a notice
+ */
+ pass = AT_PASS_MISC;
break;
case AT_GenericOptions:
ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
@@ -6786,6 +6820,8 @@ alter_table_type_to_string(AlterTableType cmdtype)
return "ALTER COLUMN ... DROP IDENTITY";
case AT_ReAddStatistics:
return NULL; /* not real grammar */
+ case AT_SetSchema:
+ return "SET SCHEMA";
}
return NULL;
@@ -6907,7 +6943,7 @@ ATSimpleRecursion(List **wqueue, Relation rel,
/* find_all_inheritors already got lock */
childrel = relation_open(childrelid, NoLock);
CheckAlterTableIsSafe(childrel);
- ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
+ ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context, NULL);
relation_close(childrel, NoLock);
}
}
@@ -6970,7 +7006,7 @@ ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
childrel = relation_open(childrelid, lockmode);
CheckAlterTableIsSafe(childrel);
- ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
+ ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context, NULL);
relation_close(childrel, NoLock);
}
}
@@ -9641,7 +9677,7 @@ ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
newcmd->recurse = true;
newcmd->def = (Node *) nnconstr;
- ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
+ ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context, NULL);
}
}
@@ -14891,7 +14927,7 @@ ATPrepAlterColumnType(List **wqueue,
errdetail("USING expression contains a whole-row table reference.")));
pfree(attmap);
}
- ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
+ ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context, NULL);
relation_close(childrel, NoLock);
}
}
@@ -19189,6 +19225,7 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
RangeVar *newrv;
ObjectAddresses *objsMoved;
ObjectAddress myself;
+ List *pendingNotice = NIL;
relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
stmt->missing_ok ? RVR_MISSING_OK : 0,
@@ -19205,6 +19242,13 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
rel = relation_open(relid, NoLock);
+ /*
+ * SET SCHEMA doesn't recurse to children, emit a notice if ONLY is not
+ * specified. As this action doesn't go through ATPrepCmd, we have to emit
+ * the notice here.
+ */
+ CollectPartitionNoRecurseNotice(AT_SetSchema, rel, stmt->relation->inh, false, &pendingNotice);
+
oldNspOid = RelationGetNamespace(rel);
/* If it's an owned sequence, disallow moving it by itself. */
@@ -19242,6 +19286,8 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
/* close rel, but keep lock until commit */
relation_close(rel, NoLock);
+ EmitPartitionNoRecurseNotice(pendingNotice);
+
return myself;
}
@@ -23601,3 +23647,69 @@ ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
/* Restore the userid and security context. */
SetUserIdAndSecContext(save_userid, save_sec_context);
}
+
+/*
+ * When ONLY is not specified with a partitioned table, it is expected that the
+ * command recurses to all partitions. However, some sub-commands do not recurse.
+ * In such cases, emit a NOTICE to make this behavior explicit to the user.
+ */
+static void
+CollectPartitionNoRecurseNotice(AlterTableType cmdtype, Relation rel, bool recurse, bool recursing,
+ List **pendingNotice)
+{
+ if (pendingNotice == NULL)
+ return;
+
+ /* Only emit the notice at the top level of recursion */
+ if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && recurse && !recursing)
+ {
+ PartitionDesc pd = RelationGetPartitionDesc(rel, true);
+ int nparts = pd->nparts;
+ const char *action_str;
+ char *notice_msg;
+ const ListCell *cell;
+
+ /* Emit a notice only if there are partitions */
+ if (nparts == 0)
+ return;
+
+ action_str = alter_table_type_to_string(cmdtype);
+ notice_msg = psprintf(_("ALTER action %s on relation \"%s\" does not affect present partitions"),
+ action_str,
+ RelationGetRelationName(rel));
+
+ foreach(cell, *pendingNotice)
+ {
+ if (strcmp((char *) lfirst(cell), notice_msg) == 0)
+ {
+ /* Skip the duplicate notice message */
+ pfree(notice_msg);
+ return;
+ }
+ }
+ *pendingNotice = lappend(*pendingNotice, notice_msg);
+ }
+}
+
+static void
+EmitPartitionNoRecurseNotice(List *pendingNotice)
+{
+ ListCell *cell;
+ int len;
+ int i = 0;
+
+ len = list_length(pendingNotice);
+ foreach(cell, pendingNotice)
+ {
+ char *notice_msg = (char *) lfirst(cell);
+
+ /* Only emit the hint for the last notice */
+ i++;
+ ereport(NOTICE, errmsg("%s", notice_msg),
+ (i == len) ?
+ errhint("Partitions may be modified individually, or specify ONLY to suppress this message.")
+ : 0);
+ pfree(notice_msg);
+ }
+ list_free(pendingNotice);
+}
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index a501cdfb249..af69c0a341c 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2568,6 +2568,7 @@ typedef enum AlterTableType
AT_SetIdentity, /* SET identity column options */
AT_DropIdentity, /* DROP IDENTITY */
AT_ReAddStatistics, /* internal to commands/tablecmds.c */
+ AT_SetSchema, /* SET SCHEMA */
} AlterTableType;
typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index dad9d36937e..06973a1cd4a 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -4603,8 +4603,98 @@ SELECT * FROM list_parted;
---
(0 rows)
+CREATE TABLE list_parted4 (a int, b text) PARTITION BY LIST (a);
+CREATE TABLE list_parted4_1 PARTITION OF list_parted4 FOR VALUES IN (1);
+-- set column attribute on partitioned table without ONLY should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+NOTICE: ALTER action ALTER COLUMN ... SET on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE list_parted4 ALTER COLUMN b RESET (n_distinct);
+NOTICE: ALTER action ALTER COLUMN ... RESET on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b RESET (n_distinct);
+-- enable/disable rules on partitioned tables without ONLY should get a notice
+CREATE RULE list_parted4_rule AS ON INSERT TO list_parted4 DO INSTEAD NOTHING;
+ALTER TABLE list_parted4 DISABLE RULE list_parted4_rule;
+NOTICE: ALTER action DISABLE RULE on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 DISABLE RULE list_parted4_rule;
+ALTER TABLE list_parted4 ENABLE RULE list_parted4_rule;
+NOTICE: ALTER action ENABLE RULE on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ENABLE RULE list_parted4_rule;
+DROP RULE list_parted4_rule ON list_parted4;
+-- enable/disable row level security on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 ENABLE ROW LEVEL SECURITY;
+NOTICE: ALTER action ENABLE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 DISABLE ROW LEVEL SECURITY;
+NOTICE: ALTER action DISABLE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 DISABLE ROW LEVEL SECURITY;
+-- force/no force row level security on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 FORCE ROW LEVEL SECURITY;
+NOTICE: ALTER action FORCE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 FORCE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 NO FORCE ROW LEVEL SECURITY;
+NOTICE: ALTER action NO FORCE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 NO FORCE ROW LEVEL SECURITY;
+-- set replica identity on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 REPLICA IDENTITY FULL;
+NOTICE: ALTER action REPLICA IDENTITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY FULL;
+ALTER TABLE list_parted4 REPLICA IDENTITY NOTHING;
+NOTICE: ALTER action REPLICA IDENTITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY NOTHING;
+-- set compression on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+NOTICE: ALTER action ALTER COLUMN ... SET COMPRESSION on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+-- set owner on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 OWNER TO regress_alter_table_user1;
+NOTICE: ALTER action OWNER TO on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 OWNER TO regress_alter_table_user1;
+-- set access method on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 SET ACCESS METHOD heap;
+NOTICE: ALTER action SET ACCESS METHOD on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 SET ACCESS METHOD heap;
+-- set schema on partitioned tables without ONLY should get a notice
+CREATE SCHEMA alter_table_test_schema;
+ALTER TABLE list_parted4 SET SCHEMA alter_table_test_schema;
+NOTICE: ALTER action SET SCHEMA on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY alter_table_test_schema.list_parted4 SET SCHEMA public;
+DROP SCHEMA alter_table_test_schema CASCADE;
+-- when there are multiple sub-command, notice should not duplicated
+ALTER TABLE list_parted4
+ ALTER COLUMN b SET COMPRESSION pglz,
+ ALTER COLUMN b SET COMPRESSION pglz, -- duplicate, but should not get duplicate notice
+ FORCE ROW LEVEL SECURITY,
+ REPLICA IDENTITY FULL,
+ OWNER TO regress_alter_table_user1;
+NOTICE: ALTER action ALTER COLUMN ... SET COMPRESSION on relation "list_parted4" does not affect present partitions
+NOTICE: ALTER action FORCE ROW SECURITY on relation "list_parted4" does not affect present partitions
+NOTICE: ALTER action REPLICA IDENTITY on relation "list_parted4" does not affect present partitions
+NOTICE: ALTER action OWNER TO on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+-- when the sub-command fails, notice should not be printed
+ALTER TABLE list_parted4 SET ACCESS METHOD invalid_am;
+ERROR: access method "invalid_am" does not exist
+-- list_parted5 is a partitioned table that has no partition.
+CREATE TABLE list_parted5 (a int, b text) PARTITION BY LIST (a);
+-- as it has no partition, there should be no notice when altering it without ONLY
+ALTER TABLE list_parted5 FORCE ROW LEVEL SECURITY;
-- cleanup
-DROP TABLE list_parted, list_parted2, range_parted, list_parted3;
+DROP TABLE list_parted, list_parted2, range_parted, list_parted3, list_parted4, list_parted5;
DROP TABLE fail_def_part;
DROP TABLE hash_parted;
-- more tests for certain multi-level partitioning scenarios
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 269f163efa6..b422b42eb2d 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -549,7 +549,7 @@ SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
RESET SESSION AUTHORIZATION;
-ALTER TABLE ptnowner OWNER TO regress_ptnowner;
+ALTER TABLE ONLY ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
SELECT oid, relname, relfilenode FROM pg_partition_tree('ptnowner') AS tree
JOIN pg_class AS c ON c.oid=tree.relid;
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
index c1a95157251..8c428270118 100644
--- a/src/test/regress/expected/create_am.out
+++ b/src/test/regress/expected/create_am.out
@@ -380,7 +380,7 @@ SELECT pg_describe_object(classid, objid, objsubid) AS obj,
(0 rows)
-- New default is set, with dependency added.
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
amname
@@ -401,7 +401,7 @@ SELECT pg_describe_object(classid, objid, objsubid) AS obj,
-- Default is set, with dependency updated.
SET LOCAL default_table_access_method = 'heap2';
-ALTER TABLE am_partitioned SET ACCESS METHOD heap;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
amname
@@ -422,7 +422,7 @@ SELECT pg_describe_object(classid, objid, objsubid) AS obj,
-- Default and AM set in the clause are the same, relam should be set.
SET LOCAL default_table_access_method = 'heap2';
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
amname
@@ -431,7 +431,7 @@ SELECT a.amname FROM pg_class c, pg_am a
(1 row)
-- Reset to default
-ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD DEFAULT;
SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
relam
-------
@@ -453,10 +453,10 @@ SET LOCAL default_table_access_method = 'heap2';
CREATE TABLE am_partitioned_1 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 1);
SET LOCAL default_table_access_method = 'heap';
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
CREATE TABLE am_partitioned_2 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 2);
-ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD DEFAULT;
SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
relam
-------
@@ -466,7 +466,7 @@ SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
CREATE TABLE am_partitioned_3 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 3);
-- Partitioned table with relam at 0
-ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD DEFAULT;
CREATE TABLE am_partitioned_5p PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 5) PARTITION BY hash(y);
-- Partitions of this partitioned table inherit default AM at creation
@@ -474,7 +474,7 @@ CREATE TABLE am_partitioned_5p PARTITION OF am_partitioned
CREATE TABLE am_partitioned_5p1 PARTITION OF am_partitioned_5p
FOR VALUES WITH (MODULUS 10, REMAINDER 1);
-- Partitioned table with relam set.
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
CREATE TABLE am_partitioned_6p PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 6) PARTITION BY hash(y);
-- Partitions of this partitioned table inherit its AM.
diff --git a/src/test/regress/expected/graph_table_rls.out b/src/test/regress/expected/graph_table_rls.out
index 0e719c7ebd7..c71e2a13796 100644
--- a/src/test/regress/expected/graph_table_rls.out
+++ b/src/test/regress/expected/graph_table_rls.out
@@ -249,7 +249,7 @@ INSERT INTO accessed VALUES
-- Enable RLS and move policies p1 and p2 to parent table but leave p3 and p4 on
-- child table. The policies on child table are not applied when querying parent
-- table.
-ALTER TABLE document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY document ENABLE ROW LEVEL SECURITY;
DROP POLICY p1 ON document_people;
DROP POLICY p2 ON document_people;
CREATE POLICY p1 ON document AS PERMISSIVE
@@ -445,7 +445,7 @@ GRANT SELECT ON document TO public;
ALTER TABLE document ATTACH PARTITION document_people FOR VALUES IN ('People');
ALTER TABLE document ATTACH PARTITION document_places FOR VALUES IN ('Places');
-- Enable RLS on partitioned table
-ALTER TABLE document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY document ENABLE ROW LEVEL SECURITY;
-- create policies on partitioned table
CREATE POLICY p1 ON document AS PERMISSIVE
USING (dlevel <= (SELECT seclv FROM users WHERE pguser = current_user));
@@ -759,7 +759,7 @@ NOTICE: f_leak => New York
(12 rows)
-- FORCE ROW LEVEL SECURITY applies RLS to owners too
-ALTER TABLE document FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY document FORCE ROW LEVEL SECURITY;
EXECUTE graph_rls_query;
pguser | aid | dtitle | dlevel
--------+-----+--------+--------
diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out
index 9cb1d87066a..bd6aac72336 100644
--- a/src/test/regress/expected/merge.out
+++ b/src/test/regress/expected/merge.out
@@ -2298,8 +2298,8 @@ SELECT * FROM pa_target ORDER BY tid, val;
ROLLBACK;
-- test RLS enforcement
BEGIN;
-ALTER TABLE pa_target ENABLE ROW LEVEL SECURITY;
-ALTER TABLE pa_target FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target FORCE ROW LEVEL SECURITY;
CREATE POLICY pa_target_pol ON pa_target USING (tid != 0);
MERGE INTO pa_target t
USING pa_source s
diff --git a/src/test/regress/expected/partition_merge.out b/src/test/regress/expected/partition_merge.out
index 925fe4f570a..f629fc7f3e5 100644
--- a/src/test/regress/expected/partition_merge.out
+++ b/src/test/regress/expected/partition_merge.out
@@ -810,7 +810,7 @@ SET SESSION AUTHORIZATION regress_partition_merge_bob;
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
ERROR: must be owner of table t
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_merge_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_merge_bob;
SET SESSION AUTHORIZATION regress_partition_merge_bob;
-- ERROR: must be owner of table tp_0_1
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
diff --git a/src/test/regress/expected/partition_split.out b/src/test/regress/expected/partition_split.out
index 13ca733f9fa..90090cd7305 100644
--- a/src/test/regress/expected/partition_split.out
+++ b/src/test/regress/expected/partition_split.out
@@ -1375,7 +1375,7 @@ ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); --error
ERROR: must be owner of table t
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_split_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_split_bob;
SET SESSION AUTHORIZATION regress_partition_split_bob;
ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
(PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 7069e9febb8..35c1d0e49c8 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -1908,7 +1908,7 @@ SELECT brin_summarize_range('sro_brin', 0);
DROP TABLE sro_tab;
-- Check with a partitioned table
CREATE TABLE sro_ptab (a int) PARTITION BY RANGE (a);
-ALTER TABLE sro_ptab OWNER TO regress_sro_user;
+ALTER TABLE ONLY sro_ptab OWNER TO regress_sro_user;
CREATE TABLE sro_part PARTITION OF sro_ptab FOR VALUES FROM (1) TO (10);
ALTER TABLE sro_part OWNER TO regress_sro_user;
INSERT INTO sro_ptab VALUES (1), (2), (3);
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index 3a5e82c35bd..c75074b219b 100644
--- a/src/test/regress/expected/rowsecurity.out
+++ b/src/test/regress/expected/rowsecurity.out
@@ -1233,7 +1233,7 @@ INSERT INTO part_document VALUES
( 8, 55, 2, 'regress_rls_carol', 'great satire'),
( 9, 11, 1, 'regress_rls_dave', 'awesome science fiction'),
(10, 99, 2, 'regress_rls_dave', 'awesome technology book');
-ALTER TABLE part_document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY part_document ENABLE ROW LEVEL SECURITY;
-- Create policy on parent
-- user's security level must be higher than or equal to document's
CREATE POLICY pp1 ON part_document AS PERMISSIVE
@@ -4915,7 +4915,7 @@ CREATE TABLE rls_ptbl (a int) PARTITION BY RANGE (a);
CREATE TABLE rls_part PARTITION OF rls_ptbl FOR VALUES FROM (-100) TO (100);
INSERT INTO rls_ptbl SELECT x/10 FROM generate_series(1, 100) x;
ANALYZE rls_ptbl, rls_part;
-ALTER TABLE rls_ptbl ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY rls_ptbl ENABLE ROW LEVEL SECURITY;
ALTER TABLE rls_part ENABLE ROW LEVEL SECURITY;
GRANT SELECT ON rls_ptbl TO regress_rls_alice;
GRANT SELECT ON rls_part TO regress_rls_alice;
diff --git a/src/test/regress/expected/tablespace.out b/src/test/regress/expected/tablespace.out
index f0dd25cdf0c..3d9c44ba64f 100644
--- a/src/test/regress/expected/tablespace.out
+++ b/src/test/regress/expected/tablespace.out
@@ -290,13 +290,13 @@ CREATE TABLE testschema.part_2 PARTITION OF testschema.part FOR VALUES IN (2);
SET default_tablespace TO pg_global;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
ERROR: only shared relations can be placed in pg_global tablespace
-ALTER TABLE testschema.part SET TABLESPACE regress_tblspace;
+ALTER TABLE ONLY testschema.part SET TABLESPACE regress_tblspace;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
CREATE TABLE testschema.part_4 PARTITION OF testschema.part FOR VALUES IN (4)
TABLESPACE pg_default;
CREATE TABLE testschema.part_56 PARTITION OF testschema.part FOR VALUES IN (5, 6)
PARTITION BY LIST (a);
-ALTER TABLE testschema.part SET TABLESPACE pg_default;
+ALTER TABLE ONLY testschema.part SET TABLESPACE pg_default;
CREATE TABLE testschema.part_78 PARTITION OF testschema.part FOR VALUES IN (7, 8)
PARTITION BY LIST (a);
ERROR: only shared relations can be placed in pg_global tablespace
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index eef2bac1cbf..584b40598eb 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -607,7 +607,7 @@ DROP TRIGGER trig_d15_20 ON part_d_15_20;
DROP FUNCTION func_parted_mod_b();
-- RLS policies with update-row-movement
-----------------------------------------
-ALTER TABLE range_parted ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY range_parted ENABLE ROW LEVEL SECURITY;
CREATE USER regress_range_parted_user;
GRANT ALL ON range_parted, mintab TO regress_range_parted_user;
CREATE POLICY seeall ON range_parted AS PERMISSIVE FOR SELECT USING (true);
diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out
index d4696bc3325..f22bb2127e1 100644
--- a/src/test/regress/expected/vacuum.out
+++ b/src/test/regress/expected/vacuum.out
@@ -614,7 +614,7 @@ VACUUM (ANALYZE) vacowned_part2;
WARNING: permission denied to vacuum "vacowned_part2", skipping it
RESET ROLE;
-- Partitioned table and one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO regress_vacuum;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
@@ -634,7 +634,7 @@ VACUUM (ANALYZE) vacowned_part2;
WARNING: permission denied to vacuum "vacowned_part2", skipping it
RESET ROLE;
-- Only one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO CURRENT_USER;
+ALTER TABLE ONLY vacowned_parted OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
WARNING: permission denied to vacuum "vacowned_parted", skipping it
@@ -656,7 +656,7 @@ VACUUM (ANALYZE) vacowned_part2;
WARNING: permission denied to vacuum "vacowned_part2", skipping it
RESET ROLE;
-- Only partitioned table owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index f5f13bbd3e7..494d5d1511f 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2912,8 +2912,77 @@ ALTER TABLE list_parted2 ALTER COLUMN b TYPE text;
ALTER TABLE list_parted DROP COLUMN b;
SELECT * FROM list_parted;
+CREATE TABLE list_parted4 (a int, b text) PARTITION BY LIST (a);
+CREATE TABLE list_parted4_1 PARTITION OF list_parted4 FOR VALUES IN (1);
+
+-- set column attribute on partitioned table without ONLY should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+ALTER TABLE list_parted4 ALTER COLUMN b RESET (n_distinct);
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b RESET (n_distinct);
+
+-- enable/disable rules on partitioned tables without ONLY should get a notice
+CREATE RULE list_parted4_rule AS ON INSERT TO list_parted4 DO INSTEAD NOTHING;
+ALTER TABLE list_parted4 DISABLE RULE list_parted4_rule;
+ALTER TABLE ONLY list_parted4 DISABLE RULE list_parted4_rule;
+ALTER TABLE list_parted4 ENABLE RULE list_parted4_rule;
+ALTER TABLE ONLY list_parted4 ENABLE RULE list_parted4_rule;
+DROP RULE list_parted4_rule ON list_parted4;
+
+-- enable/disable row level security on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 DISABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 DISABLE ROW LEVEL SECURITY;
+
+-- force/no force row level security on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 FORCE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 NO FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 NO FORCE ROW LEVEL SECURITY;
+
+-- set replica identity on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 REPLICA IDENTITY FULL;
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY FULL;
+ALTER TABLE list_parted4 REPLICA IDENTITY NOTHING;
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY NOTHING;
+
+-- set compression on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+
+-- set owner on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 OWNER TO regress_alter_table_user1;
+ALTER TABLE ONLY list_parted4 OWNER TO regress_alter_table_user1;
+
+-- set access method on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 SET ACCESS METHOD heap;
+ALTER TABLE ONLY list_parted4 SET ACCESS METHOD heap;
+
+-- set schema on partitioned tables without ONLY should get a notice
+CREATE SCHEMA alter_table_test_schema;
+ALTER TABLE list_parted4 SET SCHEMA alter_table_test_schema;
+ALTER TABLE ONLY alter_table_test_schema.list_parted4 SET SCHEMA public;
+DROP SCHEMA alter_table_test_schema CASCADE;
+
+-- when there are multiple sub-command, notice should not duplicated
+ALTER TABLE list_parted4
+ ALTER COLUMN b SET COMPRESSION pglz,
+ ALTER COLUMN b SET COMPRESSION pglz, -- duplicate, but should not get duplicate notice
+ FORCE ROW LEVEL SECURITY,
+ REPLICA IDENTITY FULL,
+ OWNER TO regress_alter_table_user1;
+
+-- when the sub-command fails, notice should not be printed
+ALTER TABLE list_parted4 SET ACCESS METHOD invalid_am;
+
+-- list_parted5 is a partitioned table that has no partition.
+CREATE TABLE list_parted5 (a int, b text) PARTITION BY LIST (a);
+-- as it has no partition, there should be no notice when altering it without ONLY
+ALTER TABLE list_parted5 FORCE ROW LEVEL SECURITY;
+
-- cleanup
-DROP TABLE list_parted, list_parted2, range_parted, list_parted3;
+DROP TABLE list_parted, list_parted2, range_parted, list_parted3, list_parted4, list_parted5;
DROP TABLE fail_def_part;
DROP TABLE hash_parted;
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index f90c6ec200b..3405e93ab0c 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -260,7 +260,7 @@ ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
RESET SESSION AUTHORIZATION;
-ALTER TABLE ptnowner OWNER TO regress_ptnowner;
+ALTER TABLE ONLY ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
SELECT oid, relname, relfilenode FROM pg_partition_tree('ptnowner') AS tree
JOIN pg_class AS c ON c.oid=tree.relid;
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
index 754fe0c694b..4953be0ab3d 100644
--- a/src/test/regress/sql/create_am.sql
+++ b/src/test/regress/sql/create_am.sql
@@ -241,7 +241,7 @@ SELECT pg_describe_object(classid, objid, objsubid) AS obj,
AND pg_am.oid = pg_depend.refobjid
AND pg_depend.objid = 'am_partitioned'::regclass;
-- New default is set, with dependency added.
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
SELECT pg_describe_object(classid, objid, objsubid) AS obj,
@@ -252,7 +252,7 @@ SELECT pg_describe_object(classid, objid, objsubid) AS obj,
AND pg_depend.objid = 'am_partitioned'::regclass;
-- Default is set, with dependency updated.
SET LOCAL default_table_access_method = 'heap2';
-ALTER TABLE am_partitioned SET ACCESS METHOD heap;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
-- Dependency pinned, hence removed.
@@ -264,11 +264,11 @@ SELECT pg_describe_object(classid, objid, objsubid) AS obj,
AND pg_depend.objid = 'am_partitioned'::regclass;
-- Default and AM set in the clause are the same, relam should be set.
SET LOCAL default_table_access_method = 'heap2';
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
-- Reset to default
-ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD DEFAULT;
SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
-- Upon ALTER TABLE SET ACCESS METHOD on a partitioned table, new partitions
-- will inherit the AM set. Existing partitioned are unchanged.
@@ -280,15 +280,15 @@ SET LOCAL default_table_access_method = 'heap2';
CREATE TABLE am_partitioned_1 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 1);
SET LOCAL default_table_access_method = 'heap';
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
CREATE TABLE am_partitioned_2 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 2);
-ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD DEFAULT;
SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
CREATE TABLE am_partitioned_3 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 3);
-- Partitioned table with relam at 0
-ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD DEFAULT;
CREATE TABLE am_partitioned_5p PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 5) PARTITION BY hash(y);
-- Partitions of this partitioned table inherit default AM at creation
@@ -296,7 +296,7 @@ CREATE TABLE am_partitioned_5p PARTITION OF am_partitioned
CREATE TABLE am_partitioned_5p1 PARTITION OF am_partitioned_5p
FOR VALUES WITH (MODULUS 10, REMAINDER 1);
-- Partitioned table with relam set.
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
CREATE TABLE am_partitioned_6p PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 6) PARTITION BY hash(y);
-- Partitions of this partitioned table inherit its AM.
diff --git a/src/test/regress/sql/graph_table_rls.sql b/src/test/regress/sql/graph_table_rls.sql
index 5837eac402e..a5256170025 100644
--- a/src/test/regress/sql/graph_table_rls.sql
+++ b/src/test/regress/sql/graph_table_rls.sql
@@ -185,7 +185,7 @@ INSERT INTO accessed VALUES
-- Enable RLS and move policies p1 and p2 to parent table but leave p3 and p4 on
-- child table. The policies on child table are not applied when querying parent
-- table.
-ALTER TABLE document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY document ENABLE ROW LEVEL SECURITY;
DROP POLICY p1 ON document_people;
DROP POLICY p2 ON document_people;
CREATE POLICY p1 ON document AS PERMISSIVE
@@ -255,7 +255,7 @@ GRANT SELECT ON document TO public;
ALTER TABLE document ATTACH PARTITION document_people FOR VALUES IN ('People');
ALTER TABLE document ATTACH PARTITION document_places FOR VALUES IN ('Places');
-- Enable RLS on partitioned table
-ALTER TABLE document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY document ENABLE ROW LEVEL SECURITY;
-- create policies on partitioned table
CREATE POLICY p1 ON document AS PERMISSIVE
USING (dlevel <= (SELECT seclv FROM users WHERE pguser = current_user));
@@ -352,7 +352,7 @@ EXECUTE graph_rls_query;
SET SESSION AUTHORIZATION regress_graph_rls_alice;
EXECUTE graph_rls_query;
-- FORCE ROW LEVEL SECURITY applies RLS to owners too
-ALTER TABLE document FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY document FORCE ROW LEVEL SECURITY;
EXECUTE graph_rls_query;
SET row_security TO OFF;
EXECUTE graph_rls_query; -- error
diff --git a/src/test/regress/sql/merge.sql b/src/test/regress/sql/merge.sql
index 2660b19f238..2bffcf7aa98 100644
--- a/src/test/regress/sql/merge.sql
+++ b/src/test/regress/sql/merge.sql
@@ -1422,8 +1422,8 @@ ROLLBACK;
-- test RLS enforcement
BEGIN;
-ALTER TABLE pa_target ENABLE ROW LEVEL SECURITY;
-ALTER TABLE pa_target FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target FORCE ROW LEVEL SECURITY;
CREATE POLICY pa_target_pol ON pa_target USING (tid != 0);
MERGE INTO pa_target t
USING pa_source s
diff --git a/src/test/regress/sql/partition_merge.sql b/src/test/regress/sql/partition_merge.sql
index a211fee2ad1..73bffb530dd 100644
--- a/src/test/regress/sql/partition_merge.sql
+++ b/src/test/regress/sql/partition_merge.sql
@@ -571,7 +571,7 @@ SET SESSION AUTHORIZATION regress_partition_merge_bob;
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_merge_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_merge_bob;
SET SESSION AUTHORIZATION regress_partition_merge_bob;
-- ERROR: must be owner of table tp_0_1
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
diff --git a/src/test/regress/sql/partition_split.sql b/src/test/regress/sql/partition_split.sql
index 37c6d730840..f4f9c7886e4 100644
--- a/src/test/regress/sql/partition_split.sql
+++ b/src/test/regress/sql/partition_split.sql
@@ -960,7 +960,7 @@ ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); --error
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_split_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_split_bob;
SET SESSION AUTHORIZATION regress_partition_split_bob;
ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
(PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index 9f21c2945bd..fcb4d18aede 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -1230,7 +1230,7 @@ SELECT brin_summarize_range('sro_brin', 0);
DROP TABLE sro_tab;
-- Check with a partitioned table
CREATE TABLE sro_ptab (a int) PARTITION BY RANGE (a);
-ALTER TABLE sro_ptab OWNER TO regress_sro_user;
+ALTER TABLE ONLY sro_ptab OWNER TO regress_sro_user;
CREATE TABLE sro_part PARTITION OF sro_ptab FOR VALUES FROM (1) TO (10);
ALTER TABLE sro_part OWNER TO regress_sro_user;
INSERT INTO sro_ptab VALUES (1), (2), (3);
diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql
index 6b3566271df..1a2e80dade0 100644
--- a/src/test/regress/sql/rowsecurity.sql
+++ b/src/test/regress/sql/rowsecurity.sql
@@ -499,7 +499,7 @@ INSERT INTO part_document VALUES
( 9, 11, 1, 'regress_rls_dave', 'awesome science fiction'),
(10, 99, 2, 'regress_rls_dave', 'awesome technology book');
-ALTER TABLE part_document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY part_document ENABLE ROW LEVEL SECURITY;
-- Create policy on parent
-- user's security level must be higher than or equal to document's
@@ -2414,7 +2414,7 @@ CREATE TABLE rls_part PARTITION OF rls_ptbl FOR VALUES FROM (-100) TO (100);
INSERT INTO rls_ptbl SELECT x/10 FROM generate_series(1, 100) x;
ANALYZE rls_ptbl, rls_part;
-ALTER TABLE rls_ptbl ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY rls_ptbl ENABLE ROW LEVEL SECURITY;
ALTER TABLE rls_part ENABLE ROW LEVEL SECURITY;
GRANT SELECT ON rls_ptbl TO regress_rls_alice;
GRANT SELECT ON rls_part TO regress_rls_alice;
diff --git a/src/test/regress/sql/tablespace.sql b/src/test/regress/sql/tablespace.sql
index c43a59e5957..8eff2914a5b 100644
--- a/src/test/regress/sql/tablespace.sql
+++ b/src/test/regress/sql/tablespace.sql
@@ -195,13 +195,13 @@ SET default_tablespace TO regress_tblspace;
CREATE TABLE testschema.part_2 PARTITION OF testschema.part FOR VALUES IN (2);
SET default_tablespace TO pg_global;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
-ALTER TABLE testschema.part SET TABLESPACE regress_tblspace;
+ALTER TABLE ONLY testschema.part SET TABLESPACE regress_tblspace;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
CREATE TABLE testschema.part_4 PARTITION OF testschema.part FOR VALUES IN (4)
TABLESPACE pg_default;
CREATE TABLE testschema.part_56 PARTITION OF testschema.part FOR VALUES IN (5, 6)
PARTITION BY LIST (a);
-ALTER TABLE testschema.part SET TABLESPACE pg_default;
+ALTER TABLE ONLY testschema.part SET TABLESPACE pg_default;
CREATE TABLE testschema.part_78 PARTITION OF testschema.part FOR VALUES IN (7, 8)
PARTITION BY LIST (a);
CREATE TABLE testschema.part_910 PARTITION OF testschema.part FOR VALUES IN (9, 10)
diff --git a/src/test/regress/sql/update.sql b/src/test/regress/sql/update.sql
index 8b4707eb9c3..56b1edb00ce 100644
--- a/src/test/regress/sql/update.sql
+++ b/src/test/regress/sql/update.sql
@@ -341,7 +341,7 @@ DROP FUNCTION func_parted_mod_b();
-- RLS policies with update-row-movement
-----------------------------------------
-ALTER TABLE range_parted ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY range_parted ENABLE ROW LEVEL SECURITY;
CREATE USER regress_range_parted_user;
GRANT ALL ON range_parted, mintab TO regress_range_parted_user;
CREATE POLICY seeall ON range_parted AS PERMISSIVE FOR SELECT USING (true);
diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql
index 247b8e23b23..acff5824ebb 100644
--- a/src/test/regress/sql/vacuum.sql
+++ b/src/test/regress/sql/vacuum.sql
@@ -452,7 +452,7 @@ VACUUM (ANALYZE) vacowned_part1;
VACUUM (ANALYZE) vacowned_part2;
RESET ROLE;
-- Partitioned table and one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO regress_vacuum;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
@@ -466,7 +466,7 @@ VACUUM (ANALYZE) vacowned_part1;
VACUUM (ANALYZE) vacowned_part2;
RESET ROLE;
-- Only one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO CURRENT_USER;
+ALTER TABLE ONLY vacowned_parted OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
VACUUM vacowned_part1;
@@ -479,7 +479,7 @@ VACUUM (ANALYZE) vacowned_part1;
VACUUM (ANALYZE) vacowned_part2;
RESET ROLE;
-- Only partitioned table owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
--
2.50.1 (Apple Git-155)
^ permalink raw reply [nested|flat] 19+ messages in thread
* Re: ALTER TABLE: warn when actions do not recurse to partitions
2026-01-13 11:16 Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-14 00:52 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 05:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-22 19:27 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-23 00:11 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-01-23 09:57 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-03-09 06:46 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-10 15:32 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-11 07:04 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-11 16:39 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-12 01:14 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-12 02:09 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Greg Sabino Mullane <[email protected]>
2026-03-12 06:37 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-03-12 21:45 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Zsolt Parragi <[email protected]>
2026-03-13 03:16 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-04-01 08:01 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
2026-04-01 08:03 ` Re: ALTER TABLE: warn when actions do not recurse to partitions Chao Li <[email protected]>
@ 2026-05-04 11:24 ` Jim Jones <[email protected]>
0 siblings, 0 replies; 19+ messages in thread
From: Jim Jones @ 2026-05-04 11:24 UTC (permalink / raw)
To: Chao Li <[email protected]>; Postgres hackers <[email protected]>; +Cc: Greg Sabino Mullane <[email protected]>; David G. Johnston <[email protected]>; Zsolt Parragi <[email protected]>
Hi Chao
On 01/04/2026 10:03, Chao Li wrote:
> PFA v10 - fixed a test failure.
v10 was no longer applying due to changes in tablecmds.c, so I rebased it.
Additionally, the new REPACK (CONCURRENTLY) tests on partitioned tables
were failing. I added a fix in 0002 (for better reviewing).
Best, Jim
Attachments:
[text/x-patch] v11-0001-ALTER-TABLE-emit-NOTICE-when-ONLY-is-omitted-but.patch (51.0K, 2-v11-0001-ALTER-TABLE-emit-NOTICE-when-ONLY-is-omitted-but.patch)
download | inline diff:
From 8d43b40d2fa172d5ae4a764d43c77708a56343f6 Mon Sep 17 00:00:00 2001
From: Jim Jones <[email protected]>
Date: Mon, 4 May 2026 12:10:55 +0200
Subject: [PATCH v11 1/2] ALTER TABLE: emit NOTICE when ONLY is omitted but no
partition recursion occurs
Some ALTER TABLE sub-commands do not recurse to partitions even when ONLY
is not specified on a partitioned table. This can be surprising, since
the default expectation is that commands propagate to all partitions.
Emit a NOTICE in such cases to make the behavior explicit, and advise how
to suppress the message or modify partitions individually.
Affected sub-commands include:
- ALTER COLUMN SET/RESET attribute_option
- ALTER COLUMN SET COMPRESSION
- ENABLE/DISABLE RULE
- ENABLE/DISABLE/FORCE/NO FORCE ROW LEVEL SECURITY
- REPLICA IDENTITY
- OWNER TO
- SET ACCESS METHOD
- SET TABLESPACE
- SET SCHEMA
RENAME is intentionally excluded. Using ONLY (or not) has no effect for
RENAME, since relation names are independent by nature and there is no
expectation of recursion.
OF / NOT OF are also excluded. Using ONLY has no effect for these
commands, as they apply only to the partitioned table itself and not to
its partitions.
Regression tests are updated to use ONLY where appropriate.
Author: Chao Li <[email protected]>
Reviewed-by: David G. Johnston <[email protected]>
Reviewed-by: Greg Sabino Mullane <[email protected]>
Reviewed-by: Jim Jones <[email protected]>
Reviewed-by: Zsolt Parragi <[email protected]>
Discussion: https://postgr.es/m/CAEoWx2=SLga-xH09Cq_PAvsHhQHrBK+V0vF821JKgzS=Bm0haA@mail.gmail.com
---
src/backend/commands/tablecmds.c | 138 ++++++++++++++++--
src/include/nodes/parsenodes.h | 1 +
src/test/regress/expected/alter_table.out | 92 +++++++++++-
src/test/regress/expected/cluster.out | 2 +-
src/test/regress/expected/create_am.out | 16 +-
src/test/regress/expected/graph_table_rls.out | 6 +-
src/test/regress/expected/merge.out | 4 +-
src/test/regress/expected/partition_merge.out | 2 +-
src/test/regress/expected/partition_split.out | 2 +-
src/test/regress/expected/privileges.out | 2 +-
src/test/regress/expected/rowsecurity.out | 4 +-
src/test/regress/expected/tablespace.out | 4 +-
src/test/regress/expected/update.out | 2 +-
src/test/regress/expected/vacuum.out | 6 +-
src/test/regress/sql/alter_table.sql | 71 ++++++++-
src/test/regress/sql/cluster.sql | 2 +-
src/test/regress/sql/create_am.sql | 16 +-
src/test/regress/sql/graph_table_rls.sql | 6 +-
src/test/regress/sql/merge.sql | 4 +-
src/test/regress/sql/partition_merge.sql | 2 +-
src/test/regress/sql/partition_split.sql | 2 +-
src/test/regress/sql/privileges.sql | 2 +-
src/test/regress/sql/rowsecurity.sql | 4 +-
src/test/regress/sql/tablespace.sql | 4 +-
src/test/regress/sql/update.sql | 2 +-
src/test/regress/sql/vacuum.sql | 6 +-
26 files changed, 337 insertions(+), 65 deletions(-)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index d8d7969bf30..bd3e2b31135 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -499,7 +499,8 @@ static void ATController(AlterTableStmt *parsetree,
AlterTableUtilityContext *context);
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
bool recurse, bool recursing, LOCKMODE lockmode,
- AlterTableUtilityContext *context);
+ AlterTableUtilityContext *context,
+ List **pendingNotice);
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
AlterTableUtilityContext *context);
static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
@@ -783,6 +784,10 @@ static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation
static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
Relation rel, PartitionCmd *cmd,
AlterTableUtilityContext *context);
+static void CollectPartitionNoRecurseNotice(AlterTableType cmdtype, Relation rel,
+ bool recurse, bool recursing,
+ List **pendingNotice);
+static void EmitPartitionNoRecurseNotice(List *pendingNotice);
static List *collectPartitionIndexExtDeps(List *partitionOids);
static void applyPartitionIndexExtDeps(Oid newPartOid, List *extDepState);
static void freePartitionIndexExtDeps(List *extDepState);
@@ -4935,13 +4940,14 @@ ATController(AlterTableStmt *parsetree,
{
List *wqueue = NIL;
ListCell *lcmd;
+ List *pendingNotice = NIL;
/* Phase 1: preliminary examination of commands, create work queue */
foreach(lcmd, cmds)
{
AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
- ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
+ ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context, &pendingNotice);
}
/* Close the relation, but keep lock until commit */
@@ -4952,6 +4958,9 @@ ATController(AlterTableStmt *parsetree,
/* Phase 3: scan/rewrite tables as needed, and run afterStmts */
ATRewriteTables(parsetree, &wqueue, lockmode, context);
+
+ /* Emit post-notice for partitions that were not recursed into. */
+ EmitPartitionNoRecurseNotice(pendingNotice);
}
/*
@@ -4966,7 +4975,8 @@ ATController(AlterTableStmt *parsetree,
static void
ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
bool recurse, bool recursing, LOCKMODE lockmode,
- AlterTableUtilityContext *context)
+ AlterTableUtilityContext *context,
+ List **pendingNotice)
{
AlteredTableInfo *tab;
AlterTablePass pass = AT_PASS_UNSET;
@@ -5115,6 +5125,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATT_MATVIEW | ATT_FOREIGN_TABLE);
/* This command never recurses */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
ATSimplePermissions(cmd->subtype, rel,
@@ -5130,6 +5142,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_DropColumn: /* DROP COLUMN */
ATSimplePermissions(cmd->subtype, rel,
@@ -5197,6 +5211,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_ClusterOn: /* CLUSTER ON */
case AT_DropCluster: /* SET WITHOUT CLUSTER */
@@ -5233,6 +5249,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATPrepSetAccessMethod(tab, rel, cmd->name);
pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_SetTableSpace: /* SET TABLESPACE */
ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE |
@@ -5240,6 +5258,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
pass = AT_PASS_MISC; /* doesn't actually matter */
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_SetRelOptions: /* SET (...) */
case AT_ResetRelOptions: /* RESET (...) */
@@ -5249,6 +5269,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATT_MATVIEW | ATT_INDEX);
/* This command never recurses */
/* No command-specific prep needed */
+ /* It will check for partitioned table at exec time */
pass = AT_PASS_MISC;
break;
case AT_AddInherit: /* INHERIT */
@@ -5286,8 +5307,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATSimplePermissions(cmd->subtype, rel,
ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
pass = AT_PASS_MISC;
- /* This command never recurses */
- /* No command-specific prep needed */
+ /* This command doesn't recurse to partitions, so notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
break;
case AT_EnableTrig: /* ENABLE TRIGGER variants */
case AT_EnableAlwaysTrig:
@@ -5308,17 +5329,30 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
case AT_EnableAlwaysRule:
case AT_EnableReplicaRule:
case AT_DisableRule:
- case AT_AddOf: /* OF */
- case AT_DropOf: /* NOT OF */
- case AT_EnableRowSecurity:
+ case AT_EnableRowSecurity: /* ENABLE/DISABLE ROW SECURITY variants */
case AT_DisableRowSecurity:
- case AT_ForceRowSecurity:
+ case AT_ForceRowSecurity: /* FORCE/NO FORCE ROW SECURITY variants */
case AT_NoForceRowSecurity:
ATSimplePermissions(cmd->subtype, rel,
ATT_TABLE | ATT_PARTITIONED_TABLE);
/* These commands never recurse */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
+ /* Emit a notice if needed */
+ CollectPartitionNoRecurseNotice(cmd->subtype, rel, recurse, recursing, pendingNotice);
+ break;
+ case AT_AddOf: /* OF */
+ case AT_DropOf: /* NOT OF */
+ ATSimplePermissions(cmd->subtype, rel,
+ ATT_TABLE | ATT_PARTITIONED_TABLE);
+ /* These commands never recurse */
+ /* No command-specific prep needed */
+
+ /*
+ * They only work on partitioned tables but child partitions, thus
+ * no need to emit a notice
+ */
+ pass = AT_PASS_MISC;
break;
case AT_GenericOptions:
ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
@@ -6813,6 +6847,8 @@ alter_table_type_to_string(AlterTableType cmdtype)
return "ALTER COLUMN ... DROP IDENTITY";
case AT_ReAddStatistics:
return NULL; /* not real grammar */
+ case AT_SetSchema:
+ return "SET SCHEMA";
}
return NULL;
@@ -6934,7 +6970,7 @@ ATSimpleRecursion(List **wqueue, Relation rel,
/* find_all_inheritors already got lock */
childrel = relation_open(childrelid, NoLock);
CheckAlterTableIsSafe(childrel);
- ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
+ ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context, NULL);
relation_close(childrel, NoLock);
}
}
@@ -6997,7 +7033,7 @@ ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
childrel = relation_open(childrelid, lockmode);
CheckAlterTableIsSafe(childrel);
- ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
+ ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context, NULL);
relation_close(childrel, NoLock);
}
}
@@ -9668,7 +9704,7 @@ ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
newcmd->recurse = true;
newcmd->def = (Node *) nnconstr;
- ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
+ ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context, NULL);
}
}
@@ -14918,7 +14954,7 @@ ATPrepAlterColumnType(List **wqueue,
errdetail("USING expression contains a whole-row table reference.")));
pfree(attmap);
}
- ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
+ ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context, NULL);
relation_close(childrel, NoLock);
}
}
@@ -19216,6 +19252,7 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
RangeVar *newrv;
ObjectAddresses *objsMoved;
ObjectAddress myself;
+ List *pendingNotice = NIL;
relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
stmt->missing_ok ? RVR_MISSING_OK : 0,
@@ -19232,6 +19269,13 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
rel = relation_open(relid, NoLock);
+ /*
+ * SET SCHEMA doesn't recurse to children, emit a notice if ONLY is not
+ * specified. As this action doesn't go through ATPrepCmd, we have to emit
+ * the notice here.
+ */
+ CollectPartitionNoRecurseNotice(AT_SetSchema, rel, stmt->relation->inh, false, &pendingNotice);
+
oldNspOid = RelationGetNamespace(rel);
/* If it's an owned sequence, disallow moving it by itself. */
@@ -19269,6 +19313,8 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
/* close rel, but keep lock until commit */
relation_close(rel, NoLock);
+ EmitPartitionNoRecurseNotice(pendingNotice);
+
return myself;
}
@@ -23896,3 +23942,69 @@ ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
/* Restore the userid and security context. */
SetUserIdAndSecContext(save_userid, save_sec_context);
}
+
+/*
+ * When ONLY is not specified with a partitioned table, it is expected that the
+ * command recurses to all partitions. However, some sub-commands do not recurse.
+ * In such cases, emit a NOTICE to make this behavior explicit to the user.
+ */
+static void
+CollectPartitionNoRecurseNotice(AlterTableType cmdtype, Relation rel, bool recurse, bool recursing,
+ List **pendingNotice)
+{
+ if (pendingNotice == NULL)
+ return;
+
+ /* Only emit the notice at the top level of recursion */
+ if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && recurse && !recursing)
+ {
+ PartitionDesc pd = RelationGetPartitionDesc(rel, true);
+ int nparts = pd->nparts;
+ const char *action_str;
+ char *notice_msg;
+ const ListCell *cell;
+
+ /* Emit a notice only if there are partitions */
+ if (nparts == 0)
+ return;
+
+ action_str = alter_table_type_to_string(cmdtype);
+ notice_msg = psprintf(_("ALTER action %s on relation \"%s\" does not affect present partitions"),
+ action_str,
+ RelationGetRelationName(rel));
+
+ foreach(cell, *pendingNotice)
+ {
+ if (strcmp((char *) lfirst(cell), notice_msg) == 0)
+ {
+ /* Skip the duplicate notice message */
+ pfree(notice_msg);
+ return;
+ }
+ }
+ *pendingNotice = lappend(*pendingNotice, notice_msg);
+ }
+}
+
+static void
+EmitPartitionNoRecurseNotice(List *pendingNotice)
+{
+ ListCell *cell;
+ int len;
+ int i = 0;
+
+ len = list_length(pendingNotice);
+ foreach(cell, pendingNotice)
+ {
+ char *notice_msg = (char *) lfirst(cell);
+
+ /* Only emit the hint for the last notice */
+ i++;
+ ereport(NOTICE, errmsg("%s", notice_msg),
+ (i == len) ?
+ errhint("Partitions may be modified individually, or specify ONLY to suppress this message.")
+ : 0);
+ pfree(notice_msg);
+ }
+ list_free(pendingNotice);
+}
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 91377a6cde3..1411bd827b1 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2589,6 +2589,7 @@ typedef enum AlterTableType
AT_SetIdentity, /* SET identity column options */
AT_DropIdentity, /* DROP IDENTITY */
AT_ReAddStatistics, /* internal to commands/tablecmds.c */
+ AT_SetSchema, /* SET SCHEMA */
} AlterTableType;
typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 6dd22be0e8d..eaca10d448d 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -4603,8 +4603,98 @@ SELECT * FROM list_parted;
---
(0 rows)
+CREATE TABLE list_parted4 (a int, b text) PARTITION BY LIST (a);
+CREATE TABLE list_parted4_1 PARTITION OF list_parted4 FOR VALUES IN (1);
+-- set column attribute on partitioned table without ONLY should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+NOTICE: ALTER action ALTER COLUMN ... SET on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE list_parted4 ALTER COLUMN b RESET (n_distinct);
+NOTICE: ALTER action ALTER COLUMN ... RESET on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b RESET (n_distinct);
+-- enable/disable rules on partitioned tables without ONLY should get a notice
+CREATE RULE list_parted4_rule AS ON INSERT TO list_parted4 DO INSTEAD NOTHING;
+ALTER TABLE list_parted4 DISABLE RULE list_parted4_rule;
+NOTICE: ALTER action DISABLE RULE on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 DISABLE RULE list_parted4_rule;
+ALTER TABLE list_parted4 ENABLE RULE list_parted4_rule;
+NOTICE: ALTER action ENABLE RULE on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ENABLE RULE list_parted4_rule;
+DROP RULE list_parted4_rule ON list_parted4;
+-- enable/disable row level security on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 ENABLE ROW LEVEL SECURITY;
+NOTICE: ALTER action ENABLE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 DISABLE ROW LEVEL SECURITY;
+NOTICE: ALTER action DISABLE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 DISABLE ROW LEVEL SECURITY;
+-- force/no force row level security on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 FORCE ROW LEVEL SECURITY;
+NOTICE: ALTER action FORCE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 FORCE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 NO FORCE ROW LEVEL SECURITY;
+NOTICE: ALTER action NO FORCE ROW SECURITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 NO FORCE ROW LEVEL SECURITY;
+-- set replica identity on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 REPLICA IDENTITY FULL;
+NOTICE: ALTER action REPLICA IDENTITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY FULL;
+ALTER TABLE list_parted4 REPLICA IDENTITY NOTHING;
+NOTICE: ALTER action REPLICA IDENTITY on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY NOTHING;
+-- set compression on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+NOTICE: ALTER action ALTER COLUMN ... SET COMPRESSION on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+-- set owner on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 OWNER TO regress_alter_table_user1;
+NOTICE: ALTER action OWNER TO on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 OWNER TO regress_alter_table_user1;
+-- set access method on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 SET ACCESS METHOD heap;
+NOTICE: ALTER action SET ACCESS METHOD on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY list_parted4 SET ACCESS METHOD heap;
+-- set schema on partitioned tables without ONLY should get a notice
+CREATE SCHEMA alter_table_test_schema;
+ALTER TABLE list_parted4 SET SCHEMA alter_table_test_schema;
+NOTICE: ALTER action SET SCHEMA on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+ALTER TABLE ONLY alter_table_test_schema.list_parted4 SET SCHEMA public;
+DROP SCHEMA alter_table_test_schema CASCADE;
+-- when there are multiple sub-command, notice should not duplicated
+ALTER TABLE list_parted4
+ ALTER COLUMN b SET COMPRESSION pglz,
+ ALTER COLUMN b SET COMPRESSION pglz, -- duplicate, but should not get duplicate notice
+ FORCE ROW LEVEL SECURITY,
+ REPLICA IDENTITY FULL,
+ OWNER TO regress_alter_table_user1;
+NOTICE: ALTER action ALTER COLUMN ... SET COMPRESSION on relation "list_parted4" does not affect present partitions
+NOTICE: ALTER action FORCE ROW SECURITY on relation "list_parted4" does not affect present partitions
+NOTICE: ALTER action REPLICA IDENTITY on relation "list_parted4" does not affect present partitions
+NOTICE: ALTER action OWNER TO on relation "list_parted4" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
+-- when the sub-command fails, notice should not be printed
+ALTER TABLE list_parted4 SET ACCESS METHOD invalid_am;
+ERROR: access method "invalid_am" does not exist
+-- list_parted5 is a partitioned table that has no partition.
+CREATE TABLE list_parted5 (a int, b text) PARTITION BY LIST (a);
+-- as it has no partition, there should be no notice when altering it without ONLY
+ALTER TABLE list_parted5 FORCE ROW LEVEL SECURITY;
-- cleanup
-DROP TABLE list_parted, list_parted2, range_parted, list_parted3;
+DROP TABLE list_parted, list_parted2, range_parted, list_parted3, list_parted4, list_parted5;
DROP TABLE fail_def_part;
DROP TABLE hash_parted;
-- more tests for certain multi-level partitioning scenarios
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 0317f2f801e..77645e27cd9 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -548,7 +548,7 @@ SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
RESET SESSION AUTHORIZATION;
-ALTER TABLE ptnowner OWNER TO regress_ptnowner;
+ALTER TABLE ONLY ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
SELECT oid, relname, relfilenode FROM pg_partition_tree('ptnowner') AS tree
JOIN pg_class AS c ON c.oid=tree.relid;
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
index c1a95157251..8c428270118 100644
--- a/src/test/regress/expected/create_am.out
+++ b/src/test/regress/expected/create_am.out
@@ -380,7 +380,7 @@ SELECT pg_describe_object(classid, objid, objsubid) AS obj,
(0 rows)
-- New default is set, with dependency added.
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
amname
@@ -401,7 +401,7 @@ SELECT pg_describe_object(classid, objid, objsubid) AS obj,
-- Default is set, with dependency updated.
SET LOCAL default_table_access_method = 'heap2';
-ALTER TABLE am_partitioned SET ACCESS METHOD heap;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
amname
@@ -422,7 +422,7 @@ SELECT pg_describe_object(classid, objid, objsubid) AS obj,
-- Default and AM set in the clause are the same, relam should be set.
SET LOCAL default_table_access_method = 'heap2';
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
amname
@@ -431,7 +431,7 @@ SELECT a.amname FROM pg_class c, pg_am a
(1 row)
-- Reset to default
-ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD DEFAULT;
SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
relam
-------
@@ -453,10 +453,10 @@ SET LOCAL default_table_access_method = 'heap2';
CREATE TABLE am_partitioned_1 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 1);
SET LOCAL default_table_access_method = 'heap';
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
CREATE TABLE am_partitioned_2 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 2);
-ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD DEFAULT;
SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
relam
-------
@@ -466,7 +466,7 @@ SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
CREATE TABLE am_partitioned_3 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 3);
-- Partitioned table with relam at 0
-ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD DEFAULT;
CREATE TABLE am_partitioned_5p PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 5) PARTITION BY hash(y);
-- Partitions of this partitioned table inherit default AM at creation
@@ -474,7 +474,7 @@ CREATE TABLE am_partitioned_5p PARTITION OF am_partitioned
CREATE TABLE am_partitioned_5p1 PARTITION OF am_partitioned_5p
FOR VALUES WITH (MODULUS 10, REMAINDER 1);
-- Partitioned table with relam set.
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
CREATE TABLE am_partitioned_6p PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 6) PARTITION BY hash(y);
-- Partitions of this partitioned table inherit its AM.
diff --git a/src/test/regress/expected/graph_table_rls.out b/src/test/regress/expected/graph_table_rls.out
index 0e719c7ebd7..c71e2a13796 100644
--- a/src/test/regress/expected/graph_table_rls.out
+++ b/src/test/regress/expected/graph_table_rls.out
@@ -249,7 +249,7 @@ INSERT INTO accessed VALUES
-- Enable RLS and move policies p1 and p2 to parent table but leave p3 and p4 on
-- child table. The policies on child table are not applied when querying parent
-- table.
-ALTER TABLE document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY document ENABLE ROW LEVEL SECURITY;
DROP POLICY p1 ON document_people;
DROP POLICY p2 ON document_people;
CREATE POLICY p1 ON document AS PERMISSIVE
@@ -445,7 +445,7 @@ GRANT SELECT ON document TO public;
ALTER TABLE document ATTACH PARTITION document_people FOR VALUES IN ('People');
ALTER TABLE document ATTACH PARTITION document_places FOR VALUES IN ('Places');
-- Enable RLS on partitioned table
-ALTER TABLE document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY document ENABLE ROW LEVEL SECURITY;
-- create policies on partitioned table
CREATE POLICY p1 ON document AS PERMISSIVE
USING (dlevel <= (SELECT seclv FROM users WHERE pguser = current_user));
@@ -759,7 +759,7 @@ NOTICE: f_leak => New York
(12 rows)
-- FORCE ROW LEVEL SECURITY applies RLS to owners too
-ALTER TABLE document FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY document FORCE ROW LEVEL SECURITY;
EXECUTE graph_rls_query;
pguser | aid | dtitle | dlevel
--------+-----+--------+--------
diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out
index 9cb1d87066a..bd6aac72336 100644
--- a/src/test/regress/expected/merge.out
+++ b/src/test/regress/expected/merge.out
@@ -2298,8 +2298,8 @@ SELECT * FROM pa_target ORDER BY tid, val;
ROLLBACK;
-- test RLS enforcement
BEGIN;
-ALTER TABLE pa_target ENABLE ROW LEVEL SECURITY;
-ALTER TABLE pa_target FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target FORCE ROW LEVEL SECURITY;
CREATE POLICY pa_target_pol ON pa_target USING (tid != 0);
MERGE INTO pa_target t
USING pa_source s
diff --git a/src/test/regress/expected/partition_merge.out b/src/test/regress/expected/partition_merge.out
index 883110e25d9..f23b533df1d 100644
--- a/src/test/regress/expected/partition_merge.out
+++ b/src/test/regress/expected/partition_merge.out
@@ -810,7 +810,7 @@ SET SESSION AUTHORIZATION regress_partition_merge_bob;
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
ERROR: must be owner of table t
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_merge_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_merge_bob;
SET SESSION AUTHORIZATION regress_partition_merge_bob;
-- ERROR: must be owner of table tp_0_1
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
diff --git a/src/test/regress/expected/partition_split.out b/src/test/regress/expected/partition_split.out
index 43ca299648e..3636ea6e313 100644
--- a/src/test/regress/expected/partition_split.out
+++ b/src/test/regress/expected/partition_split.out
@@ -1375,7 +1375,7 @@ ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); --error
ERROR: must be owner of table t
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_split_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_split_bob;
SET SESSION AUTHORIZATION regress_partition_split_bob;
ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
(PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 0de13612818..b1e0d2b4ad2 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -1936,7 +1936,7 @@ SELECT brin_summarize_range('sro_brin', 0);
DROP TABLE sro_tab;
-- Check with a partitioned table
CREATE TABLE sro_ptab (a int) PARTITION BY RANGE (a);
-ALTER TABLE sro_ptab OWNER TO regress_sro_user;
+ALTER TABLE ONLY sro_ptab OWNER TO regress_sro_user;
CREATE TABLE sro_part PARTITION OF sro_ptab FOR VALUES FROM (1) TO (10);
ALTER TABLE sro_part OWNER TO regress_sro_user;
INSERT INTO sro_ptab VALUES (1), (2), (3);
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index 3a5e82c35bd..c75074b219b 100644
--- a/src/test/regress/expected/rowsecurity.out
+++ b/src/test/regress/expected/rowsecurity.out
@@ -1233,7 +1233,7 @@ INSERT INTO part_document VALUES
( 8, 55, 2, 'regress_rls_carol', 'great satire'),
( 9, 11, 1, 'regress_rls_dave', 'awesome science fiction'),
(10, 99, 2, 'regress_rls_dave', 'awesome technology book');
-ALTER TABLE part_document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY part_document ENABLE ROW LEVEL SECURITY;
-- Create policy on parent
-- user's security level must be higher than or equal to document's
CREATE POLICY pp1 ON part_document AS PERMISSIVE
@@ -4915,7 +4915,7 @@ CREATE TABLE rls_ptbl (a int) PARTITION BY RANGE (a);
CREATE TABLE rls_part PARTITION OF rls_ptbl FOR VALUES FROM (-100) TO (100);
INSERT INTO rls_ptbl SELECT x/10 FROM generate_series(1, 100) x;
ANALYZE rls_ptbl, rls_part;
-ALTER TABLE rls_ptbl ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY rls_ptbl ENABLE ROW LEVEL SECURITY;
ALTER TABLE rls_part ENABLE ROW LEVEL SECURITY;
GRANT SELECT ON rls_ptbl TO regress_rls_alice;
GRANT SELECT ON rls_part TO regress_rls_alice;
diff --git a/src/test/regress/expected/tablespace.out b/src/test/regress/expected/tablespace.out
index f0dd25cdf0c..3d9c44ba64f 100644
--- a/src/test/regress/expected/tablespace.out
+++ b/src/test/regress/expected/tablespace.out
@@ -290,13 +290,13 @@ CREATE TABLE testschema.part_2 PARTITION OF testschema.part FOR VALUES IN (2);
SET default_tablespace TO pg_global;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
ERROR: only shared relations can be placed in pg_global tablespace
-ALTER TABLE testschema.part SET TABLESPACE regress_tblspace;
+ALTER TABLE ONLY testschema.part SET TABLESPACE regress_tblspace;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
CREATE TABLE testschema.part_4 PARTITION OF testschema.part FOR VALUES IN (4)
TABLESPACE pg_default;
CREATE TABLE testschema.part_56 PARTITION OF testschema.part FOR VALUES IN (5, 6)
PARTITION BY LIST (a);
-ALTER TABLE testschema.part SET TABLESPACE pg_default;
+ALTER TABLE ONLY testschema.part SET TABLESPACE pg_default;
CREATE TABLE testschema.part_78 PARTITION OF testschema.part FOR VALUES IN (7, 8)
PARTITION BY LIST (a);
ERROR: only shared relations can be placed in pg_global tablespace
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index eef2bac1cbf..584b40598eb 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -607,7 +607,7 @@ DROP TRIGGER trig_d15_20 ON part_d_15_20;
DROP FUNCTION func_parted_mod_b();
-- RLS policies with update-row-movement
-----------------------------------------
-ALTER TABLE range_parted ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY range_parted ENABLE ROW LEVEL SECURITY;
CREATE USER regress_range_parted_user;
GRANT ALL ON range_parted, mintab TO regress_range_parted_user;
CREATE POLICY seeall ON range_parted AS PERMISSIVE FOR SELECT USING (true);
diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out
index d4696bc3325..f22bb2127e1 100644
--- a/src/test/regress/expected/vacuum.out
+++ b/src/test/regress/expected/vacuum.out
@@ -614,7 +614,7 @@ VACUUM (ANALYZE) vacowned_part2;
WARNING: permission denied to vacuum "vacowned_part2", skipping it
RESET ROLE;
-- Partitioned table and one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO regress_vacuum;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
@@ -634,7 +634,7 @@ VACUUM (ANALYZE) vacowned_part2;
WARNING: permission denied to vacuum "vacowned_part2", skipping it
RESET ROLE;
-- Only one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO CURRENT_USER;
+ALTER TABLE ONLY vacowned_parted OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
WARNING: permission denied to vacuum "vacowned_parted", skipping it
@@ -656,7 +656,7 @@ VACUUM (ANALYZE) vacowned_part2;
WARNING: permission denied to vacuum "vacowned_part2", skipping it
RESET ROLE;
-- Only partitioned table owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index f5f13bbd3e7..494d5d1511f 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2912,8 +2912,77 @@ ALTER TABLE list_parted2 ALTER COLUMN b TYPE text;
ALTER TABLE list_parted DROP COLUMN b;
SELECT * FROM list_parted;
+CREATE TABLE list_parted4 (a int, b text) PARTITION BY LIST (a);
+CREATE TABLE list_parted4_1 PARTITION OF list_parted4 FOR VALUES IN (1);
+
+-- set column attribute on partitioned table without ONLY should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+ALTER TABLE list_parted4 ALTER COLUMN b RESET (n_distinct);
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET (n_distinct = 0.2);
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b RESET (n_distinct);
+
+-- enable/disable rules on partitioned tables without ONLY should get a notice
+CREATE RULE list_parted4_rule AS ON INSERT TO list_parted4 DO INSTEAD NOTHING;
+ALTER TABLE list_parted4 DISABLE RULE list_parted4_rule;
+ALTER TABLE ONLY list_parted4 DISABLE RULE list_parted4_rule;
+ALTER TABLE list_parted4 ENABLE RULE list_parted4_rule;
+ALTER TABLE ONLY list_parted4 ENABLE RULE list_parted4_rule;
+DROP RULE list_parted4_rule ON list_parted4;
+
+-- enable/disable row level security on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 DISABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 DISABLE ROW LEVEL SECURITY;
+
+-- force/no force row level security on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 FORCE ROW LEVEL SECURITY;
+ALTER TABLE list_parted4 NO FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY list_parted4 NO FORCE ROW LEVEL SECURITY;
+
+-- set replica identity on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 REPLICA IDENTITY FULL;
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY FULL;
+ALTER TABLE list_parted4 REPLICA IDENTITY NOTHING;
+ALTER TABLE ONLY list_parted4 REPLICA IDENTITY NOTHING;
+
+-- set compression on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+ALTER TABLE ONLY list_parted4 ALTER COLUMN b SET COMPRESSION pglz;
+
+-- set owner on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 OWNER TO regress_alter_table_user1;
+ALTER TABLE ONLY list_parted4 OWNER TO regress_alter_table_user1;
+
+-- set access method on partitioned tables without ONLY should get a notice
+ALTER TABLE list_parted4 SET ACCESS METHOD heap;
+ALTER TABLE ONLY list_parted4 SET ACCESS METHOD heap;
+
+-- set schema on partitioned tables without ONLY should get a notice
+CREATE SCHEMA alter_table_test_schema;
+ALTER TABLE list_parted4 SET SCHEMA alter_table_test_schema;
+ALTER TABLE ONLY alter_table_test_schema.list_parted4 SET SCHEMA public;
+DROP SCHEMA alter_table_test_schema CASCADE;
+
+-- when there are multiple sub-command, notice should not duplicated
+ALTER TABLE list_parted4
+ ALTER COLUMN b SET COMPRESSION pglz,
+ ALTER COLUMN b SET COMPRESSION pglz, -- duplicate, but should not get duplicate notice
+ FORCE ROW LEVEL SECURITY,
+ REPLICA IDENTITY FULL,
+ OWNER TO regress_alter_table_user1;
+
+-- when the sub-command fails, notice should not be printed
+ALTER TABLE list_parted4 SET ACCESS METHOD invalid_am;
+
+-- list_parted5 is a partitioned table that has no partition.
+CREATE TABLE list_parted5 (a int, b text) PARTITION BY LIST (a);
+-- as it has no partition, there should be no notice when altering it without ONLY
+ALTER TABLE list_parted5 FORCE ROW LEVEL SECURITY;
+
-- cleanup
-DROP TABLE list_parted, list_parted2, range_parted, list_parted3;
+DROP TABLE list_parted, list_parted2, range_parted, list_parted3, list_parted4, list_parted5;
DROP TABLE fail_def_part;
DROP TABLE hash_parted;
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index c2f329ecd1b..0b81161fdc4 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -258,7 +258,7 @@ ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
RESET SESSION AUTHORIZATION;
-ALTER TABLE ptnowner OWNER TO regress_ptnowner;
+ALTER TABLE ONLY ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
SELECT oid, relname, relfilenode FROM pg_partition_tree('ptnowner') AS tree
JOIN pg_class AS c ON c.oid=tree.relid;
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
index 754fe0c694b..4953be0ab3d 100644
--- a/src/test/regress/sql/create_am.sql
+++ b/src/test/regress/sql/create_am.sql
@@ -241,7 +241,7 @@ SELECT pg_describe_object(classid, objid, objsubid) AS obj,
AND pg_am.oid = pg_depend.refobjid
AND pg_depend.objid = 'am_partitioned'::regclass;
-- New default is set, with dependency added.
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
SELECT pg_describe_object(classid, objid, objsubid) AS obj,
@@ -252,7 +252,7 @@ SELECT pg_describe_object(classid, objid, objsubid) AS obj,
AND pg_depend.objid = 'am_partitioned'::regclass;
-- Default is set, with dependency updated.
SET LOCAL default_table_access_method = 'heap2';
-ALTER TABLE am_partitioned SET ACCESS METHOD heap;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
-- Dependency pinned, hence removed.
@@ -264,11 +264,11 @@ SELECT pg_describe_object(classid, objid, objsubid) AS obj,
AND pg_depend.objid = 'am_partitioned'::regclass;
-- Default and AM set in the clause are the same, relam should be set.
SET LOCAL default_table_access_method = 'heap2';
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
-- Reset to default
-ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD DEFAULT;
SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
-- Upon ALTER TABLE SET ACCESS METHOD on a partitioned table, new partitions
-- will inherit the AM set. Existing partitioned are unchanged.
@@ -280,15 +280,15 @@ SET LOCAL default_table_access_method = 'heap2';
CREATE TABLE am_partitioned_1 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 1);
SET LOCAL default_table_access_method = 'heap';
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
CREATE TABLE am_partitioned_2 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 2);
-ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD DEFAULT;
SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
CREATE TABLE am_partitioned_3 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 3);
-- Partitioned table with relam at 0
-ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD DEFAULT;
CREATE TABLE am_partitioned_5p PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 5) PARTITION BY hash(y);
-- Partitions of this partitioned table inherit default AM at creation
@@ -296,7 +296,7 @@ CREATE TABLE am_partitioned_5p PARTITION OF am_partitioned
CREATE TABLE am_partitioned_5p1 PARTITION OF am_partitioned_5p
FOR VALUES WITH (MODULUS 10, REMAINDER 1);
-- Partitioned table with relam set.
-ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
+ALTER TABLE ONLY am_partitioned SET ACCESS METHOD heap2;
CREATE TABLE am_partitioned_6p PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 6) PARTITION BY hash(y);
-- Partitions of this partitioned table inherit its AM.
diff --git a/src/test/regress/sql/graph_table_rls.sql b/src/test/regress/sql/graph_table_rls.sql
index 5837eac402e..a5256170025 100644
--- a/src/test/regress/sql/graph_table_rls.sql
+++ b/src/test/regress/sql/graph_table_rls.sql
@@ -185,7 +185,7 @@ INSERT INTO accessed VALUES
-- Enable RLS and move policies p1 and p2 to parent table but leave p3 and p4 on
-- child table. The policies on child table are not applied when querying parent
-- table.
-ALTER TABLE document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY document ENABLE ROW LEVEL SECURITY;
DROP POLICY p1 ON document_people;
DROP POLICY p2 ON document_people;
CREATE POLICY p1 ON document AS PERMISSIVE
@@ -255,7 +255,7 @@ GRANT SELECT ON document TO public;
ALTER TABLE document ATTACH PARTITION document_people FOR VALUES IN ('People');
ALTER TABLE document ATTACH PARTITION document_places FOR VALUES IN ('Places');
-- Enable RLS on partitioned table
-ALTER TABLE document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY document ENABLE ROW LEVEL SECURITY;
-- create policies on partitioned table
CREATE POLICY p1 ON document AS PERMISSIVE
USING (dlevel <= (SELECT seclv FROM users WHERE pguser = current_user));
@@ -352,7 +352,7 @@ EXECUTE graph_rls_query;
SET SESSION AUTHORIZATION regress_graph_rls_alice;
EXECUTE graph_rls_query;
-- FORCE ROW LEVEL SECURITY applies RLS to owners too
-ALTER TABLE document FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY document FORCE ROW LEVEL SECURITY;
EXECUTE graph_rls_query;
SET row_security TO OFF;
EXECUTE graph_rls_query; -- error
diff --git a/src/test/regress/sql/merge.sql b/src/test/regress/sql/merge.sql
index 2660b19f238..2bffcf7aa98 100644
--- a/src/test/regress/sql/merge.sql
+++ b/src/test/regress/sql/merge.sql
@@ -1422,8 +1422,8 @@ ROLLBACK;
-- test RLS enforcement
BEGIN;
-ALTER TABLE pa_target ENABLE ROW LEVEL SECURITY;
-ALTER TABLE pa_target FORCE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY pa_target FORCE ROW LEVEL SECURITY;
CREATE POLICY pa_target_pol ON pa_target USING (tid != 0);
MERGE INTO pa_target t
USING pa_source s
diff --git a/src/test/regress/sql/partition_merge.sql b/src/test/regress/sql/partition_merge.sql
index a211fee2ad1..73bffb530dd 100644
--- a/src/test/regress/sql/partition_merge.sql
+++ b/src/test/regress/sql/partition_merge.sql
@@ -571,7 +571,7 @@ SET SESSION AUTHORIZATION regress_partition_merge_bob;
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_merge_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_merge_bob;
SET SESSION AUTHORIZATION regress_partition_merge_bob;
-- ERROR: must be owner of table tp_0_1
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
diff --git a/src/test/regress/sql/partition_split.sql b/src/test/regress/sql/partition_split.sql
index 44fcf208ac6..5ee7c5db631 100644
--- a/src/test/regress/sql/partition_split.sql
+++ b/src/test/regress/sql/partition_split.sql
@@ -960,7 +960,7 @@ ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); --error
RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_split_bob;
+ALTER TABLE ONLY t OWNER TO regress_partition_split_bob;
SET SESSION AUTHORIZATION regress_partition_split_bob;
ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
(PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index 95a46854b37..0e4944eac51 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -1257,7 +1257,7 @@ SELECT brin_summarize_range('sro_brin', 0);
DROP TABLE sro_tab;
-- Check with a partitioned table
CREATE TABLE sro_ptab (a int) PARTITION BY RANGE (a);
-ALTER TABLE sro_ptab OWNER TO regress_sro_user;
+ALTER TABLE ONLY sro_ptab OWNER TO regress_sro_user;
CREATE TABLE sro_part PARTITION OF sro_ptab FOR VALUES FROM (1) TO (10);
ALTER TABLE sro_part OWNER TO regress_sro_user;
INSERT INTO sro_ptab VALUES (1), (2), (3);
diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql
index 6b3566271df..1a2e80dade0 100644
--- a/src/test/regress/sql/rowsecurity.sql
+++ b/src/test/regress/sql/rowsecurity.sql
@@ -499,7 +499,7 @@ INSERT INTO part_document VALUES
( 9, 11, 1, 'regress_rls_dave', 'awesome science fiction'),
(10, 99, 2, 'regress_rls_dave', 'awesome technology book');
-ALTER TABLE part_document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY part_document ENABLE ROW LEVEL SECURITY;
-- Create policy on parent
-- user's security level must be higher than or equal to document's
@@ -2414,7 +2414,7 @@ CREATE TABLE rls_part PARTITION OF rls_ptbl FOR VALUES FROM (-100) TO (100);
INSERT INTO rls_ptbl SELECT x/10 FROM generate_series(1, 100) x;
ANALYZE rls_ptbl, rls_part;
-ALTER TABLE rls_ptbl ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY rls_ptbl ENABLE ROW LEVEL SECURITY;
ALTER TABLE rls_part ENABLE ROW LEVEL SECURITY;
GRANT SELECT ON rls_ptbl TO regress_rls_alice;
GRANT SELECT ON rls_part TO regress_rls_alice;
diff --git a/src/test/regress/sql/tablespace.sql b/src/test/regress/sql/tablespace.sql
index c43a59e5957..8eff2914a5b 100644
--- a/src/test/regress/sql/tablespace.sql
+++ b/src/test/regress/sql/tablespace.sql
@@ -195,13 +195,13 @@ SET default_tablespace TO regress_tblspace;
CREATE TABLE testschema.part_2 PARTITION OF testschema.part FOR VALUES IN (2);
SET default_tablespace TO pg_global;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
-ALTER TABLE testschema.part SET TABLESPACE regress_tblspace;
+ALTER TABLE ONLY testschema.part SET TABLESPACE regress_tblspace;
CREATE TABLE testschema.part_3 PARTITION OF testschema.part FOR VALUES IN (3);
CREATE TABLE testschema.part_4 PARTITION OF testschema.part FOR VALUES IN (4)
TABLESPACE pg_default;
CREATE TABLE testschema.part_56 PARTITION OF testschema.part FOR VALUES IN (5, 6)
PARTITION BY LIST (a);
-ALTER TABLE testschema.part SET TABLESPACE pg_default;
+ALTER TABLE ONLY testschema.part SET TABLESPACE pg_default;
CREATE TABLE testschema.part_78 PARTITION OF testschema.part FOR VALUES IN (7, 8)
PARTITION BY LIST (a);
CREATE TABLE testschema.part_910 PARTITION OF testschema.part FOR VALUES IN (9, 10)
diff --git a/src/test/regress/sql/update.sql b/src/test/regress/sql/update.sql
index 8b4707eb9c3..56b1edb00ce 100644
--- a/src/test/regress/sql/update.sql
+++ b/src/test/regress/sql/update.sql
@@ -341,7 +341,7 @@ DROP FUNCTION func_parted_mod_b();
-- RLS policies with update-row-movement
-----------------------------------------
-ALTER TABLE range_parted ENABLE ROW LEVEL SECURITY;
+ALTER TABLE ONLY range_parted ENABLE ROW LEVEL SECURITY;
CREATE USER regress_range_parted_user;
GRANT ALL ON range_parted, mintab TO regress_range_parted_user;
CREATE POLICY seeall ON range_parted AS PERMISSIVE FOR SELECT USING (true);
diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql
index 247b8e23b23..acff5824ebb 100644
--- a/src/test/regress/sql/vacuum.sql
+++ b/src/test/regress/sql/vacuum.sql
@@ -452,7 +452,7 @@ VACUUM (ANALYZE) vacowned_part1;
VACUUM (ANALYZE) vacowned_part2;
RESET ROLE;
-- Partitioned table and one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO regress_vacuum;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
@@ -466,7 +466,7 @@ VACUUM (ANALYZE) vacowned_part1;
VACUUM (ANALYZE) vacowned_part2;
RESET ROLE;
-- Only one partition owned by other user.
-ALTER TABLE vacowned_parted OWNER TO CURRENT_USER;
+ALTER TABLE ONLY vacowned_parted OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
VACUUM vacowned_part1;
@@ -479,7 +479,7 @@ VACUUM (ANALYZE) vacowned_part1;
VACUUM (ANALYZE) vacowned_part2;
RESET ROLE;
-- Only partitioned table owned by other user.
-ALTER TABLE vacowned_parted OWNER TO regress_vacuum;
+ALTER TABLE ONLY vacowned_parted OWNER TO regress_vacuum;
ALTER TABLE vacowned_part1 OWNER TO CURRENT_USER;
SET ROLE regress_vacuum;
VACUUM vacowned_parted;
--
2.54.0
[text/x-patch] v11-0002-Add-NOTICE-to-REPACK-CONCURRENTLY-tests.patch (1.1K, 3-v11-0002-Add-NOTICE-to-REPACK-CONCURRENTLY-tests.patch)
download | inline diff:
From 433f66953454b3270f28041008bd1d95e761d565 Mon Sep 17 00:00:00 2001
From: Jim Jones <[email protected]>
Date: Mon, 4 May 2026 12:57:30 +0200
Subject: [PATCH v11 2/2] Add NOTICE to REPACK (CONCURRENTLY) tests
---
contrib/test_decoding/expected/repack.out | 2 ++
1 file changed, 2 insertions(+)
diff --git a/contrib/test_decoding/expected/repack.out b/contrib/test_decoding/expected/repack.out
index 1f99f26c1f8..6008baa9a03 100644
--- a/contrib/test_decoding/expected/repack.out
+++ b/contrib/test_decoding/expected/repack.out
@@ -15,6 +15,8 @@ ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
+NOTICE: ALTER action OWNER TO on relation "ptnowner" does not affect present partitions
+HINT: Partitions may be modified individually, or specify ONLY to suppress this message.
CREATE TEMP TABLE ptnowner_oldnodes AS
SELECT oid, relname, relfilenode FROM pg_partition_tree('ptnowner') AS tree
JOIN pg_class AS c ON c.oid=tree.relid;
--
2.54.0
^ permalink raw reply [nested|flat] 19+ messages in thread
end of thread, other threads:[~2026-05-04 11:24 UTC | newest]
Thread overview: 19+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-01-13 11:16 Re: ALTER TABLE: warn when actions do not recurse to partitions Jim Jones <[email protected]>
2026-01-14 00:52 ` Chao Li <[email protected]>
2026-01-22 05:45 ` Chao Li <[email protected]>
2026-01-22 19:27 ` Jim Jones <[email protected]>
2026-01-23 00:11 ` Chao Li <[email protected]>
2026-01-23 09:57 ` Jim Jones <[email protected]>
2026-03-09 06:46 ` Chao Li <[email protected]>
2026-03-10 15:32 ` Greg Sabino Mullane <[email protected]>
2026-03-11 07:04 ` Chao Li <[email protected]>
2026-03-11 16:39 ` Greg Sabino Mullane <[email protected]>
2026-03-12 01:14 ` Chao Li <[email protected]>
2026-03-12 02:09 ` Greg Sabino Mullane <[email protected]>
2026-03-12 06:37 ` Chao Li <[email protected]>
2026-03-12 12:10 ` Greg Sabino Mullane <[email protected]>
2026-03-12 21:45 ` Zsolt Parragi <[email protected]>
2026-03-13 03:16 ` Chao Li <[email protected]>
2026-04-01 08:01 ` Chao Li <[email protected]>
2026-04-01 08:03 ` Chao Li <[email protected]>
2026-05-04 11:24 ` Jim Jones <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox