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