diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index dadf5e0dcb9..291545ee008 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -4779,44 +4779,6 @@ ALTER INDEX measurement_city_id_logdate_key
...
-
-
- There is also an option for merging multiple table partitions into
- a single partition using the
- ALTER TABLE ... MERGE PARTITIONS.
- This feature simplifies the management of partitioned tables by allowing
- 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
- ACCESS EXCLUSIVE lock, which could impact high-load
- systems due to the lock's restrictive nature. For example, we can
- merge three monthly partitions into one quarter partition:
-
-ALTER TABLE measurement
- MERGE PARTITIONS (measurement_y2006m01,
- measurement_y2006m02,
- measurement_y2006m03) INTO measurement_y2006q1;
-
-
-
-
- Similarly to merging multiple table partitions, there is an option for
- splitting a single partition into multiple using the
- ALTER TABLE ... SPLIT PARTITION.
- This feature could come in handy when one partition grows too big
- and needs to be split into multiple. It's important to note that
- this operation is not supported for hash-partitioned tables and acquires
- an ACCESS EXCLUSIVE lock, which could impact high-load
- systems due to the lock's restrictive nature. For example, we can split
- the quarter partition back to monthly partitions:
-
-ALTER TABLE measurement SPLIT PARTITION measurement_y2006q1 INTO
- (PARTITION measurement_y2006m01 FOR VALUES FROM ('2006-01-01') TO ('2006-02-01'),
- PARTITION measurement_y2006m02 FOR VALUES FROM ('2006-02-01') TO ('2006-03-01'),
- PARTITION measurement_y2006m03 FOR VALUES FROM ('2006-03-01') TO ('2006-04-01'));
-
-
-
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index ff7071bef5b..0f9d698d170 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -37,12 +37,6 @@ ALTER TABLE [ IF EXISTS ] name
ATTACH PARTITION partition_name { FOR VALUES partition_bound_spec | DEFAULT }
ALTER TABLE [ IF EXISTS ] name
DETACH PARTITION partition_name [ CONCURRENTLY | FINALIZE ]
-ALTER TABLE [ IF EXISTS ] name
- MERGE PARTITIONS (partition_name1, partition_name2 [, ...]) INTO partition_name
-ALTER TABLE [ IF EXISTS ] name
- SPLIT PARTITION partition_name INTO
- (PARTITION partition_name1 { FOR VALUES partition_bound_spec | DEFAULT },
- PARTITION partition_name2 { FOR VALUES partition_bound_spec | DEFAULT } [, ...])
where action is one of:
@@ -1185,239 +1179,18 @@ WITH ( MODULUS numeric_literal, REM
-
- MERGE PARTITIONS (partition_name1, partition_name2 [, ...]) INTO partition_name
-
-
-
- This form merges several partitions of the target table into a new partition.
- Hash-partitioned target table is not supported.
- Only simple, non-partitioned partitions can be merged.
- The new partition (partition_name)
- can have the same name as one of the merged partitions
- (partition_name1,
- partition_name2 [, ...]).
-
-
-
- If the DEFAULT partition is not in the
- list of merged partitions:
-
-
-
- For range-partitioned tables, the ranges of merged partitions
- must be adjacent; this applies even if the partitioned table
- has no default partition.
- The partition bounds of merged partitions are combined to form the new partition bound for
- partition_name.
-
-
-
-
- For list-partitioned tables, the partition bounds of
- merged partitions are combined to form the new partition bound for
- partition_name.
-
-
-
- If the DEFAULT partition is in the list of merged partitions:
-
-
-
- The partition partition_name
- will be the new DEFAULT partition of the target table.
-
-
-
-
- The partition bound specifications for merged partitions can be arbitrary.
-
-
-
-
-
- All merged partitions must have the same owner.
- The owner of merged partitions will be the owner of the new partition.
- It is the user's responsibility to setup ACL on
- the new partition.
-
-
-
- ALTER TABLE MERGE PARTITION uses the partitioned
- table itself as the template to construct the new partition.
- The new partition inherits the table access method and persistence type
- of the partitioned table. Its tablespace is selected as for a
- CREATE TABLE ... PARTITION OF command issued
- without a TABLESPACE clause: if the partitioned
- table has an explicit tablespace, the new partition uses it;
- otherwise the value of is
- taken into account, falling back to the database's default tablespace.
-
- Constraints, column defaults, column generation expressions, identity
- columns, indexes, and triggers are copied from the partitioned table to
- the new partition. But extended statistics, security policies, etc,
- won't be copied from the partitioned table.
- Indexes and identity columns copied from the partitioned table will be
- created afterward, once the data has been moved into the new partition.
-
-
-
- When partitions are merged, any objects depending on this partition,
- such as constraints, triggers, extended statistics, etc, will be
- dropped.
- Eventually, we will drop all the merged partitions
- (using RESTRICT mode) too; therefore, if any objects
- are still dependent on them,
- ALTER TABLE MERGE PARTITION would fail.
- (see ).
-
-
-
- Extension dependencies on partition indexes (created via
- ALTER INDEX ... DEPENDS ON
- EXTENSION) are preserved during merge operations.
- All source partition indexes must have the same extension dependencies;
- if they differ, an error is raised. This ensures that extension
- dependencies are not silently lost during merge.
-
-
-
-
- Merging partitions acquires an ACCESS EXCLUSIVE lock on
- the parent table, in addition to the ACCESS EXCLUSIVE
- locks on the tables being merged and on the default partition (if any).
-
-
-
-
- ALTER TABLE MERGE PARTITIONS creates a new partition and
- moves data from all merging partitions into it, which can take a long time.
- So it is not recommended to use the command to merge very big partitions
- with small ones.
-
-
-
-
-
-
-
- SPLIT PARTITION partition_name INTO (
- PARTITION partition_name1 { FOR VALUES partition_bound_spec | DEFAULT },
- PARTITION partition_name2 { FOR VALUES partition_bound_spec | DEFAULT }
- [, ...])
-
-
-
-
- This form splits a single partition of the target table into new
- partitions. Hash-partitioned target tables are not supported.
- Only a simple, non-partitioned partition can be split.
- If the split partition is the DEFAULT partition,
- one of the new partitions must be DEFAULT.
- If the partitioned table does not have a DEFAULT
- partition, a DEFAULT partition can be defined as one
- of the new partitions.
-
-
-
- The bounds of new non-DEFAULT partitions must not
- overlap with those of new or existing partitions, except
- partition_name, and must be
- contained within the bounds of the split partition
- partition_name.
- If no new DEFAULT partition is specified, the
- combined bounds of the new partitions
-
- partition_name1,
- partition_name2[, ...]
- must exactly match the bounds of the split partition
- partition_name.
- One of the new partitions can have the same name as the split partition
- partition_name.
- This is useful when splitting the DEFAULT partition,
- so that after the split, the DEFAULT partition
- keeps the same name but its partition bound changes.
-
-
-
- New partitions will have the same owner as the parent partition.
- It is the user's responsibility to setup ACL on new
- partitions.
-
-
-
- ALTER TABLE SPLIT PARTITION uses the partitioned
- table itself as the template to construct new partitions.
- New partitions inherit the table access method and persistence type of
- the partitioned table. Their tablespace is selected as for a
- CREATE TABLE ... PARTITION OF command issued
- without a TABLESPACE clause: if the partitioned
- table has an explicit tablespace, the new partitions use it;
- otherwise the value of is
- taken into account, falling back to the database's default tablespace.
-
-
-
- Constraints, column defaults, column generation expressions,
- identity columns, indexes, and triggers are copied from the partitioned
- table to the new partitions. But extended statistics, security
- policies, etc, won't be copied from the partitioned table.
- Indexes and identity columns copied from the partitioned table will be
- created afterward, once the data has been moved into the new partitions.
-
-
-
- When a partition is split, any objects that depend on this partition,
- such as constraints, triggers, extended statistics, etc, will be dropped.
- This occurs because ALTER TABLE SPLIT PARTITION uses
- the partitioned table itself as the template to reconstruct these
- objects later.
- Eventually, we will drop the split partition
- (using RESTRICT mode) too; therefore, if any objects
- are still dependent on it, ALTER TABLE SPLIT PARTITION
- would fail (see ).
-
-
-
- Extension dependencies on partition indexes (created via
- ALTER INDEX ... DEPENDS ON
- EXTENSION) are preserved during split operations.
- The new partitions' indexes will inherit the extension dependencies
- from the source partition's indexes.
-
-
-
-
- Split partition acquires an ACCESS EXCLUSIVE lock on
- the parent table, in addition to the ACCESS EXCLUSIVE
- lock on the table being split.
-
-
-
-
-
- ALTER TABLE SPLIT PARTITION creates new partitions and
- moves data from the split partition into them, which can take a long
- time. So it is not recommended to use the command for splitting a
- small fraction of rows out of a very big partition.
-
-
-
-
-
All the forms of ALTER TABLE that act on a single table,
except RENAME, SET SCHEMA,
- ATTACH PARTITION, DETACH PARTITION,
- MERGE PARTITIONS, and SPLIT PARTITION
- can be combined into
+ ATTACH PARTITION, and
+ DETACH PARTITION can be combined into
a list of multiple alterations to be applied together. For example, it
is possible to add several columns and/or alter the type of several
columns in a single command. This is particularly useful with large
- tables, since only one pass over the table needs to be made.
+ tables, since only one pass over the table need be made.
@@ -1656,19 +1429,7 @@ WITH ( MODULUS numeric_literal, REM
partition_name
- The name of the table to attach as a new partition or to detach from this table,
- or the name of split partition, or the name of the new merged partition.
-
-
-
-
-
- partition_name1
- partition_name2
-
-
- The names of the tables being merged into the new partition or split into
- new partitions.
+ The name of the table to attach as a new partition or to detach from this table.
@@ -2101,31 +1862,6 @@ ALTER TABLE measurement
DETACH PARTITION measurement_y2015m12;
-
- To split a single partition of the range-partitioned table:
-
-ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2023 INTO
- (PARTITION sales_feb2023 FOR VALUES FROM ('2023-02-01') TO ('2023-03-01'),
- PARTITION sales_mar2023 FOR VALUES FROM ('2023-03-01') TO ('2023-04-01'),
- PARTITION sales_apr2023 FOR VALUES FROM ('2023-04-01') TO ('2023-05-01'));
-
-
-
- To split a single partition of the list-partitioned table:
-
-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 ('Beijing', 'Delhi', 'Vladivostok'),
- PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv'));
-
-
-
- To merge several partitions into one partition of the target table:
-
-ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_central)
- INTO sales_all;
-
-
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 026b743275f..40114d2ddb3 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -326,63 +326,13 @@ performDeletion(const ObjectAddress *object,
}
/*
- * performDeletionCheck: Check whether a specific object can be safely deleted.
- * This function does not perform any deletion; instead, it raises an error
- * if the object cannot be deleted due to existing dependencies.
- *
- * It can be useful when you need to delete some objects later. See comments
- * in performDeletion too.
- * The behavior must be specified as DROP_RESTRICT.
- */
-void
-performDeletionCheck(const ObjectAddress *object,
- DropBehavior behavior, int flags)
-{
- Relation depRel;
- ObjectAddresses *targetObjects;
-
- Assert(behavior == DROP_RESTRICT);
-
- depRel = table_open(DependRelationId, RowExclusiveLock);
-
- AcquireDeletionLock(object, 0);
-
- /*
- * Construct a list of objects we want to delete later (ie, the given
- * object plus everything directly or indirectly dependent on it).
- */
- targetObjects = new_object_addresses();
-
- findDependentObjects(object,
- DEPFLAG_ORIGINAL,
- flags,
- NULL, /* empty stack */
- targetObjects,
- NULL, /* no pendingObjects */
- &depRel);
-
- /*
- * Check if deletion is allowed.
- */
- reportDependentObjects(targetObjects,
- behavior,
- flags,
- object);
-
- /* And clean up */
- free_object_addresses(targetObjects);
-
- table_close(depRel, RowExclusiveLock);
-}
-
-/*
- * performMultipleDeletions: Similar to performDeletion, but acts on multiple
+ * performMultipleDeletions: Similar to performDeletion, but act on multiple
* objects at once.
*
* The main difference from issuing multiple performDeletion calls is that the
* list of objects that would be implicitly dropped, for each object to be
* dropped, is the union of the implicit-object list for all objects. This
- * makes each check more relaxed.
+ * makes each check be more relaxed.
*/
void
performMultipleDeletions(const ObjectAddresses *objects,
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 8aba9cbbc57..c2fcc81a24a 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -895,7 +895,7 @@ RelationGetNotNullConstraints(Oid relid, bool cooked, bool include_noinh)
false)));
constr->is_enforced = true;
constr->skip_validation = !conForm->convalidated;
- constr->initially_valid = conForm->convalidated;
+ constr->initially_valid = true;
constr->is_no_inherit = conForm->connoinherit;
notnulls = lappend(notnulls, constr);
}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index cad394b3540..2680acf4d94 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -40,7 +40,6 @@
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
-#include "catalog/pg_extension_d.h"
#include "catalog/pg_foreign_table.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_largeobject.h"
@@ -61,7 +60,6 @@
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
-#include "commands/extension.h"
#include "commands/repack.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
@@ -368,27 +366,6 @@ typedef enum addFkConstraintSides
addFkBothSides,
} addFkConstraintSides;
-/*
- * Hold extension dependencies of one partition index, during
- * MERGE/SPLIT PARTITION processing.
- *
- * collectPartitionIndexExtDeps() builds a list of these entries sorted by
- * parentIndexOid with exactly one entry per parent partitioned index; the
- * list is then consumed by applyPartitionIndexExtDeps() to re-record the
- * same dependencies on the newly created partition's indexes.
- *
- * extensionOids is kept sorted ascending so that equality checks between
- * entries from different partitions can be done in a single pass.
- * indexOid is carried only so that conflict errors can cite specific
- * partition index names.
- */
-typedef struct PartitionIndexExtDepEntry
-{
- Oid parentIndexOid; /* OID of the parent partitioned index */
- Oid indexOid; /* OID of a representative partition index */
- List *extensionOids; /* OIDs of dependent extensions, sorted asc */
-} PartitionIndexExtDepEntry;
-
/*
* Partition tables are expected to be dropped when the parent partitioned
* table gets dropped. Hence for partitioning we use AUTO dependency.
@@ -787,14 +764,6 @@ static void ATDetachCheckNoForeignKeyRefs(Relation partition);
static char GetAttributeCompression(Oid atttypid, const char *compression);
static char GetAttributeStorage(Oid atttypid, const char *storagemode);
-static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
- PartitionCmd *cmd, AlterTableUtilityContext *context);
-static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
- Relation rel, PartitionCmd *cmd,
- AlterTableUtilityContext *context);
-static List *collectPartitionIndexExtDeps(List *partitionOids);
-static void applyPartitionIndexExtDeps(Oid newPartOid, List *extDepState);
-static void freePartitionIndexExtDeps(List *extDepState);
/* ----------------------------------------------------------------
* DefineRelation
@@ -4918,11 +4887,6 @@ AlterTableGetLockLevel(List *cmds)
cmd_lockmode = ShareUpdateExclusiveLock;
break;
- case AT_MergePartitions:
- case AT_SplitPartition:
- cmd_lockmode = AccessExclusiveLock;
- break;
-
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -5358,12 +5322,6 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
- case AT_MergePartitions:
- case AT_SplitPartition:
- ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
- /* No command-specific prep needed */
- pass = AT_PASS_MISC;
- break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -5760,22 +5718,6 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
case AT_DetachPartitionFinalize:
address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
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;
- 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;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -6818,10 +6760,6 @@ alter_table_type_to_string(AlterTableType cmdtype)
return "DETACH PARTITION";
case AT_DetachPartitionFinalize:
return "DETACH PARTITION ... FINALIZE";
- case AT_MergePartitions:
- return "MERGE PARTITIONS";
- case AT_SplitPartition:
- return "SPLIT PARTITION";
case AT_AddIdentity:
return "ALTER COLUMN ... ADD IDENTITY";
case AT_SetIdentity:
@@ -20948,40 +20886,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)
-{
- /*
- * Create an inheritance; the relevant checks are performed inside the
- * function.
- */
- 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 ATTACH PARTITION FOR VALUES
*
@@ -21221,10 +21125,26 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
check_new_partition_bound(RelationGetRelationName(attachrel), rel,
cmd->bound, pstate);
- 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);
/*
- * Generate a partition constraint from the partition bound specification.
+ * 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.
* If the parent itself is a partition, make sure to include its
* constraint as well.
*/
@@ -22832,1536 +22752,3 @@ GetAttributeStorage(Oid atttypid, const char *storagemode)
return cstorage;
}
-
-/*
- * buildExpressionExecutionStates: build the needed expression execution states
- * for new partition (newPartRel) checks and initialize expressions for
- * generated columns. All expressions should be created in "tab"
- * (AlteredTableInfo structure).
- */
-static void
-buildExpressionExecutionStates(AlteredTableInfo *tab, Relation newPartRel, EState *estate)
-{
- /*
- * Build the needed expression execution states. Here, we expect only NOT
- * NULL and CHECK constraint.
- */
- foreach_ptr(NewConstraint, con, tab->constraints)
- {
- switch (con->contype)
- {
- case CONSTR_CHECK:
-
- /*
- * We already expanded virtual expression in
- * createTableConstraints.
- */
- con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
- break;
- case CONSTR_NOTNULL:
- /* Nothing to do here. */
- break;
- default:
- elog(ERROR, "unrecognized constraint type: %d",
- (int) con->contype);
- }
- }
-
- /* Expression already planned in createTableConstraints */
- foreach_ptr(NewColumnValue, ex, tab->newvals)
- ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
-}
-
-/*
- * evaluateGeneratedExpressionsAndCheckConstraints: evaluate any generated
- * expressions for "tab" (AlteredTableInfo structure) whose inputs come from
- * the new tuple (insertslot) of the new partition (newPartRel).
- */
-static void
-evaluateGeneratedExpressionsAndCheckConstraints(AlteredTableInfo *tab,
- Relation newPartRel,
- TupleTableSlot *insertslot,
- ExprContext *econtext)
-{
- econtext->ecxt_scantuple = insertslot;
-
- foreach_ptr(NewColumnValue, ex, tab->newvals)
- {
- if (!ex->is_generated)
- continue;
-
- insertslot->tts_values[ex->attnum - 1]
- = ExecEvalExpr(ex->exprstate,
- econtext,
- &insertslot->tts_isnull[ex->attnum - 1]);
- }
-
- foreach_ptr(NewConstraint, con, tab->constraints)
- {
- switch (con->contype)
- {
- case CONSTR_CHECK:
- if (!ExecCheck(con->qualstate, econtext))
- ereport(ERROR,
- errcode(ERRCODE_CHECK_VIOLATION),
- errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
- con->name, RelationGetRelationName(newPartRel)),
- errtableconstraint(newPartRel, con->name));
- break;
- case CONSTR_NOTNULL:
- case CONSTR_FOREIGN:
- /* Nothing to do here */
- break;
- default:
- elog(ERROR, "unrecognized constraint type: %d",
- (int) con->contype);
- }
- }
-}
-
-/*
- * getAttributesList: build a list of columns (ColumnDef) based on parent_rel
- */
-static List *
-getAttributesList(Relation parent_rel)
-{
- AttrNumber parent_attno;
- TupleDesc modelDesc;
- List *colList = NIL;
-
- modelDesc = RelationGetDescr(parent_rel);
-
- for (parent_attno = 1; parent_attno <= modelDesc->natts;
- parent_attno++)
- {
- Form_pg_attribute attribute = TupleDescAttr(modelDesc,
- parent_attno - 1);
- ColumnDef *def;
-
- /* Ignore dropped columns in the parent. */
- if (attribute->attisdropped)
- continue;
-
- def = makeColumnDef(NameStr(attribute->attname), attribute->atttypid,
- attribute->atttypmod, attribute->attcollation);
-
- def->is_not_null = attribute->attnotnull;
-
- /* Copy identity. */
- def->identity = attribute->attidentity;
-
- /* Copy attgenerated. */
- def->generated = attribute->attgenerated;
-
- def->storage = attribute->attstorage;
-
- /* Likewise, copy compression. */
- if (CompressionMethodIsValid(attribute->attcompression))
- def->compression =
- pstrdup(GetCompressionMethodName(attribute->attcompression));
- else
- def->compression = NULL;
-
- /* Add to column list. */
- colList = lappend(colList, def);
- }
-
- return colList;
-}
-
-/*
- * createTableConstraints:
- * create check constraints, default values, and generated values for newRel
- * based on parent_rel. tab is pending-work queue for newRel, we may need it in
- * MergePartitionsMoveRows.
- */
-static void
-createTableConstraints(List **wqueue, AlteredTableInfo *tab,
- Relation parent_rel, Relation newRel)
-{
- TupleDesc tupleDesc;
- TupleConstr *constr;
- AttrMap *attmap;
- AttrNumber parent_attno;
- int ccnum;
- List *constraints = NIL;
- List *cookedConstraints = NIL;
-
- tupleDesc = RelationGetDescr(parent_rel);
- constr = tupleDesc->constr;
-
- if (!constr)
- return;
-
- /*
- * Construct a map from the parent relation's attnos to the child rel's.
- * This re-checks type match, etc, although it shouldn't be possible to
- * have a failure since both tables are locked.
- */
- attmap = build_attrmap_by_name(RelationGetDescr(newRel),
- tupleDesc,
- false);
-
- /* Cycle for default values. */
- for (parent_attno = 1; parent_attno <= tupleDesc->natts; parent_attno++)
- {
- Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
- parent_attno - 1);
-
- /* Ignore dropped columns in the parent. */
- if (attribute->attisdropped)
- continue;
-
- /* Copy the default, if present, and it should be copied. */
- if (attribute->atthasdef)
- {
- Node *this_default = NULL;
- bool found_whole_row;
- AttrNumber num;
- Node *def;
- NewColumnValue *newval;
-
- if (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
- this_default = build_generation_expression(parent_rel, attribute->attnum);
- else
- {
- this_default = TupleDescGetDefault(tupleDesc, attribute->attnum);
- if (this_default == NULL)
- elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
- attribute->attnum, RelationGetRelationName(parent_rel));
- }
-
- num = attmap->attnums[parent_attno - 1];
- def = map_variable_attnos(this_default, 1, 0, attmap, InvalidOid, &found_whole_row);
-
- if (found_whole_row && attribute->attgenerated != '\0')
- elog(ERROR, "cannot convert whole-row table reference");
-
- /* Add a pre-cooked default expression. */
- StoreAttrDefault(newRel, num, def, false);
-
- /*
- * Stored generated column expressions in parent_rel might
- * reference the tableoid. newRel, parent_rel tableoid clear is
- * not the same. If so, these stored generated columns require
- * recomputation for newRel within MergePartitionsMoveRows.
- */
- if (attribute->attgenerated == ATTRIBUTE_GENERATED_STORED)
- {
- newval = palloc0_object(NewColumnValue);
- newval->attnum = num;
- newval->expr = expression_planner((Expr *) def);
- newval->is_generated = (attribute->attgenerated != '\0');
- tab->newvals = lappend(tab->newvals, newval);
- }
- }
- }
-
- /* Cycle for CHECK constraints. */
- for (ccnum = 0; ccnum < constr->num_check; ccnum++)
- {
- char *ccname = constr->check[ccnum].ccname;
- char *ccbin = constr->check[ccnum].ccbin;
- bool ccenforced = constr->check[ccnum].ccenforced;
- bool ccnoinherit = constr->check[ccnum].ccnoinherit;
- bool ccvalid = constr->check[ccnum].ccvalid;
- Node *ccbin_node;
- bool found_whole_row;
- Constraint *con;
-
- /*
- * The partitioned table can not have a NO INHERIT check constraint
- * (see StoreRelCheck function for details).
- */
- Assert(!ccnoinherit);
-
- ccbin_node = map_variable_attnos(stringToNode(ccbin),
- 1, 0,
- attmap,
- InvalidOid, &found_whole_row);
-
- /*
- * For the moment we have to reject whole-row variables (as for CREATE
- * TABLE LIKE and inheritances).
- */
- if (found_whole_row)
- elog(ERROR, "Constraint \"%s\" contains a whole-row reference to table \"%s\".",
- ccname,
- RelationGetRelationName(parent_rel));
-
- con = makeNode(Constraint);
- con->contype = CONSTR_CHECK;
- con->conname = pstrdup(ccname);
- con->deferrable = false;
- con->initdeferred = false;
- con->is_enforced = ccenforced;
- con->skip_validation = !ccvalid;
- con->initially_valid = ccvalid;
- con->is_no_inherit = ccnoinherit;
- con->raw_expr = NULL;
- con->cooked_expr = nodeToString(ccbin_node);
- con->location = -1;
- constraints = lappend(constraints, con);
- }
-
- /* Install all CHECK constraints. */
- cookedConstraints = AddRelationNewConstraints(newRel, NIL, constraints,
- false, true, false, NULL);
-
- /* Make the additional catalog changes visible. */
- CommandCounterIncrement();
-
- /*
- * parent_rel check constraint expression may reference tableoid, so later
- * in MergePartitionsMoveRows, we need to evaluate the check constraint
- * again for the newRel. We can check whether the check constraint
- * contains a tableoid reference via pull_varattnos.
- */
- foreach_ptr(CookedConstraint, ccon, cookedConstraints)
- {
- if (!ccon->skip_validation)
- {
- Node *qual;
- Bitmapset *attnums = NULL;
-
- Assert(ccon->contype == CONSTR_CHECK);
- qual = expand_generated_columns_in_expr(ccon->expr, newRel, 1);
- pull_varattnos(qual, 1, &attnums);
-
- /*
- * Add a check only if it contains a tableoid
- * (TableOidAttributeNumber).
- */
- if (bms_is_member(TableOidAttributeNumber - FirstLowInvalidHeapAttributeNumber,
- attnums))
- {
- NewConstraint *newcon;
-
- newcon = palloc0_object(NewConstraint);
- newcon->name = ccon->name;
- newcon->contype = CONSTR_CHECK;
- newcon->qual = qual;
-
- tab->constraints = lappend(tab->constraints, newcon);
- }
- }
- }
-
- /* Don't need the cookedConstraints anymore. */
- list_free_deep(cookedConstraints);
-
- /* Reproduce not-null constraints. */
- if (constr->has_not_null)
- {
- List *nnconstraints;
-
- /*
- * The "include_noinh" argument is false because a partitioned table
- * can't have NO INHERIT constraint.
- */
- nnconstraints = RelationGetNotNullConstraints(RelationGetRelid(parent_rel),
- false, false);
-
- Assert(list_length(nnconstraints) > 0);
-
- /*
- * We already set pg_attribute.attnotnull in createPartitionTable. No
- * need call set_attnotnull again.
- */
- AddRelationNewConstraints(newRel, NIL, nnconstraints, false, true, false, NULL);
- }
-}
-
-/*
- * createPartitionTable:
- *
- * Create a new partition (newPartName) for the partitioned table (parent_rel).
- * ownerId is determined by the partition on which the operation is performed,
- * so it is passed separately. The new partition will inherit the access method
- * and persistence type from the parent table.
- *
- * Returns the created relation (locked in AccessExclusiveLock mode).
- */
-static Relation
-createPartitionTable(List **wqueue, RangeVar *newPartName,
- Relation parent_rel, Oid ownerId)
-{
- Relation newRel;
- Oid newRelId;
- Oid existingRelid;
- Oid tablespaceId;
- TupleDesc descriptor;
- List *colList = NIL;
- Oid relamId;
- Oid namespaceId;
- AlteredTableInfo *new_partrel_tab;
- Form_pg_class parent_relform = parent_rel->rd_rel;
-
- /* If the existing rel is temp, it must belong to this session. */
- if (RELATION_IS_OTHER_TEMP(parent_rel))
- ereport(ERROR,
- errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot create as partition of temporary relation of another session"));
-
- /* Look up inheritance ancestors and generate the relation schema. */
- colList = getAttributesList(parent_rel);
-
- /* Create a tuple descriptor from the relation schema. */
- descriptor = BuildDescForRelation(colList);
-
- /* Look up the access method for the new relation. */
- relamId = (parent_relform->relam != InvalidOid) ? parent_relform->relam : HEAP_TABLE_AM_OID;
-
- /* Look up the namespace in which we are supposed to create the relation. */
- namespaceId =
- RangeVarGetAndCheckCreationNamespace(newPartName, NoLock, &existingRelid);
- if (OidIsValid(existingRelid))
- ereport(ERROR,
- errcode(ERRCODE_DUPLICATE_TABLE),
- errmsg("relation \"%s\" already exists", newPartName->relname));
-
- /*
- * 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 (parent_relform->relpersistence != RELPERSISTENCE_TEMP &&
- newPartName->relpersistence == RELPERSISTENCE_TEMP)
- ereport(ERROR,
- errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
- RelationGetRelationName(parent_rel)));
-
- /* Permanent rels cannot be partitions belonging to a temporary parent. */
- if (newPartName->relpersistence != RELPERSISTENCE_TEMP &&
- parent_relform->relpersistence == RELPERSISTENCE_TEMP)
- ereport(ERROR,
- errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
- RelationGetRelationName(parent_rel)));
-
- /*
- * Select the tablespace for the new partition. Mirror the logic that
- * CREATE TABLE foo PARTITION OF ... uses in DefineRelation: take the
- * partitioned parent's explicit tablespace if it has one, otherwise take
- * default_tablespace into account, and finally use the database default.
- */
- tablespaceId = parent_relform->reltablespace;
- if (!OidIsValid(tablespaceId))
- tablespaceId = GetDefaultTablespace(newPartName->relpersistence, false);
-
- /* Check permissions except when using database's default */
- if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
- {
- AclResult aclresult;
-
- aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId,
- GetUserId(), ACL_CREATE);
- if (aclresult != ACLCHECK_OK)
- aclcheck_error(aclresult, OBJECT_TABLESPACE,
- get_tablespace_name(tablespaceId));
- }
-
- /* In all cases disallow placing user relations in pg_global */
- if (tablespaceId == GLOBALTABLESPACE_OID)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("only shared relations can be placed in pg_global tablespace")));
-
- /* Create the relation. */
- newRelId = heap_create_with_catalog(newPartName->relname,
- namespaceId,
- tablespaceId,
- InvalidOid,
- InvalidOid,
- InvalidOid,
- ownerId,
- relamId,
- descriptor,
- NIL,
- RELKIND_RELATION,
- newPartName->relpersistence,
- false,
- false,
- ONCOMMIT_NOOP,
- (Datum) 0,
- true,
- allowSystemTableMods,
- false, /* is_internal */
- InvalidOid,
- NULL);
-
- /*
- * We must bump the command counter to make the newly-created relation
- * tuple visible for opening.
- */
- CommandCounterIncrement();
-
- /*
- * Create a TOAST table if the table needs one. MERGE/SPLIT PARTITION
- * moves rows from existing partition(s) into new partition(s), which may
- * carry out-of-line varlena values that the new relation must be able to
- * store. Also, the new partition must be able to receive out-of-line
- * varlena values after the DDL operation is complete.
- */
- NewRelationCreateToastTable(newRelId, (Datum) 0);
-
- /*
- * Open the new partition with no lock, because we already have an
- * AccessExclusiveLock placed there after creation.
- */
- newRel = table_open(newRelId, NoLock);
-
- /* Find or create a work queue entry for the newly created table. */
- new_partrel_tab = ATGetQueueEntry(wqueue, newRel);
-
- /* Create constraints, default values, and generated values. */
- createTableConstraints(wqueue, new_partrel_tab, parent_rel, newRel);
-
- /*
- * Need to call CommandCounterIncrement, so a fresh relcache entry has
- * newly installed constraint info.
- */
- CommandCounterIncrement();
-
- return newRel;
-}
-
-/*
- * MergePartitionsMoveRows: scan partitions to be merged (mergingPartitions)
- * of the partitioned table and move rows into the new partition
- * (newPartRel). We also verify check constraints against these rows.
- */
-static void
-MergePartitionsMoveRows(List **wqueue, List *mergingPartitions, Relation newPartRel)
-{
- CommandId mycid;
- EState *estate;
- AlteredTableInfo *tab;
- ListCell *ltab;
-
- /* The FSM is empty, so don't bother using it. */
- uint32 ti_options = TABLE_INSERT_SKIP_FSM;
- BulkInsertState bistate; /* state of bulk inserts for partition */
- TupleTableSlot *dstslot;
-
- /* Find the work queue entry for the new partition table: newPartRel. */
- tab = ATGetQueueEntry(wqueue, newPartRel);
-
- /* Generate the constraint and default execution states. */
- estate = CreateExecutorState();
-
- buildExpressionExecutionStates(tab, newPartRel, estate);
-
- mycid = GetCurrentCommandId(true);
-
- /* Prepare a BulkInsertState for table_tuple_insert. */
- bistate = GetBulkInsertState();
-
- /* Create the necessary tuple slot. */
- dstslot = table_slot_create(newPartRel, NULL);
-
- foreach_oid(merging_oid, mergingPartitions)
- {
- ExprContext *econtext;
- TupleTableSlot *srcslot;
- TupleConversionMap *tuple_map;
- TableScanDesc scan;
- MemoryContext oldCxt;
- Snapshot snapshot;
- Relation mergingPartition;
-
- econtext = GetPerTupleExprContext(estate);
-
- /*
- * Partition is already locked in the transformPartitionCmdForMerge
- * function.
- */
- mergingPartition = table_open(merging_oid, NoLock);
-
- /* Create a source tuple slot for the partition being merged. */
- srcslot = table_slot_create(mergingPartition, NULL);
-
- /*
- * Map computing for moving attributes of the merged partition to the
- * 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,
- SO_NONE);
-
- /*
- * 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))
- {
- TupleTableSlot *insertslot;
-
- CHECK_FOR_INTERRUPTS();
-
- if (tuple_map)
- {
- /* Need to use a map to copy attributes. */
- insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
- }
- else
- {
- slot_getallattrs(srcslot);
-
- /* 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);
- }
-
- /*
- * Constraints and GENERATED expressions might reference the
- * tableoid column, so fill tts_tableOid with the desired value.
- * (We must do this each time, because it gets overwritten with
- * newrel's OID during storing.)
- */
- insertslot->tts_tableOid = RelationGetRelid(newPartRel);
-
- /*
- * Now, evaluate any generated expressions whose inputs come from
- * the new tuple. We assume these columns won't reference each
- * other, so that there's no ordering dependency.
- */
- evaluateGeneratedExpressionsAndCheckConstraints(tab, newPartRel,
- insertslot, econtext);
-
- /* Write the tuple out to the new relation. */
- table_tuple_insert(newPartRel, insertslot, mycid,
- ti_options, bistate);
-
- ResetExprContext(econtext);
- }
-
- MemoryContextSwitchTo(oldCxt);
- table_endscan(scan);
- UnregisterSnapshot(snapshot);
-
- if (tuple_map)
- free_conversion_map(tuple_map);
-
- ExecDropSingleTupleTableSlot(srcslot);
- table_close(mergingPartition, NoLock);
- }
-
- FreeExecutorState(estate);
- ExecDropSingleTupleTableSlot(dstslot);
- FreeBulkInsertState(bistate);
-
- table_finish_bulk_insert(newPartRel, ti_options);
-
- /*
- * We don't need to process this newPartRel since we already processed it
- * here, so delete the ALTER TABLE queue for it.
- */
- foreach(ltab, *wqueue)
- {
- tab = (AlteredTableInfo *) lfirst(ltab);
- if (tab->relid == RelationGetRelid(newPartRel))
- {
- *wqueue = list_delete_cell(*wqueue, ltab);
- break;
- }
- }
-}
-
-/*
- * detachPartitionTable: detach partition "child_rel" from partitioned table
- * "parent_rel" with default partition identifier "defaultPartOid"
- */
-static void
-detachPartitionTable(Relation parent_rel, Relation child_rel, Oid defaultPartOid)
-{
- /* Remove the pg_inherits row first. */
- RemoveInheritance(child_rel, parent_rel, false);
-
- /*
- * Detaching the partition might involve TOAST table access, so ensure we
- * have a valid snapshot.
- */
- PushActiveSnapshot(GetTransactionSnapshot());
-
- /* Do the final part of detaching. */
- DetachPartitionFinalize(parent_rel, child_rel, false, defaultPartOid);
-
- PopActiveSnapshot();
-}
-
-/*
- * equal_oid_lists: return true if two OID lists, each sorted in ascending
- * order, contain the same OIDs in the same order.
- */
-static bool
-equal_oid_lists(const List *a, const List *b)
-{
- ListCell *la,
- *lb;
-
- if (list_length(a) != list_length(b))
- return false;
-
- forboth(la, a, lb, b)
- {
- if (lfirst_oid(la) != lfirst_oid(lb))
- return false;
- }
- return true;
-}
-
-/*
- * Comparator for list_sort() on a list of PartitionIndexExtDepEntry *.
- * Orders by parentIndexOid, then by indexOid as a tiebreaker so conflict
- * reports for different parent indexes are deterministic.
- */
-static int
-cmp_partition_index_ext_dep(const ListCell *a, const ListCell *b)
-{
- const PartitionIndexExtDepEntry *ea = lfirst(a);
- const PartitionIndexExtDepEntry *eb = lfirst(b);
-
- if (ea->parentIndexOid != eb->parentIndexOid)
- return pg_cmp_u32(ea->parentIndexOid, eb->parentIndexOid);
- return pg_cmp_u32(ea->indexOid, eb->indexOid);
-}
-
-/*
- * collectPartitionIndexExtDeps: collect extension dependencies from indexes
- * on the given partitions.
- *
- * For each partition index that has a parent partitioned index, we collect
- * extension dependencies. All source partition indexes sharing the same
- * parent partitioned index must depend on exactly the same set of
- * extensions; otherwise an error is raised so that we neither silently drop
- * nor silently add dependencies on the merged partition's index.
- *
- * Indexes that don't have a parent partitioned index (i.e., indexes created
- * directly on a partition without a corresponding parent index) are skipped.
- *
- * The returned list is sorted by parentIndexOid with exactly one entry per
- * parent partitioned index, so applyPartitionIndexExtDeps() can scan it
- * linearly.
- */
-static List *
-collectPartitionIndexExtDeps(List *partitionOids)
-{
- List *collected = NIL;
- List *result = NIL;
- PartitionIndexExtDepEntry *prev = NULL;
-
- /*
- * Phase 1: collect one entry per (partition index -> parent index) pair,
- * with its extension dependency OIDs sorted ascending.
- */
- foreach_oid(partOid, partitionOids)
- {
- Relation partRel;
- List *indexList;
-
- /*
- * Use NoLock since the caller already holds AccessExclusiveLock on
- * these partitions.
- */
- partRel = table_open(partOid, NoLock);
- indexList = RelationGetIndexList(partRel);
-
- foreach_oid(indexOid, indexList)
- {
- Oid parentIndexOid;
- PartitionIndexExtDepEntry *entry;
-
- if (!get_rel_relispartition(indexOid))
- continue;
-
- parentIndexOid = get_partition_parent(indexOid, true);
- if (!OidIsValid(parentIndexOid))
- continue;
-
- entry = palloc(sizeof(PartitionIndexExtDepEntry));
- entry->parentIndexOid = parentIndexOid;
- entry->indexOid = indexOid;
- entry->extensionOids = getAutoExtensionsOfObject(RelationRelationId,
- indexOid);
- list_sort(entry->extensionOids, list_oid_cmp);
-
- collected = lappend(collected, entry);
- }
-
- list_free(indexList);
- table_close(partRel, NoLock);
- }
-
- /*
- * Phase 2: sort by parentIndexOid so entries sharing a parent index sit
- * adjacent.
- */
- list_sort(collected, cmp_partition_index_ext_dep);
-
- /*
- * Phase 3: single linear pass verifying that adjacent entries sharing a
- * parent index have identical extension dependencies, and keeping one
- * representative entry per parent index.
- */
- foreach_ptr(PartitionIndexExtDepEntry, entry, collected)
- {
- if (prev != NULL && prev->parentIndexOid == entry->parentIndexOid)
- {
- if (!equal_oid_lists(prev->extensionOids, entry->extensionOids))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot merge partitions with conflicting extension dependencies"),
- errdetail("Partition indexes \"%s\" and \"%s\" depend on different extensions.",
- get_rel_name(prev->indexOid),
- get_rel_name(entry->indexOid))));
-
- /* Duplicate entry for the same parent index; discard. */
- list_free(entry->extensionOids);
- pfree(entry);
- continue;
- }
-
- result = lappend(result, entry);
- prev = entry;
- }
-
- list_free(collected);
-
- return result;
-}
-
-/*
- * applyPartitionIndexExtDeps: apply collected extension dependencies to
- * indexes on a new partition.
- *
- * For each index on the new partition, look up its parent index in the
- * extDepState list. If found, record extension dependencies on the new index.
- * extDepState is sorted by parentIndexOid, so the inner scan can bail out
- * as soon as it passes the target OID.
- */
-static void
-applyPartitionIndexExtDeps(Oid newPartOid, List *extDepState)
-{
- Relation partRel;
- List *indexList;
-
- if (extDepState == NIL)
- return;
-
- /*
- * Use NoLock since the caller already holds AccessExclusiveLock on the
- * new partition.
- */
- partRel = table_open(newPartOid, NoLock);
- indexList = RelationGetIndexList(partRel);
-
- foreach_oid(indexOid, indexList)
- {
- Oid parentIdxOid;
-
- if (!get_rel_relispartition(indexOid))
- continue;
-
- parentIdxOid = get_partition_parent(indexOid, true);
- if (!OidIsValid(parentIdxOid))
- continue;
-
- foreach_ptr(PartitionIndexExtDepEntry, entry, extDepState)
- {
- ObjectAddress indexAddr;
-
- if (entry->parentIndexOid > parentIdxOid)
- break;
- if (entry->parentIndexOid < parentIdxOid)
- continue;
-
- ObjectAddressSet(indexAddr, RelationRelationId, indexOid);
-
- foreach_oid(extOid, entry->extensionOids)
- {
- ObjectAddress extAddr;
-
- ObjectAddressSet(extAddr, ExtensionRelationId, extOid);
- recordDependencyOn(&indexAddr, &extAddr,
- DEPENDENCY_AUTO_EXTENSION);
- }
- break;
- }
- }
-
- list_free(indexList);
- table_close(partRel, NoLock);
-}
-
-/*
- * freePartitionIndexExtDeps: free memory allocated by collectPartitionIndexExtDeps.
- */
-static void
-freePartitionIndexExtDeps(List *extDepState)
-{
- foreach_ptr(PartitionIndexExtDepEntry, entry, extDepState)
- {
- list_free(entry->extensionOids);
- pfree(entry);
- }
- list_free(extDepState);
-}
-
-/*
- * ALTER TABLE MERGE PARTITIONS INTO
- */
-static void
-ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
- PartitionCmd *cmd, AlterTableUtilityContext *context)
-{
- Relation newPartRel;
- List *mergingPartitions = NIL;
- List *extDepState = NIL;
- Oid defaultPartOid;
- Oid existingRelid;
- Oid ownerId = InvalidOid;
- Oid save_userid;
- int save_sec_context;
- int save_nestlevel;
-
- /*
- * Check ownership of merged partitions - partitions with different owners
- * cannot be merged. Also, collect the OIDs of these partitions during the
- * check.
- */
- foreach_node(RangeVar, name, cmd->partlist)
- {
- Relation mergingPartition;
-
- /*
- * We are going to detach and remove this partition. We already took
- * AccessExclusiveLock lock on transformPartitionCmdForMerge, so here,
- * NoLock is fine.
- */
- mergingPartition = table_openrv_extended(name, NoLock, false);
- Assert(CheckRelationLockedByMe(mergingPartition, AccessExclusiveLock, false));
-
- if (OidIsValid(ownerId))
- {
- /* Do the partitions being merged have different owners? */
- if (ownerId != mergingPartition->rd_rel->relowner)
- ereport(ERROR,
- errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("partitions being merged have different owners"));
- }
- else
- ownerId = mergingPartition->rd_rel->relowner;
-
- /* Store the next merging partition into the list. */
- mergingPartitions = lappend_oid(mergingPartitions,
- RelationGetRelid(mergingPartition));
-
- table_close(mergingPartition, NoLock);
- }
-
- /* Look up the existing relation by the new partition name. */
- RangeVarGetAndCheckCreationNamespace(cmd->name, NoLock, &existingRelid);
-
- /*
- * 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.
- */
- if (OidIsValid(existingRelid))
- {
- if (list_member_oid(mergingPartitions, existingRelid))
- {
- /*
- * The new partition has the same name as one of the merging
- * partitions.
- */
- char tmpRelName[NAMEDATALEN];
-
- /* Generate a 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(existingRelid, tmpRelName, true, 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));
- }
- }
-
- defaultPartOid =
- get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
-
- /*
- * Collect extension dependencies from indexes on the merging partitions.
- * We must do this before detaching them, so we can restore the
- * dependencies on the new partition's indexes later.
- */
- extDepState = collectPartitionIndexExtDeps(mergingPartitions);
-
- /* Detach all merging partitions. */
- foreach_oid(mergingPartitionOid, mergingPartitions)
- {
- Relation child_rel;
-
- child_rel = table_open(mergingPartitionOid, NoLock);
-
- detachPartitionTable(rel, child_rel, defaultPartOid);
-
- table_close(child_rel, NoLock);
- }
-
- /*
- * Perform a preliminary check to determine whether it's safe to drop all
- * merging partitions before we actually do so later. After merging rows
- * into the new partitions via MergePartitionsMoveRows, all old partitions
- * need to be dropped. However, since the drop behavior is DROP_RESTRICT
- * and the merge process (MergePartitionsMoveRows) can be time-consuming,
- * performing an early check on the drop eligibility of old partitions is
- * preferable.
- */
- foreach_oid(mergingPartitionOid, mergingPartitions)
- {
- ObjectAddress object;
-
- /* Get oid of the later to be dropped relation. */
- object.objectId = mergingPartitionOid;
- object.classId = RelationRelationId;
- object.objectSubId = 0;
-
- performDeletionCheck(&object, DROP_RESTRICT, 0);
- }
-
- /*
- * Create a table for the new partition, using the partitioned table as a
- * model.
- */
- Assert(OidIsValid(ownerId));
- newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId);
-
- /*
- * Switch to the table owner's userid, so that any index functions are run
- * as that user. Also, lockdown security-restricted operations and
- * arrange to make GUC variable changes local to this command.
- *
- * Need to do it after determining the namespace in the
- * createPartitionTable() call.
- */
- GetUserIdAndSecContext(&save_userid, &save_sec_context);
- SetUserIdAndSecContext(ownerId,
- save_sec_context | SECURITY_RESTRICTED_OPERATION);
- save_nestlevel = NewGUCNestLevel();
- RestrictSearchPath();
-
- /* Copy data from merged partitions to the new partition. */
- MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel);
-
- /* Drop the current partitions before attaching the new one. */
- foreach_oid(mergingPartitionOid, mergingPartitions)
- {
- ObjectAddress object;
-
- object.objectId = mergingPartitionOid;
- object.classId = RelationRelationId;
- object.objectSubId = 0;
-
- performDeletion(&object, DROP_RESTRICT, 0);
- }
-
- list_free(mergingPartitions);
-
- /*
- * Attach a new partition to the partitioned table. wqueue = NULL:
- * verification for each cloned constraint is not needed.
- */
- attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
-
- /*
- * Apply extension dependencies to the new partition's indexes. This
- * preserves any "DEPENDS ON EXTENSION" settings from the merged
- * partitions.
- */
- applyPartitionIndexExtDeps(RelationGetRelid(newPartRel), extDepState);
-
- freePartitionIndexExtDeps(extDepState);
-
- /* Keep the lock until commit. */
- table_close(newPartRel, NoLock);
-
- /* Roll back any GUC changes executed by index functions. */
- AtEOXact_GUC(false, save_nestlevel);
-
- /* Restore the userid and security context. */
- SetUserIdAndSecContext(save_userid, save_sec_context);
-}
-
-/*
- * Struct with the context of the new partition for inserting rows from the
- * split partition.
- */
-typedef struct SplitPartitionContext
-{
- ExprState *partqualstate; /* expression for checking a slot for a
- * partition (NULL for DEFAULT partition) */
- BulkInsertState bistate; /* state of bulk inserts for partition */
- TupleTableSlot *dstslot; /* slot for inserting row into partition */
- AlteredTableInfo *tab; /* structure with generated column expressions
- * and check constraint expressions. */
- Relation partRel; /* relation for partition */
-} SplitPartitionContext;
-
-/*
- * createSplitPartitionContext: create context for partition and fill it
- */
-static SplitPartitionContext *
-createSplitPartitionContext(Relation partRel)
-{
- SplitPartitionContext *pc;
-
- pc = palloc0_object(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 a destination tuple slot for the new partition. */
- pc->dstslot = table_slot_create(pc->partRel, NULL);
-
- return pc;
-}
-
-/*
- * deleteSplitPartitionContext: delete context for partition
- */
-static void
-deleteSplitPartitionContext(SplitPartitionContext *pc, List **wqueue, uint32 ti_options)
-{
- ListCell *ltab;
-
- ExecDropSingleTupleTableSlot(pc->dstslot);
- FreeBulkInsertState(pc->bistate);
-
- table_finish_bulk_insert(pc->partRel, ti_options);
-
- /*
- * We don't need to process this pc->partRel so delete the ALTER TABLE
- * queue of it.
- */
- foreach(ltab, *wqueue)
- {
- AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
-
- if (tab->relid == RelationGetRelid(pc->partRel))
- {
- *wqueue = list_delete_cell(*wqueue, ltab);
- break;
- }
- }
-
- pfree(pc);
-}
-
-/*
- * SplitPartitionMoveRows: scan split partition (splitRel) of partitioned table
- * (rel) and move rows into new partitions.
- *
- * New partitions description:
- * partlist: list of pointers to SinglePartitionSpec structures. It contains
- * the partition specification details for all new partitions.
- * newPartRels: list of Relations, new partitions created in
- * ATExecSplitPartition.
- */
-static void
-SplitPartitionMoveRows(List **wqueue, Relation rel, Relation splitRel,
- List *partlist, List *newPartRels)
-{
- /* The FSM is empty, so don't bother using it. */
- uint32 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;
-
- mycid = GetCurrentCommandId(true);
-
- estate = CreateExecutorState();
-
- forboth(listptr, partlist, listptr2, newPartRels)
- {
- SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
-
- pc = createSplitPartitionContext((Relation) lfirst(listptr2));
-
- /* Find the work queue entry for the new partition table: newPartRel. */
- pc->tab = ATGetQueueEntry(wqueue, pc->partRel);
-
- buildExpressionExecutionStates(pc->tab, pc->partRel, estate);
-
- if (sps->bound->is_default)
- {
- /*
- * We should not create a structure to check the partition
- * constraint for the new 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 a 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 a list. */
- partContexts = lappend(partContexts, pc);
- }
-
- econtext = GetPerTupleExprContext(estate);
-
- /* Create the necessary tuple slot. */
- srcslot = table_slot_create(splitRel, NULL);
-
- /*
- * Map computing for moving attributes of the split partition to the new
- * partition (for the 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,
- SO_NONE);
-
- /*
- * 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;
-
- CHECK_FOR_INTERRUPTS();
-
- econtext->ecxt_scantuple = srcslot;
-
- /* Search partition for the current slot, srcslot. */
- foreach(listptr, partContexts)
- {
- pc = (SplitPartitionContext *) lfirst(listptr);
-
- /* skip DEFAULT partition */
- if (pc->partqualstate && ExecCheck(pc->partqualstate, econtext))
- {
- found = true;
- break;
- }
- }
- if (!found)
- {
- /* Use the DEFAULT partition if it exists. */
- if (defaultPartCtx)
- pc = defaultPartCtx;
- else
- ereport(ERROR,
- errcode(ERRCODE_CHECK_VIOLATION),
- errmsg("cannot find partition for split partition row"),
- errtable(splitRel));
- }
-
- if (tuple_map)
- {
- /* Need to use a map to copy attributes. */
- insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
- }
- else
- {
- /* Extract data from the old tuple. */
- slot_getallattrs(srcslot);
-
- /* 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);
- }
-
- /*
- * Constraints and GENERATED expressions might reference the tableoid
- * column, so fill tts_tableOid with the desired value. (We must do
- * this each time, because it gets overwritten with newrel's OID
- * during storing.)
- */
- insertslot->tts_tableOid = RelationGetRelid(pc->partRel);
-
- /*
- * Now, evaluate any generated expressions whose inputs come from the
- * new tuple. We assume these columns won't reference each other, so
- * that there's no ordering dependency.
- */
- evaluateGeneratedExpressionsAndCheckConstraints(pc->tab, pc->partRel,
- insertslot, econtext);
-
- /* Write the tuple out to the new relation. */
- table_tuple_insert(pc->partRel, insertslot, mycid,
- ti_options, pc->bistate);
-
- ResetExprContext(econtext);
- }
-
- MemoryContextSwitchTo(oldCxt);
-
- table_endscan(scan);
- UnregisterSnapshot(snapshot);
-
- if (tuple_map)
- free_conversion_map(tuple_map);
-
- ExecDropSingleTupleTableSlot(srcslot);
-
- FreeExecutorState(estate);
-
- foreach_ptr(SplitPartitionContext, spc, partContexts)
- deleteSplitPartitionContext(spc, wqueue, ti_options);
-}
-
-/*
- * ALTER TABLE SPLIT PARTITION INTO
- */
-static void
-ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
- PartitionCmd *cmd, AlterTableUtilityContext *context)
-{
- Relation splitRel;
- Oid splitRelOid;
- ListCell *listptr,
- *listptr2;
- bool isSameName = false;
- char tmpRelName[NAMEDATALEN];
- List *newPartRels = NIL;
- List *extDepState = NIL;
- ObjectAddress object;
- Oid defaultPartOid;
- Oid save_userid;
- int save_sec_context;
- int save_nestlevel;
- List *splitPartList;
-
- defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
-
- /*
- * Partition is already locked in the transformPartitionCmdForSplit
- * function.
- */
- splitRel = table_openrv(cmd->name, NoLock);
-
- splitRelOid = RelationGetRelid(splitRel);
-
- /* Check descriptions of new partitions. */
- foreach_node(SinglePartitionSpec, sps, cmd->partlist)
- {
- Oid existingRelid;
-
- /* Look up the existing relation by the new partition name. */
- RangeVarGetAndCheckCreationNamespace(sps->name, NoLock, &existingRelid);
-
- /*
- * This would fail later on anyway if the relation already exists. But
- * by catching it here, we can emit a nicer error message.
- */
- if (existingRelid == splitRelOid && !isSameName)
- /* One new partition can have the same name as a split partition. */
- isSameName = true;
- else if (OidIsValid(existingRelid))
- ereport(ERROR,
- errcode(ERRCODE_DUPLICATE_TABLE),
- errmsg("relation \"%s\" already exists", sps->name->relname));
- }
-
- /*
- * Collect extension dependencies from indexes on the split partition. We
- * must do this before detaching it, so we can restore the dependencies on
- * the new partitions' indexes later.
- */
- splitPartList = list_make1_oid(splitRelOid);
-
- extDepState = collectPartitionIndexExtDeps(splitPartList);
- list_free(splitPartList);
-
- /* Detach the split partition. */
- detachPartitionTable(rel, splitRel, defaultPartOid);
-
- /*
- * Perform a preliminary check to determine whether it's safe to drop the
- * split partition before we actually do so later. After merging rows into
- * the new partitions via SplitPartitionMoveRows, all old partitions need
- * to be dropped. However, since the drop behavior is DROP_RESTRICT and
- * the merge process (SplitPartitionMoveRows) can be time-consuming,
- * performing an early check on the drop eligibility of old partitions is
- * preferable.
- */
- object.objectId = splitRelOid;
- object.classId = RelationRelationId;
- object.objectSubId = 0;
- performDeletionCheck(&object, DROP_RESTRICT, 0);
-
- /*
- * If a new partition has the same name as the split partition, then we
- * should rename the split partition to reuse its 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, true, false);
-
- /*
- * We must bump the command counter to make the split partition tuple
- * visible after renaming.
- */
- CommandCounterIncrement();
- }
-
- /* Create new partitions (like a split partition), without indexes. */
- foreach_node(SinglePartitionSpec, sps, cmd->partlist)
- {
- Relation newPartRel;
-
- newPartRel = createPartitionTable(wqueue, sps->name, rel,
- splitRel->rd_rel->relowner);
- newPartRels = lappend(newPartRels, newPartRel);
- }
-
- /*
- * Switch to the table owner's userid, so that any index functions are run
- * as that user. Also, lockdown security-restricted operations and
- * arrange to make GUC variable changes local to this command.
- *
- * Need to do it after determining the namespace in the
- * createPartitionTable() call.
- */
- GetUserIdAndSecContext(&save_userid, &save_sec_context);
- SetUserIdAndSecContext(splitRel->rd_rel->relowner,
- save_sec_context | SECURITY_RESTRICTED_OPERATION);
- save_nestlevel = NewGUCNestLevel();
- RestrictSearchPath();
-
- /* Copy data from the split partition to the new partitions. */
- SplitPartitionMoveRows(wqueue, rel, splitRel, cmd->partlist, newPartRels);
- /* Keep the lock until commit. */
- table_close(splitRel, NoLock);
-
- /* Attach new partitions to the 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);
-
- /*
- * Apply extension dependencies to the new partition's indexes. This
- * preserves any "DEPENDS ON EXTENSION" settings from the split
- * partition.
- */
- applyPartitionIndexExtDeps(RelationGetRelid(newPartRel), extDepState);
-
- /* Keep the lock until commit. */
- table_close(newPartRel, NoLock);
- }
-
- freePartitionIndexExtDeps(extDepState);
-
- /* Drop the split partition. */
- object.classId = RelationRelationId;
- object.objectId = splitRelOid;
- object.objectSubId = 0;
- /* Probably DROP_CASCADE is not needed. */
- performDeletion(&object, DROP_RESTRICT, 0);
-
- /* Roll back any GUC changes executed by index functions. */
- AtEOXact_GUC(false, save_nestlevel);
-
- /* Restore the userid and security context. */
- SetUserIdAndSecContext(save_userid, save_sec_context);
-}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b152c294ca..091d4423592 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -262,7 +262,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
PartitionElem *partelem;
PartitionSpec *partspec;
PartitionBoundSpec *partboundspec;
- SinglePartitionSpec *singlepartspec;
RoleSpec *rolespec;
PublicationObjSpec *publicationobjectspec;
PublicationAllObjSpec *publicationallobjectspec;
@@ -654,8 +653,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type part_elem
%type part_params
%type PartitionBoundSpec
-%type SinglePartitionSpec
-%type partitions_list
%type hash_partbound
%type hash_partbound_elem
@@ -800,7 +797,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
PERIOD PLACING PLAN PLANS POLICY PORTION
POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PROPERTIES PROPERTY PUBLICATION
@@ -815,7 +812,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
@@ -2436,23 +2433,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 ATTACH PARTITION FOR VALUES */
ATTACH PARTITION qualified_name PartitionBoundSpec
@@ -2463,7 +2443,6 @@ partition_cmd:
n->subtype = AT_AttachPartition;
cmd->name = $3;
cmd->bound = $4;
- cmd->partlist = NIL;
cmd->concurrent = false;
n->def = (Node *) cmd;
@@ -2478,7 +2457,6 @@ partition_cmd:
n->subtype = AT_DetachPartition;
cmd->name = $3;
cmd->bound = NULL;
- cmd->partlist = NIL;
cmd->concurrent = $4;
n->def = (Node *) cmd;
@@ -2492,35 +2470,6 @@ partition_cmd:
n->subtype = AT_DetachPartitionFinalize;
cmd->name = $3;
cmd->bound = NULL;
- cmd->partlist = NIL;
- cmd->concurrent = false;
- n->def = (Node *) cmd;
- $$ = (Node *) n;
- }
- /* ALTER TABLE SPLIT PARTITION 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 MERGE PARTITIONS () INTO */
- | 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;
@@ -2537,7 +2486,6 @@ index_partition_cmd:
n->subtype = AT_AttachPartition;
cmd->name = $3;
cmd->bound = NULL;
- cmd->partlist = NIL;
cmd->concurrent = false;
n->def = (Node *) cmd;
@@ -19020,7 +18968,6 @@ unreserved_keyword:
| PARSER
| PARTIAL
| PARTITION
- | PARTITIONS
| PASSING
| PASSWORD
| PATH
@@ -19095,7 +19042,6 @@ unreserved_keyword:
| SKIP
| SNAPSHOT
| SOURCE
- | SPLIT
| SQL_P
| STABLE
| STANDALONE_P
@@ -19664,7 +19610,6 @@ bare_label_keyword:
| PARSER
| PARTIAL
| PARTITION
- | PARTITIONS
| PASSING
| PASSWORD
| PATH
@@ -19750,7 +19695,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 5d90c6692c5..424cb1dc878 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -33,7 +33,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"
@@ -60,8 +59,6 @@
#include "parser/parse_type.h"
#include "parser/parse_utilcmd.h"
#include "parser/parser.h"
-#include "partitioning/partbounds.h"
-#include "partitioning/partdesc.h"
#include "rewrite/rewriteManip.h"
#include "utils/acl.h"
#include "utils/builtins.h"
@@ -132,7 +129,7 @@ static void checkSchemaNameList(const char *context_schema,
static CreateStmt *transformCreateSchemaCreateTable(ParseState *pstate,
CreateStmt *stmt,
List **fk_elements);
-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);
@@ -3532,287 +3529,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
}
-/*
- * checkPartition
- * Check whether partRelOid is a leaf partition of the parent table (rel).
- * isMerge: true indicates the operation is "ALTER TABLE ... MERGE PARTITIONS";
- * false indicates the operation is "ALTER TABLE ... SPLIT PARTITION".
- */
-static void
-checkPartition(Relation rel, Oid partRelOid, bool isMerge)
-{
- Relation partRel;
-
- partRel = table_open(partRelOid, NoLock);
-
- if (partRel->rd_rel->relkind != RELKIND_RELATION)
- ereport(ERROR,
- errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table", RelationGetRelationName(partRel)),
- isMerge
- ? errhint("ALTER TABLE ... MERGE PARTITIONS can only merge partitions that don't have sub-partitions.")
- : errhint("ALTER TABLE ... SPLIT PARTITION can only split partitions that don't have sub-partitions."));
-
- if (!partRel->rd_rel->relispartition)
- ereport(ERROR,
- errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a partition of partitioned table \"%s\"",
- RelationGetRelationName(partRel), RelationGetRelationName(rel)),
- isMerge
- ? errhint("ALTER TABLE ... MERGE PARTITIONS can only merge partitions that don't have sub-partitions.")
- : errhint("ALTER TABLE ... SPLIT PARTITION can only split partitions that don't have sub-partitions."));
-
- if (get_partition_parent(partRelOid, false) != RelationGetRelid(rel))
- ereport(ERROR,
- errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("relation \"%s\" is not a partition of relation \"%s\"",
- RelationGetRelationName(partRel), RelationGetRelationName(rel)),
- isMerge
- ? errhint("ALTER TABLE ... MERGE PARTITIONS can only merge partitions that don't have sub-partitions.")
- : errhint("ALTER TABLE ... SPLIT PARTITION can only split partitions that don't have sub-partitions."));
-
- table_close(partRel, NoLock);
-}
-
-/*
- * 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;
- PartitionKey key;
- char strategy;
- Oid splitPartOid;
- Oid defaultPartOid;
- int default_index = -1;
- bool isSplitPartDefault;
- ListCell *listptr,
- *listptr2;
- List *splitlist;
-
- splitlist = partcmd->partlist;
- key = RelationGetPartitionKey(parent);
- strategy = get_partition_strategy(key);
- defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(parent, true));
-
- /* Transform partition bounds for all partitions in the list: */
- foreach_node(SinglePartitionSpec, sps, splitlist)
- {
- cxt->partbound = NULL;
- transformPartitionCmd(cxt, sps->bound);
- /* Assign the transformed value of the partition bound. */
- sps->bound = cxt->partbound;
- }
-
- /*
- * Open and lock the partition, check ownership along the way. We need to
- * use AccessExclusiveLock here because this split partition will be
- * detached, then dropped in ATExecSplitPartition.
- */
- splitPartOid = RangeVarGetRelidExtended(partcmd->name, AccessExclusiveLock,
- 0, RangeVarCallbackOwnsRelation,
- NULL);
-
- checkPartition(parent, splitPartOid, false);
-
- switch (strategy)
- {
- case PARTITION_STRATEGY_LIST:
- case PARTITION_STRATEGY_RANGE:
- {
- foreach_node(SinglePartitionSpec, sps, splitlist)
- {
- if (sps->bound->is_default)
- {
- if (default_index != -1)
- ereport(ERROR,
- errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("cannot specify more than one DEFAULT partition"),
- parser_errposition(cxt->pstate, sps->name->location));
-
- default_index = foreach_current_index(sps);
- }
- }
- }
- 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;
- }
-
- /* isSplitPartDefault: is the being split partition a DEFAULT partition? */
- isSplitPartDefault = (defaultPartOid == splitPartOid);
-
- if (isSplitPartDefault && default_index == -1)
- ereport(ERROR,
- errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("cannot split DEFAULT partition \"%s\"",
- get_rel_name(splitPartOid)),
- errhint("To split a DEFAULT partition, one of the new partitions must be DEFAULT."));
-
- /*
- * If the partition being split is not the DEFAULT partition, but the
- * DEFAULT partition exists, then none of the resulting split partitions
- * can be the DEFAULT.
- */
- if (!isSplitPartDefault && (default_index != -1) && OidIsValid(defaultPartOid))
- {
- SinglePartitionSpec *spsDef =
- (SinglePartitionSpec *) list_nth(splitlist, default_index);
-
- ereport(ERROR,
- errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("cannot split non-DEFAULT partition \"%s\"",
- get_rel_name(splitPartOid)),
- errdetail("New partition cannot be DEFAULT because DEFAULT partition \"%s\" already exists.",
- get_rel_name(defaultPartOid)),
- parser_errposition(cxt->pstate, spsDef->name->location));
- }
-
- foreach(listptr, splitlist)
- {
- Oid nspid;
- SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
- RangeVar *name = sps->name;
-
- nspid = RangeVarGetCreationNamespace(sps->name);
-
- /* Partitions in the list should have different names. */
- for_each_cell(listptr2, splitlist, lnext(splitlist, listptr))
- {
- Oid nspid2;
- SinglePartitionSpec *sps2 = (SinglePartitionSpec *) lfirst(listptr2);
- RangeVar *name2 = sps2->name;
-
- 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));
-
- nspid2 = RangeVarGetCreationNamespace(sps2->name);
-
- if (nspid2 == nspid && strcmp(name->relname, name2->relname) == 0)
- ereport(ERROR,
- errcode(ERRCODE_DUPLICATE_TABLE),
- errmsg("partition with name \"%s\" is already used", name->relname),
- parser_errposition(cxt->pstate, name2->location));
- }
- }
-
- /* Then we should check partitions with transformed bounds. */
- check_partitions_for_split(parent, splitPartOid, splitlist, cxt->pstate);
-}
-
-
-/*
- * transformPartitionCmdForMerge -
- * analyze the ALTER TABLE ... MERGE PARTITIONS command
- *
- * Does simple checks for merged partitions. Calculates bound of the 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;
-
- 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"));
-
- /* Does the partitioned table (parent) have 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 the DEFAULT partition in the list. Open and lock partitions
- * before calculating the boundary for resulting partition, we also
- * check for ownership along the way. We need to use
- * AccessExclusiveLock here, because these merged partitions will be
- * detached and then dropped in ATExecMergePartitions.
- */
- partOid = RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
- RangeVarCallbackOwnsRelation,
- NULL);
- /* Is the current partition a DEFAULT partition? */
- if (partOid == defaultPartOid)
- isDefaultPart = true;
-
- /*
- * Extended check because the same partition can have different names
- * (for example, "part_name" and "public.part_name").
- */
- foreach(listptr2, partOids)
- {
- Oid curOid = lfirst_oid(listptr2);
-
- if (curOid == partOid)
- ereport(ERROR,
- errcode(ERRCODE_DUPLICATE_TABLE),
- errmsg("partition with name \"%s\" is already used", name->relname),
- parser_errposition(cxt->pstate, name->location));
- }
-
- checkPartition(parent, partOid, true);
-
- partOids = lappend_oid(partOids, partOid);
- }
-
- /* Allocate the bound of the resulting partition. */
- Assert(partcmd->bound == NULL);
- partcmd->bound = makeNode(PartitionBoundSpec);
-
- /* Fill the 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
@@ -4082,48 +3798,20 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
{
PartitionCmd *partcmd = (PartitionCmd *) cmd->def;
- transformPartitionCmd(&cxt, partcmd->bound);
- /* assign the transformed value of the partition bound */
+ transformPartitionCmd(&cxt, partcmd);
+ /* assign transformed value of the partition bound */
partcmd->bound = cxt.partbound;
}
newcmds = lappend(newcmds, cmd);
break;
- case AT_MergePartitions:
- {
- PartitionCmd *partcmd = (PartitionCmd *) cmd->def;
-
- if (list_length(partcmd->partlist) < 2)
- ereport(ERROR,
- errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("list of partitions to be merged should include at least two partitions"));
-
- transformPartitionCmdForMerge(&cxt, partcmd);
- newcmds = lappend(newcmds, cmd);
- break;
- }
-
- case AT_SplitPartition:
- {
- PartitionCmd *partcmd = (PartitionCmd *) cmd->def;
-
- if (list_length(partcmd->partlist) < 2)
- ereport(ERROR,
- errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("list of new partitions must contain at least two partitions"));
-
- transformPartitionCmdForSplit(&cxt, partcmd);
- newcmds = lappend(newcmds, cmd);
- break;
- }
-
default:
/*
- * Currently, we shouldn't actually get here for the
- * subcommand types that don't require transformation; but if
- * we do, just emit them unchanged.
+ * Currently, we shouldn't actually get here for subcommand
+ * types that don't require transformation; but if we do, just
+ * emit them unchanged.
*/
newcmds = lappend(newcmds, cmd);
break;
@@ -4791,13 +4479,13 @@ transformCreateSchemaCreateTable(ParseState *pstate,
/*
* 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;
@@ -4806,9 +4494,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:
@@ -4816,7 +4504,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 a7822a4192b..c400ba206c4 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -17,7 +17,6 @@
#include "access/relation.h"
#include "access/table.h"
#include "access/tableam.h"
-#include "catalog/namespace.h"
#include "catalog/partition.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_type.h"
@@ -4982,1056 +4981,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 the upper
- * bound of first_bound and the lower bound of second_bound. These bounds
- * should be equal except when "defaultPart == true" (this means that one of
- * the split partitions is DEFAULT). In this case, the upper bound of
- * first_bound can be less than the lower bound of second_bound because
- * the space between these bounds will be included in the DEFAULT partition.
- *
- * parent: partitioned table
- * first_name: name of the first partition
- * first_bound: bound of the first partition
- * second_name: name of the second partition
- * second_bound: bound of the second partition
- * defaultPart: true if one of the new partitions is DEFAULT
- * is_merge: true indicates the operation is MERGE PARTITIONS;
- * false indicates the operation is SPLIT PARTITION.
- * 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,
- bool is_merge,
- 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 argument of partition_rbound_cmp() is set to false for the
- * correct comparison result of the 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);
-
- if (is_merge)
- ereport(ERROR,
- errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("cannot merge partition \"%s\" together with partition \"%s\"",
- second_name->relname, first_name->relname),
- errdetail("The lower bound of partition \"%s\" is not equal to the upper bound of partition \"%s\".",
- second_name->relname, first_name->relname),
- errhint("ALTER TABLE ... MERGE PARTITIONS requires the partition bounds to be adjacent."),
- parser_errposition(pstate, datum->location));
- else
- ereport(ERROR,
- errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("cannot split to partition \"%s\" together with partition \"%s\"",
- second_name->relname, first_name->relname),
- errdetail("The lower bound of partition \"%s\" is not equal to the upper bound of partition \"%s\".",
- second_name->relname, first_name->relname),
- errhint("ALTER TABLE ... SPLIT PARTITION requires the partition bounds to be adjacent."),
- parser_errposition(pstate, datum->location));
- }
-}
-
-/*
- * get_partition_bound_spec
- *
- * Returns the PartitionBoundSpec for the partition with the given OID partOid.
- */
-static PartitionBoundSpec *
-get_partition_bound_spec(Oid partOid)
-{
- HeapTuple tuple;
- Datum datum;
- bool isnull;
- PartitionBoundSpec *boundspec = NULL;
-
- /* Try fetching the tuple from the catcache, for speed. */
- tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(partOid));
- if (!HeapTupleIsValid(tuple))
- elog(ERROR, "cache lookup failed for relation %u", partOid);
-
- datum = SysCacheGetAttr(RELOID, tuple,
- Anum_pg_class_relpartbound,
- &isnull);
- if (isnull)
- elog(ERROR, "partition bound for relation %u is null",
- partOid);
-
- boundspec = stringToNode(TextDatumGetCString(datum));
-
- if (!IsA(boundspec, PartitionBoundSpec))
- elog(ERROR, "expected PartitionBoundSpec for relation %u",
- partOid);
-
- ReleaseSysCache(tuple);
- return boundspec;
-}
-
-/*
- * calculate_partition_bound_for_merge
- *
- * Calculates the bound of the merged partition "spec" by using the bounds of
- * the 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 to 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 = palloc0_array(PartitionRangeBound *, nparts);
-
- /*
- * Create an array of lower bounds and a list of
- * PartitionBoundSpec.
- */
- foreach_oid(partoid, partOids)
- {
- bound = get_partition_bound_spec(partoid);
- i = foreach_current_index(partoid);
-
- lower_bounds[i] = make_one_partition_rbound(key, i, bound->lowerdatums, true);
- bounds = lappend(bounds, bound);
- }
-
- /* Sort the array of lower bounds. */
- qsort_arg(lower_bounds, nparts, sizeof(PartitionRangeBound *),
- qsort_partition_rbound_cmp, key);
-
- /* Ranges of partitions should be adjacent. */
- 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,
- true,
- pstate);
- }
-
- /*
- * The lower bound of the first partition is the lower bound
- * of the merged partition.
- */
- spec->lowerdatums =
- ((PartitionBoundSpec *) list_nth(bounds, lower_bounds[0]->index))->lowerdatums;
-
- /*
- * The upper bound of the last partition is the upper bound of
- * the 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:
- {
- /* Consolidate bounds for all partitions in the list. */
- foreach_oid(partoid, partOids)
- {
- bound = get_partition_bound_spec(partoid);
- spec->listdatums = list_concat(spec->listdatums, bound->listdatums);
- }
- break;
- }
-
- default:
- elog(ERROR, "unexpected partition strategy: %d",
- (int) key->strategy);
- }
-}
-
-/*
- * partitions_listdatum_intersection
- *
- * (function for BY LIST partitioning)
- *
- * Function compares lists of values for different partitions.
- * Return a list that contains *one* cell that is present in both list1 and
- * list2. The returned list is freshly allocated via palloc(), but the
- * cells themselves point to the same objects as the cells of the
- * input lists.
- *
- * Currently, there is no need to collect all common partition datums from the
- * two lists.
- */
-static List *
-partitions_listdatum_intersection(FmgrInfo *partsupfunc, Oid *partcollation,
- const List *list1, const List *list2)
-{
- List *result = NIL;
-
- if (list1 == NIL || list2 == NIL)
- return result;
-
- foreach_node(Const, val1, list1)
- {
- bool isnull1 = val1->constisnull;
-
- foreach_node(Const, val2, list2)
- {
- if (val2->constisnull)
- {
- if (isnull1)
- {
- result = lappend(result, val1);
- return result;
- }
- continue;
- }
- else if (isnull1)
- continue;
-
- /* Compare two datum values. */
- if (DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
- partcollation[0],
- val1->constvalue,
- val2->constvalue)) == 0)
- {
- result = lappend(result, val1);
- return result;
- }
- }
- }
-
- return result;
-}
-
-/*
- * 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 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];
-
- overlap = partitions_listdatum_intersection(&key->partsupfunc[0],
- key->partcollation,
- sps1->bound->listdatums,
- sps2->bound->listdatums);
- if (list_length(overlap) > 0)
- {
- Const *val = (Const *) linitial_node(Const, overlap);
-
- 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, exprLocation((Node *) val)));
- }
- }
- }
-}
-
-/*
- * 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 the new partitions), then the lower bound of "spec" should be
- * equal (or greater than or equal in case defaultPart=true) to the lower
- * bound of the split partition. If last=true (this means that "spec" is the
- * last of the new partitions), then the upper bound of "spec" should be
- * equal (or less than or equal in case defaultPart=true) to the upper bound
- * of the split partition.
- *
- * parent: partitioned table
- * relname: name of the new partition
- * spec: bounds specification of the new partition
- * splitPartOid: split partition Oid
- * first: true iff the new partition "spec" is the first of the
- * new partitions
- * last: true iff the new partition "spec" is the last of the
- * new partitions
- * defaultPart: true iff new partitions contain the DEFAULT partition
- * pstate: pointer to ParseState struct to determine error position
- */
-static void
-check_partition_bounds_for_split_range(Relation parent,
- char *relname,
- PartitionBoundSpec *spec,
- Oid splitPartOid,
- 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 the 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 the 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, exprLocation((Node *) datum)));
- }
-
- /*
- * Need to check first and last partitions (from the set of new
- * partitions)
- */
- if (first || last)
- {
- PartitionBoundSpec *split_spec = get_partition_bound_spec(splitPartOid);
- 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);
- if (cmpval != 0)
- datum = list_nth(spec->lowerdatums, abs(cmpval) - 1);
-
- /*
- * The lower bound of "spec" must equal the lower bound of the
- * split partition. However, if one of the new partitions is
- * DEFAULT, then it is ok for the new partition's lower bound to
- * be greater than that of the split partition.
- */
- if (!defaultPart)
- {
- if (cmpval != 0)
- ereport(ERROR,
- errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("lower bound of partition \"%s\" is not equal to lower bound of split partition \"%s\"",
- relname,
- get_rel_name(splitPartOid)),
- errhint("%s requires the combined bounds of the new partitions to exactly match the bound of the split partition.",
- "ALTER TABLE ... SPLIT PARTITION"),
- parser_errposition(pstate, exprLocation((Node *) datum)));
- }
- else if (cmpval < 0)
- ereport(ERROR,
- errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("lower bound of partition \"%s\" is less than lower bound of split partition \"%s\"",
- relname,
- get_rel_name(splitPartOid)),
- errhint("Explicit partition bounds must be contained within the bounds of the split partition when a DEFAULT partition is specified."),
- parser_errposition(pstate, exprLocation((Node *) datum)));
- }
-
- if (last)
- {
- 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);
- if (cmpval != 0)
- datum = list_nth(spec->upperdatums, abs(cmpval) - 1);
-
- /*
- * The upper bound of "spec" must equal the upper bound of the
- * split partition. However, if one of the new partitions is
- * DEFAULT, then it is ok for the new partition's upper bound to
- * be less than that of the split partition.
- */
- if (!defaultPart)
- {
- if (cmpval != 0)
- ereport(ERROR,
- errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("upper bound of partition \"%s\" is not equal to upper bound of split partition \"%s\"",
- relname,
- get_rel_name(splitPartOid)),
- errhint("%s requires the combined bounds of the new partitions to exactly match the bound of the split partition.",
- "ALTER TABLE ... SPLIT PARTITION"),
- parser_errposition(pstate, exprLocation((Node *) datum)));
- }
- else if (cmpval > 0)
- ereport(ERROR,
- errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("upper bound of partition \"%s\" is greater than upper bound of split partition \"%s\"",
- relname,
- get_rel_name(splitPartOid)),
- errhint("Explicit partition bounds must be contained within the bounds of the split partition when a DEFAULT partition is specified."),
- parser_errposition(pstate, exprLocation((Node *) datum)));
- }
- }
-}
-
-/*
- * check_partition_bounds_for_split_list
- *
- * (function for BY LIST partitioning)
- *
- * Checks that the bounds of the new partition are inside the bounds of the
- * 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 to 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;
-
- Assert(key->strategy == PARTITION_STRATEGY_LIST);
- Assert(spec->strategy == PARTITION_STRATEGY_LIST);
- Assert(boundinfo && boundinfo->strategy == PARTITION_STRATEGY_LIST);
-
- /*
- * Search each value of the new partition "spec" in the existing
- * partitions. All of them should be in the split partition (with Oid
- * splitPartOid).
- */
- foreach_node(Const, val, spec->listdatums)
- {
- overlap_location = exprLocation((Node *) val);
- 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 \"%s\" does not have it",
- relname,
- get_rel_name(splitPartOid)),
- 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 \"%s\" does not have it",
- relname,
- get_rel_name(splitPartOid)),
- 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 iff any of the new partitions contains the value
- * "value".
- *
- * partsupfunc: information about the comparison function associated with
- * the partition key
- * partcollation: partitioning collation
- * parts: pointer to an array with new partition 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)
-{
- for (int i = 0; i < nparts; i++)
- {
- SinglePartitionSpec *sps = parts[i];
-
- foreach_node(Const, val, sps->bound->listdatums)
- {
- 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) are contained
- * in new partitions.
- *
- * parent: partitioned table
- * partOid: split partition Oid
- * parts: pointer to an array with new partition descriptions
- * nparts: number of new partitions
- * pstate: pointer to ParseState struct to 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;
- Datum datum = PointerGetDatum(NULL);
-
- Assert(key->strategy == PARTITION_STRATEGY_LIST);
-
- /*
- * Special processing for NULL value. Search for a 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;
- }
-
- if (!found)
- ereport(ERROR,
- errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("new partitions' combined partition bounds do not contain value (%s) but split partition \"%s\" does",
- "NULL",
- get_rel_name(partOid)),
- errhint("%s requires the combined bounds of the new partitions to exactly match the bound of the split partition.",
- "ALTER TABLE ... SPLIT PARTITION"));
-
- /*
- * Search all values of split partition with partOid in the PartitionDesc
- * of partitioned table.
- */
- for (i = 0; i < boundinfo->ndatums; i++)
- {
- if (partdesc->oids[boundinfo->indexes[i]] == partOid)
- {
- /* We found the value that the 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;
-
- /*
- * Make a Const for getting the string representation of the missing
- * 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' combined partition bounds do not contain value (%s) but split partition \"%s\" does",
- deparse_expression((Node *) notFoundVal, NIL, false, false),
- get_rel_name(partOid)),
- errhint("%s requires the combined bounds of the new partitions to exactly match the bound of the split partition.",
- "ALTER TABLE ... SPLIT PARTITION"));
- }
-}
-
-/*
- * split_partition_values_contained_in_new_part
- *
- * (function for BY LIST partitioning)
- *
- * Returns true if all values in the LIST bound of the partition being split
- * are contained in the specified non-DEFAULT replacement partition's bound.
- *
- * The caller must already have verified containment in the other direction,
- * so this check is sufficient to prove that the two LIST bounds are equal.
- */
-static bool
-split_partition_values_contained_in_new_part(Relation parent,
- Oid splitPartOid,
- SinglePartitionSpec *part)
-{
- PartitionKey key = RelationGetPartitionKey(parent);
- PartitionDesc partdesc = RelationGetPartitionDesc(parent, false);
- PartitionBoundInfo boundinfo = partdesc->boundinfo;
- SinglePartitionSpec *parts[1];
- Datum datum = PointerGetDatum(NULL);
-
- Assert(key->strategy == PARTITION_STRATEGY_LIST);
-
- parts[0] = part;
-
- /*
- * Special processing for NULL value. Search for a NULL value if the
- * split partition contains it.
- */
- if (partition_bound_accepts_nulls(boundinfo) &&
- partdesc->oids[boundinfo->null_index] == splitPartOid)
- {
- if (!find_value_in_new_partitions_list(&key->partsupfunc[0],
- key->partcollation, parts, 1,
- datum, true))
- return false;
- }
-
- /*
- * Search all values of the split partition in the single non-DEFAULT
- * replacement partition.
- */
- for (int i = 0; i < boundinfo->ndatums; i++)
- {
- if (partdesc->oids[boundinfo->indexes[i]] == splitPartOid)
- {
- datum = boundinfo->datums[i][0];
-
- if (!find_value_in_new_partitions_list(&key->partsupfunc[0],
- key->partcollation, parts, 1,
- datum, false))
- return false;
- }
- }
-
- return true;
-}
-
-/*
- * check_split_partition_not_same_bound
- *
- * Reject splitting a non-DEFAULT partition into one non-DEFAULT partition
- * with the original bound plus a DEFAULT partition. That form does not
- * perform a real split; it merely adds a DEFAULT partition to the parent
- * table through the split-partition path. Users should use
- * CREATE TABLE ... PARTITION OF ... DEFAULT or ALTER TABLE ... ATTACH
- * PARTITION ... DEFAULT for that.
- *
- * Must be called after the per-partition bound validation in
- * check_partitions_for_split() so that containment of new bounds within the
- * split partition is already established. Given containment, RANGE bounds
- * are equal iff their lower and upper rbounds match; LIST bound sets are
- * equal iff the split partition's values are also contained in the new
- * partition (the containment is then bidirectional). Both checks go
- * through the partition operator family (partition_rbound_cmp /
- * find_value_in_new_partitions_list) rather than byte equality, so e.g.
- * -0.0 and 0.0 -- which have different bit patterns but compare equal
- * under float8 -- are correctly recognised as the same bound.
- */
-static void
-check_split_partition_not_same_bound(Relation parent,
- Oid splitPartOid,
- SinglePartitionSpec **parts,
- int nparts,
- ParseState *pstate)
-{
- PartitionKey key = RelationGetPartitionKey(parent);
- PartitionBoundSpec *new_spec;
- PartitionBoundSpec *split_spec;
-
- if (nparts != 1)
- return;
-
- new_spec = parts[0]->bound;
- split_spec = get_partition_bound_spec(splitPartOid);
-
- Assert(new_spec->strategy == split_spec->strategy);
-
- if (key->strategy == PARTITION_STRATEGY_RANGE)
- {
- PartitionRangeBound *new_lower;
- PartitionRangeBound *new_upper;
- PartitionRangeBound *split_lower;
- PartitionRangeBound *split_upper;
-
- new_lower = make_one_partition_rbound(key, -1, new_spec->lowerdatums, true);
- new_upper = make_one_partition_rbound(key, -1, new_spec->upperdatums, false);
- split_lower = make_one_partition_rbound(key, -1, split_spec->lowerdatums, true);
- split_upper = make_one_partition_rbound(key, -1, split_spec->upperdatums, false);
-
- if (partition_rbound_cmp(key->partnatts, key->partsupfunc,
- key->partcollation,
- new_lower->datums, new_lower->kind, true,
- split_lower) != 0)
- return;
- if (partition_rbound_cmp(key->partnatts, key->partsupfunc,
- key->partcollation,
- new_upper->datums, new_upper->kind, false,
- split_upper) != 0)
- return;
- }
- else
- {
- Assert(key->strategy == PARTITION_STRATEGY_LIST);
-
- if (!split_partition_values_contained_in_new_part(parent, splitPartOid,
- parts[0]))
- return;
- }
-
- ereport(ERROR,
- errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("cannot split partition \"%s\" only to add a DEFAULT partition",
- get_rel_name(splitPartOid)),
- errdetail("The non-DEFAULT partition would keep the same partition bound."),
- errhint("Use CREATE TABLE ... PARTITION OF ... DEFAULT to add a DEFAULT partition."),
- parser_errposition(pstate, parts[0]->name->location));
-}
-
-/*
- * check_partitions_for_split
- *
- * Checks new partitions for the SPLIT PARTITION command:
- * 1. Bounds of new partitions should not overlap with new and existing
- * partitions.
- * 2. In the case when new or existing partitions contain the DEFAULT
- * partition, new partitions can have any bounds inside the split partition
- * bound (can be spaces between partition bounds).
- * 3. In case new partitions don't contain the DEFAULT partition and the
- * partitioned table does not have the DEFAULT partition, the following
- * should be true: the sum of the bounds of new partitions should be equal
- * to the bound of the split partition.
- *
- * parent: partitioned table
- * splitPartOid: split partition Oid
- * partlist: list of new partitions after partition split
- * pstate: pointer to ParseState struct for determine error position
- */
-void
-check_partitions_for_split(Relation parent,
- Oid splitPartOid,
- List *partlist,
- ParseState *pstate)
-{
- PartitionKey key;
- char strategy;
- Oid defaultPartOid;
- bool isSplitPartDefault;
- bool createDefaultPart = false;
- int default_index = -1;
- int i;
- SinglePartitionSpec **new_parts;
- SinglePartitionSpec *spsPrev = NULL;
-
- /*
- * nparts counts the number of split partitions, but it exclude the
- * default partition.
- */
- int nparts = 0;
-
- key = RelationGetPartitionKey(parent);
- strategy = get_partition_strategy(key);
-
- defaultPartOid =
- get_default_oid_from_partdesc(RelationGetPartitionDesc(parent, true));
-
- Assert(strategy == PARTITION_STRATEGY_RANGE ||
- strategy == PARTITION_STRATEGY_LIST);
-
- /*
- * Make an array new_parts with new partitions except the DEFAULT
- * partition.
- */
- new_parts = palloc0_array(SinglePartitionSpec *, list_length(partlist));
-
- /* isSplitPartDefault flag: is split partition a DEFAULT partition? */
- isSplitPartDefault = (defaultPartOid == splitPartOid);
-
- foreach_node(SinglePartitionSpec, sps, partlist)
- {
- if (sps->bound->is_default)
- default_index = foreach_current_index(sps);
- else
- new_parts[nparts++] = sps;
- }
-
- /* An indicator that the DEFAULT partition will be created. */
- if (default_index != -1)
- {
- createDefaultPart = true;
- Assert(nparts == list_length(partlist) - 1);
- }
-
- if (strategy == PARTITION_STRATEGY_RANGE)
- {
- PartitionRangeBound **lower_bounds;
- SinglePartitionSpec **tmp_new_parts;
-
- /*
- * To simplify the check for ranges of new partitions, we need to sort
- * all partitions in ascending order of their bounds (we compare the
- * lower bound only).
- */
- lower_bounds = palloc0_array(PartitionRangeBound *, nparts);
-
- /* Create an 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 the array of lower bounds. */
- qsort_arg(lower_bounds, nparts, sizeof(PartitionRangeBound *),
- qsort_partition_rbound_cmp, (void *) key);
-
- /* Reorder the array of partitions. */
- tmp_new_parts = new_parts;
- new_parts = palloc0_array(SinglePartitionSpec *, nparts);
- for (i = 0; i < nparts; i++)
- new_parts[i] = tmp_new_parts[lower_bounds[i]->index];
-
- pfree(tmp_new_parts);
- pfree(lower_bounds);
- }
-
- for (i = 0; i < nparts; i++)
- {
- SinglePartitionSpec *sps = new_parts[i];
-
- if (isSplitPartDefault)
- {
- /*
- * When the split partition is the 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 the bounds of the current partition are inside the
- * bounds of the split partition. For range partitioning: checks
- * that the upper bound of the previous partition is equal to the
- * lower bound of the current partition. For list partitioning:
- * checks that the split partition contains all values of the
- * 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, first, last,
- createDefaultPart, 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,
- createDefaultPart,
- false,
- pstate);
-
- spsPrev = sps;
- }
-
- 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 the split partition are contained
- * in the new partitions. Skip this check if the DEFAULT partition
- * exists.
- */
- if (!createDefaultPart)
- check_parent_values_in_new_partitions(parent, splitPartOid,
- new_parts, nparts, pstate);
- }
-
- /*
- * Reject the degenerate form where the single non-DEFAULT replacement
- * partition keeps the bound of the split partition; the command then does
- * nothing beyond adding a DEFAULT partition. Containment was established
- * by the per-partition validation above, so an equality check is enough.
- */
- if (!isSplitPartDefault && createDefaultPart)
- check_split_partition_not_same_bound(parent, splitPartOid, new_parts,
- nparts, pstate);
-
- pfree(new_parts);
-}
diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c
index 807d1d3b527..5b914d603bb 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -2835,7 +2835,6 @@ match_previous_words(int pattern_id,
"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"))
@@ -3098,10 +3097,10 @@ match_previous_words(int pattern_id,
COMPLETE_WITH("FROM (", "IN (", "WITH (");
/*
- * If we have ALTER TABLE DETACH|SPLIT PARTITION, provide a list of
+ * If we have ALTER TABLE DETACH PARTITION, provide a list of
* partitions of .
*/
- else if (Matches("ALTER", "TABLE", MatchAny, "DETACH|SPLIT", "PARTITION"))
+ else if (Matches("ALTER", "TABLE", MatchAny, "DETACH", "PARTITION"))
{
set_completion_reference(prev3_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_partition_of_table);
@@ -3109,19 +3108,6 @@ match_previous_words(int pattern_id,
else if (Matches("ALTER", "TABLE", MatchAny, "DETACH", "PARTITION", MatchAny))
COMPLETE_WITH("CONCURRENTLY", "FINALIZE");
- /* ALTER TABLE SPLIT PARTITION */
- else if (Matches("ALTER", "TABLE", MatchAny, "SPLIT", "PARTITION", MatchAny))
- COMPLETE_WITH("INTO ( PARTITION");
-
- /* ALTER TABLE 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 OF */
else if (Matches("ALTER", "TABLE", MatchAny, "OF"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_composite_datatypes);
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 214ef1e7e2d..7da6a3942e1 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -107,8 +107,6 @@ extern void ReleaseDeletionLock(const ObjectAddress *object);
extern void performDeletion(const ObjectAddress *object,
DropBehavior behavior, int flags);
-extern void performDeletionCheck(const ObjectAddress *object,
- DropBehavior behavior, int flags);
extern void performMultipleDeletions(const ObjectAddresses *objects,
DropBehavior behavior, int flags);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 2fcea826003..f48a0c62d49 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -984,39 +984,13 @@ typedef struct PartitionRangeDatum
} PartitionRangeDatum;
/*
- * PartitionDesc - info about a single partition for the ALTER TABLE SPLIT
- * PARTITION command
- */
-typedef struct SinglePartitionSpec
-{
- NodeTag type;
-
- RangeVar *name; /* name of partition */
- PartitionBoundSpec *bound; /* FOR VALUES, if attaching */
-} SinglePartitionSpec;
-
-/*
- * PartitionCmd - info for ALTER TABLE/INDEX ATTACH/DETACH PARTITION and for
- * ALTER TABLE SPLIT/MERGE PARTITION(S) commands
+ * PartitionCmd - info for ALTER TABLE/INDEX ATTACH/DETACH PARTITION commands
*/
typedef struct PartitionCmd
{
NodeTag type;
-
- /* name of partition to attach/detach/merge/split */
- RangeVar *name;
-
- /* FOR VALUES, if attaching */
- PartitionBoundSpec *bound;
-
- /*
- * list of partitions to be split/merged, used in ALTER TABLE MERGE
- * PARTITIONS and ALTER TABLE SPLIT PARTITIONS. For merge partitions,
- * partlist is a list of RangeVar; For split partition, it is a list of
- * SinglePartitionSpec.
- */
- List *partlist;
-
+ RangeVar *name; /* name of partition to attach/detach */
+ PartitionBoundSpec *bound; /* FOR VALUES, if attaching */
bool concurrent;
} PartitionCmd;
@@ -2578,8 +2552,6 @@ typedef enum AlterTableType
AT_AttachPartition, /* ATTACH PARTITION */
AT_DetachPartition, /* DETACH PARTITION */
AT_DetachPartitionFinalize, /* DETACH PARTITION FINALIZE */
- AT_SplitPartition, /* SPLIT PARTITION */
- AT_MergePartitions, /* MERGE PARTITIONS */
AT_AddIdentity, /* ADD IDENTITY */
AT_SetIdentity, /* SET identity column options */
AT_DropIdentity, /* DROP IDENTITY */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 51ead54f015..a12d550ef60 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -344,7 +344,6 @@ PG_KEYWORD("parameter", PARAMETER, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("parser", PARSER, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("partial", PARTIAL, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("partition", PARTITION, UNRESERVED_KEYWORD, BARE_LABEL)
-PG_KEYWORD("partitions", PARTITIONS, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("passing", PASSING, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("password", PASSWORD, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("path", PATH, UNRESERVED_KEYWORD, BARE_LABEL)
@@ -434,7 +433,6 @@ PG_KEYWORD("smallint", SMALLINT, COL_NAME_KEYWORD, BARE_LABEL)
PG_KEYWORD("snapshot", SNAPSHOT, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("some", SOME, RESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("source", SOURCE, UNRESERVED_KEYWORD, BARE_LABEL)
-PG_KEYWORD("split", SPLIT, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("sql", SQL_P, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("stable", STABLE, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("standalone", STANDALONE_P, UNRESERVED_KEYWORD, BARE_LABEL)
diff --git a/src/include/partitioning/partbounds.h b/src/include/partitioning/partbounds.h
index 25c926f4c8b..d66918f89c1 100644
--- a/src/include/partitioning/partbounds.h
+++ b/src/include/partitioning/partbounds.h
@@ -143,14 +143,4 @@ extern int partition_range_datum_bsearch(FmgrInfo *partsupfunc,
extern int partition_hash_bsearch(PartitionBoundInfo boundinfo,
int modulus, int remainder);
-extern void check_partitions_for_split(Relation parent,
- Oid splitPartOid,
- List *partlist,
- ParseState *pstate);
-extern void calculate_partition_bound_for_merge(Relation parent,
- List *partNames,
- List *partOids,
- PartitionBoundSpec *spec,
- ParseState *pstate);
-
#endif /* PARTBOUNDS_H */
diff --git a/src/test/isolation/expected/partition-merge.out b/src/test/isolation/expected/partition-merge.out
deleted file mode 100644
index 5f6472671b9..00000000000
--- a/src/test/isolation/expected/partition-merge.out
+++ /dev/null
@@ -1,243 +0,0 @@
-Parsed test spec with 2 sessions
-
-starting permutation: s2b s2i s2c s1b s1merg s2b s2u s1c s2c s2s
-step s2b: BEGIN;
-step s2i: INSERT INTO tpart VALUES (1, 'text01');
-step s2c: COMMIT;
-step s1b: BEGIN;
-step s1merg: ALTER TABLE tpart MERGE PARTITIONS (tpart_00_10, tpart_10_20) INTO tpart_00_20;
-step s2b: BEGIN;
-step s2u: UPDATE tpart SET t = 'text01modif' where i = 1;
-step s1c: COMMIT;
-step s2u: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+-----------
-tpart_00_20 | 1|text01modif
-tpart_00_20 | 5|text05
-tpart_00_20 |15|text15
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(5 rows)
-
-
-starting permutation: s2b s2i s2c s1brr s1merg s2b s2u s1c s2c s2s
-step s2b: BEGIN;
-step s2i: INSERT INTO tpart VALUES (1, 'text01');
-step s2c: COMMIT;
-step s1brr: BEGIN ISOLATION LEVEL REPEATABLE READ;
-step s1merg: ALTER TABLE tpart MERGE PARTITIONS (tpart_00_10, tpart_10_20) INTO tpart_00_20;
-step s2b: BEGIN;
-step s2u: UPDATE tpart SET t = 'text01modif' where i = 1;
-step s1c: COMMIT;
-step s2u: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+-----------
-tpart_00_20 | 1|text01modif
-tpart_00_20 | 5|text05
-tpart_00_20 |15|text15
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(5 rows)
-
-
-starting permutation: s2b s2i s2c s1bs s1merg s2b s2u s1c s2c s2s
-step s2b: BEGIN;
-step s2i: INSERT INTO tpart VALUES (1, 'text01');
-step s2c: COMMIT;
-step s1bs: BEGIN ISOLATION LEVEL SERIALIZABLE;
-step s1merg: ALTER TABLE tpart MERGE PARTITIONS (tpart_00_10, tpart_10_20) INTO tpart_00_20;
-step s2b: BEGIN;
-step s2u: UPDATE tpart SET t = 'text01modif' where i = 1;
-step s1c: COMMIT;
-step s2u: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+-----------
-tpart_00_20 | 1|text01modif
-tpart_00_20 | 5|text05
-tpart_00_20 |15|text15
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(5 rows)
-
-
-starting permutation: s2brr s2i s2c s1b s1merg s2b s2u s1c s2c s2s
-step s2brr: BEGIN ISOLATION LEVEL REPEATABLE READ;
-step s2i: INSERT INTO tpart VALUES (1, 'text01');
-step s2c: COMMIT;
-step s1b: BEGIN;
-step s1merg: ALTER TABLE tpart MERGE PARTITIONS (tpart_00_10, tpart_10_20) INTO tpart_00_20;
-step s2b: BEGIN;
-step s2u: UPDATE tpart SET t = 'text01modif' where i = 1;
-step s1c: COMMIT;
-step s2u: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+-----------
-tpart_00_20 | 1|text01modif
-tpart_00_20 | 5|text05
-tpart_00_20 |15|text15
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(5 rows)
-
-
-starting permutation: s2brr s2i s2c s1brr s1merg s2b s2u s1c s2c s2s
-step s2brr: BEGIN ISOLATION LEVEL REPEATABLE READ;
-step s2i: INSERT INTO tpart VALUES (1, 'text01');
-step s2c: COMMIT;
-step s1brr: BEGIN ISOLATION LEVEL REPEATABLE READ;
-step s1merg: ALTER TABLE tpart MERGE PARTITIONS (tpart_00_10, tpart_10_20) INTO tpart_00_20;
-step s2b: BEGIN;
-step s2u: UPDATE tpart SET t = 'text01modif' where i = 1;
-step s1c: COMMIT;
-step s2u: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+-----------
-tpart_00_20 | 1|text01modif
-tpart_00_20 | 5|text05
-tpart_00_20 |15|text15
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(5 rows)
-
-
-starting permutation: s2brr s2i s2c s1bs s1merg s2b s2u s1c s2c s2s
-step s2brr: BEGIN ISOLATION LEVEL REPEATABLE READ;
-step s2i: INSERT INTO tpart VALUES (1, 'text01');
-step s2c: COMMIT;
-step s1bs: BEGIN ISOLATION LEVEL SERIALIZABLE;
-step s1merg: ALTER TABLE tpart MERGE PARTITIONS (tpart_00_10, tpart_10_20) INTO tpart_00_20;
-step s2b: BEGIN;
-step s2u: UPDATE tpart SET t = 'text01modif' where i = 1;
-step s1c: COMMIT;
-step s2u: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+-----------
-tpart_00_20 | 1|text01modif
-tpart_00_20 | 5|text05
-tpart_00_20 |15|text15
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(5 rows)
-
-
-starting permutation: s2bs s2i s2c s1b s1merg s2b s2u s1c s2c s2s
-step s2bs: BEGIN ISOLATION LEVEL SERIALIZABLE;
-step s2i: INSERT INTO tpart VALUES (1, 'text01');
-step s2c: COMMIT;
-step s1b: BEGIN;
-step s1merg: ALTER TABLE tpart MERGE PARTITIONS (tpart_00_10, tpart_10_20) INTO tpart_00_20;
-step s2b: BEGIN;
-step s2u: UPDATE tpart SET t = 'text01modif' where i = 1;
-step s1c: COMMIT;
-step s2u: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+-----------
-tpart_00_20 | 1|text01modif
-tpart_00_20 | 5|text05
-tpart_00_20 |15|text15
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(5 rows)
-
-
-starting permutation: s2bs s2i s2c s1brr s1merg s2b s2u s1c s2c s2s
-step s2bs: BEGIN ISOLATION LEVEL SERIALIZABLE;
-step s2i: INSERT INTO tpart VALUES (1, 'text01');
-step s2c: COMMIT;
-step s1brr: BEGIN ISOLATION LEVEL REPEATABLE READ;
-step s1merg: ALTER TABLE tpart MERGE PARTITIONS (tpart_00_10, tpart_10_20) INTO tpart_00_20;
-step s2b: BEGIN;
-step s2u: UPDATE tpart SET t = 'text01modif' where i = 1;
-step s1c: COMMIT;
-step s2u: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+-----------
-tpart_00_20 | 1|text01modif
-tpart_00_20 | 5|text05
-tpart_00_20 |15|text15
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(5 rows)
-
-
-starting permutation: s2bs s2i s2c s1bs s1merg s2b s2u s1c s2c s2s
-step s2bs: BEGIN ISOLATION LEVEL SERIALIZABLE;
-step s2i: INSERT INTO tpart VALUES (1, 'text01');
-step s2c: COMMIT;
-step s1bs: BEGIN ISOLATION LEVEL SERIALIZABLE;
-step s1merg: ALTER TABLE tpart MERGE PARTITIONS (tpart_00_10, tpart_10_20) INTO tpart_00_20;
-step s2b: BEGIN;
-step s2u: UPDATE tpart SET t = 'text01modif' where i = 1;
-step s1c: COMMIT;
-step s2u: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+-----------
-tpart_00_20 | 1|text01modif
-tpart_00_20 | 5|text05
-tpart_00_20 |15|text15
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(5 rows)
-
-
-starting permutation: s2b s2i s2c s1b s1merg s2b s2u2 s1c s2c s2s
-step s2b: BEGIN;
-step s2i: INSERT INTO tpart VALUES (1, 'text01');
-step s2c: COMMIT;
-step s1b: BEGIN;
-step s1merg: ALTER TABLE tpart MERGE PARTITIONS (tpart_00_10, tpart_10_20) INTO tpart_00_20;
-step s2b: BEGIN;
-step s2u2: UPDATE tpart SET i = 21 where i = 1;
-step s1c: COMMIT;
-step s2u2: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+------
-tpart_00_20 | 5|text05
-tpart_00_20 |15|text15
-tpart_20_30 |21|text01
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(5 rows)
-
-
-starting permutation: s2b s2i s2c s1b s1merg s2b s2u3 s1c s2c s2s
-step s2b: BEGIN;
-step s2i: INSERT INTO tpart VALUES (1, 'text01');
-step s2c: COMMIT;
-step s1b: BEGIN;
-step s1merg: ALTER TABLE tpart MERGE PARTITIONS (tpart_00_10, tpart_10_20) INTO tpart_00_20;
-step s2b: BEGIN;
-step s2u3: UPDATE tpart SET i = 11 where i = 1;
-step s1c: COMMIT;
-step s2u3: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+------
-tpart_00_20 | 5|text05
-tpart_00_20 |11|text01
-tpart_00_20 |15|text15
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(5 rows)
-
diff --git a/src/test/isolation/expected/partition-split.out b/src/test/isolation/expected/partition-split.out
deleted file mode 100644
index 02a5bb4f1f5..00000000000
--- a/src/test/isolation/expected/partition-split.out
+++ /dev/null
@@ -1,230 +0,0 @@
-Parsed test spec with 2 sessions
-
-starting permutation: s1b s1splt s2b s2i s1c s2c s2s
-step s1b: BEGIN;
-step s1splt: ALTER TABLE tpart SPLIT PARTITION tpart_10_20 INTO
- (PARTITION tpart_10_15 FOR VALUES FROM (10) TO (15),
- PARTITION tpart_15_20 FOR VALUES FROM (15) TO (20));
-step s2b: BEGIN;
-step s2i: INSERT INTO tpart VALUES (1, 'text01');
-step s1c: COMMIT;
-step s2i: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+------
-tpart_00_10 | 1|text01
-tpart_00_10 | 5|text05
-tpart_15_20 |15|text15
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(5 rows)
-
-
-starting permutation: s1b s1splt s2brr s2i s1c s2c s2s
-step s1b: BEGIN;
-step s1splt: ALTER TABLE tpart SPLIT PARTITION tpart_10_20 INTO
- (PARTITION tpart_10_15 FOR VALUES FROM (10) TO (15),
- PARTITION tpart_15_20 FOR VALUES FROM (15) TO (20));
-step s2brr: BEGIN ISOLATION LEVEL REPEATABLE READ;
-step s2i: INSERT INTO tpart VALUES (1, 'text01');
-step s1c: COMMIT;
-step s2i: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+------
-tpart_00_10 | 1|text01
-tpart_00_10 | 5|text05
-tpart_15_20 |15|text15
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(5 rows)
-
-
-starting permutation: s1b s1splt s2bs s2i s1c s2c s2s
-step s1b: BEGIN;
-step s1splt: ALTER TABLE tpart SPLIT PARTITION tpart_10_20 INTO
- (PARTITION tpart_10_15 FOR VALUES FROM (10) TO (15),
- PARTITION tpart_15_20 FOR VALUES FROM (15) TO (20));
-step s2bs: BEGIN ISOLATION LEVEL SERIALIZABLE;
-step s2i: INSERT INTO tpart VALUES (1, 'text01');
-step s1c: COMMIT;
-step s2i: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+------
-tpart_00_10 | 1|text01
-tpart_00_10 | 5|text05
-tpart_15_20 |15|text15
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(5 rows)
-
-
-starting permutation: s1brr s1splt s2b s2i s1c s2c s2s
-step s1brr: BEGIN ISOLATION LEVEL REPEATABLE READ;
-step s1splt: ALTER TABLE tpart SPLIT PARTITION tpart_10_20 INTO
- (PARTITION tpart_10_15 FOR VALUES FROM (10) TO (15),
- PARTITION tpart_15_20 FOR VALUES FROM (15) TO (20));
-step s2b: BEGIN;
-step s2i: INSERT INTO tpart VALUES (1, 'text01');
-step s1c: COMMIT;
-step s2i: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+------
-tpart_00_10 | 1|text01
-tpart_00_10 | 5|text05
-tpart_15_20 |15|text15
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(5 rows)
-
-
-starting permutation: s1brr s1splt s2brr s2i s1c s2c s2s
-step s1brr: BEGIN ISOLATION LEVEL REPEATABLE READ;
-step s1splt: ALTER TABLE tpart SPLIT PARTITION tpart_10_20 INTO
- (PARTITION tpart_10_15 FOR VALUES FROM (10) TO (15),
- PARTITION tpart_15_20 FOR VALUES FROM (15) TO (20));
-step s2brr: BEGIN ISOLATION LEVEL REPEATABLE READ;
-step s2i: INSERT INTO tpart VALUES (1, 'text01');
-step s1c: COMMIT;
-step s2i: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+------
-tpart_00_10 | 1|text01
-tpart_00_10 | 5|text05
-tpart_15_20 |15|text15
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(5 rows)
-
-
-starting permutation: s1brr s1splt s2bs s2i s1c s2c s2s
-step s1brr: BEGIN ISOLATION LEVEL REPEATABLE READ;
-step s1splt: ALTER TABLE tpart SPLIT PARTITION tpart_10_20 INTO
- (PARTITION tpart_10_15 FOR VALUES FROM (10) TO (15),
- PARTITION tpart_15_20 FOR VALUES FROM (15) TO (20));
-step s2bs: BEGIN ISOLATION LEVEL SERIALIZABLE;
-step s2i: INSERT INTO tpart VALUES (1, 'text01');
-step s1c: COMMIT;
-step s2i: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+------
-tpart_00_10 | 1|text01
-tpart_00_10 | 5|text05
-tpart_15_20 |15|text15
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(5 rows)
-
-
-starting permutation: s1bs s1splt s2b s2i s1c s2c s2s
-step s1bs: BEGIN ISOLATION LEVEL SERIALIZABLE;
-step s1splt: ALTER TABLE tpart SPLIT PARTITION tpart_10_20 INTO
- (PARTITION tpart_10_15 FOR VALUES FROM (10) TO (15),
- PARTITION tpart_15_20 FOR VALUES FROM (15) TO (20));
-step s2b: BEGIN;
-step s2i: INSERT INTO tpart VALUES (1, 'text01');
-step s1c: COMMIT;
-step s2i: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+------
-tpart_00_10 | 1|text01
-tpart_00_10 | 5|text05
-tpart_15_20 |15|text15
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(5 rows)
-
-
-starting permutation: s1bs s1splt s2brr s2i s1c s2c s2s
-step s1bs: BEGIN ISOLATION LEVEL SERIALIZABLE;
-step s1splt: ALTER TABLE tpart SPLIT PARTITION tpart_10_20 INTO
- (PARTITION tpart_10_15 FOR VALUES FROM (10) TO (15),
- PARTITION tpart_15_20 FOR VALUES FROM (15) TO (20));
-step s2brr: BEGIN ISOLATION LEVEL REPEATABLE READ;
-step s2i: INSERT INTO tpart VALUES (1, 'text01');
-step s1c: COMMIT;
-step s2i: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+------
-tpart_00_10 | 1|text01
-tpart_00_10 | 5|text05
-tpart_15_20 |15|text15
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(5 rows)
-
-
-starting permutation: s1bs s1splt s2bs s2i s1c s2c s2s
-step s1bs: BEGIN ISOLATION LEVEL SERIALIZABLE;
-step s1splt: ALTER TABLE tpart SPLIT PARTITION tpart_10_20 INTO
- (PARTITION tpart_10_15 FOR VALUES FROM (10) TO (15),
- PARTITION tpart_15_20 FOR VALUES FROM (15) TO (20));
-step s2bs: BEGIN ISOLATION LEVEL SERIALIZABLE;
-step s2i: INSERT INTO tpart VALUES (1, 'text01');
-step s1c: COMMIT;
-step s2i: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+------
-tpart_00_10 | 1|text01
-tpart_00_10 | 5|text05
-tpart_15_20 |15|text15
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(5 rows)
-
-
-starting permutation: s1b s1splt s2b s2u s1c s2c s2s
-step s1b: BEGIN;
-step s1splt: ALTER TABLE tpart SPLIT PARTITION tpart_10_20 INTO
- (PARTITION tpart_10_15 FOR VALUES FROM (10) TO (15),
- PARTITION tpart_15_20 FOR VALUES FROM (15) TO (20));
-step s2b: BEGIN;
-step s2u: UPDATE tpart SET i = 16 where i = 5;
-step s1c: COMMIT;
-step s2u: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+------
-tpart_15_20 |15|text15
-tpart_15_20 |16|text05
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(4 rows)
-
-
-starting permutation: s1b s1splt s2b s2u2 s1c s2c s2s
-step s1b: BEGIN;
-step s1splt: ALTER TABLE tpart SPLIT PARTITION tpart_10_20 INTO
- (PARTITION tpart_10_15 FOR VALUES FROM (10) TO (15),
- PARTITION tpart_15_20 FOR VALUES FROM (15) TO (20));
-step s2b: BEGIN;
-step s2u2: UPDATE tpart SET i = 11 where i = 15;
-step s1c: COMMIT;
-step s2u2: <... completed>
-step s2c: COMMIT;
-step s2s: SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i;
-tableoid | i|t
--------------+--+------
-tpart_00_10 | 5|text05
-tpart_10_15 |11|text15
-tpart_20_30 |25|text25
-tpart_default|35|text35
-(4 rows)
-
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 6469aafa2e1..1fcf4e63238 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -116,8 +116,6 @@ test: partition-key-update-1
test: partition-key-update-2
test: partition-key-update-3
test: partition-key-update-4
-test: partition-merge
-test: partition-split
test: plpgsql-toast
test: cluster-conflict
test: cluster-conflict-partition
diff --git a/src/test/isolation/specs/partition-merge.spec b/src/test/isolation/specs/partition-merge.spec
deleted file mode 100644
index f3c5ce2fbf1..00000000000
--- a/src/test/isolation/specs/partition-merge.spec
+++ /dev/null
@@ -1,62 +0,0 @@
-# Verify that MERGE operation locks DML operations with partitioned table
-
-setup
-{
- DROP TABLE IF EXISTS tpart;
- CREATE TABLE tpart(i int, t text) partition by range(i);
- CREATE TABLE tpart_00_10 PARTITION OF tpart FOR VALUES FROM (0) TO (10);
- CREATE TABLE tpart_10_20 PARTITION OF tpart FOR VALUES FROM (10) TO (20);
- CREATE TABLE tpart_20_30 PARTITION OF tpart FOR VALUES FROM (20) TO (30);
- CREATE TABLE tpart_default PARTITION OF tpart DEFAULT;
- INSERT INTO tpart VALUES (5, 'text05');
- INSERT INTO tpart VALUES (15, 'text15');
- INSERT INTO tpart VALUES (25, 'text25');
- INSERT INTO tpart VALUES (35, 'text35');
-}
-
-teardown
-{
- DROP TABLE tpart;
-}
-
-session s1
-step s1b { BEGIN; }
-step s1brr { BEGIN ISOLATION LEVEL REPEATABLE READ; }
-step s1bs { BEGIN ISOLATION LEVEL SERIALIZABLE; }
-step s1merg { ALTER TABLE tpart MERGE PARTITIONS (tpart_00_10, tpart_10_20) INTO tpart_00_20; }
-step s1c { COMMIT; }
-
-
-session s2
-step s2b { BEGIN; }
-step s2brr { BEGIN ISOLATION LEVEL REPEATABLE READ; }
-step s2bs { BEGIN ISOLATION LEVEL SERIALIZABLE; }
-step s2i { INSERT INTO tpart VALUES (1, 'text01'); }
-step s2u { UPDATE tpart SET t = 'text01modif' where i = 1; }
-step s2u2 { UPDATE tpart SET i = 21 where i = 1; }
-step s2u3 { UPDATE tpart SET i = 11 where i = 1; }
-step s2c { COMMIT; }
-step s2s { SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i; }
-
-
-# s2 inserts row into table. s1 starts MERGE PARTITIONS then
-# 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
-permutation s2b s2i s2c s1brr s1merg s2b s2u s1c s2c s2s
-permutation s2b s2i s2c s1bs s1merg s2b s2u s1c s2c s2s
-
-permutation s2brr s2i s2c s1b s1merg s2b s2u s1c s2c s2s
-permutation s2brr s2i s2c s1brr s1merg s2b s2u s1c s2c s2s
-permutation s2brr s2i s2c s1bs s1merg s2b s2u s1c s2c s2s
-
-permutation s2bs s2i s2c s1b s1merg s2b s2u s1c s2c s2s
-permutation s2bs s2i s2c s1brr s1merg s2b s2u s1c s2c s2s
-permutation s2bs s2i s2c s1bs s1merg s2b s2u s1c s2c s2s
-
-# Tuple routing between partitions.
-permutation s2b s2i s2c s1b s1merg s2b s2u2 s1c s2c s2s
-
-# Tuple routing between merging partitions.
-permutation s2b s2i s2c s1b s1merg s2b s2u3 s1c s2c s2s
diff --git a/src/test/isolation/specs/partition-split.spec b/src/test/isolation/specs/partition-split.spec
deleted file mode 100644
index af954be5dc0..00000000000
--- a/src/test/isolation/specs/partition-split.spec
+++ /dev/null
@@ -1,62 +0,0 @@
-# Verify that SPLIT operation locks DML operations with partitioned table
-
-setup
-{
- DROP TABLE IF EXISTS tpart;
- CREATE TABLE tpart(i int, t text) partition by range(i);
- CREATE TABLE tpart_00_10 PARTITION OF tpart FOR VALUES FROM (0) TO (10);
- CREATE TABLE tpart_10_20 PARTITION OF tpart FOR VALUES FROM (10) TO (20);
- CREATE TABLE tpart_20_30 PARTITION OF tpart FOR VALUES FROM (20) TO (30);
- CREATE TABLE tpart_default PARTITION OF tpart DEFAULT;
- INSERT INTO tpart VALUES (5, 'text05');
- INSERT INTO tpart VALUES (15, 'text15');
- INSERT INTO tpart VALUES (25, 'text25');
- INSERT INTO tpart VALUES (35, 'text35');
-}
-
-teardown
-{
- DROP TABLE tpart;
-}
-
-session s1
-step s1b { BEGIN; }
-step s1brr { BEGIN ISOLATION LEVEL REPEATABLE READ; }
-step s1bs { BEGIN ISOLATION LEVEL SERIALIZABLE; }
-step s1splt { ALTER TABLE tpart SPLIT PARTITION tpart_10_20 INTO
- (PARTITION tpart_10_15 FOR VALUES FROM (10) TO (15),
- PARTITION tpart_15_20 FOR VALUES FROM (15) TO (20)); }
-step s1c { COMMIT; }
-
-
-session s2
-step s2b { BEGIN; }
-step s2brr { BEGIN ISOLATION LEVEL REPEATABLE READ; }
-step s2bs { BEGIN ISOLATION LEVEL SERIALIZABLE; }
-step s2i { INSERT INTO tpart VALUES (1, 'text01'); }
-step s2c { COMMIT; }
-step s2s { SELECT tableoid::regclass, * FROM tpart ORDER BY tableoid::regclass::text COLLATE "C", i; }
-step s2u { UPDATE tpart SET i = 16 where i = 5; }
-step s2u2 { UPDATE tpart SET i = 11 where i = 15; }
-
-
-# s1 starts SPLIT PARTITION then s2 trying to insert row and
-# waits until s1 finished SPLIT operation.
-
-permutation s1b s1splt s2b s2i s1c s2c s2s
-permutation s1b s1splt s2brr s2i s1c s2c s2s
-permutation s1b s1splt s2bs s2i s1c s2c s2s
-
-permutation s1brr s1splt s2b s2i s1c s2c s2s
-permutation s1brr s1splt s2brr s2i s1c s2c s2s
-permutation s1brr s1splt s2bs s2i s1c s2c s2s
-
-permutation s1bs s1splt s2b s2i s1c s2c s2s
-permutation s1bs s1splt s2brr s2i s1c s2c s2s
-permutation s1bs s1splt s2bs s2i s1c s2c s2s
-
-# Tuple routing between partitions.
-permutation s1b s1splt s2b s2u s1c s2c s2s
-
-# Tuple routing inside splitting partition.
-permutation s1b s1splt s2b s2u2 s1c s2c s2s
diff --git a/src/test/modules/test_ddl_deparse/expected/alter_table.out b/src/test/modules/test_ddl_deparse/expected/alter_table.out
index 3a2f576f3b6..f1c6f05fe17 100644
--- a/src/test/modules/test_ddl_deparse/expected/alter_table.out
+++ b/src/test/modules/test_ddl_deparse/expected/alter_table.out
@@ -113,16 +113,6 @@ ALTER TABLE part DETACH PARTITION part2;
NOTICE: DDL test: type alter table, tag ALTER TABLE
NOTICE: subcommand: type DETACH PARTITION desc table part2
DROP TABLE part2;
-CREATE TABLE part2 PARTITION OF part FOR VALUES FROM (100) to (200);
-NOTICE: DDL test: type simple, tag CREATE TABLE
-ALTER TABLE part MERGE PARTITIONS (part1, part2) INTO part1;
-NOTICE: DDL test: type alter table, tag ALTER TABLE
-NOTICE: subcommand: type MERGE PARTITIONS desc
-ALTER TABLE part SPLIT PARTITION part1 INTO
- (PARTITION part1 FOR VALUES FROM (1) to (100),
- PARTITION part2 FOR VALUES FROM (100) to (200));
-NOTICE: DDL test: type alter table, tag ALTER TABLE
-NOTICE: subcommand: type SPLIT PARTITION desc
ALTER TABLE part ADD PRIMARY KEY (a);
NOTICE: DDL test: type alter table, tag ALTER TABLE
NOTICE: subcommand: type ADD CONSTRAINT (and recurse) desc constraint part_a_not_null on table part
diff --git a/src/test/modules/test_ddl_deparse/sql/alter_table.sql b/src/test/modules/test_ddl_deparse/sql/alter_table.sql
index 0980097048e..380ba266075 100644
--- a/src/test/modules/test_ddl_deparse/sql/alter_table.sql
+++ b/src/test/modules/test_ddl_deparse/sql/alter_table.sql
@@ -60,13 +60,6 @@ ALTER TABLE part ATTACH PARTITION part2 FOR VALUES FROM (101) to (200);
ALTER TABLE part DETACH PARTITION part2;
DROP TABLE part2;
-CREATE TABLE part2 PARTITION OF part FOR VALUES FROM (100) to (200);
-ALTER TABLE part MERGE PARTITIONS (part1, part2) INTO part1;
-
-ALTER TABLE part SPLIT PARTITION part1 INTO
- (PARTITION part1 FOR VALUES FROM (1) to (100),
- PARTITION part2 FOR VALUES FROM (100) to (200));
-
ALTER TABLE part ADD PRIMARY KEY (a);
CREATE TABLE tbl (
diff --git a/src/test/modules/test_ddl_deparse/test_ddl_deparse.c b/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
index 64a1dfa9f79..105872a3517 100644
--- a/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
+++ b/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
@@ -297,12 +297,6 @@ get_altertable_subcmdinfo(PG_FUNCTION_ARGS)
case AT_DetachPartitionFinalize:
strtype = "DETACH PARTITION ... FINALIZE";
break;
- case AT_SplitPartition:
- strtype = "SPLIT PARTITION";
- break;
- case AT_MergePartitions:
- strtype = "MERGE PARTITIONS";
- break;
case AT_AddIdentity:
strtype = "ADD IDENTITY";
break;
diff --git a/src/test/modules/test_extensions/expected/test_extdepend.out b/src/test/modules/test_extensions/expected/test_extdepend.out
index ede5dc64c04..0b62015d18c 100644
--- a/src/test/modules/test_extensions/expected/test_extdepend.out
+++ b/src/test/modules/test_extensions/expected/test_extdepend.out
@@ -186,123 +186,3 @@ DROP MATERIALIZED VIEW d;
DROP INDEX e;
DROP SCHEMA test_ext CASCADE;
NOTICE: drop cascades to table a
--- Fifth test: extension dependencies on partition indexes survive MERGE and
--- SPLIT PARTITION operations, and mismatches between source partitions are
--- reported.
-RESET search_path;
-CREATE EXTENSION test_ext3;
-CREATE EXTENSION test_ext5;
-CREATE TABLE part_extdep (i int, x int) PARTITION BY RANGE (i);
-CREATE TABLE part_extdep_1 PARTITION OF part_extdep FOR VALUES FROM (1) TO (2);
-CREATE TABLE part_extdep_2 PARTITION OF part_extdep FOR VALUES FROM (2) TO (3);
-CREATE TABLE part_extdep_3 PARTITION OF part_extdep FOR VALUES FROM (3) TO (4);
-CREATE TABLE part_extdep_4 PARTITION OF part_extdep FOR VALUES FROM (4) TO (5);
-CREATE TABLE part_extdep_5 PARTITION OF part_extdep FOR VALUES FROM (5) TO (6);
-CREATE INDEX part_extdep_i_idx ON part_extdep(i);
-CREATE INDEX part_extdep_x_idx ON part_extdep(x);
--- Partitions 1, 2, 3 depend on the same two extensions.
-ALTER INDEX part_extdep_1_i_idx DEPENDS ON EXTENSION test_ext3;
-ALTER INDEX part_extdep_1_x_idx DEPENDS ON EXTENSION test_ext5;
-ALTER INDEX part_extdep_2_i_idx DEPENDS ON EXTENSION test_ext3;
-ALTER INDEX part_extdep_2_x_idx DEPENDS ON EXTENSION test_ext5;
-ALTER INDEX part_extdep_3_i_idx DEPENDS ON EXTENSION test_ext3;
-ALTER INDEX part_extdep_3_x_idx DEPENDS ON EXTENSION test_ext5;
--- Partition 4 depends on a different extension on one index.
-ALTER INDEX part_extdep_4_i_idx DEPENDS ON EXTENSION test_ext5;
--- Partition 5 has no dependency at all.
--- Merge matching partitions: should succeed and preserve dependencies on the
--- new partition's indexes (DROP EXTENSION must fail, naming the new index).
-ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_1, part_extdep_2)
- INTO part_extdep_merged;
-DROP EXTENSION test_ext3;
-ERROR: cannot drop index part_extdep_merged_i_idx because index part_extdep_i_idx requires it
-HINT: You can drop index part_extdep_i_idx instead.
-SELECT c.relname, e.extname
-FROM pg_depend d
-JOIN pg_class c ON d.objid = c.oid
-JOIN pg_extension e ON d.refobjid = e.oid
-WHERE c.relname IN ('part_extdep_merged_i_idx', 'part_extdep_merged_x_idx')
- AND e.extname IN ('test_ext3', 'test_ext5')
- AND d.deptype = 'x'
-ORDER BY c.relname, e.extname;
- relname | extname
---------------------------+-----------
- part_extdep_merged_i_idx | test_ext3
- part_extdep_merged_x_idx | test_ext5
-(2 rows)
-
--- An index created directly on a partition has no parent in the partitioned
--- index tree. Such an index is dropped with its old partition during merge,
--- and any extension dependency it carries goes away with it: the dep is not
--- promoted to the merged partition. Verify by attaching test_ext9 to such
--- an orphan index, merging, and observing that test_ext9 becomes droppable.
-CREATE EXTENSION test_ext9;
-CREATE INDEX part_extdep_3_extra_idx ON part_extdep_3(x);
-ALTER INDEX part_extdep_3_extra_idx DEPENDS ON EXTENSION test_ext9;
-ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_merged, part_extdep_3)
- INTO part_extdep_merged2;
-DROP EXTENSION test_ext9;
--- Mismatched dependencies: partition 4's index depends on a different
--- extension than partition_merged2's. Both orderings must fail, and the
--- error must cite both partition indexes.
-ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_merged2, part_extdep_4)
- INTO part_extdep_bad;
-ERROR: cannot merge partitions with conflicting extension dependencies
-DETAIL: Partition indexes "part_extdep_4_i_idx" and "part_extdep_merged2_i_idx" depend on different extensions.
-ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_4, part_extdep_merged2)
- INTO part_extdep_bad;
-ERROR: cannot merge partitions with conflicting extension dependencies
-DETAIL: Partition indexes "part_extdep_4_i_idx" and "part_extdep_merged2_i_idx" depend on different extensions.
--- Empty vs non-empty dependency set (the subset case the earlier linear
--- check missed in one direction).
-ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_4, part_extdep_5)
- INTO part_extdep_bad;
-ERROR: cannot merge partitions with conflicting extension dependencies
-DETAIL: Partition indexes "part_extdep_4_i_idx" and "part_extdep_5_i_idx" depend on different extensions.
-ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_5, part_extdep_4)
- INTO part_extdep_bad;
-ERROR: cannot merge partitions with conflicting extension dependencies
-DETAIL: Partition indexes "part_extdep_4_i_idx" and "part_extdep_5_i_idx" depend on different extensions.
--- Subset: partition 5's i_idx depends on a strict superset of partition 4's
--- i_idx dependencies. Partition 4 = {test_ext5}, partition 5 will be
--- {test_ext3, test_ext5}. Both orderings must fail; in particular the case
--- where the first partition we walk has fewer extensions than the second
--- must still be rejected.
-ALTER INDEX part_extdep_5_i_idx DEPENDS ON EXTENSION test_ext3;
-ALTER INDEX part_extdep_5_i_idx DEPENDS ON EXTENSION test_ext5;
-ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_4, part_extdep_5)
- INTO part_extdep_bad;
-ERROR: cannot merge partitions with conflicting extension dependencies
-DETAIL: Partition indexes "part_extdep_4_i_idx" and "part_extdep_5_i_idx" depend on different extensions.
-ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_5, part_extdep_4)
- INTO part_extdep_bad;
-ERROR: cannot merge partitions with conflicting extension dependencies
-DETAIL: Partition indexes "part_extdep_4_i_idx" and "part_extdep_5_i_idx" depend on different extensions.
--- Reset partition 5 so it doesn't interfere with the SPLIT test below.
-ALTER INDEX part_extdep_5_i_idx NO DEPENDS ON EXTENSION test_ext3;
-ALTER INDEX part_extdep_5_i_idx NO DEPENDS ON EXTENSION test_ext5;
--- Split: the single source partition's dependencies must appear on every
--- new partition's matching index, identified by extension name.
-ALTER TABLE part_extdep SPLIT PARTITION part_extdep_merged2 INTO
- (PARTITION part_extdep_s1 FOR VALUES FROM (1) TO (3),
- PARTITION part_extdep_s2 FOR VALUES FROM (3) TO (4));
-SELECT c.relname, e.extname
-FROM pg_depend d
-JOIN pg_class c ON d.objid = c.oid
-JOIN pg_extension e ON d.refobjid = e.oid
-WHERE c.relname IN ('part_extdep_s1_i_idx', 'part_extdep_s1_x_idx',
- 'part_extdep_s2_i_idx', 'part_extdep_s2_x_idx')
- AND e.extname IN ('test_ext3', 'test_ext5')
- AND d.deptype = 'x'
-ORDER BY c.relname, e.extname;
- relname | extname
-----------------------+-----------
- part_extdep_s1_i_idx | test_ext3
- part_extdep_s1_x_idx | test_ext5
- part_extdep_s2_i_idx | test_ext3
- part_extdep_s2_x_idx | test_ext5
-(4 rows)
-
-DROP TABLE part_extdep;
-DROP EXTENSION test_ext3;
-DROP EXTENSION test_ext5;
diff --git a/src/test/modules/test_extensions/sql/test_extdepend.sql b/src/test/modules/test_extensions/sql/test_extdepend.sql
index ad734af1e71..63240a1af5d 100644
--- a/src/test/modules/test_extensions/sql/test_extdepend.sql
+++ b/src/test/modules/test_extensions/sql/test_extdepend.sql
@@ -88,107 +88,3 @@ DROP FUNCTION b();
DROP MATERIALIZED VIEW d;
DROP INDEX e;
DROP SCHEMA test_ext CASCADE;
-
--- Fifth test: extension dependencies on partition indexes survive MERGE and
--- SPLIT PARTITION operations, and mismatches between source partitions are
--- reported.
-RESET search_path;
-CREATE EXTENSION test_ext3;
-CREATE EXTENSION test_ext5;
-
-CREATE TABLE part_extdep (i int, x int) PARTITION BY RANGE (i);
-CREATE TABLE part_extdep_1 PARTITION OF part_extdep FOR VALUES FROM (1) TO (2);
-CREATE TABLE part_extdep_2 PARTITION OF part_extdep FOR VALUES FROM (2) TO (3);
-CREATE TABLE part_extdep_3 PARTITION OF part_extdep FOR VALUES FROM (3) TO (4);
-CREATE TABLE part_extdep_4 PARTITION OF part_extdep FOR VALUES FROM (4) TO (5);
-CREATE TABLE part_extdep_5 PARTITION OF part_extdep FOR VALUES FROM (5) TO (6);
-CREATE INDEX part_extdep_i_idx ON part_extdep(i);
-CREATE INDEX part_extdep_x_idx ON part_extdep(x);
-
--- Partitions 1, 2, 3 depend on the same two extensions.
-ALTER INDEX part_extdep_1_i_idx DEPENDS ON EXTENSION test_ext3;
-ALTER INDEX part_extdep_1_x_idx DEPENDS ON EXTENSION test_ext5;
-ALTER INDEX part_extdep_2_i_idx DEPENDS ON EXTENSION test_ext3;
-ALTER INDEX part_extdep_2_x_idx DEPENDS ON EXTENSION test_ext5;
-ALTER INDEX part_extdep_3_i_idx DEPENDS ON EXTENSION test_ext3;
-ALTER INDEX part_extdep_3_x_idx DEPENDS ON EXTENSION test_ext5;
-
--- Partition 4 depends on a different extension on one index.
-ALTER INDEX part_extdep_4_i_idx DEPENDS ON EXTENSION test_ext5;
-
--- Partition 5 has no dependency at all.
-
--- Merge matching partitions: should succeed and preserve dependencies on the
--- new partition's indexes (DROP EXTENSION must fail, naming the new index).
-ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_1, part_extdep_2)
- INTO part_extdep_merged;
-DROP EXTENSION test_ext3;
-SELECT c.relname, e.extname
-FROM pg_depend d
-JOIN pg_class c ON d.objid = c.oid
-JOIN pg_extension e ON d.refobjid = e.oid
-WHERE c.relname IN ('part_extdep_merged_i_idx', 'part_extdep_merged_x_idx')
- AND e.extname IN ('test_ext3', 'test_ext5')
- AND d.deptype = 'x'
-ORDER BY c.relname, e.extname;
-
--- An index created directly on a partition has no parent in the partitioned
--- index tree. Such an index is dropped with its old partition during merge,
--- and any extension dependency it carries goes away with it: the dep is not
--- promoted to the merged partition. Verify by attaching test_ext9 to such
--- an orphan index, merging, and observing that test_ext9 becomes droppable.
-CREATE EXTENSION test_ext9;
-CREATE INDEX part_extdep_3_extra_idx ON part_extdep_3(x);
-ALTER INDEX part_extdep_3_extra_idx DEPENDS ON EXTENSION test_ext9;
-ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_merged, part_extdep_3)
- INTO part_extdep_merged2;
-DROP EXTENSION test_ext9;
-
--- Mismatched dependencies: partition 4's index depends on a different
--- extension than partition_merged2's. Both orderings must fail, and the
--- error must cite both partition indexes.
-ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_merged2, part_extdep_4)
- INTO part_extdep_bad;
-ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_4, part_extdep_merged2)
- INTO part_extdep_bad;
-
--- Empty vs non-empty dependency set (the subset case the earlier linear
--- check missed in one direction).
-ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_4, part_extdep_5)
- INTO part_extdep_bad;
-ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_5, part_extdep_4)
- INTO part_extdep_bad;
-
--- Subset: partition 5's i_idx depends on a strict superset of partition 4's
--- i_idx dependencies. Partition 4 = {test_ext5}, partition 5 will be
--- {test_ext3, test_ext5}. Both orderings must fail; in particular the case
--- where the first partition we walk has fewer extensions than the second
--- must still be rejected.
-ALTER INDEX part_extdep_5_i_idx DEPENDS ON EXTENSION test_ext3;
-ALTER INDEX part_extdep_5_i_idx DEPENDS ON EXTENSION test_ext5;
-ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_4, part_extdep_5)
- INTO part_extdep_bad;
-ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_5, part_extdep_4)
- INTO part_extdep_bad;
--- Reset partition 5 so it doesn't interfere with the SPLIT test below.
-ALTER INDEX part_extdep_5_i_idx NO DEPENDS ON EXTENSION test_ext3;
-ALTER INDEX part_extdep_5_i_idx NO DEPENDS ON EXTENSION test_ext5;
-
--- Split: the single source partition's dependencies must appear on every
--- new partition's matching index, identified by extension name.
-ALTER TABLE part_extdep SPLIT PARTITION part_extdep_merged2 INTO
- (PARTITION part_extdep_s1 FOR VALUES FROM (1) TO (3),
- PARTITION part_extdep_s2 FOR VALUES FROM (3) TO (4));
-SELECT c.relname, e.extname
-FROM pg_depend d
-JOIN pg_class c ON d.objid = c.oid
-JOIN pg_extension e ON d.refobjid = e.oid
-WHERE c.relname IN ('part_extdep_s1_i_idx', 'part_extdep_s1_x_idx',
- 'part_extdep_s2_i_idx', 'part_extdep_s2_x_idx')
- AND e.extname IN ('test_ext3', 'test_ext5')
- AND d.deptype = 'x'
-ORDER BY c.relname, e.extname;
-
-DROP TABLE part_extdep;
-DROP EXTENSION test_ext3;
-DROP EXTENSION test_ext5;
diff --git a/src/test/regress/expected/partition_merge.out b/src/test/regress/expected/partition_merge.out
deleted file mode 100644
index ccda2b5843b..00000000000
--- a/src/test/regress/expected/partition_merge.out
+++ /dev/null
@@ -1,1174 +0,0 @@
---
--- PARTITIONS_MERGE
--- Tests for "ALTER TABLE ... MERGE PARTITIONS ..." command
---
-CREATE SCHEMA partitions_merge_schema;
-CREATE SCHEMA partitions_merge_schema2;
-SET search_path = partitions_merge_schema, public;
---
--- BY RANGE partitioning
---
---
--- Test for error codes
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_name VARCHAR(30), sales_amount INT, sales_date DATE) PARTITION BY RANGE (sales_date);
-CREATE TABLE sales_dec2021 PARTITION OF sales_range FOR VALUES FROM ('2021-12-01') TO ('2021-12-31');
-CREATE TABLE sales_jan2022 PARTITION OF sales_range FOR VALUES FROM ('2022-01-01') TO ('2022-02-01');
-CREATE TABLE sales_feb2022 PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-03-01');
-CREATE TABLE sales_mar2022 PARTITION OF sales_range FOR VALUES FROM ('2022-03-01') TO ('2022-04-01');
-CREATE TABLE sales_apr2022 (salesperson_id INT, salesperson_name VARCHAR(30), sales_amount INT, sales_date DATE) PARTITION BY RANGE (sales_date);
-CREATE TABLE sales_apr_1 PARTITION OF sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-04-15');
-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
-ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_feb2022) INTO sales_feb_mar_apr2022;
-ERROR: partition with name "sales_feb2022" is already used
-LINE 1: ...e MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_feb2...
- ^
--- ERROR
-ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_apr2022) INTO sales_feb_mar_apr2022;
-ERROR: "sales_apr2022" is not a table
-HINT: ALTER TABLE ... MERGE PARTITIONS can only merge partitions that don't have sub-partitions.
--- ERROR
--- (space between sections sales_jan2022 and sales_mar2022)
-ALTER TABLE sales_range MERGE PARTITIONS (sales_jan2022, sales_mar2022) INTO sales_jan_mar2022;
-ERROR: cannot merge partition "sales_mar2022" together with partition "sales_jan2022"
-DETAIL: The lower bound of partition "sales_mar2022" is not equal to the upper bound of partition "sales_jan2022".
-HINT: ALTER TABLE ... MERGE PARTITIONS requires the partition bounds to be adjacent.
--- ERROR
--- (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: cannot merge partition "sales_jan2022" together with partition "sales_dec2021"
-DETAIL: The lower bound of partition "sales_jan2022" is not equal to the upper bound of partition "sales_dec2021".
-HINT: ALTER TABLE ... MERGE PARTITIONS requires the partition bounds to be adjacent.
--- ERROR
-ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, partitions_merge_schema.sales_feb2022) INTO sales_feb_mar_apr2022;
-ERROR: partition with name "sales_feb2022" is already used
-LINE 1: ...e MERGE PARTITIONS (sales_feb2022, sales_mar2022, partitions...
- ^
--- ERROR
-ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_jan2022) INTO sales_apr_2;
-ERROR: relation "sales_apr_2" already exists
-CREATE VIEW jan2022v as SELECT * FROM sales_jan2022;
-ALTER TABLE sales_range MERGE PARTITIONS (sales_jan2022, sales_feb2022) INTO sales_dec_jan_feb2022;
-ERROR: cannot drop table sales_jan2022 because other objects depend on it
-DETAIL: view jan2022v depends on table sales_jan2022
-HINT: Use DROP ... CASCADE to drop the dependent objects too.
-DROP VIEW jan2022v;
--- NO ERROR: test for custom partitions order, source partitions not in the search_path
-SET search_path = partitions_merge_schema2, public;
-ALTER TABLE partitions_merge_schema.sales_range MERGE PARTITIONS (
- partitions_merge_schema.sales_feb2022,
- partitions_merge_schema.sales_mar2022,
- partitions_merge_schema.sales_jan2022) INTO sales_jan_feb_mar2022;
-SET search_path = partitions_merge_schema, public;
-PREPARE get_partition_info(regclass[]) AS
-SELECT c.oid::pg_catalog.regclass,
- c.relpersistence,
- c.relkind,
- i.inhdetachpending,
- pg_catalog.pg_get_expr(c.relpartbound, c.oid)
-FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
-WHERE c.oid = i.inhrelid AND i.inhparent = ANY($1)
-ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT',
- c.oid::regclass::text COLLATE "C";
-EXECUTE get_partition_info('{sales_range}');
- oid | relpersistence | relkind | inhdetachpending | pg_get_expr
-------------------------------------------------+----------------+---------+------------------+--------------------------------------------------
- partitions_merge_schema2.sales_jan_feb_mar2022 | p | r | f | FOR VALUES FROM ('01-01-2022') TO ('04-01-2022')
- sales_apr2022 | p | p | f | FOR VALUES FROM ('04-01-2022') TO ('05-01-2022')
- sales_dec2021 | p | r | f | FOR VALUES FROM ('12-01-2021') TO ('12-31-2021')
- sales_others | p | r | f | DEFAULT
-(4 rows)
-
-DROP TABLE sales_range;
---
--- Add rows into partitioned table, then merge partitions
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_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');
-CREATE TABLE sales_feb2022 PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-03-01');
-CREATE TABLE sales_mar2022 PARTITION OF sales_range FOR VALUES FROM ('2022-03-01') TO ('2022-04-01');
-CREATE TABLE sales_apr2022 PARTITION OF sales_range FOR VALUES FROM ('2022-04-01') TO ('2022-05-01');
-CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
-CREATE INDEX sales_range_sales_date_idx ON sales_range USING btree (sales_date);
-INSERT INTO sales_range VALUES
- (1, 'May', 1000, '2022-01-31'),
- (2, 'Smirnoff', 500, '2022-02-10'),
- (3, 'Ford', 2000, '2022-04-30'),
- (4, 'Ivanov', 750, '2022-04-13'),
- (5, 'Deev', 250, '2022-04-07'),
- (6, 'Poirot', 150, '2022-02-11'),
- (7, 'Li', 175, '2022-03-08'),
- (8, 'Ericsson', 185, '2022-02-23'),
- (9, 'Muller', 250, '2022-03-11'),
- (10, 'Halder', 350, '2022-01-28'),
- (11, 'Trump', 380, '2022-04-06'),
- (12, 'Plato', 350, '2022-03-19'),
- (13, 'Gandi', 377, '2022-01-09'),
- (14, 'Smith', 510, '2022-05-04');
-SELECT pg_catalog.pg_get_partkeydef('sales_range'::regclass);
- pg_get_partkeydef
---------------------
- RANGE (sales_date)
-(1 row)
-
--- show partitions with conditions:
-EXECUTE get_partition_info('{sales_range}');
- oid | relpersistence | relkind | inhdetachpending | pg_get_expr
----------------+----------------+---------+------------------+--------------------------------------------------
- sales_apr2022 | p | r | f | FOR VALUES FROM ('04-01-2022') TO ('05-01-2022')
- sales_feb2022 | p | r | f | FOR VALUES FROM ('02-01-2022') TO ('03-01-2022')
- sales_jan2022 | p | r | f | FOR VALUES FROM ('01-01-2022') TO ('02-01-2022')
- sales_mar2022 | p | r | f | FOR VALUES FROM ('03-01-2022') TO ('04-01-2022')
- sales_others | p | r | f | DEFAULT
-(5 rows)
-
--- check schema-qualified name of the new partition
-ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_apr2022) INTO partitions_merge_schema2.sales_feb_mar_apr2022;
--- show partitions with conditions:
-EXECUTE get_partition_info('{sales_range}');
- oid | relpersistence | relkind | inhdetachpending | pg_get_expr
-------------------------------------------------+----------------+---------+------------------+--------------------------------------------------
- partitions_merge_schema2.sales_feb_mar_apr2022 | p | r | f | FOR VALUES FROM ('02-01-2022') TO ('05-01-2022')
- sales_jan2022 | p | r | f | FOR VALUES FROM ('01-01-2022') TO ('02-01-2022')
- sales_others | p | r | f | DEFAULT
-(3 rows)
-
-SELECT * FROM pg_indexes WHERE tablename = 'sales_feb_mar_apr2022' and schemaname = 'partitions_merge_schema2';
- schemaname | tablename | indexname | tablespace | indexdef
---------------------------+-----------------------+--------------------------------------+------------+------------------------------------------------------------------------------------------------------------------------------
- partitions_merge_schema2 | sales_feb_mar_apr2022 | sales_feb_mar_apr2022_sales_date_idx | | CREATE INDEX sales_feb_mar_apr2022_sales_date_idx ON partitions_merge_schema2.sales_feb_mar_apr2022 USING btree (sales_date)
-(1 row)
-
-SELECT tableoid::regclass, * FROM sales_range ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
- tableoid | salesperson_id | salesperson_name | sales_amount | sales_date
-------------------------------------------------+----------------+------------------+--------------+------------
- partitions_merge_schema2.sales_feb_mar_apr2022 | 2 | Smirnoff | 500 | 02-10-2022
- partitions_merge_schema2.sales_feb_mar_apr2022 | 3 | Ford | 2000 | 04-30-2022
- partitions_merge_schema2.sales_feb_mar_apr2022 | 4 | Ivanov | 750 | 04-13-2022
- partitions_merge_schema2.sales_feb_mar_apr2022 | 5 | Deev | 250 | 04-07-2022
- partitions_merge_schema2.sales_feb_mar_apr2022 | 6 | Poirot | 150 | 02-11-2022
- partitions_merge_schema2.sales_feb_mar_apr2022 | 7 | Li | 175 | 03-08-2022
- partitions_merge_schema2.sales_feb_mar_apr2022 | 8 | Ericsson | 185 | 02-23-2022
- partitions_merge_schema2.sales_feb_mar_apr2022 | 9 | Muller | 250 | 03-11-2022
- partitions_merge_schema2.sales_feb_mar_apr2022 | 11 | Trump | 380 | 04-06-2022
- partitions_merge_schema2.sales_feb_mar_apr2022 | 12 | Plato | 350 | 03-19-2022
- sales_jan2022 | 1 | May | 1000 | 01-31-2022
- sales_jan2022 | 10 | Halder | 350 | 01-28-2022
- sales_jan2022 | 13 | Gandi | 377 | 01-09-2022
- sales_others | 14 | Smith | 510 | 05-04-2022
-(14 rows)
-
--- Use indexscan for testing indexes
-SET enable_seqscan = OFF;
-EXPLAIN (COSTS OFF) SELECT * FROM partitions_merge_schema2.sales_feb_mar_apr2022 where sales_date > '2022-01-01';
- QUERY PLAN
---------------------------------------------------------------------------------
- Index Scan using sales_feb_mar_apr2022_sales_date_idx on sales_feb_mar_apr2022
- Index Cond: (sales_date > '01-01-2022'::date)
-(2 rows)
-
-SELECT * FROM partitions_merge_schema2.sales_feb_mar_apr2022 where sales_date > '2022-01-01';
- salesperson_id | salesperson_name | sales_amount | sales_date
-----------------+------------------+--------------+------------
- 2 | Smirnoff | 500 | 02-10-2022
- 6 | Poirot | 150 | 02-11-2022
- 8 | Ericsson | 185 | 02-23-2022
- 7 | Li | 175 | 03-08-2022
- 9 | Muller | 250 | 03-11-2022
- 12 | Plato | 350 | 03-19-2022
- 11 | Trump | 380 | 04-06-2022
- 5 | Deev | 250 | 04-07-2022
- 4 | Ivanov | 750 | 04-13-2022
- 3 | Ford | 2000 | 04-30-2022
-(10 rows)
-
-RESET enable_seqscan;
-DROP TABLE sales_range;
---
--- Merge some partitions into DEFAULT partition
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_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');
-CREATE TABLE sales_feb2022 PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-03-01');
-CREATE TABLE sales_mar2022 PARTITION OF sales_range FOR VALUES FROM ('2022-03-01') TO ('2022-04-01');
-CREATE TABLE sales_apr2022 PARTITION OF sales_range FOR VALUES FROM ('2022-04-01') TO ('2022-05-01');
-CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
-CREATE INDEX sales_range_sales_date_idx ON sales_range USING btree (sales_date);
-INSERT INTO sales_range VALUES
- (1, 'May', 1000, '2022-01-31'),
- (2, 'Smirnoff', 500, '2022-02-10'),
- (3, 'Ford', 2000, '2022-04-30'),
- (4, 'Ivanov', 750, '2022-04-13'),
- (5, 'Deev', 250, '2022-04-07'),
- (6, 'Poirot', 150, '2022-02-11'),
- (7, 'Li', 175, '2022-03-08'),
- (8, 'Ericsson', 185, '2022-02-23'),
- (9, 'Muller', 250, '2022-03-11'),
- (10, 'Halder', 350, '2022-01-28'),
- (11, 'Trump', 380, '2022-04-06'),
- (12, 'Plato', 350, '2022-03-19'),
- (13, 'Gandi', 377, '2022-01-09'),
- (14, 'Smith', 510, '2022-05-04');
--- Merge partitions (include DEFAULT partition) into partition with the same
--- name
-ALTER TABLE sales_range MERGE PARTITIONS
- (sales_jan2022, sales_mar2022, partitions_merge_schema.sales_others) INTO sales_others;
-SELECT * FROM sales_others ORDER BY salesperson_id;
- salesperson_id | salesperson_name | sales_amount | sales_date
-----------------+------------------+--------------+------------
- 1 | May | 1000 | 01-31-2022
- 7 | Li | 175 | 03-08-2022
- 9 | Muller | 250 | 03-11-2022
- 10 | Halder | 350 | 01-28-2022
- 12 | Plato | 350 | 03-19-2022
- 13 | Gandi | 377 | 01-09-2022
- 14 | Smith | 510 | 05-04-2022
-(7 rows)
-
--- show partitions with conditions:
-EXECUTE get_partition_info('{sales_range}');
- oid | relpersistence | relkind | inhdetachpending | pg_get_expr
----------------+----------------+---------+------------------+--------------------------------------------------
- sales_apr2022 | p | r | f | FOR VALUES FROM ('04-01-2022') TO ('05-01-2022')
- sales_feb2022 | p | r | f | FOR VALUES FROM ('02-01-2022') TO ('03-01-2022')
- sales_others | p | r | f | DEFAULT
-(3 rows)
-
-DROP TABLE sales_range;
---
--- Test for:
--- * composite partition key;
--- * GENERATED column;
--- * column with DEFAULT value.
---
-CREATE TABLE sales_date (salesperson_name VARCHAR(30), sales_year INT, sales_month INT, sales_day INT,
- sales_date VARCHAR(10) GENERATED ALWAYS AS
- (LPAD(sales_year::text, 4, '0') || '.' || LPAD(sales_month::text, 2, '0') || '.' || LPAD(sales_day::text, 2, '0')) STORED,
- sales_department VARCHAR(30) DEFAULT 'Sales department')
- PARTITION BY RANGE (sales_year, sales_month, sales_day);
-CREATE TABLE sales_dec2022 PARTITION OF sales_date FOR VALUES FROM (2021, 12, 1) TO (2022, 1, 1);
-CREATE TABLE sales_jan2022 PARTITION OF sales_date FOR VALUES FROM (2022, 1, 1) TO (2022, 2, 1);
-CREATE TABLE sales_feb2022 PARTITION OF sales_date FOR VALUES FROM (2022, 2, 1) TO (2022, 3, 1);
-CREATE TABLE sales_other PARTITION OF sales_date FOR VALUES FROM (2022, 3, 1) TO (MAXVALUE, MAXVALUE, MAXVALUE);
-INSERT INTO sales_date(salesperson_name, sales_year, sales_month, sales_day) VALUES
- ('Manager1', 2021, 12, 7),
- ('Manager2', 2021, 12, 8),
- ('Manager3', 2022, 1, 1),
- ('Manager1', 2022, 2, 4),
- ('Manager2', 2022, 1, 2),
- ('Manager3', 2022, 2, 1),
- ('Manager1', 2022, 3, 3),
- ('Manager2', 2022, 3, 4),
- ('Manager3', 2022, 5, 1);
-SELECT tableoid::regclass, * FROM sales_date;
- tableoid | salesperson_name | sales_year | sales_month | sales_day | sales_date | sales_department
----------------+------------------+------------+-------------+-----------+------------+------------------
- sales_dec2022 | Manager1 | 2021 | 12 | 7 | 2021.12.07 | Sales department
- sales_dec2022 | Manager2 | 2021 | 12 | 8 | 2021.12.08 | Sales department
- sales_jan2022 | Manager3 | 2022 | 1 | 1 | 2022.01.01 | Sales department
- sales_jan2022 | Manager2 | 2022 | 1 | 2 | 2022.01.02 | Sales department
- sales_feb2022 | Manager1 | 2022 | 2 | 4 | 2022.02.04 | Sales department
- sales_feb2022 | Manager3 | 2022 | 2 | 1 | 2022.02.01 | Sales department
- sales_other | Manager1 | 2022 | 3 | 3 | 2022.03.03 | Sales department
- sales_other | Manager2 | 2022 | 3 | 4 | 2022.03.04 | Sales department
- sales_other | Manager3 | 2022 | 5 | 1 | 2022.05.01 | Sales department
-(9 rows)
-
-ALTER TABLE sales_date MERGE PARTITIONS (sales_jan2022, sales_feb2022) INTO sales_jan_feb2022;
-INSERT INTO sales_date(salesperson_name, sales_year, sales_month, sales_day) VALUES
- ('Manager1', 2022, 1, 10),
- ('Manager2', 2022, 2, 10);
-SELECT tableoid::regclass, * FROM sales_date;
- tableoid | salesperson_name | sales_year | sales_month | sales_day | sales_date | sales_department
--------------------+------------------+------------+-------------+-----------+------------+------------------
- sales_dec2022 | Manager1 | 2021 | 12 | 7 | 2021.12.07 | Sales department
- sales_dec2022 | Manager2 | 2021 | 12 | 8 | 2021.12.08 | Sales department
- sales_jan_feb2022 | Manager3 | 2022 | 1 | 1 | 2022.01.01 | Sales department
- sales_jan_feb2022 | Manager2 | 2022 | 1 | 2 | 2022.01.02 | Sales department
- sales_jan_feb2022 | Manager1 | 2022 | 2 | 4 | 2022.02.04 | Sales department
- sales_jan_feb2022 | Manager3 | 2022 | 2 | 1 | 2022.02.01 | Sales department
- sales_jan_feb2022 | Manager1 | 2022 | 1 | 10 | 2022.01.10 | Sales department
- sales_jan_feb2022 | Manager2 | 2022 | 2 | 10 | 2022.02.10 | Sales department
- sales_other | Manager1 | 2022 | 3 | 3 | 2022.03.03 | Sales department
- sales_other | Manager2 | 2022 | 3 | 4 | 2022.03.04 | Sales department
- sales_other | Manager3 | 2022 | 5 | 1 | 2022.05.01 | Sales department
-(11 rows)
-
-DROP TABLE sales_date;
---
--- Test: merge partitions of partitioned table with triggers
---
-CREATE TABLE salespeople(salesperson_id INT PRIMARY KEY, salesperson_name VARCHAR(30)) PARTITION BY RANGE (salesperson_id);
-CREATE TABLE salespeople01_10 PARTITION OF salespeople FOR VALUES FROM (1) TO (10);
-CREATE TABLE salespeople10_20 PARTITION OF salespeople FOR VALUES FROM (10) TO (20);
-CREATE TABLE salespeople20_30 PARTITION OF salespeople FOR VALUES FROM (20) TO (30);
-CREATE TABLE salespeople30_40 PARTITION OF salespeople FOR VALUES FROM (30) TO (40);
-INSERT INTO salespeople VALUES (1, 'Poirot');
-CREATE OR REPLACE FUNCTION after_insert_row_trigger() RETURNS trigger LANGUAGE 'plpgsql' AS $BODY$
-BEGIN
- RAISE NOTICE 'trigger(%) called: action = %, when = %, level = %', TG_ARGV[0], TG_OP, TG_WHEN, TG_LEVEL;
- RETURN NULL;
-END;
-$BODY$;
-CREATE TRIGGER salespeople_after_insert_statement_trigger
- AFTER INSERT
- ON salespeople
- FOR EACH STATEMENT
- EXECUTE PROCEDURE after_insert_row_trigger('salespeople');
-CREATE TRIGGER salespeople_after_insert_row_trigger
- AFTER INSERT
- ON salespeople
- FOR EACH ROW
- EXECUTE PROCEDURE after_insert_row_trigger('salespeople');
--- 2 triggers should fire here (row + statement):
-INSERT INTO salespeople VALUES (10, 'May');
-NOTICE: trigger(salespeople) called: action = INSERT, when = AFTER, level = ROW
-NOTICE: trigger(salespeople) called: action = INSERT, when = AFTER, level = STATEMENT
--- 1 trigger should fire here (row):
-INSERT INTO salespeople10_20 VALUES (19, 'Ivanov');
-NOTICE: trigger(salespeople) called: action = INSERT, when = AFTER, level = ROW
-ALTER TABLE salespeople MERGE PARTITIONS (salespeople10_20, salespeople20_30, salespeople30_40) INTO salespeople10_40;
--- 2 triggers should fire here (row + statement):
-INSERT INTO salespeople VALUES (20, 'Smirnoff');
-NOTICE: trigger(salespeople) called: action = INSERT, when = AFTER, level = ROW
-NOTICE: trigger(salespeople) called: action = INSERT, when = AFTER, level = STATEMENT
--- 1 trigger should fire here (row):
-INSERT INTO salespeople10_40 VALUES (30, 'Ford');
-NOTICE: trigger(salespeople) called: action = INSERT, when = AFTER, level = ROW
-SELECT * FROM salespeople01_10;
- salesperson_id | salesperson_name
-----------------+------------------
- 1 | Poirot
-(1 row)
-
-SELECT * FROM salespeople10_40;
- salesperson_id | salesperson_name
-----------------+------------------
- 10 | May
- 19 | Ivanov
- 20 | Smirnoff
- 30 | Ford
-(4 rows)
-
-DROP TABLE salespeople;
-DROP FUNCTION after_insert_row_trigger();
---
--- Test: merge partitions with deleted columns
---
-CREATE TABLE salespeople(salesperson_id INT PRIMARY KEY, salesperson_name VARCHAR(30)) PARTITION BY RANGE (salesperson_id);
-CREATE TABLE salespeople01_10 PARTITION OF salespeople FOR VALUES FROM (1) TO (10);
--- Create partitions with some deleted columns:
-CREATE TABLE salespeople10_20(d1 VARCHAR(30), salesperson_id INT PRIMARY KEY, salesperson_name VARCHAR(30));
-CREATE TABLE salespeople20_30(salesperson_id INT PRIMARY KEY, d2 INT, salesperson_name VARCHAR(30));
-CREATE TABLE salespeople30_40(salesperson_id INT PRIMARY KEY, d3 DATE, salesperson_name VARCHAR(30));
-INSERT INTO salespeople10_20 VALUES ('dummy value 1', 19, 'Ivanov');
-INSERT INTO salespeople20_30 VALUES (20, 101, 'Smirnoff');
-INSERT INTO salespeople30_40 VALUES (31, now(), 'Popov');
-ALTER TABLE salespeople10_20 DROP COLUMN d1;
-ALTER TABLE salespeople20_30 DROP COLUMN d2;
-ALTER TABLE salespeople30_40 DROP COLUMN d3;
-ALTER TABLE salespeople ATTACH PARTITION salespeople10_20 FOR VALUES FROM (10) TO (20);
-ALTER TABLE salespeople ATTACH PARTITION salespeople20_30 FOR VALUES FROM (20) TO (30);
-ALTER TABLE salespeople ATTACH PARTITION salespeople30_40 FOR VALUES FROM (30) TO (40);
-INSERT INTO salespeople VALUES
- (1, 'Poirot'),
- (10, 'May'),
- (30, 'Ford');
-ALTER TABLE salespeople MERGE PARTITIONS (salespeople10_20, salespeople20_30, salespeople30_40) INTO salespeople10_40;
-select * from salespeople;
- salesperson_id | salesperson_name
-----------------+------------------
- 1 | Poirot
- 19 | Ivanov
- 10 | May
- 20 | Smirnoff
- 31 | Popov
- 30 | Ford
-(6 rows)
-
-select * from salespeople01_10;
- salesperson_id | salesperson_name
-----------------+------------------
- 1 | Poirot
-(1 row)
-
-select * from salespeople10_40;
- salesperson_id | salesperson_name
-----------------+------------------
- 19 | Ivanov
- 10 | May
- 20 | Smirnoff
- 31 | Popov
- 30 | Ford
-(5 rows)
-
-DROP TABLE salespeople;
---
--- Test: merge sub-partitions
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_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');
-CREATE TABLE sales_feb2022 PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-03-01');
-CREATE TABLE sales_mar2022 PARTITION OF sales_range FOR VALUES FROM ('2022-03-01') TO ('2022-04-01');
-CREATE TABLE sales_apr2022 (salesperson_id INT, salesperson_name VARCHAR(30), sales_amount INT, sales_date DATE) PARTITION BY RANGE (sales_date);
-CREATE TABLE sales_apr2022_01_10 PARTITION OF sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-04-10');
-CREATE TABLE sales_apr2022_10_20 PARTITION OF sales_apr2022 FOR VALUES FROM ('2022-04-10') TO ('2022-04-20');
-CREATE TABLE sales_apr2022_20_30 PARTITION OF sales_apr2022 FOR VALUES FROM ('2022-04-20') 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;
-CREATE INDEX sales_range_sales_date_idx ON sales_range USING btree (sales_date);
-INSERT INTO sales_range VALUES
- (1, 'May', 1000, '2022-01-31'),
- (2, 'Smirnoff', 500, '2022-02-10'),
- (3, 'Ford', 2000, '2022-04-30'),
- (4, 'Ivanov', 750, '2022-04-13'),
- (5, 'Deev', 250, '2022-04-07'),
- (6, 'Poirot', 150, '2022-02-11'),
- (7, 'Li', 175, '2022-03-08'),
- (8, 'Ericsson', 185, '2022-02-23'),
- (9, 'Muller', 250, '2022-03-11'),
- (10, 'Halder', 350, '2022-01-28'),
- (11, 'Trump', 380, '2022-04-06'),
- (12, 'Plato', 350, '2022-03-19'),
- (13, 'Gandi', 377, '2022-01-09'),
- (14, 'Smith', 510, '2022-05-04');
-SELECT tableoid::regclass, * FROM sales_apr2022 ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
- tableoid | salesperson_id | salesperson_name | sales_amount | sales_date
----------------------+----------------+------------------+--------------+------------
- sales_apr2022_01_10 | 5 | Deev | 250 | 04-07-2022
- sales_apr2022_01_10 | 11 | Trump | 380 | 04-06-2022
- sales_apr2022_10_20 | 4 | Ivanov | 750 | 04-13-2022
- sales_apr2022_20_30 | 3 | Ford | 2000 | 04-30-2022
-(4 rows)
-
-ALTER TABLE sales_apr2022 MERGE PARTITIONS (sales_apr2022_01_10, sales_apr2022_10_20, sales_apr2022_20_30) INTO sales_apr_all;
-SELECT tableoid::regclass, * FROM sales_apr2022 ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
- tableoid | salesperson_id | salesperson_name | sales_amount | sales_date
----------------+----------------+------------------+--------------+------------
- sales_apr_all | 3 | Ford | 2000 | 04-30-2022
- sales_apr_all | 4 | Ivanov | 750 | 04-13-2022
- sales_apr_all | 5 | Deev | 250 | 04-07-2022
- sales_apr_all | 11 | Trump | 380 | 04-06-2022
-(4 rows)
-
-DROP TABLE sales_range;
---
--- BY LIST partitioning
---
---
--- Test: specific errors for BY LIST partitioning
---
-CREATE TABLE sales_list
-(salesperson_id INT GENERATED ALWAYS AS IDENTITY,
- salesperson_name VARCHAR(30),
- sales_state VARCHAR(20),
- sales_amount INT,
- sales_date DATE)
-PARTITION BY LIST (sales_state);
-CREATE TABLE sales_nord PARTITION OF sales_list FOR VALUES IN ('Oslo', 'St. Petersburg', 'Helsinki');
-CREATE TABLE sales_west PARTITION OF sales_list FOR VALUES IN ('Lisbon', 'New York', 'Madrid');
-CREATE TABLE sales_east PARTITION OF sales_list FOR VALUES IN ('Beijing', 'Delhi', 'Vladivostok');
-CREATE TABLE sales_central PARTITION OF sales_list FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv');
-CREATE TABLE sales_others PARTITION OF sales_list DEFAULT;
-CREATE TABLE sales_list2 (LIKE sales_list) PARTITION BY LIST (sales_state);
-CREATE TABLE sales_nord2 PARTITION OF sales_list2 FOR VALUES IN ('Oslo', 'St. Petersburg', 'Helsinki');
-CREATE TABLE sales_others2 PARTITION OF sales_list2 DEFAULT;
-CREATE TABLE sales_external (LIKE sales_list);
-CREATE TABLE sales_external2 (vch VARCHAR(5));
--- ERROR
-ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_external) INTO sales_all;
-ERROR: "sales_external" is not a partition of partitioned table "sales_list"
-HINT: ALTER TABLE ... MERGE PARTITIONS can only merge partitions that don't have sub-partitions.
--- ERROR
-ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_external2) INTO sales_all;
-ERROR: "sales_external2" is not a partition of partitioned table "sales_list"
-HINT: ALTER TABLE ... MERGE PARTITIONS can only merge partitions that don't have sub-partitions.
--- ERROR
-ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_nord2, sales_east) INTO sales_all;
-ERROR: relation "sales_nord2" is not a partition of relation "sales_list"
-HINT: ALTER TABLE ... MERGE PARTITIONS can only merge partitions that don't have sub-partitions.
-DROP TABLE sales_external2;
-DROP TABLE sales_external;
-DROP TABLE sales_list2;
-DROP TABLE sales_list;
---
--- Test: BY LIST partitioning, MERGE PARTITIONS with data
---
-CREATE TABLE sales_list
-(salesperson_id INT GENERATED ALWAYS AS IDENTITY,
- salesperson_name VARCHAR(30),
- sales_state VARCHAR(20),
- sales_amount INT,
- sales_date DATE)
-PARTITION BY LIST (sales_state);
-CREATE INDEX sales_list_salesperson_name_idx ON sales_list USING btree (salesperson_name);
-CREATE INDEX sales_list_sales_state_idx ON sales_list USING btree (sales_state);
-CREATE TABLE sales_nord PARTITION OF sales_list FOR VALUES IN ('Oslo', 'St. Petersburg', 'Helsinki');
-CREATE TABLE sales_west PARTITION OF sales_list FOR VALUES IN ('Lisbon', 'New York', 'Madrid');
-CREATE TABLE sales_east PARTITION OF sales_list FOR VALUES IN ('Beijing', 'Delhi', 'Vladivostok');
-CREATE TABLE sales_central PARTITION OF sales_list FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv');
-CREATE TABLE sales_others PARTITION OF sales_list DEFAULT;
-INSERT INTO sales_list (salesperson_name, sales_state, sales_amount, sales_date) VALUES
- ('Trump', 'Beijing', 1000, '2022-03-01'),
- ('Smirnoff', 'New York', 500, '2022-03-03'),
- ('Ford', 'St. Petersburg', 2000, '2022-03-05'),
- ('Ivanov', 'Warsaw', 750, '2022-03-04'),
- ('Deev', 'Lisbon', 250, '2022-03-07'),
- ('Poirot', 'Berlin', 1000, '2022-03-01'),
- ('May', 'Helsinki', 1200, '2022-03-06'),
- ('Li', 'Vladivostok', 1150, '2022-03-09'),
- ('May', 'Helsinki', 1200, '2022-03-11'),
- ('Halder', 'Oslo', 800, '2022-03-02'),
- ('Muller', 'Madrid', 650, '2022-03-05'),
- ('Smith', 'Kyiv', 350, '2022-03-10'),
- ('Gandi', 'Warsaw', 150, '2022-03-08'),
- ('Plato', 'Lisbon', 950, '2022-03-05');
--- show partitions with conditions:
-EXECUTE get_partition_info('{sales_list}');
- oid | relpersistence | relkind | inhdetachpending | pg_get_expr
----------------+----------------+---------+------------------+------------------------------------------------------
- sales_central | p | r | f | FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv')
- sales_east | p | r | f | FOR VALUES IN ('Beijing', 'Delhi', 'Vladivostok')
- sales_nord | p | r | f | FOR VALUES IN ('Oslo', 'St. Petersburg', 'Helsinki')
- sales_west | p | r | f | FOR VALUES IN ('Lisbon', 'New York', 'Madrid')
- sales_others | p | r | f | DEFAULT
-(5 rows)
-
-ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_central) INTO sales_all;
--- show partitions with conditions:
-EXECUTE get_partition_info('{sales_list}');
- oid | relpersistence | relkind | inhdetachpending | pg_get_expr
---------------+----------------+---------+------------------+---------------------------------------------------------------------------------------------------------------
- sales_all | p | r | f | FOR VALUES IN ('Lisbon', 'New York', 'Madrid', 'Beijing', 'Delhi', 'Vladivostok', 'Warsaw', 'Berlin', 'Kyiv')
- sales_nord | p | r | f | FOR VALUES IN ('Oslo', 'St. Petersburg', 'Helsinki')
- sales_others | p | r | f | DEFAULT
-(3 rows)
-
-SELECT tableoid::regclass, * FROM sales_list ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
- tableoid | salesperson_id | salesperson_name | sales_state | sales_amount | sales_date
-------------+----------------+------------------+----------------+--------------+------------
- sales_all | 1 | Trump | Beijing | 1000 | 03-01-2022
- sales_all | 2 | Smirnoff | New York | 500 | 03-03-2022
- sales_all | 4 | Ivanov | Warsaw | 750 | 03-04-2022
- sales_all | 5 | Deev | Lisbon | 250 | 03-07-2022
- sales_all | 6 | Poirot | Berlin | 1000 | 03-01-2022
- sales_all | 8 | Li | Vladivostok | 1150 | 03-09-2022
- sales_all | 11 | Muller | Madrid | 650 | 03-05-2022
- sales_all | 12 | Smith | Kyiv | 350 | 03-10-2022
- sales_all | 13 | Gandi | Warsaw | 150 | 03-08-2022
- sales_all | 14 | Plato | Lisbon | 950 | 03-05-2022
- sales_nord | 3 | Ford | St. Petersburg | 2000 | 03-05-2022
- sales_nord | 7 | May | Helsinki | 1200 | 03-06-2022
- sales_nord | 9 | May | Helsinki | 1200 | 03-11-2022
- sales_nord | 10 | Halder | Oslo | 800 | 03-02-2022
-(14 rows)
-
--- Use indexscan for testing indexes after merging partitions
-SET enable_seqscan = OFF;
-EXPLAIN (COSTS OFF) SELECT * FROM sales_all WHERE sales_state = 'Warsaw';
- QUERY PLAN
----------------------------------------------------------
- Index Scan using sales_all_sales_state_idx on sales_all
- Index Cond: ((sales_state)::text = 'Warsaw'::text)
-(2 rows)
-
-SELECT * FROM sales_all WHERE sales_state = 'Warsaw';
- salesperson_id | salesperson_name | sales_state | sales_amount | sales_date
-----------------+------------------+-------------+--------------+------------
- 4 | Ivanov | Warsaw | 750 | 03-04-2022
- 13 | Gandi | Warsaw | 150 | 03-08-2022
-(2 rows)
-
-EXPLAIN (COSTS OFF) SELECT * FROM sales_list WHERE sales_state = 'Warsaw';
- QUERY PLAN
---------------------------------------------------------------------
- Index Scan using sales_all_sales_state_idx on sales_all sales_list
- Index Cond: ((sales_state)::text = 'Warsaw'::text)
-(2 rows)
-
-SELECT * FROM sales_list WHERE sales_state = 'Warsaw';
- salesperson_id | salesperson_name | sales_state | sales_amount | sales_date
-----------------+------------------+-------------+--------------+------------
- 4 | Ivanov | Warsaw | 750 | 03-04-2022
- 13 | Gandi | Warsaw | 150 | 03-08-2022
-(2 rows)
-
-EXPLAIN (COSTS OFF) SELECT * FROM sales_list WHERE salesperson_name = 'Ivanov';
- QUERY PLAN
----------------------------------------------------------------------------------
- Append
- -> Index Scan using sales_all_salesperson_name_idx on sales_all sales_list_1
- Index Cond: ((salesperson_name)::text = 'Ivanov'::text)
- -> Bitmap Heap Scan on sales_nord sales_list_2
- Recheck Cond: ((salesperson_name)::text = 'Ivanov'::text)
- -> Bitmap Index Scan on sales_nord_salesperson_name_idx
- Index Cond: ((salesperson_name)::text = 'Ivanov'::text)
- -> Bitmap Heap Scan on sales_others sales_list_3
- Recheck Cond: ((salesperson_name)::text = 'Ivanov'::text)
- -> Bitmap Index Scan on sales_others_salesperson_name_idx
- Index Cond: ((salesperson_name)::text = 'Ivanov'::text)
-(11 rows)
-
-SELECT * FROM sales_list WHERE salesperson_name = 'Ivanov';
- salesperson_id | salesperson_name | sales_state | sales_amount | sales_date
-----------------+------------------+-------------+--------------+------------
- 4 | Ivanov | Warsaw | 750 | 03-04-2022
-(1 row)
-
-RESET enable_seqscan;
-DROP TABLE sales_list;
---
--- Try to MERGE partitions of another table.
---
-CREATE TABLE t1 (i int, a int, b int, c int) PARTITION BY RANGE (a, b);
-CREATE TABLE t1p1 PARTITION OF t1 FOR VALUES FROM (1, 1) TO (1, 2);
-CREATE TABLE t2 (i int, t text) PARTITION BY RANGE (t);
-CREATE TABLE t2pa PARTITION OF t2 FOR VALUES FROM ('A') TO ('C');
-CREATE TABLE t3 (i int, t text);
--- ERROR
-ALTER TABLE t2 MERGE PARTITIONS (t1p1, t2pa) INTO t2p;
-ERROR: relation "t1p1" is not a partition of relation "t2"
-HINT: ALTER TABLE ... MERGE PARTITIONS can only merge partitions that don't have sub-partitions.
--- ERROR
-ALTER TABLE t2 MERGE PARTITIONS (t2pa, t3) INTO t2p;
-ERROR: "t3" is not a partition of partitioned table "t2"
-HINT: ALTER TABLE ... MERGE PARTITIONS can only merge partitions that don't have sub-partitions.
-DROP TABLE t3;
-DROP TABLE t2;
-DROP TABLE t1;
---
--- Check the partition index name if the partition name is the same as one
--- of the merged partitions.
---
-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 values should be 'tp_1_2_pkey' and 'tp_1_2_i_idx'.
-\d+ tp_1_2
- Table "partitions_merge_schema.tp_1_2"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------+---------+--------------+-------------
- i | integer | | not null | | plain | |
-Partition of: t FOR VALUES FROM (0) TO (2)
-Partition constraint: ((i IS NOT NULL) AND (i >= 0) AND (i < 2))
-Indexes:
- "tp_1_2_pkey" PRIMARY KEY, btree (i)
- "tp_1_2_i_idx" btree (i)
-Not-null constraints:
- "t_i_not_null" NOT NULL "i" (inherited)
-
-DROP TABLE t;
---
--- Try to MERGE partitions of temporary table.
---
-BEGIN;
-SHOW search_path;
- search_path
----------------------------------
- partitions_merge_schema, public
-(1 row)
-
-CREATE TEMP TABLE t (i int) PARTITION BY RANGE (i) ON COMMIT DROP;
-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);
-CREATE TEMP TABLE tp_2_3 PARTITION OF t FOR VALUES FROM (2) TO (3);
-CREATE TEMP TABLE tp_3_4 PARTITION OF t FOR VALUES FROM (3) TO (4);
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO pg_temp.tp_0_2;
-ALTER TABLE t MERGE PARTITIONS (tp_0_2, tp_2_3) INTO pg_temp.tp_0_3;
--- Partition should be temporary.
-EXECUTE get_partition_info('{t}');
- oid | relpersistence | relkind | inhdetachpending | pg_get_expr
---------+----------------+---------+------------------+----------------------------
- tp_0_3 | t | r | f | FOR VALUES FROM (0) TO (3)
- tp_3_4 | t | r | f | FOR VALUES FROM (3) TO (4)
-(2 rows)
-
--- ERROR
-ALTER TABLE t MERGE PARTITIONS (tp_0_3, tp_3_4) INTO tp_0_4;
-ERROR: cannot create a permanent relation as partition of temporary relation "t"
-ROLLBACK;
---
--- Try mixing permanent and temporary partitions.
---
-BEGIN;
-SET search_path = partitions_merge_schema, pg_temp, public;
-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)
-
-EXECUTE get_partition_info('{t}');
- oid | relpersistence | relkind | inhdetachpending | pg_get_expr
---------+----------------+---------+------------------+----------------------------
- tp_0_1 | p | r | f | FOR VALUES FROM (0) TO (1)
- tp_1_2 | p | r | f | FOR VALUES FROM (1) TO (2)
-(2 rows)
-
-SAVEPOINT s;
-SET search_path = pg_temp, partitions_merge_schema, 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"
-ROLLBACK TO SAVEPOINT s;
-SET search_path = partitions_merge_schema, public;
--- 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"
-ROLLBACK;
-BEGIN;
-SET search_path = pg_temp, partitions_merge_schema, public;
-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)
-
-EXECUTE get_partition_info('{t}');
- oid | relpersistence | relkind | inhdetachpending | pg_get_expr
---------+----------------+---------+------------------+----------------------------
- tp_0_1 | t | r | f | FOR VALUES FROM (0) TO (1)
- tp_1_2 | t | r | f | FOR VALUES FROM (1) TO (2)
-(2 rows)
-
-SET search_path = partitions_merge_schema, pg_temp, public;
--- Can't merge temporary partitions into a persistent partition
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
-ERROR: cannot create a permanent relation as partition of temporary relation "t"
-ROLLBACK;
-DEALLOCATE get_partition_info;
--- Check the new partition inherits parent's tablespace
-SET search_path = partitions_merge_schema, public;
-CREATE TABLE t (i int PRIMARY KEY USING INDEX TABLESPACE regress_tblspace)
- PARTITION BY RANGE (i) TABLESPACE regress_tblspace;
-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);
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
-SELECT tablename, tablespace FROM pg_tables
- WHERE tablename IN ('t', 'tp_0_2') AND schemaname = 'partitions_merge_schema'
- ORDER BY tablename COLLATE "C", tablespace COLLATE "C";
- tablename | tablespace
------------+------------------
- t | regress_tblspace
- tp_0_2 | regress_tblspace
-(2 rows)
-
-SELECT tablename, indexname, tablespace FROM pg_indexes
- WHERE tablename IN ('t', 'tp_0_2') AND schemaname = 'partitions_merge_schema'
- ORDER BY tablename COLLATE "C", indexname COLLATE "C", tablespace COLLATE "C";
- tablename | indexname | tablespace
------------+-------------+------------------
- t | t_pkey | regress_tblspace
- tp_0_2 | tp_0_2_pkey | regress_tblspace
-(2 rows)
-
-DROP TABLE t;
--- Check the new partition inherits parent's table access method
-SET search_path = partitions_merge_schema, public;
-CREATE ACCESS METHOD partitions_merge_heap TYPE TABLE HANDLER heap_tableam_handler;
-CREATE TABLE t (i int) PARTITION BY RANGE (i) USING partitions_merge_heap;
-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);
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
-SELECT c.relname, a.amname
-FROM pg_class c JOIN pg_am a ON c.relam = a.oid
-WHERE c.oid IN ('t'::regclass, 'tp_0_2'::regclass)
-ORDER BY c.relname COLLATE "C";
- relname | amname
----------+-----------------------
- t | partitions_merge_heap
- tp_0_2 | partitions_merge_heap
-(2 rows)
-
-DROP TABLE t;
-DROP ACCESS METHOD partitions_merge_heap;
--- Test permission checks. The user needs to own the parent table and all
--- the merging partitions to do the merge.
-CREATE ROLE regress_partition_merge_alice;
-CREATE ROLE regress_partition_merge_bob;
-GRANT ALL ON SCHEMA partitions_merge_schema TO regress_partition_merge_alice;
-GRANT ALL ON SCHEMA partitions_merge_schema TO regress_partition_merge_bob;
-SET SESSION AUTHORIZATION regress_partition_merge_alice;
-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);
-SET SESSION AUTHORIZATION regress_partition_merge_bob;
--- ERROR
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
-ERROR: must be owner of table t
-RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_merge_bob;
-SET SESSION AUTHORIZATION regress_partition_merge_bob;
--- ERROR
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
-ERROR: must be owner of table tp_0_1
-RESET SESSION AUTHORIZATION;
-ALTER TABLE tp_0_1 OWNER TO regress_partition_merge_bob;
-SET SESSION AUTHORIZATION regress_partition_merge_bob;
--- ERROR
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
-ERROR: must be owner of table tp_1_2
-RESET SESSION AUTHORIZATION;
-ALTER TABLE tp_1_2 OWNER TO regress_partition_merge_bob;
-SET SESSION AUTHORIZATION regress_partition_merge_bob;
--- Ok:
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
-RESET SESSION AUTHORIZATION;
-DROP TABLE t;
--- Test: we can't merge partitions with different owners
-CREATE TABLE tp_0_1(i int);
-ALTER TABLE tp_0_1 OWNER TO regress_partition_merge_alice;
-CREATE TABLE tp_1_2(i int);
-ALTER TABLE tp_1_2 OWNER TO regress_partition_merge_bob;
-CREATE TABLE t (i int) PARTITION BY RANGE (i);
-ALTER TABLE t ATTACH PARTITION tp_0_1 FOR VALUES FROM (0) TO (1);
-ALTER TABLE t ATTACH PARTITION tp_1_2 FOR VALUES FROM (1) TO (2);
--- Owner is 'regress_partition_merge_alice':
-\dt tp_0_1
- List of tables
- Schema | Name | Type | Owner
--------------------------+--------+-------+-------------------------------
- partitions_merge_schema | tp_0_1 | table | regress_partition_merge_alice
-(1 row)
-
--- Owner is 'regress_partition_merge_bob':
-\dt tp_1_2
- List of tables
- Schema | Name | Type | Owner
--------------------------+--------+-------+-----------------------------
- partitions_merge_schema | tp_1_2 | table | regress_partition_merge_bob
-(1 row)
-
--- ERROR
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
-ERROR: partitions being merged have different owners
-DROP TABLE t;
-REVOKE ALL ON SCHEMA partitions_merge_schema FROM regress_partition_merge_alice;
-REVOKE ALL ON SCHEMA partitions_merge_schema FROM regress_partition_merge_bob;
-DROP ROLE regress_partition_merge_alice;
-DROP ROLE regress_partition_merge_bob;
--- Test for hash partitioned table
-CREATE TABLE t (i int) PARTITION BY HASH(i);
-CREATE TABLE tp1 PARTITION OF t FOR VALUES WITH (MODULUS 2, REMAINDER 0);
-CREATE TABLE tp2 PARTITION OF t FOR VALUES WITH (MODULUS 2, REMAINDER 1);
--- ERROR
-ALTER TABLE t MERGE PARTITIONS (tp1, tp2) INTO tp3;
-ERROR: partition of hash-partitioned table cannot be merged
--- ERROR
-ALTER TABLE t MERGE PARTITIONS (tp1) INTO tp3;
-ERROR: list of partitions to be merged should include at least two partitions
-DROP TABLE t;
--- Test for merged partition properties:
--- * STATISTICS is empty
--- * COMMENT is empty
--- * DEFAULTS are the same as DEFAULTS for partitioned table
--- * STORAGE is the same as STORAGE for partitioned table
--- * GENERATED and CONSTRAINTS are the same as GENERATED and CONSTRAINTS for partitioned table
--- * TRIGGERS are the same as TRIGGERS for partitioned table
-\set HIDE_TOAST_COMPRESSION false
-CREATE TABLE t
-(i int NOT NULL,
- t text STORAGE EXTENDED COMPRESSION pglz DEFAULT 'default_t',
- b bigint,
- d date GENERATED ALWAYS as ('2022-01-01') STORED) PARTITION BY RANGE (abs(i));
-COMMENT ON COLUMN t.i IS 't1.i';
-CREATE TABLE tp_0_1
-(i int NOT NULL,
- t text STORAGE MAIN DEFAULT 'default_tp_0_1',
- b bigint,
- d date GENERATED ALWAYS as ('2022-02-02') STORED);
-ALTER TABLE t ATTACH PARTITION tp_0_1 FOR VALUES FROM (0) TO (1);
-COMMENT ON COLUMN tp_0_1.i IS 'tp_0_1.i';
-CREATE TABLE tp_1_2
-(i int NOT NULL,
- t text STORAGE MAIN DEFAULT 'default_tp_1_2',
- b bigint,
- d date GENERATED ALWAYS as ('2022-03-03') STORED);
-ALTER TABLE t ATTACH PARTITION tp_1_2 FOR VALUES FROM (1) TO (2);
-COMMENT ON COLUMN tp_1_2.i IS 'tp_1_2.i';
-CREATE STATISTICS t_stat (DEPENDENCIES) on i, b from t;
-CREATE STATISTICS tp_0_1_stat (DEPENDENCIES) on i, b from tp_0_1;
-CREATE STATISTICS tp_1_2_stat (DEPENDENCIES) on i, b from tp_1_2;
-ALTER TABLE t ADD CONSTRAINT t_b_check CHECK (b > 0);
-ALTER TABLE t ADD CONSTRAINT t_b_check1 CHECK (b > 0) NOT ENFORCED;
-ALTER TABLE t ADD CONSTRAINT t_b_check2 CHECK (b > 0) NOT VALID;
-ALTER TABLE t ADD CONSTRAINT t_b_nn NOT NULL b NOT VALID;
-INSERT INTO tp_0_1(i, t, b) VALUES(0, DEFAULT, 1);
-INSERT INTO tp_1_2(i, t, b) VALUES(1, DEFAULT, 2);
-CREATE OR REPLACE FUNCTION trigger_function() RETURNS trigger LANGUAGE 'plpgsql' AS
-$BODY$
-BEGIN
- RAISE NOTICE 'trigger(%) called: action = %, when = %, level = %', TG_ARGV[0], TG_OP, TG_WHEN, TG_LEVEL;
- RETURN new;
-END;
-$BODY$;
-CREATE TRIGGER t_before_insert_row_trigger BEFORE INSERT ON t FOR EACH ROW
- EXECUTE PROCEDURE trigger_function('t');
-CREATE TRIGGER tp_0_1_before_insert_row_trigger BEFORE INSERT ON tp_0_1 FOR EACH ROW
- EXECUTE PROCEDURE trigger_function('tp_0_1');
-CREATE TRIGGER tp_1_2_before_insert_row_trigger BEFORE INSERT ON tp_1_2 FOR EACH ROW
- EXECUTE PROCEDURE trigger_function('tp_1_2');
-\d+ tp_0_1
- Table "partitions_merge_schema.tp_0_1"
- Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
---------+---------+-----------+----------+-------------------------------------------------+---------+-------------+--------------+-------------
- i | integer | | not null | | plain | | | tp_0_1.i
- t | text | | | 'default_tp_0_1'::text | main | | |
- b | bigint | | not null | | plain | | |
- d | date | | | generated always as ('02-02-2022'::date) stored | plain | | |
-Partition of: t FOR VALUES FROM (0) TO (1)
-Partition constraint: ((abs(i) IS NOT NULL) AND (abs(i) >= 0) AND (abs(i) < 1))
-Check constraints:
- "t_b_check" CHECK (b > 0)
- "t_b_check1" CHECK (b > 0) NOT ENFORCED
- "t_b_check2" CHECK (b > 0) NOT VALID
-Statistics objects:
- "partitions_merge_schema.tp_0_1_stat" (dependencies) ON i, b FROM tp_0_1
-Not-null constraints:
- "tp_0_1_i_not_null" NOT NULL "i" (inherited)
- "t_b_nn" NOT NULL "b" (inherited) NOT VALID
-Triggers:
- t_before_insert_row_trigger BEFORE INSERT ON tp_0_1 FOR EACH ROW EXECUTE FUNCTION trigger_function('t'), ON TABLE t
- tp_0_1_before_insert_row_trigger BEFORE INSERT ON tp_0_1 FOR EACH ROW EXECUTE FUNCTION trigger_function('tp_0_1')
-
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_1;
-\d+ tp_0_1
- Table "partitions_merge_schema.tp_0_1"
- Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
---------+---------+-----------+----------+-------------------------------------------------+----------+-------------+--------------+-------------
- i | integer | | not null | | plain | | |
- t | text | | | 'default_t'::text | extended | pglz | |
- b | bigint | | not null | | plain | | |
- d | date | | | generated always as ('01-01-2022'::date) stored | plain | | |
-Partition of: t FOR VALUES FROM (0) TO (2)
-Partition constraint: ((abs(i) IS NOT NULL) AND (abs(i) >= 0) AND (abs(i) < 2))
-Check constraints:
- "t_b_check" CHECK (b > 0)
- "t_b_check1" CHECK (b > 0) NOT ENFORCED
- "t_b_check2" CHECK (b > 0) NOT VALID
-Not-null constraints:
- "t_i_not_null" NOT NULL "i" (inherited)
- "t_b_nn" NOT NULL "b" (inherited) NOT VALID
-Triggers:
- t_before_insert_row_trigger BEFORE INSERT ON tp_0_1 FOR EACH ROW EXECUTE FUNCTION trigger_function('t'), ON TABLE t
-
-INSERT INTO t(i, t, b) VALUES(1, DEFAULT, 3);
-NOTICE: trigger(t) called: action = INSERT, when = BEFORE, level = ROW
-SELECT tableoid::regclass, * FROM t ORDER BY b;
- tableoid | i | t | b | d
-----------+---+----------------+---+------------
- tp_0_1 | 0 | default_tp_0_1 | 1 | 01-01-2022
- tp_0_1 | 1 | default_tp_1_2 | 2 | 01-01-2022
- tp_0_1 | 1 | default_t | 3 | 01-01-2022
-(3 rows)
-
-DROP TABLE t;
-DROP FUNCTION trigger_function();
-\set HIDE_TOAST_COMPRESSION true
--- Test MERGE PARTITIONS with not valid foreign key constraint
-CREATE TABLE t (i INT PRIMARY KEY) 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);
-CREATE TABLE t_fk (i INT);
-INSERT INTO t_fk VALUES (1), (2);
-ALTER TABLE t_fk ADD CONSTRAINT t_fk_i_fkey FOREIGN KEY (i) REFERENCES t NOT VALID;
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
--- Should be NOT VALID FOREIGN KEY
-\d tp_0_2
- Table "partitions_merge_schema.tp_0_2"
- Column | Type | Collation | Nullable | Default
---------+---------+-----------+----------+---------
- i | integer | | not null |
-Partition of: t FOR VALUES FROM (0) TO (2)
-Indexes:
- "tp_0_2_pkey" PRIMARY KEY, btree (i)
-Referenced by:
- TABLE "t_fk" CONSTRAINT "t_fk_i_fkey" FOREIGN KEY (i) REFERENCES t(i) NOT VALID
-
--- ERROR
-ALTER TABLE t_fk VALIDATE CONSTRAINT t_fk_i_fkey;
-ERROR: insert or update on table "t_fk" violates foreign key constraint "t_fk_i_fkey"
-DETAIL: Key (i)=(2) is not present in table "t".
-DROP TABLE t_fk;
-DROP TABLE t;
--- Test MERGE PARTITIONS with not enforced foreign key constraint
-CREATE TABLE t (i INT PRIMARY KEY) 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);
-CREATE TABLE t_fk (i INT);
-INSERT INTO t_fk VALUES (1), (2);
-ALTER TABLE t_fk ADD CONSTRAINT t_fk_i_fkey FOREIGN KEY (i) REFERENCES t NOT ENFORCED;
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
--- Should be NOT ENFORCED FOREIGN KEY
-\d tp_0_2
- Table "partitions_merge_schema.tp_0_2"
- Column | Type | Collation | Nullable | Default
---------+---------+-----------+----------+---------
- i | integer | | not null |
-Partition of: t FOR VALUES FROM (0) TO (2)
-Indexes:
- "tp_0_2_pkey" PRIMARY KEY, btree (i)
-Referenced by:
- TABLE "t_fk" CONSTRAINT "t_fk_i_fkey" FOREIGN KEY (i) REFERENCES t(i) NOT ENFORCED
-
--- ERROR
-ALTER TABLE t_fk ALTER CONSTRAINT t_fk_i_fkey ENFORCED;
-ERROR: insert or update on table "t_fk" violates foreign key constraint "t_fk_i_fkey"
-DETAIL: Key (i)=(2) is not present in table "t".
-DROP TABLE t_fk;
-DROP TABLE t;
--- Test for recomputation of stored generated columns.
-CREATE TABLE t (i int, tab_id int generated always as (tableoid) stored) 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);
-ALTER TABLE t ADD CONSTRAINT cc CHECK(tableoid <> 123456789);
-INSERT INTO t VALUES (0), (1);
--- Should be 0 because partition identifier for row with i=0 is different from
--- partition identifier for row with i=1.
-SELECT count(*) FROM t WHERE i = 0 AND tab_id IN (SELECT tab_id FROM t WHERE i = 1);
- count
--------
- 0
-(1 row)
-
--- "tab_id" column (stored generated column) with "tableoid" attribute requires
--- recomputation here.
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
--- Should be 1 because partition identifier for row with i=0 is the same as
--- partition identifier for row with i=1.
-SELECT count(*) FROM t WHERE i = 0 AND tab_id IN (SELECT tab_id FROM t WHERE i = 1);
- count
--------
- 1
-(1 row)
-
-DROP TABLE t;
--- Test for generated columns (different order of columns in partitioned table
--- and partitions).
-CREATE TABLE t (i int, g int GENERATED ALWAYS AS (i + tableoid::int)) PARTITION BY RANGE (i);
-CREATE TABLE tp_1 (g int GENERATED ALWAYS AS (i + tableoid::int), i int);
-CREATE TABLE tp_2 (g int GENERATED ALWAYS AS (i + tableoid::int), i int);
-ALTER TABLE t ATTACH PARTITION tp_1 FOR VALUES FROM (-1) TO (10);
-ALTER TABLE t ATTACH PARTITION tp_2 FOR VALUES FROM (10) TO (20);
-ALTER TABLE t ADD CHECK (g > 0);
-ALTER TABLE t ADD CHECK (i > 0);
-INSERT INTO t VALUES (5), (15);
-ALTER TABLE t MERGE PARTITIONS (tp_1, tp_2) INTO tp_12;
-INSERT INTO t VALUES (16);
--- ERROR
-INSERT INTO t VALUES (0);
-ERROR: new row for relation "tp_12" violates check constraint "t_i_check"
-DETAIL: Failing row contains (0, virtual).
--- Should be 3 rows: (5), (15), (16):
-SELECT i FROM t ORDER BY i;
- i
-----
- 5
- 15
- 16
-(3 rows)
-
--- Should be 1 because for the same tableoid (15 + tableoid) = (5 + tableoid) + 10:
-SELECT count(*) FROM t WHERE i = 15 AND g IN (SELECT g + 10 FROM t WHERE i = 5);
- count
--------
- 1
-(1 row)
-
-DROP TABLE t;
--- A merged partition needs its own TOAST table; otherwise an out-of-line
--- varlena value carried over from one of the merging partitions has
--- nowhere to be stored. SET STORAGE EXTERNAL forces externalization
--- for any value over the TOAST threshold, so a string over that threshold
--- suffices to exercise the toast-table dependency.
-CREATE TABLE t (a text) PARTITION BY RANGE(a);
-ALTER TABLE t ALTER COLUMN a SET STORAGE EXTERNAL;
-CREATE TABLE tp_def PARTITION OF t DEFAULT;
-CREATE TABLE tp_2_3 PARTITION OF t FOR VALUES FROM ('2') TO ('3');
-INSERT INTO t SELECT repeat('1', 10000);
-ALTER TABLE t MERGE PARTITIONS (tp_def, tp_2_3) INTO tp_merged;
-SELECT reltoastrelid <> 0 AS has_toast,
- pg_relation_size(reltoastrelid) > 0 AS toast_used
- FROM pg_class WHERE relname = 'tp_merged';
- has_toast | toast_used
------------+------------
- t | t
-(1 row)
-
-SELECT length(a) FROM t;
- length
---------
- 10000
-(1 row)
-
-DROP TABLE t;
--- Tablespace selection for the new merged partition mirrors
--- CREATE TABLE ... PARTITION OF: the partitioned root's explicit
--- tablespace wins; otherwise default_tablespace applies; otherwise the
--- database default is used.
-CREATE TABLE t (i int) PARTITION BY RANGE(i) TABLESPACE regress_tblspace;
-CREATE TABLE tp_0_5 PARTITION OF t FOR VALUES FROM (0) TO (5);
-CREATE TABLE tp_5_10 PARTITION OF t FOR VALUES FROM (5) TO (10);
-INSERT INTO t SELECT generate_series(0, 9);
-ALTER TABLE t MERGE PARTITIONS (tp_0_5, tp_5_10) INTO tp_merged;
-SELECT spcname FROM pg_class c LEFT JOIN pg_tablespace s
- ON c.reltablespace = s.oid WHERE c.relname = 'tp_merged';
- spcname
-------------------
- regress_tblspace
-(1 row)
-
-DROP TABLE t;
--- Parent has no explicit tablespace, but default_tablespace is set: the
--- new partition lands on default_tablespace.
-CREATE TABLE t (i int) PARTITION BY RANGE(i);
-CREATE TABLE tp_0_5 PARTITION OF t FOR VALUES FROM (0) TO (5);
-CREATE TABLE tp_5_10 PARTITION OF t FOR VALUES FROM (5) TO (10);
-INSERT INTO t SELECT generate_series(0, 9);
-SET default_tablespace TO regress_tblspace;
-ALTER TABLE t MERGE PARTITIONS (tp_0_5, tp_5_10) INTO tp_merged;
-RESET default_tablespace;
-SELECT spcname FROM pg_class c LEFT JOIN pg_tablespace s
- ON c.reltablespace = s.oid WHERE c.relname = 'tp_merged';
- spcname
-------------------
- regress_tblspace
-(1 row)
-
-DROP TABLE t;
-CREATE TABLE t (i int) PARTITION BY RANGE(i);
-CREATE TABLE tp_0_5 PARTITION OF t FOR VALUES FROM (0) TO (5);
-CREATE TABLE tp_5_10 PARTITION OF t FOR VALUES FROM (5) TO (10);
-INSERT INTO t SELECT generate_series(0, 9);
--- pg_global is rejected when picked up from default_tablespace.
-SET default_tablespace TO pg_global;
-ALTER TABLE t MERGE PARTITIONS (tp_0_5, tp_5_10) INTO tp_merged; -- fails
-ERROR: only shared relations can be placed in pg_global tablespace
-RESET default_tablespace;
--- Parent has no explicit tablespace and default_tablespace is empty: the
--- new partition uses the database default (reltablespace = 0).
-ALTER TABLE t MERGE PARTITIONS (tp_0_5, tp_5_10) INTO tp_merged;
-SELECT reltablespace FROM pg_class WHERE relname = 'tp_merged';
- reltablespace
----------------
- 0
-(1 row)
-
-DROP TABLE t;
-RESET search_path;
---
-DROP SCHEMA partitions_merge_schema;
-DROP SCHEMA partitions_merge_schema2;
diff --git a/src/test/regress/expected/partition_split.out b/src/test/regress/expected/partition_split.out
deleted file mode 100644
index 089f89ed6ac..00000000000
--- a/src/test/regress/expected/partition_split.out
+++ /dev/null
@@ -1,1758 +0,0 @@
---
--- PARTITION_SPLIT
--- Tests for "ALTER TABLE ... SPLIT PARTITION ..." command
---
-CREATE SCHEMA partition_split_schema;
-CREATE SCHEMA partition_split_schema2;
-SET search_path = partition_split_schema, public;
---
--- BY RANGE partitioning
---
---
--- Test for error codes
---
-CREATE TABLE sales_range (salesperson_id 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');
-CREATE TABLE sales_feb_mar_apr2022 PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-05-01');
-CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
--- ERROR
-ALTER TABLE sales_range SPLIT PARTITION sales_xxx INTO
- (PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- 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: relation "sales_xxx" does not exist
--- ERROR
-ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
- (PARTITION sales_jan2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- 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: relation "sales_jan2022" already exists
--- ERROR
-ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
- (PARTITION sales_jan2022 FOR VALUES IN ('2022-05-01', '2022-06-01'),
- 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: invalid bound specification for a range partition
-LINE 2: (PARTITION sales_jan2022 FOR VALUES IN ('2022-05-01', '202...
- ^
--- ERROR
-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-03-01') TO ('2022-02-01'),
- PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01'));
-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
-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 must contain at least two partitions
--- ERROR
-ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
- (PARTITION sales_feb2022 FOR VALUES FROM ('2022-01-01') TO ('2022-03-01'),
- 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: lower bound of partition "sales_feb2022" is not equal to lower bound of split partition "sales_feb_mar_apr2022"
-LINE 2: (PARTITION sales_feb2022 FOR VALUES FROM ('2022-01-01') TO...
- ^
-HINT: ALTER TABLE ... SPLIT PARTITION requires the combined bounds of the new partitions to exactly match the bound of the split partition.
--- ERROR
--- (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: partition with name "sales_feb_mar_apr2022" is already used
-LINE 3: PARTITION sales_feb_mar_apr2022 FOR VALUES FROM ('2022-03...
- ^
--- ERROR
-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: partition with name "sales_feb2022" is already used
-LINE 3: PARTITION sales_feb2022 FOR VALUES FROM ('2022-03-01') TO...
- ^
--- ERROR
-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 partition_split_schema.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: partition with name "sales_feb2022" is already used
-LINE 3: PARTITION partition_split_schema.sales_feb2022 FOR VALUES...
- ^
--- ERROR
-ALTER TABLE sales_feb_mar_apr2022 SPLIT PARTITION sales_feb_mar_apr2022 INTO
- (PARTITION sales_jan2022 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: ALTER action SPLIT PARTITION cannot be performed on relation "sales_feb_mar_apr2022"
-DETAIL: This operation is not supported for tables.
--- ERROR
-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-03-01') TO ('2022-04-01'),
- PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-06-01'));
-ERROR: upper bound of partition "sales_apr2022" is not equal to upper bound of split partition "sales_feb_mar_apr2022"
-LINE 4: ... sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-06-0...
- ^
-HINT: ALTER TABLE ... SPLIT PARTITION requires the combined bounds of the new partitions to exactly match the bound of the split partition.
--- ERROR
-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'),
- PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01'));
-ERROR: cannot split to partition "sales_mar2022" together with partition "sales_feb2022"
-LINE 3: PARTITION sales_mar2022 FOR VALUES FROM ('2022-02-01') TO...
- ^
-DETAIL: The lower bound of partition "sales_mar2022" is not equal to the upper bound of partition "sales_feb2022".
-HINT: ALTER TABLE ... SPLIT PARTITION requires the partition bounds to be adjacent.
--- Tests for spaces between partitions, them should be executed without DEFAULT partition
-ALTER TABLE sales_range DETACH PARTITION sales_others;
--- ERROR
-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'),
- PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01'));
-ERROR: lower bound of partition "sales_feb2022" is not equal to lower bound of split partition "sales_feb_mar_apr2022"
-LINE 2: (PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-02') TO...
- ^
-HINT: ALTER TABLE ... SPLIT PARTITION requires the combined bounds of the new partitions to exactly match the bound of the split partition.
--- Check the source partition not in the search path
-SET search_path = partition_split_schema2, public;
-ALTER TABLE partition_split_schema.sales_range
-SPLIT PARTITION partition_split_schema.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-03-01') TO ('2022-04-01'),
- PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01'));
-SET search_path = partition_split_schema, public;
-\d+ sales_range
- Partitioned table "partition_split_schema.sales_range"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------------+---------+-----------+----------+---------+---------+--------------+-------------
- salesperson_id | integer | | | | plain | |
- sales_date | date | | | | plain | |
-Partition key: RANGE (sales_date)
-Partitions:
- partition_split_schema2.sales_apr2022 FOR VALUES FROM ('04-01-2022') TO ('05-01-2022')
- partition_split_schema2.sales_feb2022 FOR VALUES FROM ('02-01-2022') TO ('03-01-2022')
- partition_split_schema2.sales_mar2022 FOR VALUES FROM ('03-01-2022') TO ('04-01-2022')
- sales_jan2022 FOR VALUES FROM ('01-01-2022') TO ('02-01-2022')
-
-DROP TABLE sales_range;
-DROP TABLE sales_others;
--- Additional tests for error messages, no default partition
-CREATE TABLE sales_range (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');
-CREATE TABLE sales_feb_mar_apr2022 PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-05-01');
--- ERROR
-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-03-01') TO ('2022-04-01'),
- PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-06-01'));
-ERROR: upper bound of partition "sales_apr2022" is not equal to upper bound of split partition "sales_feb_mar_apr2022"
-LINE 4: ... sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-06-0...
- ^
-HINT: ALTER TABLE ... SPLIT PARTITION requires the combined bounds of the new partitions to exactly match the bound of the split partition.
-DROP TABLE sales_range;
---
--- Add rows into partitioned table then split partition
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_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');
-CREATE TABLE sales_feb_mar_apr2022 PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-05-01');
-CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
-INSERT INTO sales_range VALUES
- (1, 'May', 1000, '2022-01-31'),
- (2, 'Smirnoff', 500, '2022-02-10'),
- (3, 'Ford', 2000, '2022-04-30'),
- (4, 'Ivanov', 750, '2022-04-13'),
- (5, 'Deev', 250, '2022-04-07'),
- (6, 'Poirot', 150, '2022-02-11'),
- (7, 'Li', 175, '2022-03-08'),
- (8, 'Ericsson', 185, '2022-02-23'),
- (9, 'Muller', 250, '2022-03-11'),
- (10, 'Halder', 350, '2022-01-28'),
- (11, 'Trump', 380, '2022-04-06'),
- (12, 'Plato', 350, '2022-03-19'),
- (13, 'Gandi', 377, '2022-01-09'),
- (14, 'Smith', 510, '2022-05-04');
-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-03-01') TO ('2022-04-01'),
- PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01'));
-SELECT tableoid::regclass, * FROM sales_range ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
- tableoid | salesperson_id | salesperson_name | sales_amount | sales_date
----------------+----------------+------------------+--------------+------------
- sales_apr2022 | 3 | Ford | 2000 | 04-30-2022
- sales_apr2022 | 4 | Ivanov | 750 | 04-13-2022
- sales_apr2022 | 5 | Deev | 250 | 04-07-2022
- sales_apr2022 | 11 | Trump | 380 | 04-06-2022
- sales_feb2022 | 2 | Smirnoff | 500 | 02-10-2022
- sales_feb2022 | 6 | Poirot | 150 | 02-11-2022
- sales_feb2022 | 8 | Ericsson | 185 | 02-23-2022
- sales_jan2022 | 1 | May | 1000 | 01-31-2022
- sales_jan2022 | 10 | Halder | 350 | 01-28-2022
- sales_jan2022 | 13 | Gandi | 377 | 01-09-2022
- sales_mar2022 | 7 | Li | 175 | 03-08-2022
- sales_mar2022 | 9 | Muller | 250 | 03-11-2022
- sales_mar2022 | 12 | Plato | 350 | 03-19-2022
- sales_others | 14 | Smith | 510 | 05-04-2022
-(14 rows)
-
-DROP TABLE sales_range CASCADE;
---
--- Add split partition, then add rows into partitioned table
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_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');
-CREATE TABLE sales_feb_mar_apr2022 PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-05-01');
-CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
-INSERT INTO sales_range VALUES
- (1, 'May', 1000, '2022-01-31'),
- (2, 'Smirnoff', 500, '2022-02-10'),
- (3, 'Ford', 2000, '2022-04-30'),
- (4, 'Ivanov', 750, '2022-04-13'),
- (5, 'Deev', 250, '2022-04-07'),
- (6, 'Poirot', 150, '2022-02-11'),
- (7, 'Li', 175, '2022-03-08'),
- (8, 'Ericsson', 185, '2022-02-23'),
- (9, 'Muller', 250, '2022-03-11'),
- (10, 'Halder', 350, '2022-01-28'),
- (11, 'Trump', 380, '2022-04-06'),
- (12, 'Plato', 350, '2022-03-19'),
- (13, 'Gandi', 377, '2022-01-09'),
- (14, 'Smith', 510, '2022-05-04');
--- Split partition, also check schema qualification of new partitions
-ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
- (PARTITION partition_split_schema.sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- PARTITION partition_split_schema2.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'));
-\d+ sales_range
- Partitioned table "partition_split_schema.sales_range"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-------------------+-----------------------+-----------+----------+---------+----------+--------------+-------------
- salesperson_id | integer | | | | plain | |
- salesperson_name | character varying(30) | | | | extended | |
- sales_amount | integer | | | | plain | |
- sales_date | date | | | | plain | |
-Partition key: RANGE (sales_date)
-Partitions:
- partition_split_schema2.sales_mar2022 FOR VALUES FROM ('03-01-2022') TO ('04-01-2022')
- sales_apr2022 FOR VALUES FROM ('04-01-2022') TO ('05-01-2022')
- sales_feb2022 FOR VALUES FROM ('02-01-2022') TO ('03-01-2022')
- sales_jan2022 FOR VALUES FROM ('01-01-2022') TO ('02-01-2022')
- sales_others DEFAULT
-
-SELECT tableoid::regclass, * FROM sales_range ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
- tableoid | salesperson_id | salesperson_name | sales_amount | sales_date
----------------------------------------+----------------+------------------+--------------+------------
- partition_split_schema2.sales_mar2022 | 7 | Li | 175 | 03-08-2022
- partition_split_schema2.sales_mar2022 | 9 | Muller | 250 | 03-11-2022
- partition_split_schema2.sales_mar2022 | 12 | Plato | 350 | 03-19-2022
- sales_apr2022 | 3 | Ford | 2000 | 04-30-2022
- sales_apr2022 | 4 | Ivanov | 750 | 04-13-2022
- sales_apr2022 | 5 | Deev | 250 | 04-07-2022
- sales_apr2022 | 11 | Trump | 380 | 04-06-2022
- sales_feb2022 | 2 | Smirnoff | 500 | 02-10-2022
- sales_feb2022 | 6 | Poirot | 150 | 02-11-2022
- sales_feb2022 | 8 | Ericsson | 185 | 02-23-2022
- sales_jan2022 | 1 | May | 1000 | 01-31-2022
- sales_jan2022 | 10 | Halder | 350 | 01-28-2022
- sales_jan2022 | 13 | Gandi | 377 | 01-09-2022
- sales_others | 14 | Smith | 510 | 05-04-2022
-(14 rows)
-
-DROP TABLE sales_range CASCADE;
---
--- Test for:
--- * composite partition key;
--- * GENERATED column;
--- * column with DEFAULT value.
---
-CREATE TABLE sales_date (salesperson_name VARCHAR(30), sales_year INT, sales_month INT, sales_day INT,
- sales_date VARCHAR(10) GENERATED ALWAYS AS
- (LPAD(sales_year::text, 4, '0') || '.' || LPAD(sales_month::text, 2, '0') || '.' || LPAD(sales_day::text, 2, '0')) STORED,
- sales_department VARCHAR(30) DEFAULT 'Sales department')
- PARTITION BY RANGE (sales_year, sales_month, sales_day);
-CREATE TABLE sales_dec2021 PARTITION OF sales_date FOR VALUES FROM (2021, 12, 1) TO (2022, 1, 1);
-CREATE TABLE sales_jan_feb2022 PARTITION OF sales_date FOR VALUES FROM (2022, 1, 1) TO (2022, 3, 1);
-CREATE TABLE sales_other PARTITION OF sales_date FOR VALUES FROM (2022, 3, 1) TO (MAXVALUE, MAXVALUE, MAXVALUE);
-INSERT INTO sales_date(salesperson_name, sales_year, sales_month, sales_day) VALUES
- ('Manager1', 2021, 12, 7),
- ('Manager2', 2021, 12, 8),
- ('Manager3', 2022, 1, 1),
- ('Manager1', 2022, 2, 4),
- ('Manager2', 2022, 1, 2),
- ('Manager3', 2022, 2, 1),
- ('Manager1', 2022, 3, 3),
- ('Manager2', 2022, 3, 4),
- ('Manager3', 2022, 5, 1);
-SELECT tableoid::regclass, * FROM sales_date ORDER BY tableoid::regclass::text COLLATE "C", sales_year, sales_month, sales_day;
- tableoid | salesperson_name | sales_year | sales_month | sales_day | sales_date | sales_department
--------------------+------------------+------------+-------------+-----------+------------+------------------
- sales_dec2021 | Manager1 | 2021 | 12 | 7 | 2021.12.07 | Sales department
- sales_dec2021 | Manager2 | 2021 | 12 | 8 | 2021.12.08 | Sales department
- sales_jan_feb2022 | Manager3 | 2022 | 1 | 1 | 2022.01.01 | Sales department
- sales_jan_feb2022 | Manager2 | 2022 | 1 | 2 | 2022.01.02 | Sales department
- sales_jan_feb2022 | Manager3 | 2022 | 2 | 1 | 2022.02.01 | Sales department
- sales_jan_feb2022 | Manager1 | 2022 | 2 | 4 | 2022.02.04 | Sales department
- sales_other | Manager1 | 2022 | 3 | 3 | 2022.03.03 | Sales department
- sales_other | Manager2 | 2022 | 3 | 4 | 2022.03.04 | Sales department
- sales_other | Manager3 | 2022 | 5 | 1 | 2022.05.01 | Sales department
-(9 rows)
-
-ALTER TABLE sales_date SPLIT PARTITION sales_jan_feb2022 INTO
- (PARTITION sales_jan2022 FOR VALUES FROM (2022, 1, 1) TO (2022, 2, 1),
- PARTITION sales_feb2022 FOR VALUES FROM (2022, 2, 1) TO (2022, 3, 1));
-INSERT INTO sales_date(salesperson_name, sales_year, sales_month, sales_day) VALUES
- ('Manager1', 2022, 1, 10),
- ('Manager2', 2022, 2, 10);
-SELECT tableoid::regclass, * FROM sales_date ORDER BY tableoid::regclass::text COLLATE "C", sales_year, sales_month, sales_day;
- tableoid | salesperson_name | sales_year | sales_month | sales_day | sales_date | sales_department
----------------+------------------+------------+-------------+-----------+------------+------------------
- sales_dec2021 | Manager1 | 2021 | 12 | 7 | 2021.12.07 | Sales department
- sales_dec2021 | Manager2 | 2021 | 12 | 8 | 2021.12.08 | Sales department
- sales_feb2022 | Manager3 | 2022 | 2 | 1 | 2022.02.01 | Sales department
- sales_feb2022 | Manager1 | 2022 | 2 | 4 | 2022.02.04 | Sales department
- sales_feb2022 | Manager2 | 2022 | 2 | 10 | 2022.02.10 | Sales department
- sales_jan2022 | Manager3 | 2022 | 1 | 1 | 2022.01.01 | Sales department
- sales_jan2022 | Manager2 | 2022 | 1 | 2 | 2022.01.02 | Sales department
- sales_jan2022 | Manager1 | 2022 | 1 | 10 | 2022.01.10 | Sales department
- sales_other | Manager1 | 2022 | 3 | 3 | 2022.03.03 | Sales department
- sales_other | Manager2 | 2022 | 3 | 4 | 2022.03.04 | Sales department
- sales_other | Manager3 | 2022 | 5 | 1 | 2022.05.01 | Sales department
-(11 rows)
-
-DROP TABLE sales_date CASCADE;
---
--- Test: split DEFAULT partition; use an index on partition key; check index after split
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_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');
-CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
-CREATE INDEX sales_range_sales_date_idx ON sales_range USING btree (sales_date);
-INSERT INTO sales_range VALUES
- (1, 'May', 1000, '2022-01-31'),
- (2, 'Smirnoff', 500, '2022-02-10'),
- (3, 'Ford', 2000, '2022-04-30'),
- (4, 'Ivanov', 750, '2022-04-13'),
- (5, 'Deev', 250, '2022-04-07'),
- (6, 'Poirot', 150, '2022-02-11'),
- (7, 'Li', 175, '2022-03-08'),
- (8, 'Ericsson', 185, '2022-02-23'),
- (9, 'Muller', 250, '2022-03-11'),
- (10, 'Halder', 350, '2022-01-28'),
- (11, 'Trump', 380, '2022-04-06'),
- (12, 'Plato', 350, '2022-03-19'),
- (13, 'Gandi', 377, '2022-01-09'),
- (14, 'Smith', 510, '2022-05-04');
-SELECT * FROM sales_others;
- salesperson_id | salesperson_name | sales_amount | sales_date
-----------------+------------------+--------------+------------
- 2 | Smirnoff | 500 | 02-10-2022
- 3 | Ford | 2000 | 04-30-2022
- 4 | Ivanov | 750 | 04-13-2022
- 5 | Deev | 250 | 04-07-2022
- 6 | Poirot | 150 | 02-11-2022
- 7 | Li | 175 | 03-08-2022
- 8 | Ericsson | 185 | 02-23-2022
- 9 | Muller | 250 | 03-11-2022
- 11 | Trump | 380 | 04-06-2022
- 12 | Plato | 350 | 03-19-2022
- 14 | Smith | 510 | 05-04-2022
-(11 rows)
-
-SELECT * FROM pg_indexes WHERE tablename = 'sales_others' and schemaname = 'partition_split_schema' ORDER BY indexname COLLATE "C";
- schemaname | tablename | indexname | tablespace | indexdef
-------------------------+--------------+-----------------------------+------------+----------------------------------------------------------------------------------------------------------
- partition_split_schema | sales_others | sales_others_sales_date_idx | | CREATE INDEX sales_others_sales_date_idx ON partition_split_schema.sales_others USING btree (sales_date)
-(1 row)
-
-ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
- (PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- 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 testing indexes
-SET enable_seqscan = OFF;
-EXPLAIN (COSTS OFF) SELECT * FROM sales_feb2022 where sales_date > '2022-01-01';
- QUERY PLAN
-----------------------------------------------------------------
- Index Scan using sales_feb2022_sales_date_idx on sales_feb2022
- Index Cond: (sales_date > '01-01-2022'::date)
-(2 rows)
-
-SELECT * FROM sales_feb2022 where sales_date > '2022-01-01';
- salesperson_id | salesperson_name | sales_amount | sales_date
-----------------+------------------+--------------+------------
- 2 | Smirnoff | 500 | 02-10-2022
- 6 | Poirot | 150 | 02-11-2022
- 8 | Ericsson | 185 | 02-23-2022
-(3 rows)
-
-EXPLAIN (COSTS OFF) SELECT * FROM sales_mar2022 where sales_date > '2022-01-01';
- QUERY PLAN
-----------------------------------------------------------------
- Index Scan using sales_mar2022_sales_date_idx on sales_mar2022
- Index Cond: (sales_date > '01-01-2022'::date)
-(2 rows)
-
-SELECT * FROM sales_mar2022 where sales_date > '2022-01-01';
- salesperson_id | salesperson_name | sales_amount | sales_date
-----------------+------------------+--------------+------------
- 7 | Li | 175 | 03-08-2022
- 9 | Muller | 250 | 03-11-2022
- 12 | Plato | 350 | 03-19-2022
-(3 rows)
-
-EXPLAIN (COSTS OFF) SELECT * FROM sales_apr2022 where sales_date > '2022-01-01';
- QUERY PLAN
-----------------------------------------------------------------
- Index Scan using sales_apr2022_sales_date_idx on sales_apr2022
- Index Cond: (sales_date > '01-01-2022'::date)
-(2 rows)
-
-SELECT * FROM sales_apr2022 where sales_date > '2022-01-01';
- salesperson_id | salesperson_name | sales_amount | sales_date
-----------------+------------------+--------------+------------
- 11 | Trump | 380 | 04-06-2022
- 5 | Deev | 250 | 04-07-2022
- 4 | Ivanov | 750 | 04-13-2022
- 3 | Ford | 2000 | 04-30-2022
-(4 rows)
-
-EXPLAIN (COSTS OFF) SELECT * FROM sales_others where sales_date > '2022-01-01';
- QUERY PLAN
----------------------------------------------------------------
- Index Scan using sales_others_sales_date_idx1 on sales_others
- Index Cond: (sales_date > '01-01-2022'::date)
-(2 rows)
-
-SELECT * FROM sales_others where sales_date > '2022-01-01';
- salesperson_id | salesperson_name | sales_amount | sales_date
-----------------+------------------+--------------+------------
- 14 | Smith | 510 | 05-04-2022
-(1 row)
-
-RESET enable_seqscan;
-SELECT * FROM pg_indexes
-WHERE tablename in ('sales_feb2022', 'sales_mar2022', 'sales_apr2022', 'sales_others')
-AND schemaname = 'partition_split_schema'
-ORDER BY indexname COLLATE "C";
- schemaname | tablename | indexname | tablespace | indexdef
-------------------------+---------------+------------------------------+------------+------------------------------------------------------------------------------------------------------------
- partition_split_schema | sales_apr2022 | sales_apr2022_sales_date_idx | | CREATE INDEX sales_apr2022_sales_date_idx ON partition_split_schema.sales_apr2022 USING btree (sales_date)
- partition_split_schema | sales_feb2022 | sales_feb2022_sales_date_idx | | CREATE INDEX sales_feb2022_sales_date_idx ON partition_split_schema.sales_feb2022 USING btree (sales_date)
- partition_split_schema | sales_mar2022 | sales_mar2022_sales_date_idx | | CREATE INDEX sales_mar2022_sales_date_idx ON partition_split_schema.sales_mar2022 USING btree (sales_date)
- partition_split_schema | sales_others | sales_others_sales_date_idx1 | | CREATE INDEX sales_others_sales_date_idx1 ON partition_split_schema.sales_others USING btree (sales_date)
-(4 rows)
-
-DROP TABLE sales_range CASCADE;
---
--- Test: some cases for splitting DEFAULT partition (different bounds)
---
-CREATE TABLE sales_range (salesperson_id INT, sales_date date) PARTITION BY RANGE (sales_date);
-CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
--- sales_error intersects with sales_dec2021 (lower bound)
--- ERROR
-ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
- (PARTITION sales_dec2021 FOR VALUES FROM ('2021-12-01') TO ('2022-01-01'),
- PARTITION sales_error FOR VALUES FROM ('2021-12-30') TO ('2022-02-01'),
- PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- PARTITION sales_others DEFAULT);
-ERROR: cannot split to partition "sales_error" together with partition "sales_dec2021"
-LINE 3: PARTITION sales_error FOR VALUES FROM ('2021-12-30') TO (...
- ^
-DETAIL: The lower bound of partition "sales_error" is not equal to the upper bound of partition "sales_dec2021".
-HINT: ALTER TABLE ... SPLIT PARTITION requires the partition bounds to be adjacent.
--- sales_error intersects with sales_feb2022 (upper bound)
--- ERROR
-ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
- (PARTITION sales_dec2021 FOR VALUES FROM ('2021-12-01') TO ('2022-01-01'),
- PARTITION sales_error FOR VALUES FROM ('2022-01-01') TO ('2022-02-02'),
- PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- PARTITION sales_others DEFAULT);
-ERROR: cannot split to partition "sales_feb2022" together with partition "sales_error"
-LINE 4: PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO...
- ^
-DETAIL: The lower bound of partition "sales_feb2022" is not equal to the upper bound of partition "sales_error".
-HINT: ALTER TABLE ... SPLIT PARTITION requires the partition bounds to be adjacent.
--- sales_error intersects with sales_dec2021 (inside bound)
--- ERROR
-ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
- (PARTITION sales_dec2021 FOR VALUES FROM ('2021-12-01') TO ('2022-01-01'),
- PARTITION sales_error FOR VALUES FROM ('2021-12-10') TO ('2021-12-20'),
- PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- PARTITION sales_others DEFAULT);
-ERROR: cannot split to partition "sales_error" together with partition "sales_dec2021"
-LINE 3: PARTITION sales_error FOR VALUES FROM ('2021-12-10') TO (...
- ^
-DETAIL: The lower bound of partition "sales_error" is not equal to the upper bound of partition "sales_dec2021".
-HINT: ALTER TABLE ... SPLIT PARTITION requires the partition bounds to be adjacent.
--- sales_error intersects with sales_dec2021 (exactly the same bounds)
--- ERROR
-ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
- (PARTITION sales_dec2021 FOR VALUES FROM ('2021-12-01') TO ('2022-01-01'),
- PARTITION sales_error FOR VALUES FROM ('2021-12-01') TO ('2022-01-01'),
- PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- PARTITION sales_others DEFAULT);
-ERROR: cannot split to partition "sales_error" together with partition "sales_dec2021"
-LINE 3: PARTITION sales_error FOR VALUES FROM ('2021-12-01') TO (...
- ^
-DETAIL: The lower bound of partition "sales_error" is not equal to the upper bound of partition "sales_dec2021".
-HINT: ALTER TABLE ... SPLIT PARTITION requires the partition bounds to be adjacent.
--- ERROR
-ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
- (PARTITION sales_dec2021 FOR VALUES FROM ('2021-12-01') TO ('2022-01-01'),
- PARTITION sales_jan2022 FOR VALUES FROM ('2022-01-01') TO ('2022-02-01'),
- PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'));
-ERROR: cannot split DEFAULT partition "sales_others"
-HINT: To split a DEFAULT partition, one of the new partitions must be DEFAULT.
--- no error: bounds of sales_noerror are between sales_dec2021 and sales_feb2022
-ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
- (PARTITION sales_dec2021 FOR VALUES FROM ('2021-12-01') TO ('2022-01-01'),
- PARTITION sales_noerror FOR VALUES FROM ('2022-01-10') TO ('2022-01-20'),
- PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- PARTITION sales_others DEFAULT);
-DROP TABLE sales_range;
-CREATE TABLE sales_range (sales_date date) PARTITION BY RANGE (sales_date);
-CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
--- no error: bounds of sales_noerror are equal to lower and upper bounds of sales_dec2021 and sales_feb2022
-ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
- (PARTITION sales_dec2021 FOR VALUES FROM ('2021-12-01') TO ('2022-01-01'),
- PARTITION sales_noerror FOR VALUES FROM ('2022-01-01') TO ('2022-02-01'),
- PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- PARTITION sales_others DEFAULT);
-DROP TABLE sales_range;
---
--- Test: split partition with CHECK and FOREIGN KEY CONSTRAINTs on partitioned table
---
-CREATE TABLE salespeople(salesperson_id INT PRIMARY KEY, salesperson_name VARCHAR(30));
-INSERT INTO salespeople VALUES (1, 'Poirot');
-CREATE TABLE sales_range (
-salesperson_id INT REFERENCES salespeople(salesperson_id),
-sales_amount INT CHECK (sales_amount > 1),
-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');
-CREATE TABLE sales_feb_mar_apr2022 PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-05-01');
-CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
-SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conrelid = 'sales_feb_mar_apr2022'::regclass::oid ORDER BY conname COLLATE "C";
- pg_get_constraintdef | conname | conkey
----------------------------------------------------------------------+---------------------------------+--------
- CHECK ((sales_amount > 1)) | sales_range_sales_amount_check | {2}
- FOREIGN KEY (salesperson_id) REFERENCES salespeople(salesperson_id) | sales_range_salesperson_id_fkey | {1}
-(2 rows)
-
-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-03-01') TO ('2022-04-01'),
- PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01'));
--- We should see the same CONSTRAINTs as on sales_feb_mar_apr2022 partition
-SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conrelid = 'sales_feb2022'::regclass::oid ORDER BY conname COLLATE "C";
- pg_get_constraintdef | conname | conkey
----------------------------------------------------------------------+---------------------------------+--------
- CHECK ((sales_amount > 1)) | sales_range_sales_amount_check | {2}
- FOREIGN KEY (salesperson_id) REFERENCES salespeople(salesperson_id) | sales_range_salesperson_id_fkey | {1}
-(2 rows)
-
-SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conrelid = 'sales_mar2022'::regclass::oid ORDER BY conname COLLATE "C";
- pg_get_constraintdef | conname | conkey
----------------------------------------------------------------------+---------------------------------+--------
- CHECK ((sales_amount > 1)) | sales_range_sales_amount_check | {2}
- FOREIGN KEY (salesperson_id) REFERENCES salespeople(salesperson_id) | sales_range_salesperson_id_fkey | {1}
-(2 rows)
-
-SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conrelid = 'sales_apr2022'::regclass::oid ORDER BY conname COLLATE "C";
- pg_get_constraintdef | conname | conkey
----------------------------------------------------------------------+---------------------------------+--------
- CHECK ((sales_amount > 1)) | sales_range_sales_amount_check | {2}
- FOREIGN KEY (salesperson_id) REFERENCES salespeople(salesperson_id) | sales_range_salesperson_id_fkey | {1}
-(2 rows)
-
--- ERROR
-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).
--- ERROR
-INSERT INTO sales_range VALUES (-1, 10, '2022-03-11');
-ERROR: insert or update on table "sales_mar2022" violates foreign key constraint "sales_range_salesperson_id_fkey"
-DETAIL: Key (salesperson_id)=(-1) is not present in table "salespeople".
--- ok
-INSERT INTO sales_range VALUES (1, 10, '2022-03-11');
-DROP TABLE sales_range CASCADE;
-DROP TABLE salespeople CASCADE;
---
--- Test: split partition on partitioned table in case of existing FOREIGN KEY reference from another table
---
-CREATE TABLE salespeople(salesperson_id INT PRIMARY KEY, salesperson_name VARCHAR(30)) PARTITION BY RANGE (salesperson_id);
-CREATE TABLE sales (salesperson_id INT REFERENCES salespeople(salesperson_id), sales_amount INT, sales_date DATE);
-CREATE TABLE salespeople01_10 PARTITION OF salespeople FOR VALUES FROM (1) TO (10);
-CREATE TABLE salespeople10_40 PARTITION OF salespeople FOR VALUES FROM (10) TO (40);
-INSERT INTO salespeople VALUES
- (1, 'Poirot'),
- (10, 'May'),
- (19, 'Ivanov'),
- (20, 'Smirnoff'),
- (30, 'Ford');
-INSERT INTO sales VALUES
- (1, 100, '2022-03-01'),
- (1, 110, '2022-03-02'),
- (10, 150, '2022-03-01'),
- (10, 90, '2022-03-03'),
- (19, 200, '2022-03-04'),
- (20, 50, '2022-03-12'),
- (20, 170, '2022-03-02'),
- (30, 30, '2022-03-04');
-SELECT tableoid::regclass, * FROM salespeople ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
- tableoid | salesperson_id | salesperson_name
-------------------+----------------+------------------
- salespeople01_10 | 1 | Poirot
- salespeople10_40 | 10 | May
- salespeople10_40 | 19 | Ivanov
- salespeople10_40 | 20 | Smirnoff
- salespeople10_40 | 30 | Ford
-(5 rows)
-
-ALTER TABLE salespeople SPLIT PARTITION salespeople10_40 INTO
- (PARTITION salespeople10_20 FOR VALUES FROM (10) TO (20),
- PARTITION salespeople20_30 FOR VALUES FROM (20) TO (30),
- PARTITION salespeople30_40 FOR VALUES FROM (30) TO (40));
-SELECT tableoid::regclass, * FROM salespeople ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
- tableoid | salesperson_id | salesperson_name
-------------------+----------------+------------------
- salespeople01_10 | 1 | Poirot
- salespeople10_20 | 10 | May
- salespeople10_20 | 19 | Ivanov
- salespeople20_30 | 20 | Smirnoff
- salespeople30_40 | 30 | Ford
-(5 rows)
-
--- ERROR
-INSERT INTO sales VALUES (40, 50, '2022-03-04');
-ERROR: insert or update on table "sales" violates foreign key constraint "sales_salesperson_id_fkey"
-DETAIL: Key (salesperson_id)=(40) is not present in table "salespeople".
--- ok
-INSERT INTO sales VALUES (30, 50, '2022-03-04');
-DROP TABLE sales CASCADE;
-DROP TABLE salespeople CASCADE;
---
--- Test: split partition of partitioned table with triggers
---
-CREATE TABLE salespeople(salesperson_id INT PRIMARY KEY, salesperson_name VARCHAR(30)) PARTITION BY RANGE (salesperson_id);
-CREATE TABLE salespeople01_10 PARTITION OF salespeople FOR VALUES FROM (1) TO (10);
-CREATE TABLE salespeople10_40 PARTITION OF salespeople FOR VALUES FROM (10) TO (40);
-INSERT INTO salespeople VALUES (1, 'Poirot');
-CREATE OR REPLACE FUNCTION after_insert_row_trigger() RETURNS trigger LANGUAGE 'plpgsql' AS $BODY$
-BEGIN
- RAISE NOTICE 'trigger(%) called: action = %, when = %, level = %', TG_ARGV[0], TG_OP, TG_WHEN, TG_LEVEL;
- RETURN NULL;
-END;
-$BODY$;
-CREATE TRIGGER salespeople_after_insert_statement_trigger
- AFTER INSERT
- ON salespeople
- FOR EACH STATEMENT
- EXECUTE PROCEDURE after_insert_row_trigger('salespeople');
-CREATE TRIGGER salespeople_after_insert_row_trigger
- AFTER INSERT
- ON salespeople
- FOR EACH ROW
- EXECUTE PROCEDURE after_insert_row_trigger('salespeople');
--- 2 triggers should fire here (row + statement):
-INSERT INTO salespeople VALUES (10, 'May');
-NOTICE: trigger(salespeople) called: action = INSERT, when = AFTER, level = ROW
-NOTICE: trigger(salespeople) called: action = INSERT, when = AFTER, level = STATEMENT
--- 1 trigger should fire here (row):
-INSERT INTO salespeople10_40 VALUES (19, 'Ivanov');
-NOTICE: trigger(salespeople) called: action = INSERT, when = AFTER, level = ROW
-ALTER TABLE salespeople SPLIT PARTITION salespeople10_40 INTO
- (PARTITION salespeople10_20 FOR VALUES FROM (10) TO (20),
- PARTITION salespeople20_30 FOR VALUES FROM (20) TO (30),
- PARTITION salespeople30_40 FOR VALUES FROM (30) TO (40));
--- 2 triggers should fire here (row + statement):
-INSERT INTO salespeople VALUES (20, 'Smirnoff');
-NOTICE: trigger(salespeople) called: action = INSERT, when = AFTER, level = ROW
-NOTICE: trigger(salespeople) called: action = INSERT, when = AFTER, level = STATEMENT
--- 1 trigger should fire here (row):
-INSERT INTO salespeople30_40 VALUES (30, 'Ford');
-NOTICE: trigger(salespeople) called: action = INSERT, when = AFTER, level = ROW
-SELECT tableoid::regclass, * FROM salespeople ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
- tableoid | salesperson_id | salesperson_name
-------------------+----------------+------------------
- salespeople01_10 | 1 | Poirot
- salespeople10_20 | 10 | May
- salespeople10_20 | 19 | Ivanov
- salespeople20_30 | 20 | Smirnoff
- salespeople30_40 | 30 | Ford
-(5 rows)
-
-DROP TABLE salespeople 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.
---
-CREATE TABLE salespeople(salesperson_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, salesperson_name VARCHAR(30)) PARTITION BY RANGE (salesperson_id);
-CREATE TABLE salespeople1_2 PARTITION OF salespeople FOR VALUES FROM (1) TO (2);
--- Create new partition with identity column:
-CREATE TABLE salespeople2_5(salesperson_id INT NOT NULL, salesperson_name VARCHAR(30));
-ALTER TABLE salespeople ATTACH PARTITION salespeople2_5 FOR VALUES FROM (2) TO (5);
-INSERT INTO salespeople (salesperson_name) VALUES ('Poirot'), ('Ivanov');
-ALTER TABLE salespeople SPLIT PARTITION salespeople2_5 INTO
- (PARTITION salespeople2_3 FOR VALUES FROM (2) TO (3),
- PARTITION salespeople3_4 FOR VALUES FROM (3) TO (4),
- PARTITION salespeople4_5 FOR VALUES FROM (4) TO (5));
-INSERT INTO salespeople (salesperson_name) VALUES ('May'), ('Ford');
-SELECT tableoid::regclass, * FROM salespeople ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
- tableoid | salesperson_id | salesperson_name
-----------------+----------------+------------------
- salespeople1_2 | 1 | Poirot
- salespeople2_3 | 2 | Ivanov
- salespeople3_4 | 3 | May
- salespeople4_5 | 4 | Ford
-(4 rows)
-
--- check new partitions have identity or not after split partition
-SELECT attrelid::regclass, attname, attidentity, attgenerated FROM pg_attribute
-WHERE attnum > 0
-AND attrelid::regclass IN (
- 'salespeople2_3'::regclass, 'salespeople', 'salespeople2_3',
- 'salespeople1_2', 'salespeople3_4', 'salespeople4_5')
-ORDER BY attrelid::regclass::text COLLATE "C", attnum;
- attrelid | attname | attidentity | attgenerated
-----------------+------------------+-------------+--------------
- salespeople | salesperson_id | a |
- salespeople | salesperson_name | |
- salespeople1_2 | salesperson_id | a |
- salespeople1_2 | salesperson_name | |
- salespeople2_3 | salesperson_id | a |
- salespeople2_3 | salesperson_name | |
- salespeople3_4 | salesperson_id | a |
- salespeople3_4 | salesperson_name | |
- salespeople4_5 | salesperson_id | a |
- salespeople4_5 | salesperson_name | |
-(10 rows)
-
-DROP TABLE salespeople CASCADE;
---
--- Test: split partition with deleted columns
---
-CREATE TABLE salespeople(salesperson_id INT PRIMARY KEY, salesperson_name VARCHAR(30)) PARTITION BY RANGE (salesperson_id);
-CREATE TABLE salespeople01_10 PARTITION OF salespeople FOR VALUES FROM (1) TO (10);
--- Create new partition with some deleted columns:
-CREATE TABLE salespeople10_40(d1 VARCHAR(30), salesperson_id INT PRIMARY KEY, d2 INT, d3 DATE, salesperson_name VARCHAR(30));
-INSERT INTO salespeople10_40 VALUES
- ('dummy value 1', 19, 100, now(), 'Ivanov'),
- ('dummy value 2', 20, 101, now(), 'Smirnoff');
-ALTER TABLE salespeople10_40 DROP COLUMN d1;
-ALTER TABLE salespeople10_40 DROP COLUMN d2;
-ALTER TABLE salespeople10_40 DROP COLUMN d3;
-ALTER TABLE salespeople ATTACH PARTITION salespeople10_40 FOR VALUES FROM (10) TO (40);
-INSERT INTO salespeople VALUES
- (1, 'Poirot'),
- (10, 'May'),
- (30, 'Ford');
-ALTER TABLE salespeople SPLIT PARTITION salespeople10_40 INTO
- (PARTITION salespeople10_20 FOR VALUES FROM (10) TO (20),
- PARTITION salespeople20_30 FOR VALUES FROM (20) TO (30),
- PARTITION salespeople30_40 FOR VALUES FROM (30) TO (40));
-SELECT tableoid::regclass, * FROM salespeople ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
- tableoid | salesperson_id | salesperson_name
-------------------+----------------+------------------
- salespeople01_10 | 1 | Poirot
- salespeople10_20 | 10 | May
- salespeople10_20 | 19 | Ivanov
- salespeople20_30 | 20 | Smirnoff
- salespeople30_40 | 30 | Ford
-(5 rows)
-
-DROP TABLE salespeople CASCADE;
---
--- Test: split sub-partition
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_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');
-CREATE TABLE sales_feb2022 PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-03-01');
-CREATE TABLE sales_mar2022 PARTITION OF sales_range FOR VALUES FROM ('2022-03-01') TO ('2022-04-01');
-CREATE TABLE sales_apr2022 (salesperson_id INT, salesperson_name VARCHAR(30), sales_amount INT, sales_date DATE) PARTITION BY RANGE (sales_date);
-CREATE TABLE sales_apr_all PARTITION OF sales_apr2022 FOR VALUES FROM ('2022-04-01') 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;
-CREATE INDEX sales_range_sales_date_idx ON sales_range USING btree (sales_date);
-INSERT INTO sales_range VALUES
- (1, 'May', 1000, '2022-01-31'),
- (2, 'Smirnoff', 500, '2022-02-10'),
- (3, 'Ford', 2000, '2022-04-30'),
- (4, 'Ivanov', 750, '2022-04-13'),
- (5, 'Deev', 250, '2022-04-07'),
- (6, 'Poirot', 150, '2022-02-11'),
- (7, 'Li', 175, '2022-03-08'),
- (8, 'Ericsson', 185, '2022-02-23'),
- (9, 'Muller', 250, '2022-03-11'),
- (10, 'Halder', 350, '2022-01-28'),
- (11, 'Trump', 380, '2022-04-06'),
- (12, 'Plato', 350, '2022-03-19'),
- (13, 'Gandi', 377, '2022-01-09'),
- (14, 'Smith', 510, '2022-05-04');
-SELECT tableoid::regclass, * FROM sales_range ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
- tableoid | salesperson_id | salesperson_name | sales_amount | sales_date
----------------+----------------+------------------+--------------+------------
- sales_apr_all | 3 | Ford | 2000 | 04-30-2022
- sales_apr_all | 4 | Ivanov | 750 | 04-13-2022
- sales_apr_all | 5 | Deev | 250 | 04-07-2022
- sales_apr_all | 11 | Trump | 380 | 04-06-2022
- sales_feb2022 | 2 | Smirnoff | 500 | 02-10-2022
- sales_feb2022 | 6 | Poirot | 150 | 02-11-2022
- sales_feb2022 | 8 | Ericsson | 185 | 02-23-2022
- sales_jan2022 | 1 | May | 1000 | 01-31-2022
- sales_jan2022 | 10 | Halder | 350 | 01-28-2022
- sales_jan2022 | 13 | Gandi | 377 | 01-09-2022
- sales_mar2022 | 7 | Li | 175 | 03-08-2022
- sales_mar2022 | 9 | Muller | 250 | 03-11-2022
- sales_mar2022 | 12 | Plato | 350 | 03-19-2022
- sales_others | 14 | Smith | 510 | 05-04-2022
-(14 rows)
-
-ALTER TABLE sales_apr2022 SPLIT PARTITION sales_apr_all INTO
- (PARTITION sales_apr2022_01_10 FOR VALUES FROM ('2022-04-01') TO ('2022-04-10'),
- PARTITION sales_apr2022_10_20 FOR VALUES FROM ('2022-04-10') TO ('2022-04-20'),
- PARTITION sales_apr2022_20_30 FOR VALUES FROM ('2022-04-20') TO ('2022-05-01'));
-SELECT tableoid::regclass, * FROM sales_range ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
- tableoid | salesperson_id | salesperson_name | sales_amount | sales_date
----------------------+----------------+------------------+--------------+------------
- sales_apr2022_01_10 | 5 | Deev | 250 | 04-07-2022
- sales_apr2022_01_10 | 11 | Trump | 380 | 04-06-2022
- sales_apr2022_10_20 | 4 | Ivanov | 750 | 04-13-2022
- sales_apr2022_20_30 | 3 | Ford | 2000 | 04-30-2022
- sales_feb2022 | 2 | Smirnoff | 500 | 02-10-2022
- sales_feb2022 | 6 | Poirot | 150 | 02-11-2022
- sales_feb2022 | 8 | Ericsson | 185 | 02-23-2022
- sales_jan2022 | 1 | May | 1000 | 01-31-2022
- sales_jan2022 | 10 | Halder | 350 | 01-28-2022
- sales_jan2022 | 13 | Gandi | 377 | 01-09-2022
- sales_mar2022 | 7 | Li | 175 | 03-08-2022
- sales_mar2022 | 9 | Muller | 250 | 03-11-2022
- sales_mar2022 | 12 | Plato | 350 | 03-19-2022
- sales_others | 14 | Smith | 510 | 05-04-2022
-(14 rows)
-
-DROP TABLE sales_range;
---
--- BY LIST partitioning
---
---
--- Test: specific errors for BY LIST partitioning
---
-CREATE TABLE sales_list (sales_state VARCHAR(20)) 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', 'Beijing', 'Berlin', 'Delhi', 'Kyiv', 'Vladivostok');
-CREATE TABLE sales_others PARTITION OF sales_list DEFAULT;
--- ERROR
-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 ('Beijing', 'Delhi', 'Vladivostok', 'Helsinki'),
- PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv'));
-ERROR: new partition "sales_east" would overlap with another (not split) partition "sales_nord"
-LINE 3: ...FOR VALUES IN ('Beijing', 'Delhi', 'Vladivostok', 'Helsinki'...
- ^
--- ERROR
-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 ('Beijing', 'Delhi', 'Vladivostok'),
- PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Lisbon', 'Kyiv'));
-ERROR: new partition "sales_west" would overlap with another new partition "sales_central"
-LINE 2: (PARTITION sales_west FOR VALUES IN ('Lisbon', 'New York',...
- ^
--- ERROR
-ALTER TABLE sales_list SPLIT PARTITION sales_all INTO
- (PARTITION sales_west FOR VALUES IN ('Lisbon', 'New York', 'Madrid', NULL),
- PARTITION sales_east FOR VALUES IN ('Beijing', 'Delhi', 'Vladivostok'),
- PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv'));
-ERROR: new partition "sales_west" cannot have NULL value because split partition "sales_all" does not have it
-LINE 2: ...s_west FOR VALUES IN ('Lisbon', 'New York', 'Madrid', NULL),
- ^
--- ERROR
-ALTER TABLE sales_list SPLIT PARTITION sales_all INTO
- (PARTITION sales_west FOR VALUES IN ('Lisbon', 'New York', 'Madrid', 'Melbourne'),
- PARTITION sales_east FOR VALUES IN ('Beijing', 'Delhi', 'Vladivostok'),
- PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv'));
-ERROR: new partition "sales_west" cannot have this value because split partition "sales_all" does not have it
-LINE 2: ...st FOR VALUES IN ('Lisbon', 'New York', 'Madrid', 'Melbourne...
- ^
--- ERROR
-ALTER TABLE sales_list SPLIT PARTITION sales_all INTO
- (PARTITION sales_west FOR VALUES IN ('Lisbon', 'New York', 'Madrid', 'Melbourne'),
- PARTITION sales_east FOR VALUES IN ('Beijing', 'Delhi', 'Vladivostok'),
- PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv'),
- PARTITION sales_others2 DEFAULT);
-ERROR: cannot split non-DEFAULT partition "sales_all"
-LINE 5: PARTITION sales_others2 DEFAULT);
- ^
-DETAIL: New partition cannot be DEFAULT because DEFAULT partition "sales_others" already exists.
-DROP TABLE sales_list;
--- Test for non-symbolic comparison of values (numeric values '0' and '0.0' are equal).
-CREATE TABLE t (a numeric) PARTITION BY LIST (a);
-CREATE TABLE t1 PARTITION OF t FOR VALUES in ('0', '1');
--- ERROR
-ALTER TABLE t SPLIT PARTITION t1 INTO
- (PARTITION x FOR VALUES IN ('0'),
- PARTITION x1 FOR VALUES IN ('0.0', '1'));
-ERROR: new partition "x" would overlap with another new partition "x1"
-LINE 2: (PARTITION x FOR VALUES IN ('0'),
- ^
-DROP TABLE t;
---
--- Test: two specific errors for BY LIST partitioning:
--- * 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(sales_state VARCHAR(20)) 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', 'Beijing', 'Berlin', 'Delhi', 'Kyiv', 'Vladivostok', NULL);
--- ERROR
-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 ('Beijing', 'Delhi', 'Vladivostok'),
- PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv'));
-ERROR: new partitions' combined partition bounds do not contain value (NULL) but split partition "sales_all" does
-HINT: ALTER TABLE ... SPLIT PARTITION requires the combined bounds of the new partitions to exactly match the bound of the split partition.
--- ERROR
-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 ('Beijing', 'Delhi', 'Vladivostok'),
- PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', NULL));
-ERROR: new partitions' combined partition bounds do not contain value ('Kyiv'::character varying(20)) but split partition "sales_all" does
-HINT: ALTER TABLE ... SPLIT PARTITION requires the combined bounds of the new partitions to exactly match the bound of the split partition.
--- ERROR
-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 ('Beijing', 'Delhi', 'Vladivostok'),
- PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv'),
- PARTITION sales_others DEFAULT,
- PARTITION sales_others2 DEFAULT);
-ERROR: cannot specify more than one DEFAULT partition
-LINE 6: PARTITION sales_others2 DEFAULT);
- ^
-DROP TABLE sales_list;
---
--- Test: BY LIST partitioning, SPLIT PARTITION with data
---
-CREATE TABLE sales_list
-(salesperson_id SERIAL,
- salesperson_name VARCHAR(30),
- sales_state VARCHAR(20),
- sales_amount INT,
- sales_date DATE)
-PARTITION BY LIST (sales_state);
-CREATE INDEX sales_list_salesperson_name_idx ON sales_list USING btree (salesperson_name);
-CREATE INDEX sales_list_sales_state_idx ON sales_list USING btree (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', 'Beijing', 'Berlin', 'Delhi', 'Kyiv', 'Vladivostok');
-CREATE TABLE sales_others PARTITION OF sales_list DEFAULT;
-INSERT INTO sales_list (salesperson_name, sales_state, sales_amount, sales_date) VALUES
- ('Trump', 'Beijing', 1000, '2022-03-01'),
- ('Smirnoff', 'New York', 500, '2022-03-03'),
- ('Ford', 'St. Petersburg', 2000, '2022-03-05'),
- ('Ivanov', 'Warsaw', 750, '2022-03-04'),
- ('Deev', 'Lisbon', 250, '2022-03-07'),
- ('Poirot', 'Berlin', 1000, '2022-03-01'),
- ('May', 'Oslo', 1200, '2022-03-06'),
- ('Li', 'Vladivostok', 1150, '2022-03-09'),
- ('May', 'Oslo', 1200, '2022-03-11'),
- ('Halder', 'Helsinki', 800, '2022-03-02'),
- ('Muller', 'Madrid', 650, '2022-03-05'),
- ('Smith', 'Kyiv', 350, '2022-03-10'),
- ('Gandi', 'Warsaw', 150, '2022-03-08'),
- ('Plato', 'Lisbon', 950, '2022-03-05');
-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 ('Beijing', 'Delhi', 'Vladivostok'),
- PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv'));
-SELECT tableoid::regclass, * FROM sales_list ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
- tableoid | salesperson_id | salesperson_name | sales_state | sales_amount | sales_date
----------------+----------------+------------------+----------------+--------------+------------
- sales_central | 4 | Ivanov | Warsaw | 750 | 03-04-2022
- sales_central | 6 | Poirot | Berlin | 1000 | 03-01-2022
- sales_central | 12 | Smith | Kyiv | 350 | 03-10-2022
- sales_central | 13 | Gandi | Warsaw | 150 | 03-08-2022
- sales_east | 1 | Trump | Beijing | 1000 | 03-01-2022
- sales_east | 8 | Li | Vladivostok | 1150 | 03-09-2022
- sales_nord | 3 | Ford | St. Petersburg | 2000 | 03-05-2022
- sales_nord | 7 | May | Oslo | 1200 | 03-06-2022
- sales_nord | 9 | May | Oslo | 1200 | 03-11-2022
- sales_nord | 10 | Halder | Helsinki | 800 | 03-02-2022
- sales_west | 2 | Smirnoff | New York | 500 | 03-03-2022
- sales_west | 5 | Deev | Lisbon | 250 | 03-07-2022
- sales_west | 11 | Muller | Madrid | 650 | 03-05-2022
- sales_west | 14 | Plato | Lisbon | 950 | 03-05-2022
-(14 rows)
-
--- Use indexscan for testing indexes after splitting partition
-SET enable_seqscan = OFF;
-EXPLAIN (COSTS OFF) SELECT * FROM sales_central WHERE sales_state = 'Warsaw';
- QUERY PLAN
------------------------------------------------------------------
- Index Scan using sales_central_sales_state_idx on sales_central
- Index Cond: ((sales_state)::text = 'Warsaw'::text)
-(2 rows)
-
-SELECT * FROM sales_central WHERE sales_state = 'Warsaw';
- salesperson_id | salesperson_name | sales_state | sales_amount | sales_date
-----------------+------------------+-------------+--------------+------------
- 4 | Ivanov | Warsaw | 750 | 03-04-2022
- 13 | Gandi | Warsaw | 150 | 03-08-2022
-(2 rows)
-
-EXPLAIN (COSTS OFF) SELECT * FROM sales_list WHERE sales_state = 'Warsaw';
- QUERY PLAN
-----------------------------------------------------------------------------
- Index Scan using sales_central_sales_state_idx on sales_central sales_list
- Index Cond: ((sales_state)::text = 'Warsaw'::text)
-(2 rows)
-
-SELECT * FROM sales_list WHERE sales_state = 'Warsaw';
- salesperson_id | salesperson_name | sales_state | sales_amount | sales_date
-----------------+------------------+-------------+--------------+------------
- 4 | Ivanov | Warsaw | 750 | 03-04-2022
- 13 | Gandi | Warsaw | 150 | 03-08-2022
-(2 rows)
-
-EXPLAIN (COSTS OFF) SELECT * FROM sales_list WHERE salesperson_name = 'Ivanov';
- QUERY PLAN
------------------------------------------------------------------------------------------
- Append
- -> Index Scan using sales_east_salesperson_name_idx on sales_east sales_list_1
- Index Cond: ((salesperson_name)::text = 'Ivanov'::text)
- -> Index Scan using sales_central_salesperson_name_idx on sales_central sales_list_2
- Index Cond: ((salesperson_name)::text = 'Ivanov'::text)
- -> Bitmap Heap Scan on sales_nord sales_list_3
- Recheck Cond: ((salesperson_name)::text = 'Ivanov'::text)
- -> Bitmap Index Scan on sales_nord_salesperson_name_idx
- Index Cond: ((salesperson_name)::text = 'Ivanov'::text)
- -> Index Scan using sales_west_salesperson_name_idx on sales_west sales_list_4
- Index Cond: ((salesperson_name)::text = 'Ivanov'::text)
- -> Bitmap Heap Scan on sales_others sales_list_5
- Recheck Cond: ((salesperson_name)::text = 'Ivanov'::text)
- -> Bitmap Index Scan on sales_others_salesperson_name_idx
- Index Cond: ((salesperson_name)::text = 'Ivanov'::text)
-(15 rows)
-
-SELECT * FROM sales_list WHERE salesperson_name = 'Ivanov';
- salesperson_id | salesperson_name | sales_state | sales_amount | sales_date
-----------------+------------------+-------------+--------------+------------
- 4 | Ivanov | Warsaw | 750 | 03-04-2022
-(1 row)
-
-RESET enable_seqscan;
-DROP TABLE sales_list;
---
--- Test for:
--- * split DEFAULT partition to partitions with spaces between bounds;
--- * random order of partitions in SPLIT PARTITION command.
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_name VARCHAR(30), sales_amount INT, sales_date DATE) PARTITION BY RANGE (sales_date);
-CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
-INSERT INTO sales_range VALUES
- (1, 'May', 1000, '2022-01-31'),
- (2, 'Smirnoff', 500, '2022-02-09'),
- (3, 'Ford', 2000, '2022-04-30'),
- (4, 'Ivanov', 750, '2022-04-13'),
- (5, 'Deev', 250, '2022-04-07'),
- (6, 'Poirot', 150, '2022-02-07'),
- (7, 'Li', 175, '2022-03-08'),
- (8, 'Ericsson', 185, '2022-02-23'),
- (9, 'Muller', 250, '2022-03-11'),
- (10, 'Halder', 350, '2022-01-28'),
- (11, 'Trump', 380, '2022-04-06'),
- (12, 'Plato', 350, '2022-03-19'),
- (13, 'Gandi', 377, '2022-01-09'),
- (14, 'Smith', 510, '2022-05-04');
-ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
- (PARTITION sales_others DEFAULT,
- PARTITION sales_mar2022_1decade FOR VALUES FROM ('2022-03-01') TO ('2022-03-10'),
- PARTITION sales_jan2022_1decade FOR VALUES FROM ('2022-01-01') TO ('2022-01-10'),
- PARTITION sales_feb2022_1decade FOR VALUES FROM ('2022-02-01') TO ('2022-02-10'),
- PARTITION sales_apr2022_1decade FOR VALUES FROM ('2022-04-01') TO ('2022-04-10'));
-SELECT tableoid::regclass, * FROM sales_range ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
- tableoid | salesperson_id | salesperson_name | sales_amount | sales_date
------------------------+----------------+------------------+--------------+------------
- sales_apr2022_1decade | 5 | Deev | 250 | 04-07-2022
- sales_apr2022_1decade | 11 | Trump | 380 | 04-06-2022
- sales_feb2022_1decade | 2 | Smirnoff | 500 | 02-09-2022
- sales_feb2022_1decade | 6 | Poirot | 150 | 02-07-2022
- sales_jan2022_1decade | 13 | Gandi | 377 | 01-09-2022
- sales_mar2022_1decade | 7 | Li | 175 | 03-08-2022
- sales_others | 1 | May | 1000 | 01-31-2022
- sales_others | 3 | Ford | 2000 | 04-30-2022
- sales_others | 4 | Ivanov | 750 | 04-13-2022
- sales_others | 8 | Ericsson | 185 | 02-23-2022
- sales_others | 9 | Muller | 250 | 03-11-2022
- sales_others | 10 | Halder | 350 | 01-28-2022
- sales_others | 12 | Plato | 350 | 03-19-2022
- sales_others | 14 | Smith | 510 | 05-04-2022
-(14 rows)
-
-DROP TABLE sales_range;
---
--- Test for:
--- * split non-DEFAULT partition to partitions with spaces between bounds;
--- * random order of partitions in SPLIT PARTITION command.
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_name VARCHAR(30), sales_amount INT, sales_date DATE) PARTITION BY RANGE (sales_date);
-CREATE TABLE sales_all PARTITION OF sales_range FOR VALUES FROM ('2022-01-01') TO ('2022-05-01');
-INSERT INTO sales_range VALUES
- (1, 'May', 1000, '2022-01-31'),
- (2, 'Smirnoff', 500, '2022-02-09'),
- (3, 'Ford', 2000, '2022-04-30'),
- (4, 'Ivanov', 750, '2022-04-13'),
- (5, 'Deev', 250, '2022-04-07'),
- (6, 'Poirot', 150, '2022-02-07'),
- (7, 'Li', 175, '2022-03-08'),
- (8, 'Ericsson', 185, '2022-02-23'),
- (9, 'Muller', 250, '2022-03-11'),
- (10, 'Halder', 350, '2022-01-28'),
- (11, 'Trump', 380, '2022-04-06'),
- (12, 'Plato', 350, '2022-03-19'),
- (13, 'Gandi', 377, '2022-01-09');
-ALTER TABLE sales_range SPLIT PARTITION sales_all INTO
- (PARTITION sales_mar2022_1decade FOR VALUES FROM ('2022-03-01') TO ('2022-03-10'),
- PARTITION sales_jan2022_1decade FOR VALUES FROM ('2022-01-01') TO ('2022-01-10'),
- PARTITION sales_feb2022_1decade FOR VALUES FROM ('2022-02-01') TO ('2022-02-10'),
- PARTITION sales_apr2022_1decade FOR VALUES FROM ('2022-04-01') TO ('2022-04-10'),
- PARTITION sales_others DEFAULT);
-SELECT tableoid::regclass, * FROM sales_range ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
- tableoid | salesperson_id | salesperson_name | sales_amount | sales_date
------------------------+----------------+------------------+--------------+------------
- sales_apr2022_1decade | 5 | Deev | 250 | 04-07-2022
- sales_apr2022_1decade | 11 | Trump | 380 | 04-06-2022
- sales_feb2022_1decade | 2 | Smirnoff | 500 | 02-09-2022
- sales_feb2022_1decade | 6 | Poirot | 150 | 02-07-2022
- sales_jan2022_1decade | 13 | Gandi | 377 | 01-09-2022
- sales_mar2022_1decade | 7 | Li | 175 | 03-08-2022
- sales_others | 1 | May | 1000 | 01-31-2022
- sales_others | 3 | Ford | 2000 | 04-30-2022
- sales_others | 4 | Ivanov | 750 | 04-13-2022
- sales_others | 8 | Ericsson | 185 | 02-23-2022
- sales_others | 9 | Muller | 250 | 03-11-2022
- sales_others | 10 | Halder | 350 | 01-28-2022
- sales_others | 12 | Plato | 350 | 03-19-2022
-(13 rows)
-
-DROP TABLE sales_range;
---
--- Test for split non-DEFAULT partition to DEFAULT partition + partitions
--- with spaces between bounds.
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_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');
-CREATE TABLE sales_all PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-05-01');
-INSERT INTO sales_range VALUES
- (1, 'May', 1000, '2022-01-31'),
- (2, 'Smirnoff', 500, '2022-02-10'),
- (3, 'Ford', 2000, '2022-04-30'),
- (4, 'Ivanov', 750, '2022-04-13'),
- (5, 'Deev', 250, '2022-04-07'),
- (6, 'Poirot', 150, '2022-02-11'),
- (7, 'Li', 175, '2022-03-08'),
- (8, 'Ericsson', 185, '2022-02-23'),
- (9, 'Muller', 250, '2022-03-11'),
- (10, 'Halder', 350, '2022-01-28'),
- (11, 'Trump', 380, '2022-04-06'),
- (12, 'Plato', 350, '2022-03-19'),
- (13, 'Gandi', 377, '2022-01-09');
-ALTER TABLE sales_range SPLIT PARTITION sales_all INTO
- (PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01'),
- PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- PARTITION sales_others DEFAULT);
-INSERT INTO sales_range VALUES (14, 'Smith', 510, '2022-05-04');
-SELECT tableoid::regclass, * FROM sales_range ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
- tableoid | salesperson_id | salesperson_name | sales_amount | sales_date
----------------+----------------+------------------+--------------+------------
- sales_apr2022 | 3 | Ford | 2000 | 04-30-2022
- sales_apr2022 | 4 | Ivanov | 750 | 04-13-2022
- sales_apr2022 | 5 | Deev | 250 | 04-07-2022
- sales_apr2022 | 11 | Trump | 380 | 04-06-2022
- sales_feb2022 | 2 | Smirnoff | 500 | 02-10-2022
- sales_feb2022 | 6 | Poirot | 150 | 02-11-2022
- sales_feb2022 | 8 | Ericsson | 185 | 02-23-2022
- sales_jan2022 | 1 | May | 1000 | 01-31-2022
- sales_jan2022 | 10 | Halder | 350 | 01-28-2022
- sales_jan2022 | 13 | Gandi | 377 | 01-09-2022
- sales_others | 7 | Li | 175 | 03-08-2022
- sales_others | 9 | Muller | 250 | 03-11-2022
- sales_others | 12 | Plato | 350 | 03-19-2022
- sales_others | 14 | Smith | 510 | 05-04-2022
-(14 rows)
-
-DROP TABLE sales_range;
---
--- Test that SPLIT PARTITION rejects the degenerate case where the only
--- non-DEFAULT replacement partition keeps the original bound and the command
--- merely adds a DEFAULT partition.
---
-CREATE TABLE t (i int) PARTITION BY RANGE (i);
-CREATE TABLE tp_0_50 PARTITION OF t FOR VALUES FROM (0) TO (50);
-INSERT INTO t VALUES (1);
--- ERROR
-ALTER TABLE t SPLIT PARTITION tp_0_50 INTO
- (PARTITION tp_0_50 FOR VALUES FROM (0) TO (50),
- PARTITION tp_default DEFAULT);
-ERROR: cannot split partition "tp_0_50" only to add a DEFAULT partition
-LINE 2: (PARTITION tp_0_50 FOR VALUES FROM (0) TO (50),
- ^
-DETAIL: The non-DEFAULT partition would keep the same partition bound.
-HINT: Use CREATE TABLE ... PARTITION OF ... DEFAULT to add a DEFAULT partition.
-DROP TABLE t;
---
--- Test that a LIST split with DEFAULT is not considered degenerate when
--- only NULL is removed from the explicit replacement partition.
---
-CREATE TABLE t (i int) PARTITION BY LIST (i);
-CREATE TABLE tp_null_1 PARTITION OF t FOR VALUES IN (NULL, 1);
-ALTER TABLE t SPLIT PARTITION tp_null_1 INTO
- (PARTITION tp_1 FOR VALUES IN (1),
- PARTITION tp_default DEFAULT);
-INSERT INTO t VALUES (NULL), (1), (2);
-SELECT tableoid::regclass, i FROM t ORDER BY tableoid::regclass::text COLLATE "C", i NULLS FIRST;
- tableoid | i
-------------+---
- tp_1 | 1
- tp_default |
- tp_default | 2
-(3 rows)
-
-DROP TABLE t;
---
--- Test that the same-bound check for LIST partitioning uses the
--- partition operator family, not byte equality. -0.0 and 0.0 have
--- different bit patterns but compare equal under float8, so the
--- replacement bound (-0.0, 1.0) is the same set as the original
--- (0.0, 1.0) and the SPLIT is degenerate. A datumIsEqual()-based
--- check would let this through; the partsupfunc-based check correctly
--- rejects it.
---
-CREATE TABLE t (v float8) PARTITION BY LIST (v);
-CREATE TABLE tp_zero_one PARTITION OF t FOR VALUES IN (0.0, 1.0);
--- ERROR
-ALTER TABLE t SPLIT PARTITION tp_zero_one INTO
- (PARTITION tp_zero_one FOR VALUES IN (-0.0, 1.0),
- PARTITION tp_default DEFAULT);
-ERROR: cannot split partition "tp_zero_one" only to add a DEFAULT partition
-LINE 2: (PARTITION tp_zero_one FOR VALUES IN (-0.0, 1.0),
- ^
-DETAIL: The non-DEFAULT partition would keep the same partition bound.
-HINT: Use CREATE TABLE ... PARTITION OF ... DEFAULT to add a DEFAULT partition.
-DROP TABLE t;
---
--- Test that the explicit partition bound cannot extend outside the split
--- partition's bound when a DEFAULT partition is specified.
---
-CREATE TABLE t (i int) PARTITION BY RANGE (i);
-CREATE TABLE tp_0_51 PARTITION OF t FOR VALUES FROM (0) TO (51);
-CREATE TABLE tp_51_100 PARTITION OF t FOR VALUES FROM (51) TO (100);
--- ERROR
-ALTER TABLE t SPLIT PARTITION tp_0_51 INTO
- (PARTITION tp_0_51 FOR VALUES FROM (0) TO (53),
- PARTITION tp_default DEFAULT);
-ERROR: upper bound of partition "tp_0_51" is greater than upper bound of split partition "tp_0_51"
-LINE 2: (PARTITION tp_0_51 FOR VALUES FROM (0) TO (53),
- ^
-HINT: Explicit partition bounds must be contained within the bounds of the split partition when a DEFAULT partition is specified.
-DROP TABLE t;
---
--- Try to SPLIT partition of another table.
---
-CREATE TABLE t1(i int, t text) PARTITION BY LIST (t);
-CREATE TABLE t1pa PARTITION OF t1 FOR VALUES IN ('A');
-CREATE TABLE t2 (i int, t text) PARTITION BY RANGE (t);
--- ERROR
-ALTER TABLE t2 SPLIT PARTITION t1pa INTO
- (PARTITION t2a FOR VALUES FROM ('A') TO ('B'),
- PARTITION t2b FOR VALUES FROM ('B') TO ('C'));
-ERROR: relation "t1pa" is not a partition of relation "t2"
-HINT: ALTER TABLE ... SPLIT PARTITION can only split partitions that don't have sub-partitions.
-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 COLLATE "C";
- oid | pg_get_expr | relpersistence
---------+----------------------------+----------------
- tp_0_2 | FOR VALUES FROM (0) TO (2) | t
-(1 row)
-
--- ERROR
-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));
-ERROR: cannot create a permanent relation as partition of temporary relation "t"
-ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
- (PARTITION pg_temp.tp_0_1 FOR VALUES FROM (0) TO (1),
- PARTITION pg_temp.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 COLLATE "C";
- 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;
--- Check the new partitions inherit parent's tablespace
-CREATE TABLE t (i int PRIMARY KEY USING INDEX TABLESPACE regress_tblspace)
- PARTITION BY RANGE (i) TABLESPACE regress_tblspace;
-CREATE TABLE tp_0_2 PARTITION OF t FOR VALUES FROM (0) TO (2);
-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));
-SELECT tablename, tablespace FROM pg_tables
- WHERE tablename IN ('t', 'tp_0_1', 'tp_1_2') AND schemaname = 'partition_split_schema'
- ORDER BY tablename COLLATE "C", tablespace COLLATE "C";
- tablename | tablespace
------------+------------------
- t | regress_tblspace
- tp_0_1 | regress_tblspace
- tp_1_2 | regress_tblspace
-(3 rows)
-
-SELECT tablename, indexname, tablespace FROM pg_indexes
- WHERE tablename IN ('t', 'tp_0_1', 'tp_1_2') AND schemaname = 'partition_split_schema'
- ORDER BY tablename COLLATE "C", indexname COLLATE "C", tablespace COLLATE "C";
- tablename | indexname | tablespace
------------+-------------+------------------
- t | t_pkey | regress_tblspace
- tp_0_1 | tp_0_1_pkey | regress_tblspace
- tp_1_2 | tp_1_2_pkey | regress_tblspace
-(3 rows)
-
-DROP TABLE t;
--- Check new partitions inherits parent's table access method
-CREATE ACCESS METHOD partition_split_heap TYPE TABLE HANDLER heap_tableam_handler;
-CREATE TABLE t (i int) PARTITION BY RANGE (i) USING partition_split_heap;
-CREATE TABLE tp_0_2 PARTITION OF t FOR VALUES FROM (0) TO (2);
-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));
-SELECT c.relname, a.amname
-FROM pg_class c JOIN pg_am a ON c.relam = a.oid
-WHERE c.oid IN ('t'::regclass, 'tp_0_1'::regclass, 'tp_1_2'::regclass)
-ORDER BY c.relname COLLATE "C";
- relname | amname
----------+----------------------
- t | partition_split_heap
- tp_0_1 | partition_split_heap
- tp_1_2 | partition_split_heap
-(3 rows)
-
-DROP TABLE t;
-DROP ACCESS METHOD partition_split_heap;
--- Split partition of a temporary table when one of the partitions after
--- split has the same name as the partition being split
-CREATE TEMP TABLE t (a int) PARTITION BY RANGE (a);
-CREATE TEMP TABLE tp_0 PARTITION OF t FOR VALUES FROM (0) TO (2);
-ALTER TABLE t SPLIT PARTITION tp_0 INTO
- (PARTITION pg_temp.tp_0 FOR VALUES FROM (0) TO (1),
- PARTITION pg_temp.tp_1 FOR VALUES FROM (1) TO (2));
-DROP TABLE t;
--- Check defaults and constraints of new partitions
-CREATE TABLE t_bigint (
- b bigint,
- i int DEFAULT (3+10),
- j int DEFAULT 101,
- k int GENERATED ALWAYS AS (b+10) STORED
-)
-PARTITION BY RANGE (b);
-CREATE TABLE t_bigint_default PARTITION OF t_bigint DEFAULT;
--- Show defaults/constraints before SPLIT PARTITION
-\d+ t_bigint
- Partitioned table "partition_split_schema.t_bigint"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------------------------------------+---------+--------------+-------------
- b | bigint | | | | plain | |
- i | integer | | | 3 + 10 | plain | |
- j | integer | | | 101 | plain | |
- k | integer | | | generated always as ((b + 10)) stored | plain | |
-Partition key: RANGE (b)
-Partitions:
- t_bigint_default DEFAULT
-
-\d+ t_bigint_default
- Table "partition_split_schema.t_bigint_default"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------------------------------------+---------+--------------+-------------
- b | bigint | | | | plain | |
- i | integer | | | 3 + 10 | plain | |
- j | integer | | | 101 | plain | |
- k | integer | | | generated always as ((b + 10)) stored | plain | |
-Partition of: t_bigint DEFAULT
-No partition constraint
-
-ALTER TABLE t_bigint SPLIT PARTITION t_bigint_default INTO
- (PARTITION t_bigint_01_10 FOR VALUES FROM (0) TO (10),
- PARTITION t_bigint_default DEFAULT);
--- Show defaults/constraints after SPLIT PARTITION
-\d+ t_bigint_default
- Table "partition_split_schema.t_bigint_default"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------------------------------------+---------+--------------+-------------
- b | bigint | | | | plain | |
- i | integer | | | 3 + 10 | plain | |
- j | integer | | | 101 | plain | |
- k | integer | | | generated always as ((b + 10)) stored | plain | |
-Partition of: t_bigint DEFAULT
-Partition constraint: (NOT ((b IS NOT NULL) AND ((b >= '0'::bigint) AND (b < '10'::bigint))))
-
-\d+ t_bigint_01_10
- Table "partition_split_schema.t_bigint_01_10"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+---------------------------------------+---------+--------------+-------------
- b | bigint | | | | plain | |
- i | integer | | | 3 + 10 | plain | |
- j | integer | | | 101 | plain | |
- k | integer | | | generated always as ((b + 10)) stored | plain | |
-Partition of: t_bigint FOR VALUES FROM ('0') TO ('10')
-Partition constraint: ((b IS NOT NULL) AND (b >= '0'::bigint) AND (b < '10'::bigint))
-
-DROP TABLE t_bigint;
--- Test permission checks. The user needs to own the parent table and the
--- the partition to split to do the split.
-CREATE ROLE regress_partition_split_alice;
-CREATE ROLE regress_partition_split_bob;
-GRANT ALL ON SCHEMA partition_split_schema TO regress_partition_split_alice;
-GRANT ALL ON SCHEMA partition_split_schema TO regress_partition_split_bob;
-SET SESSION AUTHORIZATION regress_partition_split_alice;
-CREATE TABLE t (i int) PARTITION BY RANGE (i);
-CREATE TABLE tp_0_2 PARTITION OF t FOR VALUES FROM (0) TO (2);
-SET SESSION AUTHORIZATION regress_partition_split_bob;
-ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
- (PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
- PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); --error
-ERROR: must be owner of table t
-RESET SESSION AUTHORIZATION;
-ALTER TABLE t OWNER TO regress_partition_split_bob;
-SET SESSION AUTHORIZATION regress_partition_split_bob;
-ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
- (PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
- PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); --error
-ERROR: must be owner of table tp_0_2
-RESET SESSION AUTHORIZATION;
-ALTER TABLE tp_0_2 OWNER TO regress_partition_split_bob;
-SET SESSION AUTHORIZATION regress_partition_split_bob;
-ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
- (PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
- PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); --ok
-RESET SESSION AUTHORIZATION;
-DROP TABLE t;
--- Test: owner of new partitions should be the same as owner of split partition
-CREATE TABLE t (i int) PARTITION BY RANGE (i);
-SET SESSION AUTHORIZATION regress_partition_split_alice;
-CREATE TABLE tp_0_2(i int);
-RESET SESSION AUTHORIZATION;
-ALTER TABLE t ATTACH PARTITION tp_0_2 FOR VALUES FROM (0) TO (2);
--- Owner is 'regress_partition_split_alice':
-\dt tp_0_2
- List of tables
- Schema | Name | Type | Owner
-------------------------+--------+-------+-------------------------------
- partition_split_schema | tp_0_2 | table | regress_partition_split_alice
-(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));
--- Owner should be 'regress_partition_split_alice':
-\dt tp_0_1
- List of tables
- Schema | Name | Type | Owner
-------------------------+--------+-------+-------------------------------
- partition_split_schema | tp_0_1 | table | regress_partition_split_alice
-(1 row)
-
-\dt tp_1_2
- List of tables
- Schema | Name | Type | Owner
-------------------------+--------+-------+-------------------------------
- partition_split_schema | tp_1_2 | table | regress_partition_split_alice
-(1 row)
-
-DROP TABLE t;
--- Test: index of new partitions should be created with same owner as split
--- partition
-SET SESSION AUTHORIZATION regress_partition_split_alice;
-CREATE TABLE t (i int) PARTITION BY RANGE (i);
-CREATE TABLE tp_10_20 PARTITION OF t FOR VALUES FROM (10) TO (20);
-INSERT INTO t VALUES (11), (16);
-CREATE OR REPLACE FUNCTION run_me(integer) RETURNS integer AS $$
-BEGIN
- RAISE NOTICE 'you are running me as %', CURRENT_USER;
- RETURN $1;
-END
-$$ LANGUAGE PLPGSQL IMMUTABLE;
--- Owner is 'regress_partition_split_alice':
-CREATE INDEX ON t (run_me(i));
-NOTICE: you are running me as regress_partition_split_alice
-NOTICE: you are running me as regress_partition_split_alice
-RESET SESSION AUTHORIZATION;
--- Owner should be 'regress_partition_split_alice':
-ALTER TABLE t SPLIT PARTITION tp_10_20 INTO
- (PARTITION tp_10_15 FOR VALUES FROM (10) TO (15),
- PARTITION tp_15_20 FOR VALUES FROM (15) TO (20));
-NOTICE: you are running me as regress_partition_split_alice
-NOTICE: you are running me as regress_partition_split_alice
-DROP TABLE t;
-DROP FUNCTION run_me(integer);
-REVOKE ALL ON SCHEMA partition_split_schema FROM regress_partition_split_alice;
-REVOKE ALL ON SCHEMA partition_split_schema FROM regress_partition_split_bob;
-DROP ROLE regress_partition_split_alice;
-DROP ROLE regress_partition_split_bob;
--- Test for hash partitioned table
-CREATE TABLE t (i int) PARTITION BY HASH(i);
-CREATE TABLE tp1 PARTITION OF t FOR VALUES WITH (MODULUS 2, REMAINDER 0);
-CREATE TABLE tp2 PARTITION OF t FOR VALUES WITH (MODULUS 2, REMAINDER 1);
--- ERROR
-ALTER TABLE t SPLIT PARTITION tp1 INTO
- (PARTITION tp1_1 FOR VALUES WITH (MODULUS 4, REMAINDER 0),
- PARTITION tp1_2 FOR VALUES WITH (MODULUS 4, REMAINDER 2));
-ERROR: partition of hash-partitioned table cannot be split
--- ERROR
-ALTER TABLE t SPLIT PARTITION tp1 INTO
- (PARTITION tp1_1 FOR VALUES WITH (MODULUS 4, REMAINDER 0));
-ERROR: list of new partitions must contain at least two partitions
-DROP TABLE t;
--- Test for split partition properties:
--- * STATISTICS is empty
--- * COMMENT is empty
--- * DEFAULTS are the same as DEFAULTS for partitioned table
--- * STORAGE is the same as STORAGE for partitioned table
--- * GENERATED and CONSTRAINTS are the same as GENERATED and CONSTRAINTS for partitioned table
--- * TRIGGERS are the same as TRIGGERS for partitioned table
-CREATE TABLE t
-(i int NOT NULL,
- t text STORAGE EXTENDED COMPRESSION pglz DEFAULT 'default_t',
- b bigint,
- d date GENERATED ALWAYS as ('2022-01-01') STORED) PARTITION BY RANGE (abs(i));
-COMMENT ON COLUMN t.i IS 't1.i';
-CREATE TABLE tp_x
-(i int NOT NULL,
- t text STORAGE MAIN DEFAULT 'default_tp_x',
- b bigint,
- d date GENERATED ALWAYS as ('2022-02-02') STORED);
-ALTER TABLE t ATTACH PARTITION tp_x FOR VALUES FROM (0) TO (2);
-COMMENT ON COLUMN tp_x.i IS 'tp_x.i';
-CREATE STATISTICS t_stat (DEPENDENCIES) on i, b from t;
-CREATE STATISTICS tp_x_stat (DEPENDENCIES) on i, b from tp_x;
-ALTER TABLE t ADD CONSTRAINT t_b_check CHECK (b > 0);
-ALTER TABLE t ADD CONSTRAINT t_b_check1 CHECK (b > 0) NOT ENFORCED;
-ALTER TABLE t ADD CONSTRAINT t_b_check2 CHECK (b > 0) NOT VALID;
-ALTER TABLE t ADD CONSTRAINT t_b_nn NOT NULL b NOT VALID;
-INSERT INTO tp_x(i, t, b) VALUES(0, DEFAULT, 1);
-INSERT INTO tp_x(i, t, b) VALUES(1, DEFAULT, 2);
-CREATE OR REPLACE FUNCTION trigger_function() RETURNS trigger LANGUAGE 'plpgsql' AS
-$BODY$
-BEGIN
- RAISE NOTICE 'trigger(%) called: action = %, when = %, level = %', TG_ARGV[0], TG_OP, TG_WHEN, TG_LEVEL;
- RETURN new;
-END;
-$BODY$;
-CREATE TRIGGER t_before_insert_row_trigger BEFORE INSERT ON t FOR EACH ROW
- EXECUTE PROCEDURE trigger_function('t');
-CREATE TRIGGER tp_x_before_insert_row_trigger BEFORE INSERT ON tp_x FOR EACH ROW
- EXECUTE PROCEDURE trigger_function('tp_x');
-\d+ tp_x
- Table "partition_split_schema.tp_x"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+-------------------------------------------------+---------+--------------+-------------
- i | integer | | not null | | plain | | tp_x.i
- t | text | | | 'default_tp_x'::text | main | |
- b | bigint | | not null | | plain | |
- d | date | | | generated always as ('02-02-2022'::date) stored | plain | |
-Partition of: t FOR VALUES FROM (0) TO (2)
-Partition constraint: ((abs(i) IS NOT NULL) AND (abs(i) >= 0) AND (abs(i) < 2))
-Check constraints:
- "t_b_check" CHECK (b > 0)
- "t_b_check1" CHECK (b > 0) NOT ENFORCED
- "t_b_check2" CHECK (b > 0) NOT VALID
-Statistics objects:
- "partition_split_schema.tp_x_stat" (dependencies) ON i, b FROM tp_x
-Not-null constraints:
- "tp_x_i_not_null" NOT NULL "i" (inherited)
- "t_b_nn" NOT NULL "b" (inherited) NOT VALID
-Triggers:
- t_before_insert_row_trigger BEFORE INSERT ON tp_x FOR EACH ROW EXECUTE FUNCTION trigger_function('t'), ON TABLE t
- tp_x_before_insert_row_trigger BEFORE INSERT ON tp_x FOR EACH ROW EXECUTE FUNCTION trigger_function('tp_x')
-
-ALTER TABLE t SPLIT PARTITION tp_x INTO
- (PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
- PARTITION tp_x FOR VALUES FROM (1) TO (2));
-\d+ tp_x
- Table "partition_split_schema.tp_x"
- Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
---------+---------+-----------+----------+-------------------------------------------------+----------+--------------+-------------
- i | integer | | not null | | plain | |
- t | text | | | 'default_t'::text | extended | |
- b | bigint | | not null | | plain | |
- d | date | | | generated always as ('01-01-2022'::date) stored | plain | |
-Partition of: t FOR VALUES FROM (1) TO (2)
-Partition constraint: ((abs(i) IS NOT NULL) AND (abs(i) >= 1) AND (abs(i) < 2))
-Check constraints:
- "t_b_check" CHECK (b > 0)
- "t_b_check1" CHECK (b > 0) NOT ENFORCED
- "t_b_check2" CHECK (b > 0) NOT VALID
-Not-null constraints:
- "t_i_not_null" NOT NULL "i" (inherited)
- "t_b_nn" NOT NULL "b" (inherited) NOT VALID
-Triggers:
- t_before_insert_row_trigger BEFORE INSERT ON tp_x FOR EACH ROW EXECUTE FUNCTION trigger_function('t'), ON TABLE t
-
-INSERT INTO t(i, t, b) VALUES(1, DEFAULT, 3);
-NOTICE: trigger(t) called: action = INSERT, when = BEFORE, level = ROW
-SELECT tableoid::regclass, * FROM t ORDER BY tableoid::regclass::text COLLATE "C", b;
- tableoid | i | t | b | d
-----------+---+--------------+---+------------
- tp_0_1 | 0 | default_tp_x | 1 | 01-01-2022
- tp_x | 1 | default_tp_x | 2 | 01-01-2022
- tp_x | 1 | default_t | 3 | 01-01-2022
-(3 rows)
-
-DROP TABLE t;
-DROP FUNCTION trigger_function();
--- Test for recomputation of stored generated columns.
-CREATE TABLE t (i int, tab_id int generated always as (tableoid) stored) PARTITION BY RANGE (i);
-CREATE TABLE tp_0_2 PARTITION OF t FOR VALUES FROM (0) TO (2);
-ALTER TABLE t ADD CONSTRAINT cc CHECK(tableoid <> 123456789);
-INSERT INTO t VALUES (0), (1);
--- Should be 1 because partition identifier for row with i=0 is the same as
--- partition identifier for row with i=1.
-SELECT count(*) FROM t WHERE i = 0 AND tab_id IN (SELECT tab_id FROM t WHERE i = 1);
- count
--------
- 1
-(1 row)
-
--- "tab_id" column (stored generated column) with "tableoid" attribute requires
--- recomputation here.
-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));
--- Should be 0 because partition identifier for row with i=0 is different from
--- partition identifier for row with i=1.
-SELECT count(*) FROM t WHERE i = 0 AND tab_id IN (SELECT tab_id FROM t WHERE i = 1);
- count
--------
- 0
-(1 row)
-
-DROP TABLE t;
--- Each new partition produced by SPLIT must get its own TOAST table so
--- that out-of-line varlena attributes coming from the source partition
--- can be stored. SET STORAGE EXTERNAL forces externalization for any
--- value over the TOAST threshold, so a string over that threshold
--- suffices to exercise the toast-table dependency.
-CREATE TABLE t (a text) PARTITION BY RANGE(a);
-ALTER TABLE t ALTER COLUMN a SET STORAGE EXTERNAL;
-CREATE TABLE tp_all PARTITION OF t FOR VALUES FROM (MINVALUE) TO (MAXVALUE);
-INSERT INTO t SELECT repeat('1', 10000);
-ALTER TABLE t SPLIT PARTITION tp_all INTO (
- PARTITION tp_lo FOR VALUES FROM (MINVALUE) TO ('2'),
- PARTITION tp_hi FOR VALUES FROM ('2') TO (MAXVALUE)
-);
-SELECT relname,
- reltoastrelid <> 0 AS has_toast,
- pg_relation_size(reltoastrelid) > 0 AS toast_used
- FROM pg_class WHERE relname IN ('tp_lo', 'tp_hi') ORDER BY relname;
- relname | has_toast | toast_used
----------+-----------+------------
- tp_hi | t | f
- tp_lo | t | t
-(2 rows)
-
-SELECT length(a) FROM t;
- length
---------
- 10000
-(1 row)
-
-DROP TABLE t;
--- Tablespace selection for the new partitions mirrors
--- CREATE TABLE ... PARTITION OF: the partitioned root's explicit
--- tablespace wins; otherwise default_tablespace applies; otherwise the
--- database default is used.
-CREATE TABLE t (i int) PARTITION BY RANGE(i) TABLESPACE regress_tblspace;
-CREATE TABLE tp_all PARTITION OF t FOR VALUES FROM (0) TO (10);
-INSERT INTO t SELECT generate_series(0, 9);
-ALTER TABLE t SPLIT PARTITION tp_all INTO (
- PARTITION tp_lo FOR VALUES FROM (0) TO (5),
- PARTITION tp_hi FOR VALUES FROM (5) TO (10)
-);
-SELECT c.relname, s.spcname FROM pg_class c LEFT JOIN pg_tablespace s
- ON c.reltablespace = s.oid WHERE c.relname IN ('tp_lo', 'tp_hi')
- ORDER BY c.relname;
- relname | spcname
----------+------------------
- tp_hi | regress_tblspace
- tp_lo | regress_tblspace
-(2 rows)
-
-DROP TABLE t;
--- Parent has no explicit tablespace, but default_tablespace is set: the
--- new partitions land on default_tablespace.
-CREATE TABLE t (i int) PARTITION BY RANGE(i);
-CREATE TABLE tp_all PARTITION OF t FOR VALUES FROM (0) TO (10);
-INSERT INTO t SELECT generate_series(0, 9);
-SET default_tablespace TO regress_tblspace;
-ALTER TABLE t SPLIT PARTITION tp_all INTO (
- PARTITION tp_lo FOR VALUES FROM (0) TO (5),
- PARTITION tp_hi FOR VALUES FROM (5) TO (10)
-);
-RESET default_tablespace;
-SELECT c.relname, s.spcname FROM pg_class c LEFT JOIN pg_tablespace s
- ON c.reltablespace = s.oid WHERE c.relname IN ('tp_lo', 'tp_hi')
- ORDER BY c.relname;
- relname | spcname
----------+------------------
- tp_hi | regress_tblspace
- tp_lo | regress_tblspace
-(2 rows)
-
-DROP TABLE t;
-CREATE TABLE t (i int) PARTITION BY RANGE(i);
-CREATE TABLE tp_all PARTITION OF t FOR VALUES FROM (0) TO (10);
-INSERT INTO t SELECT generate_series(0, 9);
--- pg_global is rejected when picked up from default_tablespace.
-SET default_tablespace TO pg_global;
-ALTER TABLE t SPLIT PARTITION tp_all INTO (
- PARTITION tp_lo FOR VALUES FROM (0) TO (5),
- PARTITION tp_hi FOR VALUES FROM (5) TO (10)
-); -- fails
-ERROR: only shared relations can be placed in pg_global tablespace
-RESET default_tablespace;
--- Parent has no explicit tablespace and default_tablespace is empty: new
--- partitions use the database default (reltablespace = 0).
-ALTER TABLE t SPLIT PARTITION tp_all INTO (
- PARTITION tp_lo FOR VALUES FROM (0) TO (5),
- PARTITION tp_hi FOR VALUES FROM (5) TO (10)
-);
-SELECT relname, reltablespace FROM pg_class
- WHERE relname IN ('tp_lo', 'tp_hi') ORDER BY relname;
- relname | reltablespace
----------+---------------
- tp_hi | 0
- tp_lo | 0
-(2 rows)
-
-DROP TABLE t;
-RESET search_path;
---
-DROP SCHEMA partition_split_schema;
-DROP SCHEMA partition_split_schema2;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 8fa0a6c47fb..8356ca98ef2 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -123,7 +123,7 @@ test: plancache limit plpgsql copy2 temp domain rangefuncs prepare conversion tr
# The stats test resets stats, so nothing else needing stats access can be in
# this group.
# ----------
-test: partition_merge partition_split partition_join partition_prune reloptions hash_part indexing partition_aggregate partition_info tuplesort explain memoize stats predicate numa eager_aggregate graph_table_rls planner_est
+test: partition_join partition_prune reloptions hash_part indexing partition_aggregate partition_info tuplesort explain memoize stats predicate numa eager_aggregate graph_table_rls planner_est
# ----------
# Another group of parallel tests (compression)
diff --git a/src/test/regress/sql/partition_merge.sql b/src/test/regress/sql/partition_merge.sql
deleted file mode 100644
index 80dc365b0ce..00000000000
--- a/src/test/regress/sql/partition_merge.sql
+++ /dev/null
@@ -1,846 +0,0 @@
---
--- PARTITIONS_MERGE
--- Tests for "ALTER TABLE ... MERGE PARTITIONS ..." command
---
-
-CREATE SCHEMA partitions_merge_schema;
-CREATE SCHEMA partitions_merge_schema2;
-SET search_path = partitions_merge_schema, public;
-
---
--- BY RANGE partitioning
---
-
---
--- Test for error codes
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_name VARCHAR(30), sales_amount INT, sales_date DATE) PARTITION BY RANGE (sales_date);
-CREATE TABLE sales_dec2021 PARTITION OF sales_range FOR VALUES FROM ('2021-12-01') TO ('2021-12-31');
-CREATE TABLE sales_jan2022 PARTITION OF sales_range FOR VALUES FROM ('2022-01-01') TO ('2022-02-01');
-CREATE TABLE sales_feb2022 PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-03-01');
-CREATE TABLE sales_mar2022 PARTITION OF sales_range FOR VALUES FROM ('2022-03-01') TO ('2022-04-01');
-
-CREATE TABLE sales_apr2022 (salesperson_id INT, salesperson_name VARCHAR(30), sales_amount INT, sales_date DATE) PARTITION BY RANGE (sales_date);
-CREATE TABLE sales_apr_1 PARTITION OF sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-04-15');
-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
-ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_feb2022) INTO sales_feb_mar_apr2022;
--- ERROR
-ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_apr2022) INTO sales_feb_mar_apr2022;
--- ERROR
--- (space between sections sales_jan2022 and sales_mar2022)
-ALTER TABLE sales_range MERGE PARTITIONS (sales_jan2022, sales_mar2022) INTO sales_jan_mar2022;
--- ERROR
--- (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
-ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, partitions_merge_schema.sales_feb2022) INTO sales_feb_mar_apr2022;
--- ERROR
-ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_jan2022) INTO sales_apr_2;
-
-CREATE VIEW jan2022v as SELECT * FROM sales_jan2022;
-ALTER TABLE sales_range MERGE PARTITIONS (sales_jan2022, sales_feb2022) INTO sales_dec_jan_feb2022;
-DROP VIEW jan2022v;
-
--- NO ERROR: test for custom partitions order, source partitions not in the search_path
-SET search_path = partitions_merge_schema2, public;
-ALTER TABLE partitions_merge_schema.sales_range MERGE PARTITIONS (
- partitions_merge_schema.sales_feb2022,
- partitions_merge_schema.sales_mar2022,
- partitions_merge_schema.sales_jan2022) INTO sales_jan_feb_mar2022;
-SET search_path = partitions_merge_schema, public;
-
-PREPARE get_partition_info(regclass[]) AS
-SELECT c.oid::pg_catalog.regclass,
- c.relpersistence,
- c.relkind,
- i.inhdetachpending,
- pg_catalog.pg_get_expr(c.relpartbound, c.oid)
-FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
-WHERE c.oid = i.inhrelid AND i.inhparent = ANY($1)
-ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT',
- c.oid::regclass::text COLLATE "C";
-
-EXECUTE get_partition_info('{sales_range}');
-
-DROP TABLE sales_range;
-
---
--- Add rows into partitioned table, then merge partitions
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_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');
-CREATE TABLE sales_feb2022 PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-03-01');
-CREATE TABLE sales_mar2022 PARTITION OF sales_range FOR VALUES FROM ('2022-03-01') TO ('2022-04-01');
-CREATE TABLE sales_apr2022 PARTITION OF sales_range FOR VALUES FROM ('2022-04-01') TO ('2022-05-01');
-CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
-CREATE INDEX sales_range_sales_date_idx ON sales_range USING btree (sales_date);
-
-INSERT INTO sales_range VALUES
- (1, 'May', 1000, '2022-01-31'),
- (2, 'Smirnoff', 500, '2022-02-10'),
- (3, 'Ford', 2000, '2022-04-30'),
- (4, 'Ivanov', 750, '2022-04-13'),
- (5, 'Deev', 250, '2022-04-07'),
- (6, 'Poirot', 150, '2022-02-11'),
- (7, 'Li', 175, '2022-03-08'),
- (8, 'Ericsson', 185, '2022-02-23'),
- (9, 'Muller', 250, '2022-03-11'),
- (10, 'Halder', 350, '2022-01-28'),
- (11, 'Trump', 380, '2022-04-06'),
- (12, 'Plato', 350, '2022-03-19'),
- (13, 'Gandi', 377, '2022-01-09'),
- (14, 'Smith', 510, '2022-05-04');
-
-SELECT pg_catalog.pg_get_partkeydef('sales_range'::regclass);
-
--- show partitions with conditions:
-EXECUTE get_partition_info('{sales_range}');
-
--- check schema-qualified name of the new partition
-ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_apr2022) INTO partitions_merge_schema2.sales_feb_mar_apr2022;
-
--- show partitions with conditions:
-EXECUTE get_partition_info('{sales_range}');
-
-SELECT * FROM pg_indexes WHERE tablename = 'sales_feb_mar_apr2022' and schemaname = 'partitions_merge_schema2';
-
-SELECT tableoid::regclass, * FROM sales_range ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
-
--- Use indexscan for testing indexes
-SET enable_seqscan = OFF;
-
-EXPLAIN (COSTS OFF) SELECT * FROM partitions_merge_schema2.sales_feb_mar_apr2022 where sales_date > '2022-01-01';
-SELECT * FROM partitions_merge_schema2.sales_feb_mar_apr2022 where sales_date > '2022-01-01';
-
-RESET enable_seqscan;
-
-DROP TABLE sales_range;
-
---
--- Merge some partitions into DEFAULT partition
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_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');
-CREATE TABLE sales_feb2022 PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-03-01');
-CREATE TABLE sales_mar2022 PARTITION OF sales_range FOR VALUES FROM ('2022-03-01') TO ('2022-04-01');
-CREATE TABLE sales_apr2022 PARTITION OF sales_range FOR VALUES FROM ('2022-04-01') TO ('2022-05-01');
-CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
-CREATE INDEX sales_range_sales_date_idx ON sales_range USING btree (sales_date);
-
-INSERT INTO sales_range VALUES
- (1, 'May', 1000, '2022-01-31'),
- (2, 'Smirnoff', 500, '2022-02-10'),
- (3, 'Ford', 2000, '2022-04-30'),
- (4, 'Ivanov', 750, '2022-04-13'),
- (5, 'Deev', 250, '2022-04-07'),
- (6, 'Poirot', 150, '2022-02-11'),
- (7, 'Li', 175, '2022-03-08'),
- (8, 'Ericsson', 185, '2022-02-23'),
- (9, 'Muller', 250, '2022-03-11'),
- (10, 'Halder', 350, '2022-01-28'),
- (11, 'Trump', 380, '2022-04-06'),
- (12, 'Plato', 350, '2022-03-19'),
- (13, 'Gandi', 377, '2022-01-09'),
- (14, 'Smith', 510, '2022-05-04');
-
--- Merge partitions (include DEFAULT partition) into partition with the same
--- name
-ALTER TABLE sales_range MERGE PARTITIONS
- (sales_jan2022, sales_mar2022, partitions_merge_schema.sales_others) INTO sales_others;
-
-SELECT * FROM sales_others ORDER BY salesperson_id;
-
--- show partitions with conditions:
-EXECUTE get_partition_info('{sales_range}');
-
-DROP TABLE sales_range;
-
---
--- Test for:
--- * composite partition key;
--- * GENERATED column;
--- * column with DEFAULT value.
---
-CREATE TABLE sales_date (salesperson_name VARCHAR(30), sales_year INT, sales_month INT, sales_day INT,
- sales_date VARCHAR(10) GENERATED ALWAYS AS
- (LPAD(sales_year::text, 4, '0') || '.' || LPAD(sales_month::text, 2, '0') || '.' || LPAD(sales_day::text, 2, '0')) STORED,
- sales_department VARCHAR(30) DEFAULT 'Sales department')
- PARTITION BY RANGE (sales_year, sales_month, sales_day);
-
-CREATE TABLE sales_dec2022 PARTITION OF sales_date FOR VALUES FROM (2021, 12, 1) TO (2022, 1, 1);
-CREATE TABLE sales_jan2022 PARTITION OF sales_date FOR VALUES FROM (2022, 1, 1) TO (2022, 2, 1);
-CREATE TABLE sales_feb2022 PARTITION OF sales_date FOR VALUES FROM (2022, 2, 1) TO (2022, 3, 1);
-CREATE TABLE sales_other PARTITION OF sales_date FOR VALUES FROM (2022, 3, 1) TO (MAXVALUE, MAXVALUE, MAXVALUE);
-
-INSERT INTO sales_date(salesperson_name, sales_year, sales_month, sales_day) VALUES
- ('Manager1', 2021, 12, 7),
- ('Manager2', 2021, 12, 8),
- ('Manager3', 2022, 1, 1),
- ('Manager1', 2022, 2, 4),
- ('Manager2', 2022, 1, 2),
- ('Manager3', 2022, 2, 1),
- ('Manager1', 2022, 3, 3),
- ('Manager2', 2022, 3, 4),
- ('Manager3', 2022, 5, 1);
-
-SELECT tableoid::regclass, * FROM sales_date;
-
-ALTER TABLE sales_date MERGE PARTITIONS (sales_jan2022, sales_feb2022) INTO sales_jan_feb2022;
-
-INSERT INTO sales_date(salesperson_name, sales_year, sales_month, sales_day) VALUES
- ('Manager1', 2022, 1, 10),
- ('Manager2', 2022, 2, 10);
-
-SELECT tableoid::regclass, * FROM sales_date;
-DROP TABLE sales_date;
-
---
--- Test: merge partitions of partitioned table with triggers
---
-CREATE TABLE salespeople(salesperson_id INT PRIMARY KEY, salesperson_name VARCHAR(30)) PARTITION BY RANGE (salesperson_id);
-
-CREATE TABLE salespeople01_10 PARTITION OF salespeople FOR VALUES FROM (1) TO (10);
-CREATE TABLE salespeople10_20 PARTITION OF salespeople FOR VALUES FROM (10) TO (20);
-CREATE TABLE salespeople20_30 PARTITION OF salespeople FOR VALUES FROM (20) TO (30);
-CREATE TABLE salespeople30_40 PARTITION OF salespeople FOR VALUES FROM (30) TO (40);
-
-INSERT INTO salespeople VALUES (1, 'Poirot');
-
-CREATE OR REPLACE FUNCTION after_insert_row_trigger() RETURNS trigger LANGUAGE 'plpgsql' AS $BODY$
-BEGIN
- RAISE NOTICE 'trigger(%) called: action = %, when = %, level = %', TG_ARGV[0], TG_OP, TG_WHEN, TG_LEVEL;
- RETURN NULL;
-END;
-$BODY$;
-
-CREATE TRIGGER salespeople_after_insert_statement_trigger
- AFTER INSERT
- ON salespeople
- FOR EACH STATEMENT
- EXECUTE PROCEDURE after_insert_row_trigger('salespeople');
-
-CREATE TRIGGER salespeople_after_insert_row_trigger
- AFTER INSERT
- ON salespeople
- FOR EACH ROW
- EXECUTE PROCEDURE after_insert_row_trigger('salespeople');
-
--- 2 triggers should fire here (row + statement):
-INSERT INTO salespeople VALUES (10, 'May');
--- 1 trigger should fire here (row):
-INSERT INTO salespeople10_20 VALUES (19, 'Ivanov');
-
-ALTER TABLE salespeople MERGE PARTITIONS (salespeople10_20, salespeople20_30, salespeople30_40) INTO salespeople10_40;
-
--- 2 triggers should fire here (row + statement):
-INSERT INTO salespeople VALUES (20, 'Smirnoff');
--- 1 trigger should fire here (row):
-INSERT INTO salespeople10_40 VALUES (30, 'Ford');
-
-SELECT * FROM salespeople01_10;
-SELECT * FROM salespeople10_40;
-
-DROP TABLE salespeople;
-DROP FUNCTION after_insert_row_trigger();
-
---
--- Test: merge partitions with deleted columns
---
-CREATE TABLE salespeople(salesperson_id INT PRIMARY KEY, salesperson_name VARCHAR(30)) PARTITION BY RANGE (salesperson_id);
-
-CREATE TABLE salespeople01_10 PARTITION OF salespeople FOR VALUES FROM (1) TO (10);
--- Create partitions with some deleted columns:
-CREATE TABLE salespeople10_20(d1 VARCHAR(30), salesperson_id INT PRIMARY KEY, salesperson_name VARCHAR(30));
-CREATE TABLE salespeople20_30(salesperson_id INT PRIMARY KEY, d2 INT, salesperson_name VARCHAR(30));
-CREATE TABLE salespeople30_40(salesperson_id INT PRIMARY KEY, d3 DATE, salesperson_name VARCHAR(30));
-
-INSERT INTO salespeople10_20 VALUES ('dummy value 1', 19, 'Ivanov');
-INSERT INTO salespeople20_30 VALUES (20, 101, 'Smirnoff');
-INSERT INTO salespeople30_40 VALUES (31, now(), 'Popov');
-
-ALTER TABLE salespeople10_20 DROP COLUMN d1;
-ALTER TABLE salespeople20_30 DROP COLUMN d2;
-ALTER TABLE salespeople30_40 DROP COLUMN d3;
-
-ALTER TABLE salespeople ATTACH PARTITION salespeople10_20 FOR VALUES FROM (10) TO (20);
-ALTER TABLE salespeople ATTACH PARTITION salespeople20_30 FOR VALUES FROM (20) TO (30);
-ALTER TABLE salespeople ATTACH PARTITION salespeople30_40 FOR VALUES FROM (30) TO (40);
-
-INSERT INTO salespeople VALUES
- (1, 'Poirot'),
- (10, 'May'),
- (30, 'Ford');
-
-ALTER TABLE salespeople MERGE PARTITIONS (salespeople10_20, salespeople20_30, salespeople30_40) INTO salespeople10_40;
-
-select * from salespeople;
-select * from salespeople01_10;
-select * from salespeople10_40;
-
-DROP TABLE salespeople;
-
---
--- Test: merge sub-partitions
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_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');
-CREATE TABLE sales_feb2022 PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-03-01');
-CREATE TABLE sales_mar2022 PARTITION OF sales_range FOR VALUES FROM ('2022-03-01') TO ('2022-04-01');
-
-CREATE TABLE sales_apr2022 (salesperson_id INT, salesperson_name VARCHAR(30), sales_amount INT, sales_date DATE) PARTITION BY RANGE (sales_date);
-CREATE TABLE sales_apr2022_01_10 PARTITION OF sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-04-10');
-CREATE TABLE sales_apr2022_10_20 PARTITION OF sales_apr2022 FOR VALUES FROM ('2022-04-10') TO ('2022-04-20');
-CREATE TABLE sales_apr2022_20_30 PARTITION OF sales_apr2022 FOR VALUES FROM ('2022-04-20') 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;
-
-CREATE INDEX sales_range_sales_date_idx ON sales_range USING btree (sales_date);
-
-INSERT INTO sales_range VALUES
- (1, 'May', 1000, '2022-01-31'),
- (2, 'Smirnoff', 500, '2022-02-10'),
- (3, 'Ford', 2000, '2022-04-30'),
- (4, 'Ivanov', 750, '2022-04-13'),
- (5, 'Deev', 250, '2022-04-07'),
- (6, 'Poirot', 150, '2022-02-11'),
- (7, 'Li', 175, '2022-03-08'),
- (8, 'Ericsson', 185, '2022-02-23'),
- (9, 'Muller', 250, '2022-03-11'),
- (10, 'Halder', 350, '2022-01-28'),
- (11, 'Trump', 380, '2022-04-06'),
- (12, 'Plato', 350, '2022-03-19'),
- (13, 'Gandi', 377, '2022-01-09'),
- (14, 'Smith', 510, '2022-05-04');
-
-SELECT tableoid::regclass, * FROM sales_apr2022 ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
-
-ALTER TABLE sales_apr2022 MERGE PARTITIONS (sales_apr2022_01_10, sales_apr2022_10_20, sales_apr2022_20_30) INTO sales_apr_all;
-
-SELECT tableoid::regclass, * FROM sales_apr2022 ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
-
-DROP TABLE sales_range;
-
---
--- BY LIST partitioning
---
-
---
--- Test: specific errors for BY LIST partitioning
---
-CREATE TABLE sales_list
-(salesperson_id INT GENERATED ALWAYS AS IDENTITY,
- salesperson_name VARCHAR(30),
- sales_state VARCHAR(20),
- sales_amount INT,
- sales_date DATE)
-PARTITION BY LIST (sales_state);
-CREATE TABLE sales_nord PARTITION OF sales_list FOR VALUES IN ('Oslo', 'St. Petersburg', 'Helsinki');
-CREATE TABLE sales_west PARTITION OF sales_list FOR VALUES IN ('Lisbon', 'New York', 'Madrid');
-CREATE TABLE sales_east PARTITION OF sales_list FOR VALUES IN ('Beijing', 'Delhi', 'Vladivostok');
-CREATE TABLE sales_central PARTITION OF sales_list FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv');
-CREATE TABLE sales_others PARTITION OF sales_list DEFAULT;
-
-
-CREATE TABLE sales_list2 (LIKE sales_list) PARTITION BY LIST (sales_state);
-CREATE TABLE sales_nord2 PARTITION OF sales_list2 FOR VALUES IN ('Oslo', 'St. Petersburg', 'Helsinki');
-CREATE TABLE sales_others2 PARTITION OF sales_list2 DEFAULT;
-
-
-CREATE TABLE sales_external (LIKE sales_list);
-CREATE TABLE sales_external2 (vch VARCHAR(5));
-
--- ERROR
-ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_external) INTO sales_all;
--- ERROR
-ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_external2) INTO sales_all;
--- ERROR
-ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_nord2, sales_east) INTO sales_all;
-
-DROP TABLE sales_external2;
-DROP TABLE sales_external;
-DROP TABLE sales_list2;
-DROP TABLE sales_list;
-
---
--- Test: BY LIST partitioning, MERGE PARTITIONS with data
---
-CREATE TABLE sales_list
-(salesperson_id INT GENERATED ALWAYS AS IDENTITY,
- salesperson_name VARCHAR(30),
- sales_state VARCHAR(20),
- sales_amount INT,
- sales_date DATE)
-PARTITION BY LIST (sales_state);
-
-CREATE INDEX sales_list_salesperson_name_idx ON sales_list USING btree (salesperson_name);
-CREATE INDEX sales_list_sales_state_idx ON sales_list USING btree (sales_state);
-
-CREATE TABLE sales_nord PARTITION OF sales_list FOR VALUES IN ('Oslo', 'St. Petersburg', 'Helsinki');
-CREATE TABLE sales_west PARTITION OF sales_list FOR VALUES IN ('Lisbon', 'New York', 'Madrid');
-CREATE TABLE sales_east PARTITION OF sales_list FOR VALUES IN ('Beijing', 'Delhi', 'Vladivostok');
-CREATE TABLE sales_central PARTITION OF sales_list FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv');
-CREATE TABLE sales_others PARTITION OF sales_list DEFAULT;
-
-INSERT INTO sales_list (salesperson_name, sales_state, sales_amount, sales_date) VALUES
- ('Trump', 'Beijing', 1000, '2022-03-01'),
- ('Smirnoff', 'New York', 500, '2022-03-03'),
- ('Ford', 'St. Petersburg', 2000, '2022-03-05'),
- ('Ivanov', 'Warsaw', 750, '2022-03-04'),
- ('Deev', 'Lisbon', 250, '2022-03-07'),
- ('Poirot', 'Berlin', 1000, '2022-03-01'),
- ('May', 'Helsinki', 1200, '2022-03-06'),
- ('Li', 'Vladivostok', 1150, '2022-03-09'),
- ('May', 'Helsinki', 1200, '2022-03-11'),
- ('Halder', 'Oslo', 800, '2022-03-02'),
- ('Muller', 'Madrid', 650, '2022-03-05'),
- ('Smith', 'Kyiv', 350, '2022-03-10'),
- ('Gandi', 'Warsaw', 150, '2022-03-08'),
- ('Plato', 'Lisbon', 950, '2022-03-05');
-
--- show partitions with conditions:
-EXECUTE get_partition_info('{sales_list}');
-
-ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_central) INTO sales_all;
-
--- show partitions with conditions:
-EXECUTE get_partition_info('{sales_list}');
-
-SELECT tableoid::regclass, * FROM sales_list ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
-
--- Use indexscan for testing indexes after merging partitions
-SET enable_seqscan = OFF;
-
-EXPLAIN (COSTS OFF) SELECT * FROM sales_all WHERE sales_state = 'Warsaw';
-SELECT * FROM sales_all WHERE sales_state = 'Warsaw';
-EXPLAIN (COSTS OFF) SELECT * FROM sales_list WHERE sales_state = 'Warsaw';
-SELECT * FROM sales_list WHERE sales_state = 'Warsaw';
-EXPLAIN (COSTS OFF) SELECT * FROM sales_list WHERE salesperson_name = 'Ivanov';
-SELECT * FROM sales_list WHERE salesperson_name = 'Ivanov';
-
-RESET enable_seqscan;
-
-DROP TABLE sales_list;
-
---
--- Try to MERGE partitions of another table.
---
-CREATE TABLE t1 (i int, a int, b int, c int) PARTITION BY RANGE (a, b);
-CREATE TABLE t1p1 PARTITION OF t1 FOR VALUES FROM (1, 1) TO (1, 2);
-CREATE TABLE t2 (i int, t text) PARTITION BY RANGE (t);
-CREATE TABLE t2pa PARTITION OF t2 FOR VALUES FROM ('A') TO ('C');
-CREATE TABLE t3 (i int, t text);
-
--- ERROR
-ALTER TABLE t2 MERGE PARTITIONS (t1p1, t2pa) INTO t2p;
--- ERROR
-ALTER TABLE t2 MERGE PARTITIONS (t2pa, t3) INTO t2p;
-
-DROP TABLE t3;
-DROP TABLE t2;
-DROP TABLE t1;
-
-
---
--- Check the partition index name if the partition name is the same as one
--- of the merged partitions.
---
-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 values should be 'tp_1_2_pkey' and 'tp_1_2_i_idx'.
-\d+ tp_1_2
-
-DROP TABLE t;
-
---
--- Try to MERGE partitions of temporary table.
---
-BEGIN;
-SHOW search_path;
-CREATE TEMP TABLE t (i int) PARTITION BY RANGE (i) ON COMMIT DROP;
-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);
-CREATE TEMP TABLE tp_2_3 PARTITION OF t FOR VALUES FROM (2) TO (3);
-CREATE TEMP TABLE tp_3_4 PARTITION OF t FOR VALUES FROM (3) TO (4);
-
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO pg_temp.tp_0_2;
-ALTER TABLE t MERGE PARTITIONS (tp_0_2, tp_2_3) INTO pg_temp.tp_0_3;
-
--- Partition should be temporary.
-EXECUTE get_partition_info('{t}');
--- ERROR
-ALTER TABLE t MERGE PARTITIONS (tp_0_3, tp_3_4) INTO tp_0_4;
-ROLLBACK;
-
---
--- Try mixing permanent and temporary partitions.
---
-BEGIN;
-SET search_path = partitions_merge_schema, pg_temp, public;
-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;
-EXECUTE get_partition_info('{t}');
-SAVEPOINT s;
-
-SET search_path = pg_temp, partitions_merge_schema, 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;
-
-ROLLBACK TO SAVEPOINT s;
-SET search_path = partitions_merge_schema, public;
--- 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;
-ROLLBACK;
-
-BEGIN;
-SET search_path = pg_temp, partitions_merge_schema, public;
-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;
-EXECUTE get_partition_info('{t}');
-
-SET search_path = partitions_merge_schema, pg_temp, public;
-
--- Can't merge temporary partitions into a persistent partition
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
-ROLLBACK;
-
-DEALLOCATE get_partition_info;
-
--- Check the new partition inherits parent's tablespace
-SET search_path = partitions_merge_schema, public;
-CREATE TABLE t (i int PRIMARY KEY USING INDEX TABLESPACE regress_tblspace)
- PARTITION BY RANGE (i) TABLESPACE regress_tblspace;
-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);
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
-SELECT tablename, tablespace FROM pg_tables
- WHERE tablename IN ('t', 'tp_0_2') AND schemaname = 'partitions_merge_schema'
- ORDER BY tablename COLLATE "C", tablespace COLLATE "C";
-SELECT tablename, indexname, tablespace FROM pg_indexes
- WHERE tablename IN ('t', 'tp_0_2') AND schemaname = 'partitions_merge_schema'
- ORDER BY tablename COLLATE "C", indexname COLLATE "C", tablespace COLLATE "C";
-DROP TABLE t;
-
--- Check the new partition inherits parent's table access method
-SET search_path = partitions_merge_schema, public;
-CREATE ACCESS METHOD partitions_merge_heap TYPE TABLE HANDLER heap_tableam_handler;
-CREATE TABLE t (i int) PARTITION BY RANGE (i) USING partitions_merge_heap;
-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);
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
-SELECT c.relname, a.amname
-FROM pg_class c JOIN pg_am a ON c.relam = a.oid
-WHERE c.oid IN ('t'::regclass, 'tp_0_2'::regclass)
-ORDER BY c.relname COLLATE "C";
-DROP TABLE t;
-DROP ACCESS METHOD partitions_merge_heap;
-
--- Test permission checks. The user needs to own the parent table and all
--- the merging partitions to do the merge.
-CREATE ROLE regress_partition_merge_alice;
-CREATE ROLE regress_partition_merge_bob;
-GRANT ALL ON SCHEMA partitions_merge_schema TO regress_partition_merge_alice;
-GRANT ALL ON SCHEMA partitions_merge_schema TO regress_partition_merge_bob;
-
-SET SESSION AUTHORIZATION regress_partition_merge_alice;
-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);
-
-SET SESSION AUTHORIZATION regress_partition_merge_bob;
--- ERROR
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
-RESET SESSION AUTHORIZATION;
-
-ALTER TABLE t OWNER TO regress_partition_merge_bob;
-SET SESSION AUTHORIZATION regress_partition_merge_bob;
--- ERROR
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
-RESET SESSION AUTHORIZATION;
-
-ALTER TABLE tp_0_1 OWNER TO regress_partition_merge_bob;
-SET SESSION AUTHORIZATION regress_partition_merge_bob;
--- ERROR
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
-RESET SESSION AUTHORIZATION;
-
-ALTER TABLE tp_1_2 OWNER TO regress_partition_merge_bob;
-SET SESSION AUTHORIZATION regress_partition_merge_bob;
--- Ok:
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
-RESET SESSION AUTHORIZATION;
-
-DROP TABLE t;
-
--- Test: we can't merge partitions with different owners
-CREATE TABLE tp_0_1(i int);
-ALTER TABLE tp_0_1 OWNER TO regress_partition_merge_alice;
-CREATE TABLE tp_1_2(i int);
-ALTER TABLE tp_1_2 OWNER TO regress_partition_merge_bob;
-
-CREATE TABLE t (i int) PARTITION BY RANGE (i);
-
-ALTER TABLE t ATTACH PARTITION tp_0_1 FOR VALUES FROM (0) TO (1);
-ALTER TABLE t ATTACH PARTITION tp_1_2 FOR VALUES FROM (1) TO (2);
-
--- Owner is 'regress_partition_merge_alice':
-\dt tp_0_1
--- Owner is 'regress_partition_merge_bob':
-\dt tp_1_2
-
--- ERROR
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
-
-DROP TABLE t;
-REVOKE ALL ON SCHEMA partitions_merge_schema FROM regress_partition_merge_alice;
-REVOKE ALL ON SCHEMA partitions_merge_schema FROM regress_partition_merge_bob;
-DROP ROLE regress_partition_merge_alice;
-DROP ROLE regress_partition_merge_bob;
-
-
--- Test for hash partitioned table
-CREATE TABLE t (i int) PARTITION BY HASH(i);
-CREATE TABLE tp1 PARTITION OF t FOR VALUES WITH (MODULUS 2, REMAINDER 0);
-CREATE TABLE tp2 PARTITION OF t FOR VALUES WITH (MODULUS 2, REMAINDER 1);
-
--- ERROR
-ALTER TABLE t MERGE PARTITIONS (tp1, tp2) INTO tp3;
-
--- ERROR
-ALTER TABLE t MERGE PARTITIONS (tp1) INTO tp3;
-
-DROP TABLE t;
-
-
--- Test for merged partition properties:
--- * STATISTICS is empty
--- * COMMENT is empty
--- * DEFAULTS are the same as DEFAULTS for partitioned table
--- * STORAGE is the same as STORAGE for partitioned table
--- * GENERATED and CONSTRAINTS are the same as GENERATED and CONSTRAINTS for partitioned table
--- * TRIGGERS are the same as TRIGGERS for partitioned table
-\set HIDE_TOAST_COMPRESSION false
-
-CREATE TABLE t
-(i int NOT NULL,
- t text STORAGE EXTENDED COMPRESSION pglz DEFAULT 'default_t',
- b bigint,
- d date GENERATED ALWAYS as ('2022-01-01') STORED) PARTITION BY RANGE (abs(i));
-COMMENT ON COLUMN t.i IS 't1.i';
-
-CREATE TABLE tp_0_1
-(i int NOT NULL,
- t text STORAGE MAIN DEFAULT 'default_tp_0_1',
- b bigint,
- d date GENERATED ALWAYS as ('2022-02-02') STORED);
-ALTER TABLE t ATTACH PARTITION tp_0_1 FOR VALUES FROM (0) TO (1);
-COMMENT ON COLUMN tp_0_1.i IS 'tp_0_1.i';
-
-CREATE TABLE tp_1_2
-(i int NOT NULL,
- t text STORAGE MAIN DEFAULT 'default_tp_1_2',
- b bigint,
- d date GENERATED ALWAYS as ('2022-03-03') STORED);
-ALTER TABLE t ATTACH PARTITION tp_1_2 FOR VALUES FROM (1) TO (2);
-COMMENT ON COLUMN tp_1_2.i IS 'tp_1_2.i';
-
-CREATE STATISTICS t_stat (DEPENDENCIES) on i, b from t;
-CREATE STATISTICS tp_0_1_stat (DEPENDENCIES) on i, b from tp_0_1;
-CREATE STATISTICS tp_1_2_stat (DEPENDENCIES) on i, b from tp_1_2;
-
-ALTER TABLE t ADD CONSTRAINT t_b_check CHECK (b > 0);
-ALTER TABLE t ADD CONSTRAINT t_b_check1 CHECK (b > 0) NOT ENFORCED;
-ALTER TABLE t ADD CONSTRAINT t_b_check2 CHECK (b > 0) NOT VALID;
-ALTER TABLE t ADD CONSTRAINT t_b_nn NOT NULL b NOT VALID;
-
-INSERT INTO tp_0_1(i, t, b) VALUES(0, DEFAULT, 1);
-INSERT INTO tp_1_2(i, t, b) VALUES(1, DEFAULT, 2);
-CREATE OR REPLACE FUNCTION trigger_function() RETURNS trigger LANGUAGE 'plpgsql' AS
-$BODY$
-BEGIN
- RAISE NOTICE 'trigger(%) called: action = %, when = %, level = %', TG_ARGV[0], TG_OP, TG_WHEN, TG_LEVEL;
- RETURN new;
-END;
-$BODY$;
-
-CREATE TRIGGER t_before_insert_row_trigger BEFORE INSERT ON t FOR EACH ROW
- EXECUTE PROCEDURE trigger_function('t');
-CREATE TRIGGER tp_0_1_before_insert_row_trigger BEFORE INSERT ON tp_0_1 FOR EACH ROW
- EXECUTE PROCEDURE trigger_function('tp_0_1');
-CREATE TRIGGER tp_1_2_before_insert_row_trigger BEFORE INSERT ON tp_1_2 FOR EACH ROW
- EXECUTE PROCEDURE trigger_function('tp_1_2');
-
-\d+ tp_0_1
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_1;
-\d+ tp_0_1
-
-INSERT INTO t(i, t, b) VALUES(1, DEFAULT, 3);
-SELECT tableoid::regclass, * FROM t ORDER BY b;
-DROP TABLE t;
-DROP FUNCTION trigger_function();
-\set HIDE_TOAST_COMPRESSION true
-
-
--- Test MERGE PARTITIONS with not valid foreign key constraint
-CREATE TABLE t (i INT PRIMARY KEY) 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);
-CREATE TABLE t_fk (i INT);
-INSERT INTO t_fk VALUES (1), (2);
-ALTER TABLE t_fk ADD CONSTRAINT t_fk_i_fkey FOREIGN KEY (i) REFERENCES t NOT VALID;
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
-
--- Should be NOT VALID FOREIGN KEY
-\d tp_0_2
--- ERROR
-ALTER TABLE t_fk VALIDATE CONSTRAINT t_fk_i_fkey;
-
-DROP TABLE t_fk;
-DROP TABLE t;
-
--- Test MERGE PARTITIONS with not enforced foreign key constraint
-CREATE TABLE t (i INT PRIMARY KEY) 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);
-CREATE TABLE t_fk (i INT);
-INSERT INTO t_fk VALUES (1), (2);
-
-ALTER TABLE t_fk ADD CONSTRAINT t_fk_i_fkey FOREIGN KEY (i) REFERENCES t NOT ENFORCED;
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
-
--- Should be NOT ENFORCED FOREIGN KEY
-\d tp_0_2
--- ERROR
-ALTER TABLE t_fk ALTER CONSTRAINT t_fk_i_fkey ENFORCED;
-
-DROP TABLE t_fk;
-DROP TABLE t;
-
-
--- Test for recomputation of stored generated columns.
-CREATE TABLE t (i int, tab_id int generated always as (tableoid) stored) 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);
-ALTER TABLE t ADD CONSTRAINT cc CHECK(tableoid <> 123456789);
-INSERT INTO t VALUES (0), (1);
-
--- Should be 0 because partition identifier for row with i=0 is different from
--- partition identifier for row with i=1.
-SELECT count(*) FROM t WHERE i = 0 AND tab_id IN (SELECT tab_id FROM t WHERE i = 1);
-
--- "tab_id" column (stored generated column) with "tableoid" attribute requires
--- recomputation here.
-ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
-
--- Should be 1 because partition identifier for row with i=0 is the same as
--- partition identifier for row with i=1.
-SELECT count(*) FROM t WHERE i = 0 AND tab_id IN (SELECT tab_id FROM t WHERE i = 1);
-
-DROP TABLE t;
-
-
--- Test for generated columns (different order of columns in partitioned table
--- and partitions).
-CREATE TABLE t (i int, g int GENERATED ALWAYS AS (i + tableoid::int)) PARTITION BY RANGE (i);
-CREATE TABLE tp_1 (g int GENERATED ALWAYS AS (i + tableoid::int), i int);
-CREATE TABLE tp_2 (g int GENERATED ALWAYS AS (i + tableoid::int), i int);
-ALTER TABLE t ATTACH PARTITION tp_1 FOR VALUES FROM (-1) TO (10);
-ALTER TABLE t ATTACH PARTITION tp_2 FOR VALUES FROM (10) TO (20);
-ALTER TABLE t ADD CHECK (g > 0);
-ALTER TABLE t ADD CHECK (i > 0);
-INSERT INTO t VALUES (5), (15);
-
-ALTER TABLE t MERGE PARTITIONS (tp_1, tp_2) INTO tp_12;
-
-INSERT INTO t VALUES (16);
--- ERROR
-INSERT INTO t VALUES (0);
--- Should be 3 rows: (5), (15), (16):
-SELECT i FROM t ORDER BY i;
--- Should be 1 because for the same tableoid (15 + tableoid) = (5 + tableoid) + 10:
-SELECT count(*) FROM t WHERE i = 15 AND g IN (SELECT g + 10 FROM t WHERE i = 5);
-
-DROP TABLE t;
-
--- A merged partition needs its own TOAST table; otherwise an out-of-line
--- varlena value carried over from one of the merging partitions has
--- nowhere to be stored. SET STORAGE EXTERNAL forces externalization
--- for any value over the TOAST threshold, so a string over that threshold
--- suffices to exercise the toast-table dependency.
-CREATE TABLE t (a text) PARTITION BY RANGE(a);
-ALTER TABLE t ALTER COLUMN a SET STORAGE EXTERNAL;
-CREATE TABLE tp_def PARTITION OF t DEFAULT;
-CREATE TABLE tp_2_3 PARTITION OF t FOR VALUES FROM ('2') TO ('3');
-INSERT INTO t SELECT repeat('1', 10000);
-ALTER TABLE t MERGE PARTITIONS (tp_def, tp_2_3) INTO tp_merged;
-SELECT reltoastrelid <> 0 AS has_toast,
- pg_relation_size(reltoastrelid) > 0 AS toast_used
- FROM pg_class WHERE relname = 'tp_merged';
-SELECT length(a) FROM t;
-DROP TABLE t;
-
--- Tablespace selection for the new merged partition mirrors
--- CREATE TABLE ... PARTITION OF: the partitioned root's explicit
--- tablespace wins; otherwise default_tablespace applies; otherwise the
--- database default is used.
-CREATE TABLE t (i int) PARTITION BY RANGE(i) TABLESPACE regress_tblspace;
-CREATE TABLE tp_0_5 PARTITION OF t FOR VALUES FROM (0) TO (5);
-CREATE TABLE tp_5_10 PARTITION OF t FOR VALUES FROM (5) TO (10);
-INSERT INTO t SELECT generate_series(0, 9);
-ALTER TABLE t MERGE PARTITIONS (tp_0_5, tp_5_10) INTO tp_merged;
-SELECT spcname FROM pg_class c LEFT JOIN pg_tablespace s
- ON c.reltablespace = s.oid WHERE c.relname = 'tp_merged';
-DROP TABLE t;
-
--- Parent has no explicit tablespace, but default_tablespace is set: the
--- new partition lands on default_tablespace.
-CREATE TABLE t (i int) PARTITION BY RANGE(i);
-CREATE TABLE tp_0_5 PARTITION OF t FOR VALUES FROM (0) TO (5);
-CREATE TABLE tp_5_10 PARTITION OF t FOR VALUES FROM (5) TO (10);
-INSERT INTO t SELECT generate_series(0, 9);
-SET default_tablespace TO regress_tblspace;
-ALTER TABLE t MERGE PARTITIONS (tp_0_5, tp_5_10) INTO tp_merged;
-RESET default_tablespace;
-SELECT spcname FROM pg_class c LEFT JOIN pg_tablespace s
- ON c.reltablespace = s.oid WHERE c.relname = 'tp_merged';
-DROP TABLE t;
-
-CREATE TABLE t (i int) PARTITION BY RANGE(i);
-CREATE TABLE tp_0_5 PARTITION OF t FOR VALUES FROM (0) TO (5);
-CREATE TABLE tp_5_10 PARTITION OF t FOR VALUES FROM (5) TO (10);
-INSERT INTO t SELECT generate_series(0, 9);
--- pg_global is rejected when picked up from default_tablespace.
-SET default_tablespace TO pg_global;
-ALTER TABLE t MERGE PARTITIONS (tp_0_5, tp_5_10) INTO tp_merged; -- fails
-RESET default_tablespace;
--- Parent has no explicit tablespace and default_tablespace is empty: the
--- new partition uses the database default (reltablespace = 0).
-ALTER TABLE t MERGE PARTITIONS (tp_0_5, tp_5_10) INTO tp_merged;
-SELECT reltablespace FROM pg_class WHERE relname = 'tp_merged';
-DROP TABLE t;
-
-
-RESET search_path;
-
---
-DROP SCHEMA partitions_merge_schema;
-DROP SCHEMA partitions_merge_schema2;
diff --git a/src/test/regress/sql/partition_split.sql b/src/test/regress/sql/partition_split.sql
deleted file mode 100644
index ffd15e7f969..00000000000
--- a/src/test/regress/sql/partition_split.sql
+++ /dev/null
@@ -1,1263 +0,0 @@
---
--- PARTITION_SPLIT
--- Tests for "ALTER TABLE ... SPLIT PARTITION ..." command
---
-
-CREATE SCHEMA partition_split_schema;
-CREATE SCHEMA partition_split_schema2;
-SET search_path = partition_split_schema, public;
-
---
--- BY RANGE partitioning
---
-
---
--- Test for error codes
---
-CREATE TABLE sales_range (salesperson_id 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');
-CREATE TABLE sales_feb_mar_apr2022 PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-05-01');
-CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
-
--- ERROR
-ALTER TABLE sales_range SPLIT PARTITION sales_xxx INTO
- (PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- 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
-ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
- (PARTITION sales_jan2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- 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
-ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
- (PARTITION sales_jan2022 FOR VALUES IN ('2022-05-01', '2022-06-01'),
- 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
-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-03-01') TO ('2022-02-01'),
- PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01'));
-
--- ERROR
-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
-ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
- (PARTITION sales_feb2022 FOR VALUES FROM ('2022-01-01') TO ('2022-03-01'),
- 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
--- (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
-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
-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 partition_split_schema.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
-ALTER TABLE sales_feb_mar_apr2022 SPLIT PARTITION sales_feb_mar_apr2022 INTO
- (PARTITION sales_jan2022 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
-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-03-01') TO ('2022-04-01'),
- PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-06-01'));
-
--- ERROR
-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'),
- PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01'));
-
--- Tests for spaces between partitions, them should be executed without DEFAULT partition
-ALTER TABLE sales_range DETACH PARTITION sales_others;
-
--- ERROR
-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'),
- PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01'));
-
--- Check the source partition not in the search path
-SET search_path = partition_split_schema2, public;
-ALTER TABLE partition_split_schema.sales_range
-SPLIT PARTITION partition_split_schema.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-03-01') TO ('2022-04-01'),
- PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01'));
-SET search_path = partition_split_schema, public;
-\d+ sales_range
-
-DROP TABLE sales_range;
-DROP TABLE sales_others;
-
--- Additional tests for error messages, no default partition
-CREATE TABLE sales_range (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');
-CREATE TABLE sales_feb_mar_apr2022 PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-05-01');
-
--- ERROR
-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-03-01') TO ('2022-04-01'),
- PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-06-01'));
-
-DROP TABLE sales_range;
-
---
--- Add rows into partitioned table then split partition
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_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');
-CREATE TABLE sales_feb_mar_apr2022 PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-05-01');
-CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
-
-INSERT INTO sales_range VALUES
- (1, 'May', 1000, '2022-01-31'),
- (2, 'Smirnoff', 500, '2022-02-10'),
- (3, 'Ford', 2000, '2022-04-30'),
- (4, 'Ivanov', 750, '2022-04-13'),
- (5, 'Deev', 250, '2022-04-07'),
- (6, 'Poirot', 150, '2022-02-11'),
- (7, 'Li', 175, '2022-03-08'),
- (8, 'Ericsson', 185, '2022-02-23'),
- (9, 'Muller', 250, '2022-03-11'),
- (10, 'Halder', 350, '2022-01-28'),
- (11, 'Trump', 380, '2022-04-06'),
- (12, 'Plato', 350, '2022-03-19'),
- (13, 'Gandi', 377, '2022-01-09'),
- (14, 'Smith', 510, '2022-05-04');
-
-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-03-01') TO ('2022-04-01'),
- PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01'));
-
-SELECT tableoid::regclass, * FROM sales_range ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
-DROP TABLE sales_range CASCADE;
-
---
--- Add split partition, then add rows into partitioned table
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_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');
-CREATE TABLE sales_feb_mar_apr2022 PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-05-01');
-CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
-
-INSERT INTO sales_range VALUES
- (1, 'May', 1000, '2022-01-31'),
- (2, 'Smirnoff', 500, '2022-02-10'),
- (3, 'Ford', 2000, '2022-04-30'),
- (4, 'Ivanov', 750, '2022-04-13'),
- (5, 'Deev', 250, '2022-04-07'),
- (6, 'Poirot', 150, '2022-02-11'),
- (7, 'Li', 175, '2022-03-08'),
- (8, 'Ericsson', 185, '2022-02-23'),
- (9, 'Muller', 250, '2022-03-11'),
- (10, 'Halder', 350, '2022-01-28'),
- (11, 'Trump', 380, '2022-04-06'),
- (12, 'Plato', 350, '2022-03-19'),
- (13, 'Gandi', 377, '2022-01-09'),
- (14, 'Smith', 510, '2022-05-04');
-
--- Split partition, also check schema qualification of new partitions
-ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2022 INTO
- (PARTITION partition_split_schema.sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- PARTITION partition_split_schema2.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'));
-\d+ sales_range
-
-SELECT tableoid::regclass, * FROM sales_range ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
-
-DROP TABLE sales_range CASCADE;
-
---
--- Test for:
--- * composite partition key;
--- * GENERATED column;
--- * column with DEFAULT value.
---
-CREATE TABLE sales_date (salesperson_name VARCHAR(30), sales_year INT, sales_month INT, sales_day INT,
- sales_date VARCHAR(10) GENERATED ALWAYS AS
- (LPAD(sales_year::text, 4, '0') || '.' || LPAD(sales_month::text, 2, '0') || '.' || LPAD(sales_day::text, 2, '0')) STORED,
- sales_department VARCHAR(30) DEFAULT 'Sales department')
- PARTITION BY RANGE (sales_year, sales_month, sales_day);
-
-CREATE TABLE sales_dec2021 PARTITION OF sales_date FOR VALUES FROM (2021, 12, 1) TO (2022, 1, 1);
-CREATE TABLE sales_jan_feb2022 PARTITION OF sales_date FOR VALUES FROM (2022, 1, 1) TO (2022, 3, 1);
-CREATE TABLE sales_other PARTITION OF sales_date FOR VALUES FROM (2022, 3, 1) TO (MAXVALUE, MAXVALUE, MAXVALUE);
-
-INSERT INTO sales_date(salesperson_name, sales_year, sales_month, sales_day) VALUES
- ('Manager1', 2021, 12, 7),
- ('Manager2', 2021, 12, 8),
- ('Manager3', 2022, 1, 1),
- ('Manager1', 2022, 2, 4),
- ('Manager2', 2022, 1, 2),
- ('Manager3', 2022, 2, 1),
- ('Manager1', 2022, 3, 3),
- ('Manager2', 2022, 3, 4),
- ('Manager3', 2022, 5, 1);
-
-SELECT tableoid::regclass, * FROM sales_date ORDER BY tableoid::regclass::text COLLATE "C", sales_year, sales_month, sales_day;
-
-ALTER TABLE sales_date SPLIT PARTITION sales_jan_feb2022 INTO
- (PARTITION sales_jan2022 FOR VALUES FROM (2022, 1, 1) TO (2022, 2, 1),
- PARTITION sales_feb2022 FOR VALUES FROM (2022, 2, 1) TO (2022, 3, 1));
-
-INSERT INTO sales_date(salesperson_name, sales_year, sales_month, sales_day) VALUES
- ('Manager1', 2022, 1, 10),
- ('Manager2', 2022, 2, 10);
-
-SELECT tableoid::regclass, * FROM sales_date ORDER BY tableoid::regclass::text COLLATE "C", sales_year, sales_month, sales_day;
-
-DROP TABLE sales_date CASCADE;
-
---
--- Test: split DEFAULT partition; use an index on partition key; check index after split
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_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');
-CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
-CREATE INDEX sales_range_sales_date_idx ON sales_range USING btree (sales_date);
-
-INSERT INTO sales_range VALUES
- (1, 'May', 1000, '2022-01-31'),
- (2, 'Smirnoff', 500, '2022-02-10'),
- (3, 'Ford', 2000, '2022-04-30'),
- (4, 'Ivanov', 750, '2022-04-13'),
- (5, 'Deev', 250, '2022-04-07'),
- (6, 'Poirot', 150, '2022-02-11'),
- (7, 'Li', 175, '2022-03-08'),
- (8, 'Ericsson', 185, '2022-02-23'),
- (9, 'Muller', 250, '2022-03-11'),
- (10, 'Halder', 350, '2022-01-28'),
- (11, 'Trump', 380, '2022-04-06'),
- (12, 'Plato', 350, '2022-03-19'),
- (13, 'Gandi', 377, '2022-01-09'),
- (14, 'Smith', 510, '2022-05-04');
-
-SELECT * FROM sales_others;
-SELECT * FROM pg_indexes WHERE tablename = 'sales_others' and schemaname = 'partition_split_schema' ORDER BY indexname COLLATE "C";
-
-ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
- (PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- 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 testing indexes
-SET enable_seqscan = OFF;
-
-EXPLAIN (COSTS OFF) SELECT * FROM sales_feb2022 where sales_date > '2022-01-01';
-SELECT * FROM sales_feb2022 where sales_date > '2022-01-01';
-EXPLAIN (COSTS OFF) SELECT * FROM sales_mar2022 where sales_date > '2022-01-01';
-SELECT * FROM sales_mar2022 where sales_date > '2022-01-01';
-EXPLAIN (COSTS OFF) SELECT * FROM sales_apr2022 where sales_date > '2022-01-01';
-SELECT * FROM sales_apr2022 where sales_date > '2022-01-01';
-EXPLAIN (COSTS OFF) SELECT * FROM sales_others where sales_date > '2022-01-01';
-SELECT * FROM sales_others where sales_date > '2022-01-01';
-
-RESET enable_seqscan;
-
-SELECT * FROM pg_indexes
-WHERE tablename in ('sales_feb2022', 'sales_mar2022', 'sales_apr2022', 'sales_others')
-AND schemaname = 'partition_split_schema'
-ORDER BY indexname COLLATE "C";
-
-DROP TABLE sales_range CASCADE;
-
---
--- Test: some cases for splitting DEFAULT partition (different bounds)
---
-CREATE TABLE sales_range (salesperson_id INT, sales_date date) PARTITION BY RANGE (sales_date);
-CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
-
--- sales_error intersects with sales_dec2021 (lower bound)
--- ERROR
-ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
- (PARTITION sales_dec2021 FOR VALUES FROM ('2021-12-01') TO ('2022-01-01'),
- PARTITION sales_error FOR VALUES FROM ('2021-12-30') TO ('2022-02-01'),
- PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- PARTITION sales_others DEFAULT);
-
--- sales_error intersects with sales_feb2022 (upper bound)
--- ERROR
-ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
- (PARTITION sales_dec2021 FOR VALUES FROM ('2021-12-01') TO ('2022-01-01'),
- PARTITION sales_error FOR VALUES FROM ('2022-01-01') TO ('2022-02-02'),
- PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- PARTITION sales_others DEFAULT);
-
--- sales_error intersects with sales_dec2021 (inside bound)
--- ERROR
-ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
- (PARTITION sales_dec2021 FOR VALUES FROM ('2021-12-01') TO ('2022-01-01'),
- PARTITION sales_error FOR VALUES FROM ('2021-12-10') TO ('2021-12-20'),
- PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- PARTITION sales_others DEFAULT);
-
--- sales_error intersects with sales_dec2021 (exactly the same bounds)
--- ERROR
-ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
- (PARTITION sales_dec2021 FOR VALUES FROM ('2021-12-01') TO ('2022-01-01'),
- PARTITION sales_error FOR VALUES FROM ('2021-12-01') TO ('2022-01-01'),
- PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- PARTITION sales_others DEFAULT);
-
--- ERROR
-ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
- (PARTITION sales_dec2021 FOR VALUES FROM ('2021-12-01') TO ('2022-01-01'),
- PARTITION sales_jan2022 FOR VALUES FROM ('2022-01-01') TO ('2022-02-01'),
- PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'));
-
--- no error: bounds of sales_noerror are between sales_dec2021 and sales_feb2022
-ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
- (PARTITION sales_dec2021 FOR VALUES FROM ('2021-12-01') TO ('2022-01-01'),
- PARTITION sales_noerror FOR VALUES FROM ('2022-01-10') TO ('2022-01-20'),
- PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- PARTITION sales_others DEFAULT);
-
-DROP TABLE sales_range;
-
-CREATE TABLE sales_range (sales_date date) PARTITION BY RANGE (sales_date);
-CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
-
--- no error: bounds of sales_noerror are equal to lower and upper bounds of sales_dec2021 and sales_feb2022
-ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
- (PARTITION sales_dec2021 FOR VALUES FROM ('2021-12-01') TO ('2022-01-01'),
- PARTITION sales_noerror FOR VALUES FROM ('2022-01-01') TO ('2022-02-01'),
- PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- PARTITION sales_others DEFAULT);
-
-DROP TABLE sales_range;
-
---
--- Test: split partition with CHECK and FOREIGN KEY CONSTRAINTs on partitioned table
---
-CREATE TABLE salespeople(salesperson_id INT PRIMARY KEY, salesperson_name VARCHAR(30));
-INSERT INTO salespeople VALUES (1, 'Poirot');
-
-CREATE TABLE sales_range (
-salesperson_id INT REFERENCES salespeople(salesperson_id),
-sales_amount INT CHECK (sales_amount > 1),
-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');
-CREATE TABLE sales_feb_mar_apr2022 PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-05-01');
-CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
-
-SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conrelid = 'sales_feb_mar_apr2022'::regclass::oid ORDER BY conname COLLATE "C";
-
-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-03-01') TO ('2022-04-01'),
- PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01'));
-
--- We should see the same CONSTRAINTs as on sales_feb_mar_apr2022 partition
-SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conrelid = 'sales_feb2022'::regclass::oid ORDER BY conname COLLATE "C";
-SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conrelid = 'sales_mar2022'::regclass::oid ORDER BY conname COLLATE "C";
-SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conrelid = 'sales_apr2022'::regclass::oid ORDER BY conname COLLATE "C";
-
--- ERROR
-INSERT INTO sales_range VALUES (1, 0, '2022-03-11');
--- ERROR
-INSERT INTO sales_range VALUES (-1, 10, '2022-03-11');
--- ok
-INSERT INTO sales_range VALUES (1, 10, '2022-03-11');
-
-DROP TABLE sales_range CASCADE;
-DROP TABLE salespeople CASCADE;
-
---
--- Test: split partition on partitioned table in case of existing FOREIGN KEY reference from another table
---
-CREATE TABLE salespeople(salesperson_id INT PRIMARY KEY, salesperson_name VARCHAR(30)) PARTITION BY RANGE (salesperson_id);
-CREATE TABLE sales (salesperson_id INT REFERENCES salespeople(salesperson_id), sales_amount INT, sales_date DATE);
-
-CREATE TABLE salespeople01_10 PARTITION OF salespeople FOR VALUES FROM (1) TO (10);
-CREATE TABLE salespeople10_40 PARTITION OF salespeople FOR VALUES FROM (10) TO (40);
-
-INSERT INTO salespeople VALUES
- (1, 'Poirot'),
- (10, 'May'),
- (19, 'Ivanov'),
- (20, 'Smirnoff'),
- (30, 'Ford');
-
-INSERT INTO sales VALUES
- (1, 100, '2022-03-01'),
- (1, 110, '2022-03-02'),
- (10, 150, '2022-03-01'),
- (10, 90, '2022-03-03'),
- (19, 200, '2022-03-04'),
- (20, 50, '2022-03-12'),
- (20, 170, '2022-03-02'),
- (30, 30, '2022-03-04');
-
-SELECT tableoid::regclass, * FROM salespeople ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
-
-ALTER TABLE salespeople SPLIT PARTITION salespeople10_40 INTO
- (PARTITION salespeople10_20 FOR VALUES FROM (10) TO (20),
- PARTITION salespeople20_30 FOR VALUES FROM (20) TO (30),
- PARTITION salespeople30_40 FOR VALUES FROM (30) TO (40));
-
-SELECT tableoid::regclass, * FROM salespeople ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
-
--- ERROR
-INSERT INTO sales VALUES (40, 50, '2022-03-04');
--- ok
-INSERT INTO sales VALUES (30, 50, '2022-03-04');
-
-DROP TABLE sales CASCADE;
-DROP TABLE salespeople CASCADE;
-
---
--- Test: split partition of partitioned table with triggers
---
-CREATE TABLE salespeople(salesperson_id INT PRIMARY KEY, salesperson_name VARCHAR(30)) PARTITION BY RANGE (salesperson_id);
-
-CREATE TABLE salespeople01_10 PARTITION OF salespeople FOR VALUES FROM (1) TO (10);
-CREATE TABLE salespeople10_40 PARTITION OF salespeople FOR VALUES FROM (10) TO (40);
-
-INSERT INTO salespeople VALUES (1, 'Poirot');
-
-CREATE OR REPLACE FUNCTION after_insert_row_trigger() RETURNS trigger LANGUAGE 'plpgsql' AS $BODY$
-BEGIN
- RAISE NOTICE 'trigger(%) called: action = %, when = %, level = %', TG_ARGV[0], TG_OP, TG_WHEN, TG_LEVEL;
- RETURN NULL;
-END;
-$BODY$;
-
-CREATE TRIGGER salespeople_after_insert_statement_trigger
- AFTER INSERT
- ON salespeople
- FOR EACH STATEMENT
- EXECUTE PROCEDURE after_insert_row_trigger('salespeople');
-
-CREATE TRIGGER salespeople_after_insert_row_trigger
- AFTER INSERT
- ON salespeople
- FOR EACH ROW
- EXECUTE PROCEDURE after_insert_row_trigger('salespeople');
-
--- 2 triggers should fire here (row + statement):
-INSERT INTO salespeople VALUES (10, 'May');
--- 1 trigger should fire here (row):
-INSERT INTO salespeople10_40 VALUES (19, 'Ivanov');
-
-ALTER TABLE salespeople SPLIT PARTITION salespeople10_40 INTO
- (PARTITION salespeople10_20 FOR VALUES FROM (10) TO (20),
- PARTITION salespeople20_30 FOR VALUES FROM (20) TO (30),
- PARTITION salespeople30_40 FOR VALUES FROM (30) TO (40));
-
--- 2 triggers should fire here (row + statement):
-INSERT INTO salespeople VALUES (20, 'Smirnoff');
--- 1 trigger should fire here (row):
-INSERT INTO salespeople30_40 VALUES (30, 'Ford');
-
-SELECT tableoid::regclass, * FROM salespeople ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
-
-DROP TABLE salespeople 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.
---
-CREATE TABLE salespeople(salesperson_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, salesperson_name VARCHAR(30)) PARTITION BY RANGE (salesperson_id);
-
-CREATE TABLE salespeople1_2 PARTITION OF salespeople FOR VALUES FROM (1) TO (2);
--- Create new partition with identity column:
-CREATE TABLE salespeople2_5(salesperson_id INT NOT NULL, salesperson_name VARCHAR(30));
-ALTER TABLE salespeople ATTACH PARTITION salespeople2_5 FOR VALUES FROM (2) TO (5);
-
-INSERT INTO salespeople (salesperson_name) VALUES ('Poirot'), ('Ivanov');
-
-ALTER TABLE salespeople SPLIT PARTITION salespeople2_5 INTO
- (PARTITION salespeople2_3 FOR VALUES FROM (2) TO (3),
- PARTITION salespeople3_4 FOR VALUES FROM (3) TO (4),
- PARTITION salespeople4_5 FOR VALUES FROM (4) TO (5));
-
-INSERT INTO salespeople (salesperson_name) VALUES ('May'), ('Ford');
-
-SELECT tableoid::regclass, * FROM salespeople ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
-
--- check new partitions have identity or not after split partition
-SELECT attrelid::regclass, attname, attidentity, attgenerated FROM pg_attribute
-WHERE attnum > 0
-AND attrelid::regclass IN (
- 'salespeople2_3'::regclass, 'salespeople', 'salespeople2_3',
- 'salespeople1_2', 'salespeople3_4', 'salespeople4_5')
-ORDER BY attrelid::regclass::text COLLATE "C", attnum;
-
-DROP TABLE salespeople CASCADE;
-
---
--- Test: split partition with deleted columns
---
-CREATE TABLE salespeople(salesperson_id INT PRIMARY KEY, salesperson_name VARCHAR(30)) PARTITION BY RANGE (salesperson_id);
-
-CREATE TABLE salespeople01_10 PARTITION OF salespeople FOR VALUES FROM (1) TO (10);
--- Create new partition with some deleted columns:
-CREATE TABLE salespeople10_40(d1 VARCHAR(30), salesperson_id INT PRIMARY KEY, d2 INT, d3 DATE, salesperson_name VARCHAR(30));
-
-INSERT INTO salespeople10_40 VALUES
- ('dummy value 1', 19, 100, now(), 'Ivanov'),
- ('dummy value 2', 20, 101, now(), 'Smirnoff');
-
-ALTER TABLE salespeople10_40 DROP COLUMN d1;
-ALTER TABLE salespeople10_40 DROP COLUMN d2;
-ALTER TABLE salespeople10_40 DROP COLUMN d3;
-
-ALTER TABLE salespeople ATTACH PARTITION salespeople10_40 FOR VALUES FROM (10) TO (40);
-
-INSERT INTO salespeople VALUES
- (1, 'Poirot'),
- (10, 'May'),
- (30, 'Ford');
-
-ALTER TABLE salespeople SPLIT PARTITION salespeople10_40 INTO
- (PARTITION salespeople10_20 FOR VALUES FROM (10) TO (20),
- PARTITION salespeople20_30 FOR VALUES FROM (20) TO (30),
- PARTITION salespeople30_40 FOR VALUES FROM (30) TO (40));
-
-SELECT tableoid::regclass, * FROM salespeople ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
-
-DROP TABLE salespeople CASCADE;
-
---
--- Test: split sub-partition
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_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');
-CREATE TABLE sales_feb2022 PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-03-01');
-CREATE TABLE sales_mar2022 PARTITION OF sales_range FOR VALUES FROM ('2022-03-01') TO ('2022-04-01');
-
-CREATE TABLE sales_apr2022 (salesperson_id INT, salesperson_name VARCHAR(30), sales_amount INT, sales_date DATE) PARTITION BY RANGE (sales_date);
-CREATE TABLE sales_apr_all PARTITION OF sales_apr2022 FOR VALUES FROM ('2022-04-01') 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;
-
-CREATE INDEX sales_range_sales_date_idx ON sales_range USING btree (sales_date);
-
-INSERT INTO sales_range VALUES
- (1, 'May', 1000, '2022-01-31'),
- (2, 'Smirnoff', 500, '2022-02-10'),
- (3, 'Ford', 2000, '2022-04-30'),
- (4, 'Ivanov', 750, '2022-04-13'),
- (5, 'Deev', 250, '2022-04-07'),
- (6, 'Poirot', 150, '2022-02-11'),
- (7, 'Li', 175, '2022-03-08'),
- (8, 'Ericsson', 185, '2022-02-23'),
- (9, 'Muller', 250, '2022-03-11'),
- (10, 'Halder', 350, '2022-01-28'),
- (11, 'Trump', 380, '2022-04-06'),
- (12, 'Plato', 350, '2022-03-19'),
- (13, 'Gandi', 377, '2022-01-09'),
- (14, 'Smith', 510, '2022-05-04');
-
-SELECT tableoid::regclass, * FROM sales_range ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
-
-ALTER TABLE sales_apr2022 SPLIT PARTITION sales_apr_all INTO
- (PARTITION sales_apr2022_01_10 FOR VALUES FROM ('2022-04-01') TO ('2022-04-10'),
- PARTITION sales_apr2022_10_20 FOR VALUES FROM ('2022-04-10') TO ('2022-04-20'),
- PARTITION sales_apr2022_20_30 FOR VALUES FROM ('2022-04-20') TO ('2022-05-01'));
-
-SELECT tableoid::regclass, * FROM sales_range ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
-
-DROP TABLE sales_range;
-
---
--- BY LIST partitioning
---
-
---
--- Test: specific errors for BY LIST partitioning
---
-CREATE TABLE sales_list (sales_state VARCHAR(20)) 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', 'Beijing', 'Berlin', 'Delhi', 'Kyiv', 'Vladivostok');
-CREATE TABLE sales_others PARTITION OF sales_list DEFAULT;
-
--- ERROR
-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 ('Beijing', 'Delhi', 'Vladivostok', 'Helsinki'),
- PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv'));
-
--- ERROR
-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 ('Beijing', 'Delhi', 'Vladivostok'),
- PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Lisbon', 'Kyiv'));
-
--- ERROR
-ALTER TABLE sales_list SPLIT PARTITION sales_all INTO
- (PARTITION sales_west FOR VALUES IN ('Lisbon', 'New York', 'Madrid', NULL),
- PARTITION sales_east FOR VALUES IN ('Beijing', 'Delhi', 'Vladivostok'),
- PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv'));
-
--- ERROR
-ALTER TABLE sales_list SPLIT PARTITION sales_all INTO
- (PARTITION sales_west FOR VALUES IN ('Lisbon', 'New York', 'Madrid', 'Melbourne'),
- PARTITION sales_east FOR VALUES IN ('Beijing', 'Delhi', 'Vladivostok'),
- PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv'));
-
--- ERROR
-ALTER TABLE sales_list SPLIT PARTITION sales_all INTO
- (PARTITION sales_west FOR VALUES IN ('Lisbon', 'New York', 'Madrid', 'Melbourne'),
- PARTITION sales_east FOR VALUES IN ('Beijing', 'Delhi', 'Vladivostok'),
- PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv'),
- PARTITION sales_others2 DEFAULT);
-
-DROP TABLE sales_list;
-
--- Test for non-symbolic comparison of values (numeric values '0' and '0.0' are equal).
-CREATE TABLE t (a numeric) PARTITION BY LIST (a);
-CREATE TABLE t1 PARTITION OF t FOR VALUES in ('0', '1');
--- ERROR
-ALTER TABLE t SPLIT PARTITION t1 INTO
- (PARTITION x FOR VALUES IN ('0'),
- PARTITION x1 FOR VALUES IN ('0.0', '1'));
-DROP TABLE t;
-
---
--- Test: two specific errors for BY LIST partitioning:
--- * 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(sales_state VARCHAR(20)) 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', 'Beijing', 'Berlin', 'Delhi', 'Kyiv', 'Vladivostok', NULL);
-
--- ERROR
-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 ('Beijing', 'Delhi', 'Vladivostok'),
- PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv'));
-
--- ERROR
-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 ('Beijing', 'Delhi', 'Vladivostok'),
- PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', NULL));
-
--- ERROR
-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 ('Beijing', 'Delhi', 'Vladivostok'),
- PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv'),
- PARTITION sales_others DEFAULT,
- PARTITION sales_others2 DEFAULT);
-
-DROP TABLE sales_list;
-
---
--- Test: BY LIST partitioning, SPLIT PARTITION with data
---
-CREATE TABLE sales_list
-(salesperson_id SERIAL,
- salesperson_name VARCHAR(30),
- sales_state VARCHAR(20),
- sales_amount INT,
- sales_date DATE)
-PARTITION BY LIST (sales_state);
-
-CREATE INDEX sales_list_salesperson_name_idx ON sales_list USING btree (salesperson_name);
-CREATE INDEX sales_list_sales_state_idx ON sales_list USING btree (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', 'Beijing', 'Berlin', 'Delhi', 'Kyiv', 'Vladivostok');
-CREATE TABLE sales_others PARTITION OF sales_list DEFAULT;
-
-INSERT INTO sales_list (salesperson_name, sales_state, sales_amount, sales_date) VALUES
- ('Trump', 'Beijing', 1000, '2022-03-01'),
- ('Smirnoff', 'New York', 500, '2022-03-03'),
- ('Ford', 'St. Petersburg', 2000, '2022-03-05'),
- ('Ivanov', 'Warsaw', 750, '2022-03-04'),
- ('Deev', 'Lisbon', 250, '2022-03-07'),
- ('Poirot', 'Berlin', 1000, '2022-03-01'),
- ('May', 'Oslo', 1200, '2022-03-06'),
- ('Li', 'Vladivostok', 1150, '2022-03-09'),
- ('May', 'Oslo', 1200, '2022-03-11'),
- ('Halder', 'Helsinki', 800, '2022-03-02'),
- ('Muller', 'Madrid', 650, '2022-03-05'),
- ('Smith', 'Kyiv', 350, '2022-03-10'),
- ('Gandi', 'Warsaw', 150, '2022-03-08'),
- ('Plato', 'Lisbon', 950, '2022-03-05');
-
-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 ('Beijing', 'Delhi', 'Vladivostok'),
- PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv'));
-
-SELECT tableoid::regclass, * FROM sales_list ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
-
--- Use indexscan for testing indexes after splitting partition
-SET enable_seqscan = OFF;
-
-EXPLAIN (COSTS OFF) SELECT * FROM sales_central WHERE sales_state = 'Warsaw';
-SELECT * FROM sales_central WHERE sales_state = 'Warsaw';
-EXPLAIN (COSTS OFF) SELECT * FROM sales_list WHERE sales_state = 'Warsaw';
-SELECT * FROM sales_list WHERE sales_state = 'Warsaw';
-EXPLAIN (COSTS OFF) SELECT * FROM sales_list WHERE salesperson_name = 'Ivanov';
-SELECT * FROM sales_list WHERE salesperson_name = 'Ivanov';
-
-RESET enable_seqscan;
-
-DROP TABLE sales_list;
-
---
--- Test for:
--- * split DEFAULT partition to partitions with spaces between bounds;
--- * random order of partitions in SPLIT PARTITION command.
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_name VARCHAR(30), sales_amount INT, sales_date DATE) PARTITION BY RANGE (sales_date);
-CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
-
-INSERT INTO sales_range VALUES
- (1, 'May', 1000, '2022-01-31'),
- (2, 'Smirnoff', 500, '2022-02-09'),
- (3, 'Ford', 2000, '2022-04-30'),
- (4, 'Ivanov', 750, '2022-04-13'),
- (5, 'Deev', 250, '2022-04-07'),
- (6, 'Poirot', 150, '2022-02-07'),
- (7, 'Li', 175, '2022-03-08'),
- (8, 'Ericsson', 185, '2022-02-23'),
- (9, 'Muller', 250, '2022-03-11'),
- (10, 'Halder', 350, '2022-01-28'),
- (11, 'Trump', 380, '2022-04-06'),
- (12, 'Plato', 350, '2022-03-19'),
- (13, 'Gandi', 377, '2022-01-09'),
- (14, 'Smith', 510, '2022-05-04');
-
-ALTER TABLE sales_range SPLIT PARTITION sales_others INTO
- (PARTITION sales_others DEFAULT,
- PARTITION sales_mar2022_1decade FOR VALUES FROM ('2022-03-01') TO ('2022-03-10'),
- PARTITION sales_jan2022_1decade FOR VALUES FROM ('2022-01-01') TO ('2022-01-10'),
- PARTITION sales_feb2022_1decade FOR VALUES FROM ('2022-02-01') TO ('2022-02-10'),
- PARTITION sales_apr2022_1decade FOR VALUES FROM ('2022-04-01') TO ('2022-04-10'));
-
-SELECT tableoid::regclass, * FROM sales_range ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
-
-DROP TABLE sales_range;
-
---
--- Test for:
--- * split non-DEFAULT partition to partitions with spaces between bounds;
--- * random order of partitions in SPLIT PARTITION command.
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_name VARCHAR(30), sales_amount INT, sales_date DATE) PARTITION BY RANGE (sales_date);
-CREATE TABLE sales_all PARTITION OF sales_range FOR VALUES FROM ('2022-01-01') TO ('2022-05-01');
-
-INSERT INTO sales_range VALUES
- (1, 'May', 1000, '2022-01-31'),
- (2, 'Smirnoff', 500, '2022-02-09'),
- (3, 'Ford', 2000, '2022-04-30'),
- (4, 'Ivanov', 750, '2022-04-13'),
- (5, 'Deev', 250, '2022-04-07'),
- (6, 'Poirot', 150, '2022-02-07'),
- (7, 'Li', 175, '2022-03-08'),
- (8, 'Ericsson', 185, '2022-02-23'),
- (9, 'Muller', 250, '2022-03-11'),
- (10, 'Halder', 350, '2022-01-28'),
- (11, 'Trump', 380, '2022-04-06'),
- (12, 'Plato', 350, '2022-03-19'),
- (13, 'Gandi', 377, '2022-01-09');
-
-ALTER TABLE sales_range SPLIT PARTITION sales_all INTO
- (PARTITION sales_mar2022_1decade FOR VALUES FROM ('2022-03-01') TO ('2022-03-10'),
- PARTITION sales_jan2022_1decade FOR VALUES FROM ('2022-01-01') TO ('2022-01-10'),
- PARTITION sales_feb2022_1decade FOR VALUES FROM ('2022-02-01') TO ('2022-02-10'),
- PARTITION sales_apr2022_1decade FOR VALUES FROM ('2022-04-01') TO ('2022-04-10'),
- PARTITION sales_others DEFAULT);
-
-SELECT tableoid::regclass, * FROM sales_range ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
-
-DROP TABLE sales_range;
-
---
--- Test for split non-DEFAULT partition to DEFAULT partition + partitions
--- with spaces between bounds.
---
-CREATE TABLE sales_range (salesperson_id INT, salesperson_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');
-CREATE TABLE sales_all PARTITION OF sales_range FOR VALUES FROM ('2022-02-01') TO ('2022-05-01');
-
-INSERT INTO sales_range VALUES
- (1, 'May', 1000, '2022-01-31'),
- (2, 'Smirnoff', 500, '2022-02-10'),
- (3, 'Ford', 2000, '2022-04-30'),
- (4, 'Ivanov', 750, '2022-04-13'),
- (5, 'Deev', 250, '2022-04-07'),
- (6, 'Poirot', 150, '2022-02-11'),
- (7, 'Li', 175, '2022-03-08'),
- (8, 'Ericsson', 185, '2022-02-23'),
- (9, 'Muller', 250, '2022-03-11'),
- (10, 'Halder', 350, '2022-01-28'),
- (11, 'Trump', 380, '2022-04-06'),
- (12, 'Plato', 350, '2022-03-19'),
- (13, 'Gandi', 377, '2022-01-09');
-
-ALTER TABLE sales_range SPLIT PARTITION sales_all INTO
- (PARTITION sales_apr2022 FOR VALUES FROM ('2022-04-01') TO ('2022-05-01'),
- PARTITION sales_feb2022 FOR VALUES FROM ('2022-02-01') TO ('2022-03-01'),
- PARTITION sales_others DEFAULT);
-
-INSERT INTO sales_range VALUES (14, 'Smith', 510, '2022-05-04');
-
-SELECT tableoid::regclass, * FROM sales_range ORDER BY tableoid::regclass::text COLLATE "C", salesperson_id;
-
-DROP TABLE sales_range;
-
---
--- Test that SPLIT PARTITION rejects the degenerate case where the only
--- non-DEFAULT replacement partition keeps the original bound and the command
--- merely adds a DEFAULT partition.
---
-CREATE TABLE t (i int) PARTITION BY RANGE (i);
-CREATE TABLE tp_0_50 PARTITION OF t FOR VALUES FROM (0) TO (50);
-INSERT INTO t VALUES (1);
-
--- ERROR
-ALTER TABLE t SPLIT PARTITION tp_0_50 INTO
- (PARTITION tp_0_50 FOR VALUES FROM (0) TO (50),
- PARTITION tp_default DEFAULT);
-
-DROP TABLE t;
-
---
--- Test that a LIST split with DEFAULT is not considered degenerate when
--- only NULL is removed from the explicit replacement partition.
---
-CREATE TABLE t (i int) PARTITION BY LIST (i);
-CREATE TABLE tp_null_1 PARTITION OF t FOR VALUES IN (NULL, 1);
-
-ALTER TABLE t SPLIT PARTITION tp_null_1 INTO
- (PARTITION tp_1 FOR VALUES IN (1),
- PARTITION tp_default DEFAULT);
-
-INSERT INTO t VALUES (NULL), (1), (2);
-SELECT tableoid::regclass, i FROM t ORDER BY tableoid::regclass::text COLLATE "C", i NULLS FIRST;
-
-DROP TABLE t;
-
---
--- Test that the same-bound check for LIST partitioning uses the
--- partition operator family, not byte equality. -0.0 and 0.0 have
--- different bit patterns but compare equal under float8, so the
--- replacement bound (-0.0, 1.0) is the same set as the original
--- (0.0, 1.0) and the SPLIT is degenerate. A datumIsEqual()-based
--- check would let this through; the partsupfunc-based check correctly
--- rejects it.
---
-CREATE TABLE t (v float8) PARTITION BY LIST (v);
-CREATE TABLE tp_zero_one PARTITION OF t FOR VALUES IN (0.0, 1.0);
-
--- ERROR
-ALTER TABLE t SPLIT PARTITION tp_zero_one INTO
- (PARTITION tp_zero_one FOR VALUES IN (-0.0, 1.0),
- PARTITION tp_default DEFAULT);
-
-DROP TABLE t;
-
---
--- Test that the explicit partition bound cannot extend outside the split
--- partition's bound when a DEFAULT partition is specified.
---
-CREATE TABLE t (i int) PARTITION BY RANGE (i);
-CREATE TABLE tp_0_51 PARTITION OF t FOR VALUES FROM (0) TO (51);
-CREATE TABLE tp_51_100 PARTITION OF t FOR VALUES FROM (51) TO (100);
-
--- ERROR
-ALTER TABLE t SPLIT PARTITION tp_0_51 INTO
- (PARTITION tp_0_51 FOR VALUES FROM (0) TO (53),
- PARTITION tp_default DEFAULT);
-
-DROP TABLE t;
-
---
--- Try to SPLIT partition of another table.
---
-CREATE TABLE t1(i int, t text) PARTITION BY LIST (t);
-CREATE TABLE t1pa PARTITION OF t1 FOR VALUES IN ('A');
-CREATE TABLE t2 (i int, t text) PARTITION BY RANGE (t);
-
--- ERROR
-ALTER TABLE t2 SPLIT PARTITION t1pa INTO
- (PARTITION t2a FOR VALUES FROM ('A') TO ('B'),
- PARTITION t2b FOR VALUES FROM ('B') TO ('C'));
-
-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 COLLATE "C";
-
--- ERROR
-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));
-
-ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
- (PARTITION pg_temp.tp_0_1 FOR VALUES FROM (0) TO (1),
- PARTITION pg_temp.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 COLLATE "C";
-
-DROP TABLE t;
-
--- Check the new partitions inherit parent's tablespace
-CREATE TABLE t (i int PRIMARY KEY USING INDEX TABLESPACE regress_tblspace)
- PARTITION BY RANGE (i) TABLESPACE regress_tblspace;
-CREATE TABLE tp_0_2 PARTITION OF t FOR VALUES FROM (0) TO (2);
-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));
-SELECT tablename, tablespace FROM pg_tables
- WHERE tablename IN ('t', 'tp_0_1', 'tp_1_2') AND schemaname = 'partition_split_schema'
- ORDER BY tablename COLLATE "C", tablespace COLLATE "C";
-SELECT tablename, indexname, tablespace FROM pg_indexes
- WHERE tablename IN ('t', 'tp_0_1', 'tp_1_2') AND schemaname = 'partition_split_schema'
- ORDER BY tablename COLLATE "C", indexname COLLATE "C", tablespace COLLATE "C";
-DROP TABLE t;
-
--- Check new partitions inherits parent's table access method
-CREATE ACCESS METHOD partition_split_heap TYPE TABLE HANDLER heap_tableam_handler;
-CREATE TABLE t (i int) PARTITION BY RANGE (i) USING partition_split_heap;
-CREATE TABLE tp_0_2 PARTITION OF t FOR VALUES FROM (0) TO (2);
-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));
-SELECT c.relname, a.amname
-FROM pg_class c JOIN pg_am a ON c.relam = a.oid
-WHERE c.oid IN ('t'::regclass, 'tp_0_1'::regclass, 'tp_1_2'::regclass)
-ORDER BY c.relname COLLATE "C";
-DROP TABLE t;
-DROP ACCESS METHOD partition_split_heap;
-
--- Split partition of a temporary table when one of the partitions after
--- split has the same name as the partition being split
-CREATE TEMP TABLE t (a int) PARTITION BY RANGE (a);
-CREATE TEMP TABLE tp_0 PARTITION OF t FOR VALUES FROM (0) TO (2);
-ALTER TABLE t SPLIT PARTITION tp_0 INTO
- (PARTITION pg_temp.tp_0 FOR VALUES FROM (0) TO (1),
- PARTITION pg_temp.tp_1 FOR VALUES FROM (1) TO (2));
-DROP TABLE t;
-
--- Check defaults and constraints of new partitions
-CREATE TABLE t_bigint (
- b bigint,
- i int DEFAULT (3+10),
- j int DEFAULT 101,
- k int GENERATED ALWAYS AS (b+10) STORED
-)
-PARTITION BY RANGE (b);
-CREATE TABLE t_bigint_default PARTITION OF t_bigint DEFAULT;
--- Show defaults/constraints before SPLIT PARTITION
-\d+ t_bigint
-\d+ t_bigint_default
-ALTER TABLE t_bigint SPLIT PARTITION t_bigint_default INTO
- (PARTITION t_bigint_01_10 FOR VALUES FROM (0) TO (10),
- PARTITION t_bigint_default DEFAULT);
--- Show defaults/constraints after SPLIT PARTITION
-\d+ t_bigint_default
-\d+ t_bigint_01_10
-DROP TABLE t_bigint;
-
--- Test permission checks. The user needs to own the parent table and the
--- the partition to split to do the split.
-CREATE ROLE regress_partition_split_alice;
-CREATE ROLE regress_partition_split_bob;
-GRANT ALL ON SCHEMA partition_split_schema TO regress_partition_split_alice;
-GRANT ALL ON SCHEMA partition_split_schema TO regress_partition_split_bob;
-
-SET SESSION AUTHORIZATION regress_partition_split_alice;
-CREATE TABLE t (i int) PARTITION BY RANGE (i);
-CREATE TABLE tp_0_2 PARTITION OF t FOR VALUES FROM (0) TO (2);
-
-SET SESSION AUTHORIZATION regress_partition_split_bob;
-ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
- (PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
- PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); --error
-RESET SESSION AUTHORIZATION;
-
-ALTER TABLE t OWNER TO regress_partition_split_bob;
-SET SESSION AUTHORIZATION regress_partition_split_bob;
-ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
- (PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
- PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); --error
-RESET SESSION AUTHORIZATION;
-
-ALTER TABLE tp_0_2 OWNER TO regress_partition_split_bob;
-SET SESSION AUTHORIZATION regress_partition_split_bob;
-ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
- (PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
- PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); --ok
-RESET SESSION AUTHORIZATION;
-
-DROP TABLE t;
-
--- Test: owner of new partitions should be the same as owner of split partition
-CREATE TABLE t (i int) PARTITION BY RANGE (i);
-
-SET SESSION AUTHORIZATION regress_partition_split_alice;
-CREATE TABLE tp_0_2(i int);
-RESET SESSION AUTHORIZATION;
-
-ALTER TABLE t ATTACH PARTITION tp_0_2 FOR VALUES FROM (0) TO (2);
-
--- Owner is 'regress_partition_split_alice':
-\dt tp_0_2
-
-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));
-
--- Owner should be 'regress_partition_split_alice':
-\dt tp_0_1
-\dt tp_1_2
-
-DROP TABLE t;
-
--- Test: index of new partitions should be created with same owner as split
--- partition
-SET SESSION AUTHORIZATION regress_partition_split_alice;
-CREATE TABLE t (i int) PARTITION BY RANGE (i);
-CREATE TABLE tp_10_20 PARTITION OF t FOR VALUES FROM (10) TO (20);
-INSERT INTO t VALUES (11), (16);
-CREATE OR REPLACE FUNCTION run_me(integer) RETURNS integer AS $$
-BEGIN
- RAISE NOTICE 'you are running me as %', CURRENT_USER;
- RETURN $1;
-END
-$$ LANGUAGE PLPGSQL IMMUTABLE;
-
--- Owner is 'regress_partition_split_alice':
-CREATE INDEX ON t (run_me(i));
-RESET SESSION AUTHORIZATION;
-
--- Owner should be 'regress_partition_split_alice':
-ALTER TABLE t SPLIT PARTITION tp_10_20 INTO
- (PARTITION tp_10_15 FOR VALUES FROM (10) TO (15),
- PARTITION tp_15_20 FOR VALUES FROM (15) TO (20));
-
-DROP TABLE t;
-DROP FUNCTION run_me(integer);
-
-REVOKE ALL ON SCHEMA partition_split_schema FROM regress_partition_split_alice;
-REVOKE ALL ON SCHEMA partition_split_schema FROM regress_partition_split_bob;
-DROP ROLE regress_partition_split_alice;
-DROP ROLE regress_partition_split_bob;
-
--- Test for hash partitioned table
-CREATE TABLE t (i int) PARTITION BY HASH(i);
-CREATE TABLE tp1 PARTITION OF t FOR VALUES WITH (MODULUS 2, REMAINDER 0);
-CREATE TABLE tp2 PARTITION OF t FOR VALUES WITH (MODULUS 2, REMAINDER 1);
-
--- ERROR
-ALTER TABLE t SPLIT PARTITION tp1 INTO
- (PARTITION tp1_1 FOR VALUES WITH (MODULUS 4, REMAINDER 0),
- PARTITION tp1_2 FOR VALUES WITH (MODULUS 4, REMAINDER 2));
-
--- ERROR
-ALTER TABLE t SPLIT PARTITION tp1 INTO
- (PARTITION tp1_1 FOR VALUES WITH (MODULUS 4, REMAINDER 0));
-
-DROP TABLE t;
-
-
--- Test for split partition properties:
--- * STATISTICS is empty
--- * COMMENT is empty
--- * DEFAULTS are the same as DEFAULTS for partitioned table
--- * STORAGE is the same as STORAGE for partitioned table
--- * GENERATED and CONSTRAINTS are the same as GENERATED and CONSTRAINTS for partitioned table
--- * TRIGGERS are the same as TRIGGERS for partitioned table
-
-CREATE TABLE t
-(i int NOT NULL,
- t text STORAGE EXTENDED COMPRESSION pglz DEFAULT 'default_t',
- b bigint,
- d date GENERATED ALWAYS as ('2022-01-01') STORED) PARTITION BY RANGE (abs(i));
-COMMENT ON COLUMN t.i IS 't1.i';
-
-CREATE TABLE tp_x
-(i int NOT NULL,
- t text STORAGE MAIN DEFAULT 'default_tp_x',
- b bigint,
- d date GENERATED ALWAYS as ('2022-02-02') STORED);
-ALTER TABLE t ATTACH PARTITION tp_x FOR VALUES FROM (0) TO (2);
-COMMENT ON COLUMN tp_x.i IS 'tp_x.i';
-
-CREATE STATISTICS t_stat (DEPENDENCIES) on i, b from t;
-CREATE STATISTICS tp_x_stat (DEPENDENCIES) on i, b from tp_x;
-
-ALTER TABLE t ADD CONSTRAINT t_b_check CHECK (b > 0);
-ALTER TABLE t ADD CONSTRAINT t_b_check1 CHECK (b > 0) NOT ENFORCED;
-ALTER TABLE t ADD CONSTRAINT t_b_check2 CHECK (b > 0) NOT VALID;
-ALTER TABLE t ADD CONSTRAINT t_b_nn NOT NULL b NOT VALID;
-
-INSERT INTO tp_x(i, t, b) VALUES(0, DEFAULT, 1);
-INSERT INTO tp_x(i, t, b) VALUES(1, DEFAULT, 2);
-
-CREATE OR REPLACE FUNCTION trigger_function() RETURNS trigger LANGUAGE 'plpgsql' AS
-$BODY$
-BEGIN
- RAISE NOTICE 'trigger(%) called: action = %, when = %, level = %', TG_ARGV[0], TG_OP, TG_WHEN, TG_LEVEL;
- RETURN new;
-END;
-$BODY$;
-
-CREATE TRIGGER t_before_insert_row_trigger BEFORE INSERT ON t FOR EACH ROW
- EXECUTE PROCEDURE trigger_function('t');
-CREATE TRIGGER tp_x_before_insert_row_trigger BEFORE INSERT ON tp_x FOR EACH ROW
- EXECUTE PROCEDURE trigger_function('tp_x');
-
-\d+ tp_x
-ALTER TABLE t SPLIT PARTITION tp_x INTO
- (PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
- PARTITION tp_x FOR VALUES FROM (1) TO (2));
-\d+ tp_x
-
-INSERT INTO t(i, t, b) VALUES(1, DEFAULT, 3);
-SELECT tableoid::regclass, * FROM t ORDER BY tableoid::regclass::text COLLATE "C", b;
-DROP TABLE t;
-DROP FUNCTION trigger_function();
-
-
--- Test for recomputation of stored generated columns.
-CREATE TABLE t (i int, tab_id int generated always as (tableoid) stored) PARTITION BY RANGE (i);
-CREATE TABLE tp_0_2 PARTITION OF t FOR VALUES FROM (0) TO (2);
-ALTER TABLE t ADD CONSTRAINT cc CHECK(tableoid <> 123456789);
-INSERT INTO t VALUES (0), (1);
-
--- Should be 1 because partition identifier for row with i=0 is the same as
--- partition identifier for row with i=1.
-SELECT count(*) FROM t WHERE i = 0 AND tab_id IN (SELECT tab_id FROM t WHERE i = 1);
-
--- "tab_id" column (stored generated column) with "tableoid" attribute requires
--- recomputation here.
-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));
-
--- Should be 0 because partition identifier for row with i=0 is different from
--- partition identifier for row with i=1.
-SELECT count(*) FROM t WHERE i = 0 AND tab_id IN (SELECT tab_id FROM t WHERE i = 1);
-
-DROP TABLE t;
-
--- Each new partition produced by SPLIT must get its own TOAST table so
--- that out-of-line varlena attributes coming from the source partition
--- can be stored. SET STORAGE EXTERNAL forces externalization for any
--- value over the TOAST threshold, so a string over that threshold
--- suffices to exercise the toast-table dependency.
-CREATE TABLE t (a text) PARTITION BY RANGE(a);
-ALTER TABLE t ALTER COLUMN a SET STORAGE EXTERNAL;
-CREATE TABLE tp_all PARTITION OF t FOR VALUES FROM (MINVALUE) TO (MAXVALUE);
-INSERT INTO t SELECT repeat('1', 10000);
-ALTER TABLE t SPLIT PARTITION tp_all INTO (
- PARTITION tp_lo FOR VALUES FROM (MINVALUE) TO ('2'),
- PARTITION tp_hi FOR VALUES FROM ('2') TO (MAXVALUE)
-);
-SELECT relname,
- reltoastrelid <> 0 AS has_toast,
- pg_relation_size(reltoastrelid) > 0 AS toast_used
- FROM pg_class WHERE relname IN ('tp_lo', 'tp_hi') ORDER BY relname;
-SELECT length(a) FROM t;
-DROP TABLE t;
-
--- Tablespace selection for the new partitions mirrors
--- CREATE TABLE ... PARTITION OF: the partitioned root's explicit
--- tablespace wins; otherwise default_tablespace applies; otherwise the
--- database default is used.
-CREATE TABLE t (i int) PARTITION BY RANGE(i) TABLESPACE regress_tblspace;
-CREATE TABLE tp_all PARTITION OF t FOR VALUES FROM (0) TO (10);
-INSERT INTO t SELECT generate_series(0, 9);
-ALTER TABLE t SPLIT PARTITION tp_all INTO (
- PARTITION tp_lo FOR VALUES FROM (0) TO (5),
- PARTITION tp_hi FOR VALUES FROM (5) TO (10)
-);
-SELECT c.relname, s.spcname FROM pg_class c LEFT JOIN pg_tablespace s
- ON c.reltablespace = s.oid WHERE c.relname IN ('tp_lo', 'tp_hi')
- ORDER BY c.relname;
-DROP TABLE t;
-
--- Parent has no explicit tablespace, but default_tablespace is set: the
--- new partitions land on default_tablespace.
-CREATE TABLE t (i int) PARTITION BY RANGE(i);
-CREATE TABLE tp_all PARTITION OF t FOR VALUES FROM (0) TO (10);
-INSERT INTO t SELECT generate_series(0, 9);
-SET default_tablespace TO regress_tblspace;
-ALTER TABLE t SPLIT PARTITION tp_all INTO (
- PARTITION tp_lo FOR VALUES FROM (0) TO (5),
- PARTITION tp_hi FOR VALUES FROM (5) TO (10)
-);
-RESET default_tablespace;
-SELECT c.relname, s.spcname FROM pg_class c LEFT JOIN pg_tablespace s
- ON c.reltablespace = s.oid WHERE c.relname IN ('tp_lo', 'tp_hi')
- ORDER BY c.relname;
-DROP TABLE t;
-
-CREATE TABLE t (i int) PARTITION BY RANGE(i);
-CREATE TABLE tp_all PARTITION OF t FOR VALUES FROM (0) TO (10);
-INSERT INTO t SELECT generate_series(0, 9);
--- pg_global is rejected when picked up from default_tablespace.
-SET default_tablespace TO pg_global;
-ALTER TABLE t SPLIT PARTITION tp_all INTO (
- PARTITION tp_lo FOR VALUES FROM (0) TO (5),
- PARTITION tp_hi FOR VALUES FROM (5) TO (10)
-); -- fails
-RESET default_tablespace;
--- Parent has no explicit tablespace and default_tablespace is empty: new
--- partitions use the database default (reltablespace = 0).
-ALTER TABLE t SPLIT PARTITION tp_all INTO (
- PARTITION tp_lo FOR VALUES FROM (0) TO (5),
- PARTITION tp_hi FOR VALUES FROM (5) TO (10)
-);
-SELECT relname, reltablespace FROM pg_class
- WHERE relname IN ('tp_lo', 'tp_hi') ORDER BY relname;
-DROP TABLE t;
-
-RESET search_path;
-
---
-DROP SCHEMA partition_split_schema;
-DROP SCHEMA partition_split_schema2;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 23d4e6c0651..bcf981a02a2 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2211,7 +2211,6 @@ PartitionDirectoryEntry
PartitionDispatch
PartitionElem
PartitionHashBound
-PartitionIndexExtDepEntry
PartitionKey
PartitionListValue
PartitionMap
@@ -2924,7 +2923,6 @@ SimpleStats
SimpleStringList
SimpleStringListCell
SingleBoundSortItem
-SinglePartitionSpec
Size
SkipPages
SkipSupport
@@ -2996,7 +2994,6 @@ SpinDelayStatus
SplitInterval
SplitLR
SplitPageLayout
-SplitPartitionContext
SplitPoint
SplitTextOutputData
SplitVar