Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 0b292be

Browse files
committed
Close old gap in dependency checks for functions returning composite.
The dependency logic failed to register a column-level dependency when a view or rule contains a reference to a specific column of the result of a function-returning-composite. That meant you could drop the column from the composite type, causing trouble for future executions of the view. We've known about this for years, but never summoned the energy to actually fix it, instead installing various low-level defenses to prevent crashing on references to dropped columns. We had to do that to plug the hole in stable branches, where there might be pre-existing broken references; but let's fix the root cause today. To do that, add some logic (borrowed from get_rte_attribute_is_dropped) to find_expr_references_walker, to check whether a Var referencing an RTE_FUNCTION RTE is referencing a column of a composite type, and if so add the proper dependency. However ... it seems mighty unwise to remove said low-level defenses, since there could be other bugs now or in the future that allow reaching them. By the same token, letting those defenses go untested seems unwise. Hence, rather than just dropping the associated test cases, hack them to continue working by the expedient of manually dropping the pg_depend entries that this fix installs. Back-patch into v15. I don't want to risk changing this behavior in stable branches, but it seems not too late for v15. (Since we have already forced initdb for beta3, we can be sure that all production v15 installations will have these added dependencies.) Discussion: https://postgr.es/m/182492.1658431155@sss.pgh.pa.us
1 parent 90474c1 commit 0b292be

File tree

6 files changed

+267
-7
lines changed

6 files changed

+267
-7
lines changed

src/backend/catalog/dependency.c

