Fix bugs around handling of params passed to the datanodes.
authorPavan Deolasee <[email protected]>
Tue, 1 Mar 2016 05:57:20 +0000 (11:27 +0530)
committerPavan Deolasee <[email protected]>
Tue, 1 Mar 2016 05:57:20 +0000 (11:27 +0530)
This fixes problems reported in plpgsql function calls since
fast-query-shipping was added. But fixing the problem also highlighted other
bugs in the protocol handling. For example, we would set a datanode connection
state to be IDLE upon receiving a CommandComplete. But for simple query
protocol, we should really be waiting for ReadyForQuery before changing the
state. This patch has related fixes.

plpgsql test case's expected output is also fixed now that the underlying bug
has been take care of

src/backend/commands/prepare.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/outfuncs.c
src/backend/pgxc/barrier/barrier.c
src/backend/pgxc/pool/execRemote.c
src/backend/pgxc/pool/pgxcnode.c
src/include/pgxc/execRemote.h
src/include/pgxc/pgxcnode.h
src/include/pgxc/planner.h
src/test/regress/expected/plpgsql_1.out

index e66d2bc70b0969b4894b46e172307572046153c8..fa90742b7f95bac7c7c78b4dd0b4abbcaaf557ff 100644 (file)
@@ -490,7 +490,7 @@ SetRemoteStatementName(Plan *plan, const char *stmt_name, int num_params,
                char name[NAMEDATALEN];
 
                /* Nothing to do if parameters are already set for this query */
-               if (remotequery->remote_num_params != 0)
+               if (remotequery->rq_num_params != 0)
                        return 0;
 
                if (stmt_name)
@@ -533,8 +533,8 @@ SetRemoteStatementName(Plan *plan, const char *stmt_name, int num_params,
                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                         errmsg("Passing parameters in PREPARE statement is not supported")));
 
-               remotequery->remote_num_params = num_params;
-               remotequery->remote_param_types = param_types;
+               remotequery->rq_num_params = num_params;
+               remotequery->rq_param_types = param_types;
        }
        else if (IsA(plan, ModifyTable))
        {
index 52366b1f2259e127c8000e558adaeb7e215c98cd..012febb645dfb30bfbbff357de6849b6782f27b1 100644 (file)
@@ -1112,9 +1112,12 @@ _copyRemoteQuery(const RemoteQuery *from)
        COPY_SCALAR_FIELD(force_autocommit);
        COPY_STRING_FIELD(statement);
        COPY_STRING_FIELD(cursor);
-       COPY_SCALAR_FIELD(remote_num_params);
-       COPY_POINTER_FIELD(remote_param_types,
-          sizeof(from->remote_param_types[0]) * from->remote_num_params);
+       COPY_SCALAR_FIELD(rq_num_params);
+       if (from->rq_param_types)
+               COPY_POINTER_FIELD(rq_param_types,
+                       sizeof(from->rq_param_types[0]) * from->rq_num_params);
+       else
+               newnode->rq_param_types = NULL;
        COPY_SCALAR_FIELD(exec_type);
 
        COPY_SCALAR_FIELD(reduce_level);
index 8eb790154fcfe2d63c308e14e1be81ad2cb83f99..525abdcc29b97ccb86c7fb556d6b6d95ce3fb94f 100644 (file)
@@ -768,11 +768,11 @@ _outRemoteQuery(StringInfo str, const RemoteQuery *node)
        WRITE_BOOL_FIELD(force_autocommit);
        WRITE_STRING_FIELD(statement);
        WRITE_STRING_FIELD(cursor);
-       WRITE_INT_FIELD(remote_num_params);
+       WRITE_INT_FIELD(rq_num_params);
 
-       appendStringInfo(str, " :remote_param_types");
-       for (i = 0; i < node->remote_num_params; i++)
-               appendStringInfo(str, " %d", node->remote_param_types[i]);
+       appendStringInfo(str, " :rq_param_types");
+       for (i = 0; i < node->rq_num_params; i++)
+               appendStringInfo(str, " %d", node->rq_param_types[i]);
 
        WRITE_ENUM_FIELD(exec_type, RemoteQueryExecType);
        WRITE_BOOL_FIELD(has_row_marks);
index e532d6a262ef94a2311154bf389e07a6a19502f8..0fe377a4a471e5b6b4fc43cb75b831bd9334f4a1 100644 (file)
@@ -200,7 +200,7 @@ SendBarrierPrepareRequest(List *coords, const char *id)
                memcpy(handle->outBuffer + handle->outEnd, id, barrier_idlen);
                handle->outEnd += barrier_idlen;
 
-               handle->state = DN_CONNECTION_STATE_QUERY;
+               PGXCNodeSetConnectionState(handle, DN_CONNECTION_STATE_QUERY);
 
                pgxc_node_flush(handle);
        }
@@ -286,7 +286,7 @@ SendBarrierEndRequest(PGXCNodeAllHandles *coord_handles, const char *id)
                memcpy(handle->outBuffer + handle->outEnd, id, barrier_idlen);
                handle->outEnd += barrier_idlen;
 
-               handle->state = DN_CONNECTION_STATE_QUERY;
+               PGXCNodeSetConnectionState(handle, DN_CONNECTION_STATE_QUERY);
                pgxc_node_flush(handle);
        }
 
@@ -398,7 +398,7 @@ ExecuteBarrier(const char *id)
                memcpy(handle->outBuffer + handle->outEnd, id, barrier_idlen);
                handle->outEnd += barrier_idlen;
 
-               handle->state = DN_CONNECTION_STATE_QUERY;
+               PGXCNodeSetConnectionState(handle, DN_CONNECTION_STATE_QUERY);
                pgxc_node_flush(handle);
        }
 
