hi.

CREATE TABLE gtest1 (a int, b int GENERATED ALWAYS AS (a * 2) STORED);
CREATE VIEW gtest1v AS SELECT * FROM gtest1;
ALTER VIEW gtest1v ALTER COLUMN b SET DEFAULT 100;

INSERT INTO gtest1v VALUES (8, DEFAULT) returning *;
ERROR:  cannot insert a non-DEFAULT value into column "b"
DETAIL:  Column "b" is a generated column.

we can make
ALTER VIEW gtest1v ALTER COLUMN b SET DEFAULT 100;
error out,
then
INSERT INTO gtest1v VALUES (8, DEFAULT) returning *;
will work just fine.

obviously,
INSERT INTO gtest1v VALUES (8, 1) returning *;
will fail.


we can do this by in ATExecColumnDefault,
checking if
* gtest1v is updatable view or not
* column b is an updatable column or not
* column b on view corresponding base relation's column is a generated
column or not.

if all these conditions meet then, we error out saying
``cannot alter column \"%s\" on updateable view ``.


what do you think?
From 8e973bcd093ce25a5728f10aa9e73eb838406758 Mon Sep 17 00:00:00 2001
From: jian he <jian.universal...@gmail.com>
Date: Fri, 11 Apr 2025 15:41:10 +0800
Subject: [PATCH v1 1/1] disallow set default when baserel column is generated

disallow change updatable view column default expression when the corresponding
base column is generated column.

discussion: https://postgr.es/m/
---
 src/backend/commands/tablecmds.c              | 54 +++++++++++++++++++
 src/backend/rewrite/rewriteHandler.c          |  2 +-
 src/include/rewrite/rewriteHandler.h          |  4 ++
 .../regress/expected/generated_stored.out     | 29 +++++-----
 .../regress/expected/generated_virtual.out    | 29 +++++-----
 src/test/regress/sql/generated_stored.sql     |  6 +--
 src/test/regress/sql/generated_virtual.sql    |  6 +--
 7 files changed, 95 insertions(+), 35 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 686f1850cab..5548b629a0c 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -81,6 +81,7 @@
 #include "parser/parse_relation.h"
 #include "parser/parse_type.h"
 #include "parser/parse_utilcmd.h"
+#include "parser/parsetree.h"
 #include "parser/parser.h"
 #include "partitioning/partbounds.h"
 #include "partitioning/partdesc.h"
