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);
 }
 
 /*