Subject: Re: Add SPLIT PARTITION/MERGE PARTITIONS commands

From: Alexander Korotkov <[email-masked]>
Date: Wed, 10 Apr 2024 02:03:40 +0300

On Mon, Apr 8, 2024 at 11:43 PM Dmitry Koval <[email-masked]> wrote:
> Attached fix for the problems found by Alexander Lakhin.
>
> About grammar errors.
> Unfortunately, I don't know English well.
> Therefore, I plan (in the coming days) to show the text to specialists
> who perform technical translation of documentation.

Thank you.  I've pushed this fix with minor corrections from me.

------
Regards,
Alexander Korotkov

---

From: Alexander Lakhin <[email-masked]>
Date: Wed, 10 Apr 2024 12:00:00 +0300

Hello Alexander and Dmitry,

> Thank you.  I've pushed this fix with minor corrections from me.

Thank you for fixing that defect!

Please look at an error message emitted for foreign tables:
CREATE TABLE t (i int) PARTITION BY RANGE (i);
CREATE FOREIGN TABLE ftp_0_1 PARTITION OF t
   FOR VALUES FROM (0) TO (1)
   SERVER loopback OPTIONS (table_name 'lt_0_1');
CREATE FOREIGN TABLE ftp_1_2 PARTITION OF t
   FOR VALUES FROM (1) TO (2)
   SERVER loopback OPTIONS (table_name 'lt_1_2');
ALTER TABLE t MERGE PARTITIONS (ftp_0_1, ftp_1_2) INTO ftp_0_2;
ERROR:  "ftp_0_1" is not a table

Shouldn't it be more correct/precise?

Best regards,
Alexander

---

From: Alexander Lakhin <[email-masked]>
Date: Wed, 10 Apr 2024 15:00:00 +0300

>> Thank you.  I've pushed this fix with minor corrections from me.

Please look at another anomaly with MERGE.

CREATE TEMP TABLE t (i int) PARTITION BY RANGE (i);
CREATE TABLE tp_0_2 PARTITION OF t
   FOR VALUES FROM (0) TO (2);
fails with
ERROR:  cannot create a permanent relation as partition of temporary relation "t"

But
CREATE TEMP TABLE t (i int) PARTITION BY RANGE (i);
CREATE TEMP TABLE tp_0_1 PARTITION OF t
   FOR VALUES FROM (0) TO (1);
CREATE TEMP TABLE tp_1_2 PARTITION OF t
   FOR VALUES FROM (1) TO (2);
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
succeeds and we get:
regression=# \d+ t*
                                     Partitioned table "pg_temp_1.t"
  Column |  Type   | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
  i      | integer |           |          |         | plain |             |              |
Partition key: RANGE (i)
Partitions: tp_0_2 FOR VALUES FROM (0) TO (2)

                                          Table "public.tp_0_2"
  Column |  Type   | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
  i      | integer |           |          |         | plain |             |              |
Partition of: t FOR VALUES FROM (0) TO (2)
Partition constraint: ((i IS NOT NULL) AND (i >= 0) AND (i < 2))

Best regards,
Alexander

---

From: Dmitry Koval <[email-masked]>
Date: Wed, 10 Apr 2024 20:22:35 +0300

Hi!

Alexander Korotkov, thanks for the commit of previous fix.
Alexander Lakhin, thanks for the problem you found.

There are two corrections attached to the letter:

1) v1-0001-Fix-for-SPLIT-MERGE-partitions-of-temporary-table.patch - fix 
for the problem [1].

2) v1-0002-Fixes-for-english-text.patch - fixes for English text 
(comments, error messages etc.).

Links:
[1] 
https://www.postgresql.org/message-id/dbc8b96c-3cf0-d1ee-860d-0e491da20485%40gmail.com

-- 
With best regards,
Dmitry Koval

Postgres Professional: http://postgrespro.com

From 3c443a57c334c74e9218fd4e2f1ced45e6d4141d Mon Sep 17 00:00:00 2001
From: Koval Dmitry <[email-masked]>
Date: Wed, 10 Apr 2024 18:54:05 +0300
Subject: [PATCH v1 1/2] Fix for SPLIT/MERGE partitions of temporary table

---
 src/backend/commands/tablecmds.c              | 11 ++++---
 src/backend/parser/parse_utilcmd.c            |  2 +-
 src/test/regress/expected/partition_merge.out | 32 +++++++++++++++++--
 src/test/regress/expected/partition_split.out | 29 +++++++++++++++++
 src/test/regress/sql/partition_merge.sql      | 24 +++++++++++++-
 src/test/regress/sql/partition_split.sql      | 23 +++++++++++++
 6 files changed, 113 insertions(+), 8 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 8a98a0af48..3da9f6389d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -21145,17 +21145,19 @@ moveSplitTableRows(Relation rel, Relation splitRel, List *partlist, List *newPar
  * createPartitionTable: create table for a new partition with given name
  * (newPartName) like table (modelRelName)
  *
- * Emulates command: CREATE TABLE <newPartName> (LIKE <modelRelName>
+ * Emulates command: CREATE [TEMP] TABLE <newPartName> (LIKE <modelRelName>
  * INCLUDING ALL EXCLUDING INDEXES EXCLUDING IDENTITY)
  */
 static void
-createPartitionTable(RangeVar *newPartName, RangeVar *modelRelName,
+createPartitionTable(Relation rel, RangeVar *newPartName, RangeVar *modelRelName,
 					 AlterTableUtilityContext *context)
 {
 	CreateStmt *createStmt;
 	TableLikeClause *tlc;
 	PlannedStmt *wrapper;
 
+	newPartName->relpersistence = rel->rd_rel->relpersistence;
+
 	createStmt = makeNode(CreateStmt);
 	createStmt->relation = newPartName;
 	createStmt->tableElts = NIL;
@@ -21291,7 +21293,7 @@ ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
 		Relation	newPartRel;
 
-		createPartitionTable(sps->name, parentName, context);
+		createPartitionTable(rel, sps->name, parentName, context);
 
 		/* Open the new partition and acquire exclusive lock on it. */
 		newPartRel = table_openrv(sps->name, AccessExclusiveLock);
@@ -21491,7 +21493,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		mergePartName = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
 									 tmpRelName, -1);
 	}
-	createPartitionTable(mergePartName,
+	createPartitionTable(rel,
+						 mergePartName,
 						 makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
 									  RelationGetRelationName(rel), -1),
 						 context);
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index ceba069905..ef1a2a97c0 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3430,7 +3430,7 @@ checkPartition(Relation rel, Oid partRelOid)
 	if (partRel->rd_rel->relkind != RELKIND_RELATION)
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("\"%s\" is not a table",
+				 errmsg("\"%s\" is not a ordinary table",
 						RelationGetRelationName(partRel))));
 
 	if (!partRel->rd_rel->relispartition)
diff --git a/src/test/regress/expected/partition_merge.out b/src/test/regress/expected/partition_merge.out
index 60eacf6bf3..c69a717aaa 100644
--- a/src/test/regress/expected/partition_merge.out
+++ b/src/test/regress/expected/partition_merge.out
@@ -25,9 +25,9 @@ ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_fe
 ERROR:  partition with name "sales_feb2022" already used
 LINE 1: ...e MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_feb2...
                                                              ^
--- ERROR:  "sales_apr2022" is not a table
+-- ERROR:  "sales_apr2022" is not a ordinary table
 ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_apr2022) INTO sales_feb_mar_apr2022;
-ERROR:  "sales_apr2022" is not a table
+ERROR:  "sales_apr2022" is not a ordinary table
 -- ERROR:  invalid partitions order, partition "sales_mar2022" can not be merged
 -- (space between sections sales_jan2022 and sales_mar2022)
 ALTER TABLE sales_range MERGE PARTITIONS (sales_jan2022, sales_mar2022) INTO sales_jan_mar2022;
@@ -746,4 +746,32 @@ DROP TABLE t3;
 DROP TABLE t2;
 DROP TABLE t1;
 --
+-- Try to MERGE partitions of temporary table.
+--
+CREATE TEMP TABLE t (i int) PARTITION BY RANGE (i);
+CREATE TEMP TABLE tp_0_1 PARTITION OF t FOR VALUES FROM (0) TO (1);
+CREATE TEMP TABLE tp_1_2 PARTITION OF t FOR VALUES FROM (1) TO (2);
+SELECT c.oid::pg_catalog.regclass, pg_catalog.pg_get_expr(c.relpartbound, c.oid), c.relpersistence
+  FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
+  WHERE c.oid = i.inhrelid AND i.inhparent = 't'::regclass
+  ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT', c.oid::pg_catalog.regclass::pg_catalog.text;
+  oid   |        pg_get_expr         | relpersistence 
+--------+----------------------------+----------------
+ tp_0_1 | FOR VALUES FROM (0) TO (1) | t
+ tp_1_2 | FOR VALUES FROM (1) TO (2) | t
+(2 rows)
+
+ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
+-- Partition should be temporary.
+SELECT c.oid::pg_catalog.regclass, pg_catalog.pg_get_expr(c.relpartbound, c.oid), c.relpersistence
+  FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
+  WHERE c.oid = i.inhrelid AND i.inhparent = 't'::regclass
+  ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT', c.oid::pg_catalog.regclass::pg_catalog.text;
+  oid   |        pg_get_expr         | relpersistence 
+--------+----------------------------+----------------
+ tp_0_2 | FOR VALUES FROM (0) TO (2) | t
+(1 row)
+
+DROP TABLE t;
+--
 DROP SCHEMA partitions_merge_schema;
diff --git a/src/test/regress/expected/partition_split.out b/src/test/regress/expected/partition_split.out
index 26a0d09969..3d22083e41 100644
--- a/src/test/regress/expected/partition_split.out
+++ b/src/test/regress/expected/partition_split.out
@@ -1427,4 +1427,33 @@ ERROR:  relation "t1pa" is not a partition of relation "t2"
 DROP TABLE t2;
 DROP TABLE t1;
 --
+-- Try to SPLIT partition of temporary table.
+--
+CREATE TEMP TABLE t (i int) PARTITION BY RANGE (i);
+CREATE TEMP TABLE tp_0_2 PARTITION OF t FOR VALUES FROM (0) TO (2);
+SELECT c.oid::pg_catalog.regclass, pg_catalog.pg_get_expr(c.relpartbound, c.oid), c.relpersistence
+  FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
+  WHERE c.oid = i.inhrelid AND i.inhparent = 't'::regclass
+  ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT', c.oid::pg_catalog.regclass::pg_catalog.text;
+  oid   |        pg_get_expr         | relpersistence 
+--------+----------------------------+----------------
+ tp_0_2 | FOR VALUES FROM (0) TO (2) | t
+(1 row)
+
+ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
+  (PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
+   PARTITION tp_1_2 FOR VALUES FROM (1) TO (2));
+-- Partitions should be temporary.
+SELECT c.oid::pg_catalog.regclass, pg_catalog.pg_get_expr(c.relpartbound, c.oid), c.relpersistence
+  FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
+  WHERE c.oid = i.inhrelid AND i.inhparent = 't'::regclass
+  ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT', c.oid::pg_catalog.regclass::pg_catalog.text;
+  oid   |        pg_get_expr         | relpersistence 
+--------+----------------------------+----------------
+ tp_0_1 | FOR VALUES FROM (0) TO (1) | t
+ tp_1_2 | FOR VALUES FROM (1) TO (2) | t
+(2 rows)
+
+DROP TABLE t;
+--
 DROP SCHEMA partition_split_schema;
diff --git a/src/test/regress/sql/partition_merge.sql b/src/test/regress/sql/partition_merge.sql
index 9afed70365..cce5ffa29f 100644
--- a/src/test/regress/sql/partition_merge.sql
+++ b/src/test/regress/sql/partition_merge.sql
@@ -28,7 +28,7 @@ CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
 
 -- ERROR:  partition with name "sales_feb2022" already used
 ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_feb2022) INTO sales_feb_mar_apr2022;
--- ERROR:  "sales_apr2022" is not a table
+-- ERROR:  "sales_apr2022" is not a ordinary table
 ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_apr2022) INTO sales_feb_mar_apr2022;
 -- ERROR:  invalid partitions order, partition "sales_mar2022" can not be merged
 -- (space between sections sales_jan2022 and sales_mar2022)
@@ -444,5 +444,27 @@ DROP TABLE t3;
 DROP TABLE t2;
 DROP TABLE t1;
 
+--
+-- Try to MERGE partitions of temporary table.
+--
+CREATE TEMP TABLE t (i int) PARTITION BY RANGE (i);
+CREATE TEMP TABLE tp_0_1 PARTITION OF t FOR VALUES FROM (0) TO (1);
+CREATE TEMP TABLE tp_1_2 PARTITION OF t FOR VALUES FROM (1) TO (2);
+
+SELECT c.oid::pg_catalog.regclass, pg_catalog.pg_get_expr(c.relpartbound, c.oid), c.relpersistence
+  FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
+  WHERE c.oid = i.inhrelid AND i.inhparent = 't'::regclass
+  ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT', c.oid::pg_catalog.regclass::pg_catalog.text;
+
+ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
+
+-- Partition should be temporary.
+SELECT c.oid::pg_catalog.regclass, pg_catalog.pg_get_expr(c.relpartbound, c.oid), c.relpersistence
+  FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
+  WHERE c.oid = i.inhrelid AND i.inhparent = 't'::regclass
+  ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT', c.oid::pg_catalog.regclass::pg_catalog.text;
+
+DROP TABLE t;
+
 --
 DROP SCHEMA partitions_merge_schema;
diff --git a/src/test/regress/sql/partition_split.sql b/src/test/regress/sql/partition_split.sql
index 625b01ddd1..5dc2fc39cd 100644
--- a/src/test/regress/sql/partition_split.sql
+++ b/src/test/regress/sql/partition_split.sql
@@ -844,5 +844,28 @@ ALTER TABLE t2 SPLIT PARTITION t1pa INTO
 DROP TABLE t2;
 DROP TABLE t1;
 
+--
+-- Try to SPLIT partition of temporary table.
+--
+CREATE TEMP TABLE t (i int) PARTITION BY RANGE (i);
+CREATE TEMP TABLE tp_0_2 PARTITION OF t FOR VALUES FROM (0) TO (2);
+
+SELECT c.oid::pg_catalog.regclass, pg_catalog.pg_get_expr(c.relpartbound, c.oid), c.relpersistence
+  FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
+  WHERE c.oid = i.inhrelid AND i.inhparent = 't'::regclass
+  ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT', c.oid::pg_catalog.regclass::pg_catalog.text;
+
+ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
+  (PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
+   PARTITION tp_1_2 FOR VALUES FROM (1) TO (2));
+
+-- Partitions should be temporary.
+SELECT c.oid::pg_catalog.regclass, pg_catalog.pg_get_expr(c.relpartbound, c.oid), c.relpersistence
+  FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
+  WHERE c.oid = i.inhrelid AND i.inhparent = 't'::regclass
+  ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT', c.oid::pg_catalog.regclass::pg_catalog.text;
+
+DROP TABLE t;
+
 --
 DROP SCHEMA partition_split_schema;
-- 
2.40.1.windows.1

From ecbcbf622ae54bfeee7b92759058a117169dca5b Mon Sep 17 00:00:00 2001
From: Koval Dmitry <[email-masked]>
Date: Wed, 10 Apr 2024 19:55:18 +0300
Subject: [PATCH v1 2/2] Fixes for english text

---
 doc/src/sgml/ddl.sgml                         |  2 +-
 doc/src/sgml/ref/alter_table.sgml             |  6 +--
 src/backend/commands/tablecmds.c              | 18 +++----
 src/backend/parser/parse_utilcmd.c            |  6 +--
 src/backend/partitioning/partbounds.c         | 28 +++++------
 src/test/isolation/specs/partition-merge.spec |  2 +-
 src/test/regress/expected/partition_merge.out | 14 +++---
 src/test/regress/expected/partition_split.out | 50 +++++++++----------
 src/test/regress/sql/partition_merge.sql      | 12 ++---
 src/test/regress/sql/partition_split.sql      | 42 ++++++++--------
 10 files changed, 90 insertions(+), 90 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 00f44f56fa..026bfff70f 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -4387,7 +4387,7 @@ ALTER INDEX measurement_city_id_logdate_key
      a single partition using the
      <link linkend="sql-altertable-merge-partitions"><command>ALTER TABLE ... MERGE PARTITIONS</command></link>.
      This feature simplifies the management of partitioned tables by allowing
-     administrators to combine partitions that are no longer needed as
+     users to combine partitions that are no longer needed as
      separate entities.  It's important to note that this operation is not
      supported for hash-partitioned tables and acquires an
      <literal>ACCESS EXCLUSIVE</literal> lock, which could impact high-load
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 8f5bf185dd..fe36ff82e5 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -1175,7 +1175,7 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
       <itemizedlist>
        <listitem>
         <para>
