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