summaryrefslogtreecommitdiff
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.c403
1 files changed, 287 insertions, 116 deletions
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index f60b34deb64..b8839b56b7a 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -87,10 +87,9 @@ static void rewriteValuesRTEToNulls(Query *parsetree, RangeTblEntry *rte);
static void markQueryForLocking(Query *qry, Node *jtnode,
LockClauseStrength strength, LockWaitPolicy waitPolicy,
bool pushedDown);
-static List *matchLocks(CmdType event, RuleLock *rulelocks,
+static List *matchLocks(CmdType event, Relation relation,
int varno, Query *parsetree, bool *hasUpdate);
static Query *fireRIRrules(Query *parsetree, List *activeRIRs);
-static bool view_has_instead_trigger(Relation view, CmdType event);
static Bitmapset *adjust_view_column_set(Bitmapset *cols, List *targetlist);
@@ -1482,7 +1481,7 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti,
*/
isAutoUpdatableView = false;
if (target_relation->rd_rel->relkind == RELKIND_VIEW &&
- !view_has_instead_trigger(target_relation, CMD_INSERT))
+ !view_has_instead_trigger(target_relation, CMD_INSERT, NIL))
{
List *locks;
bool hasUpdate;
@@ -1490,7 +1489,7 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti,
ListCell *l;
/* Look for an unconditional DO INSTEAD rule */
- locks = matchLocks(CMD_INSERT, target_relation->rd_rules,
+ locks = matchLocks(CMD_INSERT, target_relation,
parsetree->resultRelation, parsetree, &hasUpdate);
found = false;
@@ -1654,15 +1653,16 @@ rewriteValuesRTEToNulls(Query *parsetree, RangeTblEntry *rte)
/*
* matchLocks -
- * match the list of locks and returns the matching rules
+ * match a relation's list of locks and returns the matching rules
*/
static List *
matchLocks(CmdType event,
- RuleLock *rulelocks,
+ Relation relation,
int varno,
Query *parsetree,
bool *hasUpdate)
{
+ RuleLock *rulelocks = relation->rd_rules;
List *matching_locks = NIL;
int nlocks;
int i;
@@ -1670,10 +1670,6 @@ matchLocks(CmdType event,
if (rulelocks == NULL)
return NIL;
- /* No rule support for MERGE */
- if (parsetree->commandType == CMD_MERGE)
- return NIL;
-
if (parsetree->commandType != CMD_SELECT)
{
if (parsetree->resultRelation != varno)
@@ -1691,7 +1687,7 @@ matchLocks(CmdType event,
/*
* Suppress ON INSERT/UPDATE/DELETE rules that are disabled or
- * configured to not fire during the current sessions replication
+ * configured to not fire during the current session's replication
* role. ON SELECT rules will always be applied in order to keep views
* working even in LOCAL or REPLICA role.
*/
@@ -1709,6 +1705,14 @@ matchLocks(CmdType event,
oneLock->enabled == RULE_DISABLED)
continue;
}
+
+ /* Non-SELECT rules are not supported for MERGE */
+ if (parsetree->commandType == CMD_MERGE)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot execute MERGE on relation \"%s\"",
+ RelationGetRelationName(relation)),
+ errdetail("MERGE is not supported for relations with rules."));
}
if (oneLock->event == event)
@@ -1755,9 +1759,9 @@ ApplyRetrieveRule(Query *parsetree,
* For INSERT, we needn't do anything. The unmodified RTE will serve
* fine as the result relation.
*
- * For UPDATE/DELETE, we need to expand the view so as to have source
- * data for the operation. But we also need an unmodified RTE to
- * serve as the target. So, copy the RTE and add the copy to the
+ * For UPDATE/DELETE/MERGE, we need to expand the view so as to have
+ * source data for the operation. But we also need an unmodified RTE
+ * to serve as the target. So, copy the RTE and add the copy to the
* rangetable. Note that the copy does not get added to the jointree.
* Also note that there's a hack in fireRIRrules to avoid calling this
* function again when it arrives at the copied RTE.
@@ -1765,7 +1769,8 @@ ApplyRetrieveRule(Query *parsetree,
if (parsetree->commandType == CMD_INSERT)
return parsetree;
else if (parsetree->commandType == CMD_UPDATE ||
- parsetree->commandType == CMD_DELETE)
+ parsetree->commandType == CMD_DELETE ||
+ parsetree->commandType == CMD_MERGE)
{
RangeTblEntry *newrte;
Var *var;
@@ -1775,6 +1780,7 @@ ApplyRetrieveRule(Query *parsetree,
newrte = copyObject(rte);
parsetree->rtable = lappend(parsetree->rtable, newrte);
parsetree->resultRelation = list_length(parsetree->rtable);
+ /* parsetree->mergeTargetRelation unchanged (use expanded view) */
/*
* For the most part, Vars referencing the view should remain as
@@ -2470,9 +2476,15 @@ get_view_query(Relation view)
* If it does, we don't want to treat it as auto-updatable. This test can't
* be folded into view_query_is_auto_updatable because it's not an error
* condition.
+ *
+ * For MERGE, this will return true if there is an INSTEAD OF trigger for
+ * every action in mergeActionList, and false if there are any actions that
+ * lack an INSTEAD OF trigger. If there are no data-modifying MERGE actions
+ * (only DO NOTHING actions), true is returned so that the view is treated
+ * as trigger-updatable, rather than erroring out if it's not auto-updatable.
*/
-static bool
-view_has_instead_trigger(Relation view, CmdType event)
+bool
+view_has_instead_trigger(Relation view, CmdType event, List *mergeActionList)
{
TriggerDesc *trigDesc = view->trigdesc;
@@ -2490,6 +2502,32 @@ view_has_instead_trigger(Relation view, CmdType event)
if (trigDesc && trigDesc->trig_delete_instead_row)
return true;
break;
+ case CMD_MERGE:
+ foreach_node(MergeAction, action, mergeActionList)
+ {
+ switch (action->commandType)
+ {
+ case CMD_INSERT:
+ if (!trigDesc || !trigDesc->trig_insert_instead_row)
+ return false;
+ break;
+ case CMD_UPDATE:
+ if (!trigDesc || !trigDesc->trig_update_instead_row)
+ return false;
+ break;
+ case CMD_DELETE:
+ if (!trigDesc || !trigDesc->trig_delete_instead_row)
+ return false;
+ break;
+ case CMD_NOTHING:
+ /* No trigger required */
+ break;
+ default:
+ elog(ERROR, "unrecognized commandType: %d", action->commandType);
+ break;
+ }
+ }
+ return true; /* no actions without an INSTEAD OF trigger */
default:
elog(ERROR, "unrecognized CmdType: %d", (int) event);
break;
@@ -3031,6 +3069,105 @@ adjust_view_column_set(Bitmapset *cols, List *targetlist)
/*
+ * error_view_not_updatable -
+ * Report an error due to an attempt to update a non-updatable view.
+ *
+ * Generally this is expected to be called from the rewriter, with suitable
+ * error detail explaining why the view is not updatable. Note, however, that
+ * the executor also performs a just-in-case check that the target view is
+ * updatable. That check is expected to never fail, but if it does, it will
+ * call this function with NULL error detail --- see CheckValidResultRel().
+ *
+ * Note: for MERGE, at least one of the actions in mergeActionList is expected
+ * to lack a suitable INSTEAD OF trigger --- see view_has_instead_trigger().
+ */
+void
+error_view_not_updatable(Relation view,
+ CmdType command,
+ List *mergeActionList,
+ const char *detail)
+{
+ TriggerDesc *trigDesc = view->trigdesc;
+
+ switch (command)
+ {
+ case CMD_INSERT:
+ ereport(ERROR,
+ errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("cannot insert into view \"%s\"",
+ RelationGetRelationName(view)),
+ detail ? errdetail_internal("%s", _(detail)) : 0,
+ errhint("To enable inserting into the view, provide an INSTEAD OF INSERT trigger or an unconditional ON INSERT DO INSTEAD rule."));
+ break;
+ case CMD_UPDATE:
+ ereport(ERROR,
+ errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("cannot update view \"%s\"",
+ RelationGetRelationName(view)),
+ detail ? errdetail_internal("%s", _(detail)) : 0,
+ errhint("To enable updating the view, provide an INSTEAD OF UPDATE trigger or an unconditional ON UPDATE DO INSTEAD rule."));
+ break;
+ case CMD_DELETE:
+ ereport(ERROR,
+ errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("cannot delete from view \"%s\"",
+ RelationGetRelationName(view)),
+ detail ? errdetail_internal("%s", _(detail)) : 0,
+ errhint("To enable deleting from the view, provide an INSTEAD OF DELETE trigger or an unconditional ON DELETE DO INSTEAD rule."));
+ break;
+ case CMD_MERGE:
+
+ /*
+ * Note that the error hints here differ from above, since MERGE
+ * doesn't support rules.
+ */
+ foreach_node(MergeAction, action, mergeActionList)
+ {
+ switch (action->commandType)
+ {
+ case CMD_INSERT:
+ if (!trigDesc || !trigDesc->trig_insert_instead_row)
+ ereport(ERROR,
+ errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("cannot insert into view \"%s\"",
+ RelationGetRelationName(view)),
+ detail ? errdetail_internal("%s", _(detail)) : 0,
+ errhint("To enable inserting into the view using MERGE, provide an INSTEAD OF INSERT trigger."));
+ break;
+ case CMD_UPDATE:
+ if (!trigDesc || !trigDesc->trig_update_instead_row)
+ ereport(ERROR,
+ errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("cannot update view \"%s\"",
+ RelationGetRelationName(view)),
+ detail ? errdetail_internal("%s", _(detail)) : 0,
+ errhint("To enable updating the view using MERGE, provide an INSTEAD OF UPDATE trigger."));
+ break;
+ case CMD_DELETE:
+ if (!trigDesc || !trigDesc->trig_delete_instead_row)
+ ereport(ERROR,
+ errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("cannot delete from view \"%s\"",
+ RelationGetRelationName(view)),
+ detail ? errdetail_internal("%s", _(detail)) : 0,
+ errhint("To enable deleting from the view using MERGE, provide an INSTEAD OF DELETE trigger."));
+ break;
+ case CMD_NOTHING:
+ break;
+ default:
+ elog(ERROR, "unrecognized commandType: %d", action->commandType);
+ break;
+ }
+ }
+ break;
+ default:
+ elog(ERROR, "unrecognized CmdType: %d", (int) command);
+ break;
+ }
+}
+
+
+/*
* rewriteTargetView -
* Attempt to rewrite a query where the target relation is a view, so that
* the view's base relation becomes the target relation.
@@ -3043,6 +3180,7 @@ static Query *
rewriteTargetView(Query *parsetree, Relation view)
{
Query *viewquery;
+ bool insert_or_update;
const char *auto_update_detail;
RangeTblRef *rtr;
int base_rt_index;
@@ -3066,55 +3204,52 @@ rewriteTargetView(Query *parsetree, Relation view)
*/
viewquery = copyObject(get_view_query(view));
- /* The view must be updatable, else fail */
- auto_update_detail =
- view_query_is_auto_updatable(viewquery,
- parsetree->commandType != CMD_DELETE);
+ /*
+ * Are we doing INSERT/UPDATE, or MERGE containing INSERT/UPDATE? If so,
+ * various additional checks on the view columns need to be applied, and
+ * any view CHECK OPTIONs need to be enforced.
+ */
+ insert_or_update =
+ (parsetree->commandType == CMD_INSERT ||
+ parsetree->commandType == CMD_UPDATE);
- if (auto_update_detail)
+ if (parsetree->commandType == CMD_MERGE)
{
- /* messages here should match execMain.c's CheckValidResultRel */
- switch (parsetree->commandType)
+ foreach_node(MergeAction, action, parsetree->mergeActionList)
{
- case CMD_INSERT:
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("cannot insert into view \"%s\"",
- RelationGetRelationName(view)),
- errdetail_internal("%s", _(auto_update_detail)),
- errhint("To enable inserting into the view, provide an INSTEAD OF INSERT trigger or an unconditional ON INSERT DO INSTEAD rule.")));
- break;
- case CMD_UPDATE:
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("cannot update view \"%s\"",
- RelationGetRelationName(view)),
- errdetail_internal("%s", _(auto_update_detail)),
- errhint("To enable updating the view, provide an INSTEAD OF UPDATE trigger or an unconditional ON UPDATE DO INSTEAD rule.")));
- break;
- case CMD_DELETE:
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("cannot delete from view \"%s\"",
- RelationGetRelationName(view)),
- errdetail_internal("%s", _(auto_update_detail)),
- errhint("To enable deleting from the view, provide an INSTEAD OF DELETE trigger or an unconditional ON DELETE DO INSTEAD rule.")));
- break;
- default:
- elog(ERROR, "unrecognized CmdType: %d",
- (int) parsetree->commandType);
+ if (action->commandType == CMD_INSERT ||
+ action->commandType == CMD_UPDATE)
+ {
+ insert_or_update = true;
break;
+ }
}
}
/*
- * For INSERT/UPDATE the modified columns must all be updatable. Note that
- * we get the modified columns from the query's targetlist, not from the
- * result RTE's insertedCols and/or updatedCols set, since
- * rewriteTargetListIU may have added additional targetlist entries for
- * view defaults, and these must also be updatable.
+ * The view must be updatable, else fail.
+ *
+ * If we are doing INSERT/UPDATE (or MERGE containing INSERT/UPDATE), we
+ * also check that there is at least one updatable column.
*/
- if (parsetree->commandType != CMD_DELETE)
+ auto_update_detail =
+ view_query_is_auto_updatable(viewquery, insert_or_update);
+
+ if (auto_update_detail)
+ error_view_not_updatable(view,
+ parsetree->commandType,
+ parsetree->mergeActionList,
+ auto_update_detail);
+
+ /*
+ * For INSERT/UPDATE (or MERGE containing INSERT/UPDATE) the modified
+ * columns must all be updatable. Note that we get the modified columns
+ * from the query's targetlist, not from the result RTE's insertedCols
+ * and/or updatedCols set, since rewriteTargetListIU may have added
+ * additional targetlist entries for view defaults, and these must also be
+ * updatable.
+ */
+ if (insert_or_update)
{
Bitmapset *modified_cols = NULL;
char *non_updatable_col;
@@ -3140,6 +3275,20 @@ rewriteTargetView(Query *parsetree, Relation view)
}
}
+ foreach_node(MergeAction, action, parsetree->mergeActionList)
+ {
+ if (action->commandType == CMD_INSERT ||
+ action->commandType == CMD_UPDATE)
+ {
+ foreach_node(TargetEntry, tle, action->targetList)
+ {
+ 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,
@@ -3168,6 +3317,14 @@ rewriteTargetView(Query *parsetree, Relation view)
RelationGetRelationName(view)),
errdetail_internal("%s", _(auto_update_detail))));
break;
+ case CMD_MERGE:
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot merge into column \"%s\" of view \"%s\"",
+ non_updatable_col,
+ RelationGetRelationName(view)),
+ errdetail_internal("%s", _(auto_update_detail))));
+ break;
default:
elog(ERROR, "unrecognized CmdType: %d",
(int) parsetree->commandType);
@@ -3176,6 +3333,28 @@ rewriteTargetView(Query *parsetree, Relation view)
}
}
+ /*
+ * For MERGE, there must not be any INSTEAD OF triggers on an otherwise
+ * updatable view. The caller already checked that there isn't a full set
+ * of INSTEAD OF triggers, so this is to guard against having a partial
+ * set (mixing auto-update and trigger-update actions in a single command
+ * isn't supported).
+ */
+ if (parsetree->commandType == CMD_MERGE)
+ {
+ foreach_node(MergeAction, action, parsetree->mergeActionList)
+ {
+ if (action->commandType != CMD_NOTHING &&
+ view_has_instead_trigger(view, action->commandType, NIL))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot merge into view \"%s\"",
+ RelationGetRelationName(view)),
+ errdetail("MERGE is not supported for views with INSTEAD OF triggers for some actions, but not others."),
+ errhint("To enable merging into the view, either provide a full set of INSTEAD OF triggers or drop the existing INSTEAD OF triggers."));
+ }
+ }
+
/* Locate RTE describing the view in the outer query */
view_rte = rt_fetch(parsetree->resultRelation, parsetree->rtable);
@@ -3239,8 +3418,8 @@ rewriteTargetView(Query *parsetree, Relation view)
new_rt_index = list_length(parsetree->rtable);
/*
- * INSERTs never inherit. For UPDATE/DELETE, we use the view query's
- * inheritance flag for the base relation.
+ * INSERTs never inherit. For UPDATE/DELETE/MERGE, we use the view
+ * query's inheritance flag for the base relation.
*/
if (parsetree->commandType == CMD_INSERT)
new_rte->inh = false;
@@ -3362,11 +3541,12 @@ rewriteTargetView(Query *parsetree, Relation view)
/*
* For INSERT/UPDATE we must also update resnos in the targetlist to refer
* to columns of the base relation, since those indicate the target
- * columns to be affected.
+ * columns to be affected. Similarly, for MERGE we must update the resnos
+ * in the merge action targetlists of any INSERT/UPDATE actions.
*
- * Note that this destroys the resno ordering of the targetlist, but that
+ * Note that this destroys the resno ordering of the targetlists, but that
* will be fixed when we recurse through RewriteQuery, which will invoke
- * rewriteTargetListIU again on the updated targetlist.
+ * rewriteTargetListIU again on the updated targetlists.
*/
if (parsetree->commandType != CMD_DELETE)
{
@@ -3385,6 +3565,28 @@ rewriteTargetView(Query *parsetree, Relation view)
elog(ERROR, "attribute number %d not found in view targetlist",
tle->resno);
}
+
+ foreach_node(MergeAction, action, parsetree->mergeActionList)
+ {
+ if (action->commandType == CMD_INSERT ||
+ action->commandType == CMD_UPDATE)
+ {
+ foreach_node(TargetEntry, tle, action->targetList)
+ {
+ TargetEntry *view_tle;
+
+ if (tle->resjunk)
+ continue;
+
+ view_tle = get_tle_by_resno(view_targetlist, tle->resno);
+ if (view_tle != NULL && !view_tle->resjunk && IsA(view_tle->expr, Var))
+ tle->resno = ((Var *) view_tle->expr)->varattno;
+ else
+ elog(ERROR, "attribute number %d not found in view targetlist",
+ tle->resno);
+ }
+ }
+ }
}
/*
@@ -3477,10 +3679,10 @@ rewriteTargetView(Query *parsetree, Relation view)
}
/*
- * For UPDATE/DELETE, pull up any WHERE quals from the view. We know that
- * any Vars in the quals must reference the one base relation, so we need
- * only adjust their varnos to reference the new target (just the same as
- * we did with the view targetlist).
+ * For UPDATE/DELETE/MERGE, pull up any WHERE quals from the view. We
+ * know that any Vars in the quals must reference the one base relation,
+ * so we need only adjust their varnos to reference the new target (just
+ * the same as we did with the view targetlist).
*
* If it's a security-barrier view, its WHERE quals must be applied before
* quals from the outer query, so we attach them to the RTE as security
@@ -3532,11 +3734,12 @@ rewriteTargetView(Query *parsetree, Relation view)
}
/*
- * 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.
+ * For INSERT/UPDATE (or MERGE containing 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)
+ if (insert_or_update)
{
bool has_wco = RelationHasCheckOption(view);
bool cascaded = RelationHasCascadedCheckOption(view);
@@ -3590,14 +3793,13 @@ rewriteTargetView(Query *parsetree, Relation view)
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, and this
- * check will already have been done.
+ * For INSERT, make sure that the query is marked correctly if
+ * the added qual has sublinks. This can be skipped for
+ * UPDATE/MERGE, since the same qual will have already been
+ * added above, and the check will already have been done.
*/
if (!parsetree->hasSubLinks &&
- parsetree->commandType != CMD_UPDATE)
+ parsetree->commandType == CMD_INSERT)
parsetree->hasSubLinks = checkExprHasSubLink(wco->qual);
}
}
@@ -3867,7 +4069,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events, int orig_rt_length)
/*
* Collect and apply the appropriate rules.
*/
- locks = matchLocks(event, rt_entry_relation->rd_rules,
+ locks = matchLocks(event, rt_entry_relation,
result_relation, parsetree, &hasUpdate);
product_orig_rt_length = list_length(parsetree->rtable);
@@ -3938,7 +4140,8 @@ RewriteQuery(Query *parsetree, List *rewrite_events, int orig_rt_length)
* automatically updated. If so, we perform the necessary query
* transformation here and add the resulting query to the
* product_queries list, so that it gets recursively rewritten if
- * necessary.
+ * necessary. For MERGE, the view must be automatically updatable if
+ * any of the merge actions lack a corresponding INSTEAD OF trigger.
*
* If the view cannot be automatically updated, we throw an error here
* which is OK since the query would fail at runtime anyway. Throwing
@@ -3948,51 +4151,19 @@ RewriteQuery(Query *parsetree, List *rewrite_events, int orig_rt_length)
*/
if (!instead &&
rt_entry_relation->rd_rel->relkind == RELKIND_VIEW &&
- !view_has_instead_trigger(rt_entry_relation, event))
+ !view_has_instead_trigger(rt_entry_relation, event,
+ parsetree->mergeActionList))
{
/*
* If there were any qualified INSTEAD rules, don't allow the view
* to be automatically updated (an unqualified INSTEAD rule or
* INSTEAD OF trigger is required).
- *
- * The messages here should match execMain.c's CheckValidResultRel
- * and in principle make those checks in executor unnecessary, but
- * we keep them just in case.
*/
if (qual_product != NULL)
- {
- switch (parsetree->commandType)
- {
- case CMD_INSERT:
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("cannot insert into view \"%s\"",
- RelationGetRelationName(rt_entry_relation)),
- errdetail("Views with conditional DO INSTEAD rules are not automatically updatable."),
- errhint("To enable inserting into the view, provide an INSTEAD OF INSERT trigger or an unconditional ON INSERT DO INSTEAD rule.")));
- break;
- case CMD_UPDATE:
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("cannot update view \"%s\"",
- RelationGetRelationName(rt_entry_relation)),
- errdetail("Views with conditional DO INSTEAD rules are not automatically updatable."),
- errhint("To enable updating the view, provide an INSTEAD OF UPDATE trigger or an unconditional ON UPDATE DO INSTEAD rule.")));
- break;
- case CMD_DELETE:
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("cannot delete from view \"%s\"",
- RelationGetRelationName(rt_entry_relation)),
- errdetail("Views with conditional DO INSTEAD rules are not automatically updatable."),
- errhint("To enable deleting from the view, provide an INSTEAD OF DELETE trigger or an unconditional ON DELETE DO INSTEAD rule.")));
- break;
- default:
- elog(ERROR, "unrecognized CmdType: %d",
- (int) parsetree->commandType);
- break;
- }
- }
+ error_view_not_updatable(rt_entry_relation,
+ parsetree->commandType,
+ parsetree->mergeActionList,
+ gettext_noop("Views with conditional DO INSTEAD rules are not automatically updatable."));
/*
* Attempt to rewrite the query to automatically update the view.