From 3d956d9562aa4811b5eaaaf5314d361c61be2ae0 Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Fri, 6 Apr 2018 19:16:11 -0400 Subject: Allow insert and update tuple routing and COPY for foreign tables. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also enable this for postgres_fdw. Etsuro Fujita, based on an earlier patch by Amit Langote. The larger patch series of which this is a part has been reviewed by Amit Langote, David Fetter, Maksim Milyutin, Álvaro Herrera, Stephen Frost, and me. Minor documentation changes to the final version by me. Discussion: http://postgr.es/m/29906a26-da12-8c86-4fb9-d8f88442f2b9@lab.ntt.co.jp --- contrib/postgres_fdw/postgres_fdw.c | 96 +++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) (limited to 'contrib/postgres_fdw/postgres_fdw.c') diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index e7441c759ba..30e572632ee 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -319,6 +319,10 @@ static TupleTableSlot *postgresExecForeignDelete(EState *estate, TupleTableSlot *planSlot); static void postgresEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo); +static void postgresBeginForeignInsert(ModifyTableState *mtstate, + ResultRelInfo *resultRelInfo); +static void postgresEndForeignInsert(EState *estate, + ResultRelInfo *resultRelInfo); static int postgresIsForeignRelUpdatable(Relation rel); static bool postgresPlanDirectModify(PlannerInfo *root, ModifyTable *plan, @@ -473,6 +477,8 @@ postgres_fdw_handler(PG_FUNCTION_ARGS) routine->ExecForeignUpdate = postgresExecForeignUpdate; routine->ExecForeignDelete = postgresExecForeignDelete; routine->EndForeignModify = postgresEndForeignModify; + routine->BeginForeignInsert = postgresBeginForeignInsert; + routine->EndForeignInsert = postgresEndForeignInsert; routine->IsForeignRelUpdatable = postgresIsForeignRelUpdatable; routine->PlanDirectModify = postgresPlanDirectModify; routine->BeginDirectModify = postgresBeginDirectModify; @@ -1959,6 +1965,96 @@ postgresEndForeignModify(EState *estate, finish_foreign_modify(fmstate); } +/* + * postgresBeginForeignInsert + * Begin an insert operation on a foreign table + */ +static void +postgresBeginForeignInsert(ModifyTableState *mtstate, + ResultRelInfo *resultRelInfo) +{ + PgFdwModifyState *fmstate; + Plan *plan = mtstate->ps.plan; + Relation rel = resultRelInfo->ri_RelationDesc; + RangeTblEntry *rte; + Query *query; + PlannerInfo *root; + TupleDesc tupdesc = RelationGetDescr(rel); + int attnum; + StringInfoData sql; + List *targetAttrs = NIL; + List *retrieved_attrs = NIL; + bool doNothing = false; + + initStringInfo(&sql); + + /* Set up largely-dummy planner state. */ + rte = makeNode(RangeTblEntry); + rte->rtekind = RTE_RELATION; + rte->relid = RelationGetRelid(rel); + rte->relkind = RELKIND_FOREIGN_TABLE; + query = makeNode(Query); + query->commandType = CMD_INSERT; + query->resultRelation = 1; + query->rtable = list_make1(rte); + root = makeNode(PlannerInfo); + root->parse = query; + + /* We transmit all columns that are defined in the foreign table. */ + for (attnum = 1; attnum <= tupdesc->natts; attnum++) + { + Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1); + + if (!attr->attisdropped) + targetAttrs = lappend_int(targetAttrs, attnum); + } + + /* Check if we add the ON CONFLICT clause to the remote query. */ + if (plan) + { + OnConflictAction onConflictAction = ((ModifyTable *) plan)->onConflictAction; + + /* We only support DO NOTHING without an inference specification. */ + if (onConflictAction == ONCONFLICT_NOTHING) + doNothing = true; + else if (onConflictAction != ONCONFLICT_NONE) + elog(ERROR, "unexpected ON CONFLICT specification: %d", + (int) onConflictAction); + } + + /* Construct the SQL command string. */ + deparseInsertSql(&sql, root, 1, rel, targetAttrs, doNothing, + resultRelInfo->ri_returningList, &retrieved_attrs); + + /* Construct an execution state. */ + fmstate = create_foreign_modify(mtstate->ps.state, + resultRelInfo, + CMD_INSERT, + NULL, + sql.data, + targetAttrs, + retrieved_attrs != NIL, + retrieved_attrs); + + resultRelInfo->ri_FdwState = fmstate; +} + +/* + * postgresEndForeignInsert + * Finish an insert operation on a foreign table + */ +static void +postgresEndForeignInsert(EState *estate, + ResultRelInfo *resultRelInfo) +{ + PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState; + + Assert(fmstate != NULL); + + /* Destroy the execution state */ + finish_foreign_modify(fmstate); +} + /* * postgresIsForeignRelUpdatable * Determine whether a foreign table supports INSERT, UPDATE and/or -- cgit v1.2.3