]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Refactor: separate function to find all objects depending on a column
authorPeter Eisentraut <peter@eisentraut.org>
Wed, 3 Jan 2024 07:48:09 +0000 (08:48 +0100)
committerPeter Eisentraut <peter@eisentraut.org>
Wed, 3 Jan 2024 07:50:37 +0000 (08:50 +0100)
Move code from ATExecAlterColumnType() that finds the all the objects
that depend on the column to a separate function.  A future patch will
reuse this code.

Author: Amul Sul <sulamul@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/CAAJ_b94yyJeGA-5M951_Lr+KfZokOp-2kXicpmEhi5FXhBeTog@mail.gmail.com

src/backend/commands/tablecmds.c

index efd6078f8b18b7df6ff815c8bc019d468ab5e4ed..b126116e256d40d82f5b276c0c6ccbaa181a56dc 100644 (file)
@@ -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);
 }
 
 /*