diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/commands/tablecmds.c | 819 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 61 | ||||
-rw-r--r-- | src/backend/parser/parse_utilcmd.c | 193 | ||||
-rw-r--r-- | src/backend/partitioning/partbounds.c | 901 | ||||
-rw-r--r-- | src/backend/tcop/utility.c | 6 | ||||
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 18 |
6 files changed, 30 insertions, 1968 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 52ce6b0c92a..dac39df83ac 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -657,11 +657,6 @@ static void ATDetachCheckNoForeignKeyRefs(Relation partition); static char GetAttributeCompression(Oid atttypid, const char *compression); static char GetAttributeStorage(Oid atttypid, const char *storagemode); -static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, - Relation rel, PartitionCmd *cmd, - AlterTableUtilityContext *context); -static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, - PartitionCmd *cmd, AlterTableUtilityContext *context); /* ---------------------------------------------------------------- * DefineRelation @@ -4672,14 +4667,6 @@ AlterTableGetLockLevel(List *cmds) cmd_lockmode = ShareUpdateExclusiveLock; break; - case AT_SplitPartition: - cmd_lockmode = AccessExclusiveLock; - break; - - case AT_MergePartitions: - cmd_lockmode = AccessExclusiveLock; - break; - case AT_CheckNotNull: /* @@ -5106,16 +5093,6 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, /* No command-specific prep needed */ pass = AT_PASS_MISC; break; - case AT_SplitPartition: - ATSimplePermissions(cmd->subtype, rel, ATT_TABLE); - /* No command-specific prep needed */ - pass = AT_PASS_MISC; - break; - case AT_MergePartitions: - ATSimplePermissions(cmd->subtype, rel, ATT_TABLE); - /* No command-specific prep needed */ - pass = AT_PASS_MISC; - break; default: /* oops */ elog(ERROR, "unrecognized alter table type: %d", (int) cmd->subtype); @@ -5512,22 +5489,6 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, case AT_DetachPartitionFinalize: address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name); break; - case AT_SplitPartition: - cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode, - cur_pass, context); - Assert(cmd != NULL); - Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE); - ATExecSplitPartition(wqueue, tab, rel, (PartitionCmd *) cmd->def, - context); - break; - case AT_MergePartitions: - cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode, - cur_pass, context); - Assert(cmd != NULL); - Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE); - ATExecMergePartitions(wqueue, tab, rel, (PartitionCmd *) cmd->def, - context); - break; default: /* oops */ elog(ERROR, "unrecognized alter table type: %d", (int) cmd->subtype); @@ -6516,10 +6477,6 @@ alter_table_type_to_string(AlterTableType cmdtype) return "DETACH PARTITION"; case AT_DetachPartitionFinalize: return "DETACH PARTITION ... FINALIZE"; - case AT_SplitPartition: - return "SPLIT PARTITION"; - case AT_MergePartitions: - return "MERGE PARTITIONS"; case AT_AddIdentity: return "ALTER COLUMN ... ADD IDENTITY"; case AT_SetIdentity: @@ -18329,37 +18286,6 @@ QueuePartitionConstraintValidation(List **wqueue, Relation scanrel, } /* - * attachPartitionTable: attach a new partition to the partitioned table - * - * wqueue: the ALTER TABLE work queue; can be NULL when not running as part - * of an ALTER TABLE sequence. - * rel: partitioned relation; - * attachrel: relation of attached partition; - * bound: bounds of attached relation. - */ -static void -attachPartitionTable(List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound) -{ - /* OK to create inheritance. Rest of the checks performed there */ - CreateInheritance(attachrel, rel, true); - - /* Update the pg_class entry. */ - StorePartitionBound(attachrel, rel, bound); - - /* Ensure there exists a correct set of indexes in the partition. */ - AttachPartitionEnsureIndexes(wqueue, rel, attachrel); - - /* and triggers */ - CloneRowTriggersToPartition(rel, attachrel); - - /* - * Clone foreign key constraints. Callee is responsible for setting up - * for phase 3 constraint verification. - */ - CloneForeignKeyConstraints(wqueue, rel, attachrel); -} - -/* * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES * * Return the address of the newly attached partition. @@ -18561,8 +18487,23 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd, check_new_partition_bound(RelationGetRelationName(attachrel), rel, cmd->bound, pstate); - /* Attach a new partition to the partitioned table. */ - attachPartitionTable(wqueue, rel, attachrel, cmd->bound); + /* OK to create inheritance. Rest of the checks performed there */ + CreateInheritance(attachrel, rel, true); + + /* Update the pg_class entry. */ + StorePartitionBound(attachrel, rel, cmd->bound); + + /* Ensure there exists a correct set of indexes in the partition. */ + AttachPartitionEnsureIndexes(wqueue, rel, attachrel); + + /* and triggers */ + CloneRowTriggersToPartition(rel, attachrel); + + /* + * Clone foreign key constraints. Callee is responsible for setting up + * for phase 3 constraint verification. + */ + CloneForeignKeyConstraints(wqueue, rel, attachrel); /* * Generate partition constraint from the partition bound specification. @@ -20077,729 +20018,3 @@ GetAttributeStorage(Oid atttypid, const char *storagemode) return cstorage; } - -/* - * Struct with context of new partition for inserting rows from split partition - */ -typedef struct SplitPartitionContext -{ - ExprState *partqualstate; /* expression for checking slot for partition - * (NULL for DEFAULT partition) */ - BulkInsertState bistate; /* state of bulk inserts for partition */ - TupleTableSlot *dstslot; /* slot for inserting row into partition */ - Relation partRel; /* relation for partition */ -} SplitPartitionContext; - - -/* - * createSplitPartitionContext: create context for partition and fill it - */ -static SplitPartitionContext * -createSplitPartitionContext(Relation partRel) -{ - SplitPartitionContext *pc; - - pc = (SplitPartitionContext *) palloc0(sizeof(SplitPartitionContext)); - pc->partRel = partRel; - - /* - * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so - * don't bother using it. - */ - pc->bistate = GetBulkInsertState(); - - /* Create tuple slot for new partition. */ - pc->dstslot = MakeSingleTupleTableSlot(RelationGetDescr(pc->partRel), - table_slot_callbacks(pc->partRel)); - ExecStoreAllNullTuple(pc->dstslot); - - return pc; -} - -/* - * deleteSplitPartitionContext: delete context for partition - */ -static void -deleteSplitPartitionContext(SplitPartitionContext *pc, int ti_options) -{ - ExecDropSingleTupleTableSlot(pc->dstslot); - FreeBulkInsertState(pc->bistate); - - table_finish_bulk_insert(pc->partRel, ti_options); - - pfree(pc); -} - -/* - * moveSplitTableRows: scan split partition (splitRel) of partitioned table - * (rel) and move rows into new partitions. - * - * New partitions description: - * partlist: list of pointers to SinglePartitionSpec structures. - * newPartRels: list of Relations. - * defaultPartOid: oid of DEFAULT partition, for table rel. - */ -static void -moveSplitTableRows(Relation rel, Relation splitRel, List *partlist, List *newPartRels, Oid defaultPartOid) -{ - /* The FSM is empty, so don't bother using it. */ - int ti_options = TABLE_INSERT_SKIP_FSM; - CommandId mycid; - EState *estate; - ListCell *listptr, - *listptr2; - TupleTableSlot *srcslot; - ExprContext *econtext; - TableScanDesc scan; - Snapshot snapshot; - MemoryContext oldCxt; - List *partContexts = NIL; - TupleConversionMap *tuple_map; - SplitPartitionContext *defaultPartCtx = NULL, - *pc; - bool isOldDefaultPart = false; - - mycid = GetCurrentCommandId(true); - - estate = CreateExecutorState(); - - forboth(listptr, partlist, listptr2, newPartRels) - { - SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr); - - pc = createSplitPartitionContext((Relation) lfirst(listptr2)); - - if (sps->bound->is_default) - { - /* We should not create constraint for detached DEFAULT partition. */ - defaultPartCtx = pc; - } - else - { - List *partConstraint; - - /* Build expression execution states for partition check quals. */ - partConstraint = get_qual_from_partbound(rel, sps->bound); - partConstraint = - (List *) eval_const_expressions(NULL, - (Node *) partConstraint); - /* Make boolean expression for ExecCheck(). */ - partConstraint = list_make1(make_ands_explicit(partConstraint)); - - /* - * Map the vars in the constraint expression from rel's attnos to - * splitRel's. - */ - partConstraint = map_partition_varattnos(partConstraint, - 1, splitRel, rel); - - pc->partqualstate = - ExecPrepareExpr((Expr *) linitial(partConstraint), estate); - Assert(pc->partqualstate != NULL); - } - - /* Store partition context into list. */ - partContexts = lappend(partContexts, pc); - } - - /* - * Create partition context for DEFAULT partition. We can insert values - * into this partition in case spaces with values between new partitions. - */ - if (!defaultPartCtx && OidIsValid(defaultPartOid)) - { - /* Indicate that we allocate context for old DEFAULT partition */ - isOldDefaultPart = true; - defaultPartCtx = createSplitPartitionContext(table_open(defaultPartOid, AccessExclusiveLock)); - } - - econtext = GetPerTupleExprContext(estate); - - /* Create necessary tuple slot. */ - srcslot = MakeSingleTupleTableSlot(RelationGetDescr(splitRel), - table_slot_callbacks(splitRel)); - - /* - * Map computing for moving attributes of split partition to new partition - * (for first new partition, but other new partitions can use the same - * map). - */ - pc = (SplitPartitionContext *) lfirst(list_head(partContexts)); - tuple_map = convert_tuples_by_name(RelationGetDescr(splitRel), - RelationGetDescr(pc->partRel)); - - /* Scan through the rows. */ - snapshot = RegisterSnapshot(GetLatestSnapshot()); - scan = table_beginscan(splitRel, snapshot, 0, NULL); - - /* - * Switch to per-tuple memory context and reset it for each tuple - * produced, so we don't leak memory. - */ - oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); - - while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot)) - { - bool found = false; - TupleTableSlot *insertslot; - - /* Extract data from old tuple. */ - slot_getallattrs(srcslot); - - econtext->ecxt_scantuple = srcslot; - - /* Search partition for current slot srcslot. */ - foreach(listptr, partContexts) - { - pc = (SplitPartitionContext *) lfirst(listptr); - - if (pc->partqualstate /* skip DEFAULT partition */ && - ExecCheck(pc->partqualstate, econtext)) - { - found = true; - break; - } - ResetExprContext(econtext); - } - if (!found) - { - /* Use DEFAULT partition if it exists. */ - if (defaultPartCtx) - pc = defaultPartCtx; - else - ereport(ERROR, - (errcode(ERRCODE_CHECK_VIOLATION), - errmsg("can not find partition for split partition row"), - errtable(splitRel))); - } - - if (tuple_map) - { - /* Need to use map to copy attributes. */ - insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot); - } - else - { - /* Copy attributes directly. */ - insertslot = pc->dstslot; - - ExecClearTuple(insertslot); - - memcpy(insertslot->tts_values, srcslot->tts_values, - sizeof(Datum) * srcslot->tts_nvalid); - memcpy(insertslot->tts_isnull, srcslot->tts_isnull, - sizeof(bool) * srcslot->tts_nvalid); - - ExecStoreVirtualTuple(insertslot); - } - - /* Write the tuple out to the new relation. */ - table_tuple_insert(pc->partRel, insertslot, mycid, - ti_options, pc->bistate); - - ResetExprContext(econtext); - - CHECK_FOR_INTERRUPTS(); - } - - MemoryContextSwitchTo(oldCxt); - - table_endscan(scan); - UnregisterSnapshot(snapshot); - - if (tuple_map) - free_conversion_map(tuple_map); - - ExecDropSingleTupleTableSlot(srcslot); - - FreeExecutorState(estate); - - foreach(listptr, partContexts) - deleteSplitPartitionContext((SplitPartitionContext *) lfirst(listptr), ti_options); - - /* Need to close table and free buffers for DEFAULT partition. */ - if (isOldDefaultPart) - { - Relation defaultPartRel = defaultPartCtx->partRel; - - deleteSplitPartitionContext(defaultPartCtx, ti_options); - /* Keep the lock until commit. */ - table_close(defaultPartRel, NoLock); - } -} - -/* - * createPartitionTable: create table for a new partition with given name - * (newPartName) like table (modelRel) - * - * Emulates command: CREATE [TEMP] TABLE <newPartName> (LIKE <modelRel's name> - * INCLUDING ALL EXCLUDING INDEXES EXCLUDING IDENTITY EXCLUDING STATISTICS) - * - * Also, this function sets the new partition access method same as parent - * table access methods (similarly to CREATE TABLE ... PARTITION OF). It - * checks that parent and child tables have compatible persistence. - * - * Function returns the created relation (locked in AccessExclusiveLock mode). - */ -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; - createStmt->tableElts = NIL; - createStmt->inhRelations = NIL; - createStmt->constraints = NIL; - createStmt->options = NIL; - createStmt->oncommit = ONCOMMIT_NOOP; - createStmt->tablespacename = get_tablespace_name(modelRel->rd_rel->reltablespace); - createStmt->if_not_exists = false; - createStmt->accessMethod = get_am_name(modelRel->rd_rel->relam); - - tlc = makeNode(TableLikeClause); - tlc->relation = makeRangeVar(get_namespace_name(RelationGetNamespace(modelRel)), - RelationGetRelationName(modelRel), -1); - - /* - * Indexes will be inherited on "attach new partitions" stage, after data - * moving. We also don't copy the extended statistics for consistency - * with CREATE TABLE PARTITION OF. - */ - tlc->options = CREATE_TABLE_LIKE_ALL & - ~(CREATE_TABLE_LIKE_INDEXES | CREATE_TABLE_LIKE_IDENTITY | CREATE_TABLE_LIKE_STATISTICS); - tlc->relationOid = InvalidOid; - tlc->newRelationOid = InvalidOid; - createStmt->tableElts = lappend(createStmt->tableElts, tlc); - - /* Need to make a wrapper PlannedStmt. */ - wrapper = makeNode(PlannedStmt); - wrapper->commandType = CMD_UTILITY; - wrapper->canSetTag = false; - wrapper->utilityStmt = (Node *) createStmt; - wrapper->stmt_location = context->pstmt->stmt_location; - wrapper->stmt_len = context->pstmt->stmt_len; - - ProcessUtility(wrapper, - context->queryString, - false, - PROCESS_UTILITY_SUBCOMMAND, - NULL, - NULL, - None_Receiver, - NULL); - - /* - * Open the new partition with no lock, because we already have - * AccessExclusiveLock placed there after creation. - */ - newRel = table_open(tlc->newRelationOid, 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; -} - -/* - * ALTER TABLE <name> SPLIT PARTITION <partition-name> INTO <partition-list> - */ -static void -ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel, - PartitionCmd *cmd, AlterTableUtilityContext *context) -{ - Relation splitRel; - Oid splitRelOid; - char relname[NAMEDATALEN]; - Oid namespaceId; - ListCell *listptr, - *listptr2; - bool isSameName = false; - char tmpRelName[NAMEDATALEN]; - List *newPartRels = NIL; - ObjectAddress object; - Oid defaultPartOid; - - defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true)); - - /* - * We are going to detach and remove this partition: need to use exclusive - * lock for preventing DML-queries to the partition. - */ - splitRel = table_openrv(cmd->name, AccessExclusiveLock); - - splitRelOid = RelationGetRelid(splitRel); - - /* Check descriptions of new partitions. */ - foreach(listptr, cmd->partlist) - { - Oid existing_relid; - SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr); - - strlcpy(relname, sps->name->relname, NAMEDATALEN); - - /* - * Look up the namespace in which we are supposed to create the - * partition, check we have permission to create there, lock it - * against concurrent drop, and mark stmt->relation as - * RELPERSISTENCE_TEMP if a temporary namespace is selected. - */ - sps->name->relpersistence = rel->rd_rel->relpersistence; - namespaceId = - RangeVarGetAndCheckCreationNamespace(sps->name, NoLock, NULL); - - /* - * 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); - if (existing_relid == splitRelOid && !isSameName) - /* One new partition can have the same name as split partition. */ - isSameName = true; - else if (existing_relid != InvalidOid) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_TABLE), - errmsg("relation \"%s\" already exists", relname))); - } - - /* Detach split partition. */ - RemoveInheritance(splitRel, rel, false); - /* Do the final part of detaching. */ - DetachPartitionFinalize(rel, splitRel, false, defaultPartOid); - - /* - * If new partition has the same name as split partition then we should - * rename split partition for reusing name. - */ - if (isSameName) - { - /* - * We must bump the command counter to make the split partition tuple - * visible for renaming. - */ - CommandCounterIncrement(); - /* Rename partition. */ - sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid); - RenameRelationInternal(splitRelOid, tmpRelName, false, false); - - /* - * We must bump the command counter to make the split partition tuple - * visible after renaming. - */ - CommandCounterIncrement(); - } - - /* Create new partitions (like split partition), without indexes. */ - foreach(listptr, cmd->partlist) - { - SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr); - Relation newPartRel; - - newPartRel = createPartitionTable(sps->name, rel, context); - newPartRels = lappend(newPartRels, newPartRel); - } - - /* Copy data from split partition to new partitions. */ - moveSplitTableRows(rel, splitRel, cmd->partlist, newPartRels, defaultPartOid); - /* Keep the lock until commit. */ - table_close(splitRel, NoLock); - - /* Attach new partitions to partitioned table. */ - forboth(listptr, cmd->partlist, listptr2, newPartRels) - { - SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr); - Relation newPartRel = (Relation) lfirst(listptr2); - - /* - * 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); - } - - /* Drop split partition. */ - object.classId = RelationRelationId; - object.objectId = splitRelOid; - object.objectSubId = 0; - /* Probably DROP_CASCADE is not needed. */ - performDeletion(&object, DROP_RESTRICT, 0); -} - -/* - * moveMergedTablesRows: scan partitions to be merged (mergingPartitionsList) - * of the partitioned table (rel) and move rows into the new partition - * (newPartRel). - */ -static void -moveMergedTablesRows(Relation rel, List *mergingPartitionsList, - Relation newPartRel) -{ - CommandId mycid; - - /* The FSM is empty, so don't bother using it. */ - int ti_options = TABLE_INSERT_SKIP_FSM; - ListCell *listptr; - BulkInsertState bistate; /* state of bulk inserts for partition */ - TupleTableSlot *dstslot; - - mycid = GetCurrentCommandId(true); - - /* Prepare a BulkInsertState for table_tuple_insert. */ - bistate = GetBulkInsertState(); - - /* Create necessary tuple slot. */ - dstslot = MakeSingleTupleTableSlot(RelationGetDescr(newPartRel), - table_slot_callbacks(newPartRel)); - ExecStoreAllNullTuple(dstslot); - - foreach(listptr, mergingPartitionsList) - { - Relation mergingPartition = (Relation) lfirst(listptr); - TupleTableSlot *srcslot; - TupleConversionMap *tuple_map; - TableScanDesc scan; - Snapshot snapshot; - - /* Create tuple slot for new partition. */ - srcslot = MakeSingleTupleTableSlot(RelationGetDescr(mergingPartition), - table_slot_callbacks(mergingPartition)); - - /* - * Map computing for moving attributes of merged partition to new - * partition. - */ - tuple_map = convert_tuples_by_name(RelationGetDescr(mergingPartition), - RelationGetDescr(newPartRel)); - - /* Scan through the rows. */ - snapshot = RegisterSnapshot(GetLatestSnapshot()); - scan = table_beginscan(mergingPartition, snapshot, 0, NULL); - - while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot)) - { - TupleTableSlot *insertslot; - - /* Extract data from old tuple. */ - slot_getallattrs(srcslot); - - if (tuple_map) - { - /* Need to use map to copy attributes. */ - insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot); - } - else - { - /* Copy attributes directly. */ - insertslot = dstslot; - - ExecClearTuple(insertslot); - - memcpy(insertslot->tts_values, srcslot->tts_values, - sizeof(Datum) * srcslot->tts_nvalid); - memcpy(insertslot->tts_isnull, srcslot->tts_isnull, - sizeof(bool) * srcslot->tts_nvalid); - - ExecStoreVirtualTuple(insertslot); - } - - /* Write the tuple out to the new relation. */ - table_tuple_insert(newPartRel, insertslot, mycid, - ti_options, bistate); - - CHECK_FOR_INTERRUPTS(); - } - - table_endscan(scan); - UnregisterSnapshot(snapshot); - - if (tuple_map) - free_conversion_map(tuple_map); - - ExecDropSingleTupleTableSlot(srcslot); - } - - ExecDropSingleTupleTableSlot(dstslot); - FreeBulkInsertState(bistate); - - table_finish_bulk_insert(newPartRel, ti_options); -} - -/* - * ALTER TABLE <name> MERGE PARTITIONS <partition-list> INTO <partition-name> - */ -static void -ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, - PartitionCmd *cmd, AlterTableUtilityContext *context) -{ - Relation newPartRel; - ListCell *listptr; - List *mergingPartitionsList = NIL; - Oid defaultPartOid; - Oid namespaceId; - Oid existingRelid; - - /* - * Lock all merged partitions, check them and create list with partitions - * contexts. - */ - foreach(listptr, cmd->partlist) - { - RangeVar *name = (RangeVar *) lfirst(listptr); - Relation mergingPartition; - - /* - * We are going to detach and remove this partition: need to use - * exclusive lock for preventing DML-queries to the partition. - */ - mergingPartition = table_openrv(name, AccessExclusiveLock); - - /* Store a next merging partition into the list. */ - mergingPartitionsList = lappend(mergingPartitionsList, - mergingPartition); - } - - /* - * Look up the namespace in which we are supposed to create the partition, - * check we have permission to create there, lock it against concurrent - * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary - * namespace is selected. - */ - cmd->name->relpersistence = rel->rd_rel->relpersistence; - namespaceId = - RangeVarGetAndCheckCreationNamespace(cmd->name, NoLock, NULL); - - /* - * Check if this name is already taken. This helps us to detect the - * situation when one of the merging partitions has the same name as the - * new partition. Otherwise, this would fail later on anyway but catching - * this here allows us to emit a nicer error message. - */ - existingRelid = get_relname_relid(cmd->name->relname, namespaceId); - - if (OidIsValid(existingRelid)) - { - Relation sameNamePartition = NULL; - - foreach_ptr(RelationData, mergingPartition, mergingPartitionsList) - { - if (RelationGetRelid(mergingPartition) == existingRelid) - { - sameNamePartition = mergingPartition; - break; - } - } - - if (sameNamePartition) - { - /* - * The new partition has the same name as one of merging - * partitions. - */ - char tmpRelName[NAMEDATALEN]; - - /* Generate temporary name. */ - sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid); - - /* - * Rename the existing partition with a temporary name, leaving it - * free for the new partition. We don't need to care about this - * in the future because we're going to eventually drop the - * existing partition anyway. - */ - RenameRelationInternal(RelationGetRelid(sameNamePartition), - tmpRelName, false, false); - - /* - * We must bump the command counter to make the new partition - * tuple visible for rename. - */ - CommandCounterIncrement(); - } - else - { - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_TABLE), - errmsg("relation \"%s\" already exists", cmd->name->relname))); - } - } - - /* Detach all merged partitions. */ - defaultPartOid = - get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true)); - foreach(listptr, mergingPartitionsList) - { - Relation mergingPartition = (Relation) lfirst(listptr); - - /* Remove the pg_inherits row first. */ - RemoveInheritance(mergingPartition, rel, false); - /* Do the final part of detaching. */ - DetachPartitionFinalize(rel, mergingPartition, false, defaultPartOid); - } - - /* 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); - - /* Drop the current partitions before attaching the new one. */ - foreach(listptr, mergingPartitionsList) - { - ObjectAddress object; - Relation mergingPartition = (Relation) lfirst(listptr); - - /* Get relation id before table_close() call. */ - object.objectId = RelationGetRelid(mergingPartition); - object.classId = RelationRelationId; - object.objectSubId = 0; - - /* Keep the lock until commit. */ - table_close(mergingPartition, NoLock); - - performDeletion(&object, DROP_RESTRICT, 0); - } - list_free(mergingPartitionsList); - - /* - * 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/gram.y b/src/backend/parser/gram.y index b7d98eb9f02..84cef57a707 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -269,7 +269,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); PartitionElem *partelem; PartitionSpec *partspec; PartitionBoundSpec *partboundspec; - SinglePartitionSpec *singlepartspec; RoleSpec *rolespec; PublicationObjSpec *publicationobjectspec; struct SelectLimit *selectlimit; @@ -646,8 +645,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <partelem> part_elem %type <list> part_params %type <partboundspec> PartitionBoundSpec -%type <singlepartspec> SinglePartitionSpec -%type <list> partitions_list %type <list> hash_partbound %type <defelt> hash_partbound_elem @@ -763,7 +760,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); ORDER ORDINALITY OTHERS OUT_P OUTER_P OVER OVERLAPS OVERLAY OVERRIDING OWNED OWNER - PARALLEL PARAMETER PARSER PARTIAL PARTITION PARTITIONS PASSING PASSWORD PATH + PARALLEL PARAMETER PARSER PARTIAL PARTITION PASSING PASSWORD PATH PLACING PLAN PLANS POLICY POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION @@ -778,7 +775,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); SAVEPOINT SCALAR SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW - SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SPLIT SOURCE SQL_P STABLE STANDALONE_P + SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SOURCE SQL_P STABLE STANDALONE_P START STATEMENT STATISTICS STDIN STDOUT STORAGE STORED STRICT_P STRING_P STRIP_P SUBSCRIPTION SUBSTRING SUPPORT SYMMETRIC SYSID SYSTEM_P SYSTEM_USER @@ -2311,23 +2308,6 @@ alter_table_cmds: | alter_table_cmds ',' alter_table_cmd { $$ = lappend($1, $3); } ; -partitions_list: - SinglePartitionSpec { $$ = list_make1($1); } - | partitions_list ',' SinglePartitionSpec { $$ = lappend($1, $3); } - ; - -SinglePartitionSpec: - PARTITION qualified_name PartitionBoundSpec - { - SinglePartitionSpec *n = makeNode(SinglePartitionSpec); - - n->name = $2; - n->bound = $3; - - $$ = n; - } - ; - partition_cmd: /* ALTER TABLE <name> ATTACH PARTITION <table_name> FOR VALUES */ ATTACH PARTITION qualified_name PartitionBoundSpec @@ -2338,7 +2318,6 @@ partition_cmd: n->subtype = AT_AttachPartition; cmd->name = $3; cmd->bound = $4; - cmd->partlist = NULL; cmd->concurrent = false; n->def = (Node *) cmd; @@ -2353,7 +2332,6 @@ partition_cmd: n->subtype = AT_DetachPartition; cmd->name = $3; cmd->bound = NULL; - cmd->partlist = NULL; cmd->concurrent = $4; n->def = (Node *) cmd; @@ -2367,35 +2345,6 @@ partition_cmd: n->subtype = AT_DetachPartitionFinalize; cmd->name = $3; cmd->bound = NULL; - cmd->partlist = NULL; - cmd->concurrent = false; - n->def = (Node *) cmd; - $$ = (Node *) n; - } - /* ALTER TABLE <name> SPLIT PARTITION <partition_name> INTO () */ - | SPLIT PARTITION qualified_name INTO '(' partitions_list ')' - { - AlterTableCmd *n = makeNode(AlterTableCmd); - PartitionCmd *cmd = makeNode(PartitionCmd); - - n->subtype = AT_SplitPartition; - cmd->name = $3; - cmd->bound = NULL; - cmd->partlist = $6; - cmd->concurrent = false; - n->def = (Node *) cmd; - $$ = (Node *) n; - } - /* ALTER TABLE <name> MERGE PARTITIONS () INTO <partition_name> */ - | MERGE PARTITIONS '(' qualified_name_list ')' INTO qualified_name - { - AlterTableCmd *n = makeNode(AlterTableCmd); - PartitionCmd *cmd = makeNode(PartitionCmd); - - n->subtype = AT_MergePartitions; - cmd->name = $7; - cmd->bound = NULL; - cmd->partlist = $4; cmd->concurrent = false; n->def = (Node *) cmd; $$ = (Node *) n; @@ -2412,7 +2361,6 @@ index_partition_cmd: n->subtype = AT_AttachPartition; cmd->name = $3; cmd->bound = NULL; - cmd->partlist = NULL; cmd->concurrent = false; n->def = (Node *) cmd; @@ -4138,7 +4086,6 @@ TableLikeClause: n->relation = $2; n->options = $3; n->relationOid = InvalidOid; - n->newRelationOid = InvalidOid; $$ = (Node *) n; } ; @@ -17744,7 +17691,6 @@ unreserved_keyword: | PARSER | PARTIAL | PARTITION - | PARTITIONS | PASSING | PASSWORD | PATH @@ -17812,7 +17758,6 @@ unreserved_keyword: | SKIP | SNAPSHOT | SOURCE - | SPLIT | SQL_P | STABLE | STANDALONE_P @@ -18369,7 +18314,6 @@ bare_label_keyword: | PARSER | PARTIAL | PARTITION - | PARTITIONS | PASSING | PASSWORD | PATH @@ -18449,7 +18393,6 @@ bare_label_keyword: | SNAPSHOT | SOME | SOURCE - | SPLIT | SQL_P | STABLE | STANDALONE_P diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index d5c2b2ff0b0..79cad4ab30c 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -32,7 +32,6 @@ #include "catalog/heap.h" #include "catalog/index.h" #include "catalog/namespace.h" -#include "catalog/partition.h" #include "catalog/pg_am.h" #include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" @@ -59,8 +58,6 @@ #include "parser/parse_type.h" #include "parser/parse_utilcmd.h" #include "parser/parser.h" -#include "partitioning/partdesc.h" -#include "partitioning/partbounds.h" #include "rewrite/rewriteManip.h" #include "utils/acl.h" #include "utils/builtins.h" @@ -136,7 +133,7 @@ static void transformConstraintAttrs(CreateStmtContext *cxt, List *constraintList); static void transformColumnType(CreateStmtContext *cxt, ColumnDef *column); static void setSchemaName(const char *context_schema, char **stmt_schema_name); -static void transformPartitionCmd(CreateStmtContext *cxt, PartitionBoundSpec *bound); +static void transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd); static List *transformPartitionRangeBounds(ParseState *pstate, List *blist, Relation parent); static void validateInfiniteBounds(ParseState *pstate, List *blist); @@ -3233,160 +3230,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, /* - * checkPartition - * Check that partRelOid is an oid of partition of the parent table rel - */ -static void -checkPartition(Relation rel, Oid partRelOid) -{ - Relation partRel; - - partRel = relation_open(partRelOid, AccessShareLock); - - if (partRel->rd_rel->relkind != RELKIND_RELATION) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table", - RelationGetRelationName(partRel)))); - - if (!partRel->rd_rel->relispartition) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a partition", - RelationGetRelationName(partRel)))); - - if (get_partition_parent(partRelOid, false) != RelationGetRelid(rel)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_TABLE), - errmsg("relation \"%s\" is not a partition of relation \"%s\"", - 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); -} - -/* - * transformPartitionCmdForSplit - * Analyze the ALTER TABLE ... SPLIT PARTITION command - * - * For each new partition sps->bound is set to the transformed value of bound. - * Does checks for bounds of new partitions. - */ -static void -transformPartitionCmdForSplit(CreateStmtContext *cxt, PartitionCmd *partcmd) -{ - Relation parent = cxt->rel; - Oid splitPartOid; - ListCell *listptr; - - if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("\"%s\" is not a partitioned table", RelationGetRelationName(parent)))); - - /* Transform partition bounds for all partitions in the list: */ - foreach(listptr, partcmd->partlist) - { - SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr); - - cxt->partbound = NULL; - transformPartitionCmd(cxt, sps->bound); - /* Assign transformed value of the partition bound. */ - sps->bound = cxt->partbound; - } - - splitPartOid = RangeVarGetRelid(partcmd->name, NoLock, false); - - checkPartition(parent, splitPartOid); - - /* Then we should check partitions with transformed bounds. */ - check_partitions_for_split(parent, splitPartOid, partcmd->name, partcmd->partlist, cxt->pstate); -} - - -/* - * transformPartitionCmdForMerge - * Analyze the ALTER TABLE ... MERGE PARTITIONS command - * - * Does simple checks for merged partitions. Calculates bound of resulting - * partition. - */ -static void -transformPartitionCmdForMerge(CreateStmtContext *cxt, PartitionCmd *partcmd) -{ - Oid defaultPartOid; - Oid partOid; - Relation parent = cxt->rel; - PartitionKey key; - char strategy; - ListCell *listptr, - *listptr2; - bool isDefaultPart = false; - List *partOids = NIL; - - if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("\"%s\" is not a partitioned table", RelationGetRelationName(parent)))); - - key = RelationGetPartitionKey(parent); - strategy = get_partition_strategy(key); - - if (strategy == PARTITION_STRATEGY_HASH) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("partition of hash-partitioned table cannot be merged"))); - - /* Is current partition a DEFAULT partition? */ - defaultPartOid = get_default_oid_from_partdesc( - RelationGetPartitionDesc(parent, true)); - - foreach(listptr, partcmd->partlist) - { - RangeVar *name = (RangeVar *) lfirst(listptr); - - /* Partitions in the list should have different names. */ - for_each_cell(listptr2, partcmd->partlist, lnext(partcmd->partlist, listptr)) - { - RangeVar *name2 = (RangeVar *) lfirst(listptr2); - - if (equal(name, name2)) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_TABLE), - errmsg("partition with name \"%s\" is already used", name->relname)), - parser_errposition(cxt->pstate, name2->location)); - } - - /* Search DEFAULT partition in the list. */ - partOid = RangeVarGetRelid(name, NoLock, false); - if (partOid == defaultPartOid) - isDefaultPart = true; - - checkPartition(parent, partOid); - - partOids = lappend_oid(partOids, partOid); - } - - /* Allocate bound of resulting partition. */ - Assert(partcmd->bound == NULL); - partcmd->bound = makeNode(PartitionBoundSpec); - - /* Fill partition bound. */ - partcmd->bound->strategy = strategy; - partcmd->bound->location = -1; - partcmd->bound->is_default = isDefaultPart; - if (!isDefaultPart) - calculate_partition_bound_for_merge(parent, partcmd->partlist, - partOids, partcmd->bound, - cxt->pstate); -} - -/* * transformAlterTableStmt - * parse analysis for ALTER TABLE * @@ -3654,7 +3497,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, { PartitionCmd *partcmd = (PartitionCmd *) cmd->def; - transformPartitionCmd(&cxt, partcmd->bound); + transformPartitionCmd(&cxt, partcmd); /* assign transformed value of the partition bound */ partcmd->bound = cxt.partbound; } @@ -3662,24 +3505,6 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, newcmds = lappend(newcmds, cmd); break; - case AT_SplitPartition: - case AT_MergePartitions: - { - PartitionCmd *partcmd = (PartitionCmd *) cmd->def; - - if (list_length(partcmd->partlist) < 2) - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("list of new partitions should contain at least two items"))); - - if (cmd->subtype == AT_SplitPartition) - transformPartitionCmdForSplit(&cxt, partcmd); - else - transformPartitionCmdForMerge(&cxt, partcmd); - newcmds = lappend(newcmds, cmd); - break; - } - default: /* @@ -4070,13 +3895,13 @@ setSchemaName(const char *context_schema, char **stmt_schema_name) /* * transformPartitionCmd - * Analyze the ATTACH/DETACH/SPLIT PARTITION command + * Analyze the ATTACH/DETACH PARTITION command * - * In case of the ATTACH/SPLIT PARTITION command, cxt->partbound is set to the - * transformed value of bound. + * In case of the ATTACH PARTITION command, cxt->partbound is set to the + * transformed value of cmd->bound. */ static void -transformPartitionCmd(CreateStmtContext *cxt, PartitionBoundSpec *bound) +transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd) { Relation parentRel = cxt->rel; @@ -4085,9 +3910,9 @@ transformPartitionCmd(CreateStmtContext *cxt, PartitionBoundSpec *bound) case RELKIND_PARTITIONED_TABLE: /* transform the partition bound, if any */ Assert(RelationGetPartitionKey(parentRel) != NULL); - if (bound != NULL) + if (cmd->bound != NULL) cxt->partbound = transformPartitionBound(cxt->pstate, parentRel, - bound); + cmd->bound); break; case RELKIND_PARTITIONED_INDEX: @@ -4095,7 +3920,7 @@ transformPartitionCmd(CreateStmtContext *cxt, PartitionBoundSpec *bound) * A partitioned index cannot have a partition bound set. ALTER * INDEX prevents that with its grammar, but not ALTER TABLE. */ - if (bound != NULL) + if (cmd->bound != NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("\"%s\" is not a partitioned table", diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c index 4eda59767ce..c28639d2e3f 100644 --- a/src/backend/partitioning/partbounds.c +++ b/src/backend/partitioning/partbounds.c @@ -3214,9 +3214,8 @@ check_new_partition_bound(char *relname, Relation parent, PartitionRangeDatum *datum; /* - * Point to problematic key in the list of lower - * datums; if we have equality, point to the first - * one. + * Point to problematic key in the lower datums list; + * if we have equality, point to the first one. */ datum = cmpval == 0 ? linitial(spec->lowerdatums) : list_nth(spec->lowerdatums, abs(cmpval) - 1); @@ -4978,899 +4977,3 @@ satisfies_hash_partition(PG_FUNCTION_ARGS) PG_RETURN_BOOL(rowHash % modulus == remainder); } - -/* - * check_two_partitions_bounds_range - * - * (function for BY RANGE partitioning) - * - * 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 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 these bounds will be included in - * DEFAULT partition. - * - * parent: partitioned table - * first_name: name of first partition - * first_bound: bound of first partition - * 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 determining error position - */ -static void -check_two_partitions_bounds_range(Relation parent, - RangeVar *first_name, - PartitionBoundSpec *first_bound, - RangeVar *second_name, - PartitionBoundSpec *second_bound, - bool defaultPart, - ParseState *pstate) -{ - PartitionKey key = RelationGetPartitionKey(parent); - PartitionRangeBound *first_upper; - PartitionRangeBound *second_lower; - int cmpval; - - Assert(key->strategy == PARTITION_STRATEGY_RANGE); - - first_upper = make_one_partition_rbound(key, -1, first_bound->upperdatums, false); - second_lower = make_one_partition_rbound(key, -1, second_bound->lowerdatums, true); - - /* - * lower1=false (the second to last argument) for correct comparison of - * lower and upper bounds. - */ - cmpval = partition_rbound_cmp(key->partnatts, - key->partsupfunc, - key->partcollation, - second_lower->datums, second_lower->kind, - false, first_upper); - if ((!defaultPart && cmpval) || (defaultPart && cmpval < 0)) - { - PartitionRangeDatum *datum = linitial(second_bound->lowerdatums); - - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("lower bound of partition \"%s\" conflicts with upper bound of previous partition \"%s\"", - second_name->relname, first_name->relname), - parser_errposition(pstate, datum->location))); - } -} - -/* - * check_partitions_not_overlap_list - * - * (function for BY LIST partitioning) - * - * This is a helper function for check_partitions_for_split(). - * Checks that the values of the new partitions do not overlap. - * - * parent: partitioned table - * parts: array of SinglePartitionSpec structs with info about split partitions - * nparts: size of array "parts" - */ -static void -check_partitions_not_overlap_list(Relation parent, - SinglePartitionSpec **parts, - int nparts, - ParseState *pstate) -{ - PartitionKey key PG_USED_FOR_ASSERTS_ONLY = RelationGetPartitionKey(parent); - int overlap_location = -1; - int i, - j; - SinglePartitionSpec *sps1, - *sps2; - List *overlap; - - Assert(key->strategy == PARTITION_STRATEGY_LIST); - - for (i = 0; i < nparts; i++) - { - sps1 = parts[i]; - - for (j = i + 1; j < nparts; j++) - { - sps2 = parts[j]; - - /* - * Calculate intersection between values of two partitions. - */ - overlap = list_intersection(sps1->bound->listdatums, - sps2->bound->listdatums); - if (list_length(overlap) > 0) - { - Const *val = (Const *) lfirst(list_head(overlap)); - - overlap_location = val->location; - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("new partition \"%s\" would overlap with another new partition \"%s\"", - sps1->name->relname, sps2->name->relname), - parser_errposition(pstate, overlap_location))); - } - } - } -} - -/* - * get_partition_bound_spec - * - * Returns description of partition with Oid "partOid" and name "name". - * - * partOid: partition Oid - * name: partition name - */ -static PartitionBoundSpec * -get_partition_bound_spec(Oid partOid, RangeVar *name) -{ - HeapTuple tuple; - Datum datum; - bool isnull; - PartitionBoundSpec *boundspec = NULL; - - /* Try fetching the tuple from the catcache, for speed. */ - tuple = SearchSysCache1(RELOID, partOid); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "cache lookup failed for relation \"%s\"", - name->relname); - - datum = SysCacheGetAttr(RELOID, tuple, - Anum_pg_class_relpartbound, - &isnull); - if (isnull) - elog(ERROR, "partition bound for relation \"%s\" is null", - name->relname); - - boundspec = stringToNode(TextDatumGetCString(datum)); - - if (!IsA(boundspec, PartitionBoundSpec)) - elog(ERROR, "expected PartitionBoundSpec for relation \"%s\"", - name->relname); - - ReleaseSysCache(tuple); - return boundspec; -} - -/* - * check_partition_bounds_for_split_range - * - * (function for BY RANGE partitioning) - * - * 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 - * partition. If last=true (this means that "spec" is the last of new - * partitions) then upper bound of "spec" should be equal (or less than or - * equal in case defaultPart=true) to upper bound of split partition. - * - * parent: partitioned table - * relname: name of the new partition - * spec: bounds specification of the new partition - * splitPartOid: split partition Oid - * splitPartName: split partition name - * first: true in case new partition "spec" is first of new partitions - * last: true in case new partition "spec" is last of new partitions - * defaultPart: true in case partitioned table has DEFAULT partition - * pstate: pointer to ParseState struct for determine error position - */ -static void -check_partition_bounds_for_split_range(Relation parent, - char *relname, - PartitionBoundSpec *spec, - Oid splitPartOid, - RangeVar *splitPartName, - bool first, - bool last, - bool defaultPart, - ParseState *pstate) -{ - PartitionKey key = RelationGetPartitionKey(parent); - PartitionRangeBound *lower, - *upper; - int cmpval; - - Assert(key->strategy == PARTITION_STRATEGY_RANGE); - Assert(spec->strategy == PARTITION_STRATEGY_RANGE); - - lower = make_one_partition_rbound(key, -1, spec->lowerdatums, true); - upper = make_one_partition_rbound(key, -1, spec->upperdatums, false); - - /* - * First check if the resulting range would be empty with specified lower - * and upper bounds. partition_rbound_cmp cannot return zero here, since - * the lower-bound flags are different. - */ - cmpval = partition_rbound_cmp(key->partnatts, - key->partsupfunc, - key->partcollation, - lower->datums, lower->kind, - true, upper); - Assert(cmpval != 0); - if (cmpval > 0) - { - /* Point to problematic key in the lower datums list. */ - PartitionRangeDatum *datum = list_nth(spec->lowerdatums, cmpval - 1); - - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("empty range bound specified for partition \"%s\"", - relname), - errdetail("Specified lower bound %s is greater than or equal to upper bound %s.", - get_range_partbound_string(spec->lowerdatums), - get_range_partbound_string(spec->upperdatums)), - parser_errposition(pstate, datum->location))); - } - - /* Need to check first and last partitions (from set of new partitions) */ - if (first || last) - { - PartitionBoundSpec *split_spec = get_partition_bound_spec(splitPartOid, splitPartName); - PartitionRangeDatum *datum; - - if (first) - { - PartitionRangeBound *split_lower; - - split_lower = make_one_partition_rbound(key, -1, split_spec->lowerdatums, true); - - cmpval = partition_rbound_cmp(key->partnatts, - key->partsupfunc, - key->partcollation, - lower->datums, lower->kind, - true, split_lower); - - /* - * Lower bound of "spec" should be equal (or greater than or equal - * in case defaultPart=true) to lower bound of split partition. - */ - if (!defaultPart) - { - if (cmpval != 0) - { - datum = list_nth(spec->lowerdatums, abs(cmpval) - 1); - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("lower bound of partition \"%s\" is not equal to lower bound of split partition", - relname), - parser_errposition(pstate, datum->location))); - } - } - else - { - if (cmpval < 0) - { - datum = list_nth(spec->lowerdatums, abs(cmpval) - 1); - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("lower bound of partition \"%s\" is less than lower bound of split partition", - relname), - parser_errposition(pstate, datum->location))); - } - } - } - else - { - PartitionRangeBound *split_upper; - - split_upper = make_one_partition_rbound(key, -1, split_spec->upperdatums, false); - - cmpval = partition_rbound_cmp(key->partnatts, - key->partsupfunc, - key->partcollation, - upper->datums, upper->kind, - false, split_upper); - - /* - * Upper bound of "spec" should be equal (or less than or equal in - * case defaultPart=true) to upper bound of split partition. - */ - if (!defaultPart) - { - if (cmpval != 0) - { - datum = list_nth(spec->upperdatums, abs(cmpval) - 1); - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("upper bound of partition \"%s\" is not equal to upper bound of split partition", - relname), - parser_errposition(pstate, datum->location))); - } - } - else - { - if (cmpval > 0) - { - datum = list_nth(spec->upperdatums, abs(cmpval) - 1); - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("upper bound of partition \"%s\" is greater than upper bound of split partition", - relname), - parser_errposition(pstate, datum->location))); - } - } - } - } -} - -/* - * check_partition_bounds_for_split_list - * - * (function for BY LIST partitioning) - * - * Checks that bounds of new partition are inside bounds of split partition - * (with Oid splitPartOid). - * - * parent: partitioned table - * relname: name of the new partition - * spec: bounds specification of the new partition - * splitPartOid: split partition Oid - * pstate: pointer to ParseState struct for determine error position - */ -static void -check_partition_bounds_for_split_list(Relation parent, char *relname, - PartitionBoundSpec *spec, - Oid splitPartOid, - ParseState *pstate) -{ - PartitionKey key = RelationGetPartitionKey(parent); - PartitionDesc partdesc = RelationGetPartitionDesc(parent, false); - PartitionBoundInfo boundinfo = partdesc->boundinfo; - int with = -1; - bool overlap = false; - int overlap_location = -1; - ListCell *cell; - - Assert(key->strategy == PARTITION_STRATEGY_LIST); - Assert(spec->strategy == PARTITION_STRATEGY_LIST); - Assert(boundinfo && boundinfo->strategy == PARTITION_STRATEGY_LIST); - - /* - * Search each value of new partition "spec" in existing partitions. All - * of them should be in split partition (with Oid splitPartOid). - */ - foreach(cell, spec->listdatums) - { - Const *val = lfirst_node(Const, cell); - - overlap_location = val->location; - if (!val->constisnull) - { - int offset; - bool equal; - - offset = partition_list_bsearch(&key->partsupfunc[0], - key->partcollation, - boundinfo, - val->constvalue, - &equal); - if (offset >= 0 && equal) - { - with = boundinfo->indexes[offset]; - if (partdesc->oids[with] != splitPartOid) - { - overlap = true; - break; - } - } - else - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("new partition \"%s\" cannot have this value because split partition does not have", - relname), - parser_errposition(pstate, overlap_location))); - } - else if (partition_bound_accepts_nulls(boundinfo)) - { - with = boundinfo->null_index; - if (partdesc->oids[with] != splitPartOid) - { - overlap = true; - break; - } - } - else - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("new partition \"%s\" cannot have NULL value because split partition does not have", - relname), - parser_errposition(pstate, overlap_location))); - } - - if (overlap) - { - Assert(with >= 0); - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("new partition \"%s\" would overlap with another (not split) partition \"%s\"", - relname, get_rel_name(partdesc->oids[with])), - parser_errposition(pstate, overlap_location))); - } -} - -/* - * find_value_in_new_partitions_list - * - * (function for BY LIST partitioning) - * - * Function returns true in case any of new partitions contains value "value". - * - * partsupfunc: information about comparison function associated with the partition key - * partcollation: partitioning collation - * parts: pointer to array with new partitions descriptions - * nparts: number of new partitions - * value: the value that we are looking for - * isnull: true if the value that we are looking for is NULL - */ -static bool -find_value_in_new_partitions_list(FmgrInfo *partsupfunc, - Oid *partcollation, - SinglePartitionSpec **parts, - int nparts, - Datum value, - bool isnull) -{ - ListCell *valptr; - int i; - - for (i = 0; i < nparts; i++) - { - SinglePartitionSpec *sps = parts[i]; - - foreach(valptr, sps->bound->listdatums) - { - Const *val = lfirst_node(Const, valptr); - - if (isnull && val->constisnull) - return true; - - if (!isnull && !val->constisnull) - { - if (DatumGetInt32(FunctionCall2Coll(&partsupfunc[0], - partcollation[0], - val->constvalue, - value)) == 0) - return true; - } - } - } - return false; -} - -/* - * check_parent_values_in_new_partitions - * - * (function for BY LIST partitioning) - * - * Checks that all values of split partition (with Oid partOid) contains in new - * partitions. - * - * parent: partitioned table - * partOid: split partition Oid - * parts: pointer to array with new partitions descriptions - * nparts: number of new partitions - * pstate: pointer to ParseState struct for determine error position - */ -static void -check_parent_values_in_new_partitions(Relation parent, - Oid partOid, - SinglePartitionSpec **parts, - int nparts, - ParseState *pstate) -{ - PartitionKey key = RelationGetPartitionKey(parent); - PartitionDesc partdesc = RelationGetPartitionDesc(parent, false); - PartitionBoundInfo boundinfo = partdesc->boundinfo; - int i; - bool found = true; - bool searchNull = false; - Datum datum = PointerGetDatum(NULL); - - Assert(key->strategy == PARTITION_STRATEGY_LIST); - - /* - * 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) - { - if (!find_value_in_new_partitions_list(&key->partsupfunc[0], - key->partcollation, parts, nparts, datum, true)) - { - found = false; - searchNull = true; - } - } - - /* - * Search all values of split partition with partOid in PartitionDesc of - * partitioned table. - */ - for (i = 0; i < boundinfo->ndatums; i++) - { - if (partdesc->oids[boundinfo->indexes[i]] == partOid) - { - /* We found value that split partition contains. */ - datum = boundinfo->datums[i][0]; - if (!find_value_in_new_partitions_list(&key->partsupfunc[0], - key->partcollation, parts, nparts, datum, false)) - { - found = false; - break; - } - } - } - - if (!found) - { - Const *notFoundVal; - - if (!searchNull) - - /* - * Make Const for getting string representation of not found - * value. - */ - notFoundVal = makeConst(key->parttypid[0], - key->parttypmod[0], - key->parttypcoll[0], - key->parttyplen[0], - datum, - false, /* isnull */ - key->parttypbyval[0]); - - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("new partitions do not have value %s but split partition does", - searchNull ? "NULL" : get_list_partvalue_string(notFoundVal)))); - } -} - -/* - * check_partitions_for_split - * - * Checks new partitions for SPLIT PARTITIONS command: - * 1. DEFAULT partition should be one. - * 2. New partitions should have different names - * (with existing partitions too). - * 3. Bounds of new partitions should not overlap with new and existing - * partitions. - * 4. In case split partition is DEFAULT partition, one of new partitions - * should be DEFAULT. - * 5. In case new partitions or existing partitions contains DEFAULT - * partition, new partitions can have any bounds inside split - * partition bound (can be spaces between partitions bounds). - * 6. In case partitioned table does not have DEFAULT partition, DEFAULT - * partition can be defined as one of new partition. - * 7. In case new partitions not contains DEFAULT partition and - * partitioned table does not have DEFAULT partition the following - * should be true: sum bounds of new partitions should be equal - * to bound of split partition. - * - * parent: partitioned table - * splitPartOid: split partition Oid - * splitPartName: split partition name - * list: list of new partitions - * pstate: pointer to ParseState struct for determine error position - */ -void -check_partitions_for_split(Relation parent, - Oid splitPartOid, - RangeVar *splitPartName, - List *partlist, - ParseState *pstate) -{ - PartitionKey key; - char strategy; - Oid defaultPartOid; - bool isSplitPartDefault; - bool existsDefaultPart; - ListCell *listptr; - int default_index = -1; - int i, - j; - SinglePartitionSpec **new_parts; - SinglePartitionSpec *spsPrev = NULL; - int nparts = 0; - - key = RelationGetPartitionKey(parent); - strategy = get_partition_strategy(key); - - switch (strategy) - { - case PARTITION_STRATEGY_LIST: - case PARTITION_STRATEGY_RANGE: - { - /* - * Make array new_parts with new partitions except DEFAULT - * partition. - */ - new_parts = (SinglePartitionSpec **) - palloc0(list_length(partlist) * sizeof(SinglePartitionSpec *)); - i = 0; - foreach(listptr, partlist) - { - SinglePartitionSpec *sps = - (SinglePartitionSpec *) lfirst(listptr); - - if (sps->bound->is_default) - { - if (default_index >= 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("DEFAULT partition should be one")), - parser_errposition(pstate, sps->name->location)); - default_index = i; - } - else - { - new_parts[nparts++] = sps; - } - i++; - } - } - break; - - case PARTITION_STRATEGY_HASH: - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("partition of hash-partitioned table cannot be split"))); - break; - - default: - elog(ERROR, "unexpected partition strategy: %d", - (int) key->strategy); - break; - } - - if (strategy == PARTITION_STRATEGY_RANGE) - { - PartitionRangeBound **lower_bounds; - SinglePartitionSpec **tmp_new_parts; - - /* - * For simplify check for ranges of new partitions need to sort all - * partitions in ascending order of them bounds (we compare upper - * bound only). - */ - lower_bounds = (PartitionRangeBound **) - palloc0(nparts * sizeof(PartitionRangeBound *)); - - /* Create array of lower bounds. */ - for (i = 0; i < nparts; i++) - { - lower_bounds[i] = make_one_partition_rbound(key, i, - new_parts[i]->bound->lowerdatums, true); - } - - /* Sort array of lower bounds. */ - qsort_arg(lower_bounds, nparts, sizeof(PartitionRangeBound *), - qsort_partition_rbound_cmp, (void *) key); - - /* Reorder array of partitions. */ - tmp_new_parts = new_parts; - new_parts = (SinglePartitionSpec **) - palloc0(nparts * sizeof(SinglePartitionSpec *)); - for (i = 0; i < nparts; i++) - new_parts[i] = tmp_new_parts[lower_bounds[i]->index]; - - pfree(tmp_new_parts); - pfree(lower_bounds); - } - - defaultPartOid = - get_default_oid_from_partdesc(RelationGetPartitionDesc(parent, true)); - - /* isSplitPartDefault flag: is split partition a DEFAULT partition? */ - isSplitPartDefault = (defaultPartOid == splitPartOid); - - if (isSplitPartDefault && default_index < 0) - { - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("one partition 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)) - { - SinglePartitionSpec *spsDef = - (SinglePartitionSpec *) list_nth(partlist, default_index); - - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("new partition cannot be DEFAULT because DEFAULT partition already exists")), - parser_errposition(pstate, spsDef->name->location)); - } - - /* Indicator that partitioned table has (or will have) DEFAULT partition */ - existsDefaultPart = OidIsValid(defaultPartOid) || (default_index >= 0); - - for (i = 0; i < nparts; i++) - { - SinglePartitionSpec *sps = new_parts[i]; - - if (isSplitPartDefault) - { - /* - * In case split partition is DEFAULT partition we can use any - * free ranges - as when creating a new partition. - */ - check_new_partition_bound(sps->name->relname, parent, sps->bound, - pstate); - } - else - { - /* - * Checks that bound of current partition is inside bound of split - * partition. For range partitioning: checks that upper bound of - * previous partition is equal to lower bound of current - * partition. For list partitioning: checks that split partition - * contains all values of current partition. - */ - if (strategy == PARTITION_STRATEGY_RANGE) - { - bool first = (i == 0); - bool last = (i == (nparts - 1)); - - check_partition_bounds_for_split_range(parent, sps->name->relname, sps->bound, - splitPartOid, splitPartName, - first, last, - existsDefaultPart, pstate); - } - else - check_partition_bounds_for_split_list(parent, sps->name->relname, - sps->bound, splitPartOid, pstate); - } - - /* Ranges of new partitions should not overlap. */ - if (strategy == PARTITION_STRATEGY_RANGE && spsPrev) - check_two_partitions_bounds_range(parent, spsPrev->name, spsPrev->bound, - sps->name, sps->bound, existsDefaultPart, pstate); - - spsPrev = sps; - - /* Check: new partitions should have different names. */ - for (j = i + 1; j < nparts; j++) - { - SinglePartitionSpec *sps2 = new_parts[j]; - - if (equal(sps->name, sps2->name)) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_TABLE), - errmsg("name \"%s\" is already used", sps2->name->relname)), - parser_errposition(pstate, sps2->name->location)); - } - } - - if (strategy == PARTITION_STRATEGY_LIST) - { - /* Values of new partitions should not overlap. */ - check_partitions_not_overlap_list(parent, new_parts, nparts, - pstate); - - /* - * Need to check that all values of split partition contains in new - * partitions. Skip this check if DEFAULT partition exists. - */ - if (!existsDefaultPart) - check_parent_values_in_new_partitions(parent, splitPartOid, - new_parts, nparts, pstate); - } - - pfree(new_parts); -} - -/* - * calculate_partition_bound_for_merge - * - * Calculates the bound of merged partition "spec" by using the bounds of - * partitions to be merged. - * - * parent: partitioned table - * partNames: names of partitions to be merged - * partOids: Oids of partitions to be merged - * spec (out): bounds specification of the merged partition - * pstate: pointer to ParseState struct for determine error position - */ -void -calculate_partition_bound_for_merge(Relation parent, - List *partNames, - List *partOids, - PartitionBoundSpec *spec, - ParseState *pstate) -{ - PartitionKey key = RelationGetPartitionKey(parent); - PartitionBoundSpec *bound; - - Assert(!spec->is_default); - - switch (key->strategy) - { - case PARTITION_STRATEGY_RANGE: - { - int i; - PartitionRangeBound **lower_bounds; - int nparts = list_length(partOids); - List *bounds = NIL; - - lower_bounds = (PartitionRangeBound **) - palloc0(nparts * sizeof(PartitionRangeBound *)); - - /* - * Create array of lower bounds and list of - * PartitionBoundSpec. - */ - for (i = 0; i < nparts; i++) - { - bound = get_partition_bound_spec(list_nth_oid(partOids, i), - (RangeVar *) list_nth(partNames, i)); - - lower_bounds[i] = make_one_partition_rbound(key, i, bound->lowerdatums, true); - bounds = lappend(bounds, bound); - } - - /* Sort array of lower bounds. */ - qsort_arg(lower_bounds, nparts, sizeof(PartitionRangeBound *), - qsort_partition_rbound_cmp, (void *) key); - - /* Ranges of partitions should not overlap. */ - for (i = 1; i < nparts; i++) - { - int index = lower_bounds[i]->index; - int prev_index = lower_bounds[i - 1]->index; - - check_two_partitions_bounds_range(parent, - (RangeVar *) list_nth(partNames, prev_index), - (PartitionBoundSpec *) list_nth(bounds, prev_index), - (RangeVar *) list_nth(partNames, index), - (PartitionBoundSpec *) list_nth(bounds, index), - false, pstate); - } - - /* - * 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 the upper bound of merged - * partition. - */ - spec->upperdatums = - ((PartitionBoundSpec *) list_nth(bounds, lower_bounds[nparts - 1]->index))->upperdatums; - - pfree(lower_bounds); - list_free(bounds); - break; - } - - case PARTITION_STRATEGY_LIST: - { - ListCell *listptr, - *listptr2; - - /* Consolidate bounds for all partitions in the list. */ - forboth(listptr, partOids, listptr2, partNames) - { - RangeVar *name = (RangeVar *) lfirst(listptr2); - Oid curOid = lfirst_oid(listptr); - - bound = get_partition_bound_spec(curOid, name); - spec->listdatums = list_concat(spec->listdatums, bound->listdatums); - } - break; - } - - default: - elog(ERROR, "unexpected partition strategy: %d", - (int) key->strategy); - } -} diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index b385175e7a2..b2ea8125c92 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -1225,12 +1225,6 @@ ProcessUtilitySlow(ParseState *pstate, morestmts = expandTableLikeClause(table_rv, like); stmts = list_concat(morestmts, stmts); - - /* - * Store the OID of newly created relation to the - * TableLikeClause for the caller to use it. - */ - like->newRelationOid = address.objectId; } else { diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 4039ee0df73..00eda1b34c0 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -13296,21 +13296,3 @@ get_range_partbound_string(List *bound_datums) return buf->data; } - -/* - * get_list_partvalue_string - * A C string representation of one list partition value - */ -char * -get_list_partvalue_string(Const *val) -{ - deparse_context context; - StringInfo buf = makeStringInfo(); - - memset(&context, 0, sizeof(deparse_context)); - context.buf = buf; - - get_const_expr(val, &context, -1); - - return buf->data; -} |