-         For range-partitioned tables is necessary that the ranges
+         For range-partitioned tables it is necessary that the ranges
          of the partitions <replaceable class="parameter">partition_name1</replaceable>,
          <replaceable class="parameter">partition_name2</replaceable> [, ...] can
          be merged into one range without spaces and overlaps (otherwise an error
@@ -1185,10 +1185,10 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
        </listitem>
        <listitem>
         <para>
-         For list-partitioned tables the values lists of all partitions
+         For list-partitioned tables the value lists of all partitions
          <replaceable class="parameter">partition_name1</replaceable>,
          <replaceable class="parameter">partition_name2</replaceable> [, ...] are
-         combined and form a list of values of partition
+         combined and form the list of values of partition
          <replaceable class="parameter">partition_name</replaceable>.
         </para>
        </listitem>
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 3da9f6389d..c44d9c1db6 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -20888,7 +20888,7 @@ GetAttributeStorage(Oid atttypid, const char *storagemode)
 }
 
 /*
- * Struct with context of new partition for insert rows from splited partition
+ * Struct with context of new partition for inserting rows from split partition
  */
 typedef struct SplitPartitionContext
 {
@@ -20945,7 +20945,7 @@ deleteSplitPartitionContext(SplitPartitionContext *pc, int ti_options)
  *
  * New partitions description:
  * partlist: list of pointers to SinglePartitionSpec structures.
- * newPartRels: list of Relation's.
+ * newPartRels: list of Relations.
  * defaultPartOid: oid of DEFAULT partition, for table rel.
  */
 static void
@@ -21030,7 +21030,7 @@ moveSplitTableRows(Relation rel, Relation splitRel, List *partlist, List *newPar
 
 	/*
 	 * Map computing for moving attributes of split partition to new partition
-	 * (for first new partition but other new partitions can use the same
+	 * (for first new partition, but other new partitions can use the same
 	 * map).
 	 */
 	pc = (SplitPartitionContext *) lfirst(list_head(partContexts));
@@ -21085,7 +21085,7 @@ moveSplitTableRows(Relation rel, Relation splitRel, List *partlist, List *newPar
 
 		if (tuple_map)
 		{
-			/* Need to use map for copy attributes. */
+			/* Need to use map to copy attributes. */
 			insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
 		}
 		else
@@ -21245,7 +21245,7 @@ ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
 			RangeVarGetAndCheckCreationNamespace(sps->name, NoLock, NULL);
 
 		/*
-		 * This would fail later on anyway, if the relation already exists.
+		 * This would fail later on anyway if the relation already exists.
 		 * But by catching it here we can emit a nicer error message.
 		 */
 		existing_relid = get_relname_relid(relname, namespaceId);
@@ -21312,7 +21312,7 @@ ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
 		Relation	newPartRel = (Relation) lfirst(listptr2);
 
-		/* wqueue = NULL: verification for each cloned constraint is not need. */
+		/* wqueue = NULL: verification for each cloned constraint is not needed. */
 		attachPartitionTable(NULL, rel, newPartRel, sps->bound);
 		/* Keep the lock until commit. */
 		table_close(newPartRel, NoLock);
@@ -21386,7 +21386,7 @@ moveMergedTablesRows(Relation rel, List *mergingPartitionsList,
 
 			if (tuple_map)
 			{
-				/* Need to use map for copy attributes. */
+				/* Need to use map to copy attributes. */
 				insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
 			}
 			else
@@ -21488,7 +21488,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	/* Create table for new partition, use partitioned table as model. */
 	if (isSameName)
 	{
-		/* Create partition table with generated temparary name. */
+		/* Create partition table with generated temporary name. */
 		sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
 		mergePartName = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
 									 tmpRelName, -1);
@@ -21512,7 +21512,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
 
 	/*
 	 * Attach a new partition to the partitioned table. wqueue = NULL:
-	 * verification for each cloned constraint is not need.
+	 * verification for each cloned constraint is not needed.
 	 */
 	attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
 
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index ef1a2a97c0..82b6ba7fc6 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3492,7 +3492,7 @@ transformPartitionCmdForSplit(CreateStmtContext *cxt, PartitionCmd *partcmd)
  * transformPartitionCmdForMerge
  *		Analyze the ALTER TABLLE ... MERGE PARTITIONS command
  *
- * Does simple checks for merged partitions. Calculates bound of result
+ * Does simple checks for merged partitions. Calculates bound of resulting
  * partition.
  */
 static void
@@ -3537,7 +3537,7 @@ transformPartitionCmdForMerge(CreateStmtContext *cxt, PartitionCmd *partcmd)
 			if (equal(name, name2))
 				ereport(ERROR,
 						(errcode(ERRCODE_DUPLICATE_TABLE),
-						 errmsg("partition with name \"%s\" already used", name->relname)),
+						 errmsg("partition with name \"%s\" is already used", name->relname)),
 						parser_errposition(cxt->pstate, name2->location));
 		}
 
@@ -3551,7 +3551,7 @@ transformPartitionCmdForMerge(CreateStmtContext *cxt, PartitionCmd *partcmd)
 		partOids = lappend_oid(partOids, partOid);
 	}
 
-	/* Allocate bound of result partition. */
+	/* Allocate bound of resulting partition. */
 	Assert(partcmd->bound == NULL);
 	partcmd->bound = makeNode(PartitionBoundSpec);
 
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index c0c49b0a0b..1c8396e83c 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -3214,7 +3214,7 @@ check_new_partition_bound(char *relname, Relation parent,
 						PartitionRangeDatum *datum;
 
 						/*
-						 * Point to problematic key in the lower datums list;
+						 * Point to problematic key in the list of lower datums;
 						 * if we have equality, point to the first one.
 						 */
 						datum = cmpval == 0 ? linitial(spec->lowerdatums) :
@@ -4986,10 +4986,10 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
  * This is a helper function for check_partitions_for_split() and
  * calculate_partition_bound_for_merge().
  * This function compares upper bound of first_bound and lower bound of
- * second_bound. These bounds should be equal except case
+ * second_bound. These bounds should be equal except when
  * "defaultPart == true" (this means that one of split partitions is DEFAULT).
  * In this case upper bound of first_bound can be less than lower bound of
- * second_bound because space between of these bounds will be included in
+ * second_bound because space between these bounds will be included in
  * DEFAULT partition.
  *
  * parent:			partitioned table
@@ -4998,7 +4998,7 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
  * second_name:		name of second partition
  * second_bound:	bound of second partition
  * defaultPart:		true if one of split partitions is DEFAULT
- * pstate:			pointer to ParseState struct for determine error position
+ * pstate:			pointer to ParseState struct for determining error position
  */
 static void
 check_two_partitions_bounds_range(Relation parent,
@@ -5020,7 +5020,7 @@ check_two_partitions_bounds_range(Relation parent,
 	second_lower = make_one_partition_rbound(key, -1, second_bound->lowerdatums, true);
 
 	/*
-	 * lower1=false (the second to last argument) for correct comparison lower
+	 * lower1=false (the second to last argument) for correct comparison of lower
 	 * and upper bounds.
 	 */
 	cmpval = partition_rbound_cmp(key->partnatts,
@@ -5140,7 +5140,7 @@ get_partition_bound_spec(Oid partOid, RangeVar *name)
  *
  * (function for BY RANGE partitioning)
  *
- * Checks that bounds of new partition "spec" is inside bounds of split
+ * Checks that bounds of new partition "spec" are inside bounds of split
  * partition (with Oid splitPartOid). If first=true (this means that "spec" is
  * the first of new partitions) then lower bound of "spec" should be equal (or
  * greater than or equal in case defaultPart=true) to lower bound of split
@@ -5274,7 +5274,7 @@ check_partition_bounds_for_split_range(Relation parent,
  *
  * (function for BY LIST partitioning)
  *
- * Checks that bounds of new partition is inside bounds of split partition
+ * Checks that bounds of new partition are inside bounds of split partition
  * (with Oid splitPartOid).
  *
  * parent:			partitioned table
@@ -5445,8 +5445,8 @@ check_parent_values_in_new_partitions(Relation parent,
 	Assert(key->strategy == PARTITION_STRATEGY_LIST);
 
 	/*
-	 * Special processing for NULL value. Search NULL-value if it contains
-	 * split partition (partOid).
+	 * Special processing for NULL value. Search NULL value if the split
+	 * partition (partOid) contains it.
 	 */
 	if (partition_bound_accepts_nulls(boundinfo) &&
 		partdesc->oids[boundinfo->null_index] == partOid)
@@ -5461,7 +5461,7 @@ check_parent_values_in_new_partitions(Relation parent,
 
 	/*
 	 * Search all values of split partition with partOid in PartitionDesc of
-	 * partitionde table.
+	 * partitioned table.
 	 */
 	for (i = 0; i < boundinfo->ndatums; i++)
 	{
@@ -5498,7 +5498,7 @@ check_parent_values_in_new_partitions(Relation parent,
 
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-				 errmsg("new partitions not have value %s but split partition has",
+				 errmsg("new partitions do not have value %s but split partition has",
 						searchNull ? "NULL" : get_list_partvalue_string(notFoundVal))));
 	}
 }
@@ -5714,7 +5714,7 @@ check_partitions_for_split(Relation parent,
 			if (equal(sps->name, sps2->name))
 				ereport(ERROR,
 						(errcode(ERRCODE_DUPLICATE_TABLE),
-						 errmsg("name \"%s\" already used", sps2->name->relname)),
+						 errmsg("name \"%s\" is already used", sps2->name->relname)),
 						parser_errposition(pstate, sps2->name->location));
 		}
 	}
@@ -5805,14 +5805,14 @@ calculate_partition_bound_for_merge(Relation parent,
 				}
 
 				/*
-				 * Lower bound of first partition is a lower bound of merged
+				 * Lower bound of first partition is the lower bound of merged
 				 * partition.
 				 */
 				spec->lowerdatums =
 					((PartitionBoundSpec *) list_nth(bounds, lower_bounds[0]->index))->lowerdatums;
 
 				/*
-				 * Upper bound of last partition is a upper bound of merged
+				 * Upper bound of last partition is the upper bound of merged
 				 * partition.
 				 */
 				spec->upperdatums =
diff --git a/src/test/isolation/specs/partition-merge.spec b/src/test/isolation/specs/partition-merge.spec
index ec48732c58..dc2b9d3445 100644
--- a/src/test/isolation/specs/partition-merge.spec
+++ b/src/test/isolation/specs/partition-merge.spec
@@ -38,7 +38,7 @@ step s2s	{ SELECT * FROM tpart; }

 # s2 inserts row into table. s1 starts MERGE PARTITIONS then
-# s2 trying to update inserted row and waits until s1 finished
+# s2 is trying to update inserted row and waits until s1 finishes
 # MERGE operation.
 
 permutation s2b s2i s2c s1b s1merg s2b s2u s1c s2c s2s
diff --git a/src/test/regress/expected/partition_merge.out b/src/test/regress/expected/partition_merge.out
index c69a717aaa..6e25365124 100644
--- a/src/test/regress/expected/partition_merge.out
+++ b/src/test/regress/expected/partition_merge.out
@@ -20,19 +20,19 @@ CREATE TABLE sales_apr_1 PARTITION OF sales_apr2022 FOR VALUES FROM ('2022-04-01
 CREATE TABLE sales_apr_2 PARTITION OF sales_apr2022 FOR VALUES FROM ('2022-04-15') TO ('2022-05-01');
 ALTER TABLE sales_range ATTACH PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01');
 CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
--- ERROR:  partition with name "sales_feb2022" already used
+-- ERROR:  partition with name "sales_feb2022" is already used
 ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_feb2022) INTO sales_feb_mar_apr2022;
-ERROR:  partition with name "sales_feb2022" already used
+ERROR:  partition with name "sales_feb2022" is already used
 LINE 1: ...e MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_feb2...
                                                              ^
 -- ERROR:  "sales_apr2022" is not a ordinary table
 ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_apr2022) INTO sales_feb_mar_apr2022;
 ERROR:  "sales_apr2022" is not a ordinary table
--- ERROR:  invalid partitions order, partition "sales_mar2022" can not be merged
+-- ERROR:  lower bound of partition "sales_mar2022" conflicts with upper bound of previous partition "sales_jan2022"
 -- (space between sections sales_jan2022 and sales_mar2022)
 ALTER TABLE sales_range MERGE PARTITIONS (sales_jan2022, sales_mar2022) INTO sales_jan_mar2022;
 ERROR:  lower bound of partition "sales_mar2022" conflicts with upper bound of previous partition "sales_jan2022"
--- ERROR:  invalid partitions order, partition "sales_jan2022" can not be merged
+-- ERROR:  lower bound of partition "sales_jan2022" conflicts with upper bound of previous partition "sales_dec2021"
 -- (space between sections sales_dec2021 and sales_jan2022)
 ALTER TABLE sales_range MERGE PARTITIONS (sales_dec2021, sales_jan2022, sales_feb2022) INTO sales_dec_jan_feb2022;
 ERROR:  lower bound of partition "sales_jan2022" conflicts with upper bound of previous partition "sales_dec2021"
@@ -52,7 +52,7 @@ SELECT c.oid::pg_catalog.regclass, c.relkind, inhdetachpending, pg_catalog.pg_ge
 
 DROP TABLE sales_range;
 --
--- Add rows into partitioned table then merge partitions
+-- Add rows into partitioned table, then merge partitions
 --
 CREATE TABLE sales_range (salesman_id INT, salesman_name VARCHAR(30), sales_amount INT, sales_date DATE) PARTITION BY RANGE (sales_date);
 CREATE TABLE sales_jan2022 PARTITION OF sales_range FOR VALUES FROM ('2022-01-01') TO ('2022-02-01');
@@ -162,7 +162,7 @@ SELECT * FROM sales_others;
           14 | Smith         |          510 | 05-04-2022
 (1 row)
 
--- Use indexscan for test indexes
+-- Use indexscan for testing indexes
 SET enable_seqscan = OFF;
 SELECT * FROM sales_feb_mar_apr2022 where sales_date > '2022-01-01';
  salesman_id | salesman_name | sales_amount | sales_date 
@@ -704,7 +704,7 @@ SELECT * FROM sales_all;
           13 | Gandi         | Warsaw      |          150 | 03-08-2022
 (10 rows)
 
--- Use indexscan for test indexes after merge partitions
+-- Use indexscan for testing indexes after merging partitions
 SET enable_seqscan = OFF;
 SELECT * FROM sales_all WHERE sales_state = 'Warsaw';
  salesman_id | salesman_name | sales_state | sales_amount | sales_date 
diff --git a/src/test/regress/expected/partition_split.out b/src/test/regress/expected/partition_split.out
index 3d22083e41..87417b7796 100644
--- a/src/test/regress/expected/partition_split.out
+++ b/src/test/regress/expected/partition_split.out
@@ -43,7 +43,7 @@ ERROR:  empty range bound specified for partition "sales_mar2022"
 LINE 3:    PARTITION sales_mar2022 FOR VALUES FROM ('2022-03-01') TO...
                                                     ^
 DETAIL:  Specified lower bound ('03-01-2022') is greater than or equal to upper bound ('02-01-2022').
---ERROR:  list of split partitions should contains at least two items
+--ERROR:  list of split partitions should contain at least two items
 ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
   (PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-10-01'));
 ERROR:  list of new partitions should contain at least two items
@@ -55,21 +55,21 @@ ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
 ERROR:  lower bound of partition "sales_feb2022" is less than lower bound of split partition
 LINE 2:   (PARTITION sales_feb2022 FOR VALUES FROM ('2022-01-01') TO...
                                                     ^
--- ERROR:  name "sales_feb_mar_apr2022" already used
+-- ERROR:  name "sales_feb_mar_apr2022" is already used
 -- (We can create partition with the same name as split partition, but can't create two partitions with the same name)
 ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
   (PARTITION sales_feb_mar_apr2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
    PARTITION sales_feb_mar_apr2022 FOR VALUES FROM ('2022-03-01') TO ('2022-04-01'),
    PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01'));
-ERROR:  name "sales_feb_mar_apr2022" already used
+ERROR:  name "sales_feb_mar_apr2022" is already used
 LINE 3:    PARTITION sales_feb_mar_apr2022 FOR VALUES FROM ('2022-03...
                      ^
--- ERROR:  name "sales_feb2022" already used
+-- ERROR:  name "sales_feb2022" is already used
 ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
   (PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
    PARTITION sales_feb2022 FOR VALUES FROM ('2022-03-01') TO ('2022-04-01'),
    PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01'));
-ERROR:  name "sales_feb2022" already used
+ERROR:  name "sales_feb2022" is already used
 LINE 3:    PARTITION sales_feb2022 FOR VALUES FROM ('2022-03-01') TO...
                      ^
 -- ERROR:  "sales_feb_mar_apr2022" is not a partitioned table
@@ -86,7 +86,7 @@ ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
 ERROR:  upper bound of partition "sales_apr2022" is greater than upper bound of split partition
 LINE 4: ... sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-06-0...
                                                              ^
--- ERROR:  lower bound of partition "sales_mar2022" is not equals to upper bound of previous partition
+-- ERROR:  lower bound of partition "sales_mar2022" conflicts with upper bound of previous partition "sales_feb2022"
 ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
   (PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
    PARTITION sales_mar2022 FOR VALUES FROM ('2022-02-01') TO ('2022-04-01'),
@@ -96,7 +96,7 @@ LINE 3:    PARTITION sales_mar2022 FOR VALUES FROM ('2022-02-01') TO...
                                                     ^
 -- Tests for spaces between partitions, them should be executed without DEFAULT partition
 ALTER TABLE sales_range DETACH PARTITION sales_others;
--- ERROR:  lower bound of partition "sales_feb2022" is not equals to lower bound of split partition
+-- ERROR:  lower bound of partition "sales_feb2022" is not equal to lower bound of split partition
 ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
   (PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-02') TO ('2022-03-01'),
    PARTITION sales_mar2022 FOR VALUES FROM ('2022-03-01') TO ('2022-04-01'),
@@ -191,7 +191,7 @@ SELECT * FROM sales_others;
 
 DROP TABLE sales_range CASCADE;
 --
--- Add split partition then add rows into partitioned table
+-- Add split partition, then add rows into partitioned table
 --
 CREATE TABLE sales_range (salesman_id INT, salesman_name VARCHAR(30), sales_amount INT, sales_date DATE) PARTITION BY RANGE (sales_date);
 CREATE TABLE sales_jan2022 PARTITION OF sales_range FOR VALUES FROM ('2022-01-01') TO ('2022-02-01');
@@ -394,7 +394,7 @@ LINE 1: SELECT * FROM sales_jan_feb2022;
                       ^
 DROP TABLE sales_date CASCADE;
 --
--- Test: split DEFAULT partition; using a index on partition key; check index after split
+-- Test: split DEFAULT partition; use an index on partition key; check index after split
 --
 CREATE TABLE sales_range (salesman_id INT, salesman_name VARCHAR(30), sales_amount INT, sales_date DATE) PARTITION BY RANGE (sales_date);
 CREATE TABLE sales_jan2022 PARTITION OF sales_range FOR VALUES FROM ('2022-01-01') TO ('2022-02-01');
@@ -441,7 +441,7 @@ ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
    PARTITION sales_mar2022 FOR VALUES FROM ('2022-03-01') TO ('2022-04-01'),
    PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01'),
    PARTITION sales_others DEFAULT);
--- Use indexscan for test indexes
+-- Use indexscan for testing indexes
 SET enable_indexscan = ON;
 SET enable_seqscan = OFF;
 SELECT * FROM sales_feb2022 where sales_date > '2022-01-01';
@@ -503,7 +503,7 @@ SELECT * FROM pg_indexes WHERE tablename = 'sales_others' and schemaname = 'part
 
 DROP TABLE sales_range CASCADE;
 --
--- Test: some cases for split DEFAULT partition (different bounds)
+-- Test: some cases for splitting DEFAULT partition (different bounds)
 --
 CREATE TABLE sales_range (salesman_id INT, salesman_name VARCHAR(30), sales_amount INT, sales_date INT) PARTITION BY RANGE (sales_date);
 CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
@@ -537,7 +537,7 @@ ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
 ERROR:  lower bound of partition "sales_error" conflicts with upper bound of previous partition "sales_dec2022"
 LINE 3:    PARTITION sales_error FOR VALUES FROM (20211210) TO (2021...
                                                   ^
--- sales_error intersects with sales_dec2022 (exact the same bounds)
+-- sales_error intersects with sales_dec2022 (exactly the same bounds)
 -- ERROR:  lower bound of partition "sales_error" conflicts with upper bound of previous partition "sales_dec2022"
 ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
   (PARTITION sales_dec2022 FOR VALUES FROM (20211201) TO (20220101),
@@ -555,7 +555,7 @@ ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
 ERROR:  any partition in the list should be DEFAULT because split partition is DEFAULT
 LINE 2:   (PARTITION sales_dec2022 FOR VALUES FROM (20211201) TO (20...
                      ^
--- no error: bounds of sales_noerror between sales_dec2022 and sales_feb2022
+-- no error: bounds of sales_noerror are between sales_dec2022 and sales_feb2022
 ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
   (PARTITION sales_dec2022 FOR VALUES FROM (20211201) TO (20220101),
    PARTITION sales_noerror FOR VALUES FROM (20220110) TO (20220120),
@@ -564,7 +564,7 @@ ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
 DROP TABLE sales_range;
 CREATE TABLE sales_range (salesman_id INT, salesman_name VARCHAR(30), sales_amount INT, sales_date INT) PARTITION BY RANGE (sales_date);
 CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
--- no error: bounds of sales_noerror equals to lower and upper bounds of sales_dec2022 and sales_feb2022
+-- no error: bounds of sales_noerror are equal to lower and upper bounds of sales_dec2022 and sales_feb2022
 ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
   (PARTITION sales_dec2022 FOR VALUES FROM (20211201) TO (20220101),
    PARTITION sales_noerror FOR VALUES FROM (20210101) TO (20210201),
@@ -629,7 +629,7 @@ INSERT INTO sales_range VALUES (1, 10, '2022-03-11');
 DROP TABLE sales_range CASCADE;
 DROP TABLE salesmans CASCADE;
 --
--- Test: split partition on partitioned table in case exists FOREIGN KEY reference from another table
+-- Test: split partition on partitioned table in case of existing FOREIGN KEY reference from another table
 --
 CREATE TABLE salesmans(salesman_id INT PRIMARY KEY, salesman_name VARCHAR(30)) PARTITION BY RANGE (salesman_id);
 CREATE TABLE sales (salesman_id INT REFERENCES salesmans(salesman_id), sales_amount INT, sales_date DATE);
@@ -770,11 +770,11 @@ DROP TABLE salesmans CASCADE;
 DROP FUNCTION after_insert_row_trigger();
 --
 -- Test: split partition witch identity column
--- If split partition column is identity-column, columns of new partitions are identity-columns too.
+-- If split partition column is identity column, columns of new partitions are identity columns too.
 --
 CREATE TABLE salesmans(salesman_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, salesman_name VARCHAR(30)) PARTITION BY RANGE (salesman_id);
 CREATE TABLE salesmans1_2 PARTITION OF salesmans FOR VALUES FROM (1) TO (2);
--- Create new partition with identity-column:
+-- Create new partition with identity column:
 CREATE TABLE salesmans2_5(salesman_id INT NOT NULL, salesman_name VARCHAR(30));
 ALTER TABLE salesmans ATTACH PARTITION salesmans2_5 FOR VALUES FROM (2) TO (5);
 INSERT INTO salesmans (salesman_name) VALUES ('Poirot');
@@ -793,7 +793,7 @@ SELECT attname, attidentity, attgenerated FROM pg_attribute WHERE attnum > 0 AND
  salesman_name |             | 
 (2 rows)
 
--- Split partition has identity-column:
+-- Split partition has identity column:
 SELECT attname, attidentity, attgenerated FROM pg_attribute WHERE attnum > 0 AND attrelid = 'salesmans2_5'::regclass::oid;
     attname    | attidentity | attgenerated 
 ---------------+-------------+--------------
@@ -1063,8 +1063,8 @@ LINE 2: ...s_west FOR VALUES IN ('Lisbon', 'New York', 'Madrid', NULL),
 DROP TABLE sales_list;
 --
 -- Test: two specific errors for BY LIST partitioning:
---   * new partitions not has NULL value that split partition has.
---   * new partitions not has a value that split partition has.
+--   * new partitions do not have NULL value, which split partition has.
+--   * new partitions do not have a value that split partition has.
 --
 CREATE TABLE sales_list
 (salesman_id INT,
@@ -1075,18 +1075,18 @@ CREATE TABLE sales_list
 PARTITION BY LIST (sales_state);
 CREATE TABLE sales_nord PARTITION OF sales_list FOR VALUES IN ('Helsinki', 'St. Petersburg', 'Oslo');
 CREATE TABLE sales_all PARTITION OF sales_list FOR VALUES IN ('Warsaw', 'Lisbon', 'New York', 'Madrid', 'Bejing', 'Berlin', 'Delhi', 'Kyiv', 'Vladivostok', NULL);
--- ERROR:  new partitions not have value NULL but split partition has
+-- ERROR:  new partitions do not have value NULL but split partition has
 ALTER TABLE sales_list SPLIT PARTITION sales_all INTO
   (PARTITION sales_west FOR VALUES IN ('Lisbon', 'New York', 'Madrid'),
    PARTITION sales_east FOR VALUES IN ('Bejing', 'Delhi', 'Vladivostok'),
    PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv'));
-ERROR:  new partitions not have value NULL but split partition has
--- ERROR:  new partitions not have value 'Kyiv' but split partition has
+ERROR:  new partitions do not have value NULL but split partition has
+-- ERROR:  new partitions do not have value 'Kyiv' but split partition has
 ALTER TABLE sales_list SPLIT PARTITION sales_all INTO
   (PARTITION sales_west FOR VALUES IN ('Lisbon', 'New York', 'Madrid'),
    PARTITION sales_east FOR VALUES IN ('Bejing', 'Delhi', 'Vladivostok'),
    PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', NULL));
-ERROR:  new partitions not have value 'Kyiv' but split partition has
+ERROR:  new partitions do not have value 'Kyiv' but split partition has
 DROP TABLE sales_list;
 --
 -- Test: BY LIST partitioning, SPLIT PARTITION with data
@@ -1174,7 +1174,7 @@ SELECT * FROM sales_central;
           13 | Gandi         | Warsaw      |          150 | 03-08-2022
 (4 rows)
 
--- Use indexscan for test indexes after split partition
+-- Use indexscan for testing indexes after splitting partition
 SET enable_indexscan = ON;
 SET enable_seqscan = OFF;
 SELECT * FROM sales_central WHERE sales_state = 'Warsaw';
diff --git a/src/test/regress/sql/partition_merge.sql b/src/test/regress/sql/partition_merge.sql
index cce5ffa29f..c96120a4ad 100644
--- a/src/test/regress/sql/partition_merge.sql
+++ b/src/test/regress/sql/partition_merge.sql
@@ -26,14 +26,14 @@ ALTER TABLE sales_range ATTACH PARTITION sales_apr2022 FOR VALUES FROM ('2022-04
 
 CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
 
--- ERROR:  partition with name "sales_feb2022" already used
+-- ERROR:  partition with name "sales_feb2022" is already used
 ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_feb2022) INTO sales_feb_mar_apr2022;
 -- ERROR:  "sales_apr2022" is not a ordinary table
 ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_apr2022) INTO sales_feb_mar_apr2022;
--- ERROR:  invalid partitions order, partition "sales_mar2022" can not be merged
+-- ERROR:  lower bound of partition "sales_mar2022" conflicts with upper bound of previous partition "sales_jan2022"
 -- (space between sections sales_jan2022 and sales_mar2022)
 ALTER TABLE sales_range MERGE PARTITIONS (sales_jan2022, sales_mar2022) INTO sales_jan_mar2022;
--- ERROR:  invalid partitions order, partition "sales_jan2022" can not be merged
+-- ERROR:  lower bound of partition "sales_jan2022" conflicts with upper bound of previous partition "sales_dec2021"
 -- (space between sections sales_dec2021 and sales_jan2022)
 ALTER TABLE sales_range MERGE PARTITIONS (sales_dec2021, sales_jan2022, sales_feb2022) INTO sales_dec_jan_feb2022;
 
@@ -48,7 +48,7 @@ SELECT c.oid::pg_catalog.regclass, c.relkind, inhdetachpending, pg_catalog.pg_ge
 DROP TABLE sales_range;
 
 --
--- Add rows into partitioned table then merge partitions
+-- Add rows into partitioned table, then merge partitions
 --
 CREATE TABLE sales_range (salesman_id INT, salesman_name VARCHAR(30), sales_amount INT, sales_date DATE) PARTITION BY RANGE (sales_date);
 CREATE TABLE sales_jan2022 PARTITION OF sales_range FOR VALUES FROM ('2022-01-01') TO ('2022-02-01');
@@ -96,7 +96,7 @@ SELECT * FROM sales_jan2022;
 SELECT * FROM sales_feb_mar_apr2022;
 SELECT * FROM sales_others;
 
--- Use indexscan for test indexes
+-- Use indexscan for testing indexes
 SET enable_seqscan = OFF;
 
 SELECT * FROM sales_feb_mar_apr2022 where sales_date > '2022-01-01';
@@ -415,7 +415,7 @@ SELECT * FROM sales_list;
 SELECT * FROM sales_nord;
 SELECT * FROM sales_all;
 
--- Use indexscan for test indexes after merge partitions
+-- Use indexscan for testing indexes after merging partitions
 SET enable_seqscan = OFF;
 
 SELECT * FROM sales_all WHERE sales_state = 'Warsaw';
diff --git a/src/test/regress/sql/partition_split.sql b/src/test/regress/sql/partition_split.sql
index 5dc2fc39cd..763699c5af 100644
--- a/src/test/regress/sql/partition_split.sql
+++ b/src/test/regress/sql/partition_split.sql
@@ -42,7 +42,7 @@ ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
    PARTITION sales_mar2022 FOR VALUES FROM ('2022-03-01') TO ('2022-02-01'),
    PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01'));
 
---ERROR:  list of split partitions should contains at least two items
+--ERROR:  list of split partitions should contain at least two items
 ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
   (PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-10-01'));
 
@@ -52,14 +52,14 @@ ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
    PARTITION sales_mar2022 FOR VALUES FROM ('2022-03-01') TO ('2022-04-01'),
    PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01'));
 
--- ERROR:  name "sales_feb_mar_apr2022" already used
+-- ERROR:  name "sales_feb_mar_apr2022" is already used
 -- (We can create partition with the same name as split partition, but can't create two partitions with the same name)
 ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
   (PARTITION sales_feb_mar_apr2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
    PARTITION sales_feb_mar_apr2022 FOR VALUES FROM ('2022-03-01') TO ('2022-04-01'),
    PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01'));
 
--- ERROR:  name "sales_feb2022" already used
+-- ERROR:  name "sales_feb2022" is already used
 ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
   (PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
    PARTITION sales_feb2022 FOR VALUES FROM ('2022-03-01') TO ('2022-04-01'),
@@ -77,7 +77,7 @@ ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
    PARTITION sales_mar2022 FOR VALUES FROM ('2022-03-01') TO ('2022-04-01'),
    PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-06-01'));
 
--- ERROR:  lower bound of partition "sales_mar2022" is not equals to upper bound of previous partition
+-- ERROR:  lower bound of partition "sales_mar2022" conflicts with upper bound of previous partition "sales_feb2022"
 ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
   (PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
    PARTITION sales_mar2022 FOR VALUES FROM ('2022-02-01') TO ('2022-04-01'),
@@ -86,7 +86,7 @@ ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
 -- Tests for spaces between partitions, them should be executed without DEFAULT partition
 ALTER TABLE sales_range DETACH PARTITION sales_others;
 
--- ERROR:  lower bound of partition "sales_feb2022" is not equals to lower bound of split partition
+-- ERROR:  lower bound of partition "sales_feb2022" is not equal to lower bound of split partition
 ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
   (PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-02') TO ('2022-03-01'),
    PARTITION sales_mar2022 FOR VALUES FROM ('2022-03-01') TO ('2022-04-01'),
@@ -133,7 +133,7 @@ SELECT * FROM sales_others;
 DROP TABLE sales_range CASCADE;
 
 --
--- Add split partition then add rows into partitioned table
+-- Add split partition, then add rows into partitioned table
 --
 CREATE TABLE sales_range (salesman_id INT, salesman_name VARCHAR(30), sales_amount INT, sales_date DATE) PARTITION BY RANGE (sales_date);
 CREATE TABLE sales_jan2022 PARTITION OF sales_range FOR VALUES FROM ('2022-01-01') TO ('2022-02-01');
@@ -219,7 +219,7 @@ SELECT * FROM sales_jan_feb2022;
 DROP TABLE sales_date CASCADE;
 
 --
--- Test: split DEFAULT partition; using a index on partition key; check index after split
+-- Test: split DEFAULT partition; use an index on partition key; check index after split
 --
 CREATE TABLE sales_range (salesman_id INT, salesman_name VARCHAR(30), sales_amount INT, sales_date DATE) PARTITION BY RANGE (sales_date);
 CREATE TABLE sales_jan2022 PARTITION OF sales_range FOR VALUES FROM ('2022-01-01') TO ('2022-02-01');
@@ -250,7 +250,7 @@ ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
    PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01'),
    PARTITION sales_others DEFAULT);
 
--- Use indexscan for test indexes
+-- Use indexscan for testing indexes
 SET enable_indexscan = ON;
 SET enable_seqscan = OFF;
 
@@ -270,7 +270,7 @@ SELECT * FROM pg_indexes WHERE tablename = 'sales_others' and schemaname = 'part
 DROP TABLE sales_range CASCADE;
 
 --
--- Test: some cases for split DEFAULT partition (different bounds)
+-- Test: some cases for splitting DEFAULT partition (different bounds)
 --
 CREATE TABLE sales_range (salesman_id INT, salesman_name VARCHAR(30), sales_amount INT, sales_date INT) PARTITION BY RANGE (sales_date);
 CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
@@ -299,7 +299,7 @@ ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
    PARTITION sales_feb2022 FOR VALUES FROM (20220201) TO (20220301),
    PARTITION sales_others DEFAULT);
 
--- sales_error intersects with sales_dec2022 (exact the same bounds)
+-- sales_error intersects with sales_dec2022 (exactly the same bounds)
 -- ERROR:  lower bound of partition "sales_error" conflicts with upper bound of previous partition "sales_dec2022"
 ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
   (PARTITION sales_dec2022 FOR VALUES FROM (20211201) TO (20220101),
@@ -313,7 +313,7 @@ ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
    PARTITION sales_jan2022 FOR VALUES FROM (20220101) TO (20220201),
    PARTITION sales_feb2022 FOR VALUES FROM (20220201) TO (20220301));
 
--- no error: bounds of sales_noerror between sales_dec2022 and sales_feb2022
+-- no error: bounds of sales_noerror are between sales_dec2022 and sales_feb2022
 ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
   (PARTITION sales_dec2022 FOR VALUES FROM (20211201) TO (20220101),
    PARTITION sales_noerror FOR VALUES FROM (20220110) TO (20220120),
@@ -325,7 +325,7 @@ DROP TABLE sales_range;
 CREATE TABLE sales_range (salesman_id INT, salesman_name VARCHAR(30), sales_amount INT, sales_date INT) PARTITION BY RANGE (sales_date);
 CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
 
--- no error: bounds of sales_noerror equals to lower and upper bounds of sales_dec2022 and sales_feb2022
+-- no error: bounds of sales_noerror are equal to lower and upper bounds of sales_dec2022 and sales_feb2022
 ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
   (PARTITION sales_dec2022 FOR VALUES FROM (20211201) TO (20220101),
    PARTITION sales_noerror FOR VALUES FROM (20210101) TO (20210201),
@@ -372,7 +372,7 @@ DROP TABLE sales_range CASCADE;
 DROP TABLE salesmans CASCADE;
 
 --
--- Test: split partition on partitioned table in case exists FOREIGN KEY reference from another table
+-- Test: split partition on partitioned table in case of existing FOREIGN KEY reference from another table
 --
 CREATE TABLE salesmans(salesman_id INT PRIMARY KEY, salesman_name VARCHAR(30)) PARTITION BY RANGE (salesman_id);
 CREATE TABLE sales (salesman_id INT REFERENCES salesmans(salesman_id), sales_amount INT, sales_date DATE);
@@ -470,12 +470,12 @@ DROP FUNCTION after_insert_row_trigger();
 
 --
 -- Test: split partition witch identity column
--- If split partition column is identity-column, columns of new partitions are identity-columns too.
+-- If split partition column is identity column, columns of new partitions are identity columns too.
 --
 CREATE TABLE salesmans(salesman_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, salesman_name VARCHAR(30)) PARTITION BY RANGE (salesman_id);
 
 CREATE TABLE salesmans1_2 PARTITION OF salesmans FOR VALUES FROM (1) TO (2);
--- Create new partition with identity-column:
+-- Create new partition with identity column:
 CREATE TABLE salesmans2_5(salesman_id INT NOT NULL, salesman_name VARCHAR(30));
 ALTER TABLE salesmans ATTACH PARTITION salesmans2_5 FOR VALUES FROM (2) TO (5);
 
@@ -484,7 +484,7 @@ INSERT INTO salesmans (salesman_name) VALUES ('Ivanov');
 
 SELECT attname, attidentity, attgenerated FROM pg_attribute WHERE attnum > 0 AND attrelid = 'salesmans'::regclass::oid;
 SELECT attname, attidentity, attgenerated FROM pg_attribute WHERE attnum > 0 AND attrelid = 'salesmans1_2'::regclass::oid;
--- Split partition has identity-column:
+-- Split partition has identity column:
 SELECT attname, attidentity, attgenerated FROM pg_attribute WHERE attnum > 0 AND attrelid = 'salesmans2_5'::regclass::oid;
 
 ALTER TABLE salesmans SPLIT PARTITION salesmans2_5 INTO
@@ -631,8 +631,8 @@ DROP TABLE sales_list;
 
 --
 -- Test: two specific errors for BY LIST partitioning:
---   * new partitions not has NULL value that split partition has.
---   * new partitions not has a value that split partition has.
+--   * new partitions do not have NULL value, which split partition has.
+--   * new partitions do not have a value that split partition has.
 --
 CREATE TABLE sales_list
 (salesman_id INT,
@@ -645,13 +645,13 @@ PARTITION BY LIST (sales_state);
 CREATE TABLE sales_nord PARTITION OF sales_list FOR VALUES IN ('Helsinki', 'St. Petersburg', 'Oslo');
 CREATE TABLE sales_all PARTITION OF sales_list FOR VALUES IN ('Warsaw', 'Lisbon', 'New York', 'Madrid', 'Bejing', 'Berlin', 'Delhi', 'Kyiv', 'Vladivostok', NULL);
 
--- ERROR:  new partitions not have value NULL but split partition has
+-- ERROR:  new partitions do not have value NULL but split partition has
 ALTER TABLE sales_list SPLIT PARTITION sales_all INTO
   (PARTITION sales_west FOR VALUES IN ('Lisbon', 'New York', 'Madrid'),
    PARTITION sales_east FOR VALUES IN ('Bejing', 'Delhi', 'Vladivostok'),
    PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv'));
 
--- ERROR:  new partitions not have value 'Kyiv' but split partition has
+-- ERROR:  new partitions do not have value 'Kyiv' but split partition has
 ALTER TABLE sales_list SPLIT PARTITION sales_all INTO
   (PARTITION sales_west FOR VALUES IN ('Lisbon', 'New York', 'Madrid'),
    PARTITION sales_east FOR VALUES IN ('Bejing', 'Delhi', 'Vladivostok'),
@@ -703,7 +703,7 @@ SELECT * FROM sales_east;
 SELECT * FROM sales_nord;
 SELECT * FROM sales_central;
 
--- Use indexscan for test indexes after split partition
+-- Use indexscan for testing indexes after splitting partition
 SET enable_indexscan = ON;
 SET enable_seqscan = OFF;
 
-- 
2.40.1.windows.1

---

From: Richard Guo <[email-masked]>
Date: Thu, 11 Apr 2024 15:57:12 +0800

> (comments, error messages etc.).

FWIW, I also proposed a patch earlier that fixes error messages and
comments in the split partition code at
https://www.postgresql.org/message-id/flat/CAMbWs49DDsknxyoycBqiE72VxzL_sYHF6zqL8dSeNehKPJhkKg%40mail.gmail.com

Thanks
Richard

---

From: Dmitry Koval <[email-masked]>
Date: Thu, 11 Apr 2024 11:59:10 +0300

Hi!

> comments in the split partition code

Sorry, I thought all the fixes you suggested were already included in 
v1-0002-Fixes-for-english-text.patch (but they are not).
Added missing lines to v2-0002-Fixes-for-english-text.patch.

-- 
With best regards,
Dmitry Koval

From cfd5a56d15e7ead6dd7ae66cd382de3fa38150d7 Mon Sep 17 00:00:00 2001
From: Koval Dmitry <[email-masked]>
Date: Wed, 10 Apr 2024 18:54:05 +0300
Subject: [PATCH v2 1/2] Fix for SPLIT/MERGE partitions of temporary table

---

 
 
index ceba069905..a3bbcc99c0 100644
+				 errmsg("\"%s\" is not an ordinary table",
 
index 60eacf6bf3..9fe97a801d 100644
+-- ERROR:  "sales_apr2022" is not an ordinary table
+ERROR:  "sales_apr2022" is not an ordinary table
index 9afed70365..428b6cc88f 100644
 
+-- ERROR:  "sales_apr2022" is not an ordinary table
 
+--
 
+--

From 83cd86343b8a75afe89e4177fc064e53fe821b01 Mon Sep 17 00:00:00 2001
From: Koval Dmitry <[email-masked]>
Date: Wed, 10 Apr 2024 19:55:18 +0300
Subject: [PATCH v2 2/2] Fixes for english text

---
 doc/src/sgml/ddl.sgml                         |  2 +-
 doc/src/sgml/ref/alter_table.sgml             |  6 +-
 src/backend/commands/tablecmds.c              | 18 +++---
 src/backend/parser/parse_utilcmd.c            |  6 +-
 src/backend/partitioning/partbounds.c         | 30 +++++-----
 src/test/isolation/specs/partition-merge.spec |  2 +-
 src/test/regress/expected/partition_merge.out | 14 ++---
 src/test/regress/expected/partition_split.out | 58 +++++++++----------
 src/test/regress/sql/partition_merge.sql      | 12 ++--
 src/test/regress/sql/partition_split.sql      | 48 +++++++--------
 10 files changed, 98 insertions(+), 98 deletions(-)

 /*
 
 	/*
 
 		if (tuple_map)
 		{
 
 		/*

 			if (tuple_map)
 			{
 
 	/*
 
index a3bbcc99c0..1f6e90cd47 100644

 
index c0c49b0a0b..f89bdb3c86 100644
 
 						/*
 
 	/*
 
 	/*
 
 	/*
 
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-				 errmsg("new partitions not have value %s but split partition has",
+				 errmsg("new partitions do not have value %s but split partition does",
 						searchNull ? "NULL" : get_list_partvalue_string(notFoundVal))));
 	}
 }
@@ -5645,7 +5645,7 @@ check_partitions_for_split(Relation parent,
 	{
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-				 errmsg("any partition in the list should be DEFAULT because split partition is DEFAULT")),
+				 errmsg("all partitions in the list should be DEFAULT because split partition is DEFAULT")),
 				parser_errposition(pstate, ((SinglePartitionSpec *) linitial(partlist))->name->location));
 	}
 	else if (!isSplitPartDefault && (default_index >= 0) && OidIsValid(defaultPartOid))
@@ -5714,7 +5714,7 @@ check_partitions_for_split(Relation parent,
 			if (equal(sps->name, sps2->name))
 				ereport(ERROR,
 						(errcode(ERRCODE_DUPLICATE_TABLE),
-						 errmsg("name \"%s\" already used", sps2->name->relname)),
+						 errmsg("name \"%s\" is already used", sps2->name->relname)),
 						parser_errposition(pstate, sps2->name->location));
 		}
 	}
@@ -5805,14 +5805,14 @@ calculate_partition_bound_for_merge(Relation parent,
 				}
 
 				/*
 
 				/*

index 9fe97a801d..26756faf38 100644
 -- ERROR:  "sales_apr2022" is not an ordinary table
 ERROR:  "sales_apr2022" is not an ordinary table
 
 DROP TABLE sales_range;

index 3d22083e41..46f02013be 100644

 DROP TABLE sales_range CASCADE;
 --
--- Test: some cases for split DEFAULT partition (different bounds)
+-- Test: some cases for splitting DEFAULT partition (different bounds)
 --
 CREATE TABLE sales_range (salesman_id INT, salesman_name VARCHAR(30), sales_amount INT, sales_date INT) PARTITION BY RANGE (sales_date);
 CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
@@ -537,7 +537,7 @@ ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
 ERROR:  lower bound of partition "sales_error" conflicts with upper bound of previous partition "sales_dec2022"
 LINE 3:    PARTITION sales_error FOR VALUES FROM (20211210) TO (2021...
                                                   ^
--- sales_error intersects with sales_dec2022 (exact the same bounds)
+-- sales_error intersects with sales_dec2022 (exactly the same bounds)
 -- ERROR:  lower bound of partition "sales_error" conflicts with upper bound of previous partition "sales_dec2022"
 ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
   (PARTITION sales_dec2022 FOR VALUES FROM (20211201) TO (20220101),
@@ -547,15 +547,15 @@ ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
 ERROR:  lower bound of partition "sales_error" conflicts with upper bound of previous partition "sales_dec2022"
 LINE 3:    PARTITION sales_error FOR VALUES FROM (20211201) TO (2022...
                                                   ^
--- ERROR:  any partition in the list should be DEFAULT because split partition is DEFAULT
+-- ERROR:  all partitions in the list should be DEFAULT because split partition is DEFAULT
 ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
   (PARTITION sales_dec2022 FOR VALUES FROM (20211201) TO (20220101),
    PARTITION sales_jan2022 FOR VALUES FROM (20220101) TO (20220201),
    PARTITION sales_feb2022 FOR VALUES FROM (20220201) TO (20220301));
-ERROR:  any partition in the list should be DEFAULT because split partition is DEFAULT
+ERROR:  all partitions in the list should be DEFAULT because split partition is DEFAULT
 LINE 2:   (PARTITION sales_dec2022 FOR VALUES FROM (20211201) TO (20...
                      ^
--- no error: bounds of sales_noerror between sales_dec2022 and sales_feb2022
+-- no error: bounds of sales_noerror are between sales_dec2022 and sales_feb2022
 ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
   (PARTITION sales_dec2022 FOR VALUES FROM (20211201) TO (20220101),
    PARTITION sales_noerror FOR VALUES FROM (20220110) TO (20220120),
@@ -564,7 +564,7 @@ ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
 DROP TABLE sales_range;
 CREATE TABLE sales_range (salesman_id INT, salesman_name VARCHAR(30), sales_amount INT, sales_date INT) PARTITION BY RANGE (sales_date);
 CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
--- no error: bounds of sales_noerror equals to lower and upper bounds of sales_dec2022 and sales_feb2022
+-- no error: bounds of sales_noerror are equal to lower and upper bounds of sales_dec2022 and sales_feb2022
 ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
   (PARTITION sales_dec2022 FOR VALUES FROM (20211201) TO (20220101),
    PARTITION sales_noerror FOR VALUES FROM (20210101) TO (20210201),
@@ -616,7 +616,7 @@ SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conre
  FOREIGN KEY (salesman_id) REFERENCES salesmans(salesman_id) | sales_range_salesman_id_fkey   | {1}
 (2 rows)
 
--- ERROR:  new row for relation "sales_mar2022" violates check constraint "sales_range_salesman_id_check"
+-- ERROR:  new row for relation "sales_mar2022" violates check constraint "sales_range_sales_amount_check"
 INSERT INTO sales_range VALUES (1, 0, '2022-03-11');
 ERROR:  new row for relation "sales_mar2022" violates check constraint "sales_range_sales_amount_check"
 DETAIL:  Failing row contains (1, 0, 03-11-2022).
 
--- Split partition has identity-column:
+-- Split partition has identity column:
 SELECT attname, attidentity, attgenerated FROM pg_attribute WHERE attnum > 0 AND attrelid = 'salesmans2_5'::regclass::oid;
     attname    | attidentity | attgenerated 
 ---------------+-------------+--------------
@@ -1036,7 +1036,7 @@ PARTITION BY LIST (sales_state);
 CREATE TABLE sales_nord PARTITION OF sales_list FOR VALUES IN ('Oslo', 'St. Petersburg', 'Helsinki');
 CREATE TABLE sales_all PARTITION OF sales_list FOR VALUES IN ('Warsaw', 'Lisbon', 'New York', 'Madrid', 'Bejing', 'Berlin', 'Delhi', 'Kyiv', 'Vladivostok');
 CREATE TABLE sales_others PARTITION OF sales_list DEFAULT;
--- ERROR:  partition "sales_east" would overlap partition "sales_nord"
+-- ERROR:  new partition "sales_east" would overlap with another (not split) partition "sales_nord"
 ALTER TABLE sales_list SPLIT PARTITION sales_all INTO
   (PARTITION sales_west FOR VALUES IN ('Lisbon', 'New York', 'Madrid'),
    PARTITION sales_east FOR VALUES IN ('Bejing', 'Delhi', 'Vladivostok', 'Helsinki'),
@@ -1063,8 +1063,8 @@ LINE 2: ...s_west FOR VALUES IN ('Lisbon', 'New York', 'Madrid', NULL),
 DROP TABLE sales_list;
 --
 -- Test: two specific errors for BY LIST partitioning:
---   * new partitions not has NULL value that split partition has.
---   * new partitions not has a value that split partition has.
+--   * new partitions do not have NULL value, which split partition has.
+--   * new partitions do not have a value that split partition has.
 --
 CREATE TABLE sales_list
 (salesman_id INT,
@@ -1075,18 +1075,18 @@ CREATE TABLE sales_list
 PARTITION BY LIST (sales_state);
 CREATE TABLE sales_nord PARTITION OF sales_list FOR VALUES IN ('Helsinki', 'St. Petersburg', 'Oslo');
 CREATE TABLE sales_all PARTITION OF sales_list FOR VALUES IN ('Warsaw', 'Lisbon', 'New York', 'Madrid', 'Bejing', 'Berlin', 'Delhi', 'Kyiv', 'Vladivostok', NULL);
--- ERROR:  new partitions not have value NULL but split partition has
+-- ERROR:  new partitions do not have value NULL but split partition does
 ALTER TABLE sales_list SPLIT PARTITION sales_all INTO
   (PARTITION sales_west FOR VALUES IN ('Lisbon', 'New York', 'Madrid'),
    PARTITION sales_east FOR VALUES IN ('Bejing', 'Delhi', 'Vladivostok'),
    PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv'));
-ERROR:  new partitions not have value NULL but split partition has
--- ERROR:  new partitions not have value 'Kyiv' but split partition has
+ERROR:  new partitions do not have value NULL but split partition does
+-- ERROR:  new partitions do not have value 'Kyiv' but split partition does
 ALTER TABLE sales_list SPLIT PARTITION sales_all INTO
   (PARTITION sales_west FOR VALUES IN ('Lisbon', 'New York', 'Madrid'),
    PARTITION sales_east FOR VALUES IN ('Bejing', 'Delhi', 'Vladivostok'),
    PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', NULL));
-ERROR:  new partitions not have value 'Kyiv' but split partition has
+ERROR:  new partitions do not have value 'Kyiv' but split partition does
 DROP TABLE sales_list;
 --
 -- Test: BY LIST partitioning, SPLIT PARTITION with data
@@ -1174,7 +1174,7 @@ SELECT * FROM sales_central;
           13 | Gandi         | Warsaw      |          150 | 03-08-2022
 (4 rows)
 
index 428b6cc88f..99b8f11a18 100644

 -- ERROR:  "sales_apr2022" is not an ordinary table

 

 
index 5dc2fc39cd..576f9f0f63 100644

 

 

 

 
@@ -307,13 +307,13 @@ ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
 
--- ERROR:  any partition in the list should be DEFAULT because split partition is DEFAULT
+-- ERROR:  all partitions in the list should be DEFAULT because split partition is DEFAULT
 ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
   (PARTITION sales_dec2022 FOR VALUES FROM (20211201) TO (20220101),
    PARTITION sales_jan2022 FOR VALUES FROM (20220101) TO (20220201),
    PARTITION sales_feb2022 FOR VALUES FROM (20220201) TO (20220301));

--- no error: bounds of sales_noerror equals to lower and upper bounds of sales_dec2022 and sales_feb2022
+-- no error: bounds of sales_noerror are equal to lower and upper bounds of sales_dec2022 and sales_feb2022
 ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
   (PARTITION sales_dec2022 FOR VALUES FROM (20211201) TO (20220101),
    PARTITION sales_noerror FOR VALUES FROM (20210101) TO (20210201),
@@ -361,7 +361,7 @@ SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conre
 SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conrelid = 'sales_mar2022'::regclass::oid;
 SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conrelid = 'sales_apr2022'::regclass::oid;
 
--- ERROR:  new row for relation "sales_mar2022" violates check constraint "sales_range_salesman_id_check"
+-- ERROR:  new row for relation "sales_mar2022" violates check constraint "sales_range_sales_amount_check"
 INSERT INTO sales_range VALUES (1, 0, '2022-03-11');
 -- ERROR:  insert or update on table "sales_mar2022" violates foreign key constraint "sales_range_salesman_id_fkey"
 INSERT INTO sales_range VALUES (-1, 10, '2022-03-11');
@@ -372,7 +372,7 @@ DROP TABLE sales_range CASCADE;
 DROP TABLE salesmans CASCADE;

 

 
 ALTER TABLE salesmans SPLIT PARTITION salesmans2_5 INTO
@@ -609,7 +609,7 @@ CREATE TABLE sales_nord PARTITION OF sales_list FOR VALUES IN ('Oslo', 'St. Pete
 CREATE TABLE sales_all PARTITION OF sales_list FOR VALUES IN ('Warsaw', 'Lisbon', 'New York', 'Madrid', 'Bejing', 'Berlin', 'Delhi', 'Kyiv', 'Vladivostok');
 CREATE TABLE sales_others PARTITION OF sales_list DEFAULT;
 
--- ERROR:  partition "sales_east" would overlap partition "sales_nord"
+-- ERROR:  new partition "sales_east" would overlap with another (not split) partition "sales_nord"
 ALTER TABLE sales_list SPLIT PARTITION sales_all INTO
   (PARTITION sales_west FOR VALUES IN ('Lisbon', 'New York', 'Madrid'),
    PARTITION sales_east FOR VALUES IN ('Bejing', 'Delhi', 'Vladivostok', 'Helsinki'),
@@ -631,8 +631,8 @@ DROP TABLE sales_list;

+-- ERROR:  new partitions do not have value NULL but split partition does
 
+-- ERROR:  new partitions do not have value 'Kyiv' but split partition does

-- 
2.40.1.windows.1

---

From: Alexander Lakhin <[email-masked]>
Date: Thu, 11 Apr 2024 15:00:00 +0300

Hi Dmitry,

11.04.2024 11:59, Dmitry Koval wrote:
> Sorry, I thought all the fixes you suggested were already included in v1-0002-Fixes-for-english-text.patch (but they 
> are not).

It seems to me that v2-0001-Fix-for-SPLIT-MERGE-partitions-of-temporary-table.patch
is not complete either.
Take a look, please:
CREATE TABLE t (i int) PARTITION BY RANGE (i);
SET search_path = pg_temp, public;
CREATE TABLE tp_0_1 PARTITION OF t
   FOR VALUES FROM (0) TO (1);
-- fails with:
ERROR:  cannot create a temporary relation as partition of permanent relation "t"

But:
CREATE TABLE t (i int) PARTITION BY RANGE (i);
CREATE TABLE tp_0_1 PARTITION OF t
   FOR VALUES FROM (0) TO (1);
CREATE TABLE tp_1_2 PARTITION OF t
   FOR VALUES FROM (1) TO (2);
INSERT INTO t VALUES(0), (1);
SELECT * FROM t;
-- the expected result is:
  i
---
  0
  1
(2 rows)

SET search_path = pg_temp, public;
ALTER TABLE t
MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
-- succeeds, and
\c -
SELECT * FROM t;
-- gives:
  i
---
(0 rows)

Please also ask your tech writers to check contents of src/test/sql/*, if
possible (perhaps, they'll fix "salesmans" and improve grammar).

Best regards,
Alexander

---

From: Dmitry Koval <[email-masked]>
Date: Thu, 11 Apr 2024 16:27:40 +0300

Hi!

1.
Alexander Lakhin sent a question about index name after MERGE (partition 
name is the same as one of the merged partitions):

----start of quote----
I'm also confused by an index name after MERGE:
CREATE TABLE t (i int) PARTITION BY RANGE (i);

CREATE TABLE tp_0_1 PARTITION OF t FOR VALUES FROM (0) TO (1);
CREATE TABLE tp_1_2 PARTITION OF t FOR VALUES FROM (1) TO (2);

CREATE INDEX tidx ON t(i);
ALTER TABLE t MERGE PARTITIONS (tp_1_2, tp_0_1) INTO tp_1_2;
\d+ t*

                                          Table "public.tp_1_2"
  Column |  Type   | Collation | Nullable | Default | Storage | 
Compression | Stats target | Description
--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
  i      | integer |           |          |         | plain   | 
    |              |
Partition of: t FOR VALUES FROM (0) TO (2)
Partition constraint: ((i IS NOT NULL) AND (i >= 0) AND (i < 2))
Indexes:
     "merge-16385-3A14B2-tmp_i_idx" btree (i)

Is the name "merge-16385-3A14B2-tmp_i_idx" valid or it's something 
temporary?
----end of quote----

Fix for this case added to file 
v3-0001-Fix-for-SPLIT-MERGE-partitions-of-temporary-table.patch.

----

2.
 >It seems to me that v2-0001-Fix-for-SPLIT-MERGE-partitions-of-
 >temporary-table.patch is not complete either.

Added correction (and test), see 
v3-0001-Fix-for-SPLIT-MERGE-partitions-of-temporary-table.patch.

-- 
With best regards,
Dmitry Koval

From 58eb4abd4f065b6aa423bbddf62acd1799eba22e Mon Sep 17 00:00:00 2001
From: Koval Dmitry <[email-masked]>
Date: Wed, 10 Apr 2024 18:54:05 +0300
Subject: [PATCH v3 1/2] Fix for SPLIT/MERGE partitions of temporary table

---
 src/backend/commands/tablecmds.c              | 30 ++++---
 src/backend/parser/parse_utilcmd.c            |  2 +-
 src/test/regress/expected/partition_merge.out | 87 ++++++++++++++++++-
 src/test/regress/expected/partition_split.out | 29 +++++++
 src/test/regress/sql/partition_merge.sql      | 70 ++++++++++++++-
 src/test/regress/sql/partition_split.sql      | 23 +++++
 6 files changed, 227 insertions(+), 14 deletions(-)

index 8a98a0af48..2769be55be 100644
@@ -21145,17 +21145,20 @@ moveSplitTableRows(Relation rel, Relation splitRel, List *partlist, List *newPar
 
+	newPartName->relpersistence = rel->rd_rel->relpersistence;
+	newPartName->schemaname = modelRelName->schemaname;
+
 	createStmt = makeNode(CreateStmt);
 	createStmt->relation = newPartName;
 	createStmt->tableElts = NIL;
@@ -21291,7 +21294,7 @@ ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
 		Relation	newPartRel;

 		/* Open the new partition and acquire exclusive lock on it. */
 		newPartRel = table_openrv(sps->name, AccessExclusiveLock);
@@ -21491,7 +21494,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		mergePartName = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
 									 tmpRelName, -1);
 	}
-	createPartitionTable(mergePartName,
+	createPartitionTable(rel,
+						 mergePartName,
 						 makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
 									  RelationGetRelationName(rel), -1),
 						 context);
@@ -21507,12 +21511,6 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	/* Copy data from merged partitions to new partition. */
 	moveMergedTablesRows(rel, mergingPartitionsList, newPartRel);
 
-	/*
-	 * Attach a new partition to the partitioned table. wqueue = NULL:
-	 * verification for each cloned constraint is not need.
-	 */
-	attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
-
 	/* Unlock and drop merged partitions. */
 	foreach(listptr, mergingPartitionsList)
 	{
@@ -21542,7 +21540,19 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		/* Rename partition. */
 		RenameRelationInternal(RelationGetRelid(newPartRel),
 							   cmd->name->relname, false, false);
+		/*
+		 * Bump the command counter to make the tuple of renamed partition
+		 * visible for attach partition operation.
+		 */
+		CommandCounterIncrement();
 	}
+
+	/*
+	 * Attach a new partition to the partitioned table. wqueue = NULL:
+	 * verification for each cloned constraint is not needed.
+	 */
+	attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
+
 	/* Keep the lock until commit. */
 	table_close(newPartRel, NoLock);
 }
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index ceba069905..a3bbcc99c0 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3430,7 +3430,7 @@ checkPartition(Relation rel, Oid partRelOid)
 	if (partRel->rd_rel->relkind != RELKIND_RELATION)
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("\"%s\" is not a table",
+				 errmsg("\"%s\" is not an ordinary table",
 						RelationGetRelationName(partRel))));
 
 	if (!partRel->rd_rel->relispartition)
diff --git a/src/test/regress/expected/partition_merge.out b/src/test/regress/expected/partition_merge.out
index 60eacf6bf3..9d48262a82 100644
--- a/src/test/regress/expected/partition_merge.out
+++ b/src/test/regress/expected/partition_merge.out
@@ -25,9 +25,9 @@ ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_fe
 ERROR:  partition with name "sales_feb2022" already used
 LINE 1: ...e MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_feb2...
                                                              ^
--- ERROR:  "sales_apr2022" is not a table
+-- ERROR:  "sales_apr2022" is not an ordinary table
 ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_apr2022) INTO sales_feb_mar_apr2022;
-ERROR:  "sales_apr2022" is not a table
+ERROR:  "sales_apr2022" is not an ordinary table
 -- ERROR:  invalid partitions order, partition "sales_mar2022" can not be merged
 -- (space between sections sales_jan2022 and sales_mar2022)
 ALTER TABLE sales_range MERGE PARTITIONS (sales_jan2022, sales_mar2022) INTO sales_jan_mar2022;
@@ -746,4 +746,87 @@ DROP TABLE t3;
 DROP TABLE t2;
 DROP TABLE t1;
 --
+-- Try to MERGE partitions of temporary table.
+--
+CREATE TEMP TABLE t (i int) PARTITION BY RANGE (i);
+CREATE TEMP TABLE tp_0_1 PARTITION OF t FOR VALUES FROM (0) TO (1);
+CREATE TEMP TABLE tp_1_2 PARTITION OF t FOR VALUES FROM (1) TO (2);
+SELECT c.oid::pg_catalog.regclass, pg_catalog.pg_get_expr(c.relpartbound, c.oid), c.relpersistence
+  FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
+  WHERE c.oid = i.inhrelid AND i.inhparent = 't'::regclass
+  ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT', c.oid::pg_catalog.regclass::pg_catalog.text;
+  oid   |        pg_get_expr         | relpersistence 
+--------+----------------------------+----------------
+ tp_0_1 | FOR VALUES FROM (0) TO (1) | t
+ tp_1_2 | FOR VALUES FROM (1) TO (2) | t
+(2 rows)
+
+ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
+-- Partition should be temporary.
+SELECT c.oid::pg_catalog.regclass, pg_catalog.pg_get_expr(c.relpartbound, c.oid), c.relpersistence
+  FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
+  WHERE c.oid = i.inhrelid AND i.inhparent = 't'::regclass
+  ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT', c.oid::pg_catalog.regclass::pg_catalog.text;
+  oid   |        pg_get_expr         | relpersistence 
+--------+----------------------------+----------------
+ tp_0_2 | FOR VALUES FROM (0) TO (2) | t
+(1 row)
+
+DROP TABLE t;
+--
+-- Check the partition index name if the partition name is the same as one
+-- of the merged partitions.
+--
+CREATE TABLE t (i int) PARTITION BY RANGE (i);
+CREATE TABLE tp_0_1 PARTITION OF t FOR VALUES FROM (0) TO (1);
+CREATE TABLE tp_1_2 PARTITION OF t FOR VALUES FROM (1) TO (2);
+CREATE INDEX tidx ON t(i);
+ALTER TABLE t MERGE PARTITIONS (tp_1_2, tp_0_1) INTO tp_1_2;
+-- Indexname value should be 'tp_1_2_i_idx'.
+SELECT indexname FROM pg_indexes WHERE tablename = 'tp_1_2';
+  indexname   
+--------------
+ tp_1_2_i_idx
+(1 row)
+
+DROP TABLE t;
+--
+-- Try creating a partition in the temporary schema.
+--
+SET search_path = public, pg_temp;
+CREATE TABLE t (i int) PARTITION BY RANGE (i);
+CREATE TABLE tp_0_1 PARTITION OF t FOR VALUES FROM (0) TO (1);
+CREATE TABLE tp_1_2 PARTITION OF t FOR VALUES FROM (1) TO (2);
+SELECT c.oid::pg_catalog.regclass, c.relpersistence FROM pg_catalog.pg_class c WHERE c.oid = 't'::regclass;
+ oid | relpersistence 
+-----+----------------
+ t   | p
+(1 row)
+
+SELECT c.oid::pg_catalog.regclass, pg_catalog.pg_get_expr(c.relpartbound, c.oid), c.relpersistence
+  FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
+  WHERE c.oid = i.inhrelid AND i.inhparent = 't'::regclass
+  ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT', c.oid::pg_catalog.regclass::pg_catalog.text;
+  oid   |        pg_get_expr         | relpersistence 
+--------+----------------------------+----------------
+ tp_0_1 | FOR VALUES FROM (0) TO (1) | p
+ tp_1_2 | FOR VALUES FROM (1) TO (2) | p
+(2 rows)
+
+SET search_path = pg_temp, public;
+ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
+-- Partition tp_0_2 should be permanent (p).
+SELECT c.oid::pg_catalog.regclass, pg_catalog.pg_get_expr(c.relpartbound, c.oid), c.relpersistence
+  FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
+  WHERE c.oid = i.inhrelid AND i.inhparent = 't'::regclass
+  ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT', c.oid::pg_catalog.regclass::pg_catalog.text;
+  oid   |        pg_get_expr         | relpersistence 
+--------+----------------------------+----------------
+ tp_0_2 | FOR VALUES FROM (0) TO (2) | p
+(1 row)
+
+SET search_path = public, pg_temp;
+DROP TABLE t;
+RESET search_path;
+--
 DROP SCHEMA partitions_merge_schema;
diff --git a/src/test/regress/expected/partition_split.out b/src/test/regress/expected/partition_split.out
index 26a0d09969..3d22083e41 100644
--- a/src/test/regress/expected/partition_split.out
+++ b/src/test/regress/expected/partition_split.out
@@ -1427,4 +1427,33 @@ ERROR:  relation "t1pa" is not a partition of relation "t2"
 DROP TABLE t2;
 DROP TABLE t1;
 --
+-- Try to SPLIT partition of temporary table.
+--
+CREATE TEMP TABLE t (i int) PARTITION BY RANGE (i);
+CREATE TEMP TABLE tp_0_2 PARTITION OF t FOR VALUES FROM (0) TO (2);
+SELECT c.oid::pg_catalog.regclass, pg_catalog.pg_get_expr(c.relpartbound, c.oid), c.relpersistence
+  FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
+  WHERE c.oid = i.inhrelid AND i.inhparent = 't'::regclass
+  ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT', c.oid::pg_catalog.regclass::pg_catalog.text;
+  oid   |        pg_get_expr         | relpersistence 
+--------+----------------------------+----------------
+ tp_0_2 | FOR VALUES FROM (0) TO (2) | t
+(1 row)
+
+ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
+  (PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
+   PARTITION tp_1_2 FOR VALUES FROM (1) TO (2));
+-- Partitions should be temporary.
+SELECT c.oid::pg_catalog.regclass, pg_catalog.pg_get_expr(c.relpartbound, c.oid), c.relpersistence
+  FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
+  WHERE c.oid = i.inhrelid AND i.inhparent = 't'::regclass
+  ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT', c.oid::pg_catalog.regclass::pg_catalog.text;
+  oid   |        pg_get_expr         | relpersistence 
+--------+----------------------------+----------------
+ tp_0_1 | FOR VALUES FROM (0) TO (1) | t
+ tp_1_2 | FOR VALUES FROM (1) TO (2) | t
+(2 rows)
+
+DROP TABLE t;
+--
 DROP SCHEMA partition_split_schema;
diff --git a/src/test/regress/sql/partition_merge.sql b/src/test/regress/sql/partition_merge.sql
index 9afed70365..312f0e49d3 100644
--- a/src/test/regress/sql/partition_merge.sql
+++ b/src/test/regress/sql/partition_merge.sql
@@ -28,7 +28,7 @@ CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
 
@@ -444,5 +444,73 @@ DROP TABLE t3;
 DROP TABLE t2;
 DROP TABLE t1;
 
+--
+-- Try to MERGE partitions of temporary table.
+--
+CREATE TEMP TABLE t (i int) PARTITION BY RANGE (i);
+CREATE TEMP TABLE tp_0_1 PARTITION OF t FOR VALUES FROM (0) TO (1);
+CREATE TEMP TABLE tp_1_2 PARTITION OF t FOR VALUES FROM (1) TO (2);
+
+SELECT c.oid::pg_catalog.regclass, pg_catalog.pg_get_expr(c.relpartbound, c.oid), c.relpersistence
+  FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
+  WHERE c.oid = i.inhrelid AND i.inhparent = 't'::regclass
+  ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT', c.oid::pg_catalog.regclass::pg_catalog.text;
+
+ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
+
+-- Partition should be temporary.
+SELECT c.oid::pg_catalog.regclass, pg_catalog.pg_get_expr(c.relpartbound, c.oid), c.relpersistence
+  FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
+  WHERE c.oid = i.inhrelid AND i.inhparent = 't'::regclass
+  ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT', c.oid::pg_catalog.regclass::pg_catalog.text;
+
+DROP TABLE t;
+
+--
+-- Check the partition index name if the partition name is the same as one
+-- of the merged partitions.
+--
+CREATE TABLE t (i int) PARTITION BY RANGE (i);
+
+CREATE TABLE tp_0_1 PARTITION OF t FOR VALUES FROM (0) TO (1);
+CREATE TABLE tp_1_2 PARTITION OF t FOR VALUES FROM (1) TO (2);
+
+CREATE INDEX tidx ON t(i);
+ALTER TABLE t MERGE PARTITIONS (tp_1_2, tp_0_1) INTO tp_1_2;
+
+-- Indexname value should be 'tp_1_2_i_idx'.
+SELECT indexname FROM pg_indexes WHERE tablename = 'tp_1_2';
+
+DROP TABLE t;
+
+--
+-- Try creating a partition in the temporary schema.
+--
+SET search_path = public, pg_temp;
+CREATE TABLE t (i int) PARTITION BY RANGE (i);
+CREATE TABLE tp_0_1 PARTITION OF t FOR VALUES FROM (0) TO (1);
+CREATE TABLE tp_1_2 PARTITION OF t FOR VALUES FROM (1) TO (2);
+
+SELECT c.oid::pg_catalog.regclass, c.relpersistence FROM pg_catalog.pg_class c WHERE c.oid = 't'::regclass;
+SELECT c.oid::pg_catalog.regclass, pg_catalog.pg_get_expr(c.relpartbound, c.oid), c.relpersistence
+  FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
+  WHERE c.oid = i.inhrelid AND i.inhparent = 't'::regclass
+  ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT', c.oid::pg_catalog.regclass::pg_catalog.text;
+
+SET search_path = pg_temp, public;
+
+ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
+
+-- Partition tp_0_2 should be permanent (p).
+SELECT c.oid::pg_catalog.regclass, pg_catalog.pg_get_expr(c.relpartbound, c.oid), c.relpersistence
+  FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
+  WHERE c.oid = i.inhrelid AND i.inhparent = 't'::regclass
+  ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT', c.oid::pg_catalog.regclass::pg_catalog.text;
+
+SET search_path = public, pg_temp;
+
+DROP TABLE t;
+RESET search_path;
+
 --
 DROP SCHEMA partitions_merge_schema;
diff --git a/src/test/regress/sql/partition_split.sql b/src/test/regress/sql/partition_split.sql
index 625b01ddd1..5dc2fc39cd 100644
--- a/src/test/regress/sql/partition_split.sql
+++ b/src/test/regress/sql/partition_split.sql
@@ -844,5 +844,28 @@ ALTER TABLE t2 SPLIT PARTITION t1pa INTO
 DROP TABLE t2;
 DROP TABLE t1;
 
+--

From f80a240209810749d64203fd469552aab1864d48 Mon Sep 17 00:00:00 2001
From: Koval Dmitry <[email-masked]>
Date: Wed, 10 Apr 2024 19:55:18 +0300
Subject: [PATCH v3 2/2] Fixes for english text

---
 src/backend/commands/tablecmds.c              | 16 ++---
 10 files changed, 97 insertions(+), 97 deletions(-)

index 2769be55be..dff9ac9df1 100644
 
 /*
 
 	/*
 
 		if (tuple_map)
 		{
@@ -21246,7 +21246,7 @@ ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
 
 		/*
@@ -21313,7 +21313,7 @@ ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
 
@@ -21387,7 +21387,7 @@ moveMergedTablesRows(Relation rel, List *mergingPartitionsList,
 
 			if (tuple_map)
 			{
@@ -21489,7 +21489,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,

 
 
 						/*
 
 	/*
 
 	/*
 
 	/*
 
 		ereport(ERROR,
 
 				/*
 
 				/*

index 9d48262a82..39c3b90b9c 100644
 
 DROP TABLE sales_range;

 

 
 
index 312f0e49d3..38cb60585d 100644

 

 

 

 

 

 

 

 

 

 

 

 
-- 
2.40.1.windows.1

---

From: Alexander Korotkov <[email-masked]>
Date: Thu, 11 Apr 2024 17:21:17 +0300

Hi, Dmitry!

> Compression | Stats target | Description

Thank you, I'll review this later today.

------
Regards,
Alexander Korotkov

---

From: Alexander Lakhin <[email-masked]>
Date: Thu, 11 Apr 2024 20:00:00 +0300

11.04.2024 16:27, Dmitry Koval wrote:
>
> Added correction (and test), see v3-0001-Fix-for-SPLIT-MERGE-partitions-of-temporary-table.patch.
>

Thank you for the correction, but may be an attempt to merge into implicit
pg_temp should fail just like CREATE TABLE ... PARTITION OF ... does?

Please look also at another anomaly with schemas:
CREATE SCHEMA s1;
CREATE TABLE t (i int) PARTITION BY RANGE (i);
CREATE TABLE tp_0_2 PARTITION OF t
   FOR VALUES FROM (0) TO (2);
ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
   (PARTITION s1.tp0 FOR VALUES FROM (0) TO (1), PARTITION s1.tp1 FOR VALUES FROM (1) TO (2));
results in:
\d+ s1.*
Did not find any relation named "s1.*"
\d+ tp*
                                           Table "public.tp0"
...
                                            Table "public.tp1"
...

Best regards,
Alexander

---

From: Alexander Korotkov <[email-masked]>
Date: Fri, 12 Apr 2024 04:53:43 +0300

> Did not find any relation named "s1.*"

+1
I think we shouldn't unconditionally copy schema name and
relpersistence from the parent table.  Instead we should throw the
error on a mismatch like CREATE TABLE ... PARTITION OF ... does.  I'm
working on revising this fix.

------
Regards,
Alexander Korotkov

---

From: Robert Haas <[email-masked]>
Date: Thu, 11 Apr 2024 22:20:53 -0400

> working on revising this fix.

We definitely shouldn't copy the schema name from the parent table. It
should be possible to schema-qualify the new partition names, and if
you don't, then the search_path should determine where they get
placed.

But I am inclined to think that relpersistence should be copied. It's
weird that you split an unlogged partition and you get logged
partitions.

One of the things I dislike about this type of feature -- not this
implementation specifically, but just this kind of idea in general --
is that the syntax mentions a whole bunch of tables but in a way where
you can't set their properties. Persistence, reloptions, whatever.
There's just no place to mention any of that stuff - and if you wanted
to create a place, you'd have to invent special syntax for each
separate thing. That's why I think it's good that the normal way of
creating a partition is CREATE TABLE .. PARTITION OF. Because that
way, we know that the full power of the CREATE TABLE statement is
always available, and you can set anything that you could set for a
table that is not a partition.

Of course, that is not to say that some people won't like to have a
feature of this sort. I expect they will. The approach does have some
drawbacks, though.

-- 
Robert Haas
EDB: http://www.enterprisedb.com

---

From: Dmitry Koval <[email-masked]>
Date: Fri, 12 Apr 2024 16:04:23 +0300

Hi!

Attached is a patch with corrections based on comments in previous 
letters (I think these corrections are not final).
I'll be very grateful for feedbacks and bug reports.

11.04.2024 20:00, Alexander Lakhin wrote:
 > may be an attempt to merge into implicit

Corrected. Result is:

\d+ s1.*
Table "s1.tp0"
...
Table "s1.tp1"
...
\d+ tp*
Did not find any relation named "tp*".

12.04.2024 4:53, Alexander Korotkov wrote:
 > error on a mismatch like CREATE TABLE ... PARTITION OF ... does.
12.04.2024 5:20, Robert Haas wrote:
 > We definitely shouldn't copy the schema name from the parent table.

Fixed.

 > you can't set their properties. Persistence, reloptions, whatever.

In next releases I want to allow specifying options (probably, first of 
all, specifying tablespace of the partitions).
But before that, I would like to get a users reaction - what options 
they really need?

-- 
With best regards,
Dmitry Koval

From 707d15cf9ee4673a1deed2825f48ffbc09a34e9d Mon Sep 17 00:00:00 2001
From: Koval Dmitry <[email-masked]>
Date: Wed, 10 Apr 2024 18:54:05 +0300
Subject: [PATCH v4 1/2] Fix for SPLIT/MERGE partitions of temporary table

---
 src/backend/commands/tablecmds.c              |  32 +++--
 src/backend/parser/parse_utilcmd.c            |  19 ++-
 src/test/regress/expected/partition_merge.out | 118 +++++++++++++++++-
 src/test/regress/expected/partition_split.out |  29 +++++
 src/test/regress/sql/partition_merge.sql      |  95 +++++++++++++-
 src/test/regress/sql/partition_split.sql      |  23 ++++
 6 files changed, 297 insertions(+), 19 deletions(-)

index 8a98a0af48..b59e1dda03 100644

 
 		/* Open the new partition and acquire exclusive lock on it. */
 		newPartRel = table_openrv(sps->name, AccessExclusiveLock);
@@ -21488,10 +21490,10 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	{
 		/* Create partition table with generated temparary name. */
 		sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
-		mergePartName = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
-									 tmpRelName, -1);
+		mergePartName = makeRangeVar(cmd->name->schemaname, tmpRelName, -1);
 	}
-	createPartitionTable(mergePartName,
+	createPartitionTable(rel,
+						 mergePartName,
 						 makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
 									  RelationGetRelationName(rel), -1),
 						 context);
@@ -21507,12 +21509,6 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	/* Copy data from merged partitions to new partition. */
 	moveMergedTablesRows(rel, mergingPartitionsList, newPartRel);
 
-	/*
-	 * Attach a new partition to the partitioned table. wqueue = NULL:
-	 * verification for each cloned constraint is not need.
-	 */
-	attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
-
 	/* Unlock and drop merged partitions. */
 	foreach(listptr, mergingPartitionsList)
 	{
@@ -21542,7 +21538,19 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		/* Rename partition. */
 		RenameRelationInternal(RelationGetRelid(newPartRel),
 							   cmd->name->relname, false, false);
+		/*
+		 * Bump the command counter to make the tuple of renamed partition
+		 * visible for attach partition operation.
+		 */
+		CommandCounterIncrement();
 	}
+
+	/*
+	 * Attach a new partition to the partitioned table. wqueue = NULL:
+	 * verification for each cloned constraint is not needed.
+	 */
+	attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
+
 	/* Keep the lock until commit. */
 	table_close(newPartRel, NoLock);
 }
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index ceba069905..6b4dd81470 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3421,16 +3421,27 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
  *		Check that partRelOid is an oid of partition of the parent table rel
  */
 static void
-checkPartition(Relation rel, Oid partRelOid)
+checkPartition(Relation rel, RangeVar *partName, Oid partRelOid)
 {
 	Relation	partRel;
+	Oid			nspid;
+
+	nspid = RangeVarGetCreationNamespace(partName);
+
+	/* If the parent table is permanent, so must be all of its partitions. */
+	if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
+		isTempOrTempToastNamespace(nspid))
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
+						RelationGetRelationName(rel))));
 
 	partRel = relation_open(partRelOid, AccessShareLock);

 	if (!partRel->rd_rel->relispartition)
@@ -3481,7 +3492,7 @@ transformPartitionCmdForSplit(CreateStmtContext *cxt, PartitionCmd *partcmd)
 
 	splitPartOid = RangeVarGetRelid(partcmd->name, NoLock, false);
 
-	checkPartition(parent, splitPartOid);
+	checkPartition(parent, partcmd->name, splitPartOid);
 
 	/* Then we should check partitions with transformed bounds. */
 	check_partitions_for_split(parent, splitPartOid, partcmd->name, partcmd->partlist, cxt->pstate);
@@ -3546,7 +3557,7 @@ transformPartitionCmdForMerge(CreateStmtContext *cxt, PartitionCmd *partcmd)
 		if (partOid == defaultPartOid)
 			isDefaultPart = true;
 
-		checkPartition(parent, partOid);
+		checkPartition(parent, name, partOid);
 
index 60eacf6bf3..23fe99ba28 100644
@@ -746,4 +746,118 @@ DROP TABLE t3;
+-- Try creating a partition of the permanent relation in a temporary schema.
+-- ERROR:  cannot create a temporary relation as partition of permanent relation "t"
+ERROR:  cannot create a temporary relation as partition of permanent relation "t"
+-- Try creating a partition of the temporary relation in a permanent schema.
+ t   | t
+-- There is no problem with creating a temporary partition in a permanent schema.
+-- Partition tp_0_2 should be temporary (t).
+SET search_path = pg_temp, pg_temp;
index 9afed70365..98738e165b 100644
 
@@ -444,5 +444,98 @@ DROP TABLE t3;
 DROP TABLE t2;
 DROP TABLE t1;
 
+--
+-- Try creating a partition of the permanent relation in a temporary schema.
+-- ERROR:  cannot create a temporary relation as partition of permanent relation "t"
+-- Try creating a partition of the temporary relation in a permanent schema.
+-- There is no problem with creating a temporary partition in a permanent schema.
+-- Partition tp_0_2 should be temporary (t).
+SET search_path = pg_temp, pg_temp;
 
+--

From 1f34e2130bb836ef4cf847e8a40c79092fba77c7 Mon Sep 17 00:00:00 2001
From: Koval Dmitry <[email-masked]>
Date: Wed, 10 Apr 2024 19:55:18 +0300
Subject: [PATCH v4 2/2] Fixes for english text

---

index b59e1dda03..f58ff43699 100644
 
 /*
 
 	/*
 
 		if (tuple_map)
 		{
 
 		/*

 			if (tuple_map)
 			{
 		mergePartName = makeRangeVar(cmd->name->schemaname, tmpRelName, -1);
index 6b4dd81470..361b31b7d3 100644
@@ -3503,7 +3503,7 @@ transformPartitionCmdForSplit(CreateStmtContext *cxt, PartitionCmd *partcmd)
@@ -3548,7 +3548,7 @@ transformPartitionCmdForMerge(CreateStmtContext *cxt, PartitionCmd *partcmd)
 
@@ -3562,7 +3562,7 @@ transformPartitionCmdForMerge(CreateStmtContext *cxt, PartitionCmd *partcmd)
 		partOids = lappend_oid(partOids, partOid);
 	}

 
 						/*
 
 	/*
 
 	/*
 
 	/*
 
 		ereport(ERROR,
 
 				/*
 
 				/*

index 23fe99ba28..94b3c59cac 100644
 
 DROP TABLE sales_range;

 

 
 
index 98738e165b..af5c2dab6e 100644

 

 

 

 

 

 

 

 

 

 

 

 
-- 
2.40.1.windows.1

---

From: Alexander Lakhin <[email-masked]>
Date: Fri, 12 Apr 2024 20:00:00 +0300

Hi Dmitry,

12.04.2024 16:04, Dmitry Koval wrote:
> Hi!
>
> Attached is a patch with corrections based on comments in previous letters (I think these corrections are not final).

Thank you!
Still now we're able to create a partition in the pg_temp schema
explicitly. Please try:
ALTER TABLE t
MERGE PARTITIONS (tp_0_1, tp_1_2) INTO pg_temp.tp_0_2;

in the scenario [1] and you'll get the same empty table.

[1] https://www.postgresql.org/message-id/fdaa003e-919c-cbc9-4f0c-e4546e96bd65%40gmail.com

Best regards,
Alexander

---

From: Dmitry Koval <[email-masked]>
Date: Fri, 12 Apr 2024 22:59:57 +0300

Thanks, Alexander!

> explicitly.

Attached patches with fix.

-- 
With best regards,
Dmitry Koval

From 2b68bbdb068e881e8ca6e34dec735f7ce656374f Mon Sep 17 00:00:00 2001
From: Koval Dmitry <[email-masked]>
Date: Wed, 10 Apr 2024 18:54:05 +0300
Subject: [PATCH v5 1/2] Fix for SPLIT/MERGE partitions of temporary table

---
 src/backend/commands/tablecmds.c              |  32 +++--
 src/backend/parser/parse_utilcmd.c            |  36 ++++-
 src/test/regress/expected/partition_merge.out | 124 +++++++++++++++++-
 src/test/regress/expected/partition_split.out |  29 ++++
 src/test/regress/sql/partition_merge.sql      | 101 +++++++++++++-
 src/test/regress/sql/partition_split.sql      |  23 ++++
 6 files changed, 324 insertions(+), 21 deletions(-)

 

-	/*
index ceba069905..997f9bedf2 100644
@@ -3415,13 +3415,12 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
 	table_close(rel, NoLock);
 }
 
-
 /*
- * checkPartition
+ * checkOldPartition
  *		Check that partRelOid is an oid of partition of the parent table rel
  */
 static void
-checkPartition(Relation rel, Oid partRelOid)
+checkOldPartition(Relation rel, Oid partRelOid)
 {
 	Relation	partRel;
 
@@ -3430,7 +3429,7 @@ checkPartition(Relation rel, Oid partRelOid)
 
 	if (!partRel->rd_rel->relispartition)
@@ -3449,6 +3448,26 @@ checkPartition(Relation rel, Oid partRelOid)
 	relation_close(partRel, AccessShareLock);
 }
 
+/*
+ * checkNewPartition
+ *		Check that the new partition has the correct namespace.
+ */
+static void
+checkNewPartition(Relation rel, RangeVar *partName)
+{
+	Oid			nspid;
+
+	nspid = RangeVarGetCreationNamespace(partName);
+
+	/* If the parent table is permanent, so must be all of its partitions. */
+	if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
+		isTempOrTempToastNamespace(nspid))
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
+						RelationGetRelationName(rel))));
+}
+
 /*
  * transformPartitionCmdForSplit
  *		Analyze the ALTER TABLLE ... SPLIT PARTITION command
@@ -3477,11 +3496,13 @@ transformPartitionCmdForSplit(CreateStmtContext *cxt, PartitionCmd *partcmd)
 		transformPartitionCmd(cxt, sps->bound);
 		/* Assign transformed value of the partition bound. */
 		sps->bound = cxt->partbound;
+		/* Check that the new partition has the correct namespace. */
+		checkNewPartition(parent, sps->name);
 	}

-	checkPartition(parent, splitPartOid);
+	checkOldPartition(parent, splitPartOid);
 
 	/* Then we should check partitions with transformed bounds. */
 	check_partitions_for_split(parent, splitPartOid, partcmd->name, partcmd->partlist, cxt->pstate);
@@ -3521,6 +3542,9 @@ transformPartitionCmdForMerge(CreateStmtContext *cxt, PartitionCmd *partcmd)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("partition of hash-partitioned table cannot be merged")));
 
+	/* Check that the new partition has the correct namespace. */
+	checkNewPartition(parent, partcmd->name);
+
 	/* Is current partition a DEFAULT partition? */
 	defaultPartOid = get_default_oid_from_partdesc(
 												   RelationGetPartitionDesc(parent, true));
@@ -3546,7 +3570,7 @@ transformPartitionCmdForMerge(CreateStmtContext *cxt, PartitionCmd *partcmd)
 		if (partOid == defaultPartOid)
 			isDefaultPart = true;
 
-		checkPartition(parent, partOid);
+		checkOldPartition(parent, partOid);
 
index 60eacf6bf3..2d6a8474e7 100644
@@ -746,4 +746,124 @@ DROP TABLE t3;
+-- 1. Try creating a partition of the permanent relation in a temporary schema.
+-- 2. Try to create a partition in the pg_temp schema explicitly.
+-- 1.
+-- 2.
+ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO pg_temp.tp_0_2;
index 9afed70365..8a3c3f4a4b 100644
 
@@ -444,5 +444,104 @@ DROP TABLE t3;
 DROP TABLE t2;
 DROP TABLE t1;
 
+--
+-- 1. Try creating a partition of the permanent relation in a temporary schema.
+-- 2. Try to create a partition in the pg_temp schema explicitly.
+-- 1.
+-- 2.
+ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO pg_temp.tp_0_2;
 
+--

From c4dccd827e769bd0ba03c65634235a8537bf30f4 Mon Sep 17 00:00:00 2001
From: Koval Dmitry <[email-masked]>
Date: Wed, 10 Apr 2024 19:55:18 +0300
Subject: [PATCH v5 2/2] Fixes for english text

---

 /*
 
 	/*
 
 		if (tuple_map)
 		{
 
 		/*

 			if (tuple_map)
 			{
index 997f9bedf2..75362df50a 100644
@@ -3513,7 +3513,7 @@ transformPartitionCmdForSplit(CreateStmtContext *cxt, PartitionCmd *partcmd)
@@ -3561,7 +3561,7 @@ transformPartitionCmdForMerge(CreateStmtContext *cxt, PartitionCmd *partcmd)
 
@@ -3575,7 +3575,7 @@ transformPartitionCmdForMerge(CreateStmtContext *cxt, PartitionCmd *partcmd)
 		partOids = lappend_oid(partOids, partOid);
 	}

 
 						/*
 
 	/*
 
 	/*
 
 	/*
 
 		ereport(ERROR,
 
 				/*
 
 				/*

index 2d6a8474e7..cb50229b87 100644
 
 DROP TABLE sales_range;

 

 
 
index 8a3c3f4a4b..677d404647 100644

 

 

 

 

 

 

 

 

 

 

 

 
-- 
2.40.1.windows.1

---

From: Alexander Korotkov <[email-masked]>
Date: Sat, 13 Apr 2024 13:04:58 +0300

Hi, Dmitry!

> Attached patches with fix.

Please, find a my version of this fix attached.  I think we need to
check relpersistence in a similar way ATTACH PARTITION or CREATE TABLE
... PARTITION OF do.  I'm going to polish this a little bit more.

------
Regards,
Alexander Korotkov

---

From: Robert Haas <[email-masked]>
Date: Mon, 15 Apr 2024 10:30:57 -0400

This is not a phrasing that we use in any other error message. We
always just say "is not a table".

+ * Open the new partition and acquire exclusive lock on it.  This will

A minor nitpick is that this should probably say access exclusive
rather than exclusive. But the bigger thing that confuses me here is
that if we just created the partition, surely we must *already* hold
AccessExclusiveLoc on it. No?

---

From: Alexander Lakhin <[email-masked]>
Date: Mon, 15 Apr 2024 18:00:00 +0300

Hello Robert,

> always just say "is not a table".

Initially I was confused by that message, because of:
(Isn't a foreign table a table?)

And also:
CREATE TABLE t (i int) PARTITION BY RANGE (i);
CREATE TABLE tp_0_1 PARTITION OF t
   FOR VALUES FROM (0) TO (1);
CREATE TABLE t2 (i int) PARTITION BY RANGE (i);
ALTER TABLE t MERGE PARTITIONS (tp_0_1, t2) INTO tpn;
ERROR:  "t2" is not a table
(Isn't a partitioned table a table?)

And in fact, an ordinary table is not suitable for MERGE anyway:
CREATE TABLE t (i int) PARTITION BY RANGE (i);
CREATE TABLE tp_0_1 PARTITION OF t
   FOR VALUES FROM (0) TO (1);
CREATE TABLE t2 (i int);
ALTER TABLE t MERGE PARTITIONS (tp_0_1, t2) INTO tpn;
ERROR:  "t2" is not a partition

So I don't think that "an ordinary table" is a good (unambiguous) term
either.

Best regards,
Alexander

---

From: Dmitry Koval <[email-masked]>
Date: Mon, 15 Apr 2024 18:26:56 +0300

Hi!

> Please, find a my version of this fix attached.

Is it possible to make a small addition to the file v6-0001 ... .patch 
(see attachment)?

Most important:
1) Line 19:

(temporary table should use the same schema as the partition);

2) Lines 116-123:

+RESET search_path;
+
+-- Can't merge persistent partitions into a temporary partition
+ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO pg_temp.tp_0_2;
+
+SET search_path = pg_temp, public;

(Alexandr Lakhin's test for using of pg_temp schema explicitly).

The rest of the changes in v6_afterfix.diff are not very important and 
can be ignored.

-- 
With best regards,
Dmitry Koval

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index c6ce7b94d9..bce5e39b64 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -21130,6 +21130,7 @@ moveSplitTableRows(Relation rel, Relation splitRel, List *partlist, List *newPar
  *
  * Emulates command: CREATE [TEMP] TABLE <newPartName> (LIKE <modelRelName>
  * INCLUDING ALL EXCLUDING INDEXES EXCLUDING IDENTITY)
+ * Function locks created relation in AccessExclusiveLock mode and returns it.
  */
 static Relation
 createPartitionTable(Relation rel, RangeVar *newPartName, RangeVar *modelRelName,
@@ -21500,8 +21501,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	{
 		/* Create partition table with generated temparary name. */
 		sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
-		mergePartName = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
-									 tmpRelName, -1);
+		mergePartName = makeRangeVar(cmd->name->schemaname, tmpRelName, -1);
 	}
 
 	newPartRel = createPartitionTable(rel,
diff --git a/src/test/regress/expected/partition_merge.out b/src/test/regress/expected/partition_merge.out
index b1d0b50b0b..0a4022f714 100644
--- a/src/test/regress/expected/partition_merge.out
+++ b/src/test/regress/expected/partition_merge.out
@@ -793,23 +793,58 @@ SELECT indexname FROM pg_indexes WHERE tablename = 'tp_1_2';
 
 DROP TABLE t;
 --
--- Try creating a partition in the temporary schema.
+-- Try mixing permanent and temporary partitions.
 --
 SET search_path = public, pg_temp;
 CREATE TABLE t (i int) PARTITION BY RANGE (i);
 CREATE TABLE tp_0_1 PARTITION OF t FOR VALUES FROM (0) TO (1);
 CREATE TABLE tp_1_2 PARTITION OF t FOR VALUES FROM (1) TO (2);
+SELECT c.oid::pg_catalog.regclass, c.relpersistence FROM pg_catalog.pg_class c WHERE c.oid = 't'::regclass;
+ oid | relpersistence 
+-----+----------------
+ t   | p
+(1 row)
+
+SELECT c.oid::pg_catalog.regclass, pg_catalog.pg_get_expr(c.relpartbound, c.oid), c.relpersistence
+  FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
+  WHERE c.oid = i.inhrelid AND i.inhparent = 't'::regclass
+  ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT', c.oid::pg_catalog.regclass::pg_catalog.text;
+  oid   |        pg_get_expr         | relpersistence 
+--------+----------------------------+----------------
+ tp_0_1 | FOR VALUES FROM (0) TO (1) | p
+ tp_1_2 | FOR VALUES FROM (1) TO (2) | p
+(2 rows)
+
 SET search_path = pg_temp, public;
 -- Can't merge persistent partitions into a temporary partition
 ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
 ERROR:  cannot create a temporary relation as partition of permanent relation "t"
+RESET search_path;
+-- Can't merge persistent partitions into a temporary partition
+ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO pg_temp.tp_0_2;
+ERROR:  cannot create a temporary relation as partition of permanent relation "t"
 DROP TABLE t;
-DROP TAble tp_0_2;
-ERROR:  table "tp_0_2" does not exist
+SET search_path = pg_temp, public;
 BEGIN;
 CREATE TABLE t (i int) PARTITION BY RANGE (i);
 CREATE TABLE tp_0_1 PARTITION OF t FOR VALUES FROM (0) TO (1);
 CREATE TABLE tp_1_2 PARTITION OF t FOR VALUES FROM (1) TO (2);
+SELECT c.oid::pg_catalog.regclass, c.relpersistence FROM pg_catalog.pg_class c WHERE c.oid = 't'::regclass;
+ oid | relpersistence 
+-----+----------------
+ t   | t
+(1 row)
+
+SELECT c.oid::pg_catalog.regclass, pg_catalog.pg_get_expr(c.relpartbound, c.oid), c.relpersistence
+  FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
+  WHERE c.oid = i.inhrelid AND i.inhparent = 't'::regclass
+  ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT', c.oid::pg_catalog.regclass::pg_catalog.text;
+  oid   |        pg_get_expr         | relpersistence 
+--------+----------------------------+----------------
+ tp_0_1 | FOR VALUES FROM (0) TO (1) | t
+ tp_1_2 | FOR VALUES FROM (1) TO (2) | t
+(2 rows)
+
 SET search_path = public, pg_temp;
 -- Can't merge temporary partitions into a persistent partition
 ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
diff --git a/src/test/regress/sql/partition_merge.sql b/src/test/regress/sql/partition_merge.sql
index 2d55e0f5a3..56adcf4bfc 100644
--- a/src/test/regress/sql/partition_merge.sql
+++ b/src/test/regress/sql/partition_merge.sql
@@ -484,25 +484,43 @@ SELECT indexname FROM pg_indexes WHERE tablename = 'tp_1_2';
 DROP TABLE t;
 
 --
--- Try creating a partition in the temporary schema.
+-- Try mixing permanent and temporary partitions.
 --
 SET search_path = public, pg_temp;
 CREATE TABLE t (i int) PARTITION BY RANGE (i);
 CREATE TABLE tp_0_1 PARTITION OF t FOR VALUES FROM (0) TO (1);
 CREATE TABLE tp_1_2 PARTITION OF t FOR VALUES FROM (1) TO (2);

 -- Can't merge persistent partitions into a temporary partition
 ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
+
+RESET search_path;
+
+-- Can't merge persistent partitions into a temporary partition
+ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO pg_temp.tp_0_2;
 DROP TABLE t;
-DROP TAble tp_0_2;
+
+SET search_path = pg_temp, public;
 
 BEGIN;
 
 SET search_path = public, pg_temp;
 
 -- Can't merge temporary partitions into a persistent partition

---

From: Robert Haas <[email-masked]>
Date: Mon, 15 Apr 2024 11:38:04 -0400

> (Isn't a foreign table a table?)

I agree that this can be confusing, but a patch that is about adding
SPLIT and MERGE PARTITION operations cannot decide to also invent a
new error message phraseology and use it only in one place. We need to
maintain consistency across the whole code base.

---

From: Alexander Korotkov <[email-masked]>
Date: Thu, 18 Apr 2024 13:35:41 +0300

Hi, Dmitry!

> The rest of the changes in v6_afterfix.diff are not very important and

Thank you.  I've integrated your changes.

The revised patchset is attached.
1) I've split the fix for the CommandCounterIncrement() issue and the
fix for relation persistence issue into a separate patch.
2) I've validated that the lock on the new partition is held in
createPartitionTable() after ProcessUtility() as pointed out by
Robert.  So, no need to place the lock again.
3) Added fix for problematic error message as a separate patch [1].
4) Added rename "salemans" => "salesmen" for tests as a separate patch.

I think these fixes are reaching committable shape, but I'd like
someone to check it before I push.

Links.
1. https://postgr.es/m/20240408.152402.1485994009160660141.horikyota.ntt%40gmail.com

------
Regards,
Alexander Korotkov

---

From: Alexander Lakhin <[email-masked]>
Date: Thu, 18 Apr 2024 19:00:00 +0300

Hi Alexander,

> someone to check it before I push.

I think the feature implementation should also provide tab completion for
SPLIT/MERGE.
(ALTER TABLE t S<Tab>
fills in only SET now.)

Also, the following MERGE operation:
CREATE TABLE t (i int, PRIMARY KEY(i)) PARTITION BY RANGE (i);
CREATE TABLE tp_0 PARTITION OF t FOR VALUES FROM (0) TO (1);
CREATE TABLE tp_1 PARTITION OF t FOR VALUES FROM (1) TO (2);
ALTER TABLE t MERGE PARTITIONS (tp_0, tp_1) INTO tp_0;

leaves a strange constraint:
\d+ t*
                                           Table "public.tp_0"
...
Not-null constraints:
     "merge-16385-26BCB0-tmp_i_not_null" NOT NULL "i"

Best regards,
Alexander

---

From: Dagfinn Ilmari Mannsåker <[email-masked]>
Date: Thu, 18 Apr 2024 18:03:21 +0100

> I think the feature implementation should also provide tab completion for

Here's a patch for that.  One thing I noticed while testing it was that
the tab completeion for partitions (Query_for_partition_of_table) shows
all the schemas in the DB, even ones that don't contain any partitions
of the table being altered.

- ilmari

>From 26db03b7a7675aa7dbff1f18ee084296caa1e181 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dagfinn=20Ilmari=20Manns=C3=A5ker?= <[email-masked]>
Date: Thu, 18 Apr 2024 17:47:22 +0100
Subject: [PATCH] =?UTF-8?q?Add=20tab=20completion=20for=20ALTER=20TABLE=20?=
 =?UTF-8?q?=E2=80=A6=20SPLIT|MERGE=20PARTITION(S)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/bin/psql/tab-complete.c | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 6fee3160f0..97cd5d9f62 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -2353,6 +2353,7 @@ psql_completion(const char *text, int start, int end)
 					  "OWNER TO", "SET", "VALIDATE CONSTRAINT",
 					  "REPLICA IDENTITY", "ATTACH PARTITION",
 					  "DETACH PARTITION", "FORCE ROW LEVEL SECURITY",
+					  "SPLIT PARTITION", "MERGE PARTITIONS (",
 					  "OF", "NOT OF");
 	/* ALTER TABLE xxx ADD */
 	else if (Matches("ALTER", "TABLE", MatchAny, "ADD"))
@@ -2609,10 +2610,10 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH("FROM (", "IN (", "WITH (");
 
 	/*
-	 * If we have ALTER TABLE <foo> DETACH PARTITION, provide a list of
+	 * If we have ALTER TABLE <foo> DETACH|SPLIT PARTITION, provide a list of
 	 * partitions of <foo>.
 	 */
-	else if (Matches("ALTER", "TABLE", MatchAny, "DETACH", "PARTITION"))
+	else if (Matches("ALTER", "TABLE", MatchAny, "DETACH|SPLIT", "PARTITION"))
 	{
 		set_completion_reference(prev3_wd);
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_partition_of_table);
@@ -2620,6 +2621,19 @@ psql_completion(const char *text, int start, int end)
 	else if (Matches("ALTER", "TABLE", MatchAny, "DETACH", "PARTITION", MatchAny))
 		COMPLETE_WITH("CONCURRENTLY", "FINALIZE");
 
+	/* ALTER TABLE <name> SPLIT PARTITION <name> */
+	else if (Matches("ALTER", "TABLE", MatchAny, "SPLIT", "PARTITION", MatchAny))
+		COMPLETE_WITH("INTO ( PARTITION");
+
+	/* ALTER TABLE <name> MERGE PARTITIONS ( */
+	else if (Matches("ALTER", "TABLE", MatchAny, "MERGE", "PARTITIONS", "("))
+	{
+		set_completion_reference(prev4_wd);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_partition_of_table);
+	}
+	else if (Matches("ALTER", "TABLE", MatchAny, "MERGE", "PARTITIONS", "(*)"))
+		COMPLETE_WITH("INTO");
+
 	/* ALTER TABLE <name> OF */
 	else if (Matches("ALTER", "TABLE", MatchAny, "OF"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_composite_datatypes);
-- 
2.39.2

---

From: Robert Haas <[email-masked]>
Date: Thu, 18 Apr 2024 15:59:01 -0400

> someone to check it before I push.

Reviewing 0001:

- Seems mostly fine. I think the comment /* Unlock and drop merged
partitions. */ is wrong. I think it should say something like /* Drop
the current partitions before adding the new one. */ because (a) it
doesn't unlock anything, and there's another comment saying that and
(b) we now know that the drop vs. add order matters.

Reviewing 0002:

- Commit message typos: behavious, corresponsing

- Given the change to the header comment of createPartitionTable, it's
rather surprising to me that this patch doesn't touch the
documentation. Isn't that a big change in semantics?

- My previous review comment was really about the code comment, I
believe, rather than the use of AccessExclusiveLock. NoLock is
probably fine, but if it were me I'd be tempted to write
AccessExclusiveLock and just make the comment say something like /* We
should already have the lock, but do it this way just to be certain
*/. But what you have is probably fine, too. Mostly, I want to clarify
the intent of my previous comment.

- Do we, or can we, have a test that if you split a partition that's
not in the search path, the resulting partitions end up in your
creation namespace? And similarly for merge? And maybe also that
schema-qualification works properly?

I haven't exhaustively verified the patch, but these are some things I
noticed when scrolling through it.

Reviewing 0003:

- Are you sure this can't dereference datum when datum is NULL, in
either the upper or lower half? It sure looks strange to have code
that looks like it can make datum a null pointer, and then an
unconditional deference just after.

- In general I think the wording changes are improvements. I'm
slightly suspicious that there might be an even better way to word it,
but I can't think of it right at this very moment.

- I'm kind of unhappy (but not totally unhappy) with the semantics.
Suppose I have a partition that allows values from 0 to 1000, but
actually only contains values that are either between 0 and 99 or
between 901 and 1000. If I try to to split the partition into one that
allows 0..100 and a second that allows 900..1000, it will fail. Maybe
that's good, because that means that if a failure is going to happen,
it will happen right at the beginning, rather than maybe after doing a
lot of work. But on the other hand, it also kind of stinks, because it
feels like I'm being told I can't do something that I know is
perfectly fine.

Reviewing 0004:

- Obviously this is quite trivial and there's no real problem with it,
but if we're changing it anyway, how about a gender-neutral term
(salesperson/salespeople)?

---

From: Dmitry Koval <[email-masked]>
Date: Fri, 19 Apr 2024 02:26:07 +0300

Hi!

>      "merge-16385-26BCB0-tmp_i_not_null" NOT NULL "i"

Thanks!
Attached fix (with test) for this case.
The patch should be applied after patches
v6-0001- ... .patch ... v6-0004- ... .patch

-- 
With best regards,
Dmitry Koval

From 58e4b7fb1d3b15cdf1c742c28690392dda34915d Mon Sep 17 00:00:00 2001
From: Koval Dmitry <[email-masked]>
Date: Fri, 19 Apr 2024 01:57:49 +0300
Subject: [PATCH v6] Fix

---
 src/backend/commands/tablecmds.c              | 49 ++++++-------------
 src/test/regress/expected/partition_merge.out | 13 ++++-
 src/test/regress/sql/partition_merge.sql      |  8 ++-
 3 files changed, 33 insertions(+), 37 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 72874295cb..8985747180 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -21508,9 +21508,6 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	ListCell   *listptr;
 	List	   *mergingPartitionsList = NIL;
 	Oid			defaultPartOid;
-	char		tmpRelName[NAMEDATALEN];
-	RangeVar   *mergePartName = cmd->name;
-	bool		isSameName = false;
 
 	/*
 	 * Lock all merged partitions, check them and create list with partitions
@@ -21532,8 +21529,22 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		 * function transformPartitionCmdForMerge().
 		 */
 		if (equal(name, cmd->name))
+		{
 			/* One new partition can have the same name as merged partition. */
-			isSameName = true;
+			char		tmpRelName[NAMEDATALEN];
+
+			/* Generate temporary name. */
+			sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
+
+			/* Rename partition. */
+			RenameRelationInternal(RelationGetRelid(mergingPartition),
+								   tmpRelName, false, false);
+			/*
+			 * We must bump the command counter to make the new partition tuple
+			 * visible for rename.
+			 */
+			CommandCounterIncrement();
+		}
 
 		/* Store a next merging partition into the list. */
 		mergingPartitionsList = lappend(mergingPartitionsList,
@@ -21553,16 +21564,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		DetachPartitionFinalize(rel, mergingPartition, false, defaultPartOid);
 	}
 
-	/* Create table for new partition, use partitioned table as model. */
-	if (isSameName)
-	{
-		/* Create partition table with generated temporary name. */
-		sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
-		mergePartName = makeRangeVar(cmd->name->schemaname, tmpRelName, -1);
-	}
-
 	newPartRel = createPartitionTable(rel,
-									  mergePartName,
+									  cmd->name,
 									  makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
 												   RelationGetRelationName(rel), -1),
 									  context);
@@ -21588,26 +21591,6 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	}
 	list_free(mergingPartitionsList);
 
-	/* Rename new partition if it is needed. */
-	if (isSameName)
-	{
-		/*
-		 * We must bump the command counter to make the new partition tuple
-		 * visible for rename.
-		 */
-		CommandCounterIncrement();
-
-		/* Rename partition. */
-		RenameRelationInternal(RelationGetRelid(newPartRel),
-							   cmd->name->relname, false, false);
-
-		/*
-		 * Bump the command counter to make the tuple of renamed partition
-		 * visible for attach partition operation.
-		 */
-		CommandCounterIncrement();
-	}
-
 	/*
 	 * Attach a new partition to the partitioned table. wqueue = NULL:
 	 * verification for each cloned constraint is not needed.
diff --git a/src/test/regress/expected/partition_merge.out b/src/test/regress/expected/partition_merge.out
index b5744ed498..2499a314fb 100644
--- a/src/test/regress/expected/partition_merge.out
+++ b/src/test/regress/expected/partition_merge.out
@@ -779,17 +779,26 @@ DROP TABLE t;
 -- Check the partition index name if the partition name is the same as one
 -- of the merged partitions.
 --
-CREATE TABLE t (i int) PARTITION BY RANGE (i);
+CREATE TABLE t (i int, PRIMARY KEY(i)) PARTITION BY RANGE (i);
 CREATE TABLE tp_0_1 PARTITION OF t FOR VALUES FROM (0) TO (1);
 CREATE TABLE tp_1_2 PARTITION OF t FOR VALUES FROM (1) TO (2);
 CREATE INDEX tidx ON t(i);
 ALTER TABLE t MERGE PARTITIONS (tp_1_2, tp_0_1) INTO tp_1_2;
--- Indexname value should be 'tp_1_2_i_idx'.
+-- Indexname values should be 'tp_1_2_pkey' and 'tp_1_2_i_idx'.
 SELECT indexname FROM pg_indexes
 WHERE tablename = 'tp_1_2' AND schemaname = 'partitions_merge_schema';
   indexname   
 --------------
+ tp_1_2_pkey
  tp_1_2_i_idx
+(2 rows)
+
+-- Conname value should be 'tp_1_2_i_not_null'.
+SELECT conname FROM pg_constraint
+WHERE conrelid='tp_1_2'::regclass AND contype='n';
+      conname      
+-------------------
+ tp_1_2_i_not_null
 (1 row)
 
 DROP TABLE t;
diff --git a/src/test/regress/sql/partition_merge.sql b/src/test/regress/sql/partition_merge.sql
index df737e214e..6614512f69 100644
--- a/src/test/regress/sql/partition_merge.sql
+++ b/src/test/regress/sql/partition_merge.sql
@@ -470,7 +470,7 @@ DROP TABLE t;
 -- Check the partition index name if the partition name is the same as one
 -- of the merged partitions.
 --
-CREATE TABLE t (i int) PARTITION BY RANGE (i);
+CREATE TABLE t (i int, PRIMARY KEY(i)) PARTITION BY RANGE (i);
 
@@ -478,10 +478,14 @@ CREATE TABLE tp_1_2 PARTITION OF t FOR VALUES FROM (1) TO (2);
 
--- Indexname value should be 'tp_1_2_i_idx'.
+-- Indexname values should be 'tp_1_2_pkey' and 'tp_1_2_i_idx'.
 SELECT indexname FROM pg_indexes
 WHERE tablename = 'tp_1_2' AND schemaname = 'partitions_merge_schema';
 
+-- Conname value should be 'tp_1_2_i_not_null'.
+SELECT conname FROM pg_constraint
+WHERE conrelid='tp_1_2'::regclass AND contype='n';
+
 DROP TABLE t;
 
 --
-- 
2.40.1.windows.1

---

From: Justin Pryzby <[email-masked]>
Date: Fri, 19 Apr 2024 06:34:46 -0500

> you don't, then the search_path should determine where they get

+1.  Alexander Lakhin reported an issue with schemas and SPLIT, and I
noticed an issue with schemas with MERGE.  The issue I hit is occurs
when MERGE'ing into a partition with the same name, and it's fixed like
so:

--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -21526,8 +21526,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	{
 		/* Create partition table with generated temporary name. */
 		sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
-		mergePartName = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
-									 tmpRelName, -1);
+		mergePartName = makeRangeVar(mergePartName->schemaname, tmpRelName, -1);
 	}
 	createPartitionTable(mergePartName,
 						 makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),

> table that is not a partition.

Right.  The current feature is useful and will probably work for 90% of
people's partitioned tables.

Currently, CREATE TABLE .. PARTITION OF does not create stats objects on
the child table, but MERGE PARTITIONS does, which seems strange.
Maybe stats should not be included on the new child ?

Note that stats on parent table are not analagous to indexes -
partitioned indexes do nothing other than cause indexes to be created on
any new/attached partitions.  But stats objects on the parent 1) cause
extended stats to be collected and computed across the whole partition
heirarchy, and 2) do not cause stats to be computed for the individual
partitions.

Partitions can have different column definitions, for example null
constraints, FKs, defaults.  And currently, if you MERGE partitions,
those will all be lost (or rather, replaced by whatever LIKE parent
gives).  I think that's totally fine - anyone using different defaults
on child tables could either not use MERGE PARTITIONS, or fix up the
defaults afterwards.  There's not much confusion that the details of the
differences between individual partitions will be lost when the
individual partitions are merged and no longer exist.
But I think it'd be useful to document how the new partitions will be
constructed.

-- 
Justin

---

From: Alexander Korotkov <[email-masked]>
Date: Fri, 19 Apr 2024 16:29:44 +0300

> v6-0001- ... .patch ... v6-0004- ... .patch

I've incorporated this fix with 0001 patch.

Also added to the patchset
005 – tab completion by Dagfinn [1]
006 – draft fix for table AM issue spotted by Alexander Lakhin [2]
007 – doc review by Justin [3]

I'm continuing work on this.

Links
1. https://www.postgresql.org/message-id/87plumiox2.fsf%40wibble.ilmari.org
2. https://www.postgresql.org/message-id/84ada05b-be5c-473e-6d1c-ebe5dd21b190%40gmail.com
3. https://www.postgresql.org/message-id/ZiGH0xc1lxJ71ZfB%40pryzbyj2023

------
Regards,
Alexander Korotkov

---

From: Alexander Korotkov <[email-masked]>
Date: Mon, 22 Apr 2024 13:31:48 +0300

Hi!

> 3. https://www.postgresql.org/message-id/ZiGH0xc1lxJ71ZfB%40pryzbyj2023

0001
The way we handle name collisions during MERGE PARTITIONS operation is
reworked by integration of patch [3].  This makes note about commit in
[2] not relevant.

0002
The persistence of the new partition is copied as suggested in [1].
But the checks are in-place, because search_path could influence new
table persistence.  Per review [2], commit message typos are fixed,
documentation is revised, revised tests to cover schema-qualification,
usage of search_path.

0003
Making code more clear that we're not going to dereference the NULL
datum per note in [2].

0004
Gender-neutral terms are used per suggestions in [2].

0005
Commit message revised

0006
Revise documentation mentioning we're going to copy the parent's table
AM.  Regression tests are added.  Commit message revised.

0007
Commit message revised

Links
1. https://www.postgresql.org/message-id/CA%2BTgmoYcjL%2Bw2BQzku5iNXKR5fyxJMSP3avQta8xngioTX7D7A%40mail.gmail.com
2. https://www.postgresql.org/message-id/CA%2BTgmoY_4r6BeeSCTim04nAiCmmXg-1pG1toxQovZOP2qaFJ0A%40mail.gmail.com
3. https://www.postgresql.org/message-id/f8b5cbf5-965e-4e5b-b506-33bbf41b0d50%40postgrespro.ru

------
Regards,
Alexander Korotkov

---

From: Justin Pryzby <[email-masked]>
Date: Wed, 24 Apr 2024 15:26:47 -0500

> reworked by integration of patch [3].  This makes note about commit in

This patch also/already fixes the schema issue I reported.  Thanks.

If you wanted to include a test case for that:

begin;
CREATE SCHEMA s;
CREATE SCHEMA t;
CREATE TABLE p(i int) PARTITION BY RANGE(i);
CREATE TABLE s.c1 PARTITION OF p FOR VALUES FROM (1)TO(2);
CREATE TABLE s.c2 PARTITION OF p FOR VALUES FROM (2)TO(3);
ALTER TABLE p MERGE PARTITIONS (s.c1, s.c2) INTO s.c1; -- misbehaves if merging into the same name as an existing partition
\d+ p
...
Partitions: c1 FOR VALUES FROM (1) TO (3)

> documentation is revised, revised tests to cover schema-qualification,

Subject: [PATCH v8 2/7] Make new partitions with parent's persistence during MERGE/SPLIT operations

This patch adds documentation saying:
+      Any indexes, constraints and user-defined row-level triggers that exist
+      in the parent table are cloned on new partitions [...]

Which is good to say, and addresses part of my message [0]
[0] ZiJW1g2nbQs9ekwK@pryzbyj2023

But it doesn't have anything to do with "creating new partitions with
parent's persistence".  Maybe there was a merge conflict and the docs
ended up in the wrong patch ?

Also, defaults, storage options, compression are also copied.  As will
be anything else from LIKE.  And since anything added in the future will
also be copied, maybe it's better to just say that the tables will be
created the same way as "LIKE .. INCLUDING ALL EXCLUDING ..", or
similar.  Otherwise, the next person who adds a new option for LIKE
would have to remember to update this paragraph...

Also, extended stats objects are currently cloned to new child tables.
But I suggested in [0] that they probably shouldn't be.

> 007 – doc review by Justin [3]

I suggest to drop this patch for now.  I'll send some more minor fixes to
docs and code comments once the other patches are settled.

-- 
Justin

---

From: Pavel Borisov <[email-masked]>
Date: Fri, 26 Apr 2024 17:33:33 +0400

Hi, Hackers!

On Thu, 25 Apr 2024 at 00:26, Justin Pryzby <[email-masked]> wrote:

> https://www.postgresql.org/message-id/87plumiox2.fsf%40wibble.ilmari.org
> > > 2.
> https://www.postgresql.org/message-id/84ada05b-be5c-473e-6d1c-ebe5dd21b190%40gmail.com
> > > 3.
> https://www.postgresql.org/message-id/ZiGH0xc1lxJ71ZfB%40pryzbyj2023
> >
> > 0001
> ALTER TABLE p MERGE PARTITIONS (s.c1, s.c2) INTO s.c1; -- misbehaves if
> merging into the same name as an existing partition
> \d+ p
> ...
I've looked at the patchset:

0001 Look good.
0002 Also right with docs modification proposed by Justin.
0003:
Looks like unused code
5268             datum = cmpval ? list_nth(spec->lowerdatums, abs(cmpval) -
1) : NULL;
overridden by
5278                     datum = list_nth(spec->upperdatums, abs(cmpval) -
1);
and
5290                     datum = list_nth(spec->upperdatums, abs(cmpval) -
1);

Otherwise - good.

0004:
I suggest also getting rid of thee-noun compound words like:
salesperson_name. Maybe salesperson -> clerk? Or maybe use the same terms
like in pgbench: branches, tellers, accounts, balance.

0005: Good
0006: Patch is right
In comments:
+      New partitions will have the same table access method,
+      same column names and types as the partitioned table to which they
belong.
(I'd suggest to remove second "same")

Tests are passed. I suppose that it's better to add similar tests for
SPLIT/MERGE PARTITION(S)  to those covering ATTACH/DETACH PARTITION (e.g.:
subscription/t/013_partition.pl and regression tests)

Overall, great work! Thanks!

Regards,
Pavel Borisov,
Supabase.

---

From: Alexander Korotkov <[email-masked]>
Date: Sun, 28 Apr 2024 03:59:37 +0300

Hi, Pavel.

Thank you for the review.

> 0002 Also right with docs modification proposed by Justin.

Modified as proposed by Justin.  The documentation for the way new
partitions are created is now in separate patch.

> 0003:
> Looks like unused code
> 5268             datum = cmpval ? list_nth(spec->lowerdatums, abs(cmpval) - 1) : NULL;
> overridden by
> 5278                     datum = list_nth(spec->upperdatums, abs(cmpval) - 1);
> and
> 5290                     datum = list_nth(spec->upperdatums, abs(cmpval) - 1);
>
> Otherwise - good.

Fixed, thanks.

> 0004:
> I suggest also getting rid of thee-noun compound words like: salesperson_name. Maybe salesperson -> clerk? Or maybe use the same terms like in pgbench: branches, tellers, accounts, balance.

Thank you, but I'd like to prefer keeping these modifications simple.
It's just regression tests, we don't need to have perfect naming here.
My intention is to fix just obvious errors.

> +      same column names and types as the partitioned table to which they belong.

Documentation is modified per proposal by Justin.  Thus double "same"
is already gone.

> Tests are passed. I suppose that it's better to add similar tests for SPLIT/MERGE PARTITION(S)  to those covering ATTACH/DETACH PARTITION (e.g.: subscription/t/013_partition.pl and regression tests)

The revised patchset is attached.  I'm going to push it if there are
no objections.

Thank you for your suggestions about adding tests similar to
subscription/t/013_partition.pl.  I will work on this after pushing
this patchset.

------
Regards,
Alexander Korotkov
Supabase

---

From: Alexander Korotkov <[email-masked]>
Date: Sun, 28 Apr 2024 04:04:54 +0300

Hi Justin,

Thank you for your review.  Please check v9 of the patchset [1].

> Partitions: c1 FOR VALUES FROM (1) TO (3)

There is already a test which checks merging into the same name as an
existing partition.  And there are tests with schema-qualified names.
I'm not yet convinced we need a test with both these properties
together.

> ended up in the wrong patch ?

Makes sense.  Extracted this into a separate patch in v10.

> would have to remember to update this paragraph...

Reworded that way.  Thank you.

> But I suggested in [0] that they probably shouldn't be.

I will explore this.  Do we copy extended stats when we do CREATE
TABLE ... PARTITION OF?  I think we need to do the same here.

> docs and code comments once the other patches are settled.

Your edits are welcome.  Dropped this for now.  And waiting for the
next revision from you.

Links.
1. https://www.postgresql.org/message-id/CAPpHfduYuYECrqpHMgcOsNr%2B4j3uJK%2BJPUJ_zDBn-tqjjh3p1Q%40mail.gmail.com

------
Regards,
Alexander Korotkov
Supabase

---

From: Alexander Lakhin <[email-masked]>
Date: Sun, 28 Apr 2024 14:00:00 +0300

Hello,

> The revised patchset is attached.  I'm going to push it if there are

I have one additional question regarding security, if you don't mind:
What permissions should a user have to perform split/merge?

When we deal with mixed ownership, say, bob is an owner of a
partitioned table, but not an owner of a partition, should we
allow him to perform merge with that partition?
Consider the following script:
CREATE ROLE alice;
GRANT CREATE ON SCHEMA public TO alice;

SET SESSION AUTHORIZATION alice;
CREATE TABLE t (i int PRIMARY KEY, t text, u text) PARTITION BY RANGE (i);
CREATE TABLE tp_00 PARTITION OF t FOR VALUES FROM (0) TO (10);
CREATE TABLE tp_10 PARTITION OF t FOR VALUES FROM (10) TO (20);

CREATE POLICY p1 ON tp_00 USING (u = current_user);
ALTER TABLE tp_00 ENABLE ROW LEVEL SECURITY;

INSERT INTO t(i, t, u)  VALUES (0, 'info for bob', 'bob');
INSERT INTO t(i, t, u)  VALUES (1, 'info for alice', 'alice');
RESET SESSION AUTHORIZATION;

CREATE ROLE bob;
GRANT CREATE ON SCHEMA public TO bob;
ALTER TABLE t OWNER TO bob;
GRANT SELECT ON TABLE tp_00 TO bob;

SET SESSION AUTHORIZATION bob;
SELECT * FROM tp_00;
--- here bob can see his info only
\d
  Schema | Name  |       Type        | Owner
--------+-------+-------------------+-------
  public | t     | partitioned table | bob
  public | tp_00 | table             | alice
  public | tp_10 | table             | alice

-- but then bob can do:
ALTER TABLE t MERGE PARTITIONS (tp_00, tp_10) INTO tp_00;
-- (yes, he also can detach the partition tp_00, but then he couldn't
-- re-attach nor read it)

\d
  Schema | Name  |       Type        | Owner
--------+-------+-------------------+-------
  public | t     | partitioned table | bob
  public | tp_00 | table             | bob

Thus bob effectively have captured the partition with the data.

What do you think, does this create a new security risk?

Best regards,
Alexander

---

From: Alexander Korotkov <[email-masked]>
Date: Sun, 28 Apr 2024 14:36:51 +0300

>   Schema | Name  |       Type        | Owner

Alexander, thank you for discovering this.  I believe that the one who
merges partitions should have permissions for all the partitions
merged.  I'll recheck this and provide the patch.

------
Regards,
Alexander Korotkov

---

From: Justin Pryzby <[email-masked]>
Date: Sun, 28 Apr 2024 08:18:42 -0500

> I'm not yet convinced we need a test with both these properties

I mentioned that the combination of schemas and merge-into-same-name is
what currently doesn't work right.

> TABLE ... PARTITION OF?  I think we need to do the same here.

Right, they're not copied because an extended stats objs on the parent
does something different than putting stats objects on each child.
I've convinced myself that it's wrong to copy the parent's stats obj.
If someone wants stats objects on each child, they'll have to handle
them specially after MERGE/SPLIT, just as they would for per-child
defaults/constraints/etc.

> Makes sense.  Extracted this into a separate patch in v10.

I adjusted the language some and fixed a typo in the commit message.

s/parition/partition/

-- 
Justin
>From e00033fc4b8254c70bf8a3d41d513edd9540e2d7 Mon Sep 17 00:00:00 2001
From: Alexander Korotkov <[email-masked]>
Date: Sun, 28 Apr 2024 03:39:30 +0300
Subject: [PATCH] Document the way partition MERGE/SPLIT operations create new
 partitions

Reported-by: Justin Pryzby
Discussion: https://postgr.es/m/ZilrByTp-pbz6Mvf%40pryzbyj2023
---
 doc/src/sgml/ref/alter_table.sgml | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index fe36ff82e52..fc2dfffe49f 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -1153,6 +1153,12 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
       splitting we have a partition with the same name).
       Only simple, non-partitioned partition can be split.
      </para>
+     <para>
+      The new partitions will be created the same as tables created with the
+      SQL command <literal>CREATE TABLE <replaceable class="parameter">partition_nameN</replaceable> (LIKE <replaceable class="parameter">name</replaceable> INCLUDING ALL EXCLUDING INDEXES EXCLUDING IDENTITY)</literal>.
+      The indexes and identity are created later, after moving the data
+      into the new partitions.
+     </para>
      <note>
       <para>
        This command acquires an <literal>ACCESS EXCLUSIVE</literal> lock.
@@ -1213,6 +1219,12 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
       can have the same name as one of the merged partitions.  Only simple,
       non-partitioned partitions can be merged.
      </para>
+     <para>
+      The new partition will be created the same as a table created with the
+      SQL command <literal>CREATE TABLE <replaceable class="parameter">partition_name</replaceable> (LIKE <replaceable class="parameter">name</replaceable> INCLUDING ALL EXCLUDING INDEXES EXCLUDING IDENTITY)</literal>.
+      The indexes and identity are created later, after moving the data
+      into the new partition.
+     </para>
      <note>
       <para>
        This command acquires an <literal>ACCESS EXCLUSIVE</literal> lock.
-- 
2.42.0

---

From: "David G. Johnston" <[email-masked]>
Date: Sun, 28 Apr 2024 07:09:09 -0700

> allow him to perform merge with that partition?
Attaching via alter table requires the user to own both the partitioned
table and the table being acted upon.  Merge needs to behave similarly.

The fact that we let the superuser break the requirement of common
ownership is unfortunate but I guess understandable.  But given the
existing behavior of attach merge should likewise fail if it find the user
doesn’t own the partitions being merged.  The fact that the user can select
from those tables can be acted upon manually if desired; these
administrative commands should all ensure common ownership and fail if that
precondition is not met.

David J.

---

From: Justin Pryzby <[email-masked]>
Date: Sun, 28 Apr 2024 09:54:16 -0500

> defaults/constraints/etc.

I dug up this thread, in which the idea of copying extended stats from
parent to child was considered some 6 years ago, but never implemented;
for consistency, MERGE/SPLIT shouldn't copy extended stats, either.

https://www.postgresql.org/message-id/20180305195750.aecbpihhcvuskzba%40alvherre.pgsql

-- 
Justin

---

From: Alexander Lakhin <[email-masked]>
Date: Mon, 29 Apr 2024 21:00:01 +0300

Hi Dmitry,

> v6-0001- ... .patch ... v6-0004- ... .patch

I still wonder, why that constraint (now with a less questionable name) is
created during MERGE?

That is, before MERGE, two partitions have only PRIMARY KEY indexes,
with no not-null constraint, and you can manually remove the constraint
after MERGE, so maybe it's not necessary...

Best regards,
Alexander

---

From: Dmitry Koval <[email-masked]>
Date: Tue, 30 Apr 2024 03:10:47 +0300

Hi!

1.
> I still wonder, why that constraint (now with a less questionable name) is

The SPLIT/MERGE PARTITION(S) commands for creating partitions reuse the 
existing code of CREATE TABLE .. LIKE ... command. A new partition was 
created with the name "merge-16385-26BCB0-tmp" (since there was an old 
partition with the same name). The constraint 
"merge-16385-26BCB0-tmp_i_not_null" was created too together with the 
partition. Subsequently, the table was renamed, but the constraint was not.
Now a new partition is immediately created with the correct name (the 
old partition is renamed).

2.
Just in case, I am attaching a small fix v9_fix.diff for situation [1].

[1] 
https://www.postgresql.org/message-id/0520c72e-8d97-245e-53f9-173beca2ab2e%40gmail.com

-- 
With best regards,
Dmitry Koval

diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index fef084f5d5..e918a623c5 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3446,6 +3446,11 @@ checkPartition(Relation rel, Oid partRelOid)
 						RelationGetRelationName(partRel),
 						RelationGetRelationName(rel))));
 
