@@ -388,6 +388,8 @@ static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
388388static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
389389 Node *def, LOCKMODE lockmode);
390390static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
391+ static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recursing);
392+ static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
391393static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
392394 Node *newValue, LOCKMODE lockmode);
393395static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
@@ -3672,6 +3674,7 @@ AlterTableGetLockLevel(List *cmds)
36723674 case AT_AddIdentity:
36733675 case AT_DropIdentity:
36743676 case AT_SetIdentity:
3677+ case AT_DropExpression:
36753678 cmd_lockmode = AccessExclusiveLock;
36763679 break;
36773680
@@ -3946,6 +3949,12 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
39463949 /* No command-specific prep needed */
39473950 pass = AT_PASS_COL_ATTRS;
39483951 break;
3952+ case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
3953+ ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3954+ ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
3955+ ATPrepDropExpression(rel, cmd, recursing);
3956+ pass = AT_PASS_DROP;
3957+ break;
39493958 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
39503959 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
39513960 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
@@ -4265,6 +4274,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
42654274 case AT_CheckNotNull: /* check column is already marked NOT NULL */
42664275 ATExecCheckNotNull(tab, rel, cmd->name, lockmode);
42674276 break;
4277+ case AT_DropExpression:
4278+ address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
4279+ break;
42684280 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
42694281 address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
42704282 break;
@@ -6457,7 +6469,9 @@ ATExecColumnDefault(Relation rel, const char *colName,
64576469 ereport(ERROR,
64586470 (errcode(ERRCODE_SYNTAX_ERROR),
64596471 errmsg("column \"%s\" of relation \"%s\" is a generated column",
6460- colName, RelationGetRelationName(rel))));
6472+ colName, RelationGetRelationName(rel)),
6473+ newDefault || TupleDescAttr(tupdesc, attnum - 1)->attgenerated != ATTRIBUTE_GENERATED_STORED ? 0 :
6474+ errhint("Use ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION instead.")));
64616475
64626476 /*
64636477 * Remove any old default for the column. We use RESTRICT here for
@@ -6725,6 +6739,151 @@ ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE
67256739 return address;
67266740}
67276741
6742+ /*
6743+ * ALTER TABLE ALTER COLUMN DROP EXPRESSION
6744+ */
6745+ static void
6746+ ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recursing)
6747+ {
6748+ /*
6749+ * Cannot drop generation expression from inherited columns.
6750+ */
6751+ if (!recursing)
6752+ {
6753+ HeapTuple tuple;
6754+ Form_pg_attribute attTup;
6755+
6756+ tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
6757+ if (!HeapTupleIsValid(tuple))
6758+ ereport(ERROR,
6759+ (errcode(ERRCODE_UNDEFINED_COLUMN),
6760+ errmsg("column \"%s\" of relation \"%s\" does not exist",
6761+ cmd->name, RelationGetRelationName(rel))));
6762+
6763+ attTup = (Form_pg_attribute) GETSTRUCT(tuple);
6764+
6765+ if (attTup->attinhcount > 0)
6766+ ereport(ERROR,
6767+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6768+ errmsg("cannot drop generation expression from inherited column")));
6769+ }
6770+ }
6771+
6772+ /*
6773+ * Return the address of the affected column.
6774+ */
6775+ static ObjectAddress
6776+ ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
6777+ {
6778+ HeapTuple tuple;
6779+ Form_pg_attribute attTup;
6780+ AttrNumber attnum;
6781+ Relation attrelation;
6782+ ObjectAddress address;
6783+
6784+ attrelation = table_open(AttributeRelationId, RowExclusiveLock);
6785+ tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
6786+ if (!HeapTupleIsValid(tuple))
6787+ ereport(ERROR,
6788+ (errcode(ERRCODE_UNDEFINED_COLUMN),
6789+ errmsg("column \"%s\" of relation \"%s\" does not exist",
6790+ colName, RelationGetRelationName(rel))));
6791+
6792+ attTup = (Form_pg_attribute) GETSTRUCT(tuple);
6793+ attnum = attTup->attnum;
6794+
6795+ if (attnum <= 0)
6796+ ereport(ERROR,
6797+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6798+ errmsg("cannot alter system column \"%s\"",
6799+ colName)));
6800+
6801+ if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
6802+ {
6803+ if (!missing_ok)
6804+ ereport(ERROR,
6805+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
6806+ errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
6807+ colName, RelationGetRelationName(rel))));
6808+ else
6809+ {
6810+ ereport(NOTICE,
6811+ (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
6812+ colName, RelationGetRelationName(rel))));
6813+ heap_freetuple(tuple);
6814+ table_close(attrelation, RowExclusiveLock);
6815+ return InvalidObjectAddress;
6816+ }
6817+ }
6818+
6819+ attTup->attgenerated = '\0';
6820+ CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
6821+
6822+ InvokeObjectPostAlterHook(RelationRelationId,
6823+ RelationGetRelid(rel),
6824+ attTup->attnum);
6825+ ObjectAddressSubSet(address, RelationRelationId,
6826+ RelationGetRelid(rel), attnum);
6827+ heap_freetuple(tuple);
6828+
6829+ table_close(attrelation, RowExclusiveLock);
6830+
6831+ CommandCounterIncrement();
6832+
6833+ RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false, false);
6834+
6835+ /*
6836+ * Remove all dependencies of this (formerly generated) column on other
6837+ * columns in the same table. (See StoreAttrDefault() for which
6838+ * dependencies are created.) We don't expect there to be dependencies
6839+ * between columns of the same table for other reasons, so it's okay to
6840+ * remove all of them.
6841+ */
6842+ {
6843+ Relation depRel;
6844+ ScanKeyData key[3];
6845+ SysScanDesc scan;
6846+ HeapTuple tup;
6847+
6848+ depRel = table_open(DependRelationId, RowExclusiveLock);
6849+
6850+ ScanKeyInit(&key[0],
6851+ Anum_pg_depend_classid,
6852+ BTEqualStrategyNumber, F_OIDEQ,
6853+ ObjectIdGetDatum(RelationRelationId));
6854+ ScanKeyInit(&key[1],
6855+ Anum_pg_depend_objid,
6856+ BTEqualStrategyNumber, F_OIDEQ,
6857+ ObjectIdGetDatum(RelationGetRelid(rel)));
6858+ ScanKeyInit(&key[2],
6859+ Anum_pg_depend_objsubid,
6860+ BTEqualStrategyNumber, F_INT4EQ,
6861+ Int32GetDatum(attnum));
6862+
6863+ scan = systable_beginscan(depRel, DependDependerIndexId, true,
6864+ NULL, 3, key);
6865+
6866+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
6867+ {
6868+ Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
6869+
6870+ if (depform->refclassid == RelationRelationId &&
6871+ depform->refobjid == RelationGetRelid(rel) &&
6872+ depform->refobjsubid != 0 &&
6873+ depform->deptype == DEPENDENCY_AUTO)
6874+ {
6875+ CatalogTupleDelete(depRel, &tup->t_self);
6876+ }
6877+ }
6878+
6879+ systable_endscan(scan);
6880+
6881+ table_close(depRel, RowExclusiveLock);
6882+ }
6883+
6884+ return address;
6885+ }
6886+
67286887/*
67296888 * ALTER TABLE ALTER COLUMN SET STATISTICS
67306889 *
0 commit comments