*** /dev/null --- b/contrib/file_fdw/data/list1.csv *************** *** 0 **** --- 1,2 ---- + 1,foo + 1,bar *** /dev/null --- b/contrib/file_fdw/data/list2.bad *************** *** 0 **** --- 1,2 ---- + 2,baz + 1,qux *** /dev/null --- b/contrib/file_fdw/data/list2.csv *************** *** 0 **** --- 1,2 ---- + 2,baz + 2,qux *** a/contrib/file_fdw/input/file_fdw.source --- b/contrib/file_fdw/input/file_fdw.source *************** *** 162,167 **** SELECT tableoid::regclass, * FROM agg FOR UPDATE; --- 162,188 ---- ALTER FOREIGN TABLE agg_csv NO INHERIT agg; DROP TABLE agg; + -- declarative partitioning tests + SET ROLE regress_file_fdw_superuser; + CREATE TABLE pt (a int, b text) partition by list (a); + CREATE FOREIGN TABLE p1 partition of pt for values in (1) SERVER file_server + OPTIONS (format 'csv', filename '@abs_srcdir@/data/list1.csv', delimiter ','); + CREATE TABLE p2 partition of pt for values in (2); + SELECT tableoid::regclass, * FROM pt; + SELECT tableoid::regclass, * FROM p1; + SELECT tableoid::regclass, * FROM p2; + COPY pt FROM '@abs_srcdir@/data/list2.bad' with (format 'csv', delimiter ','); -- ERROR + COPY pt FROM '@abs_srcdir@/data/list2.csv' with (format 'csv', delimiter ','); + SELECT tableoid::regclass, * FROM pt; + SELECT tableoid::regclass, * FROM p1; + SELECT tableoid::regclass, * FROM p2; + INSERT INTO pt VALUES (1, 'xyzzy'); -- ERROR + INSERT INTO pt VALUES (2, 'xyzzy'); + SELECT tableoid::regclass, * FROM pt; + SELECT tableoid::regclass, * FROM p1; + SELECT tableoid::regclass, * FROM p2; + DROP TABLE pt; + -- privilege tests SET ROLE regress_file_fdw_superuser; SELECT * FROM agg_text ORDER BY a; *** a/contrib/file_fdw/output/file_fdw.source --- b/contrib/file_fdw/output/file_fdw.source *************** *** 289,294 **** SELECT tableoid::regclass, * FROM agg FOR UPDATE; --- 289,375 ---- ALTER FOREIGN TABLE agg_csv NO INHERIT agg; DROP TABLE agg; + -- declarative partitioning tests + SET ROLE regress_file_fdw_superuser; + CREATE TABLE pt (a int, b text) partition by list (a); + CREATE FOREIGN TABLE p1 partition of pt for values in (1) SERVER file_server + OPTIONS (format 'csv', filename '@abs_srcdir@/data/list1.csv', delimiter ','); + CREATE TABLE p2 partition of pt for values in (2); + SELECT tableoid::regclass, * FROM pt; + tableoid | a | b + ----------+---+----- + p1 | 1 | foo + p1 | 1 | bar + (2 rows) + + SELECT tableoid::regclass, * FROM p1; + tableoid | a | b + ----------+---+----- + p1 | 1 | foo + p1 | 1 | bar + (2 rows) + + SELECT tableoid::regclass, * FROM p2; + tableoid | a | b + ----------+---+--- + (0 rows) + + COPY pt FROM '@abs_srcdir@/data/list2.bad' with (format 'csv', delimiter ','); -- ERROR + ERROR: cannot route inserted tuples to a foreign table + CONTEXT: COPY pt, line 2: "1,qux" + COPY pt FROM '@abs_srcdir@/data/list2.csv' with (format 'csv', delimiter ','); + SELECT tableoid::regclass, * FROM pt; + tableoid | a | b + ----------+---+----- + p1 | 1 | foo + p1 | 1 | bar + p2 | 2 | baz + p2 | 2 | qux + (4 rows) + + SELECT tableoid::regclass, * FROM p1; + tableoid | a | b + ----------+---+----- + p1 | 1 | foo + p1 | 1 | bar + (2 rows) + + SELECT tableoid::regclass, * FROM p2; + tableoid | a | b + ----------+---+----- + p2 | 2 | baz + p2 | 2 | qux + (2 rows) + + INSERT INTO pt VALUES (1, 'xyzzy'); -- ERROR + ERROR: cannot route inserted tuples to a foreign table + INSERT INTO pt VALUES (2, 'xyzzy'); + SELECT tableoid::regclass, * FROM pt; + tableoid | a | b + ----------+---+------- + p1 | 1 | foo + p1 | 1 | bar + p2 | 2 | baz + p2 | 2 | qux + p2 | 2 | xyzzy + (5 rows) + + SELECT tableoid::regclass, * FROM p1; + tableoid | a | b + ----------+---+----- + p1 | 1 | foo + p1 | 1 | bar + (2 rows) + + SELECT tableoid::regclass, * FROM p2; + tableoid | a | b + ----------+---+------- + p2 | 2 | baz + p2 | 2 | qux + p2 | 2 | xyzzy + (3 rows) + + DROP TABLE pt; -- privilege tests SET ROLE regress_file_fdw_superuser; SELECT * FROM agg_text ORDER BY a; *** a/src/backend/executor/execMain.c --- b/src/backend/executor/execMain.c *************** *** 1097,1103 **** InitPlan(QueryDesc *queryDesc, int eflags) * CheckValidRowMarkRel. */ void ! CheckValidResultRel(Relation resultRel, CmdType operation) { TriggerDesc *trigDesc = resultRel->trigdesc; FdwRoutine *fdwroutine; --- 1097,1103 ---- * CheckValidRowMarkRel. */ void ! CheckValidResultRel(Relation resultRel, bool is_partition, CmdType operation) { TriggerDesc *trigDesc = resultRel->trigdesc; FdwRoutine *fdwroutine; *************** *** 1168,1173 **** CheckValidResultRel(Relation resultRel, CmdType operation) --- 1168,1181 ---- RelationGetRelationName(resultRel)))); break; case RELKIND_FOREIGN_TABLE: + + /* + * If foreign partition to do tuple-routing for, nothing to do; + * it's disallowed elsewhere. + */ + if (is_partition && operation == CMD_INSERT) + break; + /* Okay only if the FDW supports it */ fdwroutine = GetFdwRoutineForRelation(resultRel, false); switch (operation) *************** *** 3310,3316 **** ExecSetupPartitionTupleRouting(Relation rel, /* * Verify result relation is a valid target for the current operation. */ ! CheckValidResultRel(partrel, CMD_INSERT); /* * Save a tuple conversion map to convert a tuple routed to this --- 3318,3324 ---- /* * Verify result relation is a valid target for the current operation. */ ! CheckValidResultRel(partrel, true, CMD_INSERT); /* * Save a tuple conversion map to convert a tuple routed to this *** a/src/backend/executor/nodeModifyTable.c --- b/src/backend/executor/nodeModifyTable.c *************** *** 1854,1860 **** ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) /* * Verify result relation is a valid target for the current operation */ ! CheckValidResultRel(resultRelInfo->ri_RelationDesc, operation); /* * If there are indices on the result relation, open them and save --- 1854,1860 ---- /* * Verify result relation is a valid target for the current operation */ ! CheckValidResultRel(resultRelInfo->ri_RelationDesc, false, operation); /* * If there are indices on the result relation, open them and save *** a/src/include/executor/executor.h --- b/src/include/executor/executor.h *************** *** 177,183 **** extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); ! extern void CheckValidResultRel(Relation resultRel, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, Index resultRelationIndex, --- 177,184 ---- extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); ! extern void CheckValidResultRel(Relation resultRel, bool is_partition, ! CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, Index resultRelationIndex,