diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index 850734ac96..989354ca06 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -191,3 +191,6 @@ PQclosePrepared           188
 PQclosePortal             189
 PQsendClosePrepared       190
 PQsendClosePortal         191
+PQsendQueryPreparedPredescribed     192
+PQgetResultPredescribed             193
+PQisBusyPredescribed                194
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 04610ccf5e..a18bead9e6 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -72,8 +72,19 @@ static int	PQsendQueryGuts(PGconn *conn,
 							const char *const *paramValues,
 							const int *paramLengths,
 							const int *paramFormats,
-							int resultFormat);
-static void parseInput(PGconn *conn);
+							int resultFormat,
+							bool sendDescribe);
+static int	PQsendQueryPreparedInternal(PGconn *conn,
+										const char *stmtName,
+										int nParams,
+										const char *const *paramValues,
+										const int *paramLengths,
+										const int *paramFormats,
+										int resultFormat,
+										bool sendDescribe);
+static int	PQisBusyInternal(PGconn *conn, PGresult *description);
+static PGresult *PQgetResultInternal(PGconn *conn, PGresult *description);
+static void parseInput(PGconn *conn, PGresult *description);
 static PGresult *getCopyResult(PGconn *conn, ExecStatusType copytype);
 static bool PQexecStart(PGconn *conn);
 static PGresult *PQexecFinish(PGconn *conn);
