Subject: Re: ALTER TABLE: warn when actions do not recurse to partitions

From: Jim Jones <[email-masked]>
Date: Tue, 13 Jan 2026 12:16:08 +0100

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

---

From: Chao Li <[email-masked]>
Date: Wed, 14 Jan 2026 08:52:18 +0800

> On Jan 13, 2026, at 19:16, Jim Jones <[email-masked]> wrote:
> 
> Hi Chao
> 

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/

---

From: Chao Li <[email-masked]>
Date: Thu, 22 Jan 2026 13:45:05 +0800

On Jan 14, 2026, at 08:52, Chao Li <[email-masked]> wrote:

Hi Chao

PSA v3:

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,

---

From: Jim Jones <[email-masked]>
Date: Thu, 22 Jan 2026 20:27:11 +0100

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
> HINT:  partitions may be modified individually, or specify ONLY to
> suppress this message
> 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

---

From: Chao Li <[email-masked]>
Date: Fri, 23 Jan 2026 08:11:35 +0800

> On Jan 23, 2026, at 03:27, Jim Jones <[email-masked]> wrote:
> 
> Hi Chao
> 

Hi Jim, thanks a lot for the review.

> 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.

> 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,

---

From: Jim Jones <[email-masked]>
Date: Fri, 23 Jan 2026 10:57:48 +0100

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

---

From: Chao Li <[email-masked]>
Date: Mon, 9 Mar 2026 14:46:57 +0800

> On Jan 23, 2026, at 17:57, Jim Jones <[email-masked]> wrote:
> 
> 
> 

Hi Jim, thank you very much for your review and test.

> 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,

---

From: Greg Sabino Mullane <[email-masked]>
Date: Tue, 10 Mar 2026 11:32:29 -0400

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?

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

---

From: Chao Li <[email-masked]>
Date: Wed, 11 Mar 2026 15:04:34 +0800

> On Mar 10, 2026, at 23:32, Greg Sabino Mullane <[email-masked]> wrote:
> 
> Review of v6:

Thank you very much for the review.

> 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.

> 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.

> postNotice is a little misleading - maybe pending_notices or just notices?

Yes, “pending” makes sense. Updated in v7.

>    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.

> 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,

---

From: Greg Sabino Mullane <[email-masked]>
Date: Wed, 11 Mar 2026 12:39:19 -0400

On Wed, Mar 11, 2026 at 3:05 AM Chao Li <[email-masked]> 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().
>

Nothing worth the trouble, to be honest. As you rightly pointed out, this
is not a hot path.

Cheers,
Greg

---

From: Chao Li <[email-masked]>
Date: Thu, 12 Mar 2026 09:14:20 +0800

> On Mar 12, 2026, at 00:39, Greg Sabino Mullane <[email-masked]> 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,

---

From: Greg Sabino Mullane <[email-masked]>
Date: Wed, 11 Mar 2026 22:09:19 -0400

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

---

From: Chao Li <[email-masked]>
Date: Thu, 12 Mar 2026 14:37:56 +0800

> On Mar 12, 2026, at 10:09, Greg Sabino Mullane <[email-masked]> wrote:
> 
> One more small idea: lose the bool found = false, and simply return; on a matching strcmp
> 

Make sense. PFA v8.

Best regards,

---

From: Greg Sabino Mullane <[email-masked]>
Date: Thu, 12 Mar 2026 08:10:53 -0400

Looks good to me!

---

From: Zsolt Parragi <[email-masked]>
Date: Thu, 12 Mar 2026 21:45:20 +0000

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.

---

From: Chao Li <[email-masked]>
Date: Fri, 13 Mar 2026 11:16:16 +0800

> On Mar 13, 2026, at 05:45, Zsolt Parragi <[email-masked]> wrote:
> 
> Hello
> 

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,

---

From: Chao Li <[email-masked]>
Date: Wed, 1 Apr 2026 16:01:56 +0800

> On Mar 13, 2026, at 11:16, Chao Li <[email-masked]> wrote:
> 
> 
> 
> <v9-0001-ALTER-TABLE-emit-NOTICE-when-ONLY-is-omitted-but-.patch>

PFA v10 - fixed a test failure.

Best regards,

---

From: Chao Li <[email-masked]>
Date: Wed, 1 Apr 2026 16:03:48 +0800

> On Apr 1, 2026, at 16:01, Chao Li <[email-masked]> wrote:
> 
> 
> 

Oops, forgot the attachment.

---

From: Jim Jones <[email-masked]>
Date: Mon, 4 May 2026 13:24:03 +0200

Hi Chao

> 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
From 8d43b40d2fa172d5ae4a764d43c77708a56343f6 Mon Sep 17 00:00:00 2001
From: Jim Jones <[email-masked]>
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.

  - 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-masked]>
Reviewed-by: David G. Johnston <[email-masked]>
Reviewed-by: Greg Sabino Mullane <[email-masked]>
Reviewed-by: Jim Jones <[email-masked]>
Reviewed-by: Zsolt Parragi <[email-masked]>
Discussion: https://postgr.es/m/CAEoWx2=SLga-xH09Cq_PAvsHhQHrBK+V0vF821JKgzS=[email-masked]
---
 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

From 433f66953454b3270f28041008bd1d95e761d565 Mon Sep 17 00:00:00 2001
From: Jim Jones <[email-masked]>
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