Skip to content

Commit 6610411

Browse files
committed
Fix crash when columns have been added to the end of a view.
expandRTE() supposed that an RTE_SUBQUERY subquery must have exactly as many non-junk tlist items as the RTE has column aliases for it. This was true at the time the code was written, and is still true so far as parse analysis is concerned --- but when the function is used during planning, the subquery might have appeared through insertion of a view that now has more columns than it did when the outer query was parsed. This results in a core dump if, for instance, we have to expand a whole-row Var that references the subquery. To avoid crashing, we can either stop expanding the RTE when we run out of aliases, or invent new aliases for the added columns. While the latter might be more useful, the former is consistent with what expandRTE() does for composite-returning functions in the RTE_FUNCTION case, so it seems like we'd better do it that way. Per bug #14876 from Samuel Horwitz. This has been busted since commit ff1ea21 allowed views to acquire more columns, so back-patch to all supported branches. Discussion: https://postgr.es/m/20171026184035.1471.82810@wrigleys.postgresql.org
1 parent 376ac92 commit 6610411

File tree

3 files changed

+116
-1
lines changed

3 files changed

+116
-1
lines changed

src/backend/parser/parse_relation.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1828,9 +1828,19 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
18281828
varattno++;
18291829
Assert(varattno == te->resno);
18301830

1831+
/*
1832+
* In scenarios where columns have been added to a view
1833+
* since the outer query was originally parsed, there can
1834+
* be more items in the subquery tlist than the outer
1835+
* query expects. We should ignore such extra column(s)
1836+
* --- compare the behavior for composite-returning
1837+
* functions, in the RTE_FUNCTION case below.
1838+
*/
1839+
if (!aliasp_item)
1840+
break;
1841+
18311842
if (colnames)
18321843
{
1833-
/* Assume there is one alias per target item */
18341844
char *label = strVal(lfirst(aliasp_item));
18351845

18361846
*colnames = lappend(*colnames, makeString(pstrdup(label)));

src/test/regress/expected/alter_table.out

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2099,6 +2099,91 @@ Foreign-key constraints:
20992099
"check_fk_presence_2_id_fkey" FOREIGN KEY (id) REFERENCES check_fk_presence_1(id)
21002100

21012101
DROP TABLE check_fk_presence_1, check_fk_presence_2;
2102+
-- check column addition within a view (bug #14876)
2103+
create table at_base_table(id int, stuff text);
2104+
insert into at_base_table values (23, 'skidoo');
2105+
create view at_view_1 as select * from at_base_table bt;
2106+
create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1;
2107+
\d+ at_view_1
2108+
View "public.at_view_1"
2109+
Column | Type | Modifiers | Storage | Description
2110+
--------+---------+-----------+----------+-------------
2111+
id | integer | | plain |
2112+
stuff | text | | extended |
2113+
View definition:
2114+
SELECT bt.id,
2115+
bt.stuff
2116+
FROM at_base_table bt;
2117+
2118+
\d+ at_view_2
2119+
View "public.at_view_2"
2120+
Column | Type | Modifiers | Storage | Description
2121+
--------+---------+-----------+----------+-------------
2122+
id | integer | | plain |
2123+
stuff | text | | extended |
2124+
j | json | | extended |
2125+
View definition:
2126+
SELECT v1.id,
2127+
v1.stuff,
2128+
to_json(v1.*) AS j
2129+
FROM at_view_1 v1;
2130+
2131+
explain (verbose, costs off) select * from at_view_2;
2132+
QUERY PLAN
2133+
----------------------------------------------------------
2134+
Seq Scan on public.at_base_table bt
2135+
Output: bt.id, bt.stuff, to_json(ROW(bt.id, bt.stuff))
2136+
(2 rows)
2137+
2138+
select * from at_view_2;
2139+
id | stuff | j
2140+
----+--------+----------------------------
2141+
23 | skidoo | {"id":23,"stuff":"skidoo"}
2142+
(1 row)
2143+
2144+
create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt;
2145+
\d+ at_view_1
2146+
View "public.at_view_1"
2147+
Column | Type | Modifiers | Storage | Description
2148+
--------+---------+-----------+----------+-------------
2149+
id | integer | | plain |
2150+
stuff | text | | extended |
2151+
more | integer | | plain |
2152+
View definition:
2153+
SELECT bt.id,
2154+
bt.stuff,
2155+
2 + 2 AS more
2156+
FROM at_base_table bt;
2157+
2158+
\d+ at_view_2
2159+
View "public.at_view_2"
2160+
Column | Type | Modifiers | Storage | Description
2161+
--------+---------+-----------+----------+-------------
2162+
id | integer | | plain |
2163+
stuff | text | | extended |
2164+
j | json | | extended |
2165+
View definition:
2166+
SELECT v1.id,
2167+
v1.stuff,
2168+
to_json(v1.*) AS j
2169+
FROM at_view_1 v1;
2170+
2171+
explain (verbose, costs off) select * from at_view_2;
2172+
QUERY PLAN
2173+
----------------------------------------------------------------
2174+
Seq Scan on public.at_base_table bt
2175+
Output: bt.id, bt.stuff, to_json(ROW(bt.id, bt.stuff, NULL))
2176+
(2 rows)
2177+
2178+
select * from at_view_2;
2179+
id | stuff | j
2180+
----+--------+----------------------------------------
2181+
23 | skidoo | {"id":23,"stuff":"skidoo","more":null}
2182+
(1 row)
2183+
2184+
drop view at_view_2;
2185+
drop view at_view_1;
2186+
drop table at_base_table;
21022187
--
21032188
-- lock levels
21042189
--

src/test/regress/sql/alter_table.sql

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,6 +1365,26 @@ ROLLBACK;
13651365
\d check_fk_presence_2
13661366
DROP TABLE check_fk_presence_1, check_fk_presence_2;
13671367

1368+
-- check column addition within a view (bug #14876)
1369+
create table at_base_table(id int, stuff text);
1370+
insert into at_base_table values (23, 'skidoo');
1371+
create view at_view_1 as select * from at_base_table bt;
1372+
create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1;
1373+
\d+ at_view_1
1374+
\d+ at_view_2
1375+
explain (verbose, costs off) select * from at_view_2;
1376+
select * from at_view_2;
1377+
1378+
create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt;
1379+
\d+ at_view_1
1380+
\d+ at_view_2
1381+
explain (verbose, costs off) select * from at_view_2;
1382+
select * from at_view_2;
1383+
1384+
drop view at_view_2;
1385+
drop view at_view_1;
1386+
drop table at_base_table;
1387+
13681388
--
13691389
-- lock levels
13701390
--

0 commit comments

Comments
 (0)