summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2013-06-12 21:52:54 +0000
committerTom Lane2013-06-12 21:53:33 +0000
commitdc3eb5638349e74a6628130a5101ce866455f4a3 (patch)
tree8554627085e942bd694093de36c283393dcad25c
parent78ed8e03c67d7333708f5c1873ec1d239ae2d7e0 (diff)
Improve updatability checking for views and foreign tables.
Extend the FDW API (which we already changed for 9.3) so that an FDW can report whether specific foreign tables are insertable/updatable/deletable. The default assumption continues to be that they're updatable if the relevant executor callback function is supplied by the FDW, but finer granularity is now possible. As a test case, add an "updatable" option to contrib/postgres_fdw. This patch also fixes the information_schema views, which previously did not think that foreign tables were ever updatable, and fixes view_is_auto_updatable() so that a view on a foreign table can be auto-updatable. initdb forced due to changes in information_schema views and the functions they rely on. This is a bit unfortunate to do post-beta1, but if we don't change this now then we'll have another API break for FDWs when we do change it. Dean Rasheed, somewhat editorialized on by Tom Lane
-rw-r--r--contrib/postgres_fdw/expected/postgres_fdw.out1
-rw-r--r--contrib/postgres_fdw/option.c8
-rw-r--r--contrib/postgres_fdw/postgres_fdw.c47
-rw-r--r--contrib/postgres_fdw/sql/postgres_fdw.sql1
-rw-r--r--doc/src/sgml/fdwhandler.sgml27
-rw-r--r--doc/src/sgml/postgres-fdw.sgml37
-rw-r--r--src/backend/catalog/information_schema.sql15
-rw-r--r--src/backend/executor/execMain.c18
-rw-r--r--src/backend/rewrite/rewriteHandler.c102
-rw-r--r--src/backend/utils/adt/misc.c51
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/pg_proc.h8
-rw-r--r--src/include/foreign/fdwapi.h3
-rw-r--r--src/include/rewrite/rewriteHandler.h2
-rw-r--r--src/include/utils/builtins.h4
15 files changed, 272 insertions, 54 deletions
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 7a13d011d5..38c6cf8162 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -79,6 +79,7 @@ ALTER FOREIGN TABLE ft2 DROP COLUMN cx;
-- configure options
ALTER SERVER testserver1 OPTIONS (
use_remote_estimate 'false',
+ updatable 'true',
fdw_startup_cost '123.456',
fdw_tuple_cost '0.123',
service 'value',
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 123cb4f010..e1d4c47733 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -106,9 +106,10 @@ postgres_fdw_validator(PG_FUNCTION_ARGS)
/*
* Validate option value, when we can do so without any context.
*/
- if (strcmp(def->defname, "use_remote_estimate") == 0)
+ if (strcmp(def->defname, "use_remote_estimate") == 0 ||
+ strcmp(def->defname, "updatable") == 0)
{
- /* use_remote_estimate accepts only boolean values */
+ /* these accept only boolean values */
(void) defGetBoolean(def);
}
else if (strcmp(def->defname, "fdw_startup_cost") == 0 ||
@@ -151,6 +152,9 @@ InitPgFdwOptions(void)
/* cost factors */
{"fdw_startup_cost", ForeignServerRelationId, false},
{"fdw_tuple_cost", ForeignServerRelationId, false},
+ /* updatable is available on both server and table */
+ {"updatable", ForeignServerRelationId, false},
+ {"updatable", ForeignTableRelationId, false},
{NULL, InvalidOid, false}
};
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index cbfecc4dd4..1c93e0c5ac 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -277,6 +277,7 @@ static TupleTableSlot *postgresExecForeignDelete(EState *estate,
TupleTableSlot *planSlot);
static void postgresEndForeignModify(EState *estate,
ResultRelInfo *resultRelInfo);
+static int postgresIsForeignRelUpdatable(Relation rel);
static void postgresExplainForeignScan(ForeignScanState *node,
ExplainState *es);
static void postgresExplainForeignModify(ModifyTableState *mtstate,
@@ -355,6 +356,7 @@ postgres_fdw_handler(PG_FUNCTION_ARGS)
routine->ExecForeignUpdate = postgresExecForeignUpdate;
routine->ExecForeignDelete = postgresExecForeignDelete;
routine->EndForeignModify = postgresEndForeignModify;
+ routine->IsForeignRelUpdatable = postgresIsForeignRelUpdatable;
/* Support functions for EXPLAIN */
routine->ExplainForeignScan = postgresExplainForeignScan;
@@ -1597,6 +1599,51 @@ postgresEndForeignModify(EState *estate,
}
/*
+ * postgresIsForeignRelUpdatable
+ * Determine whether a foreign table supports INSERT, UPDATE and/or
+ * DELETE.
+ */
+static int
+postgresIsForeignRelUpdatable(Relation rel)
+{
+ bool updatable;
+ ForeignTable *table;
+ ForeignServer *server;
+ ListCell *lc;
+
+ /*
+ * By default, all postgres_fdw foreign tables are assumed updatable. This
+ * can be overridden by a per-server setting, which in turn can be
+ * overridden by a per-table setting.
+ */
+ updatable = true;
+
+ table = GetForeignTable(RelationGetRelid(rel));
+ server = GetForeignServer(table->serverid);
+
+ foreach(lc, server->options)
+ {
+ DefElem *def = (DefElem *) lfirst(lc);
+
+ if (strcmp(def->defname, "updatable") == 0)
+ updatable = defGetBoolean(def);
+ }
+ foreach(lc, table->options)
+ {
+ DefElem *def = (DefElem *) lfirst(lc);
+
+ if (strcmp(def->defname, "updatable") == 0)
+ updatable = defGetBoolean(def);
+ }
+
+ /*
+ * Currently "updatable" means support for INSERT, UPDATE and DELETE.
+ */
+ return updatable ?
+ (1 << CMD_INSERT) | (1 << CMD_UPDATE) | (1 << CMD_DELETE) : 0;
+}
+
+/*
* postgresExplainForeignScan
* Produce extra output for EXPLAIN of a ForeignScan on a foreign table
*/
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 19221680bf..ce8bb7597b 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -88,6 +88,7 @@ ALTER FOREIGN TABLE ft2 DROP COLUMN cx;
-- configure options
ALTER SERVER testserver1 OPTIONS (
use_remote_estimate 'false',
+ updatable 'true',
fdw_startup_cost '123.456',
fdw_tuple_cost '0.123',
service 'value',
diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index 912ca8663e..6c06f1a436 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -565,6 +565,33 @@ EndForeignModify (EState *estate,
<literal>NULL</>, no action is taken during executor shutdown.
</para>
+ <para>
+<programlisting>
+int
+IsForeignRelUpdatable (Relation rel);
+</programlisting>
+
+ Report which update operations the specified foreign table supports.
+ The return value should be a bitmask of rule event numbers indicating
+ which operations are supported by the foreign table, using the
+ <literal>CmdType</> enumeration; that is,
+ <literal>(1 << CMD_UPDATE) = 4</> for <command>UPDATE</>,
+ <literal>(1 << CMD_INSERT) = 8</> for <command>INSERT</>, and
+ <literal>(1 << CMD_DELETE) = 16</> for <command>DELETE</>.
+ </para>
+
+ <para>
+ If the <function>IsForeignRelUpdatable</> pointer is set to
+ <literal>NULL</>, foreign tables are assumed to be insertable, updatable,
+ or deletable if the FDW provides <function>ExecForeignInsert</>,
+ <function>ExecForeignUpdate</>, or <function>ExecForeignDelete</>
+ respectively. This function is only needed if the FDW supports some
+ tables that are updatable and some that are not. (Even then, it's
+ permissible to throw an error in the execution routine instead of
+ checking in this function. However, this function is used to determine
+ updatability for display in the <literal>information_schema</> views.)
+ </para>
+
</sect2>
<sect2 id="fdw-callbacks-explain">
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index a1c3bebb09..35924f19f2 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -254,6 +254,43 @@
</para>
</sect3>
+
+ <sect3>
+ <title>Updatability Options</title>
+
+ <para>
+ By default all foreign tables using <filename>postgres_fdw</> are assumed
+ to be updatable. This may be overridden using the following option:
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><literal>updatable</literal></term>
+ <listitem>
+ <para>
+ This option controls whether <filename>postgres_fdw</> allows foreign
+ tables to be modified using <command>INSERT</>, <command>UPDATE</> and
+ <command>DELETE</> commands. It can be specified for a foreign table
+ or a foreign server. A table-level option overrides a server-level
+ option.
+ The default is <literal>true</>.
+ </para>
+
+ <para>
+ Of course, if the remote table is not in fact updatable, an error
+ would occur anyway. Use of this option primarily allows the error to
+ be thrown locally without querying the remote server. Note however
+ that the <literal>information_schema</> views will report a
+ <filename>postgres_fdw</> foreign table to be updatable (or not)
+ according to the setting of this option, without any check of the
+ remote server.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ </sect3>
</sect2>
<sect2>
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
index 230758654c..e1f8e7f4b1 100644
--- a/src/backend/catalog/information_schema.sql
+++ b/src/backend/catalog/information_schema.sql
@@ -731,7 +731,8 @@ CREATE VIEW columns AS
CAST(null AS character_data) AS generation_expression,
CAST(CASE WHEN c.relkind = 'r' OR
- (c.relkind = 'v' AND pg_view_is_updatable(c.oid))
+ (c.relkind IN ('v', 'f') AND
+ pg_column_is_updatable(c.oid, a.attnum, false))
THEN 'YES' ELSE 'NO' END AS yes_or_no) AS is_updatable
FROM (pg_attribute a LEFT JOIN pg_attrdef ad ON attrelid = adrelid AND attnum = adnum)
@@ -1895,7 +1896,9 @@ CREATE VIEW tables AS
CAST(t.typname AS sql_identifier) AS user_defined_type_name,
CAST(CASE WHEN c.relkind = 'r' OR
- (c.relkind = 'v' AND pg_view_is_insertable(c.oid))
+ (c.relkind IN ('v', 'f') AND
+ -- 1 << CMD_INSERT
+ pg_relation_is_updatable(c.oid, false) & 8 = 8)
THEN 'YES' ELSE 'NO' END AS yes_or_no) AS is_insertable_into,
CAST(CASE WHEN t.typname IS NOT NULL THEN 'YES' ELSE 'NO' END AS yes_or_no) AS is_typed,
@@ -2494,11 +2497,15 @@ CREATE VIEW views AS
CAST('NONE' AS character_data) AS check_option,
CAST(
- CASE WHEN pg_view_is_updatable(c.oid) THEN 'YES' ELSE 'NO' END
+ -- (1 << CMD_UPDATE) + (1 << CMD_DELETE)
+ CASE WHEN pg_relation_is_updatable(c.oid, false) & 20 = 20
+ THEN 'YES' ELSE 'NO' END
AS yes_or_no) AS is_updatable,
CAST(
- CASE WHEN pg_view_is_insertable(c.oid) THEN 'YES' ELSE 'NO' END
+ -- 1 << CMD_INSERT
+ CASE WHEN pg_relation_is_updatable(c.oid, false) & 8 = 8
+ THEN 'YES' ELSE 'NO' END
AS yes_or_no) AS is_insertable_into,
CAST(
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 9b0cd8c207..3b664d0926 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1015,6 +1015,12 @@ CheckValidResultRel(Relation resultRel, CmdType operation)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot insert into foreign table \"%s\"",
RelationGetRelationName(resultRel))));
+ if (fdwroutine->IsForeignRelUpdatable != NULL &&
+ (fdwroutine->IsForeignRelUpdatable(resultRel) & (1 << CMD_INSERT)) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("foreign table \"%s\" does not allow inserts",
+ RelationGetRelationName(resultRel))));
break;
case CMD_UPDATE:
if (fdwroutine->ExecForeignUpdate == NULL)
@@ -1022,6 +1028,12 @@ CheckValidResultRel(Relation resultRel, CmdType operation)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot update foreign table \"%s\"",
RelationGetRelationName(resultRel))));
+ if (fdwroutine->IsForeignRelUpdatable != NULL &&
+ (fdwroutine->IsForeignRelUpdatable(resultRel) & (1 << CMD_UPDATE)) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("foreign table \"%s\" does not allow updates",
+ RelationGetRelationName(resultRel))));
break;
case CMD_DELETE:
if (fdwroutine->ExecForeignDelete == NULL)
@@ -1029,6 +1041,12 @@ CheckValidResultRel(Relation resultRel, CmdType operation)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot delete from foreign table \"%s\"",
RelationGetRelationName(resultRel))));
+ if (fdwroutine->IsForeignRelUpdatable != NULL &&
+ (fdwroutine->IsForeignRelUpdatable(resultRel) & (1 << CMD_DELETE)) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("foreign table \"%s\" does not allow deletes",
+ RelationGetRelationName(resultRel))));
break;
default:
elog(ERROR, "unrecognized CmdType: %d", (int) operation);
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 01875fcd45..a467588e50 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;
}
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 829ce59888..bf06ec048f 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -528,30 +528,49 @@ pg_collation_for(PG_FUNCTION_ARGS)
/*
- * information_schema support functions
+ * pg_relation_is_updatable - determine which update events the specified
+ * relation supports.
*
- * Test whether a view (identified by pg_class OID) is insertable-into or
- * updatable. The latter requires delete capability too. This is an
- * artifact of the way the SQL standard defines the information_schema views:
- * if we defined separate functions for update and delete, we'd double the
- * work required to compute the view columns.
- *
- * These rely on relation_is_updatable(), which is in rewriteHandler.c.
+ * This relies on relation_is_updatable() in rewriteHandler.c, which see
+ * for additional information.
*/
Datum
-pg_view_is_insertable(PG_FUNCTION_ARGS)
+pg_relation_is_updatable(PG_FUNCTION_ARGS)
{
- Oid viewoid = PG_GETARG_OID(0);
- int req_events = (1 << CMD_INSERT);
+ Oid reloid = PG_GETARG_OID(0);
+ bool include_triggers = PG_GETARG_BOOL(1);
- PG_RETURN_BOOL(relation_is_updatable(viewoid, req_events));
+ PG_RETURN_INT32(relation_is_updatable(reloid, include_triggers));
}
+/*
+ * pg_column_is_updatable - determine whether a column is updatable
+ *
+ * Currently we just check whether the column's relation is updatable.
+ * Eventually we might allow views to have some updatable and some
+ * non-updatable columns.
+ *
+ * Also, this function encapsulates the decision about just what
+ * information_schema.columns.is_updatable actually means. It's not clear
+ * whether deletability of the column's relation should be required, so
+ * we want that decision in C code where we could change it without initdb.
+ */
Datum
-pg_view_is_updatable(PG_FUNCTION_ARGS)
+pg_column_is_updatable(PG_FUNCTION_ARGS)
{
- Oid viewoid = PG_GETARG_OID(0);
- int req_events = (1 << CMD_UPDATE) | (1 << CMD_DELETE);
+ Oid reloid = PG_GETARG_OID(0);
+ AttrNumber attnum = PG_GETARG_INT16(1);
+ bool include_triggers = PG_GETARG_BOOL(2);
+ int events;
+
+ /* System columns are never updatable */
+ if (attnum <= 0)
+ PG_RETURN_BOOL(false);
+
+ events = relation_is_updatable(reloid, include_triggers);
+
+ /* We require both updatability and deletability of the relation */
+#define REQ_EVENTS ((1 << CMD_UPDATE) | (1 << CMD_DELETE))
- PG_RETURN_BOOL(relation_is_updatable(viewoid, req_events));
+ PG_RETURN_BOOL((events & REQ_EVENTS) == REQ_EVENTS);
}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 392649c37e..d46fe9ede3 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201305061
+#define CATALOG_VERSION_NO 201306121
#endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 4102deca69..b5be075ee1 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1976,10 +1976,10 @@ DESCR("type of the argument");
DATA(insert OID = 3162 ( pg_collation_for PGNSP PGUID 12 1 0 0 0 f f f f f f s 1 0 25 "2276" _null_ _null_ _null_ _null_ pg_collation_for _null_ _null_ _null_ ));
DESCR("collation of the argument; implementation of the COLLATION FOR expression");
-DATA(insert OID = 3842 ( pg_view_is_insertable PGNSP PGUID 12 10 0 0 0 f f f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ pg_view_is_insertable _null_ _null_ _null_ ));
-DESCR("is a view insertable-into");
-DATA(insert OID = 3843 ( pg_view_is_updatable PGNSP PGUID 12 10 0 0 0 f f f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ pg_view_is_updatable _null_ _null_ _null_ ));
-DESCR("is a view updatable");
+DATA(insert OID = 3842 ( pg_relation_is_updatable PGNSP PGUID 12 10 0 0 0 f f f f t f s 2 0 23 "2205 16" _null_ _null_ _null_ _null_ pg_relation_is_updatable _null_ _null_ _null_ ));
+DESCR("is a relation insertable/updatable/deletable");
+DATA(insert OID = 3843 ( pg_column_is_updatable PGNSP PGUID 12 10 0 0 0 f f f f t f s 3 0 16 "2205 21 16" _null_ _null_ _null_ _null_ pg_column_is_updatable _null_ _null_ _null_ ));
+DESCR("is a column updatable");
/* Deferrable unique constraint trigger */
DATA(insert OID = 1250 ( unique_key_recheck PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 2279 "" _null_ _null_ _null_ _null_ unique_key_recheck _null_ _null_ _null_ ));
diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h
index 485eee320f..e832665269 100644
--- a/src/include/foreign/fdwapi.h
+++ b/src/include/foreign/fdwapi.h
@@ -80,6 +80,8 @@ typedef TupleTableSlot *(*ExecForeignDelete_function) (EState *estate,
typedef void (*EndForeignModify_function) (EState *estate,
ResultRelInfo *rinfo);
+typedef int (*IsForeignRelUpdatable_function) (Relation rel);
+
typedef void (*ExplainForeignScan_function) (ForeignScanState *node,
struct ExplainState *es);
@@ -134,6 +136,7 @@ typedef struct FdwRoutine
ExecForeignUpdate_function ExecForeignUpdate;
ExecForeignDelete_function ExecForeignDelete;
EndForeignModify_function EndForeignModify;
+ IsForeignRelUpdatable_function IsForeignRelUpdatable;
/* Support functions for EXPLAIN */
ExplainForeignScan_function ExplainForeignScan;
diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h
index 5983315803..1831de4640 100644
--- a/src/include/rewrite/rewriteHandler.h
+++ b/src/include/rewrite/rewriteHandler.h
@@ -21,6 +21,6 @@ extern List *QueryRewrite(Query *parsetree);
extern void AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown);
extern Node *build_column_default(Relation rel, int attrno);
-extern bool relation_is_updatable(Oid reloid, int req_events);
+extern int relation_is_updatable(Oid reloid, bool include_triggers);
#endif /* REWRITEHANDLER_H */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 8acdcaaf98..667c58b5d0 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -485,8 +485,8 @@ extern Datum pg_sleep(PG_FUNCTION_ARGS);
extern Datum pg_get_keywords(PG_FUNCTION_ARGS);
extern Datum pg_typeof(PG_FUNCTION_ARGS);
extern Datum pg_collation_for(PG_FUNCTION_ARGS);
-extern Datum pg_view_is_insertable(PG_FUNCTION_ARGS);
-extern Datum pg_view_is_updatable(PG_FUNCTION_ARGS);
+extern Datum pg_relation_is_updatable(PG_FUNCTION_ARGS);
+extern Datum pg_column_is_updatable(PG_FUNCTION_ARGS);
/* oid.c */
extern Datum oidin(PG_FUNCTION_ARGS);