+	/* Permissions checks */
+	if (!object_ownercheck(RelationRelationId, RelationGetRelid(partRel), GetUserId()))
+		aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(partRel->rd_rel->relkind),
+					   RelationGetRelationName(partRel));
+
 	relation_close(partRel, AccessShareLock);
 }

---

From: Alexander Lakhin <[email-masked]>
Date: Tue, 30 Apr 2024 06:00:00 +0300

30.04.2024 03:10, Dmitry Koval wrote:
> The SPLIT/MERGE PARTITION(S) commands for creating partitions reuse the existing code of CREATE TABLE .. LIKE ... 
> command. A new partition was created with the name "merge-16385-26BCB0-tmp" (since there was an old partition with the 
> same name). The constraint "merge-16385-26BCB0-tmp_i_not_null" was created too together with the partition. 
> Subsequently, the table was renamed, but the constraint was not.
> Now a new partition is immediately created with the correct name (the old partition is renamed).

Maybe I'm doing something wrong, but the following script:
CREATE TABLE t (i int, PRIMARY KEY(i)) PARTITION BY RANGE (i);
CREATE TABLE tp_0 PARTITION OF t FOR VALUES FROM (0) TO (1);
CREATE TABLE tp_1 PARTITION OF t FOR VALUES FROM (1) TO (2);

