diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index 051f548594..f49b5acf99 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -1670,6 +1670,233 @@ sendFailed: return 0; } +/* + * PQsendPortalBind + * + * Bind a portal to a previously prepared statement with parameters + */ +int +PQsendPortalBindParams(PGconn* conn, + const char *stmtName, + const char *portalName, + int nParams, + const char *const *paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat) +{ + int i; + + if (!PQsendQueryStart(conn)) + return 0; + + /* This isn't gonna work on a 2.0 server */ + if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("function requires at least protocol version 3.0\n")); + return 0; + } + + /* check the arguments */ + if (!stmtName) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("statement name is a null pointer\n")); + return 0; + } + if (!portalName) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("portal name is a null pointer\n")); + return 0; + } + if (nParams < 0 || nParams > 65535) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("number of parameters must be between 0 and 65535\n")); + return 0; + } + + if (conn->xactStatus != PQTRANS_INTRANS) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("a portal must be bound inside a transaction block\n")); + return 0; + } + + /* Construct the Bind message */ + if (pqPutMsgStart('B', false, conn) < 0 || + pqPuts(portalName, conn) < 0 || + pqPuts(stmtName, conn) < 0) + goto sendFailed; + + /* Send parameter formats */ + if (nParams > 0 && paramFormats) + { + if (pqPutInt(nParams, 2, conn) < 0) + goto sendFailed; + for (i = 0; i < nParams; i++) + { + if (pqPutInt(paramFormats[i], 2, conn) < 0) + goto sendFailed; + } + } + else + { + if (pqPutInt(0, 2, conn) < 0) + goto sendFailed; + } + + if (pqPutInt(nParams, 2, conn) < 0) + goto sendFailed; + + /* Send parameters */ + for (i = 0; i < nParams; i++) + { + if (paramValues && paramValues[i]) + { + int nbytes; + + if (paramFormats && paramFormats[i] != 0) + { + /* binary parameter */ + if (paramLengths) + nbytes = paramLengths[i]; + else + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("length must be given for binary parameter\n")); + goto sendFailed; + } + } + else + { + /* text parameter, do not use paramLengths */ + nbytes = strlen(paramValues[i]); + } + if (pqPutInt(nbytes, 4, conn) < 0 || + pqPutnchar(paramValues[i], nbytes, conn) < 0) + goto sendFailed; + } + else + { + /* take the param as NULL */ + if (pqPutInt(-1, 4, conn) < 0) + goto sendFailed; + } + } + if (pqPutInt(1, 2, conn) < 0 || + pqPutInt(resultFormat, 2, conn)) + goto sendFailed; + if (pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* construct the Sync message */ + if (pqPutMsgStart('S', false, conn) < 0 || + pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* remember we are using extended query protocol */ + conn->queryclass = PGQUERY_EXTENDED; + + /* we don't have query text here, so just clean up the old one */ + if (conn->last_query) + free(conn->last_query); + else + conn->last_query = NULL; + + /* + * Give the data a push. In nonblock mode, don't complain if we're unable + * to send it all; PQgetResult() will do any additional flushing needed. + */ + if (pqFlush(conn) < 0) + goto sendFailed; + + /* OK, it's launched! */ + return 1; + +sendFailed: + /* error message should be set up already */ + return 0; +} + +/* + * PQsendPortalExecute + * + * Execute a portal previously bound with PQsendPortalBindParams. + * Will send a command to the backend to execute portal and return + * nRows rows. + */ +int PQsendPortalExecute(PGconn* conn, + const char* portalName, + int nRows) +{ + if (!PQsendQueryStart(conn)) + return 0; + + /* This isn't gonna work on a 2.0 server */ + if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("function requires at least protocol version 3.0\n")); + return 0; + } + + /* check the arguments */ + if (!portalName) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("portal name is a null pointer\n")); + return 0; + } + + /* TODO Some sanity check for nRows */ + + /* construct the Describe Portal message */ + if (pqPutMsgStart('D', false, conn) < 0 || + pqPutc('P', conn) < 0 || + pqPuts(portalName, conn) < 0 || + pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* construct the Execute message */ + if (pqPutMsgStart('E', false, conn) < 0 || + pqPuts(portalName, conn) < 0 || + pqPutInt(nRows, 4, conn) < 0 || + pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* construct the Sync message */ + if (pqPutMsgStart('S', false, conn) < 0 || + pqPutMsgEnd(conn) < 0) + goto sendFailed; + + + /* remember we are using extended query protocol */ + conn->queryclass = PGQUERY_EXTENDED; + + /* we don't have query text here, so just clean up the old one */ + if (conn->last_query) + free(conn->last_query); + else + conn->last_query = NULL; + + /* + * Give the data a push. In nonblock mode, don't complain if we're unable + * to send it all; PQgetResult() will do any additional flushing needed. + */ + if (pqFlush(conn) < 0) + goto sendFailed; + + /* OK, it's launched! */ + return 1; + +sendFailed: + /* error message should be set up already */ + return 0; +} + /* * Select row-by-row processing mode */ diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c index c97841eb13..922319b67e 100644 --- a/src/interfaces/libpq/fe-protocol3.c +++ b/src/interfaces/libpq/fe-protocol3.c @@ -403,6 +403,14 @@ pqParseInput3(PGconn *conn) * the COPY command. */ break; + case 's': /* Portal Suspended */ + /* + * We see this message only when an application issued an + * execute portal command with row limit. We can effectively + * ignore the message + */ + conn->asyncStatus = PGASYNC_READY; + break; default: printfPQExpBuffer(&conn->errorMessage, libpq_gettext( diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h index 5f65db30e4..8323fea492 100644 --- a/src/interfaces/libpq/libpq-fe.h +++ b/src/interfaces/libpq/libpq-fe.h @@ -428,6 +428,17 @@ extern int PQsendQueryPrepared(PGconn *conn, const int *paramLengths, const int *paramFormats, int resultFormat); +extern int PQsendPortalBindParams(PGconn* conn, + const char *stmtName, + const char *portalName, + int nParams, + const char *const *paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); +extern int PQsendPortalExecute(PGconn* conn, + const char* portalName, + int nRows); extern int PQsetSingleRowMode(PGconn *conn); extern PGresult *PQgetResult(PGconn *conn);