diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 9db30d0d880..d4180b23577 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -24,6 +24,7 @@ #include "catalog/pg_type.h" #include "commands/trigger.h" #include "foreign/fdwapi.h" +#include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "parser/analyze.h" @@ -2524,6 +2525,11 @@ view_cols_are_auto_updatable(Query *viewquery, * non-NULL, then only the specified columns are considered when testing for * updatability. * + * Unlike the preceding functions, this does recurse to look at a view's + * base relations, so it needs to detect recursion. To do that, we pass + * a list of currently-considered outer relations. External callers need + * only pass NIL. + * * This is used for the information_schema views, which have separate concepts * of "updatable" and "trigger updatable". A relation is "updatable" if it * can be updated without the need for triggers (either because it has a @@ -2542,6 +2548,7 @@ view_cols_are_auto_updatable(Query *viewquery, */ int relation_is_updatable(Oid reloid, + List *outer_reloids, bool include_triggers, Bitmapset *include_cols) { @@ -2551,6 +2558,9 @@ relation_is_updatable(Oid reloid, #define ALL_EVENTS ((1 << CMD_INSERT) | (1 << CMD_UPDATE) | (1 << CMD_DELETE)) + /* Since this function recurses, it could be driven to stack overflow */ + check_stack_depth(); + rel = try_relation_open(reloid, AccessShareLock); /* @@ -2562,6 +2572,13 @@ relation_is_updatable(Oid reloid, if (rel == NULL) return 0; + /* If we detect a recursive view, report that it is not updatable */ + if (list_member_oid(outer_reloids, RelationGetRelid(rel))) + { + relation_close(rel, AccessShareLock); + return 0; + } + /* If the relation is a table, it is always updatable */ if (rel->rd_rel->relkind == RELKIND_RELATION) { @@ -2680,11 +2697,15 @@ relation_is_updatable(Oid reloid, if (base_rte->relkind != RELKIND_RELATION) { baseoid = base_rte->relid; + outer_reloids = lcons_oid(RelationGetRelid(rel), + outer_reloids); include_cols = adjust_view_column_set(updatable_cols, viewquery->targetList); auto_events &= relation_is_updatable(baseoid, + outer_reloids, include_triggers, include_cols); + outer_reloids = list_delete_first(outer_reloids); } events |= auto_events; } diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c index 3aff7caf9e0..44cef175164 100644 --- a/src/backend/utils/adt/misc.c +++ b/src/backend/utils/adt/misc.c @@ -686,7 +686,7 @@ pg_relation_is_updatable(PG_FUNCTION_ARGS) Oid reloid = PG_GETARG_OID(0); bool include_triggers = PG_GETARG_BOOL(1); - PG_RETURN_INT32(relation_is_updatable(reloid, include_triggers, NULL)); + PG_RETURN_INT32(relation_is_updatable(reloid, NIL, include_triggers, NULL)); } /* @@ -710,7 +710,7 @@ pg_column_is_updatable(PG_FUNCTION_ARGS) if (attnum <= 0) PG_RETURN_BOOL(false); - events = relation_is_updatable(reloid, include_triggers, + events = relation_is_updatable(reloid, NIL, include_triggers, bms_make_singleton(col)); /* We require both updatability and deletability of the relation */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 9290ee16363..86136581909 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -30,6 +30,7 @@ extern Query *get_view_query(Relation view); extern const char *view_query_is_auto_updatable(Query *viewquery, bool check_cols); extern int relation_is_updatable(Oid reloid, + List *outer_reloids, bool include_triggers, Bitmapset *include_cols);