CREATE TABLE t2 (LIKE t INCLUDING ALL);
CREATE TABLE tp2 (LIKE tp_0 INCLUDING ALL);
creates tables t2, tp2 without not-null constraints.

But after
ALTER TABLE t MERGE PARTITIONS (tp_0, tp_1) INTO tp_0;
I see:
\d+ tp_0
...
Indexes:
     "tp_0_pkey" PRIMARY KEY, btree (i)
Not-null constraints:
     "tp_0_i_not_null" NOT NULL "i"

Best regards,
Alexander

---

From: Justin Pryzby <[email-masked]>
Date: Tue, 30 Apr 2024 15:15:05 -0500

> Did not find any relation named "s1.*"

Hi,

Is this issue already fixed ?

I wasn't able to reproduce it.  Maybe it only happened with earlier
patch versions applied ?

Thanks,
-- 
Justin

---

From: Dmitry Koval <[email-masked]>
Date: Wed, 1 May 2024 22:51:24 +0300

Hi!

> I wasn't able to reproduce it.  Maybe it only happened with earlier

I think this was fixed in commit [1].

[1] 
https://github.com/postgres/postgres/commit/fcf80c5d5f0f3787e70fca8fd029d2e08a923f91

-- 
With best regards,
Dmitry Koval

---