index 626e6b6292121b9bd367849b6f39612ecb06b295..61bee4273665abdbf7fb83e40435b777cdaa2157 100644 (file)
@@ -1045,7 +1045,8 @@ BufferConnection(PGXCNodeHandle *conn)
                {
                        if (pgxc_node_receive(1, &conn, NULL))
                        {
-                               conn->state = DN_CONNECTION_STATE_ERROR_FATAL;
+                               PGXCNodeSetConnectionState(conn,
+                                               DN_CONNECTION_STATE_ERROR_FATAL);
                                add_error_message(conn, "Failed to fetch from data node");
                        }
                }
@@ -1551,6 +1552,8 @@ pgxc_node_receive_responses(const int conn_count, PGXCNodeHandle ** connections,
                while (i < count)
                {
                        int result =  handle_response(to_receive[i], combiner);
+                       elog(DEBUG5, "Received response %d on connection to node %s",
+                                       result, to_receive[i]->nodename);
                        switch (result)
                        {
                                case RESPONSE_EOF: /* have something to read, keep receiving */
@@ -1575,13 +1578,12 @@ pgxc_node_receive_responses(const int conn_count, PGXCNodeHandle ** connections,
                                        break;
 
                                case RESPONSE_WAITXIDS:
-                                       break;
-
                                case RESPONSE_ASSIGN_GXID:
+                               case RESPONSE_TUPDESC:
                                        break;
 
-                               case RESPONSE_TUPDESC:
                                case RESPONSE_DATAROW:
+                                       combiner->currentRow = NULL;
                                        break;
 
                                default:
@@ -1633,7 +1635,7 @@ handle_response(PGXCNodeHandle *conn, ResponseCombiner *combiner)
                 * as well as an error stack overflow.
                 */
                if (proc_exit_inprogress)
-                       conn->state = DN_CONNECTION_STATE_ERROR_FATAL;
+                       PGXCNodeSetConnectionState(conn, DN_CONNECTION_STATE_ERROR_FATAL);
 
                /*
                 * Don't read from from the connection if there is a fatal error.
@@ -1653,6 +1655,8 @@ handle_response(PGXCNodeHandle *conn, ResponseCombiner *combiner)
 
                /* TODO handle other possible responses */
                msg_type = get_message(conn, &msg_len, &msg);
+               elog(DEBUG5, "handle_response - received message %c, node %s, "
+                               "current_state %d", msg_type, conn->nodename, conn->state);
                switch (msg_type)
                {
                        case '\0':                      /* Not enough data in the buffer */
@@ -1663,8 +1667,13 @@ handle_response(PGXCNodeHandle *conn, ResponseCombiner *combiner)
                        case 'C':                       /* CommandComplete */
                                HandleCommandComplete(combiner, msg, msg_len, conn);
                                conn->combiner = NULL;
-                               if (conn->state == DN_CONNECTION_STATE_QUERY)
-                                       conn->state = DN_CONNECTION_STATE_IDLE;
+                               /* 
+                                * In case of simple query protocol, wait for the ReadyForQuery
+                                * before marking connection as Idle
+                                */
+                               if (combiner->extended_query &&
+                                       conn->state == DN_CONNECTION_STATE_QUERY)
+                                       PGXCNodeSetConnectionState(conn, DN_CONNECTION_STATE_IDLE);
                                return RESPONSE_COMPLETE;
                        case 'T':                       /* RowDescription */
 #ifdef DN_CONNECTION_DEBUG
@@ -1684,7 +1693,7 @@ handle_response(PGXCNodeHandle *conn, ResponseCombiner *combiner)
                                break;
                        case 's':                       /* PortalSuspended */
                                /* No activity is expected on the connection until next query */
-                               conn->state = DN_CONNECTION_STATE_IDLE;
+                               PGXCNodeSetConnectionState(conn, DN_CONNECTION_STATE_IDLE);
                                conn->combiner = NULL;
                                return RESPONSE_SUSPENDED;
                        case '1': /* ParseComplete */
@@ -1694,16 +1703,16 @@ handle_response(PGXCNodeHandle *conn, ResponseCombiner *combiner)
                                /* simple notifications, continue reading */
                                break;
                        case 'G': /* CopyInResponse */
-                               conn->state = DN_CONNECTION_STATE_COPY_IN;
+                               PGXCNodeSetConnectionState(conn, DN_CONNECTION_STATE_COPY_IN);
                                HandleCopyIn(combiner);
                                /* Done, return to caller to let it know the data can be passed in */
                                return RESPONSE_COPY;
                        case 'H': /* CopyOutResponse */
-                               conn->state = DN_CONNECTION_STATE_COPY_OUT;
+                               PGXCNodeSetConnectionState(conn, DN_CONNECTION_STATE_COPY_OUT);
                                HandleCopyOut(combiner);
                                return RESPONSE_COPY;
                        case 'd': /* CopyOutDataRow */
-                               conn->state = DN_CONNECTION_STATE_COPY_OUT;
+                               PGXCNodeSetConnectionState(conn, DN_CONNECTION_STATE_COPY_OUT);
                                HandleCopyDataRow(combiner, msg, msg_len);
                                break;
                        case 'E':                       /* ErrorResponse */
@@ -1727,7 +1736,7 @@ handle_response(PGXCNodeHandle *conn, ResponseCombiner *combiner)
                                 * with the connection
                                 */
                                conn->transaction_status = msg[0];
-                               conn->state = DN_CONNECTION_STATE_IDLE;
+                               PGXCNodeSetConnectionState(conn, DN_CONNECTION_STATE_IDLE);
                                conn->combiner = NULL;
 #ifdef DN_CONNECTION_DEBUG
                                conn->have_row_desc = false;
@@ -1738,7 +1747,7 @@ handle_response(PGXCNodeHandle *conn, ResponseCombiner *combiner)
                                HandleDatanodeCommandId(combiner, msg, msg_len);
                                break;
                        case 'b':
-                               conn->state = DN_CONNECTION_STATE_IDLE;
+                               PGXCNodeSetConnectionState(conn, DN_CONNECTION_STATE_IDLE);
                                return RESPONSE_BARRIER_OK;
                        case 'I':                       /* EmptyQuery */
                                return RESPONSE_COMPLETE;
@@ -1751,7 +1760,7 @@ handle_response(PGXCNodeHandle *conn, ResponseCombiner *combiner)
                        default:
                                /* sync lost? */
                                elog(WARNING, "Received unsupported message type: %c", msg_type);
-                               conn->state = DN_CONNECTION_STATE_ERROR_FATAL;
+                               PGXCNodeSetConnectionState(conn, DN_CONNECTION_STATE_ERROR_FATAL);
                                /* stop reading */
                                return RESPONSE_COMPLETE;
                }
@@ -1780,7 +1789,7 @@ is_data_node_ready(PGXCNodeHandle * conn)
                 * as well as an error stack overflow.
                 */
                if (proc_exit_inprogress)
-                       conn->state = DN_CONNECTION_STATE_ERROR_FATAL;
+                       PGXCNodeSetConnectionState(conn, DN_CONNECTION_STATE_ERROR_FATAL);
 
                /* don't read from from the connection if there is a fatal error */
                if (conn->state == DN_CONNECTION_STATE_ERROR_FATAL)
@@ -1800,7 +1809,7 @@ is_data_node_ready(PGXCNodeHandle * conn)
                         * with the connection
                         */
                        conn->transaction_status = msg[0];
-                       conn->state = DN_CONNECTION_STATE_IDLE;
+                       PGXCNodeSetConnectionState(conn, DN_CONNECTION_STATE_IDLE);
                        conn->combiner = NULL;
                        return true;
                }
@@ -1859,6 +1868,9 @@ pgxc_node_begin(int conn_count, PGXCNodeHandle **connections,
                        need_tran_block = true;
                else if (IS_PGXC_REMOTE_COORDINATOR)
                        need_tran_block = false;
+
+               elog(DEBUG5, "need_tran_block %d, connections[%d]->transaction_status %c",
+                               need_tran_block, i, connections[i]->transaction_status);
                /* Send BEGIN if not already in transaction */
                if (need_tran_block && connections[i]->transaction_status == 'I')
                {
@@ -1924,6 +1936,9 @@ pgxc_node_remote_cleanup_all(void)
        char               *resetcmd = "RESET ALL;RESET SESSION AUTHORIZATION;"
                                                           "RESET transaction_isolation;";
 
+       elog(DEBUG5, "pgxc_node_remote_cleanup_all - handles->co_conn_count %d,"
+                       "handles->dn_conn_count", handles->co_conn_count,
+                       handles->dn_conn_count);
        /*
         * We must handle reader and writer connections both since even a read-only
         * needs to be cleaned up.
@@ -1941,7 +1956,7 @@ pgxc_node_remote_cleanup_all(void)
                /* At this point connection should be in IDLE state */
                if (handle->state != DN_CONNECTION_STATE_IDLE)
                {
-                       handle->state = DN_CONNECTION_STATE_ERROR_FATAL;
+                       PGXCNodeSetConnectionState(handle, DN_CONNECTION_STATE_ERROR_FATAL);
                        continue;
                }
 
@@ -1954,7 +1969,7 @@ pgxc_node_remote_cleanup_all(void)
                        ereport(WARNING,
                                        (errcode(ERRCODE_INTERNAL_ERROR),
                                         errmsg("Failed to clean up data nodes")));
-                       handle->state = DN_CONNECTION_STATE_ERROR_FATAL;
+                       PGXCNodeSetConnectionState(handle, DN_CONNECTION_STATE_ERROR_FATAL);
                        continue;
                }
                new_connections[new_conn_count++] = handle;
@@ -1967,7 +1982,7 @@ pgxc_node_remote_cleanup_all(void)
                /* At this point connection should be in IDLE state */
                if (handle->state != DN_CONNECTION_STATE_IDLE)
                {
-                       handle->state = DN_CONNECTION_STATE_ERROR_FATAL;
+                       PGXCNodeSetConnectionState(handle, DN_CONNECTION_STATE_ERROR_FATAL);
                        continue;
                }
 
@@ -1980,7 +1995,7 @@ pgxc_node_remote_cleanup_all(void)
                        ereport(WARNING,
                                        (errcode(ERRCODE_INTERNAL_ERROR),
                                         errmsg("Failed to clean up data nodes")));
-                       handle->state = DN_CONNECTION_STATE_ERROR_FATAL;
+                       PGXCNodeSetConnectionState(handle, DN_CONNECTION_STATE_ERROR_FATAL);
                        continue;
                }
                new_connections[new_conn_count++] = handle;
@@ -2601,6 +2616,9 @@ pgxc_node_remote_abort(void)
 
        SetSendCommandId(false);
 
+       elog(DEBUG5, "pgxc_node_remote_abort - dn_conn_count %d, co_conn_count %d",
+                       handles->dn_conn_count, handles->co_conn_count);
+
        for (i = 0; i < handles->dn_conn_count; i++)
        {
                PGXCNodeHandle *conn = handles->datanode_handles[i];
@@ -2609,6 +2627,10 @@ pgxc_node_remote_abort(void)
                if (conn->sock == NO_SOCKET)
                        continue;
 
+               elog(DEBUG5, "node %s, conn->transaction_status %c",
+                               conn->nodename,
+                               conn->transaction_status);
+
                if (conn->transaction_status != 'I')
                {
                        /* Read in any pending input */
@@ -2619,7 +2641,7 @@ pgxc_node_remote_abort(void)
                         * Do not matter, is there committed or failed transaction,
                         * just send down rollback to finish it.
                         */
-                       if (pgxc_node_send_query(conn, rollbackCmd))
+                       if (pgxc_node_send_rollback(conn, rollbackCmd))
                        {
                                add_error_message(conn,
                                                "failed to send ROLLBACK TRANSACTION command");
@@ -2646,7 +2668,7 @@ pgxc_node_remote_abort(void)
                         * Do not matter, is there committed or failed transaction,
                         * just send down rollback to finish it.
                         */
-                       if (pgxc_node_send_query(conn, rollbackCmd))
+                       if (pgxc_node_send_rollback(conn, rollbackCmd))
                        {
                                add_error_message(conn,
                                                "failed to send ROLLBACK TRANSACTION command");
@@ -3222,6 +3244,9 @@ pgxc_start_command_on_connection(PGXCNodeHandle *connection,
        RemoteQuery     *step = (RemoteQuery *) combiner->ss.ps.plan;
        CHECK_OWNERSHIP(connection, combiner);
 
+       elog(DEBUG5, "pgxc_start_command_on_connection - node %s, state %d",
+                       connection->nodename, connection->state);
+
        /*
         * Scan descriptor would be valid and would contain a valid snapshot
         * in cases when we need to send out of order command id to data node
@@ -3234,7 +3259,7 @@ pgxc_start_command_on_connection(PGXCNodeHandle *connection,
 
        if (snapshot && pgxc_node_send_snapshot(connection, snapshot))
                return false;
-       if (step->statement || step->cursor || step->remote_param_types)
+       if (step->statement || step->cursor || remotestate->rqs_num_params)
        {
                /* need to use Extended Query Protocol */
                int     fetch = 0;
@@ -3262,8 +3287,8 @@ pgxc_start_command_on_connection(PGXCNodeHandle *connection,
                                                        prepared ? NULL : step->sql_statement,
                                                        step->statement,
                                                        step->cursor,
-                                                       step->remote_num_params,
-                                                       step->remote_param_types,
+                                                       remotestate->rqs_num_params,
+                                                       remotestate->rqs_param_types,
                                                        remotestate->paramval_len,
                                                        remotestate->paramval_data,
                                                        step->has_row_marks ? true : step->read_only,
@@ -3272,6 +3297,7 @@ pgxc_start_command_on_connection(PGXCNodeHandle *connection,
        }
        else
        {
+               combiner->extended_query = false;
                if (pgxc_node_send_query(connection, step->sql_statement) != 0)
                        return false;
        }
@@ -3684,14 +3710,16 @@ ExecCloseRemoteStatement(const char *stmt_name, List *nodelist)
                         * unclosed statement on the Datanode as a fatal issue and
                         * force connection is discarded
                         */
-                       connections[i]->state = DN_CONNECTION_STATE_ERROR_FATAL;
+                       PGXCNodeSetConnectionState(connections[i],
+                                       DN_CONNECTION_STATE_ERROR_FATAL);
                        ereport(WARNING,
                                        (errcode(ERRCODE_INTERNAL_ERROR),
                                         errmsg("Failed to close Datanode statemrnt")));
                }
                if (pgxc_node_send_sync(connections[i]) != 0)
                {
-                       connections[i]->state = DN_CONNECTION_STATE_ERROR_FATAL;
+                       PGXCNodeSetConnectionState(connections[i],
+                                       DN_CONNECTION_STATE_ERROR_FATAL);
                        ereport(WARNING,
                                        (errcode(ERRCODE_INTERNAL_ERROR),
                                         errmsg("Failed to close Datanode statement")));
@@ -3709,7 +3737,8 @@ ExecCloseRemoteStatement(const char *stmt_name, List *nodelist)
                if (pgxc_node_receive(conn_count, connections, NULL))
                {
                        for (i = 0; i <= conn_count; i++)
-                               connections[i]->state = DN_CONNECTION_STATE_ERROR_FATAL;
+                               PGXCNodeSetConnectionState(connections[i],
+                                               DN_CONNECTION_STATE_ERROR_FATAL);
 
                        ereport(ERROR,
                                        (errcode(ERRCODE_INTERNAL_ERROR),
@@ -3779,6 +3808,144 @@ DataNodeCopyInBinaryForAll(char *msg_buf, int len, int conn_count,
        return 0;
 }
 
+/*
+ * Encode parameter values to format of DataRow message (the same format is
+ * used in Bind) to prepare for sending down to Datanodes.
+ * The data row is copied to RemoteQueryState.paramval_data.
+ */
+void
+SetDataRowForExtParams(ParamListInfo paraminfo, RemoteQueryState *rq_state)
+{
+       StringInfoData buf;
+       uint16 n16;
+       int i;
+       int real_num_params = 0;
+       RemoteQuery *node = (RemoteQuery*) rq_state->combiner.ss.ps.plan;
+
+       /* If there are no parameters, there is no data to BIND. */
+       if (!paraminfo)
+               return;
+
+       Assert(!rq_state->paramval_data);
+
+       /*
+        * It is necessary to fetch parameters
+        * before looking at the output value.
+        */
+       for (i = 0; i < paraminfo->numParams; i++)
+       {
+               ParamExternData *param;
+
+               param = &paraminfo->params[i];
+
+               if (!OidIsValid(param->ptype) && paraminfo->paramFetch != NULL)
+                       (*paraminfo->paramFetch) (paraminfo, i + 1);
+
+               /*
+                * This is the last parameter found as useful, so we need
+                * to include all the previous ones to keep silent the remote
+                * nodes. All the parameters prior to the last usable having no
+                * type available will be considered as NULL entries.
+                */
+               if (OidIsValid(param->ptype))
+                       real_num_params = i + 1;
+       }
+
+       /*
+        * If there are no parameters available, simply leave.
+        * This is possible in the case of a query called through SPI
+        * and using no parameters.
+        */
+       if (real_num_params == 0)
+       {
+               rq_state->paramval_data = NULL;
+               rq_state->paramval_len = 0;
+               return;
+       }
+
+       initStringInfo(&buf);
+
+       /* Number of parameter values */
+       n16 = htons(real_num_params);
+       appendBinaryStringInfo(&buf, (char *) &n16, 2);
+
+       /* Parameter values */
+       for (i = 0; i < real_num_params; i++)
+       {
+               ParamExternData *param = &paraminfo->params[i];
+               uint32 n32;
+
+               /*
+                * Parameters with no types are considered as NULL and treated as integer
+                * The same trick is used for dropped columns for remote DML generation.
+                */
+               if (param->isnull || !OidIsValid(param->ptype))
+               {
+                       n32 = htonl(-1);
+                       appendBinaryStringInfo(&buf, (char *) &n32, 4);
+               }
+               else
+               {
+                       Oid             typOutput;
+                       bool    typIsVarlena;
+                       Datum   pval;
+                       char   *pstring;
+                       int             len;
+
+                       /* Get info needed to output the value */
+                       getTypeOutputInfo(param->ptype, &typOutput, &typIsVarlena);
+
+                       /*
+                        * If we have a toasted datum, forcibly detoast it here to avoid
+                        * memory leakage inside the type's output routine.
+                        */
+                       if (typIsVarlena)
+                               pval = PointerGetDatum(PG_DETOAST_DATUM(param->value));
+                       else
+                               pval = param->value;
+
+                       /* Convert Datum to string */
+                       pstring = OidOutputFunctionCall(typOutput, pval);
+
+                       /* copy data to the buffer */
+                       len = strlen(pstring);
+                       n32 = htonl(len);
+                       appendBinaryStringInfo(&buf, (char *) &n32, 4);
+                       appendBinaryStringInfo(&buf, pstring, len);
+               }
+       }
+
+
+       /*
+        * If parameter types are not already set, infer them from
+        * the paraminfo.
+        */
+       if (node->rq_num_params > 0)
+       {
+               /*
+                * Use the already known param types for BIND. Parameter types
+                * can be already known when the same plan is executed multiple
+                * times.
+                */
+               if (node->rq_num_params != real_num_params)
+                       elog(ERROR, "Number of user-supplied parameters do not match "
+                                               "the number of remote parameters");
+               rq_state->rqs_num_params = node->rq_num_params;
+               rq_state->rqs_param_types = node->rq_param_types;
+       }
+       else
+       {
+               rq_state->rqs_num_params = real_num_params;
+               rq_state->rqs_param_types = (Oid *) palloc(sizeof(Oid) * real_num_params);
+               for (i = 0; i < real_num_params; i++)
+                       rq_state->rqs_param_types[i] = paraminfo->params[i].ptype;
+       }
+
+       /* Assign the newly allocated data row to paramval */
+       rq_state->paramval_data = buf.data;
+       rq_state->paramval_len = buf.len;
+}
+
 /*
  * Clear per transaction remote information
  */
@@ -3972,6 +4139,7 @@ PreAbort_Remote(void)
         * certain issues for aborted transactions, we drop the connections.
         * Revisit and fix the issue
         */
+       elog(DEBUG5, "temp_object_included %d", temp_object_included);
        if (!temp_object_included)
        {
                /* Clean up remote sessions */
@@ -4381,14 +4549,9 @@ ExecInitRemoteQuery(RemoteQuery *node, EState *estate, int eflags)
 
        /*
         * If there are parameters supplied, get them into a form to be sent to the
-        * datanodes with bind message. We should not have had done this before.
+        * Datanodes with bind message. We should not have had done this before.
         */
-       if (estate->es_param_list_info)
-       {
-               Assert(!remotestate->paramval_data);
-               remotestate->paramval_len = ParamListToDataRow(estate->es_param_list_info,
-                                                                                               &remotestate->paramval_data);
-       }
+       SetDataRowForExtParams(estate->es_param_list_info, remotestate);
 
        /* We need expression context to evaluate */
        if (node->exec_nodes && node->exec_nodes->en_expr)
@@ -6112,7 +6275,7 @@ ExecEndRemoteSubplan(RemoteSubplanState *node)
                 * state will be changed back to IDLE and conn->coordinator will be
                 * cleared.
                 */
-               conn->state = DN_CONNECTION_STATE_CLOSE;
+               PGXCNodeSetConnectionState(conn, DN_CONNECTION_STATE_CLOSE);
        }
 
        while (combiner->conn_count > 0)
index f55e003e9ff7080f5fcb2f4cf110c3ecf5c045c2..87a2f6167fb96e56ec01ba88b71da514868bcbc5 100644 (file)
@@ -403,7 +403,7 @@ pgxc_node_init(PGXCNodeHandle *handle, int sock, bool global_session, int pid)
        handle->sock = sock;
        handle->backend_pid = pid;
        handle->transaction_status = 'I';
-       handle->state = DN_CONNECTION_STATE_IDLE;
+       PGXCNodeSetConnectionState(handle, DN_CONNECTION_STATE_IDLE);
        handle->read_only = true;
        handle->ck_resp_rollback = false;
        handle->combiner = NULL;
@@ -481,7 +481,8 @@ pgxc_node_receive(const int conn_count,
                else
                {
                        /* flag as bad, it will be removed from the list */
-                       connections[i]->state = DN_CONNECTION_STATE_ERROR_FATAL;
+                       PGXCNodeSetConnectionState(connections[i],
+                                       DN_CONNECTION_STATE_ERROR_FATAL);
                        pool_fd[i].fd = -1;
                        pool_fd[i].events = 0;
                }
@@ -523,7 +524,8 @@ retry:
                /* Handle timeout */
                elog(DEBUG1, "timeout %ld while waiting for any response from %d connections", timeout_ms,conn_count);
                for (i = 0; i < conn_count; i++)
-                       connections[i]->state = DN_CONNECTION_STATE_ERROR_FATAL;
+                       PGXCNodeSetConnectionState(connections[i],
+                                       DN_CONNECTION_STATE_ERROR_FATAL);
                return NO_ERROR_OCCURED;
        }
 
@@ -543,7 +545,8 @@ retry:
                                if ( read_status == EOF || read_status < 0 )
                                {
                                        /* Can not read - no more actions, just discard connection */
-                                       conn->state = DN_CONNECTION_STATE_ERROR_FATAL;
+                                       PGXCNodeSetConnectionState(conn,
+                                                       DN_CONNECTION_STATE_ERROR_FATAL);
                                        add_error_message(conn, "unexpected EOF on datanode connection.");
                                        elog(WARNING, "unexpected EOF on datanode oid connection: %d", conn->nodeoid);
 
@@ -570,7 +573,8 @@ retry:
                                        (pool_fd[i].revents & POLLNVAL)
                                        )
                        {
-                               connections[i]->state = DN_CONNECTION_STATE_ERROR_FATAL;
+                               PGXCNodeSetConnectionState(connections[i],
+                                               DN_CONNECTION_STATE_ERROR_FATAL);
                                add_error_message(conn, "unexpected network error on datanode connection");
                                elog(WARNING, "unexpected EOF on datanode oid connection: %d with event %d", conn->nodeoid,pool_fd[i].revents);
                                /* Should we check/read from the other connections before returning? */
@@ -690,7 +694,8 @@ retry:
                                                                "Datanode closed the connection unexpectedly\n"
                                        "\tThis probably means the Datanode terminated abnormally\n"
                                                                "\tbefore or while processing the request.\n");
-                               conn->state = DN_CONNECTION_STATE_ERROR_FATAL;  /* No more connection to
+                               PGXCNodeSetConnectionState(conn,
+                                               DN_CONNECTION_STATE_ERROR_FATAL);       /* No more connection to
                                                                                                                        * backend */
                                closesocket(conn->sock);
                                conn->sock = NO_SOCKET;
@@ -1528,7 +1533,7 @@ pgxc_node_send_execute(PGXCNodeHandle * handle, const char *portal, int fetch)
        memcpy(handle->outBuffer + handle->outEnd, &fetch, 4);
        handle->outEnd += 4;
 
-       handle->state = DN_CONNECTION_STATE_QUERY;
+       PGXCNodeSetConnectionState(handle, DN_CONNECTION_STATE_QUERY);
 
        return 0;
 }
@@ -1679,14 +1684,21 @@ pgxc_node_flush_read(PGXCNodeHandle *handle)
 /*
  * Send specified statement down to the PGXC node
  */
-int
-pgxc_node_send_query(PGXCNodeHandle * handle, const char *query)
+static int
+pgxc_node_send_query_internal(PGXCNodeHandle * handle, const char *query,
+               bool rollback)
 {
        int                     strLen;
        int                     msgLen;
 
-       /* Invalid connection state, return error */
-       if (handle->state != DN_CONNECTION_STATE_IDLE)
+       /*
+        * Its appropriate to send ROLLBACK commands on a failed connection, but
+        * for everything else we expect the connection to be in a sane state
+        */
+       elog(DEBUG5, "pgxc_node_send_query - handle->state %d, node %s, query %s",
+                       handle->state, handle->nodename, query);
+       if ((handle->state != DN_CONNECTION_STATE_IDLE) &&
+               !(handle->state == DN_CONNECTION_STATE_ERROR_FATAL && rollback))
                return EOF;
 
        strLen = strlen(query) + 1;
@@ -1707,11 +1719,23 @@ pgxc_node_send_query(PGXCNodeHandle * handle, const char *query)
        memcpy(handle->outBuffer + handle->outEnd, query, strLen);
        handle->outEnd += strLen;
 
-       handle->state = DN_CONNECTION_STATE_QUERY;
+       PGXCNodeSetConnectionState(handle, DN_CONNECTION_STATE_QUERY);
 
        return pgxc_node_flush(handle);
 }
 
+int
+pgxc_node_send_rollback(PGXCNodeHandle *handle, const char *query)
+{
+       return pgxc_node_send_query_internal(handle, query, true);
+}
+
+int
+pgxc_node_send_query(PGXCNodeHandle *handle, const char *query)
+{
+       return pgxc_node_send_query_internal(handle, query, false);
+}
+
 
 /*
  * Send the GXID down to the PGXC node
@@ -2717,7 +2741,7 @@ pgxc_node_set_query(PGXCNodeHandle *handle, const char *set_query)
                 * as well as an error stack overflow.
                 */
                if (proc_exit_inprogress)
-                       handle->state = DN_CONNECTION_STATE_ERROR_FATAL;
+                       PGXCNodeSetConnectionState(handle, DN_CONNECTION_STATE_ERROR_FATAL);
 
                /* don't read from from the connection if there is a fatal error */
                if (handle->state == DN_CONNECTION_STATE_ERROR_FATAL)
@@ -2738,14 +2762,14 @@ pgxc_node_set_query(PGXCNodeHandle *handle, const char *set_query)
                if (msgtype == 'E')     /* ErrorResponse */
                {
                        handle->error = pstrdup(msg);
-                       handle->state = DN_CONNECTION_STATE_ERROR_FATAL;
+                       PGXCNodeSetConnectionState(handle, DN_CONNECTION_STATE_ERROR_FATAL);
                        break;
                }
 
                if (msgtype == 'Z') /* ReadyForQuery */
                {
                        handle->transaction_status = msg[0];
-                       handle->state = DN_CONNECTION_STATE_IDLE;
+                       PGXCNodeSetConnectionState(handle, DN_CONNECTION_STATE_IDLE);
                        handle->combiner = NULL;
                        break;
                }
@@ -2795,3 +2819,11 @@ DoInvalidateRemoteHandles(void)
 
        return result;
 }
+
+void
+PGXCNodeSetConnectionState(PGXCNodeHandle *handle, DNConnectionState new_state)
+{
+       elog(DEBUG5, "Changing connection state for node %s, old state %d, "
+                       "new state %d", handle->nodename, handle->state, new_state);
+       handle->state = new_state;
+}
index eceb7be680956a4e9013f01769e2d13c63f5478b..d8c1d6fa1cda779ffeb1102b200d6408a3f57c97 100644 (file)
@@ -151,6 +151,8 @@ typedef struct RemoteQueryState
        /* Support for parameters */
        char       *paramval_data;              /* parameter data, format is like in BIND */
        int                     paramval_len;           /* length of parameter values data */
+       Oid                *rqs_param_types;    /* Types of the remote params */
+       int                     rqs_num_params;
 
        int                     eflags;                 /* capability flags to pass to tuplestore */
        bool            eof_underlying; /* reached end of underlying plan? */
@@ -272,7 +274,7 @@ extern void BufferConnection(PGXCNodeHandle *conn);
 
 extern void ExecRemoteQueryReScan(RemoteQueryState *node, ExprContext *exprCtxt);
 
-extern int ParamListToDataRow(ParamListInfo params, char** result);
+extern void SetDataRowForExtParams(ParamListInfo params, RemoteQueryState *rq_state);
 
 extern void ExecCloseRemoteStatement(const char *stmt_name, List *nodelist);
 extern char *PrePrepare_Remote(char *prepareGID, bool localNode, bool implicit);
index 163b0e4914834a4c1b1924d13d216040d3371366..64cf432ee895f1220d7d0ad8480f1546f860a2eb 100644 (file)
@@ -142,6 +142,7 @@ extern int  ensure_in_buffer_capacity(size_t bytes_needed, PGXCNodeHandle * handl
 extern int     ensure_out_buffer_capacity(size_t bytes_needed, PGXCNodeHandle * handle);
 
 extern int     pgxc_node_send_query(PGXCNodeHandle * handle, const char *query);
+extern int     pgxc_node_send_rollback(PGXCNodeHandle * handle, const char *query);
 extern int     pgxc_node_send_describe(PGXCNodeHandle * handle, bool is_statement,
                                                const char *name);
 extern int     pgxc_node_send_execute(PGXCNodeHandle * handle, const char *portal, int fetch);
@@ -187,5 +188,7 @@ extern char *PGXCNodeGetSessionParamStr(void);
 extern char *PGXCNodeGetTransactionParamStr(void);
 extern void pgxc_node_set_query(PGXCNodeHandle *handle, const char *set_query);
 extern void RequestInvalidateRemoteHandles(void);
+extern void PGXCNodeSetConnectionState(PGXCNodeHandle *handle,
+               DNConnectionState new_state);
 
 #endif /* PGXCNODE_H */
index 54713bcedf2879e1460203709fef7ef6a9ba0fda..f6357cd71155555da0c01d23b9328621987c4c5b 100644 (file)
@@ -91,11 +91,10 @@ typedef struct
        bool                    force_autocommit;       /* some commands like VACUUM require autocommit mode */
        char                    *statement;             /* if specified use it as a PreparedStatement name on Datanodes */
        char                    *cursor;                /* if specified use it as a Portal name on Datanodes */
-       int                     remote_num_params;              /* number of parameters specified for Prepared remote statement */
-       Oid                     *remote_param_types;            /* parameter types, this pointer is shared
-                                                        * across all the RemoteQuery nodes in the
-                                                        * plan. So, don't change this once set.
-                                                        */
+       int             rq_num_params;      /* number of parameters present in
+                                                                                  remote statement */
+       Oid             *rq_param_types;    /* parameter types for the remote
+                                                                                  statement */
        RemoteQueryExecType     exec_type;
        int                     reduce_level;           /* in case of reduced JOIN, it's level    */
        char                    *outer_alias;
index cc4a98231f7ed164eb1534e73025065375182c67..4f9dea9c2503db5117c5f73279d958074bd3c585 100644 (file)
@@ -2053,13 +2053,13 @@ begin
   return x;
 end$$ language plpgsql;
 select subxact_rollback_semantics();
-ERROR:  could not determine data type of parameter $1
-CONTEXT:  SQL statement "insert into foo values(x)"
-PL/pgSQL function subxact_rollback_semantics() line 5 at SQL statement
+ERROR:  Internal subtransactions not supported in Postgres-XL
+CONTEXT:  PL/pgSQL function subxact_rollback_semantics() line 6 during statement block entry
 select * from foo;
  f1 
 ----
-(0 rows)
+  1
+(1 row)
 
 drop table foo;
 create function trap_timeout() returns void as $$
@@ -2292,8 +2292,11 @@ $$ language plpgsql;
 -- PGXCTODO: This is failing due to issue 3522907, complicated SELECT queries in plpgsql functions
 select refcursor_test2(20000, 20000) as "Should be false",
        refcursor_test2(20, 20) as "Should be true";
-ERROR:  could not determine data type of parameter $1
-CONTEXT:  PL/pgSQL function refcursor_test2(integer,integer) line 7 at FETCH
+ Should be false | Should be true 
+-----------------+----------------
+ f               | t
+(1 row)
+
 --
 -- tests for cursors with named parameter arguments
 --
@@ -2314,8 +2317,11 @@ end
 $$ language plpgsql;
 select namedparmcursor_test1(20000, 20000) as "Should be false",
        namedparmcursor_test1(20, 20) as "Should be true";
-ERROR:  could not determine data type of parameter $1
-CONTEXT:  PL/pgSQL function namedparmcursor_test1(integer,integer) line 7 at FETCH
+ Should be false | Should be true 
+-----------------+----------------
+ f               | t
+(1 row)
+
 -- mixing named and positional argument notations
 create function namedparmcursor_test2(int, int) returns boolean as $$
 declare
@@ -2333,8 +2339,11 @@ begin
 end
 $$ language plpgsql;
 select namedparmcursor_test2(20, 20);
-ERROR:  could not determine data type of parameter $1
-CONTEXT:  PL/pgSQL function namedparmcursor_test2(integer,integer) line 7 at FETCH
+ namedparmcursor_test2 
+-----------------------
+ t
+(1 row)
+
 -- mixing named and positional: param2 is given twice, once in named notation
 -- and second time in positional notation. Should throw an error at parse time
 create function namedparmcursor_test3() returns void as $$
@@ -3125,9 +3134,9 @@ begin
   raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 end$$ language plpgsql;
 select footest();
-ERROR:  could not determine data type of parameter $1
-CONTEXT:  SQL statement "select * from foo where f1 = p1 and f1::text = p3"
-PL/pgSQL function footest() line 8 at SQL statement
+ERROR:  query returned no rows
+DETAIL:  parameters: p1 = '2', p3 = 'foo'
+CONTEXT:  PL/pgSQL function footest() line 8 at SQL statement
 create or replace function footest() returns void as $$
 declare
 x record;
@@ -3139,9 +3148,9 @@ begin
   raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 end$$ language plpgsql;
 select footest();
-ERROR:  could not determine data type of parameter $1
-CONTEXT:  SQL statement "select * from foo where f1 > p1 or f1::text = p3"
-PL/pgSQL function footest() line 8 at SQL statement
+ERROR:  query returned more than one row
+DETAIL:  parameters: p1 = '2', p3 = 'foo'
+CONTEXT:  PL/pgSQL function footest() line 8 at SQL statement
 create or replace function footest() returns void as $$
 declare x record;
 begin
@@ -3160,9 +3169,9 @@ begin
   raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 end$$ language plpgsql;
 select footest();
-ERROR:  there is no parameter $1
-CONTEXT:  SQL statement "select * from foo where f1 = $1 or f1::text = $2"
-PL/pgSQL function footest() line 5 at EXECUTE
+ERROR:  query returned no rows
+DETAIL:  parameters: $1 = '0', $2 = 'foo'
+CONTEXT:  PL/pgSQL function footest() line 5 at EXECUTE
 create or replace function footest() returns void as $$
 declare x record;
 begin
@@ -3171,9 +3180,9 @@ begin
   raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 end$$ language plpgsql;
 select footest();
-ERROR:  there is no parameter $1
-CONTEXT:  SQL statement "select * from foo where f1 > $1"
-PL/pgSQL function footest() line 5 at EXECUTE
+ERROR:  query returned more than one row
+DETAIL:  parameters: $1 = '1'
+CONTEXT:  PL/pgSQL function footest() line 5 at EXECUTE
 create or replace function footest() returns void as $$
 declare x record;
 begin
@@ -3197,9 +3206,8 @@ begin
   raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 end$$ language plpgsql;
 select footest();
-ERROR:  could not determine data type of parameter $1
-CONTEXT:  SQL statement "select * from foo where f1 > p1 or f1::text = p3"
-PL/pgSQL function footest() line 10 at SQL statement
+ERROR:  query returned more than one row
+CONTEXT:  PL/pgSQL function footest() line 10 at SQL statement
 reset plpgsql.print_strict_params;
 create or replace function footest() returns void as $$
 -- override the global
@@ -3214,9 +3222,9 @@ begin
   raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 end$$ language plpgsql;
 select footest();
-ERROR:  could not determine data type of parameter $1
-CONTEXT:  SQL statement "select * from foo where f1 > p1 or f1::text = p3"
-PL/pgSQL function footest() line 10 at SQL statement
+ERROR:  query returned more than one row
+DETAIL:  parameters: p1 = '2', p3 = 'foo'
+CONTEXT:  PL/pgSQL function footest() line 10 at SQL statement
 -- test warnings and errors
 set plpgsql.extra_warnings to 'all';
 ERROR:  syntax error at or near "all"
@@ -4873,8 +4881,15 @@ end;
 $$ language plpgsql;
 -- PGXCTODO: This is failing due to issue 3522907, complicated SELECT queries in plpgsql functions
 select * from conflict_test() order by 1,2;
-ERROR:  could not determine data type of parameter $1
-CONTEXT:  PL/pgSQL function conflict_test() line 6 at FOR over SELECT rows
+ q1 |        q2         
+----+-------------------
+ 42 | -4567890123456789
+ 42 |               123
+ 42 |               456
+ 42 |  4567890123456789
+ 42 |  4567890123456789
+(5 rows)
+
 create or replace function conflict_test() returns setof int8_tbl as $$
 #variable_conflict use_column
 declare r record;