@@ -147,10 +147,11 @@ typedef enum AlterTablePass
147
147
AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
148
148
AT_PASS_DROP, /* DROP (all flavors) */
149
149
AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
150
+ AT_PASS_ADD_COL, /* ADD COLUMN */
151
+ AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
150
152
AT_PASS_OLD_INDEX, /* re-add existing indexes */
151
153
AT_PASS_OLD_CONSTR, /* re-add existing constraints */
152
154
/* We could support a RENAME COLUMN pass here, but not currently used */
153
- AT_PASS_ADD_COL, /* ADD COLUMN */
154
155
AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
155
156
AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
156
157
AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
@@ -459,6 +460,8 @@ static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
459
460
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
460
461
Node *def, LOCKMODE lockmode);
461
462
static 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);
462
465
static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
463
466
static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
464
467
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
@@ -561,7 +564,7 @@ static void ATPrepAlterColumnType(List **wqueue,
561
564
static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
562
565
static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
563
566
AlterTableCmd *cmd, LOCKMODE lockmode);
564
- static void RememberAllDependentForRebuilding(AlteredTableInfo *tab,
567
+ static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
565
568
Relation rel, AttrNumber attnum, const char *colName);
566
569
static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
567
570
static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
@@ -4551,6 +4554,7 @@ AlterTableGetLockLevel(List *cmds)
4551
4554
case AT_AddIdentity:
4552
4555
case AT_DropIdentity:
4553
4556
case AT_SetIdentity:
4557
+ case AT_SetExpression:
4554
4558
case AT_DropExpression:
4555
4559
case AT_SetCompression:
4556
4560
cmd_lockmode = AccessExclusiveLock;
@@ -4852,6 +4856,11 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4852
4856
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4853
4857
pass = AT_PASS_COL_ATTRS;
4854
4858
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;
4855
4864
case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
4856
4865
ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4857
4866
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
@@ -5153,11 +5162,11 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
5153
5162
lockmode, pass, context);
5154
5163
5155
5164
/*
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).
5159
5168
*/
5160
- if (pass == AT_PASS_ALTER_TYPE)
5169
+ if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION )
5161
5170
ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5162
5171
5163
5172
if (tab->rel)
@@ -5236,6 +5245,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5236
5245
case AT_SetAttNotNull: /* set pg_attribute.attnotnull */
5237
5246
address = ATExecSetAttNotNull(wqueue, rel, cmd->name, lockmode);
5238
5247
break;
5248
+ case AT_SetExpression:
5249
+ address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5250
+ break;
5239
5251
case AT_DropExpression:
5240
5252
address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5241
5253
break;
@@ -6363,6 +6375,8 @@ alter_table_type_to_string(AlterTableType cmdtype)
6363
6375
return "ALTER COLUMN ... SET NOT NULL";
6364
6376
case AT_SetAttNotNull:
6365
6377
return NULL; /* not real grammar */
6378
+ case AT_SetExpression:
6379
+ return "ALTER COLUMN ... SET EXPRESSION";
6366
6380
case AT_DropExpression:
6367
6381
return "ALTER COLUMN ... DROP EXPRESSION";
6368
6382
case AT_SetStatistics:
@@ -8013,10 +8027,11 @@ ATExecColumnDefault(Relation rel, const char *colName,
8013
8027
(errcode(ERRCODE_SYNTAX_ERROR),
8014
8028
errmsg("column \"%s\" of relation \"%s\" is a generated column",
8015
8029
colName, RelationGetRelationName(rel)),
8016
- newDefault || TupleDescAttr(tupdesc, attnum - 1)->attgenerated != ATTRIBUTE_GENERATED_STORED ? 0 :
8030
+ newDefault ?
8017
8031
/* 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)));
8020
8035
8021
8036
/*
8022
8037
* 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
8313
8328
return address;
8314
8329
}
8315
8330
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
+
8316
8446
/*
8317
8447
* ALTER TABLE ALTER COLUMN DROP EXPRESSION
8318
8448
*/
@@ -13300,7 +13430,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
13300
13430
* the info before executing ALTER TYPE, though, else the deparser will
13301
13431
* get confused.
13302
13432
*/
13303
- RememberAllDependentForRebuilding(tab, rel, attnum, colName);
13433
+ RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
13304
13434
13305
13435
/*
13306
13436
* Now scan for dependencies of this column on other things. The only
@@ -13497,18 +13627,21 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
13497
13627
}
13498
13628
13499
13629
/*
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.
13503
13633
*/
13504
13634
static 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)
13506
13637
{
13507
13638
Relation depRel;
13508
13639
ScanKeyData key[3];
13509
13640
SysScanDesc scan;
13510
13641
HeapTuple depTup;
13511
13642
13643
+ Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
13644
+
13512
13645
depRel = table_open(DependRelationId, RowExclusiveLock);
13513
13646
13514
13647
ScanKeyInit(&key[0],
@@ -13572,12 +13705,13 @@ RememberAllDependentForRebuilding(AlteredTableInfo *tab, Relation rel, AttrNumbe
13572
13705
13573
13706
case OCLASS_REWRITE:
13574
13707
/* 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)));
13581
13715
break;
13582
13716
13583
13717
case OCLASS_TRIGGER:
@@ -13591,12 +13725,13 @@ RememberAllDependentForRebuilding(AlteredTableInfo *tab, Relation rel, AttrNumbe
13591
13725
* significant amount of new code. Since we can't easily tell
13592
13726
* which case applies, we punt for both. FIXME someday.
13593
13727
*/
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)));
13600
13735
break;
13601
13736
13602
13737
case OCLASS_POLICY:
@@ -13609,12 +13744,13 @@ RememberAllDependentForRebuilding(AlteredTableInfo *tab, Relation rel, AttrNumbe
13609
13744
* easy enough to remove and recreate the policy; still, FIXME
13610
13745
* someday.
13611
13746
*/
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)));
13618
13754
break;
13619
13755
13620
13756
case OCLASS_DEFAULT:
@@ -13634,19 +13770,20 @@ RememberAllDependentForRebuilding(AlteredTableInfo *tab, Relation rel, AttrNumbe
13634
13770
/*
13635
13771
* This must be a reference from the expression of a
13636
13772
* 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.
13641
13777
*/
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))));
13650
13787
}
13651
13788
break;
13652
13789
}
@@ -13863,11 +14000,11 @@ RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
13863
14000
}
13864
14001
13865
14002
/*
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.
13871
14008
*/
13872
14009
static void
13873
14010
ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
0 commit comments