@@ -1528,7 +1539,8 @@ PQsendQueryParams(PGconn *conn,
 						   paramValues,
 						   paramLengths,
 						   paramFormats,
-						   resultFormat);
+						   resultFormat,
+						   true /* send Describe */ );
 }
 
 /*
@@ -1643,6 +1655,26 @@ PQsendQueryPrepared(PGconn *conn,
 					const int *paramLengths,
 					const int *paramFormats,
 					int resultFormat)
+{
+	return PQsendQueryPreparedInternal(conn,
+									   stmtName,
+									   nParams,
+									   paramValues,
+									   paramLengths,
+									   paramFormats,
+									   resultFormat,
+									   true /* send Describe */ );
+}
+
+int
+PQsendQueryPreparedInternal(PGconn *conn,
+							const char *stmtName,
+							int nParams,
+							const char *const *paramValues,
+							const int *paramLengths,
+							const int *paramFormats,
+							int resultFormat,
+							bool sendDescribe)
 {
 	if (!PQsendQueryStart(conn, true))
 		return 0;
@@ -1668,7 +1700,50 @@ PQsendQueryPrepared(PGconn *conn,
 						   paramValues,
 						   paramLengths,
 						   paramFormats,
-						   resultFormat);
+						   resultFormat,
+						   sendDescribe);
+}
+
+int
+PQsendQueryPreparedPredescribed(PGconn *conn,
+								const char *stmtName,
+								int nParams,
+								const char *const *paramValues,
+								const int *paramLengths,
+								const int *paramFormats,
+								int resultFormat,
+								PGresult *description)
+{
+	int			i;
+	int			nFields;
+	int			send_result;
+
+	if (!description)
+	{
+		libpq_append_conn_error(conn, "query result description is a null pointer");
+		return 0;
+	}
+
+	send_result = PQsendQueryPreparedInternal(conn,
+											  stmtName,
+											  nParams,
+											  paramValues,
+											  paramLengths,
+											  paramFormats,
+											  resultFormat,
+											  false /* send Describe */ );
+
+	/* We only need to adjust attributes format if send succeeded */
+	if (send_result && description->attDescs != NULL)
+	{
+		nFields = description->numAttributes;
+		for (i = 0; i < nFields; i++)
+		{
+			description->attDescs[i].format = resultFormat;
+		}
+	}
+
+	return send_result;
 }
 
 /*
@@ -1766,7 +1841,8 @@ PQsendQueryGuts(PGconn *conn,
 				const char *const *paramValues,
 				const int *paramLengths,
 				const int *paramFormats,
-				int resultFormat)
+				int resultFormat,
+				bool sendDescribe)
 {
 	int			i;
 	PGcmdQueueEntry *entry;
@@ -1873,12 +1949,15 @@ PQsendQueryGuts(PGconn *conn,
 	if (pqPutMsgEnd(conn) < 0)
 		goto sendFailed;
 
-	/* construct the Describe Portal message */
-	if (pqPutMsgStart(PqMsg_Describe, conn) < 0 ||
-		pqPutc('P', conn) < 0 ||
-		pqPuts("", conn) < 0 ||
-		pqPutMsgEnd(conn) < 0)
-		goto sendFailed;
+	if (sendDescribe)
+	{
+		/* construct the Describe Portal message */
+		if (pqPutMsgStart(PqMsg_Describe, conn) < 0 ||
+			pqPutc('P', conn) < 0 ||
+			pqPuts("", conn) < 0 ||
+			pqPutMsgEnd(conn) < 0)
+			goto sendFailed;
+	}
 
 	/* construct the Execute message */
 	if (pqPutMsgStart(PqMsg_Execute, conn) < 0 ||
@@ -1990,9 +2069,9 @@ PQconsumeInput(PGconn *conn)
  * Note that this function will NOT attempt to read more data from the backend.
  */
 static void
-parseInput(PGconn *conn)
+parseInput(PGconn *conn, PGresult *description)
 {
-	pqParseInput3(conn);
+	pqParseInput3Predescribed(conn, description);
 }
 
 /*
@@ -2002,12 +2081,18 @@ parseInput(PGconn *conn)
 
 int
 PQisBusy(PGconn *conn)
+{
+	return PQisBusyInternal(conn, NULL);
+}
+
+static int
+PQisBusyInternal(PGconn *conn, PGresult *description)
 {
 	if (!conn)
 		return false;
 
 	/* Parse any available data, if our state permits. */
-	parseInput(conn);
+	parseInput(conn, description);
 
 	/*
 	 * PQgetResult will return immediately in all states except BUSY.  Also,
@@ -2020,6 +2105,12 @@ PQisBusy(PGconn *conn)
 	return conn->asyncStatus == PGASYNC_BUSY && conn->status != CONNECTION_BAD;
 }
 
+int
+PQisBusyPredescribed(PGconn *conn, PGresult *description)
+{
+	return PQisBusyInternal(conn, description);
+}
+
 /*
  * PQgetResult
  *	  Get the next PGresult produced by a query.  Returns NULL if no
@@ -2033,6 +2124,12 @@ PQisBusy(PGconn *conn)
  */
 PGresult *
 PQgetResult(PGconn *conn)
+{
+	return PQgetResultInternal(conn, NULL);
+}
+
+static PGresult *
+PQgetResultInternal(PGconn *conn, PGresult *description)
 {
 	PGresult   *res;
 
@@ -2040,7 +2137,7 @@ PQgetResult(PGconn *conn)
 		return NULL;
 
 	/* Parse any available data, if our state permits. */
-	parseInput(conn);
+	parseInput(conn, description);
 
 	/* If not ready to return something, block until we are. */
 	while (conn->asyncStatus == PGASYNC_BUSY)
@@ -2078,7 +2175,7 @@ PQgetResult(PGconn *conn)
 		}
 
 		/* Parse it. */
-		parseInput(conn);
+		parseInput(conn, description);
 
 		/*
 		 * If we had a write error, but nothing above obtained a query result
@@ -2182,6 +2279,12 @@ PQgetResult(PGconn *conn)
 	return res;
 }
 
+PGresult *
+PQgetResultPredescribed(PGconn *conn, PGresult *description)
+{
+	return PQgetResultInternal(conn, description);
+}
+
 /*
  * getCopyResult
  *	  Helper for PQgetResult: generate result for COPY-in-progress cases
@@ -2638,7 +2741,7 @@ PQnotifies(PGconn *conn)
 		return NULL;
 
 	/* Parse any available data to see if we can extract NOTIFY messages. */
-	parseInput(conn);
+	parseInput(conn, NULL);
 
 	event = conn->notifyHead;
 	if (event)
@@ -2677,7 +2780,7 @@ PQputCopyData(PGconn *conn, const char *buffer, int nbytes)
 	 * input data into the input buffer happens down inside pqSendSome, but
 	 * it's not authorized to get rid of the data again.)
 	 */
-	parseInput(conn);
+	parseInput(conn, NULL);
 
 	if (nbytes > 0)
 	{
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 5613c56b14..8fa5853f84 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -55,6 +55,7 @@ static void reportErrorPosition(PQExpBuffer msg, const char *query,
 								int loc, int encoding);
 static int	build_startup_packet(const PGconn *conn, char *packet,
 								 const PQEnvironmentOption *options);
+static void pqParseInput3Internal(PGconn *conn, PGresult *description);
 
 
 /*
@@ -62,8 +63,8 @@ static int	build_startup_packet(const PGconn *conn, char *packet,
  * until input is exhausted or a stopping state is reached.
  * Note that this function will NOT attempt to read more data from the backend.
  */
-void
-pqParseInput3(PGconn *conn)
+static void
+pqParseInput3Internal(PGconn *conn, PGresult *description)
 {
 	char		id;
 	int			msgLength;
@@ -402,11 +403,38 @@ pqParseInput3(PGconn *conn)
 					}
 					else
 					{
-						/* Set up to report error at end of query */
-						libpq_append_conn_error(conn, "server sent data (\"D\" message) without prior row description (\"T\" message)");
-						pqSaveErrorResult(conn);
-						/* Discard the unexpected message */
-						conn->inCursor += msgLength;
+						if (!description)
+						{
+							/* Set up to report error at end of query */
+							libpq_append_conn_error(conn,
+													"server sent data (\"D\" message) without prior row description (\"T\" message)");
+							pqSaveErrorResult(conn);
+							/* Discard the unexpected message */
+							conn->inCursor += msgLength;
+						}
+						else
+						{
+							/*
+							 * We didn't receive row description from the
+							 * server, but we have externally provided
+							 * description, use that.
+							 */
+							if (conn->result == NULL)
+							{
+								conn->result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK);
+							}
+							if (!conn->result)
+							{
+								libpq_append_conn_error(conn, "out of memory");
+								pqSaveErrorResult(conn);
+							}
+							else
+							{
+								PQsetResultAttrs(conn->result, description->numAttributes, description->attDescs);
+								if (getAnotherTuple(conn, msgLength))
+									return;
+							}
+						}
 					}
 					break;
 				case PqMsg_CopyInResponse:
@@ -478,6 +506,18 @@ pqParseInput3(PGconn *conn)
 	}
 }
 
