aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/optimizer/plan/setrefs.c6
-rw-r--r--src/backend/parser/analyze.c33
-rw-r--r--src/backend/replication/logical/worker.c5
-rw-r--r--src/backend/rewrite/rewriteHandler.c41
-rw-r--r--src/include/nodes/parsenodes.h16
-rw-r--r--src/include/parser/analyze.h2
-rw-r--r--src/include/rewrite/rewriteHandler.h3
-rw-r--r--src/test/regress/expected/updatable_views.out35
-rw-r--r--src/test/regress/sql/updatable_views.sql21
9 files changed, 115 insertions, 47 deletions
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 329ebd5f287..95a1c43c969 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -387,9 +387,9 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob)
* In the flat rangetable, we zero out substructure pointers that are not
* needed by the executor; this reduces the storage space and copying cost
* for cached plans. We keep only the ctename, alias and eref Alias fields,
- * which are needed by EXPLAIN, and the selectedCols, insertedCols and
- * updatedCols bitmaps, which are needed for executor-startup permissions
- * checking and for trigger event checking.
+ * which are needed by EXPLAIN, and the selectedCols, insertedCols,
+ * updatedCols, and extraUpdatedCols bitmaps, which are needed for
+ * executor-startup permissions checking and for trigger event checking.
*/
static void
add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte)
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index ecd6a8bae7d..8462e915584 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -2287,7 +2287,6 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist)
RangeTblEntry *target_rte;
ListCell *orig_tl;
ListCell *tl;
- TupleDesc tupdesc = pstate->p_target_relation->rd_att;
tlist = transformTargetList(pstate, origTlist,
EXPR_KIND_UPDATE_SOURCE);
@@ -2346,42 +2345,10 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist)
if (orig_tl != NULL)
elog(ERROR, "UPDATE target count mismatch --- internal error");
- fill_extraUpdatedCols(target_rte, tupdesc);
-
return tlist;
}
/*
- * Record in extraUpdatedCols generated columns referencing updated base
- * columns.
- */
-void
-fill_extraUpdatedCols(RangeTblEntry *target_rte, TupleDesc tupdesc)
-{
- if (tupdesc->constr &&
- tupdesc->constr->has_generated_stored)
- {
- for (int i = 0; i < tupdesc->constr->num_defval; i++)
- {
- AttrDefault defval = tupdesc->constr->defval[i];
- Node *expr;
- Bitmapset *attrs_used = NULL;
-
- /* skip if not generated column */
- if (!TupleDescAttr(tupdesc, defval.adnum - 1)->attgenerated)
- continue;
-
- expr = stringToNode(defval.adbin);
- pull_varattnos(expr, 1, &attrs_used);
-
- if (bms_overlap(target_rte->updatedCols, attrs_used))
- target_rte->extraUpdatedCols = bms_add_member(target_rte->extraUpdatedCols,
- defval.adnum - FirstLowInvalidHeapAttributeNumber);
- }
- }
-}
-
-/*
* transformReturningList -
* handle a RETURNING clause in INSERT/UPDATE/DELETE
*/
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 1bdf763d585..ce8f4106aa1 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -42,8 +42,6 @@
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "optimizer/optimizer.h"
-#include "parser/analyze.h"
-#include "parser/parse_relation.h"
#include "pgstat.h"
#include "postmaster/bgworker.h"
#include "postmaster/postmaster.h"
@@ -744,7 +742,8 @@ apply_handle_update(StringInfo s)
}
}
- fill_extraUpdatedCols(target_rte, RelationGetDescr(rel->localrel));
+ /* Also populate extraUpdatedCols, in case we have generated columns */
+ fill_extraUpdatedCols(target_rte, rel->localrel);
PushActiveSnapshot(GetTransactionSnapshot());
ExecOpenIndices(estate->es_result_relation_info, false);
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index bc49f23cc8f..98750113697 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -30,6 +30,7 @@
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
+#include "optimizer/optimizer.h"
#include "parser/analyze.h"
#include "parser/parse_coerce.h"
#include "parser/parse_relation.h"
@@ -1510,6 +1511,42 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
}
}
+/*
+ * Record in target_rte->extraUpdatedCols the indexes of any generated columns
+ * that depend on any columns mentioned in target_rte->updatedCols.
+ */
+void
+fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation)
+{
+ TupleDesc tupdesc = RelationGetDescr(target_relation);
+ TupleConstr *constr = tupdesc->constr;
+
+ target_rte->extraUpdatedCols = NULL;
+
+ if (constr && constr->has_generated_stored)
+ {
+ for (int i = 0; i < constr->num_defval; i++)
+ {
+ AttrDefault *defval = &constr->defval[i];
+ Node *expr;
+ Bitmapset *attrs_used = NULL;
+
+ /* skip if not generated column */
+ if (!TupleDescAttr(tupdesc, defval->adnum - 1)->attgenerated)
+ continue;
+
+ /* identify columns this generated column depends on */
+ expr = stringToNode(defval->adbin);
+ pull_varattnos(expr, 1, &attrs_used);
+
+ if (bms_overlap(target_rte->updatedCols, attrs_used))
+ target_rte->extraUpdatedCols =
+ bms_add_member(target_rte->extraUpdatedCols,
+ defval->adnum - FirstLowInvalidHeapAttributeNumber);
+ }
+ }
+}
+
/*
* matchLocks -
@@ -1641,6 +1678,7 @@ ApplyRetrieveRule(Query *parsetree,
rte->selectedCols = NULL;
rte->insertedCols = NULL;
rte->updatedCols = NULL;
+ rte->extraUpdatedCols = NULL;
/*
* For the most part, Vars referencing the view should remain as
@@ -3617,6 +3655,9 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
parsetree->override,
rt_entry_relation,
parsetree->resultRelation);
+
+ /* Also populate extraUpdatedCols (for generated columns) */
+ fill_extraUpdatedCols(rt_entry, rt_entry_relation);
}
else if (event == CMD_DELETE)
{
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 788e276227c..439dfea29ad 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -937,12 +937,16 @@ typedef struct PartitionCmd
*
* updatedCols is also used in some other places, for example, to determine
* which triggers to fire and in FDWs to know which changed columns they
- * need to ship off. Generated columns that are caused to be updated by an
- * update to a base column are collected in extraUpdatedCols. This is not
- * considered for permission checking, but it is useful in those places
- * that want to know the full set of columns being updated as opposed to
- * only the ones the user explicitly mentioned in the query. (There is
- * currently no need for an extraInsertedCols, but it could exist.)
+ * need to ship off.
+ *
+ * Generated columns that are caused to be updated by an update to a base
+ * column are listed in extraUpdatedCols. This is not considered for
+ * permission checking, but it is useful in those places that want to know
+ * the full set of columns being updated as opposed to only the ones the
+ * user explicitly mentioned in the query. (There is currently no need for
+ * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols
+ * is populated during query rewrite, NOT in the parser, since generated
+ * columns could be added after a rule has been parsed and stored.
*
* securityQuals is a list of security barrier quals (boolean expressions),
* to be tested in the listed order before returning a row from the
diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h
index b3bad971640..cb1d96bc35d 100644
--- a/src/include/parser/analyze.h
+++ b/src/include/parser/analyze.h
@@ -46,6 +46,4 @@ extern void applyLockingClause(Query *qry, Index rtindex,
extern List *BuildOnConflictExcludedTargetlist(Relation targetrel,
Index exclRelIndex);
-extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, TupleDesc tupdesc);
-
#endif /* ANALYZE_H */
diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h
index 77b2ffa9621..ef01740fbd3 100644
--- a/src/include/rewrite/rewriteHandler.h
+++ b/src/include/rewrite/rewriteHandler.h
@@ -26,6 +26,9 @@ extern Node *build_column_default(Relation rel, int attrno);
extern void rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
Relation target_relation);
+extern void fill_extraUpdatedCols(RangeTblEntry *target_rte,
+ Relation target_relation);
+
extern Query *get_view_query(Relation view);
extern const char *view_query_is_auto_updatable(Query *viewquery,
bool check_cols);
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index a8833eb4fb6..0d16e68084a 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -1467,6 +1467,41 @@ NOTICE: drop cascades to 3 other objects
DETAIL: drop cascades to view rw_view1
drop cascades to view rw_view2
drop cascades to view rw_view3
+-- view on table with GENERATED columns
+CREATE TABLE base_tbl (id int, idplus1 int GENERATED ALWAYS AS (id + 1) STORED);
+CREATE VIEW rw_view1 AS SELECT * FROM base_tbl;
+INSERT INTO base_tbl (id) VALUES (1);
+INSERT INTO rw_view1 (id) VALUES (2);
+INSERT INTO base_tbl (id, idplus1) VALUES (3, DEFAULT);
+INSERT INTO rw_view1 (id, idplus1) VALUES (4, DEFAULT);
+INSERT INTO base_tbl (id, idplus1) VALUES (5, 6); -- error
+ERROR: cannot insert into column "idplus1"
+DETAIL: Column "idplus1" is a generated column.
+INSERT INTO rw_view1 (id, idplus1) VALUES (6, 7); -- error
+ERROR: cannot insert into column "idplus1"
+DETAIL: Column "idplus1" is a generated column.
+SELECT * FROM base_tbl;
+ id | idplus1
+----+---------
+ 1 | 2
+ 2 | 3
+ 3 | 4
+ 4 | 5
+(4 rows)
+
+UPDATE base_tbl SET id = 2000 WHERE id = 2;
+UPDATE rw_view1 SET id = 3000 WHERE id = 3;
+SELECT * FROM base_tbl;
+ id | idplus1
+------+---------
+ 1 | 2
+ 4 | 5
+ 2000 | 2001
+ 3000 | 3001
+(4 rows)
+
+DROP TABLE base_tbl CASCADE;
+NOTICE: drop cascades to view rw_view1
-- inheritance tests
CREATE TABLE base_tbl_parent (a int);
CREATE TABLE base_tbl_child (CHECK (a > 0)) INHERITS (base_tbl_parent);
diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql
index 64f23d0902e..09328e582b2 100644
--- a/src/test/regress/sql/updatable_views.sql
+++ b/src/test/regress/sql/updatable_views.sql
@@ -697,6 +697,27 @@ SELECT events & 4 != 0 AS upd,
DROP TABLE base_tbl CASCADE;
+-- view on table with GENERATED columns
+
+CREATE TABLE base_tbl (id int, idplus1 int GENERATED ALWAYS AS (id + 1) STORED);
+CREATE VIEW rw_view1 AS SELECT * FROM base_tbl;
+
+INSERT INTO base_tbl (id) VALUES (1);
+INSERT INTO rw_view1 (id) VALUES (2);
+INSERT INTO base_tbl (id, idplus1) VALUES (3, DEFAULT);
+INSERT INTO rw_view1 (id, idplus1) VALUES (4, DEFAULT);
+INSERT INTO base_tbl (id, idplus1) VALUES (5, 6); -- error
+INSERT INTO rw_view1 (id, idplus1) VALUES (6, 7); -- error
+
+SELECT * FROM base_tbl;
+
+UPDATE base_tbl SET id = 2000 WHERE id = 2;
+UPDATE rw_view1 SET id = 3000 WHERE id = 3;
+
+SELECT * FROM base_tbl;
+
+DROP TABLE base_tbl CASCADE;
+
-- inheritance tests
CREATE TABLE base_tbl_parent (a int);