aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/parser/parse_relation.c17
-rw-r--r--src/backend/rewrite/rewriteHandler.c16
-rw-r--r--src/test/regress/expected/alter_table.out43
-rw-r--r--src/test/regress/sql/alter_table.sql20
4 files changed, 82 insertions, 14 deletions
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 713ec0e521d..4193a03ebba 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -2658,12 +2658,17 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
Assert(varattno == te->resno);
/*
- * In scenarios where columns have been added to a view
- * since the outer query was originally parsed, there can
- * be more items in the subquery tlist than the outer
- * query expects. We should ignore such extra column(s)
- * --- compare the behavior for composite-returning
- * functions, in the RTE_FUNCTION case below.
+ * In a just-parsed subquery RTE, rte->eref->colnames
+ * should always have exactly as many entries as the
+ * subquery has non-junk output columns. However, if the
+ * subquery RTE was created by expansion of a view,
+ * perhaps the subquery tlist could now have more entries
+ * than existed when the outer query was parsed. Such
+ * cases should now be prevented because ApplyRetrieveRule
+ * will extend the colnames list to match. But out of
+ * caution, we'll keep the code like this in the back
+ * branches: just ignore any columns that lack colnames
+ * entries.
*/
if (!aliasp_item)
break;
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 42a6cd465c1..fa506f96112 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -26,6 +26,7 @@
#include "catalog/dependency.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
+#include "executor/executor.h"
#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
@@ -1720,6 +1721,7 @@ ApplyRetrieveRule(Query *parsetree,
RangeTblEntry *rte,
*subrte;
RowMarkClause *rc;
+ int numCols;
if (list_length(rule->actions) != 1)
elog(ERROR, "expected just one rule action");
@@ -1879,6 +1881,20 @@ ApplyRetrieveRule(Query *parsetree,
rte->updatedCols = NULL;
rte->extraUpdatedCols = NULL;
+ /*
+ * Since we allow CREATE OR REPLACE VIEW to add columns to a view, the
+ * rule_action might emit more columns than we expected when the current
+ * query was parsed. Various places expect rte->eref->colnames to be
+ * consistent with the non-junk output columns of the subquery, so patch
+ * things up if necessary by adding some dummy column names.
+ */
+ numCols = ExecCleanTargetListLength(rule_action->targetList);
+ while (list_length(rte->eref->colnames) < numCols)
+ {
+ rte->eref->colnames = lappend(rte->eref->colnames,
+ makeString(pstrdup("?column?")));
+ }
+
return parsetree;
}
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 2cd9aa4a64e..b65389f60c9 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2549,22 +2549,51 @@ View definition:
FROM at_view_1 v1;
explain (verbose, costs off) select * from at_view_2;
- QUERY PLAN
-----------------------------------------------------------------
+ QUERY PLAN
+-------------------------------------------------------------
Seq Scan on public.at_base_table bt
- Output: bt.id, bt.stuff, to_json(ROW(bt.id, bt.stuff, NULL))
+ Output: bt.id, bt.stuff, to_json(ROW(bt.id, bt.stuff, 4))
(2 rows)
select * from at_view_2;
- id | stuff | j
-----+--------+----------------------------------------
- 23 | skidoo | {"id":23,"stuff":"skidoo","more":null}
+ id | stuff | j
+----+--------+-------------------------------------
+ 23 | skidoo | {"id":23,"stuff":"skidoo","more":4}
(1 row)
drop view at_view_2;
drop view at_view_1;
drop table at_base_table;
--- check adding a column not iself requiring a rewrite, together with
+-- related case (bug #17811)
+begin;
+create temp table t1 as select * from int8_tbl;
+create temp view v1 as select 1::int8 as q1;
+create temp view v2 as select * from v1;
+create or replace temp view v1 with (security_barrier = true)
+ as select * from t1;
+create temp table log (q1 int8, q2 int8);
+create rule v1_upd_rule as on update to v1
+ do also insert into log values (new.*);
+update v2 set q1 = q1 + 1 where q1 = 123;
+select * from t1;
+ q1 | q2
+------------------+-------------------
+ 4567890123456789 | 123
+ 4567890123456789 | 4567890123456789
+ 4567890123456789 | -4567890123456789
+ 124 | 456
+ 124 | 4567890123456789
+(5 rows)
+
+select * from log;
+ q1 | q2
+-----+------------------
+ 124 | 456
+ 124 | 4567890123456789
+(2 rows)
+
+rollback;
+-- check adding a column not itself requiring a rewrite, together with
-- a column requiring a default (bug #16038)
-- ensure that rewrites aren't silently optimized away, removing the
-- value of the test
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 4e3849da005..e848af432a0 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -1636,7 +1636,25 @@ drop view at_view_2;
drop view at_view_1;
drop table at_base_table;
--- check adding a column not iself requiring a rewrite, together with
+-- related case (bug #17811)
+begin;
+create temp table t1 as select * from int8_tbl;
+create temp view v1 as select 1::int8 as q1;
+create temp view v2 as select * from v1;
+create or replace temp view v1 with (security_barrier = true)
+ as select * from t1;
+
+create temp table log (q1 int8, q2 int8);
+create rule v1_upd_rule as on update to v1
+ do also insert into log values (new.*);
+
+update v2 set q1 = q1 + 1 where q1 = 123;
+
+select * from t1;
+select * from log;
+rollback;
+
+-- check adding a column not itself requiring a rewrite, together with
-- a column requiring a default (bug #16038)
-- ensure that rewrites aren't silently optimized away, removing the