summaryrefslogtreecommitdiff
path: root/src/backend/rewrite
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/rewrite')
-rw-r--r--src/backend/rewrite/rewriteHandler.c87
-rw-r--r--src/backend/rewrite/rowsecurity.c82
2 files changed, 140 insertions, 29 deletions
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 0fc47cb786..39302a410b 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -52,7 +52,10 @@ static Query *rewriteRuleAction(Query *parsetree,
CmdType event,
bool *returning_flag);
static List *adjustJoinTreeList(Query *parsetree, bool removert, int rt_index);
-static void rewriteTargetListIU(Query *parsetree, Relation target_relation,
+static List *rewriteTargetListIU(List *targetList,
+ CmdType commandType,
+ Relation target_relation,
+ int result_rti,
List **attrno_list);
static TargetEntry *process_matched_tle(TargetEntry *src_tle,
TargetEntry *prior_tle,
@@ -66,7 +69,7 @@ static void markQueryForLocking(Query *qry, Node *jtnode,
LockClauseStrength strength, LockWaitPolicy waitPolicy,
bool pushedDown);
static List *matchLocks(CmdType event, RuleLock *rulelocks,
- int varno, Query *parsetree);
+ int varno, Query *parsetree, bool *hasUpdate);
static Query *fireRIRrules(Query *parsetree, List *activeRIRs,
bool forUpdatePushedDown);
static bool view_has_instead_trigger(Relation view, CmdType event);
@@ -679,11 +682,13 @@ adjustJoinTreeList(Query *parsetree, bool removert, int rt_index)
* order of the original tlist's non-junk entries. This is needed for
* processing VALUES RTEs.
*/
-static void
-rewriteTargetListIU(Query *parsetree, Relation target_relation,
+static List*
+rewriteTargetListIU(List *targetList,
+ CmdType commandType,
+ Relation target_relation,
+ int result_rti,
List **attrno_list)
{
- CmdType commandType = parsetree->commandType;
TargetEntry **new_tles;
List *new_tlist = NIL;
List *junk_tlist = NIL;
@@ -709,7 +714,7 @@ rewriteTargetListIU(Query *parsetree, Relation target_relation,
new_tles = (TargetEntry **) palloc0(numattrs * sizeof(TargetEntry *));
next_junk_attrno = numattrs + 1;
- foreach(temp, parsetree->targetList)
+ foreach(temp, targetList)
{
TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
@@ -827,7 +832,7 @@ rewriteTargetListIU(Query *parsetree, Relation target_relation,
{
Node *new_expr;
- new_expr = (Node *) makeVar(parsetree->resultRelation,
+ new_expr = (Node *) makeVar(result_rti,
attrno,
att_tup->atttypid,
att_tup->atttypmod,
@@ -846,7 +851,7 @@ rewriteTargetListIU(Query *parsetree, Relation target_relation,
pfree(new_tles);
- parsetree->targetList = list_concat(new_tlist, junk_tlist);
+ return list_concat(new_tlist, junk_tlist);
}
@@ -1288,7 +1293,8 @@ static List *
matchLocks(CmdType event,
RuleLock *rulelocks,
int varno,
- Query *parsetree)
+ Query *parsetree,
+ bool *hasUpdate)
{
List *matching_locks = NIL;
int nlocks;
@@ -1309,6 +1315,9 @@ matchLocks(CmdType event,
{
RewriteRule *oneLock = rulelocks->rules[i];
+ if (oneLock->event == CMD_UPDATE)
+ *hasUpdate = true;
+
/*
* Suppress ON INSERT/UPDATE/DELETE rules that are disabled or
* configured to not fire during the current sessions replication
@@ -1766,8 +1775,8 @@ fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
/*
* Fetch any new security quals that must be applied to this RTE.
*/
- get_row_security_policies(parsetree, rte, rt_index,
- &securityQuals, &withCheckOptions,
+ get_row_security_policies(parsetree, parsetree->commandType, rte,
+ rt_index, &securityQuals, &withCheckOptions,
&hasRowSecurity, &hasSubLinks);
if (securityQuals != NIL || withCheckOptions != NIL)
@@ -2642,6 +2651,18 @@ rewriteTargetView(Query *parsetree, Relation view)
tle->resno - FirstLowInvalidHeapAttributeNumber);
}
+ if (parsetree->onConflict)
+ {
+ foreach(lc, parsetree->onConflict->onConflictSet)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+
+ if (!tle->resjunk)
+ modified_cols = bms_add_member(modified_cols,
+ tle->resno - FirstLowInvalidHeapAttributeNumber);
+ }
+ }
+
auto_update_detail = view_cols_are_auto_updatable(viewquery,
modified_cols,
NULL,
@@ -2999,6 +3020,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
CmdType event = parsetree->commandType;
bool instead = false;
bool returning = false;
+ bool updatableview = false;
Query *qual_product = NULL;
List *rewritten = NIL;
ListCell *lc1;
@@ -3081,6 +3103,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
Relation rt_entry_relation;
List *locks;
List *product_queries;
+ bool hasUpdate = false;
result_relation = parsetree->resultRelation;
Assert(result_relation != 0);
@@ -3123,19 +3146,41 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
List *attrnos;
/* Process the main targetlist ... */
- rewriteTargetListIU(parsetree, rt_entry_relation, &attrnos);
+ parsetree->targetList = rewriteTargetListIU(parsetree->targetList,
+ parsetree->commandType,
+ rt_entry_relation,
+ parsetree->resultRelation,
+ &attrnos);
/* ... and the VALUES expression lists */
rewriteValuesRTE(values_rte, rt_entry_relation, attrnos);
}
else
{
/* Process just the main targetlist */
- rewriteTargetListIU(parsetree, rt_entry_relation, NULL);
+ parsetree->targetList =
+ rewriteTargetListIU(parsetree->targetList,
+ parsetree->commandType,
+ rt_entry_relation,
+ parsetree->resultRelation, NULL);
+ }
+
+ if (parsetree->onConflict &&
+ parsetree->onConflict->action == ONCONFLICT_UPDATE)
+ {
+ parsetree->onConflict->onConflictSet =
+ rewriteTargetListIU(parsetree->onConflict->onConflictSet,
+ CMD_UPDATE,
+ rt_entry_relation,
+ parsetree->resultRelation,
+ NULL);
}
}
else if (event == CMD_UPDATE)
{
- rewriteTargetListIU(parsetree, rt_entry_relation, NULL);
+ parsetree->targetList =
+ rewriteTargetListIU(parsetree->targetList,
+ parsetree->commandType, rt_entry_relation,
+ parsetree->resultRelation, NULL);
rewriteTargetListUD(parsetree, rt_entry, rt_entry_relation);
}
else if (event == CMD_DELETE)
@@ -3149,7 +3194,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
* Collect and apply the appropriate rules.
*/
locks = matchLocks(event, rt_entry_relation->rd_rules,
- result_relation, parsetree);
+ result_relation, parsetree, &hasUpdate);
product_queries = fireRules(parsetree,
result_relation,
@@ -3198,6 +3243,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
*/
instead = true;
returning = true;
+ updatableview = true;
}
/*
@@ -3278,6 +3324,17 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
}
}
+ /*
+ * Updatable views are supported by ON CONFLICT, so don't prevent that
+ * case from proceeding
+ */
+ if (parsetree->onConflict &&
+ (product_queries != NIL || hasUpdate) &&
+ !updatableview)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("INSERT with ON CONFLICT clause cannot be used with table that has INSERT or UPDATE rules")));
+
heap_close(rt_entry_relation, NoLock);
}
diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c
index b0b308118f..2c095ce88a 100644
--- a/src/backend/rewrite/rowsecurity.c
+++ b/src/backend/rewrite/rowsecurity.c
@@ -89,9 +89,10 @@ row_security_policy_hook_type row_security_policy_hook_restrictive = NULL;
* set to true if any of the quals returned contain sublinks.
*/
void
-get_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index,
- List **securityQuals, List **withCheckOptions,
- bool *hasRowSecurity, bool *hasSubLinks)
+get_row_security_policies(Query* root, CmdType commandType, RangeTblEntry* rte,
+ int rt_index, List **securityQuals,
+ List **withCheckOptions, bool *hasRowSecurity,
+ bool *hasSubLinks)
{
Expr *rowsec_expr = NULL;
Expr *rowsec_with_check_expr = NULL;
@@ -159,7 +160,7 @@ get_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index,
/* Grab the built-in policies which should be applied to this relation. */
rel = heap_open(rte->relid, NoLock);
- rowsec_policies = pull_row_security_policies(root->commandType, rel,
+ rowsec_policies = pull_row_security_policies(commandType, rel,
user_id);
/*
@@ -201,7 +202,7 @@ get_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index,
*/
if (row_security_policy_hook_restrictive)
{
- hook_policies_restrictive = (*row_security_policy_hook_restrictive)(root->commandType, rel);
+ hook_policies_restrictive = (*row_security_policy_hook_restrictive)(commandType, rel);
/* Build the expression from any policies returned. */
if (hook_policies_restrictive != NIL)
@@ -214,7 +215,7 @@ get_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index,
if (row_security_policy_hook_permissive)
{
- hook_policies_permissive = (*row_security_policy_hook_permissive)(root->commandType, rel);
+ hook_policies_permissive = (*row_security_policy_hook_permissive)(commandType, rel);
/* Build the expression from any policies returned. */
if (hook_policies_permissive != NIL)
@@ -242,7 +243,7 @@ get_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index,
* WITH CHECK policy (this will be a copy of the USING policy, if no
* explicit WITH CHECK policy exists).
*/
- if (root->commandType == CMD_INSERT || root->commandType == CMD_UPDATE)
+ if (commandType == CMD_INSERT || commandType == CMD_UPDATE)
{
/*
* WITH CHECK OPTIONS wants a WCO node which wraps each Expr, so
@@ -259,7 +260,7 @@ get_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index,
WithCheckOption *wco;
wco = (WithCheckOption *) makeNode(WithCheckOption);
- wco->kind = root->commandType == CMD_INSERT ? WCO_RLS_INSERT_CHECK :
+ wco->kind = commandType == CMD_INSERT ? WCO_RLS_INSERT_CHECK :
WCO_RLS_UPDATE_CHECK;
wco->relname = pstrdup(RelationGetRelationName(rel));
wco->qual = (Node *) hook_with_check_expr_restrictive;
@@ -276,7 +277,7 @@ get_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index,
WithCheckOption *wco;
wco = (WithCheckOption *) makeNode(WithCheckOption);
- wco->kind = root->commandType == CMD_INSERT ? WCO_RLS_INSERT_CHECK :
+ wco->kind = commandType == CMD_INSERT ? WCO_RLS_INSERT_CHECK :
WCO_RLS_UPDATE_CHECK;
wco->relname = pstrdup(RelationGetRelationName(rel));
wco->qual = (Node *) rowsec_with_check_expr;
@@ -289,7 +290,7 @@ get_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index,
WithCheckOption *wco;
wco = (WithCheckOption *) makeNode(WithCheckOption);
- wco->kind = root->commandType == CMD_INSERT ? WCO_RLS_INSERT_CHECK :
+ wco->kind = commandType == CMD_INSERT ? WCO_RLS_INSERT_CHECK :
WCO_RLS_UPDATE_CHECK;
wco->relname = pstrdup(RelationGetRelationName(rel));
wco->qual = (Node *) hook_with_check_expr_permissive;
@@ -312,19 +313,72 @@ get_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index,
combined_qual_eval = makeBoolExpr(OR_EXPR, combined_quals, -1);
wco = (WithCheckOption *) makeNode(WithCheckOption);
- wco->kind = root->commandType == CMD_INSERT ? WCO_RLS_INSERT_CHECK :
+ wco->kind = commandType == CMD_INSERT ? WCO_RLS_INSERT_CHECK :
WCO_RLS_UPDATE_CHECK;
wco->relname = pstrdup(RelationGetRelationName(rel));
wco->qual = (Node *) combined_qual_eval;
wco->cascaded = false;
*withCheckOptions = lappend(*withCheckOptions, wco);
}
+
+ /*
+ * ON CONFLICT DO UPDATE has an RTE that is subject to both INSERT and
+ * UPDATE RLS enforcement. Those are enforced (as a special, distinct
+ * kind of WCO) on the target tuple.
+ *
+ * Make a second, recursive pass over the RTE for this, gathering
+ * UPDATE-applicable RLS checks/WCOs, and gathering and converting
+ * UPDATE-applicable security quals into WCO_RLS_CONFLICT_CHECK RLS
+ * checks/WCOs. Finally, these distinct kinds of RLS checks/WCOs are
+ * concatenated with our own INSERT-applicable list.
+ */
+ if (root->onConflict && root->onConflict->action == ONCONFLICT_UPDATE &&
+ commandType == CMD_INSERT)
+ {
+ List *conflictSecurityQuals = NIL;
+ List *conflictWCOs = NIL;
+ ListCell *item;
+ bool conflictHasRowSecurity = false;
+ bool conflictHasSublinks = false;
+
+ /* Assume that RTE is target resultRelation */
+ get_row_security_policies(root, CMD_UPDATE, rte, rt_index,
+ &conflictSecurityQuals, &conflictWCOs,
+ &conflictHasRowSecurity,
+ &conflictHasSublinks);
+
+ if (conflictHasRowSecurity)
+ *hasRowSecurity = true;
+ if (conflictHasSublinks)
+ *hasSubLinks = true;
+
+ /*
+ * Append WITH CHECK OPTIONs/RLS checks, which should not conflict
+ * between this INSERT and the auxiliary UPDATE
+ */
+ *withCheckOptions = list_concat(*withCheckOptions,
+ conflictWCOs);
+
+ foreach(item, conflictSecurityQuals)
+ {
+ Expr *conflict_rowsec_expr = (Expr *) lfirst(item);
+ WithCheckOption *wco;
+
+ wco = (WithCheckOption *) makeNode(WithCheckOption);
+
+ wco->kind = WCO_RLS_CONFLICT_CHECK;
+ wco->relname = pstrdup(RelationGetRelationName(rel));
+ wco->qual = (Node *) copyObject(conflict_rowsec_expr);
+ wco->cascaded = false;
+ *withCheckOptions = lappend(*withCheckOptions, wco);
+ }
+ }
}
/* For SELECT, UPDATE, and DELETE, set the security quals */
- if (root->commandType == CMD_SELECT
- || root->commandType == CMD_UPDATE
- || root->commandType == CMD_DELETE)
+ if (commandType == CMD_SELECT
+ || commandType == CMD_UPDATE
+ || commandType == CMD_DELETE)
{
/* restrictive policies can simply be added to the list first */
if (hook_expr_restrictive)