@@ -388,6 +388,8 @@ static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
388
388
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
389
389
Node *def, LOCKMODE lockmode);
390
390
static 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);
391
393
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
392
394
Node *newValue, LOCKMODE lockmode);
393
395
static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
@@ -3672,6 +3674,7 @@ AlterTableGetLockLevel(List *cmds)
3672
3674
case AT_AddIdentity:
3673
3675
case AT_DropIdentity:
3674
3676
case AT_SetIdentity:
3677
+ case AT_DropExpression:
3675
3678
cmd_lockmode = AccessExclusiveLock;
3676
3679
break;
3677
3680
@@ -3946,6 +3949,12 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
3946
3949
/* No command-specific prep needed */
3947
3950
pass = AT_PASS_COL_ATTRS;
3948
3951
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;
3949
3958
case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
3950
3959
ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
3951
3960
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
@@ -4265,6 +4274,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
4265
4274
case AT_CheckNotNull: /* check column is already marked NOT NULL */
4266
4275
ATExecCheckNotNull(tab, rel, cmd->name, lockmode);
4267
4276
break;
4277
+ case AT_DropExpression:
4278
+ address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
4279
+ break;
4268
4280
case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
4269
4281
address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
4270
4282
break;
@@ -6457,7 +6469,9 @@ ATExecColumnDefault(Relation rel, const char *colName,
6457
6469
ereport(ERROR,
6458
6470
(errcode(ERRCODE_SYNTAX_ERROR),
6459
6471
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.")));
6461
6475
6462
6476
/*
6463
6477
* 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
6725
6739
return address;
6726
6740
}
6727
6741
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
+
6728
6887
/*
6729
6888
* ALTER TABLE ALTER COLUMN SET STATISTICS
6730
6889
*
0 commit comments