@@ -8156,6 +8157,59 @@ ATExecColumnDefault(Relation rel, const char *colName,
 				 (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
 				  errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
 
+
+	/* Check if this is an automatically updatable view */
+	if (rel->rd_rel->relkind == RELKIND_VIEW && newDefault != NULL)
+	{
+		Query	   *viewquery = get_view_query(rel);
+
+		if (view_query_is_auto_updatable(viewquery, true) == NULL)
+		{
+			Bitmapset  *set_col	= NULL;
+
+			set_col = bms_add_member(set_col,
+									 attnum - FirstLowInvalidHeapAttributeNumber);
+
+			if (view_cols_are_auto_updatable(viewquery, set_col, NULL, NULL) == NULL)
+			{
+				RangeTblRef *rtr;
+				RangeTblEntry *base_rte;
+				Relation	base_rel;
+				TupleDesc	rel_tupdesc;
+				TargetEntry *tle;
+				AttrNumber	attno;
+
+				rtr = (RangeTblRef *) linitial(viewquery->jointree->fromlist);
+				base_rte = rt_fetch(rtr->rtindex, viewquery->rtable);
+				Assert(base_rte->rtekind == RTE_RELATION);
+
+				base_rel = table_open(base_rte->relid, AccessShareLock);
+				rel_tupdesc = RelationGetDescr(base_rel);
+
+
+				tle = (TargetEntry *) list_nth(viewquery->targetList, attnum - 1);
+				Assert(!tle->resjunk);
+				Assert(IsA(tle->expr, Var));
+
+				attno = ((Var *) tle->expr)->varattno;
+
+				if (TupleDescAttr(rel_tupdesc, attno - 1)->attgenerated)
+				{
+					Form_pg_attribute att = TupleDescAttr(tupdesc, attno - 1);
+
+					ereport(ERROR,
+							errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+							errmsg("cannot alter column \"%s\" default expression on view \"%s\"",
+									colName, RelationGetRelationName(rel)),
+							errdetail("Column \"%s\" on base relation \"%s\" is a generated column",
+									  NameStr(att->attname),
+									  RelationGetRelationName(base_rel)));
+				}
+				table_close(base_rel, AccessShareLock);
+			}
+		}
+	}
+
 	/*
 	 * Remove any old default for the column.  We use RESTRICT here for
 	 * safety, but at present we do not expect anything to depend on the
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index f0bce5f9ed9..5a3a9ed0c94 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -2776,7 +2776,7 @@ view_query_is_auto_updatable(Query *viewquery, bool check_cols)
  * We do not check whether the referenced columns of the base relation are
  * updatable.
  */
-static const char *
+const char *
 view_cols_are_auto_updatable(Query *viewquery,
 							 Bitmapset *required_cols,
 							 Bitmapset **updatable_cols,
diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h
index 99cab1a3bfa..6a4cb14d150 100644
--- a/src/include/rewrite/rewriteHandler.h
+++ b/src/include/rewrite/rewriteHandler.h
@@ -29,6 +29,10 @@ extern bool view_has_instead_trigger(Relation view, CmdType event,
 									 List *mergeActionList);
 extern const char *view_query_is_auto_updatable(Query *viewquery,
 												bool check_cols);
+extern const char *view_cols_are_auto_updatable(Query *viewquery,
+												Bitmapset *required_cols,
+												Bitmapset **updatable_cols,
+												char **non_updatable_col);
 extern int	relation_is_updatable(Oid reloid,
 								  List *outer_reloids,
 								  bool include_triggers,
diff --git a/src/test/regress/expected/generated_stored.out b/src/test/regress/expected/generated_stored.out
index ffa844ca903..4b8f242357a 100644
--- a/src/test/regress/expected/generated_stored.out
+++ b/src/test/regress/expected/generated_stored.out
@@ -286,21 +286,22 @@ INSERT INTO gtest1v VALUES (6, 66), (7, DEFAULT);  -- error
 ERROR:  cannot insert a non-DEFAULT value into column "b"
 DETAIL:  Column "b" is a generated column.
 INSERT INTO gtest1v VALUES (6, DEFAULT), (7, DEFAULT);  -- ok
-ALTER VIEW gtest1v ALTER COLUMN b SET DEFAULT 100;
-INSERT INTO gtest1v VALUES (8, DEFAULT);  -- error
-ERROR:  cannot insert a non-DEFAULT value into column "b"
-DETAIL:  Column "b" is a generated column.
-INSERT INTO gtest1v VALUES (8, DEFAULT), (9, DEFAULT);  -- error
-ERROR:  cannot insert a non-DEFAULT value into column "b"
-DETAIL:  Column "b" is a generated column.
+ALTER VIEW gtest1v ALTER COLUMN b SET DEFAULT 100; --error
+ERROR:  cannot alter column "b" default expression on view "gtest1v"
+DETAIL:  Column "b" on base relation "gtest1" is a generated column
+INSERT INTO gtest1v VALUES (8, DEFAULT);  -- ok
+INSERT INTO gtest1v VALUES (9, DEFAULT), (10, DEFAULT);  -- ok
 SELECT * FROM gtest1v;
- a | b  
----+----
- 3 |  6
- 5 | 10
- 6 | 12
- 7 | 14
-(4 rows)
+ a  | b  
+----+----
+  3 |  6
+  5 | 10
+  6 | 12
+  7 | 14
+  8 | 16
+  9 | 18
+ 10 | 20
+(7 rows)
 
 DELETE FROM gtest1v WHERE a >= 5;
 DROP VIEW gtest1v;
diff --git a/src/test/regress/expected/generated_virtual.out b/src/test/regress/expected/generated_virtual.out
index 26bbe1e9c31..1fd07d53f22 100644
--- a/src/test/regress/expected/generated_virtual.out
+++ b/src/test/regress/expected/generated_virtual.out
@@ -280,21 +280,22 @@ INSERT INTO gtest1v VALUES (6, 66), (7, DEFAULT);  -- error
 ERROR:  cannot insert a non-DEFAULT value into column "b"
 DETAIL:  Column "b" is a generated column.
 INSERT INTO gtest1v VALUES (6, DEFAULT), (7, DEFAULT);  -- ok
-ALTER VIEW gtest1v ALTER COLUMN b SET DEFAULT 100;
-INSERT INTO gtest1v VALUES (8, DEFAULT);  -- error
-ERROR:  cannot insert a non-DEFAULT value into column "b"
-DETAIL:  Column "b" is a generated column.
-INSERT INTO gtest1v VALUES (8, DEFAULT), (9, DEFAULT);  -- error
-ERROR:  cannot insert a non-DEFAULT value into column "b"
-DETAIL:  Column "b" is a generated column.
+ALTER VIEW gtest1v ALTER COLUMN b SET DEFAULT 100; --error
+ERROR:  cannot alter column "b" default expression on view "gtest1v"
+DETAIL:  Column "b" on base relation "gtest1" is a generated column
+INSERT INTO gtest1v VALUES (8, DEFAULT);  -- ok
+INSERT INTO gtest1v VALUES (9, DEFAULT), (10, DEFAULT);  -- ok
 SELECT * FROM gtest1v;
- a | b  
----+----
- 3 |  6
- 5 | 10
- 6 | 12
- 7 | 14
-(4 rows)
+ a  | b  
+----+----
+  3 |  6
+  5 | 10
+  6 | 12
+  7 | 14
+  8 | 16
+  9 | 18
+ 10 | 20
+(7 rows)
 
 DELETE FROM gtest1v WHERE a >= 5;
 DROP VIEW gtest1v;
diff --git a/src/test/regress/sql/generated_stored.sql b/src/test/regress/sql/generated_stored.sql
index ba8ae62dea0..6ee9db2df8f 100644
--- a/src/test/regress/sql/generated_stored.sql
+++ b/src/test/regress/sql/generated_stored.sql
@@ -123,9 +123,9 @@ INSERT INTO gtest1v VALUES (6, DEFAULT), (7, 77);  -- error
 INSERT INTO gtest1v VALUES (6, 66), (7, DEFAULT);  -- error
 INSERT INTO gtest1v VALUES (6, DEFAULT), (7, DEFAULT);  -- ok
 
-ALTER VIEW gtest1v ALTER COLUMN b SET DEFAULT 100;
-INSERT INTO gtest1v VALUES (8, DEFAULT);  -- error
-INSERT INTO gtest1v VALUES (8, DEFAULT), (9, DEFAULT);  -- error
+ALTER VIEW gtest1v ALTER COLUMN b SET DEFAULT 100; --error
+INSERT INTO gtest1v VALUES (8, DEFAULT);  -- ok
+INSERT INTO gtest1v VALUES (9, DEFAULT), (10, DEFAULT);  -- ok
 
 SELECT * FROM gtest1v;
 DELETE FROM gtest1v WHERE a >= 5;
diff --git a/src/test/regress/sql/generated_virtual.sql b/src/test/regress/sql/generated_virtual.sql
index 13cfbd76859..d095beb1201 100644
--- a/src/test/regress/sql/generated_virtual.sql
+++ b/src/test/regress/sql/generated_virtual.sql
@@ -123,9 +123,9 @@ INSERT INTO gtest1v VALUES (6, DEFAULT), (7, 77);  -- error
 INSERT INTO gtest1v VALUES (6, 66), (7, DEFAULT);  -- error
 INSERT INTO gtest1v VALUES (6, DEFAULT), (7, DEFAULT);  -- ok
 
-ALTER VIEW gtest1v ALTER COLUMN b SET DEFAULT 100;
-INSERT INTO gtest1v VALUES (8, DEFAULT);  -- error
-INSERT INTO gtest1v VALUES (8, DEFAULT), (9, DEFAULT);  -- error
+ALTER VIEW gtest1v ALTER COLUMN b SET DEFAULT 100; --error
+INSERT INTO gtest1v VALUES (8, DEFAULT);  -- ok
+INSERT INTO gtest1v VALUES (9, DEFAULT), (10, DEFAULT);  -- ok
 
 SELECT * FROM gtest1v;
 DELETE FROM gtest1v WHERE a >= 5;
-- 
2.34.1

Reply via email to