summaryrefslogtreecommitdiff
path: root/src/backend/commands/tablecmds.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/tablecmds.c')
-rw-r--r--src/backend/commands/tablecmds.c221
1 files changed, 190 insertions, 31 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 1f6073fb972..2a56a4357c9 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -359,7 +359,7 @@ static List *MergeAttributes(List *columns, const List *supers, char relpersiste
bool is_partition, List **supconstr,
List **supnotnulls);
static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr);
-static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
+static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
static void StoreCatalogInheritance(Oid relationId, List *supers,
bool child_is_partition);
@@ -456,10 +456,11 @@ static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
Node *newDefault);
static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
- Node *def, LOCKMODE lockmode);
+ Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
- Node *def, LOCKMODE lockmode);
-static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
+ Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
+static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
+ bool recurse, bool recursing);
static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
Node *newExpr, LOCKMODE lockmode);
static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
@@ -627,7 +628,7 @@ static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partsp
static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
List **partexprs, Oid *partopclass, Oid *partcollation,
PartitionStrategy strategy);
-static void CreateInheritance(Relation child_rel, Relation parent_rel);
+static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
static void RemoveInheritance(Relation child_rel, Relation parent_rel,
bool expect_detached);
static void ATInheritAdjustNotNulls(Relation parent_rel, Relation child_rel,
@@ -2864,6 +2865,15 @@ MergeAttributes(List *columns, const List *supers, char relpersistence,
def->is_not_null = true;
def->storage = attribute->attstorage;
def->generated = attribute->attgenerated;
+
+ /*
+ * Regular inheritance children are independent enough not to
+ * inherit identity columns. But partitions are integral part
+ * of a partitioned table and inherit identity column.
+ */
+ if (is_partition)
+ def->identity = attribute->attidentity;
+
if (CompressionMethodIsValid(attribute->attcompression))
def->compression =
pstrdup(GetCompressionMethodName(attribute->attcompression));
@@ -4824,18 +4834,24 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
break;
case AT_AddIdentity:
ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
- /* This command never recurses */
+ /* Set up recursion for phase 2; no other prep needed */
+ if (recurse)
+ cmd->recurse = true;
pass = AT_PASS_ADD_OTHERCONSTR;
break;
case AT_SetIdentity:
ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
- /* This command never recurses */
+ /* Set up recursion for phase 2; no other prep needed */
+ if (recurse)
+ cmd->recurse = true;
/* This should run after AddIdentity, so do it in MISC pass */
pass = AT_PASS_MISC;
break;
case AT_DropIdentity:
ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
- /* This command never recurses */
+ /* Set up recursion for phase 2; no other prep needed */
+ if (recurse)
+ cmd->recurse = true;
pass = AT_PASS_DROP;
break;
case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
@@ -5227,16 +5243,16 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
cur_pass, context);
Assert(cmd != NULL);
- address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode);
+ address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
break;
case AT_SetIdentity:
cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
cur_pass, context);
Assert(cmd != NULL);
- address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode);
+ address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
break;
case AT_DropIdentity:
- address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode);
+ address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
break;
case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
@@ -7092,11 +7108,17 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
}
/*
- * Cannot add identity column if table has children, because identity does
- * not inherit. (Adding column and identity separately will work.)
+ * Regular inheritance children are independent enough not to inherit the
+ * identity column from parent hence cannot recursively add identity
+ * column if the table has inheritance children.
+ *
+ * Partitions, on the other hand, are integral part of a partitioned table
+ * and inherit identity column. Hence propagate identity column down the
+ * partition hierarchy.
*/
if (colDef->identity &&
recurse &&
+ rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
find_inheritance_children(myrelid, NoLock) != NIL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
@@ -8063,7 +8085,7 @@ ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
*/
static ObjectAddress
ATExecAddIdentity(Relation rel, const char *colName,
- Node *def, LOCKMODE lockmode)
+ Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
{
Relation attrelation;
HeapTuple tuple;
@@ -8071,6 +8093,19 @@ ATExecAddIdentity(Relation rel, const char *colName,
AttrNumber attnum;
ObjectAddress address;
ColumnDef *cdef = castNode(ColumnDef, def);
+ bool ispartitioned;
+
+ ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
+ if (ispartitioned && !recurse)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("cannot add identity to a column of only the partitioned table"),
+ errhint("Do not specify the ONLY keyword.")));
+
+ if (rel->rd_rel->relispartition && !recursing)
+ ereport(ERROR,
+ errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("cannot add identity to a column of a partition"));
attrelation = table_open(AttributeRelationId, RowExclusiveLock);
@@ -8125,6 +8160,27 @@ ATExecAddIdentity(Relation rel, const char *colName,
table_close(attrelation, RowExclusiveLock);
+ /*
+ * Recurse to propagate the identity column to partitions. Identity is
+ * not inherited in regular inheritance children.
+ */
+ if (recurse && ispartitioned)
+ {
+ List *children;
+ ListCell *lc;
+
+ children = find_inheritance_children(RelationGetRelid(rel), lockmode);
+
+ foreach(lc, children)
+ {
+ Relation childrel;
+
+ childrel = table_open(lfirst_oid(lc), NoLock);
+ ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
+ table_close(childrel, NoLock);
+ }
+ }
+
return address;
}
@@ -8134,7 +8190,8 @@ ATExecAddIdentity(Relation rel, const char *colName,
* Return the address of the affected column.
*/
static ObjectAddress
-ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode)
+ATExecSetIdentity(Relation rel, const char *colName, Node *def,
+ LOCKMODE lockmode, bool recurse, bool recursing)
{
ListCell *option;
DefElem *generatedEl = NULL;
@@ -8143,6 +8200,19 @@ ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmod
AttrNumber attnum;
Relation attrelation;
ObjectAddress address;
+ bool ispartitioned;
+
+ ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
+ if (ispartitioned && !recurse)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("cannot change identity column of only the partitioned table"),
+ errhint("Do not specify the ONLY keyword.")));
+
+ if (rel->rd_rel->relispartition && !recursing)
+ ereport(ERROR,
+ errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("cannot change identity column of a partition"));
foreach(option, castNode(List, def))
{
@@ -8207,6 +8277,27 @@ ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmod
heap_freetuple(tuple);
table_close(attrelation, RowExclusiveLock);
+ /*
+ * Recurse to propagate the identity change to partitions. Identity is not
+ * inherited in regular inheritance children.
+ */
+ if (generatedEl && recurse && ispartitioned)
+ {
+ List *children;
+ ListCell *lc;
+
+ children = find_inheritance_children(RelationGetRelid(rel), lockmode);
+
+ foreach(lc, children)
+ {
+ Relation childrel;
+
+ childrel = table_open(lfirst_oid(lc), NoLock);
+ ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
+ table_close(childrel, NoLock);
+ }
+ }
+
return address;
}
@@ -8216,7 +8307,8 @@ ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmod
* Return the address of the affected column.
*/
static ObjectAddress
-ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
+ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
+ bool recurse, bool recursing)
{
HeapTuple tuple;
Form_pg_attribute attTup;
@@ -8225,6 +8317,19 @@ ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE
ObjectAddress address;
Oid seqid;
ObjectAddress seqaddress;
+ bool ispartitioned;
+
+ ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
+ if (ispartitioned && !recurse)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("cannot drop identity from a column of only the partitioned table"),
+ errhint("Do not specify the ONLY keyword.")));
+
+ if (rel->rd_rel->relispartition && !recursing)
+ ereport(ERROR,
+ errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("cannot drop identity from a column of a partition"));
attrelation = table_open(AttributeRelationId, RowExclusiveLock);
tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
@@ -8273,15 +8378,39 @@ ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE
table_close(attrelation, RowExclusiveLock);
- /* drop the internal sequence */
- seqid = getIdentitySequence(RelationGetRelid(rel), attnum, false);
- deleteDependencyRecordsForClass(RelationRelationId, seqid,
- RelationRelationId, DEPENDENCY_INTERNAL);
- CommandCounterIncrement();
- seqaddress.classId = RelationRelationId;
- seqaddress.objectId = seqid;
- seqaddress.objectSubId = 0;
- performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
+ /*
+ * Recurse to drop the identity from column in partitions. Identity is
+ * not inherited in regular inheritance children so ignore them.
+ */
+ if (recurse && ispartitioned)
+ {
+ List *children;
+ ListCell *lc;
+
+ children = find_inheritance_children(RelationGetRelid(rel), lockmode);
+
+ foreach(lc, children)
+ {
+ Relation childrel;
+
+ childrel = table_open(lfirst_oid(lc), NoLock);
+ ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
+ table_close(childrel, NoLock);
+ }
+ }
+
+ if (!recursing)
+ {
+ /* drop the internal sequence */
+ seqid = getIdentitySequence(RelationGetRelid(rel), attnum, false);
+ deleteDependencyRecordsForClass(RelationRelationId, seqid,
+ RelationRelationId, DEPENDENCY_INTERNAL);
+ CommandCounterIncrement();
+ seqaddress.classId = RelationRelationId;
+ seqaddress.objectId = seqid;
+ seqaddress.objectSubId = 0;
+ performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
+ }
return address;
}
@@ -15777,7 +15906,7 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
/* OK to create inheritance */
- CreateInheritance(child_rel, parent_rel);
+ CreateInheritance(child_rel, parent_rel, false);
/*
* If parent_rel has a primary key, then child_rel has not-null
@@ -15803,7 +15932,7 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
* Common to ATExecAddInherit() and ATExecAttachPartition().
*/
static void
-CreateInheritance(Relation child_rel, Relation parent_rel)
+CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
{
Relation catalogRelation;
SysScanDesc scan;
@@ -15848,7 +15977,7 @@ CreateInheritance(Relation child_rel, Relation parent_rel)
systable_endscan(scan);
/* Match up the columns and bump attinhcount as needed */
- MergeAttributesIntoExisting(child_rel, parent_rel);
+ MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
/* Match up the constraints and bump coninhcount as needed */
MergeConstraintsIntoExisting(child_rel, parent_rel);
@@ -15926,7 +16055,7 @@ constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
* the child must be as well. Defaults are not compared, however.
*/
static void
-MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
+MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
{
Relation attrrel;
TupleDesc parent_desc;
@@ -15996,6 +16125,14 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
/*
+ * Regular inheritance children are independent enough not to
+ * inherit identity columns. But partitions are integral part of
+ * a partitioned table and inherit identity column.
+ */
+ if (ispartition)
+ child_att->attidentity = parent_att->attidentity;
+
+ /*
* OK, bump the child column's inheritance count. (If we fail
* later on, this change will just roll back.)
*/
@@ -18780,7 +18917,10 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot attach temporary relation of another session as partition")));
- /* Check if there are any columns in attachrel that aren't in the parent */
+ /*
+ * Check if attachrel has any identity columns or any columns that aren't
+ * in the parent.
+ */
tupleDesc = RelationGetDescr(attachrel);
natts = tupleDesc->natts;
for (attno = 1; attno <= natts; attno++)
@@ -18792,6 +18932,13 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
if (attribute->attisdropped)
continue;
+ if (attribute->attidentity)
+ ereport(ERROR,
+ errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("table \"%s\" being attached contains an identity column \"%s\"",
+ RelationGetRelationName(attachrel), attributeName),
+ errdetail("The new partition may not contain an identity column."));
+
/* Try to find the column in parent (matching on column name) */
if (!SearchSysCacheExists2(ATTNAME,
ObjectIdGetDatum(RelationGetRelid(rel)),
@@ -18826,7 +18973,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
cmd->bound, pstate);
/* OK to create inheritance. Rest of the checks performed there */
- CreateInheritance(attachrel, rel);
+ CreateInheritance(attachrel, rel, true);
/* Update the pg_class entry. */
StorePartitionBound(attachrel, rel, cmd->bound);
@@ -19650,6 +19797,18 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
heap_freetuple(newtuple);
table_close(classRel, RowExclusiveLock);
+ /*
+ * Drop identity property from all identity columns of partition.
+ */
+ for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
+ {
+ Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
+
+ if (!attr->attisdropped && attr->attidentity)
+ ATExecDropIdentity(partRel, NameStr(attr->attname), false,
+ AccessExclusiveLock, true, true);
+ }
+
if (OidIsValid(defaultPartOid))
{
/*