mirror of
https://github.com/postgres/postgres.git
synced 2025-07-15 19:21:59 +03:00
WITH CHECK OPTION support for auto-updatable VIEWs
For simple views which are automatically updatable, this patch allows the user to specify what level of checking should be done on records being inserted or updated. For 'LOCAL CHECK', new tuples are validated against the conditionals of the view they are being inserted into, while for 'CASCADED CHECK' the new tuples are validated against the conditionals for all views involved (from the top down). This option is part of the SQL specification. Dean Rasheed, reviewed by Pavel Stehule
This commit is contained in:
@ -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.
|
||||
*
|
||||
* Caller must have verified that relation is a view!
|
||||
* 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 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;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user