Skip to content

Commit f595117

Browse files
committed
ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION
Add an ALTER TABLE subcommand for dropping the generated property from a column, per SQL standard. Reviewed-by: Sergei Kornilov <sk@zsrv.org> Discussion: https://www.postgresql.org/message-id/flat/2f7f1d9c-946e-0453-d841-4f38eb9d69b6%402ndquadrant.com
1 parent d751ba5 commit f595117

File tree

9 files changed

+326
-4
lines changed

9 files changed

+326
-4
lines changed

doc/src/sgml/ref/alter_table.sgml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
4646
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET DEFAULT <replaceable class="parameter">expression</replaceable>
4747
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> DROP DEFAULT
4848
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> { SET | DROP } NOT NULL
49+
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> DROP EXPRESSION [ IF EXISTS ]
4950
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> ADD GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ]
5051
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> { SET GENERATED { ALWAYS | BY DEFAULT } | SET <replaceable>sequence_option</replaceable> | RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] } [...]
5152
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> DROP IDENTITY [ IF EXISTS ]
@@ -241,6 +242,23 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
241242
</listitem>
242243
</varlistentry>
243244

245+
<varlistentry>
246+
<term><literal>DROP EXPRESSION [ IF EXISTS ]</literal></term>
247+
<listitem>
248+
<para>
249+
This form turns a stored generated column into a normal base column.
250+
Existing data in the columns is retained, but future changes will no
251+
longer apply the generation expression.
252+
</para>
253+
254+
<para>
255+
If <literal>DROP EXPRESSION IF EXISTS</literal> is specified and the
256+
column is not a stored generated column, no error is thrown. In this
257+
case a notice is issued instead.
258+
</para>
259+
</listitem>
260+
</varlistentry>
261+
244262
<varlistentry>
245263
<term><literal>ADD GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY</literal></term>
246264
<term><literal>SET GENERATED { ALWAYS | BY DEFAULT }</literal></term>

src/backend/catalog/sql_features.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ F381 Extended schema manipulation 03 ALTER TABLE statement: DROP CONSTRAINT clau
252252
F382 Alter column data type YES
253253
F383 Set column not null clause YES
254254
F384 Drop identity property clause YES
255-
F385 Drop column generation expression clause NO
255+
F385 Drop column generation expression clause YES
256256
F386 Set identity column generation clause YES
257257
F391 Long identifiers YES
258258
F392 Unicode escapes in identifiers YES

src/backend/commands/tablecmds.c

Lines changed: 160 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,8 @@ static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
388388
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
389389
Node *def, LOCKMODE lockmode);
390390
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);
391393
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
392394
Node *newValue, LOCKMODE lockmode);
393395
static 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
*

src/backend/parser/gram.y

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
639639
DOUBLE_P DROP
640640

641641
EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
642-
EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
642+
EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXPRESSION
643643
EXTENSION EXTERNAL EXTRACT
644644

645645
FALSE_P FAMILY FETCH FILTER FIRST_P FLOAT_P FOLLOWING FOR
@@ -2129,6 +2129,23 @@ alter_table_cmd:
21292129
n->name = $3;
21302130
$$ = (Node *)n;
21312131
}
2132+
/* ALTER TABLE <name> ALTER [COLUMN] <colname> DROP EXPRESSION */
2133+
| ALTER opt_column ColId DROP EXPRESSION
2134+
{
2135+
AlterTableCmd *n = makeNode(AlterTableCmd);
2136+
n->subtype = AT_DropExpression;
2137+
n->name = $3;
2138+
$$ = (Node *)n;
2139+
}
2140+
/* ALTER TABLE <name> ALTER [COLUMN] <colname> DROP EXPRESSION IF EXISTS */
2141+
| ALTER opt_column ColId DROP EXPRESSION IF_P EXISTS
2142+
{
2143+
AlterTableCmd *n = makeNode(AlterTableCmd);
2144+
n->subtype = AT_DropExpression;
2145+
n->name = $3;
2146+
n->missing_ok = true;
2147+
$$ = (Node *)n;
2148+
}
21322149
/* ALTER TABLE <name> ALTER [COLUMN] <colname> SET STATISTICS <SignedIconst> */
21332150
| ALTER opt_column ColId SET STATISTICS SignedIconst
21342151
{
@@ -15196,6 +15213,7 @@ unreserved_keyword:
1519615213
| EXCLUSIVE
1519715214
| EXECUTE
1519815215
| EXPLAIN
15216+
| EXPRESSION
1519915217
| EXTENSION
1520015218
| EXTERNAL
1520115219
| FAMILY

src/bin/psql/tab-complete.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2015,7 +2015,7 @@ psql_completion(const char *text, int start, int end)
20152015
/* ALTER TABLE ALTER [COLUMN] <foo> DROP */
20162016
else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "DROP") ||
20172017
Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "DROP"))
2018-
COMPLETE_WITH("DEFAULT", "IDENTITY", "NOT NULL");
2018+
COMPLETE_WITH("DEFAULT", "EXPRESSION", "IDENTITY", "NOT NULL");
20192019
else if (Matches("ALTER", "TABLE", MatchAny, "CLUSTER"))
20202020
COMPLETE_WITH("ON");
20212021
else if (Matches("ALTER", "TABLE", MatchAny, "CLUSTER", "ON"))

src/include/nodes/parsenodes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1785,6 +1785,7 @@ typedef enum AlterTableType
17851785
AT_ColumnDefault, /* alter column default */
17861786
AT_DropNotNull, /* alter column drop not null */
17871787
AT_SetNotNull, /* alter column set not null */
1788+
AT_DropExpression, /* alter column drop expression */
17881789
AT_CheckNotNull, /* check column is already marked not null */
17891790
AT_SetStatistics, /* alter column set statistics */
17901791
AT_SetOptions, /* alter column set ( options ) */

src/include/parser/kwlist.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ PG_KEYWORD("exclusive", EXCLUSIVE, UNRESERVED_KEYWORD)
155155
PG_KEYWORD("execute", EXECUTE, UNRESERVED_KEYWORD)
156156
PG_KEYWORD("exists", EXISTS, COL_NAME_KEYWORD)
157157
PG_KEYWORD("explain", EXPLAIN, UNRESERVED_KEYWORD)
158+
PG_KEYWORD("expression", EXPRESSION, UNRESERVED_KEYWORD)
158159
PG_KEYWORD("extension", EXTENSION, UNRESERVED_KEYWORD)
159160
PG_KEYWORD("external", EXTERNAL, UNRESERVED_KEYWORD)
160161
PG_KEYWORD("extract", EXTRACT, COL_NAME_KEYWORD)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy