Skip to content

Commit b9f3d7a

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 f409502 commit b9f3d7a

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
@@ -25,6 +25,7 @@
2525
#include "catalog/pg_type.h"
2626
#include "commands/trigger.h"
2727
#include "foreign/fdwapi.h"
28+
#include "miscadmin.h"
2829
#include "nodes/makefuncs.h"
2930
#include "nodes/nodeFuncs.h"
3031
#include "parser/analyze.h"
@@ -2582,6 +2583,11 @@ view_cols_are_auto_updatable(Query *viewquery,
25822583
* non-NULL, then only the specified columns are considered when testing for
25832584
* updatability.
25842585
*
2586+
* Unlike the preceding functions, this does recurse to look at a view's
2587+
* base relations, so it needs to detect recursion. To do that, we pass
2588+
* a list of currently-considered outer relations. External callers need
2589+
* only pass NIL.
2590+
*
25852591
* This is used for the information_schema views, which have separate concepts
25862592
* of "updatable" and "trigger updatable". A relation is "updatable" if it
25872593
* can be updated without the need for triggers (either because it has a
@@ -2600,6 +2606,7 @@ view_cols_are_auto_updatable(Query *viewquery,
26002606
*/
26012607
int
26022608
relation_is_updatable(Oid reloid,
2609+
List *outer_reloids,
26032610
bool include_triggers,
26042611
Bitmapset *include_cols)
26052612
{
@@ -2609,6 +2616,9 @@ relation_is_updatable(Oid reloid,
26092616

26102617
#define ALL_EVENTS ((1 << CMD_INSERT) | (1 << CMD_UPDATE) | (1 << CMD_DELETE))
26112618

2619+
/* Since this function recurses, it could be driven to stack overflow */
2620+
check_stack_depth();
2621+
26122622
rel = try_relation_open(reloid, AccessShareLock);
26132623

26142624
/*
@@ -2620,6 +2630,13 @@ relation_is_updatable(Oid reloid,
26202630
if (rel == NULL)
26212631
return 0;
26222632

2633+
/* If we detect a recursive view, report that it is not updatable */
2634+
if (list_member_oid(outer_reloids, RelationGetRelid(rel)))
2635+
{
2636+
relation_close(rel, AccessShareLock);
2637+
return 0;
2638+
}
2639+
26232640
/* If the relation is a table, it is always updatable */
26242641
if (rel->rd_rel->relkind == RELKIND_RELATION ||
26252642
rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
@@ -2740,11 +2757,15 @@ relation_is_updatable(Oid reloid,
27402757
base_rte->relkind != RELKIND_PARTITIONED_TABLE)
27412758
{
27422759
baseoid = base_rte->relid;
2760+
outer_reloids = lcons_oid(RelationGetRelid(rel),
2761+
outer_reloids);
27432762
include_cols = adjust_view_column_set(updatable_cols,
27442763
viewquery->targetList);
27452764
auto_events &= relation_is_updatable(baseoid,
2765+
outer_reloids,
27462766
include_triggers,
27472767
include_cols);
2768+
outer_reloids = list_delete_first(outer_reloids);
27482769
}
27492770
events |= auto_events;
27502771
}

src/backend/utils/adt/misc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ pg_relation_is_updatable(PG_FUNCTION_ARGS)
681681
Oid reloid = PG_GETARG_OID(0);
682682
bool include_triggers = PG_GETARG_BOOL(1);
683683

684-
PG_RETURN_INT32(relation_is_updatable(reloid, include_triggers, NULL));
684+
PG_RETURN_INT32(relation_is_updatable(reloid, NIL, include_triggers, NULL));
685685
}
686686

687687
/*
@@ -705,7 +705,7 @@ pg_column_is_updatable(PG_FUNCTION_ARGS)
705705
if (attnum <= 0)
706706
PG_RETURN_BOOL(false);
707707

708-
events = relation_is_updatable(reloid, include_triggers,
708+
events = relation_is_updatable(reloid, NIL, include_triggers,
709709
bms_make_singleton(col));
710710

711711
/* 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
@@ -30,6 +30,7 @@ extern Query *get_view_query(Relation view);
3030
extern const char *view_query_is_auto_updatable(Query *viewquery,
3131
bool check_cols);
3232
extern int relation_is_updatable(Oid reloid,
33+
List *outer_reloids,
3334
bool include_triggers,
3435
Bitmapset *include_cols);
3536

0 commit comments

Comments
 (0)