diff options
Diffstat (limited to 'src/backend/rewrite/rewriteHandler.c')
-rw-r--r-- | src/backend/rewrite/rewriteHandler.c | 117 |
1 files changed, 107 insertions, 10 deletions
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index d909de3a539..7f527bd74a2 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -19,6 +19,7 @@ #include "foreign/fdwapi.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" +#include "optimizer/clauses.h" #include "parser/analyze.h" #include "parser/parse_coerce.h" #include "parser/parsetree.h" @@ -1866,7 +1867,7 @@ fireRules(Query *parsetree, * Caller should have verified that the relation is a view, and therefore * we should find an ON SELECT action. */ -static Query * +Query * get_view_query(Relation view) { int i; @@ -1927,11 +1928,16 @@ view_has_instead_trigger(Relation view, CmdType event) /* * view_is_auto_updatable - - * Test if the specified view can be automatically updated. This will - * either return NULL (if the view can be updated) or a message string - * giving the reason that it cannot be. + * Retrive the view definition and options and then determine if the view + * can be auto-updated by calling view_query_is_auto_updatable(). Returns + * NULL or a message string giving the reason the view is not auto + * updateable. See view_query_is_auto_updatable() for details. + * + * The only view option which affects if a view can be auto-updated, today, + * is the security_barrier option. If other options are added later, they + * will also need to be handled here. * - * Caller must have verified that relation is a view! + * Caller must have verified that the relation is a view! * * Note that the checks performed here are local to this view. We do not * check whether the view's underlying base relation is updatable; that @@ -1940,10 +1946,32 @@ view_has_instead_trigger(Relation view, CmdType event) * Also note that we don't check for INSTEAD triggers or rules here; those * also prevent auto-update, but they must be checked for by the caller. */ -static const char * +const char * view_is_auto_updatable(Relation view) { Query *viewquery = get_view_query(view); + bool security_barrier = RelationIsSecurityView(view); + + return view_query_is_auto_updatable(viewquery, security_barrier); +} + + +/* + * view_query_is_auto_updatable - + * Test if the specified view definition can be automatically updated, given + * the view's options (currently only security_barrier affects a view's + * auto-updatable status). + * + * This will either return NULL (if the view can be updated) or a message + * string giving the reason that it cannot be. + * + * Note that the checks performed here are only based on the view + * definition. We do not check whether any base relations referred to by + * the view are updatable. + */ +const char * +view_query_is_auto_updatable(Query *viewquery, bool security_barrier) +{ RangeTblRef *rtr; RangeTblEntry *base_rte; Bitmapset *bms; @@ -1995,9 +2023,9 @@ view_is_auto_updatable(Relation view) /* * For now, we also don't support security-barrier views, because of the * difficulty of keeping upper-level qual expressions away from - * lower-level data. This might get relaxed in future. + * lower-level data. This might get relaxed in the future. */ - if (RelationIsSecurityView(view)) + if (security_barrier) return gettext_noop("Security-barrier views are not automatically updatable."); /* @@ -2532,8 +2560,7 @@ rewriteTargetView(Query *parsetree, Relation view) * only adjust their varnos to reference the new target (just the same as * we did with the view targetlist). * - * For INSERT, the view's quals can be ignored for now. When we implement - * WITH CHECK OPTION, this might be a good place to collect them. + * For INSERT, the view's quals can be ignored in the main query. */ if (parsetree->commandType != CMD_INSERT && viewquery->jointree->quals != NULL) @@ -2544,6 +2571,76 @@ rewriteTargetView(Query *parsetree, Relation view) AddQual(parsetree, (Node *) viewqual); } + /* + * For INSERT/UPDATE, if the view has the WITH CHECK OPTION, or any parent + * view specified WITH CASCADED CHECK OPTION, add the quals from the view + * to the query's withCheckOptions list. + */ + if (parsetree->commandType != CMD_DELETE) + { + bool has_wco = RelationHasCheckOption(view); + bool cascaded = RelationHasCascadedCheckOption(view); + + /* + * If the parent view has a cascaded check option, treat this view as + * if it also had a cascaded check option. + * + * New WithCheckOptions are added to the start of the list, so if there + * is a cascaded check option, it will be the first item in the list. + */ + if (parsetree->withCheckOptions != NIL) + { + WithCheckOption *parent_wco = + (WithCheckOption *) linitial(parsetree->withCheckOptions); + + if (parent_wco->cascaded) + { + has_wco = true; + cascaded = true; + } + } + + /* + * Add the new WithCheckOption to the start of the list, so that + * checks on inner views are run before checks on outer views, as + * required by the SQL standard. + * + * If the new check is CASCADED, we need to add it even if this view + * has no quals, since there may be quals on child views. A LOCAL + * check can be omitted if this view has no quals. + */ + if (has_wco && (cascaded || viewquery->jointree->quals != NULL)) + { + WithCheckOption *wco; + + wco = makeNode(WithCheckOption); + wco->viewname = pstrdup(RelationGetRelationName(view)); + wco->qual = NULL; + wco->cascaded = cascaded; + + parsetree->withCheckOptions = lcons(wco, + parsetree->withCheckOptions); + + if (viewquery->jointree->quals != NULL) + { + wco->qual = (Node *) copyObject(viewquery->jointree->quals); + ChangeVarNodes(wco->qual, base_rt_index, new_rt_index, 0); + + /* + * Make sure that the query is marked correctly if the added + * qual has sublinks. We can skip this check if the query is + * already marked, or if the command is an UPDATE, in which + * case the same qual will have already been added to the + * query's WHERE clause, and AddQual will have already done + * this check. + */ + if (!parsetree->hasSubLinks && + parsetree->commandType != CMD_UPDATE) + parsetree->hasSubLinks = checkExprHasSubLink(wco->qual); + } + } + } + return parsetree; } |