Skip to content

Commit f098290

Browse files
committed
Defend against self-referential views in relation_is_updatable().
While a self-referential view doesn't actually work, it's possible to create one, and it turns out that this breaks some of the information_schema views. Those views call relation_is_updatable(), which neglected to consider the hazards of being recursive. In older PG versions you get a "stack depth limit exceeded" error, but since v10 it'd recurse to the point of stack overrun and crash, because commit a4c35ea took out the expression_returns_set() call that was incidentally checking the stack depth. Since this function is only used by information_schema views, it seems like it'd be better to return "not updatable" than suffer an error. Hence, add tracking of what views we're examining, in just the same way that the nearby fireRIRrules() code detects self-referential views. I added a check_stack_depth() call too, just to be defensive. Per private report from Manuel Rigger. Back-patch to all supported versions.
1 parent d81d4c3 commit f098290

File tree

3 files changed

+24
-2
lines changed

3 files changed

+24
-2
lines changed

src/backend/rewrite/rewriteHandler.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "catalog/pg_type.h"
1818
#include "commands/trigger.h"
1919
#include "foreign/fdwapi.h"
20+
#include "miscadmin.h"
2021
#include "nodes/makefuncs.h"
2122
#include "nodes/nodeFuncs.h"
2223
#include "parser/analyze.h"
@@ -2374,6 +2375,11 @@ view_cols_are_auto_updatable(Query *viewquery,
23742375
* non-NULL, then only the specified columns are considered when testing for
23752376
* updatability.
23762377
*
2378+
* Unlike the preceding functions, this does recurse to look at a view's
2379+
* base relations, so it needs to detect recursion. To do that, we pass
2380+
* a list of currently-considered outer relations. External callers need
2381+
* only pass NIL.
2382+
*
23772383
* This is used for the information_schema views, which have separate concepts
23782384
* of "updatable" and "trigger updatable". A relation is "updatable" if it
23792385
* can be updated without the need for triggers (either because it has a
@@ -2392,6 +2398,7 @@ view_cols_are_auto_updatable(Query *viewquery,
23922398
*/
23932399
int
23942400
relation_is_updatable(Oid reloid,
2401+
List *outer_reloids,
23952402
bool include_triggers,
23962403
Bitmapset *include_cols)
23972404
{
@@ -2401,6 +2408,9 @@ relation_is_updatable(Oid reloid,
24012408

24022409
#define ALL_EVENTS ((1 << CMD_INSERT) | (1 << CMD_UPDATE) | (1 << CMD_DELETE))
24032410

2411+
/* Since this function recurses, it could be driven to stack overflow */
2412+
check_stack_depth();
2413+
24042414
rel = try_relation_open(reloid, AccessShareLock);
24052415

24062416
/*
@@ -2412,6 +2422,13 @@ relation_is_updatable(Oid reloid,
24122422
if (rel == NULL)
24132423
return 0;
24142424

2425+
/* If we detect a recursive view, report that it is not updatable */
2426+
if (list_member_oid(outer_reloids, RelationGetRelid(rel)))
2427+
{
2428+
relation_close(rel, AccessShareLock);
2429+
return 0;
2430+
}
2431+
24152432
/* If the relation is a table, it is always updatable */
24162433
if (rel->rd_rel->relkind == RELKIND_RELATION)
24172434
{
@@ -2530,11 +2547,15 @@ relation_is_updatable(Oid reloid,
25302547
if (base_rte->relkind != RELKIND_RELATION)
25312548
{
25322549
baseoid = base_rte->relid;
2550+
outer_reloids = lcons_oid(RelationGetRelid(rel),
2551+
outer_reloids);
25332552
include_cols = adjust_view_column_set(updatable_cols,
25342553
viewquery->targetList);
25352554
auto_events &= relation_is_updatable(baseoid,
2555+
outer_reloids,
25362556
include_triggers,
25372557
include_cols);
2558+
outer_reloids = list_delete_first(outer_reloids);
25382559
}
25392560
events |= auto_events;
25402561
}

src/backend/utils/adt/misc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,7 @@ pg_relation_is_updatable(PG_FUNCTION_ARGS)
540540
Oid reloid = PG_GETARG_OID(0);
541541
bool include_triggers = PG_GETARG_BOOL(1);
542542

543-
PG_RETURN_INT32(relation_is_updatable(reloid, include_triggers, NULL));
543+
PG_RETURN_INT32(relation_is_updatable(reloid, NIL, include_triggers, NULL));
544544
}
545545

546546
/*
@@ -564,7 +564,7 @@ pg_column_is_updatable(PG_FUNCTION_ARGS)
564564
if (attnum <= 0)
565565
PG_RETURN_BOOL(false);
566566

567-
events = relation_is_updatable(reloid, include_triggers,
567+
events = relation_is_updatable(reloid, NIL, include_triggers,
568568
bms_make_singleton(col));
569569

570570
/* We require both updatability and deletability of the relation */

src/include/rewrite/rewriteHandler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ extern Query *get_view_query(Relation view);
2727
extern const char *view_query_is_auto_updatable(Query *viewquery,
2828
bool check_cols);
2929
extern int relation_is_updatable(Oid reloid,
30+
List *outer_reloids,
3031
bool include_triggers,
3132
Bitmapset *include_cols);
3233

0 commit comments

Comments
 (0)