diff options
Diffstat (limited to 'src/backend/rewrite')
| -rw-r--r-- | src/backend/rewrite/rewriteHandler.c | 87 | ||||
| -rw-r--r-- | src/backend/rewrite/rowsecurity.c | 82 |
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) |
