in src/odbc/rsodbc/rslibpq.c [1696:1884]
SQLRETURN libpqPrepareOnThread(RS_STMT_INFO *pStmt, char *pszCmd)
{
SQLRETURN rc = SQL_SUCCESS;
RS_CONN_INFO *pConn = pStmt->phdbc;
// Lock connection sem to protect multiple stmt execution at same time.
rsLockSem(pConn->hSemMultiStmt);
// Wait for current csc thread to finish, if any.
pgWaitForCscThreadToFinish(pConn->pgConn, FALSE);
if(pszCmd)
{
PGresult *pgResult = NULL;
ExecStatusType pqRc = PGRES_COMMAND_OK;
int asyncEnable = isAsyncEnable(pStmt);
int sendStatus = 1;
RS_PREPARE_INFO *pPrepare;
// TODO: improve the code repetition in if and else
if(asyncEnable)
{
int iNoOfBindParams = countBindParams(pStmt->pStmtAttr->pAPD->pDescRecHead);
if (pStmt->iNumOfOutOnlyParams == 0 && iNoOfBindParams == 0)
sendStatus = pqSendPrepareAndDescribe(pConn->pgConn, pStmt->szCursorName, pszCmd, 0, NULL);
else {
std::vector<Oid> paramTypes = getParamTypes(
iNoOfBindParams,
pStmt->pStmtAttr->pAPD->pDescRecHead,
pConn->pConnectProps);
sendStatus = pqSendPrepareAndDescribe(
pConn->pgConn, pStmt->szCursorName, pszCmd,
iNoOfBindParams,
paramTypes.empty() ? nullptr
: paramTypes.data());
}
if(sendStatus)
{
pgResult = PQgetResult(pConn->pgConn);
pqRc = PQresultStatus(pgResult);
}
else
pqRc = PGRES_FATAL_ERROR;
}
else
{
int iNoOfBindParams = countBindParams(pStmt->pStmtAttr->pAPD->pDescRecHead);
if(pStmt->iNumOfOutOnlyParams == 0 && iNoOfBindParams == 0)
pgResult = pqPrepare(pConn->pgConn, pStmt->szCursorName, pszCmd, 0, NULL);
else {
std::vector<Oid> paramTypes = getParamTypes(
iNoOfBindParams,
pStmt->pStmtAttr->pAPD->pDescRecHead,
pConn->pConnectProps);
// OUT parameters in the stmt
pgResult = pqPrepare(
pConn->pgConn, pStmt->szCursorName, pszCmd,
iNoOfBindParams,
paramTypes.empty() ? nullptr
: paramTypes.data());
}
pqRc = PQresultStatus(pgResult);
}
// Multi prepare loop
do
{
if(!(pqRc == PGRES_COMMAND_OK
|| pqRc == PGRES_TUPLES_OK
|| pqRc == PGRES_COPY_IN
|| pqRc == PGRES_COPY_OUT))
{
char *pError = libpqErrorMsg(pConn);
// Even one result in error, we are retuning error.
rc = SQL_ERROR;
if(pError && *pError != '\0')
addError(&pStmt->pErrorList,"HY000", pError, 0, pConn);
// Clear this result because we are not storing it
PQclear(pgResult);
pgResult = NULL;
pPrepare = NULL;
}
else
{
PGresult *pgResultDescParam = PQgetResult(pConn->pgConn);
if(pgResultDescParam == NULL)
{
// 'Z' must be followed to 't' during above PQgetResult
// So read from resultForDescParam
pgResultDescParam = pqgetResultForDescribeParam(pConn->pgConn);
}
// Create prepare object
pPrepare = (RS_PREPARE_INFO *)new RS_PREPARE_INFO(pStmt, pgResult);
// Get describe param result and then get param information
rc = libpqDescribeParams(pStmt, pPrepare, pgResultDescParam);
// Add prepared to statement
addPrepare(pStmt, pPrepare);
if(pqRc == PGRES_TUPLES_OK)
{
// SELECT kind of operation returning rows description
pgResult = PQgetResult(pConn->pgConn);
if(pgResult)
{
// Create result object for description
RS_RESULT_INFO *pResult = createResultObject(pStmt, pgResult);
getResultDescription(pgResult, pResult, FALSE);
// Add result to statement
pPrepare->pResultForDescribeCol = pResult;
}
} // SELECT
} // Success
if (pqRc == PGRES_COPY_IN ||
pqRc == PGRES_COPY_OUT ||
pqRc == PGRES_COPY_BOTH ||
(pConn && pConn->pgConn && PQstatus(pConn->pgConn) == CONNECTION_BAD)
)
{
// No need to loop any more.
break;
}
// Loop for next result
if(asyncEnable)
{
// If command not send, no need to check for next result.
if(!sendStatus)
break;
}
// Get next result description
pgResult = PQgetResult(pConn->pgConn);
if(!pgResult)
{
// 'Z' must be followed to 'T' during above PQgetResult
// So read from resultForDescRowPrep and release it.
PGresult *pgResultDescRowPrep = pqgetResultForDescribeRowPrep(pConn->pgConn);
if(pgResultDescRowPrep)
{
// Create result object for description
RS_RESULT_INFO *pResult = createResultObject(pStmt, pgResultDescRowPrep);
getResultDescription(pgResultDescRowPrep, pResult, FALSE);
pPrepare->pResultForDescribeCol = pResult;
}
break;
}
// Get result status
pqRc = PQresultStatus(pgResult);
}while(TRUE); // Results loop
}
else
{
rc = SQL_ERROR;
addError(&pStmt->pErrorList,"HY000", "Invalid command buffer", 0, NULL);
goto error;
}
error:
// Unlock connection sem
rsUnlockSem(pConn->hSemMultiStmt);
return rc;
}