+void
+pqParseInput3(PGconn *conn)
+{
+	pqParseInput3Internal(conn, NULL);
+}
+
+void
+pqParseInput3Predescribed(PGconn *conn, PGresult *description)
+{
+	pqParseInput3Internal(conn, description);
+}
+
 /*
  * handleSyncLoss: clean up after loss of message-boundary sync
  *
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 97762d56f5..573b68fea2 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -462,11 +462,21 @@ extern int	PQsendQueryPrepared(PGconn *conn,
 								const int *paramLengths,
 								const int *paramFormats,
 								int resultFormat);
+extern int	PQsendQueryPreparedPredescribed(PGconn *conn,
+											const char *stmtName,
+											int nParams,
+											const char *const *paramValues,
+											const int *paramLengths,
+											const int *paramFormats,
+											int resultFormat,
+											PGresult *description);
 extern int	PQsetSingleRowMode(PGconn *conn);
 extern PGresult *PQgetResult(PGconn *conn);
+extern PGresult *PQgetResultPredescribed(PGconn *conn, PGresult *description);
 
 /* Routines for managing an asynchronous query */
 extern int	PQisBusy(PGconn *conn);
+extern int	PQisBusyPredescribed(PGconn *conn, PGresult *description);
 extern int	PQconsumeInput(PGconn *conn);
 
 /* Routines for pipeline mode management */
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index c745facfec..df91c39fbe 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -706,6 +706,7 @@ extern int	PQsendQueryContinue(PGconn *conn, const char *query);
 extern char *pqBuildStartupPacket3(PGconn *conn, int *packetlen,
 								   const PQEnvironmentOption *options);
 extern void pqParseInput3(PGconn *conn);
+extern void pqParseInput3Predescribed(PGconn *conn, PGresult *description);
 extern int	pqGetErrorNotice3(PGconn *conn, bool isError);
 extern void pqBuildErrorMessage3(PQExpBuffer msg, const PGresult *res,
 								 PGVerbosity verbosity, PGContextVisibility show_context);
