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.c102
1 files changed, 78 insertions, 24 deletions
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 01875fcd45f..a467588e50e 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -2014,6 +2014,7 @@ view_is_auto_updatable(Relation view)
base_rte = rt_fetch(rtr->rtindex, viewquery->rtable);
if (base_rte->rtekind != RTE_RELATION ||
(base_rte->relkind != RELKIND_RELATION &&
+ base_rte->relkind != RELKIND_FOREIGN_TABLE &&
base_rte->relkind != RELKIND_VIEW))
return gettext_noop("Views that do not select from a single table or view are not automatically updatable.");
@@ -2058,49 +2059,56 @@ view_is_auto_updatable(Relation view)
/*
- * relation_is_updatable - test if the specified relation is updatable.
+ * relation_is_updatable - determine which update events the specified
+ * relation supports.
*
* This is used for the information_schema views, which have separate concepts
* of "updatable" and "trigger updatable". A relation is "updatable" if it
* can be updated without the need for triggers (either because it has a
* suitable RULE, or because it is simple enough to be automatically updated).
- *
* A relation is "trigger updatable" if it has a suitable INSTEAD OF trigger.
* The SQL standard regards this as not necessarily updatable, presumably
* because there is no way of knowing what the trigger will actually do.
- * That's currently handled directly in the information_schema views, so
- * need not be considered here.
- *
- * In the case of an automatically updatable view, the base relation must
- * also be updatable.
+ * The information_schema views therefore call this function with
+ * include_triggers = false. However, other callers might only care whether
+ * data-modifying SQL will work, so they can pass include_triggers = true
+ * to have trigger updatability included in the result.
*
- * reloid is the pg_class OID to examine. req_events is a bitmask of
- * rule event numbers; the relation is considered rule-updatable if it has
- * all the specified rules. (We do it this way so that we can test for
- * UPDATE plus DELETE rules in a single call.)
+ * The return value is a bitmask of rule event numbers indicating which of
+ * the INSERT, UPDATE and DELETE operations are supported. (We do it this way
+ * so that we can test for UPDATE plus DELETE support in a single call.)
*/
-bool
-relation_is_updatable(Oid reloid, int req_events)
+int
+relation_is_updatable(Oid reloid, bool include_triggers)
{
+ int events = 0;
Relation rel;
RuleLock *rulelocks;
+#define ALL_EVENTS ((1 << CMD_INSERT) | (1 << CMD_UPDATE) | (1 << CMD_DELETE))
+
rel = try_relation_open(reloid, AccessShareLock);
/*
- * If the relation doesn't exist, say "false" rather than throwing an
+ * If the relation doesn't exist, return zero rather than throwing an
* error. This is helpful since scanning an information_schema view under
* MVCC rules can result in referencing rels that were just deleted
* according to a SnapshotNow probe.
*/
if (rel == NULL)
- return false;
+ return 0;
+
+ /* If the relation is a table, it is always updatable */
+ if (rel->rd_rel->relkind == RELKIND_RELATION)
+ {
+ relation_close(rel, AccessShareLock);
+ return ALL_EVENTS;
+ }
/* Look for unconditional DO INSTEAD rules, and note supported events */
rulelocks = rel->rd_rules;
if (rulelocks != NULL)
{
- int events = 0;
int i;
for (i = 0; i < rulelocks->numLocks; i++)
@@ -2108,16 +2116,61 @@ relation_is_updatable(Oid reloid, int req_events)
if (rulelocks->rules[i]->isInstead &&
rulelocks->rules[i]->qual == NULL)
{
- events |= 1 << rulelocks->rules[i]->event;
+ events |= ((1 << rulelocks->rules[i]->event) & ALL_EVENTS);
}
}
- /* If we have all rules needed, say "yes" */
- if ((events & req_events) == req_events)
+ /* If we have rules for all events, we're done */
+ if (events == ALL_EVENTS)
{
relation_close(rel, AccessShareLock);
- return true;
+ return events;
+ }
+ }
+
+ /* Similarly look for INSTEAD OF triggers, if they are to be included */
+ if (include_triggers)
+ {
+ TriggerDesc *trigDesc = rel->trigdesc;
+
+ if (trigDesc)
+ {
+ if (trigDesc->trig_insert_instead_row)
+ events |= (1 << CMD_INSERT);
+ if (trigDesc->trig_update_instead_row)
+ events |= (1 << CMD_UPDATE);
+ if (trigDesc->trig_delete_instead_row)
+ events |= (1 << CMD_DELETE);
+
+ /* If we have triggers for all events, we're done */
+ if (events == ALL_EVENTS)
+ {
+ relation_close(rel, AccessShareLock);
+ return events;
+ }
+ }
+ }
+
+ /* If this is a foreign table, check which update events it supports */
+ if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+ {
+ FdwRoutine *fdwroutine = GetFdwRoutineForRelation(rel, false);
+
+ if (fdwroutine->IsForeignRelUpdatable != NULL)
+ events |= fdwroutine->IsForeignRelUpdatable(rel);
+ else
+ {
+ /* Assume presence of executor functions is sufficient */
+ if (fdwroutine->ExecForeignInsert != NULL)
+ events |= (1 << CMD_INSERT);
+ if (fdwroutine->ExecForeignUpdate != NULL)
+ events |= (1 << CMD_UPDATE);
+ if (fdwroutine->ExecForeignDelete != NULL)
+ events |= (1 << CMD_DELETE);
}
+
+ relation_close(rel, AccessShareLock);
+ return events;
}
/* Check if this is an automatically updatable view */
@@ -2133,25 +2186,26 @@ relation_is_updatable(Oid reloid, int req_events)
viewquery = get_view_query(rel);
rtr = (RangeTblRef *) linitial(viewquery->jointree->fromlist);
base_rte = rt_fetch(rtr->rtindex, viewquery->rtable);
+ Assert(base_rte->rtekind == RTE_RELATION);
if (base_rte->relkind == RELKIND_RELATION)
{
/* Tables are always updatable */
relation_close(rel, AccessShareLock);
- return true;
+ return ALL_EVENTS;
}
else
{
/* Do a recursive check for any other kind of base relation */
baseoid = base_rte->relid;
relation_close(rel, AccessShareLock);
- return relation_is_updatable(baseoid, req_events);
+ return relation_is_updatable(baseoid, include_triggers);
}
}
- /* If we reach here, the relation is not updatable */
+ /* If we reach here, the relation may support some update commands */
relation_close(rel, AccessShareLock);
- return false;
+ return events;
}