SQLRETURN SQL_API RS_STMT_INFO::RS_SQLFetchScroll()

in src/odbc/rsodbc/rsresult.cpp [1247:1689]


SQLRETURN  SQL_API RS_STMT_INFO::RS_SQLFetchScroll(SQLHSTMT phstmt,
                                      SQLSMALLINT hFetchOrientation,
                                      SQLLEN iFetchOffset)
{
    SQLRETURN rc = SQL_SUCCESS;
    RS_STMT_INFO *pStmt = (RS_STMT_INFO *)phstmt;
    RS_RESULT_INFO *pResult;

    if(!VALID_HSTMT(phstmt))
    {
        rc = SQL_INVALID_HANDLE;
        goto error;
    }

    // Clear error list
    pStmt->pErrorList = clearErrorList(pStmt->pErrorList);

    if(!isScrollableCursor(pStmt)
        && (hFetchOrientation != SQL_FETCH_NEXT))
    {
        rc = SQL_ERROR;
        addError(&pStmt->pErrorList,"HY106", "Fetch type out of range", 0, NULL);
        goto error; 
    }

    pResult = pStmt->pResultHead;

    if(pResult)
    {
        RS_DESC_HEADER &pIRDDescHeader = pStmt->pIRD->pDescHeader;
        RS_DESC_HEADER &pARDDescHeader = pStmt->pStmtAttr->pARD->pDescHeader;

        // Fetch array/single row
        long lRowsToFetch = (pARDDescHeader.lArraySize <= 0) ? 1 : pARDDescHeader.lArraySize;
        long lRowFetched = 0;
        int  iBlockCursor = (lRowsToFetch > 1);
        SQLLEN  iBindOffset = (pARDDescHeader.plBindOffsetPtr) ? *(pARDDescHeader.plBindOffsetPtr) : 0;

		// Reset previous hCol for SQLGetData
		pResult->iPrevhCol = 0;

        // Loop for block cursor
        for(lRowFetched = 0; lRowFetched < lRowsToFetch; lRowFetched++)
        {
            // Find new position of the cursor
            switch(hFetchOrientation)
            {
                case SQL_FETCH_NEXT:
                {
                    int iMaxRowsReached;

                    // Do we need to read from CSC?
                    if((pResult->iCurRow + 1) >= pResult->iNumberOfRowsInMem)
                    {
                        iMaxRowsReached = ((pStmt->pStmtAttr->iMaxRows > 0) 
                                                && ((pResult->iCurRow + pResult->iRowOffset + 1) >= pStmt->pStmtAttr->iMaxRows));

                        if(!iMaxRowsReached)
                        {
                            // Check for client side cursor
                            if(pgIsFileCreatedCsc(pResult->pgResult))
                            {
                                int iCscError = 0;
                                int gotRowsFromCsc = pgReadNextBatchOfRowsCsc(pResult->pgResult,&iCscError);

                                if(gotRowsFromCsc)
                                {
                                    pResult->iRowOffset += pResult->iNumberOfRowsInMem; // We are discarding some data.
                                    pResult->iCurRow = -1; // We increment below
                                    // New #of rows in memory
                                    pResult->iNumberOfRowsInMem = PQntuples(pResult->pgResult);
                                }

                                if(iCscError)
                                {
                                    rc = SQL_ERROR;
                                    addError(&pStmt->pErrorList,"24000", "An I/O error occurred while reading client side cursor.", 0, NULL);
                                    goto error; 
                                }
                            }
							else
							if(pStmt->pCscStatementContext 
								&& isStreamingCursorMode(pStmt)
								&& !(libpqIsEndOfStreamingCursor(pStmt))
							)
							{
								int iNumberOfRowsInMem = pResult->iNumberOfRowsInMem;
                                int iError = 0;

								// Read more rows from socket
								libpqReadNextBatchOfStreamingRows(pStmt, pStmt->pCscStatementContext, pResult->pgResult, pStmt->phdbc->pgConn,&iError, FALSE);


								// Set pResult parameters
                                // New #of rows in memory
                                pResult->iNumberOfRowsInMem = PQntuples(pResult->pgResult);

								if(pResult->iNumberOfRowsInMem > 0)
								{
                                    pResult->iRowOffset += iNumberOfRowsInMem; // We are discarding some data.
                                    pResult->iCurRow = -1; // We increment below
								}

                                if(iError)
                                {
                                    rc = SQL_ERROR;
                                    addError(&pStmt->pErrorList,"24000", "An I/O error occurred while reading streaming cursor.", 0, NULL);
                                    goto error; 
                                }
							}
                        } // !Max limit
                    } // !Last row in memory

                    // In memory cursor 
                    if((pResult->iCurRow >= -1) && (pResult->iCurRow <= (pResult->iNumberOfRowsInMem - 1)))
                    {
                        iMaxRowsReached = ((pStmt->pStmtAttr->iMaxRows > 0) 
                                                && ((pResult->iCurRow + pResult->iRowOffset + 1) >= pStmt->pStmtAttr->iMaxRows));

                        if(!iMaxRowsReached)
                        {
                            (pResult->iCurRow)++;

                            if(pResult->iCurRow >= pResult->iNumberOfRowsInMem)
                                rc = SQL_NO_DATA;
                        }
                        else
                            rc = SQL_NO_DATA;
                    }
                    else
                        rc = SQL_NO_DATA;

                    break;
                }

                case SQL_FETCH_PRIOR:
                {
                    // Do we need to read from CSC?
                    if((pResult->iCurRow -1) < 0)
                    {
                        // Check for client side cursor
                        if(pgIsFileCreatedCsc(pResult->pgResult))
                        {
                            int iCscError = 0;
                            int gotRowsFromCsc = pgReadPreviousBatchOfRowsCsc(pResult->pgResult,&iCscError);

                            if(gotRowsFromCsc)
                            {
                                // New #of rows in memory
                                pResult->iNumberOfRowsInMem = PQntuples(pResult->pgResult);
                                pResult->iRowOffset -= pResult->iNumberOfRowsInMem; // Point to zeroth row in current batch w.r.t. overall result.
                                pResult->iCurRow = pResult->iNumberOfRowsInMem; // We decrement below
                            }

                            if(iCscError)
                            {
                                rc = SQL_ERROR;
                                addError(&pStmt->pErrorList,"24000", "An I/O error occurred while reading client side cursor.", 0, NULL);
                                goto error; 
                            }
                        }
                    }

                    // In memory cursor
                    if((pResult->iCurRow >= 0) && (pResult->iCurRow <= pResult->iNumberOfRowsInMem))
                    {
                        (pResult->iCurRow)--;
                        if(pResult->iCurRow <= -1)
                            rc = SQL_NO_DATA;
                    }
                    else
                        rc = SQL_NO_DATA;

                    break;

                }

                case SQL_FETCH_FIRST:
                {
                    if(pResult->iNumberOfRowsInMem > 0)
                    {
                        rc = RS_RESULT_INFO::setFetchAtFirstRow(pStmt, pResult);
                        if(rc == SQL_ERROR)
                            goto error;

                        // In memory cursor
                        pResult->iCurRow = 0;

                        if(iBlockCursor)
                            hFetchOrientation = SQL_FETCH_NEXT;
                    }
                    else
                        rc = SQL_NO_DATA;

                    break;
                }

                case SQL_FETCH_LAST:
                {
                    if(pResult->iNumberOfRowsInMem > 0)
                    {
                        rc = RS_RESULT_INFO::setFetchAtLastRow(pStmt, pResult);
                        if(rc == SQL_ERROR)
                            goto error;

                        // In memory cursor
                        if((pStmt->pStmtAttr->iMaxRows > 0) && (pStmt->pStmtAttr->iMaxRows < (pResult->iRowOffset + pResult->iNumberOfRowsInMem)))
                        {
                            pResult->iCurRow = (pStmt->pStmtAttr->iMaxRows > pResult->iRowOffset) 
                                                    ? pStmt->pStmtAttr->iMaxRows - pResult->iRowOffset - 1
                                                    : 0; // This shouldn't happen.
                        }
                        else
                            pResult->iCurRow = pResult->iNumberOfRowsInMem - 1;

                        if(iBlockCursor)
                            hFetchOrientation = SQL_FETCH_NEXT;
                    }
                    else
                        rc = SQL_NO_DATA;

                    break;
                }

                case SQL_FETCH_ABSOLUTE:
                {
                    rc = RS_RESULT_INFO::setAbsolute(pStmt, pResult, iFetchOffset);
                    if(rc == SQL_ERROR)
                        goto error;

                    if(iBlockCursor && rc == SQL_SUCCESS)
                        hFetchOrientation = SQL_FETCH_NEXT;

                    break;
                }

                case SQL_FETCH_RELATIVE:
                {
                    if(iFetchOffset == 0)
                    {
                        // Valid but no change in cursor position
                        if(pResult->iNumberOfRowsInMem == 0)
                        {
                            rc = SQL_NO_DATA;
                        }
                        else
                        if(RS_RESULT_INFO::isBeforeFirstRow(pStmt, pResult) || RS_RESULT_INFO::isAfterLastRow(pStmt, pResult)) // before first or after last
                        {
                            rc = SQL_NO_DATA;
                        }
                        else
                        {
                            rc = SQL_SUCCESS;
                        }
                    }
                    else
                    {
                        //have to add 1 since absolute expects a 1-based index
                        SQLLEN index = pResult->iCurRow + 1 + iFetchOffset;

                        // Check for client side cursor
                        if(pgIsFileCreatedCsc(pResult->pgResult))
                        {
                            // Index should be from row_offset.
                            index += pResult->iRowOffset;
                        }
                        
                        if (index < 0)
                        {
                            // Absolute -ve and relative -ve are different. Here -ve means before first row.

                            // Set at first in CSC
                            rc = RS_RESULT_INFO::setFetchAtFirstRow(pStmt, pResult);
                            if(rc == SQL_ERROR)
                                goto error;

                            // Before first
                            pResult->iCurRow = -1;
                            rc = SQL_NO_DATA;
                        }
                        else
                        {
                            rc = RS_RESULT_INFO::setAbsolute(pStmt, pResult, index);
                        }
                    }

                    if(rc == SQL_ERROR)
                        goto error;

                    if(iBlockCursor && rc == SQL_SUCCESS)
                        hFetchOrientation = SQL_FETCH_NEXT;

                    break;
                }

                case SQL_FETCH_BOOKMARK:
                {
                    rc = SQL_ERROR;
                    addError(&pStmt->pErrorList,"HYC00", "Optional feature not implemented", 0, NULL);
                    goto error; 
                }

                default:
                {
                    rc = SQL_ERROR;
                    addError(&pStmt->pErrorList,"HY106", "Fetch type out of range", 0, NULL);
                    goto error; 
                }

            } // Switch

            if(rc == SQL_SUCCESS)
            {
                if(pStmt->pStmtAttr->iRetrieveData == SQL_RD_ON)
                {
                    RS_DESC_REC *pDescRec;
                    SQLRETURN rc1;

                    // Put data in bind buffers, if any
                    for(pDescRec = pStmt->pStmtAttr->pARD->pDescRecHead; pDescRec != NULL; pDescRec = pDescRec->pNext)
                    {
                        int iValOffset = 0;
                        SQLLEN *pcbLenInd = NULL;

                        if(iBlockCursor)
                        {
                            if(pARDDescHeader.lBindType == SQL_BIND_BY_COLUMN)
                            {
                                // Column wise binding
                                iValOffset = pDescRec->iOctetLen;

                                if(!iValOffset)
                                {
                                    rc = SQL_ERROR;
                                    addError(&pStmt->pErrorList,"HY000", "Array element length is zero.", 0, NULL);
                                    goto error;
                                }

                                pcbLenInd  = (pDescRec->pcbLenInd) ? pDescRec->pcbLenInd + lRowFetched  : NULL;
                            }
                            else
                            {
                                // Row wise binding
                                iValOffset = pARDDescHeader.lBindType;

                                if(pDescRec->plOctetLen == NULL)
                                {
                                    // Same structure should have length indicator
                                    pcbLenInd  = (pDescRec->pcbLenInd) ? (SQLLEN *)(((char *)pDescRec->pcbLenInd) + (iValOffset * lRowFetched))  : NULL;
                                }
                                else
                                {
                                    // Different array should have length indicator
                                    pcbLenInd = (pDescRec->pcbLenInd) ? pDescRec->pcbLenInd + lRowFetched  : NULL;
                                }
                            }
                        }
                        else
                            pcbLenInd = (pDescRec->pcbLenInd) ? pDescRec->pcbLenInd + lRowFetched  : NULL;
                        // Get data 
                        char* pValueCharPtr_ = (char *) (pDescRec->pValue ? pDescRec->pValue : NULL);
                        pValueCharPtr_ += (lRowFetched * iValOffset) + iBindOffset;
                        pcbLenInd = (SQLLEN *)(pcbLenInd ? ((char *)pcbLenInd + iBindOffset) : NULL);
                        rc1 = RS_STMT_INFO::RS_SQLGetData(pStmt, pDescRec->hRecNumber, pDescRec->hType,
                                            (SQLPOINTER) pValueCharPtr_,
                                            pDescRec->cbLen, 
                                            pcbLenInd,
											TRUE);

                        pStmt->pResultHead->getColumnReadOffset(pDescRec->hRecNumber) = 0;
                        //TODO: Check for SQL_SUCCESS_WITH_INFO
                        if(rc1 == SQL_ERROR)
                        {
                            rc = SQL_ERROR;
                            break;
                        }
                    } // Column loop

                    // Put the fetch count
                    if(pIRDDescHeader.valid)
                    {
                        // Row count
                        if(pIRDDescHeader.plRowsProcessedPtr)
                            *(pIRDDescHeader.plRowsProcessedPtr) = lRowFetched + 1;

                        // Row status
                        if(pIRDDescHeader.phArrayStatusPtr)
                        {
                            short hRowStatus;

                            if(rc == SQL_SUCCESS)
                                hRowStatus = SQL_ROW_SUCCESS;
                            else
                            if(rc == SQL_SUCCESS_WITH_INFO)
                                hRowStatus = SQL_ROW_SUCCESS_WITH_INFO;
                            else
                            if(rc == SQL_ERROR)
                                hRowStatus = SQL_ROW_ERROR;
                            else
                                hRowStatus = SQL_ROW_ERROR;

                            *(pIRDDescHeader.phArrayStatusPtr + lRowFetched) = hRowStatus;
                        }
                    } 
                } // SQL_RD_ON
            } // Success
            else
            {
                if(rc == SQL_NO_DATA)
                {
                    // Put the row status of last row as SQL_ROW_NOROW
                    if(pStmt->pStmtAttr->iRetrieveData == SQL_RD_ON)
                    {
                        if (pIRDDescHeader.valid &&
                            pIRDDescHeader.phArrayStatusPtr) {
                            *(pIRDDescHeader.phArrayStatusPtr + lRowFetched) =
                                SQL_ROW_NOROW;
                        }
                    } // SQL_RD_ON
                }

                break; // SQL_NO_DATA
            }
        } // Rows loop

        // Check for last partial set of rows
        if(rc == SQL_NO_DATA)
        {
            if(lRowFetched > 0)
                rc = SQL_SUCCESS;
        } 
    }
    else
    {
        rc = SQL_ERROR;
        addError(&pStmt->pErrorList,"24000", "Invalid cursor state", 0, NULL);
        goto error; 
    }

error:

    return rc;
}