+68
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
#include "commands/sequence.h"
7575
#include "commands/trigger.h"
7676
#include "commands/typecmds.h"
77+
#include "funcapi.h"
7778
#include "nodes/nodeFuncs.h"
7879
#include "parser/parsetree.h"
7980
#include "rewrite/rewriteRemove.h"
@@ -205,6 +206,8 @@ static void deleteOneObject(const ObjectAddress *object,
205206
static void doDeletion(const ObjectAddress *object, int flags);
206207
static bool find_expr_references_walker(Node *node,
207208
find_expr_references_context *context);
209+
static void process_function_rte_ref(RangeTblEntry *rte, AttrNumber attnum,
210+
find_expr_references_context *context);
208211
static void eliminate_duplicate_dependencies(ObjectAddresses *addrs);
209212
static int object_address_comparator(const void *a, const void *b);
210213
static void add_object_address(ObjectClass oclass, Oid objectId, int32 subId,
@@ -1768,6 +1771,12 @@ find_expr_references_walker(Node *node,
17681771
add_object_address(OCLASS_CLASS, rte->relid, var->varattno,
17691772
context->addrs);
17701773
}
1774+
else if (rte->rtekind == RTE_FUNCTION)
1775+
{
1776+
/* Might need to add a dependency on a composite type's column */
1777+
/* (done out of line, because it's a bit bulky) */
1778+
process_function_rte_ref(rte, var->varattno, context);
1779+
}
17711780

17721781
/*
17731782
* Vars referencing other RTE types require no additional work. In
@@ -2342,6 +2351,65 @@ find_expr_references_walker(Node *node,
23422351
(void *) context);
23432352
}
23442353

2354+
/*
2355+
* find_expr_references_walker subroutine: handle a Var reference
2356+
* to an RTE_FUNCTION RTE
2357+
*/
2358+
static void
2359+
process_function_rte_ref(RangeTblEntry *rte, AttrNumber attnum,
2360+
find_expr_references_context *context)
2361+
{
2362+
int atts_done = 0;
2363+
ListCell *lc;
2364+
2365+
/*
2366+
* Identify which RangeTblFunction produces this attnum, and see if it
2367+
* returns a composite type. If so, we'd better make a dependency on the
2368+
* referenced column of the composite type (or actually, of its associated
2369+
* relation).
2370+
*/
2371+
foreach(lc, rte->functions)
2372+
{
2373+
RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
2374+
2375+
if (attnum > atts_done &&
2376+
attnum <= atts_done + rtfunc->funccolcount)
2377+
{
2378+
TupleDesc tupdesc;
2379+
2380+
tupdesc = get_expr_result_tupdesc(rtfunc->funcexpr, true);
2381+
if (tupdesc && tupdesc->tdtypeid != RECORDOID)
2382+
{
2383+
/*
2384+
* Named composite type, so individual columns could get
2385+
* dropped. Make a dependency on this specific column.
2386+
*/
2387+
Oid reltype = get_typ_typrelid(tupdesc->tdtypeid);
2388+
2389+
Assert(attnum - atts_done <= tupdesc->natts);
2390+
if (OidIsValid(reltype)) /* can this fail? */
2391+
add_object_address(OCLASS_CLASS, reltype,
2392+
attnum - atts_done,
2393+
context->addrs);
2394+
return;
2395+
}
2396+
/* Nothing to do; function's result type is handled elsewhere */
2397+
return;
2398+
}
2399+
atts_done += rtfunc->funccolcount;
2400+
}
2401+
2402+
/* If we get here, must be looking for the ordinality column */
2403+
if (rte->funcordinality && attnum == atts_done + 1)
2404+
return;
2405+
2406+
/* this probably can't happen ... */
2407+
ereport(ERROR,
2408+
(errcode(ERRCODE_UNDEFINED_COLUMN),
2409+
errmsg("column %d of relation \"%s\" does not exist",
2410+
attnum, rte->eref->aliasname)));
2411+
}
2412+
23452413
/*
23462414
* Given an array of dependency references, eliminate any duplicates.
23472415
*/

src/backend/utils/adt/ruleutils.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -7330,9 +7330,9 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
73307330
/*
73317331
* If we find a Var referencing a dropped column, it seems better to
73327332
* print something (anything) than to fail. In general this should
7333-
* not happen, but there are specific cases involving functions
7334-
* returning named composite types where we don't sufficiently enforce
7335-
* that you can't drop a column that's referenced in some view.
7333+
* not happen, but it used to be possible for some cases involving
7334+
* functions returning named composite types, and perhaps there are
7335+
* still bugs out there.
73367336
*/
73377337
if (attname == NULL)
73387338
attname = "?dropped?column?";

src/test/regress/expected/create_view.out

+80-2
Original file line numberDiff line numberDiff line change
@@ -1597,8 +1597,29 @@ select * from tt14v;
15971597
foo | baz | 42
15981598
(1 row)
15991599

1600+
alter table tt14t drop column f3; -- fail, view has explicit reference to f3
1601+
ERROR: cannot drop column f3 of table tt14t because other objects depend on it
1602+
DETAIL: view tt14v depends on column f3 of table tt14t
1603+
HINT: Use DROP ... CASCADE to drop the dependent objects too.
1604+
-- We used to have a bug that would allow the above to succeed, posing
1605+
-- hazards for later execution of the view. Check that the internal
1606+
-- defenses for those hazards haven't bit-rotted, in case some other
1607+
-- bug with similar symptoms emerges.
16001608
begin;
1601-
-- this perhaps should be rejected, but it isn't:
1609+
-- destroy the dependency entry that prevents the DROP:
1610+
delete from pg_depend where
1611+
objid = (select oid from pg_rewrite
1612+
where ev_class = 'tt14v'::regclass and rulename = '_RETURN')
1613+
and refobjsubid = 3
1614+
returning pg_describe_object(classid, objid, objsubid) as obj,
1615+
pg_describe_object(refclassid, refobjid, refobjsubid) as ref,
1616+
deptype;
1617+
obj | ref | deptype
1618+
----------------------------+--------------------------+---------
1619+
rule _RETURN on view tt14v | column f3 of table tt14t | n
1620+
(1 row)
1621+
1622+
-- this will now succeed:
16021623
alter table tt14t drop column f3;
16031624
-- column f3 is still in the view, sort of ...
16041625
select pg_get_viewdef('tt14v', true);
@@ -1629,8 +1650,26 @@ select f1, f4 from tt14v;
16291650
select * from tt14v;
16301651
ERROR: attribute 3 of type record has been dropped
16311652
rollback;
1653+
-- likewise, altering a referenced column's type is prohibited ...
1654+
alter table tt14t alter column f4 type integer using f4::integer; -- fail
1655+
ERROR: cannot alter type of a column used by a view or rule
1656+
DETAIL: rule _RETURN on view tt14v depends on column "f4"
1657+
-- ... but some bug might let it happen, so check defenses
16321658
begin;
1633-
-- this perhaps should be rejected, but it isn't:
1659+
-- destroy the dependency entry that prevents the ALTER:
1660+
delete from pg_depend where
1661+
objid = (select oid from pg_rewrite
1662+
where ev_class = 'tt14v'::regclass and rulename = '_RETURN')
1663+
and refobjsubid = 4
1664+
returning pg_describe_object(classid, objid, objsubid) as obj,
1665+
pg_describe_object(refclassid, refobjid, refobjsubid) as ref,
1666+
deptype;
1667+
obj | ref | deptype
1668+
----------------------------+--------------------------+---------
1669+
rule _RETURN on view tt14v | column f4 of table tt14t | n
1670+
(1 row)
1671+
1672+
-- this will now succeed:
16341673
alter table tt14t alter column f4 type integer using f4::integer;
16351674
-- f4 is still in the view ...
16361675
select pg_get_viewdef('tt14v', true);
@@ -1653,6 +1692,45 @@ select * from tt14v;
16531692
ERROR: attribute 4 of type record has wrong type
16541693
DETAIL: Table has type integer, but query expects text.
16551694
rollback;
1695+
drop view tt14v;
1696+
create view tt14v as select t.f1, t.f4 from tt14f() t;
1697+
select pg_get_viewdef('tt14v', true);
1698+
pg_get_viewdef
1699+
--------------------------------
1700+
SELECT t.f1, +
1701+
t.f4 +
1702+
FROM tt14f() t(f1, f3, f4);
1703+
(1 row)
1704+
1705+
select * from tt14v;
1706+
f1 | f4
1707+
-----+----
1708+
foo | 42
1709+
(1 row)
1710+
1711+
alter table tt14t drop column f3; -- ok
1712+
select pg_get_viewdef('tt14v', true);
1713+
pg_get_viewdef
1714+
----------------------------
1715+
SELECT t.f1, +
1716+
t.f4 +
1717+
FROM tt14f() t(f1, f4);
1718+
(1 row)
1719+
1720+
explain (verbose, costs off) select * from tt14v;
1721+
QUERY PLAN
1722+
----------------------------------------
1723+
Function Scan on testviewschm2.tt14f t
1724+
Output: t.f1, t.f4
1725+
Function Call: tt14f()
1726+
(3 rows)
1727+
1728+
select * from tt14v;
1729+
f1 | f4
1730+
-----+----
1731+
foo | 42
1732+
(1 row)
1733+
16561734
-- check display of whole-row variables in some corner cases
16571735
create type nestedcomposite as (x int8_tbl);
16581736
create view tt15v as select row(i)::nestedcomposite from int8_tbl i;

src/test/regress/expected/rangefuncs.out

+40
Original file line numberDiff line numberDiff line change
@@ -2247,15 +2247,55 @@ select * from usersview;
22472247
id2 | 2 | email2 | 12 | t | 11 | 2
22482248
(2 rows)
22492249

2250+
alter table users drop column moredrop; -- fail, view has reference
2251+
ERROR: cannot drop column moredrop of table users because other objects depend on it
2252+
DETAIL: view usersview depends on column moredrop of table users
2253+
HINT: Use DROP ... CASCADE to drop the dependent objects too.
2254+
-- We used to have a bug that would allow the above to succeed, posing
2255+
-- hazards for later execution of the view. Check that the internal
2256+
-- defenses for those hazards haven't bit-rotted, in case some other
2257+
-- bug with similar symptoms emerges.
22502258
begin;
2259+
-- destroy the dependency entry that prevents the DROP:
2260+
delete from pg_depend where
2261+
objid = (select oid from pg_rewrite
2262+
where ev_class = 'usersview'::regclass and rulename = '_RETURN')
2263+
and refobjsubid = 5
2264+
returning pg_describe_object(classid, objid, objsubid) as obj,
2265+
pg_describe_object(refclassid, refobjid, refobjsubid) as ref,
2266+
deptype;
2267+
obj | ref | deptype
2268+
--------------------------------+--------------------------------+---------
2269+
rule _RETURN on view usersview | column moredrop of table users | n
2270+
(1 row)
2271+
22512272
alter table users drop column moredrop;
22522273
select * from usersview; -- expect clean failure
22532274
ERROR: attribute 5 of type record has been dropped
22542275
rollback;
2276+
alter table users alter column seq type numeric; -- fail, view has reference
2277+
ERROR: cannot alter type of a column used by a view or rule
2278+
DETAIL: rule _RETURN on view usersview depends on column "seq"
2279+
-- likewise, check we don't crash if the dependency goes wrong
2280+
begin;
2281+
-- destroy the dependency entry that prevents the ALTER:
2282+
delete from pg_depend where
2283+
objid = (select oid from pg_rewrite
2284+
where ev_class = 'usersview'::regclass and rulename = '_RETURN')
2285+
and refobjsubid = 2
2286+
returning pg_describe_object(classid, objid, objsubid) as obj,
2287+
pg_describe_object(refclassid, refobjid, refobjsubid) as ref,
2288+
deptype;
2289+
obj | ref | deptype
2290+
--------------------------------+---------------------------+---------
2291+
rule _RETURN on view usersview | column seq of table users | n
2292+
(1 row)
2293+
22552294
alter table users alter column seq type numeric;
22562295
select * from usersview; -- expect clean failure
22572296
ERROR: attribute 2 of type record has wrong type
22582297
DETAIL: Table has type numeric, but query expects integer.
2298+
rollback;
22592299
drop view usersview;
22602300
drop function get_first_user();
22612301
drop function get_users();

src/test/regress/sql/create_view.sql

+43-2
Original file line numberDiff line numberDiff line change
@@ -575,9 +575,24 @@ create view tt14v as select t.* from tt14f() t;
575575
select pg_get_viewdef('tt14v', true);
576576
select * from tt14v;
577577

578+
alter table tt14t drop column f3; -- fail, view has explicit reference to f3
579+
580+
-- We used to have a bug that would allow the above to succeed, posing
581+
-- hazards for later execution of the view. Check that the internal
582+
-- defenses for those hazards haven't bit-rotted, in case some other
583+
-- bug with similar symptoms emerges.
578584
begin;
579585

580-
-- this perhaps should be rejected, but it isn't:
586+
-- destroy the dependency entry that prevents the DROP:
587+
delete from pg_depend where
588+
objid = (select oid from pg_rewrite
589+
where ev_class = 'tt14v'::regclass and rulename = '_RETURN')
590+
and refobjsubid = 3
591+
returning pg_describe_object(classid, objid, objsubid) as obj,
592+
pg_describe_object(refclassid, refobjid, refobjsubid) as ref,
593+
deptype;
594+
595+
-- this will now succeed:
581596
alter table tt14t drop column f3;
582597

583598
-- column f3 is still in the view, sort of ...
@@ -590,9 +605,22 @@ select * from tt14v;
590605

591606
rollback;
592607

608+
-- likewise, altering a referenced column's type is prohibited ...
609+
alter table tt14t alter column f4 type integer using f4::integer; -- fail
610+
611+
-- ... but some bug might let it happen, so check defenses
593612
begin;
594613

595-
-- this perhaps should be rejected, but it isn't:
614+
-- destroy the dependency entry that prevents the ALTER:
615+
delete from pg_depend where
616+
objid = (select oid from pg_rewrite
617+
where ev_class = 'tt14v'::regclass and rulename = '_RETURN')
618+
and refobjsubid = 4
619+
returning pg_describe_object(classid, objid, objsubid) as obj,
620+
pg_describe_object(refclassid, refobjid, refobjsubid) as ref,
621+
deptype;
622+
623+
-- this will now succeed:
596624
alter table tt14t alter column f4 type integer using f4::integer;
597625

598626
-- f4 is still in the view ...
@@ -603,6 +631,19 @@ select * from tt14v;
603631

604632
rollback;
605633

634+
drop view tt14v;
635+
636+
create view tt14v as select t.f1, t.f4 from tt14f() t;
637+
638+
select pg_get_viewdef('tt14v', true);
639+
select * from tt14v;
640+
641+
alter table tt14t drop column f3; -- ok
642+
643+
select pg_get_viewdef('tt14v', true);
644+
explain (verbose, costs off) select * from tt14v;
645+
select * from tt14v;
646+
606647
-- check display of whole-row variables in some corner cases
607648

608649
create type nestedcomposite as (x int8_tbl);

src/test/regress/sql/rangefuncs.sql

+33
Original file line numberDiff line numberDiff line change
@@ -682,12 +682,45 @@ SELECT * FROM ROWS FROM(get_users(), generate_series(10,11)) WITH ORDINALITY;
682682
select * from usersview;
683683
alter table users add column junk text;
684684
select * from usersview;
685+
686+
alter table users drop column moredrop; -- fail, view has reference
687+
688+
-- We used to have a bug that would allow the above to succeed, posing
689+
-- hazards for later execution of the view. Check that the internal
690+
-- defenses for those hazards haven't bit-rotted, in case some other
691+
-- bug with similar symptoms emerges.
685692
begin;
693+
694+
-- destroy the dependency entry that prevents the DROP:
695+
delete from pg_depend where
696+
objid = (select oid from pg_rewrite
697+
where ev_class = 'usersview'::regclass and rulename = '_RETURN')
698+
and refobjsubid = 5
699+
returning pg_describe_object(classid, objid, objsubid) as obj,
700+
pg_describe_object(refclassid, refobjid, refobjsubid) as ref,
701+
deptype;
702+
686703
alter table users drop column moredrop;
687704
select * from usersview; -- expect clean failure
688705
rollback;
706+
707+
alter table users alter column seq type numeric; -- fail, view has reference
708+
709+
-- likewise, check we don't crash if the dependency goes wrong
710+
begin;
711+
712+
-- destroy the dependency entry that prevents the ALTER:
713+
delete from pg_depend where
714+
objid = (select oid from pg_rewrite
715+
where ev_class = 'usersview'::regclass and rulename = '_RETURN')
716+
and refobjsubid = 2
717+
returning pg_describe_object(classid, objid, objsubid) as obj,
718+
pg_describe_object(refclassid, refobjid, refobjsubid) as ref,
719+
deptype;
720+
689721
alter table users alter column seq type numeric;
690722
select * from usersview; -- expect clean failure
723+
rollback;
691724

692725
drop view usersview;
693726
drop function get_first_user();

0 commit comments

Comments
 (0)