aboutsummaryrefslogtreecommitdiff
path: root/src/backend/rewrite/rewriteHandler.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/rewrite/rewriteHandler.c')
-rw-r--r--src/backend/rewrite/rewriteHandler.c117
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;
}