SQLRETURN libpqPrepareOnThread()

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;
}