aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/commands/tablecmds.c408
1 files changed, 214 insertions, 194 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index efd6078f8b1..b126116e256 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -561,6 +561,8 @@ static void ATPrepAlterColumnType(List **wqueue,
static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
AlterTableCmd *cmd, LOCKMODE lockmode);
+static void RememberAllDependentForRebuilding(AlteredTableInfo *tab,
+ Relation rel, AttrNumber attnum, const char *colName);
static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
@@ -13298,6 +13300,215 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
* the info before executing ALTER TYPE, though, else the deparser will
* get confused.
*/
+ RememberAllDependentForRebuilding(tab, rel, attnum, colName);
+
+ /*
+ * Now scan for dependencies of this column on other things. The only
+ * things we should find are the dependency on the column datatype and
+ * possibly a collation dependency. Those can be removed.
+ */
+ depRel = table_open(DependRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_classid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationRelationId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_objid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationGetRelid(rel)));
+ ScanKeyInit(&key[2],
+ Anum_pg_depend_objsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum((int32) attnum));
+
+ scan = systable_beginscan(depRel, DependDependerIndexId, true,
+ NULL, 3, key);
+
+ while (HeapTupleIsValid(depTup = systable_getnext(scan)))
+ {
+ Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
+ ObjectAddress foundObject;
+
+ foundObject.classId = foundDep->refclassid;
+ foundObject.objectId = foundDep->refobjid;
+ foundObject.objectSubId = foundDep->refobjsubid;
+
+ if (foundDep->deptype != DEPENDENCY_NORMAL)
+ elog(ERROR, "found unexpected dependency type '%c'",
+ foundDep->deptype);
+ if (!(foundDep->refclassid == TypeRelationId &&
+ foundDep->refobjid == attTup->atttypid) &&
+ !(foundDep->refclassid == CollationRelationId &&
+ foundDep->refobjid == attTup->attcollation))
+ elog(ERROR, "found unexpected dependency for column: %s",
+ getObjectDescription(&foundObject, false));
+
+ CatalogTupleDelete(depRel, &depTup->t_self);
+ }
+
+ systable_endscan(scan);
+
+ table_close(depRel, RowExclusiveLock);
+
+ /*
+ * Here we go --- change the recorded column type and collation. (Note
+ * heapTup is a copy of the syscache entry, so okay to scribble on.) First
+ * fix up the missing value if any.
+ */
+ if (attTup->atthasmissing)
+ {
+ Datum missingval;
+ bool missingNull;
+
+ /* if rewrite is true the missing value should already be cleared */
+ Assert(tab->rewrite == 0);
+
+ /* Get the missing value datum */
+ missingval = heap_getattr(heapTup,
+ Anum_pg_attribute_attmissingval,
+ attrelation->rd_att,
+ &missingNull);
+
+ /* if it's a null array there is nothing to do */
+
+ if (!missingNull)
+ {
+ /*
+ * Get the datum out of the array and repack it in a new array
+ * built with the new type data. We assume that since the table
+ * doesn't need rewriting, the actual Datum doesn't need to be
+ * changed, only the array metadata.
+ */
+
+ int one = 1;
+ bool isNull;
+ Datum valuesAtt[Natts_pg_attribute] = {0};
+ bool nullsAtt[Natts_pg_attribute] = {0};
+ bool replacesAtt[Natts_pg_attribute] = {0};
+ HeapTuple newTup;
+
+ missingval = array_get_element(missingval,
+ 1,
+ &one,
+ 0,
+ attTup->attlen,
+ attTup->attbyval,
+ attTup->attalign,
+ &isNull);
+ missingval = PointerGetDatum(construct_array(&missingval,
+ 1,
+ targettype,
+ tform->typlen,
+ tform->typbyval,
+ tform->typalign));
+
+ valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
+ replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
+ nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
+
+ newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
+ valuesAtt, nullsAtt, replacesAtt);
+ heap_freetuple(heapTup);
+ heapTup = newTup;
+ attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
+ }
+ }
+
+ attTup->atttypid = targettype;
+ attTup->atttypmod = targettypmod;
+ attTup->attcollation = targetcollid;
+ if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
+ ereport(ERROR,
+ errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("too many array dimensions"));
+ attTup->attndims = list_length(typeName->arrayBounds);
+ attTup->attlen = tform->typlen;
+ attTup->attbyval = tform->typbyval;
+ attTup->attalign = tform->typalign;
+ attTup->attstorage = tform->typstorage;
+ attTup->attcompression = InvalidCompressionMethod;
+
+ ReleaseSysCache(typeTuple);
+
+ CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
+
+ table_close(attrelation, RowExclusiveLock);
+
+ /* Install dependencies on new datatype and collation */
+ add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
+ add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
+
+ /*
+ * Drop any pg_statistic entry for the column, since it's now wrong type
+ */
+ RemoveStatistics(RelationGetRelid(rel), attnum);
+
+ InvokeObjectPostAlterHook(RelationRelationId,
+ RelationGetRelid(rel), attnum);
+
+ /*
+ * Update the default, if present, by brute force --- remove and re-add
+ * the default. Probably unsafe to take shortcuts, since the new version
+ * may well have additional dependencies. (It's okay to do this now,
+ * rather than after other ALTER TYPE commands, since the default won't
+ * depend on other column types.)
+ */
+ if (defaultexpr)
+ {
+ /*
+ * If it's a GENERATED default, drop its dependency records, in
+ * particular its INTERNAL dependency on the column, which would
+ * otherwise cause dependency.c to refuse to perform the deletion.
+ */
+ if (attTup->attgenerated)
+ {
+ Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
+
+ if (!OidIsValid(attrdefoid))
+ elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
+ RelationGetRelid(rel), attnum);
+ (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
+ }
+
+ /*
+ * Make updates-so-far visible, particularly the new pg_attribute row
+ * which will be updated again.
+ */
+ CommandCounterIncrement();
+
+ /*
+ * We use RESTRICT here for safety, but at present we do not expect
+ * anything to depend on the default.
+ */
+ RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
+ true);
+
+ StoreAttrDefault(rel, attnum, defaultexpr, true, false);
+ }
+
+ ObjectAddressSubSet(address, RelationRelationId,
+ RelationGetRelid(rel), attnum);
+
+ /* Cleanup */
+ heap_freetuple(heapTup);
+
+ return address;
+}
+
+/*
+ * Subroutine for ATExecAlterColumnType: Find everything that depends on the
+ * column (constraints, indexes, etc), and record enough information to let us
+ * recreate the objects.
+ */
+static void
+RememberAllDependentForRebuilding(AlteredTableInfo *tab, Relation rel, AttrNumber attnum, const char *colName)
+{
+ Relation depRel;
+ ScanKeyData key[3];
+ SysScanDesc scan;
+ HeapTuple depTup;
+
depRel = table_open(DependRelationId, RowExclusiveLock);
ScanKeyInit(&key[0],
@@ -13414,10 +13625,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
col.objectSubId == attnum)
{
/*
- * Ignore the column's own default expression, which
- * we will deal with below.
+ * Ignore the column's own default expression. The
+ * caller deals with it.
*/
- Assert(defaultexpr);
}
else
{
@@ -13501,197 +13711,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
}
systable_endscan(scan);
-
- /*
- * Now scan for dependencies of this column on other things. The only
- * things we should find are the dependency on the column datatype and
- * possibly a collation dependency. Those can be removed.
- */
- ScanKeyInit(&key[0],
- Anum_pg_depend_classid,
- BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(RelationRelationId));
- ScanKeyInit(&key[1],
- Anum_pg_depend_objid,
- BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(RelationGetRelid(rel)));
- ScanKeyInit(&key[2],
- Anum_pg_depend_objsubid,
- BTEqualStrategyNumber, F_INT4EQ,
- Int32GetDatum((int32) attnum));
-
- scan = systable_beginscan(depRel, DependDependerIndexId, true,
- NULL, 3, key);
-
- while (HeapTupleIsValid(depTup = systable_getnext(scan)))
- {
- Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
- ObjectAddress foundObject;
-
- foundObject.classId = foundDep->refclassid;
- foundObject.objectId = foundDep->refobjid;
- foundObject.objectSubId = foundDep->refobjsubid;
-
- if (foundDep->deptype != DEPENDENCY_NORMAL)
- elog(ERROR, "found unexpected dependency type '%c'",
- foundDep->deptype);
- if (!(foundDep->refclassid == TypeRelationId &&
- foundDep->refobjid == attTup->atttypid) &&
- !(foundDep->refclassid == CollationRelationId &&
- foundDep->refobjid == attTup->attcollation))
- elog(ERROR, "found unexpected dependency for column: %s",
- getObjectDescription(&foundObject, false));
-
- CatalogTupleDelete(depRel, &depTup->t_self);
- }
-
- systable_endscan(scan);
-
- table_close(depRel, RowExclusiveLock);
-
- /*
- * Here we go --- change the recorded column type and collation. (Note
- * heapTup is a copy of the syscache entry, so okay to scribble on.) First
- * fix up the missing value if any.
- */
- if (attTup->atthasmissing)
- {
- Datum missingval;
- bool missingNull;
-
- /* if rewrite is true the missing value should already be cleared */
- Assert(tab->rewrite == 0);
-
- /* Get the missing value datum */
- missingval = heap_getattr(heapTup,
- Anum_pg_attribute_attmissingval,
- attrelation->rd_att,
- &missingNull);
-
- /* if it's a null array there is nothing to do */
-
- if (!missingNull)
- {
- /*
- * Get the datum out of the array and repack it in a new array
- * built with the new type data. We assume that since the table
- * doesn't need rewriting, the actual Datum doesn't need to be
- * changed, only the array metadata.
- */
-
- int one = 1;
- bool isNull;
- Datum valuesAtt[Natts_pg_attribute] = {0};
- bool nullsAtt[Natts_pg_attribute] = {0};
- bool replacesAtt[Natts_pg_attribute] = {0};
- HeapTuple newTup;
-
- missingval = array_get_element(missingval,
- 1,
- &one,
- 0,
- attTup->attlen,
- attTup->attbyval,
- attTup->attalign,
- &isNull);
- missingval = PointerGetDatum(construct_array(&missingval,
- 1,
- targettype,
- tform->typlen,
- tform->typbyval,
- tform->typalign));
-
- valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
- replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
- nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
-
- newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
- valuesAtt, nullsAtt, replacesAtt);
- heap_freetuple(heapTup);
- heapTup = newTup;
- attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
- }
- }
-
- attTup->atttypid = targettype;
- attTup->atttypmod = targettypmod;
- attTup->attcollation = targetcollid;
- if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
- ereport(ERROR,
- errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("too many array dimensions"));
- attTup->attndims = list_length(typeName->arrayBounds);
- attTup->attlen = tform->typlen;
- attTup->attbyval = tform->typbyval;
- attTup->attalign = tform->typalign;
- attTup->attstorage = tform->typstorage;
- attTup->attcompression = InvalidCompressionMethod;
-
- ReleaseSysCache(typeTuple);
-
- CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
-
- table_close(attrelation, RowExclusiveLock);
-
- /* Install dependencies on new datatype and collation */
- add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
- add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
-
- /*
- * Drop any pg_statistic entry for the column, since it's now wrong type
- */
- RemoveStatistics(RelationGetRelid(rel), attnum);
-
- InvokeObjectPostAlterHook(RelationRelationId,
- RelationGetRelid(rel), attnum);
-
- /*
- * Update the default, if present, by brute force --- remove and re-add
- * the default. Probably unsafe to take shortcuts, since the new version
- * may well have additional dependencies. (It's okay to do this now,
- * rather than after other ALTER TYPE commands, since the default won't
- * depend on other column types.)
- */
- if (defaultexpr)
- {
- /*
- * If it's a GENERATED default, drop its dependency records, in
- * particular its INTERNAL dependency on the column, which would
- * otherwise cause dependency.c to refuse to perform the deletion.
- */
- if (attTup->attgenerated)
- {
- Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
-
- if (!OidIsValid(attrdefoid))
- elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
- RelationGetRelid(rel), attnum);
- (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
- }
-
- /*
- * Make updates-so-far visible, particularly the new pg_attribute row
- * which will be updated again.
- */
- CommandCounterIncrement();
-
- /*
- * We use RESTRICT here for safety, but at present we do not expect
- * anything to depend on the default.
- */
- RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
- true);
-
- StoreAttrDefault(rel, attnum, defaultexpr, true, false);
- }
-
- ObjectAddressSubSet(address, RelationRelationId,
- RelationGetRelid(rel), attnum);
-
- /* Cleanup */
- heap_freetuple(heapTup);
-
- return address;
+ table_close(depRel, NoLock);
}
/*