summaryrefslogtreecommitdiff
path: root/src/backend/commands/tablecmds.c
diff options
context:
space:
mode:
authorAlexander Korotkov <akorotkov@postgresql.org>2024-04-30 12:00:15 +0300
committerAlexander Korotkov <akorotkov@postgresql.org>2024-04-30 12:00:15 +0300
commitfcf80c5d5f0f3787e70fca8fd029d2e08a923f91 (patch)
tree0461724903632322b18d43578b8994ba321d5810 /src/backend/commands/tablecmds.c
parent842c9b27057e8ecea02b816e3ec6c208779b3d39 (diff)
Make new partitions with parent's persistence during MERGE/SPLIT
The createPartitionTable() function is responsible for creating new partitions for ALTER TABLE ... MERGE PARTITIONS, and ALTER TABLE ... SPLIT PARTITION commands. It emulates the behaviour of CREATE TABLE ... (LIKE ...), where new table persistence should be specified by the user. In the table partitioning persistent of the partition and its parent must match. So, this commit makes createPartitionTable() copy the persistence of the parent partition. Also, this commit makes createPartitionTable() recheck the persistence after the new table creation. This is needed because persistence might be affected by pg_temp in search_path. This commit also changes the signature of createPartitionTable() making it take the parent's Relation itself instead of the name of the parent relation, and return the Relation of new partition. That doesn't lead to complications, because both callers have the parent table open and need to open the new partition. Reported-by: Alexander Lakhin Discussion: https://postgr.es/m/dbc8b96c-3cf0-d1ee-860d-0e491da20485%40gmail.com Author: Dmitry Koval Reviewed-by: Alexander Korotkov, Robert Haas, Justin Pryzby, Pavel Borisov
Diffstat (limited to 'src/backend/commands/tablecmds.c')
-rw-r--r--src/backend/commands/tablecmds.c75
1 files changed, 50 insertions, 25 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 8fa09afdc59..c312d9d1975 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -21209,18 +21209,30 @@ moveSplitTableRows(Relation rel, Relation splitRel, List *partlist, List *newPar
/*
* createPartitionTable: create table for a new partition with given name
- * (newPartName) like table (modelRelName)
+ * (newPartName) like table (modelRel)
*
- * Emulates command: CREATE TABLE <newPartName> (LIKE <modelRelName>
+ * Emulates command: CREATE [TEMP] TABLE <newPartName> (LIKE <modelRel's name>
* INCLUDING ALL EXCLUDING INDEXES EXCLUDING IDENTITY)
+ * Function returns the created relation (locked in AccessExclusiveLock mode).
*/
-static void
-createPartitionTable(RangeVar *newPartName, RangeVar *modelRelName,
+static Relation
+createPartitionTable(RangeVar *newPartName, Relation modelRel,
AlterTableUtilityContext *context)
{
CreateStmt *createStmt;
TableLikeClause *tlc;
PlannedStmt *wrapper;
+ Relation newRel;
+
+ /* If existing rel is temp, it must belong to this session */
+ if (modelRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
+ !modelRel->rd_islocaltemp)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot create as partition of temporary relation of another session")));
+
+ /* New partition should have the same persistence as modelRel */
+ newPartName->relpersistence = modelRel->rd_rel->relpersistence;
createStmt = makeNode(CreateStmt);
createStmt->relation = newPartName;
@@ -21233,7 +21245,8 @@ createPartitionTable(RangeVar *newPartName, RangeVar *modelRelName,
createStmt->if_not_exists = false;
tlc = makeNode(TableLikeClause);
- tlc->relation = modelRelName;
+ tlc->relation = makeRangeVar(get_namespace_name(RelationGetNamespace(modelRel)),
+ RelationGetRelationName(modelRel), -1);
/*
* Indexes will be inherited on "attach new partitions" stage, after data
@@ -21259,6 +21272,35 @@ createPartitionTable(RangeVar *newPartName, RangeVar *modelRelName,
NULL,
None_Receiver,
NULL);
+
+ /*
+ * Open the new partition with no lock, because we already have
+ * AccessExclusiveLock placed there after creation.
+ */
+ newRel = table_openrv(newPartName, NoLock);
+
+ /*
+ * We intended to create the partition with the same persistence as the
+ * parent table, but we still need to recheck because that might be
+ * affected by the search_path. If the parent is permanent, so must be
+ * all of its partitions.
+ */
+ if (modelRel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
+ newRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
+ RelationGetRelationName(modelRel))));
+
+ /* Permanent rels cannot be partitions belonging to temporary parent */
+ if (newRel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
+ modelRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
+ RelationGetRelationName(modelRel))));
+
+ return newRel;
}
/*
@@ -21278,7 +21320,6 @@ ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
char tmpRelName[NAMEDATALEN];
List *newPartRels = NIL;
ObjectAddress object;
- RangeVar *parentName;
Oid defaultPartOid;
defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
@@ -21350,18 +21391,12 @@ ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
}
/* Create new partitions (like split partition), without indexes. */
- parentName = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
- RelationGetRelationName(rel), -1);
foreach(listptr, cmd->partlist)
{
SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
Relation newPartRel;
- createPartitionTable(sps->name, parentName, context);
-
- /* Open the new partition and acquire exclusive lock on it. */
- newPartRel = table_openrv(sps->name, AccessExclusiveLock);
-
+ newPartRel = createPartitionTable(sps->name, rel, context);
newPartRels = lappend(newPartRels, newPartRel);
}
@@ -21565,18 +21600,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
DetachPartitionFinalize(rel, mergingPartition, false, defaultPartOid);
}
- createPartitionTable(cmd->name,
- makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
- RelationGetRelationName(rel), -1),
- context);
-
- /*
- * Open the new partition and acquire exclusive lock on it. This will
- * stop all the operations with partitioned table. This might seem
- * excessive, but this is the way we make sure nobody is planning queries
- * involving merging partitions.
- */
- newPartRel = table_openrv(cmd->name, AccessExclusiveLock);
+ /* Create table for new partition, use partitioned table as model. */
+ newPartRel = createPartitionTable(cmd->name, rel, context);
/* Copy data from merged partitions to new partition. */
moveMergedTablesRows(rel, mergingPartitionsList, newPartRel);