From: Justin Pryzby <[email-masked]>
Date: Fri, 3 May 2024 08:23:14 -0500

On Wed, May 01, 2024 at 10:51:24PM +0300, Dmitry Koval wrote:
> [1] https://github.com/postgres/postgres/commit/fcf80c5d5f0f3787e70fca8fd029d2e08a923f91

I tried to reproduce it at fcf80c5d5f~, but couldn't.  
I don't see how that patch would fix it anyway.
I'm hoping Alexander can confirm what happened.

The other remaining issues I'm aware of are for EXCLUDING STATISTICS and
refusing to ALTER if the owners don't match.

Note that the error that led to "EXCLUDING IDENTITY" is being discused
over here:
https://www.postgresql.org/message-id/[email-masked]

It's possible that once that's addressed, the exclusion should be
removed here, too.

-- 
Justin

---

From: Alexander Korotkov <[email-masked]>
Date: Fri, 3 May 2024 16:32:25 +0300

> I'm hoping Alexander can confirm what happened.

This problem is only relevant for an old version of fix [1], which
overrides schemas for new partitions.  That version was never
committed.

> refusing to ALTER if the owners don't match.

These two are in my list.  I'm planning to work on them in the next few days.

+1

Links.
1. https://www.postgresql.org/message-id/edfbd846-dcc1-42d1-ac26-715691b687d3%40postgrespro.ru

------
Regards,
Alexander Korotkov
Supabase

---

From: Alexander Korotkov <[email-masked]>
Date: Wed, 8 May 2024 21:00:10 +0300

> overrides schemas for new partitions.  That version was never

Here are the patches.
0001 Adds permission checks on the partitions before doing MERGE/SPLIT
0002 Skips copying extended statistics while creating new partitions
in MERGE/SPLIT

0001 looks quite simple and trivial for me.  I'm going to push it if
no objections.
For 0002 I'd like to hear some feedback on wordings used in docs and comments.

------
Regards,
Alexander Korotkov
Supabase

---

From: Justin Pryzby <[email-masked]>
Date: Sat, 25 May 2024 12:53:17 -0500

> > It's possible that once that's addressed, the exclusion should be

Can EXCLUDING IDENTITY be removed now ?

I wasn't able to find why it was needed - at one point, I think there
was a test case that threw an error, but now when I remove the EXCLUDE,
nothing goes wrong.

-- 
Justin

---

From: Alexander Korotkov <[email-masked]>
Date: Sun, 26 May 2024 06:56:33 +0300

> was a test case that threw an error, but now when I remove the EXCLUDE,

Yes, it was broken before [1][2], but now it seems to work.  At the
same time, I'm not sure if we need to remove the EXCLUDE now.
IDENTITY is anyway successfully created when the new partition gets
attached.

Links.
1. https://www.postgresql.org/message-id/[email-masked]
2. https://www.postgresql.org/message-id/flat/ZiGH0xc1lxJ71ZfB%40pryzbyj2023#297b6aef85cb089abb38e9b1a9a7ffff

------
Regards,
Alexander Korotkov
Supabase