diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 5b047d16629..fb81217679f 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -27,6 +27,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" @@ -2619,6 +2620,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 @@ -2637,6 +2643,7 @@ view_cols_are_auto_updatable(Query *viewquery, */ int relation_is_updatable(Oid reloid, + List *outer_reloids, bool include_triggers, Bitmapset *include_cols) { @@ -2646,6 +2653,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); /* @@ -2657,6 +2667,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 || rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) @@ -2777,11 +2794,15 @@ relation_is_updatable(Oid reloid, base_rte->relkind != RELKIND_PARTITIONED_TABLE) { 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 d330a88e3c1..e59b61ba6f2 100644 --- a/src/backend/utils/adt/misc.c +++ b/src/backend/utils/adt/misc.c @@ -510,7 +510,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)); } /* @@ -534,7 +534,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 c55fe81a0a3..77b2ffa9621 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);