v1-0001-SET-DATA-TYPE-cope-with-generation-expression-dependency.patch
text/x-patch
Filename: v1-0001-SET-DATA-TYPE-cope-with-generation-expression-dependency.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v1-0001
Subject: SET DATA TYPE cope with generation expression dependency
| File | + | − |
|---|---|---|
| src/backend/commands/tablecmds.c | 78 | 12 |
| src/backend/utils/adt/ruleutils.c | 108 | 0 |
| src/include/utils/ruleutils.h | 1 | 0 |
| src/test/regress/expected/generated_stored.out | 47 | 12 |
| src/test/regress/expected/generated_virtual.out | 38 | 12 |
| src/test/regress/sql/generated_stored.sql | 17 | 1 |
| src/test/regress/sql/generated_virtual.sql | 12 | 1 |
From 7e48fdf35dc40261f50cff8f5f80b41db5c6d8d4 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Mon, 20 Oct 2025 15:45:03 +0800
Subject: [PATCH v1 1/1] SET DATA TYPE cope with generation expression
dependency
let ALTER COLUMN SET DATA TYPE cope with generation expression dependency
demo:
CREATE TABLE main_table (a int, b int generated always as (a) stored);
ALTER TABLE main_table ALTER COLUMN a SET DATA TYPE INT8;
RROR: cannot alter type of a column used by a generated column
DETAIL: Column "a" is used by generated column "b".
With the attached patch, the previous error will no longer occur.
discussion: https://postgr.es/m/
related discussion: https://postgr.es/m/CACJufxHZsgn3zM5g-x7YmtFGzNDnRwR07S+GYfiUs+tZ45MDDw@mail.gmail.com
---
src/backend/commands/tablecmds.c | 90 +++++++++++++--
src/backend/utils/adt/ruleutils.c | 108 ++++++++++++++++++
src/include/utils/ruleutils.h | 1 +
.../regress/expected/generated_stored.out | 59 ++++++++--
.../regress/expected/generated_virtual.out | 50 ++++++--
src/test/regress/sql/generated_stored.sql | 18 ++-
src/test/regress/sql/generated_virtual.sql | 13 ++-
7 files changed, 301 insertions(+), 38 deletions(-)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index fc89352b661..2dba15c0c0e 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -208,6 +208,8 @@ typedef struct AlteredTableInfo
char *clusterOnIndex; /* index to use for CLUSTER */
List *changedStatisticsOids; /* OIDs of statistics to rebuild */
List *changedStatisticsDefs; /* string definitions of same */
+ List *changedGenerationExprOids; /* OIDs of generation expression to rebuild */
+ List *changedGenerationExprDefs; /* string definitions of same */
} AlteredTableInfo;
/* Struct describing one new constraint to check in Phase 3 scan */
@@ -650,6 +652,7 @@ static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableT
Relation rel, AttrNumber attnum, const char *colName);
static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
+static void RememberGenerationExpressionForRebuilding(Oid indoid, AlteredTableInfo *tab);
static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
LOCKMODE lockmode);
@@ -15171,20 +15174,12 @@ RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
/*
* This must be a reference from the expression of a
* generated column elsewhere in the same table.
- * Changing the type/generated expression of a column
- * that is used by a generated column is not allowed
- * by SQL standard, so just punt for now. It might be
- * doable with some thinking and effort.
+ * Changing the type of a column that is used by a
+ * generated column is not allowed by SQL standard, but
+ * we allow it.
*/
if (subtype == AT_AlterColumnType)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot alter type of a column used by a generated column"),
- errdetail("Column \"%s\" is used by generated column \"%s\".",
- colName,
- get_attname(col.objectId,
- col.objectSubId,
- false))));
+ RememberGenerationExpressionForRebuilding(foundObject.objectId, tab);
}
break;
}
@@ -15367,6 +15362,32 @@ RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
}
}
+/*
+ * Subroutine for ATExecAlterColumnType: remember that a generation expression object
+ * needs to be rebuilt (which we might already know).
+ */
+static void
+RememberGenerationExpressionForRebuilding(Oid attrdefoid, AlteredTableInfo *tab)
+{
+ /*
+ * This de-duplication check is critical for two independent reasons: we
+ * mustn't try to recreate the same generation expression twice, and if the
+ * generation expression object depends on more than one column whose type
+ * is to be altered, we must capture its definition string before applying
+ * any of the type changes. ruleutils.c will get confused if we ask again
+ * later.
+ */
+ if (!list_member_oid(tab->changedGenerationExprOids, attrdefoid))
+ {
+ /* OK, capture the generation expression's existing definition string */
+ char *defstring = pg_get_generationdef(attrdefoid);
+
+ tab->changedGenerationExprOids = lappend_oid(tab->changedGenerationExprOids, attrdefoid);
+ tab->changedGenerationExprDefs = lappend(tab->changedGenerationExprDefs, defstring);
+ }
+}
+
+
/*
* Subroutine for ATExecAlterColumnType: remember that a statistics object
* needs to be rebuilt (which we might already know).
@@ -15507,6 +15528,17 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
add_exact_object_address(&obj, objects);
}
+ /* add dependencies for new generation expression */
+ forboth(oid_item, tab->changedGenerationExprOids,
+ def_item, tab->changedGenerationExprDefs)
+ {
+ Oid oldId = lfirst_oid(oid_item);
+
+ ATPostAlterTypeParse(oldId, tab->relid, InvalidOid,
+ (char *) lfirst(def_item),
+ wqueue, lockmode, tab->rewrite);
+ }
+
/* add dependencies for new statistics */
forboth(oid_item, tab->changedStatisticsOids,
def_item, tab->changedStatisticsDefs)
@@ -15582,6 +15614,29 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
* The objects will get recreated during subsequent passes over the work
* queue.
*/
+
+ /*
+ * We have already deleted the dependent objects; now remove these objects
+ * themselves to avoid deleting them twice.
+ */
+ list_free(tab->changedConstraintOids);
+ list_free(tab->changedConstraintDefs);
+ list_free(tab->changedIndexOids);
+ list_free(tab->changedIndexDefs);
+ list_free(tab->changedStatisticsOids);
+ list_free(tab->changedStatisticsDefs);
+ list_free(tab->changedGenerationExprOids);
+ list_free(tab->changedGenerationExprDefs);
+
+ tab->changedConstraintOids = NIL;
+ tab->changedConstraintDefs = NIL;
+ tab->changedIndexOids = NIL;
+ tab->changedIndexDefs = NIL;
+ tab->changedStatisticsOids = NIL;
+ tab->changedStatisticsDefs = NIL;
+ tab->changedGenerationExprOids = NIL;
+ tab->changedGenerationExprDefs = NIL;
+
}
/*
@@ -15743,6 +15798,17 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
else
Assert(con->contype == CONSTR_NOTNULL);
}
+ else if (cmd->subtype == AT_SetExpression)
+ {
+ /*
+ * The generation expression is rebuilt due to a data type
+ * change, but AT_PASS_ALTER_TYPE occurs earlier than
+ * AT_PASS_SET_EXPRESSION. Therefore, we need to prepend
+ * this command to the AT_PASS_SET_EXPRESSION pass.
+ */
+ tab->subcmds[AT_PASS_SET_EXPRESSION] =
+ lcons(cmd, tab->subcmds[AT_PASS_SET_EXPRESSION]);
+ }
else
elog(ERROR, "unexpected statement subtype: %d",
(int) cmd->subtype);
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 050eef97a4c..6f75733620e 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -25,6 +25,7 @@
#include "access/table.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_am.h"
+#include "catalog/pg_attrdef.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
@@ -367,6 +368,7 @@ static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags,
bool attrsOnly, bool missing_ok);
static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
int prettyFlags, bool missing_ok);
+static char *pg_get_generationdef_worker(Oid attrdefoid, bool fullCommand, int prettyFlags, bool missing_ok);
static text *pg_get_expr_worker(text *expr, Oid relid, int prettyFlags);
static int print_function_arguments(StringInfo buf, HeapTuple proctup,
bool print_table_args, bool print_defaults);
@@ -2185,6 +2187,112 @@ pg_get_constraintdef_command(Oid constraintId)
return pg_get_constraintdef_worker(constraintId, true, 0, false);
}
+/*
+ * Internal version for use by ALTER TABLE.
+ * Returns a palloc'd C string; no pretty-printing.
+ */
+char *
+pg_get_generationdef(Oid attrdefoid)
+{
+ return pg_get_generationdef_worker(attrdefoid, true, 0, false);
+}
+
+/*
+ * if fullCommand is true, return
+ * "ALTER TABLE %s ALTER COLUMN %s SET EXPRESSION AS ( %s ) ",
+ * otherwise return the text of the genereration expression
+ */
+static char *
+pg_get_generationdef_worker(Oid attrdefoid, bool fullCommand,
+ int prettyFlags, bool missing_ok)
+{
+ HeapTuple tup;
+ Form_pg_attrdef atdform;
+ StringInfoData buf;
+ SysScanDesc scandesc;
+ ScanKeyData scankey[1];
+ Relation attrdef;
+ Datum val;
+ text *result;
+ bool isnull;
+
+ Snapshot snapshot = RegisterSnapshot(GetTransactionSnapshot());
+ attrdef = table_open(AttrDefaultRelationId, AccessShareLock);
+
+ ScanKeyInit(&scankey[0],
+ Anum_pg_attrdef_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(attrdefoid));
+
+ scandesc = systable_beginscan(attrdef,
+ AttrDefaultOidIndexId,
+ true,
+ snapshot,
+ 1,
+ scankey);
+
+ /*
+ * We later use the tuple with SysCacheGetAttr() as if we had obtained it
+ * via SearchSysCache, which works fine.
+ */
+ tup = systable_getnext(scandesc);
+
+ UnregisterSnapshot(snapshot);
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (missing_ok)
+ {
+ systable_endscan(scandesc);
+ table_close(attrdef, AccessShareLock);
+ return NULL;
+ }
+ elog(ERROR, "could not find tuple for attribute defaults %u", attrdefoid);
+ }
+
+ atdform = (Form_pg_attrdef) GETSTRUCT(tup);
+
+ initStringInfo(&buf);
+
+ if (fullCommand)
+ {
+ HeapTuple tuple;
+ Form_pg_attribute attrtuple;
+
+ Assert(OidIsValid(atdform->adrelid));
+
+ tuple = SearchSysCacheAttNum(atdform->adrelid, atdform->adnum);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "column number %d of relation %u does not exist",
+ atdform->adnum, atdform->adrelid);
+
+ attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
+ appendStringInfo(&buf, "ALTER TABLE %s ALTER COLUMN %s SET EXPRESSION AS ( ",
+ generate_qualified_relation_name(atdform->adrelid),
+ quote_identifier(NameStr(attrtuple->attname)));
+
+ ReleaseSysCache(tuple);
+ }
+
+ val = fastgetattr(tup,
+ Anum_pg_attrdef_adbin,
+ RelationGetDescr(attrdef), &isnull);
+ if (isnull)
+ elog(ERROR, "null adbin for rel %u", atdform->adrelid);
+
+ result = pg_get_expr_worker(DatumGetTextPP(val), atdform->adrelid, 0);
+
+ appendStringInfo(&buf, "%s", text_to_cstring(result));
+
+ if (fullCommand)
+ appendStringInfoChar(&buf, ')');
+
+ systable_endscan(scandesc);
+ table_close(attrdef, AccessShareLock);
+
+ return buf.data;
+}
+
/*
* As of 9.4, we now use an MVCC snapshot for this.
*/
diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h
index 7ba7d887914..a1d629dccfd 100644
--- a/src/include/utils/ruleutils.h
+++ b/src/include/utils/ruleutils.h
@@ -53,5 +53,6 @@ extern char *generate_opclass_name(Oid opclass);
extern char *get_range_partbound_string(List *bound_datums);
extern char *pg_get_statisticsobjdef_string(Oid statextid);
+extern char *pg_get_generationdef(Oid attrdefoid);
#endif /* RULEUTILS_H */
diff --git a/src/test/regress/expected/generated_stored.out b/src/test/regress/expected/generated_stored.out
index adac2cedfb2..1f43f90fac1 100644
--- a/src/test/regress/expected/generated_stored.out
+++ b/src/test/regress/expected/generated_stored.out
@@ -1120,6 +1120,41 @@ SELECT * FROM gtest25 ORDER BY a;
Indexes:
"gtest25_pkey" PRIMARY KEY, btree (a)
+CREATE STATISTICS gtest25_stat ON ((a + 1) is not null or (b + 1) is not null) FROM gtest25;
+CREATE STATISTICS gtest25_1_stat ON ((b + 1) is not null) FROM gtest25;
+CREATE INDEX gtest25_a_b_idx ON gtest25((a+b));
+CREATE INDEX gtest25_b_idx ON gtest25((b));
+ALTER TABLE gtest25 ALTER COLUMN a SET DATA TYPE BIGINT USING (a);
+--change type and change generation expression together
+ALTER TABLE gtest25
+ ALTER COLUMN a SET DATA TYPE numeric USING (a + 1),
+ ALTER COLUMN b SET DATA TYPE BIGINT,
+ ALTER COLUMN b SET EXPRESSION AS (a * 4);
+\d gtest25
+ Table "generated_stored_tests.gtest25"
+ Column | Type | Collation | Nullable | Default
+--------+------------------+-----------+----------+------------------------------------------------------
+ a | numeric | | not null |
+ b | bigint | | | generated always as ((a * 4::numeric)) stored
+ c | integer | | | 42
+ x | integer | | | generated always as (c * 4) stored
+ d | double precision | | | 101
+ y | double precision | | | generated always as (d * 4::double precision) stored
+Indexes:
+ "gtest25_pkey" PRIMARY KEY, btree (a)
+ "gtest25_a_b_idx" btree ((a + b::numeric))
+ "gtest25_b_idx" btree (b)
+Statistics objects:
+ "generated_stored_tests.gtest25_1_stat" ON ((b + 1) IS NOT NULL) FROM gtest25
+ "generated_stored_tests.gtest25_stat" ON ((a + 1::numeric) IS NOT NULL OR (b + 1) IS NOT NULL) FROM gtest25
+
+SELECT * FROM gtest25 ORDER BY a;
+ a | b | c | x | d | y
+---+----+----+-----+-----+-----
+ 4 | 16 | 42 | 168 | 101 | 404
+ 5 | 20 | 42 | 168 | 101 | 404
+(2 rows)
+
-- ALTER TABLE ... ALTER COLUMN
CREATE TABLE gtest27 (
a int,
@@ -1128,14 +1163,16 @@ CREATE TABLE gtest27 (
);
INSERT INTO gtest27 (a, b) VALUES (3, 7), (4, 11);
ALTER TABLE gtest27 ALTER COLUMN a TYPE text; -- error
-ERROR: cannot alter type of a column used by a generated column
-DETAIL: Column "a" is used by generated column "x".
+ERROR: operator does not exist: text + integer
+DETAIL: No operator of that name accepts the given argument types.
+HINT: You might need to add explicit type casts.
+ALTER TABLE gtest27 ALTER COLUMN a TYPE int8; -- ok
ALTER TABLE gtest27 ALTER COLUMN x TYPE numeric;
\d gtest27
Table "generated_stored_tests.gtest27"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+--------------------------------------------
- a | integer | | |
+ a | bigint | | |
b | integer | | |
x | numeric | | | generated always as (((a + b) * 2)) stored
@@ -1171,16 +1208,14 @@ ALTER TABLE gtest27
-- Ideally you could just do this, but not today (and should x change type?):
ALTER TABLE gtest27
ALTER COLUMN a TYPE float8,
- ALTER COLUMN b TYPE float8; -- error
-ERROR: cannot alter type of a column used by a generated column
-DETAIL: Column "a" is used by generated column "x".
+ ALTER COLUMN b TYPE float8; -- ok
\d gtest27
- Table "generated_stored_tests.gtest27"
- Column | Type | Collation | Nullable | Default
---------+--------+-----------+----------+------------------------------------------
- a | bigint | | |
- b | bigint | | |
- x | bigint | | | generated always as ((a + b) * 2) stored
+ Table "generated_stored_tests.gtest27"
+ Column | Type | Collation | Nullable | Default
+--------+------------------+-----------+----------+--------------------------------------------------------------
+ a | double precision | | |
+ b | double precision | | |
+ x | bigint | | | generated always as (((a + b) * 2::double precision)) stored
SELECT * FROM gtest27;
a | b | x
diff --git a/src/test/regress/expected/generated_virtual.out b/src/test/regress/expected/generated_virtual.out
index c861bd36c5a..c748d4f85e9 100644
--- a/src/test/regress/expected/generated_virtual.out
+++ b/src/test/regress/expected/generated_virtual.out
@@ -1082,6 +1082,32 @@ SELECT * FROM gtest25 ORDER BY a;
Indexes:
"gtest25_pkey" PRIMARY KEY, btree (a)
+ALTER TABLE gtest25 ALTER COLUMN a SET DATA TYPE BIGINT USING (a);
+--change type and change generation expression together
+ALTER TABLE gtest25
+ ALTER COLUMN a SET DATA TYPE numeric USING (a + 1),
+ ALTER COLUMN b SET DATA TYPE BIGINT,
+ ALTER COLUMN b SET EXPRESSION AS (a * 4);
+\d gtest25
+ Table "generated_virtual_tests.gtest25"
+ Column | Type | Collation | Nullable | Default
+--------+------------------+-----------+----------+-----------------------------------------------
+ a | numeric | | not null |
+ b | bigint | | | generated always as ((a * 4::numeric))
+ c | integer | | | 42
+ x | integer | | | generated always as (c * 4)
+ d | double precision | | | 101
+ y | double precision | | | generated always as (d * 4::double precision)
+Indexes:
+ "gtest25_pkey" PRIMARY KEY, btree (a)
+
+SELECT * FROM gtest25 ORDER BY a;
+ a | b | c | x | d | y
+---+----+----+-----+-----+-----
+ 4 | 16 | 42 | 168 | 101 | 404
+ 5 | 20 | 42 | 168 | 101 | 404
+(2 rows)
+
-- ALTER TABLE ... ALTER COLUMN
CREATE TABLE gtest27 (
a int,
@@ -1090,14 +1116,16 @@ CREATE TABLE gtest27 (
);
INSERT INTO gtest27 (a, b) VALUES (3, 7), (4, 11);
ALTER TABLE gtest27 ALTER COLUMN a TYPE text; -- error
-ERROR: cannot alter type of a column used by a generated column
-DETAIL: Column "a" is used by generated column "x".
+ERROR: operator does not exist: text + integer
+DETAIL: No operator of that name accepts the given argument types.
+HINT: You might need to add explicit type casts.
+ALTER TABLE gtest27 ALTER COLUMN a TYPE int8; -- ok
ALTER TABLE gtest27 ALTER COLUMN x TYPE numeric;
\d gtest27
Table "generated_virtual_tests.gtest27"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+-------------------------------------
- a | integer | | |
+ a | bigint | | |
b | integer | | |
x | numeric | | | generated always as (((a + b) * 2))
@@ -1141,16 +1169,14 @@ ALTER TABLE gtest27
-- Ideally you could just do this, but not today (and should x change type?):
ALTER TABLE gtest27
ALTER COLUMN a TYPE float8,
- ALTER COLUMN b TYPE float8; -- error
-ERROR: cannot alter type of a column used by a generated column
-DETAIL: Column "a" is used by generated column "x".
+ ALTER COLUMN b TYPE float8; -- ok
\d gtest27
- Table "generated_virtual_tests.gtest27"
- Column | Type | Collation | Nullable | Default
---------+--------+-----------+----------+-----------------------------------
- a | bigint | | |
- b | bigint | | |
- x | bigint | | | generated always as ((a + b) * 2)
+ Table "generated_virtual_tests.gtest27"
+ Column | Type | Collation | Nullable | Default
+--------+------------------+-----------+----------+-------------------------------------------------------
+ a | double precision | | |
+ b | double precision | | |
+ x | bigint | | | generated always as (((a + b) * 2::double precision))
SELECT * FROM gtest27;
a | b | x
diff --git a/src/test/regress/sql/generated_stored.sql b/src/test/regress/sql/generated_stored.sql
index f56fde8d4e5..77a51edeeef 100644
--- a/src/test/regress/sql/generated_stored.sql
+++ b/src/test/regress/sql/generated_stored.sql
@@ -517,6 +517,21 @@ ALTER TABLE gtest25 ALTER COLUMN d SET DATA TYPE float8,
SELECT * FROM gtest25 ORDER BY a;
\d gtest25
+CREATE STATISTICS gtest25_stat ON ((a + 1) is not null or (b + 1) is not null) FROM gtest25;
+CREATE STATISTICS gtest25_1_stat ON ((b + 1) is not null) FROM gtest25;
+CREATE INDEX gtest25_a_b_idx ON gtest25((a+b));
+CREATE INDEX gtest25_b_idx ON gtest25((b));
+
+ALTER TABLE gtest25 ALTER COLUMN a SET DATA TYPE BIGINT USING (a);
+--change type and change generation expression together
+ALTER TABLE gtest25
+ ALTER COLUMN a SET DATA TYPE numeric USING (a + 1),
+ ALTER COLUMN b SET DATA TYPE BIGINT,
+ ALTER COLUMN b SET EXPRESSION AS (a * 4);
+\d gtest25
+
+SELECT * FROM gtest25 ORDER BY a;
+
-- ALTER TABLE ... ALTER COLUMN
CREATE TABLE gtest27 (
a int,
@@ -525,6 +540,7 @@ CREATE TABLE gtest27 (
);
INSERT INTO gtest27 (a, b) VALUES (3, 7), (4, 11);
ALTER TABLE gtest27 ALTER COLUMN a TYPE text; -- error
+ALTER TABLE gtest27 ALTER COLUMN a TYPE int8; -- ok
ALTER TABLE gtest27 ALTER COLUMN x TYPE numeric;
\d gtest27
SELECT * FROM gtest27;
@@ -540,7 +556,7 @@ ALTER TABLE gtest27
-- Ideally you could just do this, but not today (and should x change type?):
ALTER TABLE gtest27
ALTER COLUMN a TYPE float8,
- ALTER COLUMN b TYPE float8; -- error
+ ALTER COLUMN b TYPE float8; -- ok
\d gtest27
SELECT * FROM gtest27;
diff --git a/src/test/regress/sql/generated_virtual.sql b/src/test/regress/sql/generated_virtual.sql
index adfe88d74ae..b324a12bf08 100644
--- a/src/test/regress/sql/generated_virtual.sql
+++ b/src/test/regress/sql/generated_virtual.sql
@@ -560,6 +560,16 @@ ALTER TABLE gtest25 ALTER COLUMN d SET DATA TYPE float8,
SELECT * FROM gtest25 ORDER BY a;
\d gtest25
+ALTER TABLE gtest25 ALTER COLUMN a SET DATA TYPE BIGINT USING (a);
+--change type and change generation expression together
+ALTER TABLE gtest25
+ ALTER COLUMN a SET DATA TYPE numeric USING (a + 1),
+ ALTER COLUMN b SET DATA TYPE BIGINT,
+ ALTER COLUMN b SET EXPRESSION AS (a * 4);
+
+\d gtest25
+SELECT * FROM gtest25 ORDER BY a;
+
-- ALTER TABLE ... ALTER COLUMN
CREATE TABLE gtest27 (
a int,
@@ -568,6 +578,7 @@ CREATE TABLE gtest27 (
);
INSERT INTO gtest27 (a, b) VALUES (3, 7), (4, 11);
ALTER TABLE gtest27 ALTER COLUMN a TYPE text; -- error
+ALTER TABLE gtest27 ALTER COLUMN a TYPE int8; -- ok
ALTER TABLE gtest27 ALTER COLUMN x TYPE numeric;
\d gtest27
SELECT * FROM gtest27;
@@ -591,7 +602,7 @@ ALTER TABLE gtest27
-- Ideally you could just do this, but not today (and should x change type?):
ALTER TABLE gtest27
ALTER COLUMN a TYPE float8,
- ALTER COLUMN b TYPE float8; -- error
+ ALTER COLUMN b TYPE float8; -- ok
\d gtest27
SELECT * FROM gtest27;
--
2.34.1