diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/commands/tablecmds.c | 408 |
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); } /* |