diff options
Diffstat (limited to 'src/backend/commands/tablecmds.c')
-rw-r--r-- | src/backend/commands/tablecmds.c | 163 |
1 files changed, 80 insertions, 83 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index fc3fc9b3846..80faae985e9 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -7899,6 +7899,7 @@ ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMOD Form_pg_attribute attTup; AttrNumber attnum; Relation attrelation; + Oid attrdefoid; ObjectAddress address; attrelation = table_open(AttributeRelationId, RowExclusiveLock); @@ -7936,71 +7937,44 @@ ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMOD } } + /* + * Mark the column as no longer generated. (The atthasdef flag needs to + * get cleared too, but RemoveAttrDefault will handle that.) + */ attTup->attgenerated = '\0'; CatalogTupleUpdate(attrelation, &tuple->t_self, tuple); InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), - attTup->attnum); - ObjectAddressSubSet(address, RelationRelationId, - RelationGetRelid(rel), attnum); + attnum); heap_freetuple(tuple); table_close(attrelation, RowExclusiveLock); - CommandCounterIncrement(); - - RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false, false); - /* - * Remove all dependencies of this (formerly generated) column on other - * columns in the same table. (See StoreAttrDefault() for which - * dependencies are created.) We don't expect there to be dependencies - * between columns of the same table for other reasons, so it's okay to - * remove all of them. + * Drop the dependency records of the GENERATED expression, in particular + * its INTERNAL dependency on the column, which would otherwise cause + * dependency.c to refuse to perform the deletion. */ - { - Relation depRel; - ScanKeyData key[3]; - SysScanDesc scan; - HeapTuple tup; - - 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(attnum)); + 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); - scan = systable_beginscan(depRel, DependDependerIndexId, true, - NULL, 3, key); - - while (HeapTupleIsValid(tup = systable_getnext(scan))) - { - Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup); - - if (depform->refclassid == RelationRelationId && - depform->refobjid == RelationGetRelid(rel) && - depform->refobjsubid != 0 && - depform->deptype == DEPENDENCY_AUTO) - { - CatalogTupleDelete(depRel, &tup->t_self); - } - } - - systable_endscan(scan); + /* Make above changes visible */ + CommandCounterIncrement(); - table_close(depRel, RowExclusiveLock); - } + /* + * Get rid of the GENERATED expression itself. 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, + false, false); + ObjectAddressSubSet(address, RelationRelationId, + RelationGetRelid(rel), attnum); return address; } @@ -12548,21 +12522,6 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, */ Assert(foundObject.objectSubId == 0); } - else if (relKind == RELKIND_RELATION && - foundObject.objectSubId != 0 && - get_attgenerated(foundObject.objectId, foundObject.objectSubId)) - { - /* - * Changing the type of a column that is used by a - * generated column is not allowed by SQL standard. It - * might be doable with some thinking and effort. - */ - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("cannot alter type of a column used by a generated column"), - errdetail("Column \"%s\" is used by generated column \"%s\".", - colName, get_attname(foundObject.objectId, foundObject.objectSubId, false)))); - } else { /* Not expecting any other direct dependencies... */ @@ -12625,13 +12584,39 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, break; case OCLASS_DEFAULT: + { + ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId); - /* - * Ignore the column's default expression, since we will fix - * it below. - */ - Assert(defaultexpr); - break; + if (col.objectId == RelationGetRelid(rel) && + col.objectSubId == attnum) + { + /* + * Ignore the column's own default expression, which + * we will deal with below. + */ + Assert(defaultexpr); + } + else + { + /* + * This must be a reference from the expression of a + * generated column elsewhere in the same table. + * Changing the type of a column that is used by a + * generated column is not allowed by SQL standard, so + * just punt for now. It might be doable with some + * thinking and effort. + */ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot alter type of a column used by a generated column"), + errdetail("Column \"%s\" is used by generated column \"%s\".", + colName, + get_attname(col.objectId, + col.objectSubId, + false)))); + } + break; + } case OCLASS_STATISTIC_EXT: @@ -12694,9 +12679,8 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, /* * Now scan for dependencies of this column on other things. The only - * thing we should find is the dependency on the column datatype, which we - * want to remove, possibly a collation dependency, and dependencies on - * other columns if it is a generated column. + * 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, @@ -12723,18 +12707,13 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, foundObject.objectId = foundDep->refobjid; foundObject.objectSubId = foundDep->refobjsubid; - if (foundDep->deptype != DEPENDENCY_NORMAL && - foundDep->deptype != DEPENDENCY_AUTO) + 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) && - !(foundDep->refclassid == RelationRelationId && - foundDep->refobjid == RelationGetRelid(rel) && - foundDep->refobjsubid != 0) - ) + foundDep->refobjid == attTup->attcollation)) elog(ERROR, "found unexpected dependency for column: %s", getObjectDescription(&foundObject, false)); @@ -12850,7 +12829,25 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, */ if (defaultexpr) { - /* Must make new row visible since it will be updated again */ + /* + * 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(); /* |