@@ -147,10 +147,11 @@ typedef enum AlterTablePass
147147 AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
148148 AT_PASS_DROP, /* DROP (all flavors) */
149149 AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
150+ AT_PASS_ADD_COL, /* ADD COLUMN */
151+ AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
150152 AT_PASS_OLD_INDEX, /* re-add existing indexes */
151153 AT_PASS_OLD_CONSTR, /* re-add existing constraints */
152154 /* We could support a RENAME COLUMN pass here, but not currently used */
153- AT_PASS_ADD_COL, /* ADD COLUMN */
154155 AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
155156 AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
156157 AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
@@ -459,6 +460,8 @@ static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
459460static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
460461 Node *def, LOCKMODE lockmode);
461462static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
463+ static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
464+ Node *newExpr, LOCKMODE lockmode);
462465static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
463466static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
464467static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
@@ -561,7 +564,7 @@ static void ATPrepAlterColumnType(List **wqueue,
561564static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
562565static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
563566 AlterTableCmd *cmd, LOCKMODE lockmode);
564- static void RememberAllDependentForRebuilding(AlteredTableInfo *tab,
567+ static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
565568 Relation rel, AttrNumber attnum, const char *colName);
566569static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
567570static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
@@ -4551,6 +4554,7 @@ AlterTableGetLockLevel(List *cmds)
45514554 case AT_AddIdentity:
45524555 case AT_DropIdentity:
45534556 case AT_SetIdentity:
4557+ case AT_SetExpression:
45544558 case AT_DropExpression:
45554559 case AT_SetCompression:
45564560 cmd_lockmode = AccessExclusiveLock;
@@ -4852,6 +4856,11 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
48524856 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
48534857 pass = AT_PASS_COL_ATTRS;
48544858 break;
4859+ case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
4860+ ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4861+ ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4862+ pass = AT_PASS_SET_EXPRESSION;
4863+ break;
48554864 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
48564865 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
48574866 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
@@ -5153,11 +5162,11 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
51535162 lockmode, pass, context);
51545163
51555164 /*
5156- * After the ALTER TYPE pass, do cleanup work (this is not done in
5157- * ATExecAlterColumnType since it should be done only once if
5158- * multiple columns of a table are altered).
5165+ * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5166+ * (this is not done in ATExecAlterColumnType since it should be
5167+ * done only once if multiple columns of a table are altered).
51595168 */
5160- if (pass == AT_PASS_ALTER_TYPE)
5169+ if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION )
51615170 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
51625171
51635172 if (tab->rel)
@@ -5236,6 +5245,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
52365245 case AT_SetAttNotNull: /* set pg_attribute.attnotnull */
52375246 address = ATExecSetAttNotNull(wqueue, rel, cmd->name, lockmode);
52385247 break;
5248+ case AT_SetExpression:
5249+ address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5250+ break;
52395251 case AT_DropExpression:
52405252 address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
52415253 break;
@@ -6363,6 +6375,8 @@ alter_table_type_to_string(AlterTableType cmdtype)
63636375 return "ALTER COLUMN ... SET NOT NULL";
63646376 case AT_SetAttNotNull:
63656377 return NULL; /* not real grammar */
6378+ case AT_SetExpression:
6379+ return "ALTER COLUMN ... SET EXPRESSION";
63666380 case AT_DropExpression:
63676381 return "ALTER COLUMN ... DROP EXPRESSION";
63686382 case AT_SetStatistics:
@@ -8013,10 +8027,11 @@ ATExecColumnDefault(Relation rel, const char *colName,
80138027 (errcode(ERRCODE_SYNTAX_ERROR),
80148028 errmsg("column \"%s\" of relation \"%s\" is a generated column",
80158029 colName, RelationGetRelationName(rel)),
8016- newDefault || TupleDescAttr(tupdesc, attnum - 1)->attgenerated != ATTRIBUTE_GENERATED_STORED ? 0 :
8030+ newDefault ?
80178031 /* translator: %s is an SQL ALTER command */
8018- errhint("Use %s instead.",
8019- "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION")));
8032+ errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8033+ (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8034+ errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
80208035
80218036 /*
80228037 * Remove any old default for the column. We use RESTRICT here for
@@ -8313,6 +8328,121 @@ ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE
83138328 return address;
83148329}
83158330
8331+ /*
8332+ * ALTER TABLE ALTER COLUMN SET EXPRESSION
8333+ *
8334+ * Return the address of the affected column.
8335+ */
8336+ static ObjectAddress
8337+ ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8338+ Node *newExpr, LOCKMODE lockmode)
8339+ {
8340+ HeapTuple tuple;
8341+ Form_pg_attribute attTup;
8342+ AttrNumber attnum;
8343+ Oid attrdefoid;
8344+ ObjectAddress address;
8345+ Expr *defval;
8346+ NewColumnValue *newval;
8347+ RawColumnDefault *rawEnt;
8348+
8349+ tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8350+ if (!HeapTupleIsValid(tuple))
8351+ ereport(ERROR,
8352+ (errcode(ERRCODE_UNDEFINED_COLUMN),
8353+ errmsg("column \"%s\" of relation \"%s\" does not exist",
8354+ colName, RelationGetRelationName(rel))));
8355+
8356+ attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8357+ attnum = attTup->attnum;
8358+
8359+ if (attnum <= 0)
8360+ ereport(ERROR,
8361+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8362+ errmsg("cannot alter system column \"%s\"",
8363+ colName)));
8364+
8365+ if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8366+ ereport(ERROR,
8367+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8368+ errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8369+ colName, RelationGetRelationName(rel))));
8370+ ReleaseSysCache(tuple);
8371+
8372+ /*
8373+ * Clear all the missing values if we're rewriting the table, since this
8374+ * renders them pointless.
8375+ */
8376+ RelationClearMissing(rel);
8377+
8378+ /* make sure we don't conflict with later attribute modifications */
8379+ CommandCounterIncrement();
8380+
8381+ /*
8382+ * Find everything that depends on the column (constraints, indexes, etc),
8383+ * and record enough information to let us recreate the objects after
8384+ * rewrite.
8385+ */
8386+ RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
8387+
8388+ /*
8389+ * Drop the dependency records of the GENERATED expression, in particular
8390+ * its INTERNAL dependency on the column, which would otherwise cause
8391+ * dependency.c to refuse to perform the deletion.
8392+ */
8393+ attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8394+ if (!OidIsValid(attrdefoid))
8395+ elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8396+ RelationGetRelid(rel), attnum);
8397+ (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8398+
8399+ /* Make above changes visible */
8400+ CommandCounterIncrement();
8401+
8402+ /*
8403+ * Get rid of the GENERATED expression itself. We use RESTRICT here for
8404+ * safety, but at present we do not expect anything to depend on the
8405+ * expression.
8406+ */
8407+ RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8408+ false, false);
8409+
8410+ /* Prepare to store the new expression, in the catalogs */
8411+ rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8412+ rawEnt->attnum = attnum;
8413+ rawEnt->raw_default = newExpr;
8414+ rawEnt->missingMode = false;
8415+ rawEnt->generated = ATTRIBUTE_GENERATED_STORED;
8416+
8417+ /* Store the generated expression */
8418+ AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8419+ false, true, false, NULL);
8420+
8421+ /* Make above new expression visible */
8422+ CommandCounterIncrement();
8423+
8424+ /* Prepare for table rewrite */
8425+ defval = (Expr *) build_column_default(rel, attnum);
8426+
8427+ newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
8428+ newval->attnum = attnum;
8429+ newval->expr = expression_planner(defval);
8430+ newval->is_generated = true;
8431+
8432+ tab->newvals = lappend(tab->newvals, newval);
8433+ tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
8434+
8435+ /* Drop any pg_statistic entry for the column */
8436+ RemoveStatistics(RelationGetRelid(rel), attnum);
8437+
8438+ InvokeObjectPostAlterHook(RelationRelationId,
8439+ RelationGetRelid(rel), attnum);
8440+
8441+ ObjectAddressSubSet(address, RelationRelationId,
8442+ RelationGetRelid(rel), attnum);
8443+ return address;
8444+ }
8445+
83168446/*
83178447 * ALTER TABLE ALTER COLUMN DROP EXPRESSION
83188448 */
@@ -13300,7 +13430,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
1330013430 * the info before executing ALTER TYPE, though, else the deparser will
1330113431 * get confused.
1330213432 */
13303- RememberAllDependentForRebuilding(tab, rel, attnum, colName);
13433+ RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
1330413434
1330513435 /*
1330613436 * Now scan for dependencies of this column on other things. The only
@@ -13497,18 +13627,21 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
1349713627}
1349813628
1349913629/*
13500- * Subroutine for ATExecAlterColumnType: Find everything that depends on the
13501- * column (constraints, indexes, etc), and record enough information to let us
13502- * recreate the objects.
13630+ * Subroutine for ATExecAlterColumnType and ATExecSetExpression : Find everything
13631+ * that depends on the column (constraints, indexes, etc), and record enough
13632+ * information to let us recreate the objects.
1350313633 */
1350413634static void
13505- RememberAllDependentForRebuilding(AlteredTableInfo *tab, Relation rel, AttrNumber attnum, const char *colName)
13635+ RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
13636+ Relation rel, AttrNumber attnum, const char *colName)
1350613637{
1350713638 Relation depRel;
1350813639 ScanKeyData key[3];
1350913640 SysScanDesc scan;
1351013641 HeapTuple depTup;
1351113642
13643+ Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
13644+
1351213645 depRel = table_open(DependRelationId, RowExclusiveLock);
1351313646
1351413647 ScanKeyInit(&key[0],
@@ -13572,12 +13705,13 @@ RememberAllDependentForRebuilding(AlteredTableInfo *tab, Relation rel, AttrNumbe
1357213705
1357313706 case OCLASS_REWRITE:
1357413707 /* XXX someday see if we can cope with revising views */
13575- ereport(ERROR,
13576- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13577- errmsg("cannot alter type of a column used by a view or rule"),
13578- errdetail("%s depends on column \"%s\"",
13579- getObjectDescription(&foundObject, false),
13580- colName)));
13708+ if (subtype == AT_AlterColumnType)
13709+ ereport(ERROR,
13710+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13711+ errmsg("cannot alter type of a column used by a view or rule"),
13712+ errdetail("%s depends on column \"%s\"",
13713+ getObjectDescription(&foundObject, false),
13714+ colName)));
1358113715 break;
1358213716
1358313717 case OCLASS_TRIGGER:
@@ -13591,12 +13725,13 @@ RememberAllDependentForRebuilding(AlteredTableInfo *tab, Relation rel, AttrNumbe
1359113725 * significant amount of new code. Since we can't easily tell
1359213726 * which case applies, we punt for both. FIXME someday.
1359313727 */
13594- ereport(ERROR,
13595- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13596- errmsg("cannot alter type of a column used in a trigger definition"),
13597- errdetail("%s depends on column \"%s\"",
13598- getObjectDescription(&foundObject, false),
13599- colName)));
13728+ if (subtype == AT_AlterColumnType)
13729+ ereport(ERROR,
13730+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13731+ errmsg("cannot alter type of a column used in a trigger definition"),
13732+ errdetail("%s depends on column \"%s\"",
13733+ getObjectDescription(&foundObject, false),
13734+ colName)));
1360013735 break;
1360113736
1360213737 case OCLASS_POLICY:
@@ -13609,12 +13744,13 @@ RememberAllDependentForRebuilding(AlteredTableInfo *tab, Relation rel, AttrNumbe
1360913744 * easy enough to remove and recreate the policy; still, FIXME
1361013745 * someday.
1361113746 */
13612- ereport(ERROR,
13613- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13614- errmsg("cannot alter type of a column used in a policy definition"),
13615- errdetail("%s depends on column \"%s\"",
13616- getObjectDescription(&foundObject, false),
13617- colName)));
13747+ if (subtype == AT_AlterColumnType)
13748+ ereport(ERROR,
13749+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13750+ errmsg("cannot alter type of a column used in a policy definition"),
13751+ errdetail("%s depends on column \"%s\"",
13752+ getObjectDescription(&foundObject, false),
13753+ colName)));
1361813754 break;
1361913755
1362013756 case OCLASS_DEFAULT:
@@ -13634,19 +13770,20 @@ RememberAllDependentForRebuilding(AlteredTableInfo *tab, Relation rel, AttrNumbe
1363413770 /*
1363513771 * This must be a reference from the expression of a
1363613772 * generated column elsewhere in the same table.
13637- * Changing the type of a column that is used by a
13638- * generated column is not allowed by SQL standard, so
13639- * just punt for now. It might be doable with some
13640- * thinking and effort.
13773+ * Changing the type/generated expression of a column
13774+ * that is used by a generated column is not allowed
13775+ * by SQL standard, so just punt for now. It might be
13776+ * doable with some thinking and effort.
1364113777 */
13642- ereport(ERROR,
13643- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13644- errmsg("cannot alter type of a column used by a generated column"),
13645- errdetail("Column \"%s\" is used by generated column \"%s\".",
13646- colName,
13647- get_attname(col.objectId,
13648- col.objectSubId,
13649- false))));
13778+ if (subtype == AT_AlterColumnType)
13779+ ereport(ERROR,
13780+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13781+ errmsg("cannot alter type of a column used by a generated column"),
13782+ errdetail("Column \"%s\" is used by generated column \"%s\".",
13783+ colName,
13784+ get_attname(col.objectId,
13785+ col.objectSubId,
13786+ false))));
1365013787 }
1365113788 break;
1365213789 }
@@ -13863,11 +14000,11 @@ RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
1386314000}
1386414001
1386514002/*
13866- * Cleanup after we've finished all the ALTER TYPE operations for a
13867- * particular relation. We have to drop and recreate all the indexes
13868- * and constraints that depend on the altered columns. We do the
13869- * actual dropping here, but re-creation is managed by adding work
13870- * queue entries to do those steps later.
14003+ * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
14004+ * operations for a particular relation. We have to drop and recreate all the
14005+ * indexes and constraints that depend on the altered columns. We do the
14006+ * actual dropping here, but re-creation is managed by adding work queue
14007+ * entries to do those steps later.
1387114008 */
1387214009static void
1387314010ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
0 commit comments