postgres_fdw: Remove duplicate code in DML execution callback functions.
authorEtsuro Fujita
Thu, 17 Jan 2019 05:37:33 +0000 (14:37 +0900)
committerEtsuro Fujita
Thu, 17 Jan 2019 05:37:33 +0000 (14:37 +0900)
postgresExecForeignInsert(), postgresExecForeignUpdate(), and
postgresExecForeignDelete() are coded almost identically, except that
postgresExecForeignInsert() does not need CTID.  Extract that code into
a separate function and use it in all the three function implementations.

Author: Ashutosh Bapat
Reviewed-By: Michael Paquier
Discussion: https://postgr.es/m/CAFjFpRcz8yoY7cBTYofcrCLwjaDeCcGKyTUivUbRiA57y3v-bw%40mail.gmail.com

contrib/postgres_fdw/postgres_fdw.c

index 64efbdff082227b2bdb9b7077de911eb4817663c..685259a00332085dde1e51cb85340e682f063dcf 100644 (file)
@@ -391,6 +391,11 @@ static PgFdwModifyState *create_foreign_modify(EState *estate,
                      List *target_attrs,
                      bool has_returning,
                      List *retrieved_attrs);
+static TupleTableSlot *execute_foreign_modify(EState *estate,
+                      ResultRelInfo *resultRelInfo,
+                      CmdType operation,
+                      TupleTableSlot *slot,
+                      TupleTableSlot *planSlot);
 static void prepare_foreign_modify(PgFdwModifyState *fmstate);
 static const char **convert_prep_stmt_params(PgFdwModifyState *fmstate,
                         ItemPointer tupleid,
@@ -1776,58 +1781,8 @@ postgresExecForeignInsert(EState *estate,
                          TupleTableSlot *slot,
                          TupleTableSlot *planSlot)
 {
-   PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
-   const char **p_values;
-   PGresult   *res;
-   int         n_rows;
-
-   /* Set up the prepared statement on the remote server, if we didn't yet */
-   if (!fmstate->p_name)
-       prepare_foreign_modify(fmstate);
-
-   /* Convert parameters needed by prepared statement to text form */
-   p_values = convert_prep_stmt_params(fmstate, NULL, slot);
-
-   /*
-    * Execute the prepared statement.
-    */
-   if (!PQsendQueryPrepared(fmstate->conn,
-                            fmstate->p_name,
-                            fmstate->p_nums,
-                            p_values,
-                            NULL,
-                            NULL,
-                            0))
-       pgfdw_report_error(ERROR, NULL, fmstate->conn, false, fmstate->query);
-
-   /*
-    * Get the result, and check for success.
-    *
-    * We don't use a PG_TRY block here, so be careful not to throw error
-    * without releasing the PGresult.
-    */
-   res = pgfdw_get_result(fmstate->conn, fmstate->query);
-   if (PQresultStatus(res) !=
-       (fmstate->has_returning ? PGRES_TUPLES_OK : PGRES_COMMAND_OK))
-       pgfdw_report_error(ERROR, res, fmstate->conn, true, fmstate->query);
-
-   /* Check number of rows affected, and fetch RETURNING tuple if any */
-   if (fmstate->has_returning)
-   {
-       n_rows = PQntuples(res);
-       if (n_rows > 0)
-           store_returning_result(fmstate, slot, res);
-   }
-   else
-       n_rows = atoi(PQcmdTuples(res));
-
-   /* And clean up */
-   PQclear(res);
-
-   MemoryContextReset(fmstate->temp_cxt);
-
-   /* Return NULL if nothing was inserted on the remote end */
-   return (n_rows > 0) ? slot : NULL;
+   return execute_foreign_modify(estate, resultRelInfo, CMD_INSERT,
+                                 slot, planSlot);
 }
 
 /*
@@ -1840,70 +1795,8 @@ postgresExecForeignUpdate(EState *estate,
                          TupleTableSlot *slot,
                          TupleTableSlot *planSlot)
 {
-   PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
-   Datum       datum;
-   bool        isNull;
-   const char **p_values;
-   PGresult   *res;
-   int         n_rows;
-
-   /* Set up the prepared statement on the remote server, if we didn't yet */
-   if (!fmstate->p_name)
-       prepare_foreign_modify(fmstate);
-
-   /* Get the ctid that was passed up as a resjunk column */
-   datum = ExecGetJunkAttribute(planSlot,
-                                fmstate->ctidAttno,
-                                &isNull);
-   /* shouldn't ever get a null result... */
-   if (isNull)
-       elog(ERROR, "ctid is NULL");
-
-   /* Convert parameters needed by prepared statement to text form */
-   p_values = convert_prep_stmt_params(fmstate,
-                                       (ItemPointer) DatumGetPointer(datum),
-                                       slot);
-
-   /*
-    * Execute the prepared statement.
-    */
-   if (!PQsendQueryPrepared(fmstate->conn,
-                            fmstate->p_name,
-                            fmstate->p_nums,
-                            p_values,
-                            NULL,
-                            NULL,
-                            0))
-       pgfdw_report_error(ERROR, NULL, fmstate->conn, false, fmstate->query);
-
-   /*
-    * Get the result, and check for success.
-    *
-    * We don't use a PG_TRY block here, so be careful not to throw error
-    * without releasing the PGresult.
-    */
-   res = pgfdw_get_result(fmstate->conn, fmstate->query);
-   if (PQresultStatus(res) !=
-       (fmstate->has_returning ? PGRES_TUPLES_OK : PGRES_COMMAND_OK))
-       pgfdw_report_error(ERROR, res, fmstate->conn, true, fmstate->query);
-
-   /* Check number of rows affected, and fetch RETURNING tuple if any */
-   if (fmstate->has_returning)
-   {
-       n_rows = PQntuples(res);
-       if (n_rows > 0)
-           store_returning_result(fmstate, slot, res);
-   }
-   else
-       n_rows = atoi(PQcmdTuples(res));
-
-   /* And clean up */
-   PQclear(res);
-
-   MemoryContextReset(fmstate->temp_cxt);
-
-   /* Return NULL if nothing was updated on the remote end */
-   return (n_rows > 0) ? slot : NULL;
+   return execute_foreign_modify(estate, resultRelInfo, CMD_UPDATE,
+                                 slot, planSlot);
 }
 
 /*
@@ -1916,70 +1809,8 @@ postgresExecForeignDelete(EState *estate,
                          TupleTableSlot *slot,
                          TupleTableSlot *planSlot)
 {
-   PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
-   Datum       datum;
-   bool        isNull;
-   const char **p_values;
-   PGresult   *res;
-   int         n_rows;
-
-   /* Set up the prepared statement on the remote server, if we didn't yet */
-   if (!fmstate->p_name)
-       prepare_foreign_modify(fmstate);
-
-   /* Get the ctid that was passed up as a resjunk column */
-   datum = ExecGetJunkAttribute(planSlot,
-                                fmstate->ctidAttno,
-                                &isNull);
-   /* shouldn't ever get a null result... */
-   if (isNull)
-       elog(ERROR, "ctid is NULL");
-
-   /* Convert parameters needed by prepared statement to text form */
-   p_values = convert_prep_stmt_params(fmstate,
-                                       (ItemPointer) DatumGetPointer(datum),
-                                       NULL);
-
-   /*
-    * Execute the prepared statement.
-    */
-   if (!PQsendQueryPrepared(fmstate->conn,
-                            fmstate->p_name,
-                            fmstate->p_nums,
-                            p_values,
-                            NULL,
-                            NULL,
-                            0))
-       pgfdw_report_error(ERROR, NULL, fmstate->conn, false, fmstate->query);
-
-   /*
-    * Get the result, and check for success.
-    *
-    * We don't use a PG_TRY block here, so be careful not to throw error
-    * without releasing the PGresult.
-    */
-   res = pgfdw_get_result(fmstate->conn, fmstate->query);
-   if (PQresultStatus(res) !=
-       (fmstate->has_returning ? PGRES_TUPLES_OK : PGRES_COMMAND_OK))
-       pgfdw_report_error(ERROR, res, fmstate->conn, true, fmstate->query);
-
-   /* Check number of rows affected, and fetch RETURNING tuple if any */
-   if (fmstate->has_returning)
-   {
-       n_rows = PQntuples(res);
-       if (n_rows > 0)
-           store_returning_result(fmstate, slot, res);
-   }
-   else
-       n_rows = atoi(PQcmdTuples(res));
-
-   /* And clean up */
-   PQclear(res);
-
-   MemoryContextReset(fmstate->temp_cxt);
-
-   /* Return NULL if nothing was deleted on the remote end */
-   return (n_rows > 0) ? slot : NULL;
+   return execute_foreign_modify(estate, resultRelInfo, CMD_DELETE,
+                                 slot, planSlot);
 }
 
 /*
@@ -3425,6 +3256,98 @@ create_foreign_modify(EState *estate,
    return fmstate;
 }
 
+/*
+ * execute_foreign_modify
+ *     Perform foreign-table modification as required, and fetch RETURNING
+ *     result if any.  (This is the shared guts of postgresExecForeignInsert,
+ *     postgresExecForeignUpdate, and postgresExecForeignDelete.)
+ */
+static TupleTableSlot *
+execute_foreign_modify(EState *estate,
+                      ResultRelInfo *resultRelInfo,
+                      CmdType operation,
+                      TupleTableSlot *slot,
+                      TupleTableSlot *planSlot)
+{
+   PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
+   ItemPointer ctid = NULL;
+   const char **p_values;
+   PGresult   *res;
+   int         n_rows;
+
+   /* The operation should be INSERT, UPDATE, or DELETE */
+   Assert(operation == CMD_INSERT ||
+          operation == CMD_UPDATE ||
+          operation == CMD_DELETE);
+
+   /* Set up the prepared statement on the remote server, if we didn't yet */
+   if (!fmstate->p_name)
+       prepare_foreign_modify(fmstate);
+
+   /*
+    * For UPDATE/DELETE, get the ctid that was passed up as a resjunk column
+    */
+   if (operation == CMD_UPDATE || operation == CMD_DELETE)
+   {
+       Datum       datum;
+       bool        isNull;
+
+       datum = ExecGetJunkAttribute(planSlot,
+                                    fmstate->ctidAttno,
+                                    &isNull);
+       /* shouldn't ever get a null result... */
+       if (isNull)
+           elog(ERROR, "ctid is NULL");
+       ctid = (ItemPointer) DatumGetPointer(datum);
+   }
+
+   /* Convert parameters needed by prepared statement to text form */
+   p_values = convert_prep_stmt_params(fmstate, ctid, slot);
+
+   /*
+    * Execute the prepared statement.
+    */
+   if (!PQsendQueryPrepared(fmstate->conn,
+                            fmstate->p_name,
+                            fmstate->p_nums,
+                            p_values,
+                            NULL,
+                            NULL,
+                            0))
+       pgfdw_report_error(ERROR, NULL, fmstate->conn, false, fmstate->query);
+
+   /*
+    * Get the result, and check for success.
+    *
+    * We don't use a PG_TRY block here, so be careful not to throw error
+    * without releasing the PGresult.
+    */
+   res = pgfdw_get_result(fmstate->conn, fmstate->query);
+   if (PQresultStatus(res) !=
+       (fmstate->has_returning ? PGRES_TUPLES_OK : PGRES_COMMAND_OK))
+       pgfdw_report_error(ERROR, res, fmstate->conn, true, fmstate->query);
+
+   /* Check number of rows affected, and fetch RETURNING tuple if any */
+   if (fmstate->has_returning)
+   {
+       n_rows = PQntuples(res);
+       if (n_rows > 0)
+           store_returning_result(fmstate, slot, res);
+   }
+   else
+       n_rows = atoi(PQcmdTuples(res));
+
+   /* And clean up */
+   PQclear(res);
+
+   MemoryContextReset(fmstate->temp_cxt);
+
+   /*
+    * Return NULL if nothing was inserted/updated/deleted on the remote end
+    */
+   return (n_rows > 0) ? slot : NULL;
+}
+
 /*
  * prepare_foreign_modify
  *     Establish a prepared statement for execution of INSERT/UPDATE/DELETE