Annotation of sql/odbc/MFCpatches/dbcore.cpp, revision 1.1

1.1     ! paf         1: #include "stdafx.h"
        !             2: 
        !             3: #ifndef _WIN64
        !             4: 
        !             5: #ifdef AFX_DB_SEG
        !             6: #pragma code_seg(AFX_DB_SEG)
        !             7: #endif
        !             8: 
        !             9: 
        !            10: #define new DEBUG_NEW
        !            11: 
        !            12: /////////////////////////////////////////////////////////////////////////////
        !            13: // Global data
        !            14: 
        !            15: #ifdef _DEBUG
        !            16: BOOL bTraceSql = FALSE;
        !            17: #endif
        !            18: 
        !            19: AFX_STATIC_DATA const TCHAR _afxODBCTrail[] = _T("ODBC;");
        !            20: AFX_STATIC_DATA const TCHAR _afxComma[] = _T(",");
        !            21: AFX_STATIC_DATA const TCHAR _afxLiteralSeparator = '\'';
        !            22: AFX_STATIC_DATA const TCHAR _afxCall[] = _T("{CALL ");
        !            23: AFX_STATIC_DATA const TCHAR _afxParamCall[] = _T("{?");
        !            24: AFX_STATIC_DATA const TCHAR _afxSelect[] = _T("SELECT ");
        !            25: AFX_STATIC_DATA const TCHAR _afxFrom[] = _T(" FROM ");
        !            26: AFX_STATIC_DATA const TCHAR _afxWhere[] = _T(" WHERE ");
        !            27: AFX_STATIC_DATA const TCHAR _afxOrderBy[] = _T(" ORDER BY ");
        !            28: AFX_STATIC_DATA const TCHAR _afxForUpdate[] = _T(" FOR UPDATE ");
        !            29: 
        !            30: AFX_STATIC_DATA const TCHAR _afxRowFetch[] = _T("State:01S01");
        !            31: AFX_STATIC_DATA const TCHAR _afxDataTruncated[] = _T("State:01004");
        !            32: AFX_STATIC_DATA const TCHAR _afxInfoRange[] = _T("State:S1096");
        !            33: AFX_STATIC_DATA const TCHAR _afxOutOfSequence[] = _T("State:S1010");
        !            34: AFX_STATIC_DATA const TCHAR _afxDriverNotCapable[] = _T("State:S1C00");
        !            35: 
        !            36: AFX_STATIC_DATA const char _afxODBCDLL[] = "ODBC32.DLL";
        !            37: 
        !            38: /////////////////////////////////////////////////////////////////////////////
        !            39: // for dynamic load of ODBC32.DLL
        !            40: 
        !            41: #pragma comment(lib, "odbc32.lib")
        !            42: 
        !            43: /////////////////////////////////////////////////////////////////////////////
        !            44: // CDBException
        !            45: 
        !            46: void AFXAPI AfxThrowDBException(RETCODE nRetCode, CDatabase* pdb, HSTMT hstmt)
        !            47: {
        !            48:        CDBException* pException = new CDBException(nRetCode);
        !            49:        if (nRetCode == SQL_ERROR && pdb != NULL)
        !            50:                pException->BuildErrorString(pdb, hstmt);
        !            51:        else if (nRetCode > AFX_SQL_ERROR && nRetCode < AFX_SQL_ERROR_MAX)
        !            52:        {
        !            53:                VERIFY(pException->m_strError.LoadString(
        !            54:                        AFX_IDP_SQL_FIRST+(nRetCode-AFX_SQL_ERROR)));
        !            55:                TRACE(traceDatabase, 0, _T("%s\n"), pException->m_strError);
        !            56:        }
        !            57:        THROW(pException);
        !            58: }
        !            59: 
        !            60: CDBException::CDBException(RETCODE nRetCode)
        !            61: {
        !            62:        m_nRetCode = nRetCode;
        !            63: }
        !            64: 
        !            65: CDBException::~CDBException()
        !            66: {
        !            67: }
        !            68: 
        !            69: void CDBException::BuildErrorString(CDatabase* pdb, HSTMT hstmt, BOOL bTrace)
        !            70: {
        !            71:        ASSERT_VALID(this);
        !            72:        UNUSED(bTrace);  // unused in release builds
        !            73: 
        !            74:        if (pdb != NULL)
        !            75:        {
        !            76:                SWORD nOutlen;
        !            77:                RETCODE nRetCode;
        !            78:                TCHAR lpszMsg[SQL_MAX_MESSAGE_LENGTH];
        !            79:                TCHAR lpszState[SQL_SQLSTATE_SIZE+1];
        !            80:                CString strMsg;
        !            81:                CString strState;
        !            82:                SDWORD lNative;
        !            83: 
        !            84:                _AFX_DB_STATE* pDbState = _afxDbState;
        !            85:                AFX_SQL_SYNC(::SQLError(pDbState->m_henvAllConnections, pdb->m_hdbc,
        !            86:                        hstmt, reinterpret_cast<SQLTCHAR *>(lpszState), &lNative,
        !            87:                        reinterpret_cast<SQLTCHAR *>(lpszMsg), SQL_MAX_MESSAGE_LENGTH-1, &nOutlen));
        !            88:                strState = lpszState;
        !            89: 
        !            90:                // Skip non-errors
        !            91:                while ((nRetCode == SQL_SUCCESS || nRetCode == SQL_SUCCESS_WITH_INFO) &&
        !            92:                        lstrcmp(strState, _T("00000")) != 0)
        !            93:                {
        !            94:                        strMsg = lpszMsg;
        !            95: 
        !            96:                        TCHAR lpszNative[50];
        !            97:                        wsprintf(lpszNative, _T(",Native:%ld,Origin:"), lNative);
        !            98:                        strState += lpszNative;
        !            99: 
        !           100:                        // transfer [origin] from message string to StateNativeOrigin string
        !           101:                        int nCloseBracket;
        !           102:                        int nMsgLength;
        !           103:                        while (!strMsg.IsEmpty() &&
        !           104:                                strMsg[0] == '[' && (nCloseBracket = strMsg.Find(']')) >= 0)
        !           105:                        {
        !           106:                                // Skip ']'
        !           107:                                nCloseBracket++;
        !           108:                                strState += strMsg.Left(nCloseBracket);
        !           109: 
        !           110:                                nMsgLength = strMsg.GetLength();
        !           111:                                // Skip ' ', if present
        !           112:                                if (nCloseBracket < nMsgLength && strMsg[nCloseBracket] == ' ')
        !           113:                                        nCloseBracket++;
        !           114:                                strMsg = strMsg.Right(nMsgLength - nCloseBracket);
        !           115:                        }
        !           116:                        strState += _T("\n");
        !           117:                        m_strStateNativeOrigin += _T("State:") + strState;
        !           118:                        m_strError += strMsg + _T("\n");
        !           119: 
        !           120: #ifdef _DEBUG
        !           121:                        if (bTrace)
        !           122:                        {
        !           123:                                TraceErrorMessage(strMsg);
        !           124:                                TraceErrorMessage(_T("State:") + strState);
        !           125:                        }
        !           126: #endif // _DEBUG
        !           127: 
        !           128:                        AFX_SQL_SYNC(::SQLError(pDbState->m_henvAllConnections,
        !           129:                                pdb->m_hdbc, hstmt, reinterpret_cast<SQLTCHAR *>(lpszState), &lNative,
        !           130:                                reinterpret_cast<SQLTCHAR *>(lpszMsg), SQL_MAX_MESSAGE_LENGTH-1, &nOutlen));
        !           131:                        strState = lpszState;
        !           132:                }
        !           133:        }
        !           134: }
        !           135: 
        !           136: 
        !           137: BOOL CDBException::GetErrorMessage(LPTSTR lpszError, UINT nMaxError,
        !           138:                PUINT pnHelpContext /* = NULL */)
        !           139: {
        !           140:        ASSERT(lpszError != NULL && AfxIsValidString(lpszError, nMaxError));
        !           141: 
        !           142:        if (pnHelpContext != NULL)
        !           143:                *pnHelpContext = 0;
        !           144: 
        !           145:        lstrcpyn(lpszError, m_strError, nMaxError-1);
        !           146:        lpszError[nMaxError-1] = '\0';
        !           147:        return TRUE;
        !           148: }
        !           149: 
        !           150: 
        !           151: #ifdef _DEBUG
        !           152: void CDBException::TraceErrorMessage(LPCTSTR szTrace) const
        !           153: {
        !           154:        CString strTrace = szTrace;
        !           155: 
        !           156:        if (strTrace.GetLength() <= 80)
        !           157:                TRACE(traceDatabase, 0, _T("%s\n"), strTrace);
        !           158:        else
        !           159:        {
        !           160:                // Display 80 chars/line
        !           161:                while (strTrace.GetLength() > 80)
        !           162:                {
        !           163:                        TRACE(traceDatabase, 0, _T("%s\n"), strTrace.Left(80));
        !           164:                        strTrace = strTrace.Right(strTrace.GetLength() - 80);
        !           165:                }
        !           166:                TRACE(traceDatabase, 0, _T("%s\n"), strTrace);
        !           167:        }
        !           168: }
        !           169: #endif // _DEBUG
        !           170: 
        !           171: void CDBException::Empty()
        !           172: {
        !           173:        m_strError.Empty();
        !           174:        m_strStateNativeOrigin.Empty();
        !           175: }
        !           176: 
        !           177: /////////////////////////////////////////////////////////////////////////////
        !           178: // Global helper
        !           179: 
        !           180: HENV AFXAPI AfxGetHENV()
        !           181: {
        !           182:        _AFX_DB_STATE* pDbState = _afxDbState;
        !           183:        return pDbState->m_henvAllConnections;
        !           184: }
        !           185: 
        !           186: /////////////////////////////////////////////////////////////////////////////
        !           187: // CDatabase implementation
        !           188: 
        !           189: CDatabase::CDatabase()
        !           190: {
        !           191:        m_hdbc = SQL_NULL_HDBC;
        !           192:        m_hstmt = SQL_NULL_HSTMT;
        !           193: 
        !           194:        m_bUpdatable = FALSE;
        !           195:        m_bTransactions = FALSE;
        !           196:        DEBUG_ONLY(m_bTransactionPending = FALSE);
        !           197:        m_dwLoginTimeout = DEFAULT_LOGIN_TIMEOUT;
        !           198:        m_dwQueryTimeout = DEFAULT_QUERY_TIMEOUT;
        !           199: 
        !           200:        m_bStripTrailingSpaces = FALSE;
        !           201:        m_bIncRecordCountOnAdd = FALSE;
        !           202:        m_bAddForUpdate = FALSE;
        !           203: }
        !           204: 
        !           205: CDatabase::~CDatabase()
        !           206: {
        !           207:        ASSERT_VALID(this);
        !           208: 
        !           209:        Free();
        !           210: }
        !           211: 
        !           212: BOOL CDatabase::Open(LPCTSTR lpszDSN, BOOL bExclusive,
        !           213:        BOOL bReadonly, LPCTSTR lpszConnect, BOOL bUseCursorLib)
        !           214: {
        !           215:        ASSERT(lpszDSN == NULL || AfxIsValidString(lpszDSN));
        !           216:        ASSERT(lpszConnect == NULL || AfxIsValidString(lpszConnect));
        !           217: 
        !           218:        CString strConnect;
        !           219: 
        !           220:        if (lpszConnect != NULL)
        !           221:                strConnect = lpszConnect;
        !           222: 
        !           223:        // if there is a "ODBC;" (or "odbc;") prefix in the connect string...
        !           224:        if (_tcsnicmp(strConnect, _afxODBCTrail, lstrlen(_afxODBCTrail)) == 0)
        !           225:        {
        !           226:                // Strip "ODBC;"
        !           227:                strConnect = strConnect.Right(strConnect.GetLength()
        !           228:                        - lstrlen(_afxODBCTrail));
        !           229:        }
        !           230: 
        !           231:        if (lpszDSN != NULL && lstrlen(lpszDSN) != 0)
        !           232:        {
        !           233:                // Append "DSN=" lpszDSN
        !           234:                strConnect += _T(";DSN=");
        !           235:                strConnect += lpszDSN;
        !           236:        }
        !           237: 
        !           238:        DWORD dwOptions = 0;
        !           239: 
        !           240:        if (bExclusive)
        !           241:                dwOptions |= openExclusive;
        !           242: 
        !           243:        if (bReadonly)
        !           244:                dwOptions |= openReadOnly;
        !           245: 
        !           246:        if (bUseCursorLib)
        !           247:                dwOptions |= useCursorLib;
        !           248: 
        !           249:        return OpenEx(strConnect, dwOptions);
        !           250: }
        !           251: 
        !           252: BOOL CDatabase::OpenEx(LPCTSTR lpszConnectString, DWORD dwOptions)
        !           253: {
        !           254:        ASSERT_VALID(this);
        !           255:        ASSERT(lpszConnectString == NULL || AfxIsValidString(lpszConnectString));
        !           256:        ASSERT(!(dwOptions & noOdbcDialog && dwOptions & forceOdbcDialog));
        !           257: 
        !           258:        // Exclusive access not supported.
        !           259:        ASSERT(!(dwOptions & openExclusive));
        !           260: 
        !           261:        m_bUpdatable = !(dwOptions & openReadOnly);
        !           262: 
        !           263:        TRY
        !           264:        {
        !           265:                m_strConnect = lpszConnectString;
        !           266: 
        !           267:                // Allocate the HDBC and make connection
        !           268:                AllocConnect(dwOptions);
        !           269:                if(!Connect(dwOptions))
        !           270:                        return FALSE;
        !           271: 
        !           272:                // Verify support for required functionality and cache info
        !           273:                VerifyConnect();
        !           274:                GetConnectInfo();
        !           275:        }
        !           276:        CATCH_ALL(e)
        !           277:        {
        !           278:                Free();
        !           279:                THROW_LAST();
        !           280:        }
        !           281:        END_CATCH_ALL
        !           282: 
        !           283:        return TRUE;
        !           284: }
        !           285: 
        !           286: void CDatabase::ExecuteSQL(LPCTSTR lpszSQL)
        !           287: {
        !           288:        RETCODE nRetCode;
        !           289:        HSTMT hstmt;
        !           290: 
        !           291:        ASSERT_VALID(this);
        !           292:        ASSERT(AfxIsValidString(lpszSQL));
        !           293: 
        !           294:        AFX_SQL_SYNC(::SQLAllocStmt(m_hdbc, &hstmt));
        !           295:        if (!CheckHstmt(nRetCode, hstmt))
        !           296:                AfxThrowDBException(nRetCode, this, hstmt);
        !           297: 
        !           298:        TRY
        !           299:        {
        !           300:                OnSetOptions(hstmt);
        !           301: 
        !           302:                // Give derived CDatabase classes option to use parameters
        !           303:                BindParameters(hstmt);
        !           304: 
        !           305:                LPTSTR pszSQL = const_cast<LPTSTR>(lpszSQL);
        !           306:                AFX_ODBC_CALL(::SQLExecDirect(hstmt, reinterpret_cast<SQLTCHAR *>(pszSQL), SQL_NTS));
        !           307:                if (!CheckHstmt(nRetCode, hstmt))
        !           308:                        AfxThrowDBException(nRetCode, this, hstmt);
        !           309: 
        !           310:                SWORD nResultColumns;
        !           311:                do
        !           312:                {
        !           313:                        AFX_ODBC_CALL(::SQLNumResultCols(hstmt, &nResultColumns));
        !           314:                        if(!CheckHstmt(nRetCode, hstmt))
        !           315:                                AfxThrowDBException(nRetCode, this, hstmt);
        !           316: 
        !           317:                        if (nResultColumns != 0)
        !           318:                                do
        !           319:                                {
        !           320:                                        AFX_ODBC_CALL(::SQLFetch(hstmt));
        !           321:                                        if(!CheckHstmt(nRetCode, hstmt))
        !           322:                                                AfxThrowDBException(nRetCode, this, hstmt);
        !           323:                                } while (nRetCode != SQL_NO_DATA_FOUND);
        !           324:                        AFX_ODBC_CALL(::SQLMoreResults(hstmt));
        !           325:                        if(!CheckHstmt(nRetCode, hstmt))
        !           326:                                AfxThrowDBException(nRetCode, this, hstmt);
        !           327:                } while (nRetCode != SQL_NO_DATA_FOUND);
        !           328:        }
        !           329:        CATCH_ALL(e)
        !           330:        {
        !           331:                ::SQLCancel(hstmt);
        !           332:                AFX_SQL_SYNC(::SQLFreeStmt(hstmt, SQL_DROP));
        !           333:                THROW_LAST();
        !           334:        }
        !           335:        END_CATCH_ALL
        !           336: 
        !           337:        AFX_SQL_SYNC(::SQLFreeStmt(hstmt, SQL_DROP));
        !           338: }
        !           339: 
        !           340: // Shutdown pending query for CDatabase's private m_hstmt
        !           341: void CDatabase::Cancel()
        !           342: {
        !           343:        ASSERT_VALID(this);
        !           344:        ASSERT(m_hdbc != SQL_NULL_HDBC);
        !           345: 
        !           346:        ::SQLCancel(m_hstmt);
        !           347: }
        !           348: 
        !           349: // Disconnect connection
        !           350: void CDatabase::Close()
        !           351: {
        !           352:        ASSERT_VALID(this);
        !           353: 
        !           354:        // Close any open recordsets
        !           355:        AfxLockGlobals(CRIT_ODBC);
        !           356:        TRY
        !           357:        {
        !           358:                while (!m_listRecordsets.IsEmpty())
        !           359:                {
        !           360:                        CRecordset* pSet = (CRecordset*)m_listRecordsets.GetHead();
        !           361:                        pSet->Close();  // will implicitly remove from list
        !           362:                        pSet->m_pDatabase = NULL;
        !           363:                }
        !           364:        }
        !           365:        CATCH_ALL(e)
        !           366:        {
        !           367:                AfxUnlockGlobals(CRIT_ODBC);
        !           368:                THROW_LAST();
        !           369:        }
        !           370:        END_CATCH_ALL
        !           371:        AfxUnlockGlobals(CRIT_ODBC);
        !           372: 
        !           373:        if (m_hdbc != SQL_NULL_HDBC)
        !           374:        {
        !           375:                RETCODE nRetCode;
        !           376:                AFX_SQL_SYNC(::SQLDisconnect(m_hdbc));
        !           377:                AFX_SQL_SYNC(::SQLFreeConnect(m_hdbc));
        !           378:                m_hdbc = SQL_NULL_HDBC;
        !           379: 
        !           380:                _AFX_DB_STATE* pDbState = _afxDbState;
        !           381: 
        !           382:                AfxLockGlobals(CRIT_ODBC);
        !           383:                ASSERT(pDbState->m_nAllocatedConnections != 0);
        !           384:                pDbState->m_nAllocatedConnections--;
        !           385:                AfxUnlockGlobals(CRIT_ODBC);
        !           386:        }
        !           387: }
        !           388: 
        !           389: // Silently disconnect and free all ODBC resources.  Don't throw any exceptions
        !           390: void CDatabase::Free()
        !           391: {
        !           392:        ASSERT_VALID(this);
        !           393: 
        !           394:        // Trap failures upon close
        !           395:        TRY
        !           396:        {
        !           397:                Close();
        !           398:        }
        !           399:        CATCH_ALL(e)
        !           400:        {
        !           401:                // Nothing we can do
        !           402:                TRACE(traceDatabase, 0, _T("Error: exception by CDatabase::Close() ignored in CDatabase::Free().\n"));
        !           403:                DELETE_EXCEPTION(e);
        !           404:        }
        !           405:        END_CATCH_ALL
        !           406: 
        !           407:        // free henv if refcount goes to 0
        !           408:        _AFX_DB_STATE* pDbState = _afxDbState;
        !           409:        AfxLockGlobals(CRIT_ODBC);
        !           410:        if (pDbState->m_henvAllConnections != SQL_NULL_HENV)
        !           411:        {
        !           412:                ASSERT(pDbState->m_nAllocatedConnections >= 0);
        !           413:                if (pDbState->m_nAllocatedConnections == 0)
        !           414:                {
        !           415:                        // free last connection - release HENV
        !           416:                        RETCODE nRetCodeEnv = ::SQLFreeEnv(pDbState->m_henvAllConnections);
        !           417: #ifdef _DEBUG
        !           418:                        if (nRetCodeEnv != SQL_SUCCESS)
        !           419:                                // Nothing we can do
        !           420:                                TRACE(traceDatabase, 0, _T("Error: SQLFreeEnv failure ignored in CDatabase::Free().\n"));
        !           421: #endif
        !           422:                        pDbState->m_henvAllConnections = SQL_NULL_HENV;
        !           423:                }
        !           424:        }
        !           425:        AfxUnlockGlobals(CRIT_ODBC);
        !           426: }
        !           427: 
        !           428: void CDatabase::OnSetOptions(HSTMT hstmt)
        !           429: {
        !           430:        RETCODE nRetCode;
        !           431:        ASSERT_VALID(this);
        !           432:        ASSERT(m_hdbc != SQL_NULL_HDBC);
        !           433: 
        !           434:        if (m_dwQueryTimeout != -1)
        !           435:        {
        !           436:                // Attempt to set query timeout.  Ignore failure
        !           437:                AFX_SQL_SYNC(::SQLSetStmtOption(hstmt, SQL_QUERY_TIMEOUT,
        !           438:                        m_dwQueryTimeout));
        !           439:                if (!Check(nRetCode))
        !           440:                        // don't attempt it again
        !           441:                        m_dwQueryTimeout = (DWORD)-1;
        !           442:        }
        !           443: }
        !           444: 
        !           445: CString CDatabase::GetDatabaseName() const
        !           446: {
        !           447:        ASSERT_VALID(this);
        !           448:        ASSERT(m_hdbc != SQL_NULL_HDBC);
        !           449: 
        !           450:        CString str;
        !           451: 
        !           452:        SWORD nResult;
        !           453:        RETCODE nRetCode;
        !           454: 
        !           455:        AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_DATABASE_NAME, NULL, 0, &nResult));
        !           456:        if (Check(nRetCode))
        !           457:        {
        !           458:                LPTSTR pszName =
        !           459:                        str.GetBufferSetLength(nResult / sizeof(CString::XCHAR));
        !           460: 
        !           461:                AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_DATABASE_NAME, pszName,
        !           462:                        SWORD(nResult + sizeof(CString::XCHAR)), &nResult));
        !           463: 
        !           464:                str.ReleaseBuffer();
        !           465: 
        !           466:                if(!Check(nRetCode))
        !           467:                        str.Empty();
        !           468:        }
        !           469:        return str;
        !           470: }
        !           471: 
        !           472: BOOL CDatabase::BeginTrans()
        !           473: {
        !           474:        ASSERT_VALID(this);
        !           475:        ASSERT(m_hdbc != SQL_NULL_HDBC);
        !           476: 
        !           477:        if (!m_bTransactions)
        !           478:                return FALSE;
        !           479: 
        !           480:        // Only 1 level of transactions supported
        !           481: #ifdef _DEBUG
        !           482:        ASSERT(!m_bTransactionPending);
        !           483: #endif
        !           484: 
        !           485:        RETCODE nRetCode;
        !           486:        AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc, SQL_AUTOCOMMIT,
        !           487:                SQL_AUTOCOMMIT_OFF));
        !           488:        DEBUG_ONLY(m_bTransactionPending = TRUE);
        !           489: 
        !           490:        return Check(nRetCode);
        !           491: }
        !           492: 
        !           493: BOOL CDatabase::CommitTrans()
        !           494: {
        !           495:        ASSERT_VALID(this);
        !           496:        ASSERT(m_hdbc != SQL_NULL_HDBC);
        !           497: 
        !           498:        if (!m_bTransactions)
        !           499:                return FALSE;
        !           500: 
        !           501:        // BeginTrans must be called first
        !           502: #ifdef _DEBUG
        !           503:        ASSERT(m_bTransactionPending);
        !           504: #endif
        !           505: 
        !           506:        _AFX_DB_STATE* pDbState = _afxDbState;
        !           507:        RETCODE nRetCode;
        !           508:        AFX_SQL_SYNC(::SQLTransact(pDbState->m_henvAllConnections, m_hdbc, SQL_COMMIT));
        !           509:        BOOL bSuccess = Check(nRetCode);
        !           510: 
        !           511:        // Turn back on auto commit
        !           512:        AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc, SQL_AUTOCOMMIT,
        !           513:                SQL_AUTOCOMMIT_ON));
        !           514:        DEBUG_ONLY(m_bTransactionPending = FALSE);
        !           515: 
        !           516:        return bSuccess;
        !           517: }
        !           518: 
        !           519: BOOL CDatabase::Rollback()
        !           520: {
        !           521:        ASSERT_VALID(this);
        !           522:        ASSERT(m_hdbc != SQL_NULL_HDBC);
        !           523: 
        !           524:        if (!m_bTransactions)
        !           525:                return FALSE;
        !           526: 
        !           527:        // BeginTrans must be called first
        !           528: #ifdef _DEBUG
        !           529:        ASSERT(m_bTransactionPending);
        !           530: #endif
        !           531: 
        !           532:        _AFX_DB_STATE* pDbState = _afxDbState;
        !           533:        RETCODE nRetCode;
        !           534:        AFX_SQL_SYNC(::SQLTransact(pDbState->m_henvAllConnections, m_hdbc, SQL_ROLLBACK));
        !           535:        BOOL bSuccess = Check(nRetCode);
        !           536: 
        !           537:        // Turn back on auto commit
        !           538:        AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc, SQL_AUTOCOMMIT,
        !           539:                SQL_AUTOCOMMIT_ON));
        !           540:        DEBUG_ONLY(m_bTransactionPending = FALSE);
        !           541: 
        !           542:        return bSuccess;
        !           543: }
        !           544: 
        !           545: // Screen for errors.
        !           546: BOOL CDatabase::Check(RETCODE nRetCode) const
        !           547: {
        !           548:        return CheckHstmt(nRetCode, SQL_NULL_HSTMT);
        !           549: }
        !           550: 
        !           551: BOOL CDatabase::CheckHstmt(RETCODE nRetCode, HSTMT hstmt) const
        !           552: {
        !           553:        ASSERT_VALID(this);
        !           554:        UNUSED(hstmt);
        !           555: 
        !           556:        switch (nRetCode)
        !           557:        {
        !           558:        case SQL_SUCCESS_WITH_INFO:
        !           559: #ifdef _DEBUG
        !           560:        {
        !           561:                CDBException e(nRetCode);
        !           562:                TRACE(traceDatabase, 0, _T("Warning: ODBC Success With Info, "));
        !           563:                e.BuildErrorString((CDatabase*)this, hstmt);
        !           564:        }
        !           565: #endif // _DEBUG
        !           566: 
        !           567:                // Fall through
        !           568: 
        !           569:        case SQL_SUCCESS:
        !           570:        case SQL_NO_DATA_FOUND:
        !           571:                return TRUE;
        !           572:        }
        !           573: 
        !           574:        return FALSE;
        !           575: }
        !           576: 
        !           577: //////////////////////////////////////////////////////////////////////////////
        !           578: // CDatabase internal functions
        !           579: 
        !           580: //Replace brackets in SQL string with SQL_IDENTIFIER_QUOTE_CHAR
        !           581: void CDatabase::ReplaceBrackets(LPTSTR lpchSQL)
        !           582: {
        !           583:        BOOL bInLiteral = FALSE;
        !           584:        LPTSTR lpchNewSQL = lpchSQL;
        !           585: 
        !           586:        while (*lpchSQL)
        !           587:        {
        !           588:                if (*lpchSQL == _afxLiteralSeparator)
        !           589:                        {
        !           590:                                // Handle escaped literal
        !           591:                                if (*_tcsinc(lpchSQL) == _afxLiteralSeparator)
        !           592:                                {
        !           593:                                        *lpchNewSQL = *lpchSQL;
        !           594:                                        lpchSQL = _tcsinc(lpchSQL);
        !           595:                                        lpchNewSQL = _tcsinc(lpchNewSQL);
        !           596:                                }
        !           597:                                else
        !           598:                                        bInLiteral = !bInLiteral;
        !           599: 
        !           600:                                *lpchNewSQL = *lpchSQL;
        !           601:                        }
        !           602:                else if (*lpchSQL == _T('[') && !bInLiteral)
        !           603:                {
        !           604:                        if (*_tcsinc(lpchSQL) == _T('['))
        !           605:                        {
        !           606:                                // Handle escaped left bracket by inserting one '['
        !           607:                                *lpchNewSQL = *lpchSQL;
        !           608:                                lpchSQL = _tcsinc(lpchSQL);
        !           609:                        }
        !           610:                        else
        !           611:                                *lpchNewSQL = m_chIDQuoteChar;
        !           612:                }
        !           613:                else if (*lpchSQL == _T(']') && !bInLiteral)
        !           614:                {
        !           615:                        if (*_tcsinc(lpchSQL) == _T(']'))
        !           616:                        {
        !           617:                                // Handle escaped right bracket by inserting one ']'
        !           618:                                *lpchNewSQL = *lpchSQL;
        !           619:                                lpchSQL = _tcsinc(lpchSQL);
        !           620:                        }
        !           621:                        else
        !           622:                                *lpchNewSQL = m_chIDQuoteChar;
        !           623:                }
        !           624:                else
        !           625:                        *lpchNewSQL = *lpchSQL;
        !           626: 
        !           627:                lpchSQL = _tcsinc(lpchSQL);
        !           628:                lpchNewSQL = _tcsinc(lpchNewSQL);
        !           629:        }
        !           630:        *lpchNewSQL = _T('\0');
        !           631: }
        !           632: 
        !           633: // Allocate an henv (first time called) and hdbc
        !           634: void CDatabase::AllocConnect(DWORD dwOptions)
        !           635: {
        !           636:        ASSERT_VALID(this);
        !           637: 
        !           638:        if (m_hdbc != SQL_NULL_HDBC)
        !           639:                return;
        !           640: 
        !           641:        _AFX_DB_STATE* pDbState = _afxDbState;
        !           642: 
        !           643:        RETCODE nRetCode;
        !           644: 
        !           645:        AfxLockGlobals(CRIT_ODBC);
        !           646:        if (pDbState->m_henvAllConnections == SQL_NULL_HENV)
        !           647:        {
        !           648:                ASSERT(pDbState->m_nAllocatedConnections == 0);
        !           649: 
        !           650:                // need to allocate an environment for first connection
        !           651:                AFX_SQL_SYNC(::SQLAllocEnv(&pDbState->m_henvAllConnections));
        !           652:                if (!Check(nRetCode))
        !           653:                {
        !           654:                        AfxUnlockGlobals(CRIT_ODBC);
        !           655:                        AfxThrowMemoryException();  // fatal
        !           656:                }
        !           657:        }
        !           658: 
        !           659:        ASSERT(pDbState->m_henvAllConnections != SQL_NULL_HENV);
        !           660:        AFX_SQL_SYNC(::SQLAllocConnect(pDbState->m_henvAllConnections, &m_hdbc));
        !           661:        if (!Check(nRetCode))
        !           662:        {
        !           663:                AfxUnlockGlobals(CRIT_ODBC);
        !           664:                ThrowDBException(nRetCode); // fatal
        !           665:        }
        !           666:        pDbState->m_nAllocatedConnections++;    // allocated at least
        !           667:        AfxUnlockGlobals(CRIT_ODBC);
        !           668: 
        !           669: #ifdef _DEBUG
        !           670:        if (bTraceSql)
        !           671:        {
        !           672:                ::SQLSetConnectOption(m_hdbc, SQL_OPT_TRACEFILE,
        !           673:                        (SQLULEN)(DWORD_PTR)"odbccall.txt");
        !           674:                ::SQLSetConnectOption(m_hdbc, SQL_OPT_TRACE, 1);
        !           675:        }
        !           676: #endif // _DEBUG
        !           677: 
        !           678:        AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc, SQL_LOGIN_TIMEOUT,
        !           679:                m_dwLoginTimeout));
        !           680: #ifdef _DEBUG
        !           681:        if (nRetCode != SQL_SUCCESS && nRetCode != SQL_SUCCESS_WITH_INFO)
        !           682:                TRACE(traceDatabase, 0, _T("Warning: Failure setting login timeout.\n"));
        !           683: #endif
        !           684: 
        !           685:        if (!m_bUpdatable)
        !           686:        {
        !           687:                AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc, SQL_ACCESS_MODE,
        !           688:                        SQL_MODE_READ_ONLY));
        !           689: #ifdef _DEBUG
        !           690:                if (nRetCode != SQL_SUCCESS && nRetCode != SQL_SUCCESS_WITH_INFO)
        !           691:                        TRACE(traceDatabase, 0, _T("Warning: Failure setting read only access mode.\n"));
        !           692: #endif
        !           693:        }
        !           694: 
        !           695:        // Turn on cursor lib support
        !           696:        if (dwOptions & useCursorLib)
        !           697:        {
        !           698:                AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc,
        !           699:                        SQL_ODBC_CURSORS, SQL_CUR_USE_ODBC));
        !           700:                // With cursor library added records immediately in result set
        !           701:                m_bIncRecordCountOnAdd = TRUE;
        !           702:        }
        !           703: }
        !           704: 
        !           705: BOOL CDatabase::Connect(DWORD dwOptions)
        !           706: {
        !           707:        HWND hWndTop;
        !           708:        HWND hWnd = CWnd::GetSafeOwner_(NULL, &hWndTop);
        !           709:        if (hWnd == NULL)
        !           710:                hWnd = ::GetDesktopWindow();
        !           711: 
        !           712:        TCHAR szConnectOutput[MAX_CONNECT_LEN];
        !           713:        TCHAR *pszConnectInput = const_cast<LPTSTR>(static_cast<LPCTSTR>(m_strConnect));
        !           714:        RETCODE nRetCode;
        !           715:        SWORD nResult;
        !           716:        UWORD wConnectOption = SQL_DRIVER_COMPLETE;
        !           717: 
        !           718:        if (dwOptions & noOdbcDialog)
        !           719:                wConnectOption = SQL_DRIVER_NOPROMPT;
        !           720:        else if (dwOptions & forceOdbcDialog)
        !           721:                wConnectOption = SQL_DRIVER_PROMPT;
        !           722:        AFX_SQL_SYNC(::SQLDriverConnect(m_hdbc, hWnd, reinterpret_cast<SQLTCHAR *>(pszConnectInput),
        !           723:                SQL_NTS, reinterpret_cast<SQLTCHAR *>(szConnectOutput), sizeof(szConnectOutput),
        !           724:                &nResult, wConnectOption));
        !           725:        if (hWndTop != NULL)
        !           726:                ::EnableWindow(hWndTop, TRUE);
        !           727: 
        !           728:        // If user hit 'Cancel'
        !           729:        if (nRetCode == SQL_NO_DATA_FOUND)
        !           730:        {
        !           731:                Free();
        !           732:                return FALSE;
        !           733:        }
        !           734: 
        !           735:        if (!Check(nRetCode))
        !           736:        {
        !           737: #ifdef _DEBUG
        !           738:                if (hWnd == NULL)
        !           739:                        TRACE(traceDatabase, 0, _T("Error: No default window (AfxGetApp()->m_pMainWnd) for SQLDriverConnect.\n"));
        !           740: #endif
        !           741:                ThrowDBException(nRetCode);
        !           742:        }
        !           743: 
        !           744:        // Connect strings must have "ODBC;"
        !           745:        m_strConnect = _afxODBCTrail;
        !           746:        // Save connect string returned from ODBC
        !           747:        m_strConnect += szConnectOutput;
        !           748: 
        !           749:        return TRUE;
        !           750: }
        !           751: 
        !           752: void CDatabase::VerifyConnect()
        !           753: {
        !           754:        RETCODE nRetCode;
        !           755:        SWORD nResult;
        !           756: 
        !           757:        SWORD nAPIConformance;
        !           758:        AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_ODBC_API_CONFORMANCE,
        !           759:                &nAPIConformance, sizeof(nAPIConformance), &nResult));
        !           760:        if (!Check(nRetCode))
        !           761:                ThrowDBException(nRetCode);
        !           762: 
        !           763:        if (nAPIConformance < SQL_OAC_LEVEL1)
        !           764:                ThrowDBException(AFX_SQL_ERROR_API_CONFORMANCE);
        !           765: 
        !           766:        SWORD nSQLConformance;
        !           767:        AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_ODBC_SQL_CONFORMANCE,
        !           768:                &nSQLConformance, sizeof(nSQLConformance), &nResult));
        !           769:        if (!Check(nRetCode))
        !           770:                ThrowDBException(nRetCode);
        !           771: 
        !           772:        if (nSQLConformance < SQL_OSC_MINIMUM)
        !           773:                ThrowDBException(AFX_SQL_ERROR_SQL_CONFORMANCE);
        !           774: }
        !           775: 
        !           776: void CDatabase::GetConnectInfo()
        !           777: {
        !           778:        RETCODE nRetCode;
        !           779:        SWORD nResult;
        !           780: 
        !           781:        // Reset the database update options
        !           782:        m_dwUpdateOptions = 0;
        !           783: 
        !           784:        // Check for SQLSetPos support
        !           785:        UDWORD dwDriverPosOperations;
        !           786:        AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_POS_OPERATIONS,
        !           787:                &dwDriverPosOperations, sizeof(dwDriverPosOperations), &nResult));
        !           788:        if (Check(nRetCode) &&
        !           789:                (dwDriverPosOperations & SQL_POS_UPDATE) &&
        !           790:                (dwDriverPosOperations & SQL_POS_DELETE) &&
        !           791:                (dwDriverPosOperations & SQL_POS_ADD))
        !           792:                m_dwUpdateOptions |= AFX_SQL_SETPOSUPDATES;
        !           793: 
        !           794:        // Check for positioned update SQL support
        !           795:        UDWORD dwPositionedStatements;
        !           796:        AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_POSITIONED_STATEMENTS,
        !           797:                &dwPositionedStatements, sizeof(dwPositionedStatements),
        !           798:                &nResult));
        !           799:        if (Check(nRetCode) &&
        !           800:                (dwPositionedStatements & SQL_PS_POSITIONED_DELETE) &&
        !           801:                (dwPositionedStatements & SQL_PS_POSITIONED_UPDATE))
        !           802:                m_dwUpdateOptions |= AFX_SQL_POSITIONEDSQL;
        !           803: 
        !           804:        // Check for transaction support
        !           805:        SWORD nTxnCapable;
        !           806:        AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_TXN_CAPABLE, &nTxnCapable,
        !           807:                sizeof(nTxnCapable), &nResult));
        !           808:        if (Check(nRetCode) && nTxnCapable != SQL_TC_NONE)
        !           809:                m_bTransactions = TRUE;
        !           810: 
        !           811:        // Cache the effect of transactions on cursors
        !           812:        AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_CURSOR_COMMIT_BEHAVIOR,
        !           813:                &m_nCursorCommitBehavior, sizeof(m_nCursorCommitBehavior),
        !           814:                &nResult));
        !           815:        if (!Check(nRetCode))
        !           816:                m_nCursorCommitBehavior = SQL_ERROR;
        !           817: 
        !           818:        AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_CURSOR_ROLLBACK_BEHAVIOR,
        !           819:                &m_nCursorRollbackBehavior, sizeof(m_nCursorRollbackBehavior),
        !           820:                &nResult));
        !           821:        if (!Check(nRetCode))
        !           822:                m_nCursorRollbackBehavior = SQL_ERROR;
        !           823: 
        !           824:        // Cache bookmark attributes
        !           825:        AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_BOOKMARK_PERSISTENCE,
        !           826:                &m_dwBookmarkAttributes, sizeof(m_dwBookmarkAttributes),
        !           827:                &nResult));
        !           828:        Check(nRetCode);
        !           829: 
        !           830:        // Check for SQLGetData support req'd by RFX_LongBinary
        !           831:        UDWORD dwGetDataExtensions;
        !           832:        AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_GETDATA_EXTENSIONS,
        !           833:                &dwGetDataExtensions, sizeof(dwGetDataExtensions),
        !           834:                &nResult));
        !           835:        if (!Check(nRetCode))
        !           836:                dwGetDataExtensions = 0;
        !           837:        if (dwGetDataExtensions & SQL_GD_BOUND)
        !           838:                m_dwUpdateOptions |= AFX_SQL_GDBOUND;
        !           839: 
        !           840:        if (m_bUpdatable)
        !           841:        {
        !           842:                // Make sure data source is Updatable
        !           843:                TCHAR szReadOnly[10];
        !           844:                AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_DATA_SOURCE_READ_ONLY,
        !           845:                        szReadOnly, sizeof(szReadOnly), &nResult));
        !           846:                if (Check(nRetCode) && nResult == sizeof(TCHAR))
        !           847:                        m_bUpdatable = szReadOnly[0] == _T('N');
        !           848:                else
        !           849:                        m_bUpdatable = FALSE;
        !           850: #ifdef _DEBUG
        !           851:                if (!m_bUpdatable)
        !           852:                        TRACE(traceDatabase, 0, _T("Warning: data source is readonly.\n"));
        !           853: #endif
        !           854:        }
        !           855:        else
        !           856:        {
        !           857:                // Make data source is !Updatable
        !           858:                AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc,
        !           859:                        SQL_ACCESS_MODE, SQL_MODE_READ_ONLY));
        !           860:        }
        !           861: 
        !           862:        // Cache the quote char to use when constructing SQL
        !           863:        TCHAR szIDQuoteChar[2];
        !           864:        AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_IDENTIFIER_QUOTE_CHAR,
        !           865:                szIDQuoteChar, sizeof(szIDQuoteChar), &nResult));
        !           866:        if (Check(nRetCode) && nResult == sizeof(TCHAR))
        !           867:                m_chIDQuoteChar = szIDQuoteChar[0];
        !           868:        else
        !           869:                m_chIDQuoteChar = '\"';
        !           870: 
        !           871: #ifdef _DEBUG
        !           872:        TCHAR szInfo[64];
        !           873:        AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_DBMS_NAME,
        !           874:                szInfo, sizeof(szInfo), &nResult));
        !           875:        if (Check(nRetCode))
        !           876:        {
        !           877:                TRACE(traceDatabase, 0, _T("DBMS: %s\n"), szInfo);
        !           878:                AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_DBMS_VER,
        !           879:                        szInfo, sizeof(szInfo), &nResult));
        !           880:                if (Check(nRetCode))
        !           881:                        TRACE(traceDatabase, 0, _T("Version: %s\n"), szInfo);
        !           882:        }
        !           883: 
        !           884:        AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_ODBC_VER,
        !           885:                szInfo, sizeof(szInfo), &nResult));
        !           886:        if(Check(nRetCode))
        !           887:                TRACE(traceDatabase, 0, _T("ODBC Driver Manager Version: %s\n"), szInfo);
        !           888: #endif // _DEBUG
        !           889: }
        !           890: 
        !           891: void CDatabase::BindParameters(HSTMT /* hstmt */)
        !           892: {
        !           893:        // Must override and call SQLBindParameter directly
        !           894: }
        !           895: 
        !           896: //////////////////////////////////////////////////////////////////////////////
        !           897: // CDatabase diagnostics
        !           898: 
        !           899: #ifdef _DEBUG
        !           900: void CDatabase::AssertValid() const
        !           901: {
        !           902:        CObject::AssertValid();
        !           903: }
        !           904: 
        !           905: void CDatabase::Dump(CDumpContext& dc) const
        !           906: {
        !           907:        CObject::Dump(dc);
        !           908: 
        !           909:        dc << _T("m_hdbc = ") << m_hdbc;
        !           910:        dc << _T("\nm_strConnect = ") << m_strConnect;
        !           911:        dc << _T("\nm_bUpdatable = ") << m_bUpdatable;
        !           912:        dc << _T("\nm_bTransactions = ") << m_bTransactions;
        !           913:        dc << _T("\nm_bTransactionPending = ") << m_bTransactionPending;
        !           914:        dc << _T("\nm_dwLoginTimeout = ") << m_dwLoginTimeout;
        !           915:        dc << _T("\nm_dwQueryTimeout = ") << m_dwQueryTimeout;
        !           916: 
        !           917:        if (dc.GetDepth() > 0)
        !           918:        {
        !           919:                _AFX_DB_STATE* pDbState = _afxDbState;
        !           920:                dc << _T("\nwith env:");
        !           921:                dc << _T("\n\tnAllocated = ") << pDbState->m_nAllocatedConnections;
        !           922:                dc << _T("\n\thenvAllConnections = ") << pDbState->m_henvAllConnections;
        !           923:        }
        !           924: 
        !           925:        dc << _T("\n");
        !           926: }
        !           927: 
        !           928: #endif // _DEBUG
        !           929: 
        !           930: 
        !           931: //////////////////////////////////////////////////////////////////////////////
        !           932: // CRecordset helpers
        !           933: 
        !           934: void AFXAPI _AfxSetCurrentRecord(long* plCurrentRecord, long nRows, RETCODE nRetCode);
        !           935: void AFXAPI _AfxSetRecordCount(long* plRecordCount, long lCurrentRecord,
        !           936:        BOOL bEOFSeen, RETCODE nRetCode);
        !           937: 
        !           938: //////////////////////////////////////////////////////////////////////////////
        !           939: // CRecordset
        !           940: 
        !           941: CRecordset::CRecordset(CDatabase* pDatabase)
        !           942: {
        !           943:        ASSERT(pDatabase == NULL || AfxIsValidAddress(pDatabase, sizeof(CDatabase)));
        !           944:        m_pDatabase = pDatabase;
        !           945: 
        !           946:        m_nOpenType = snapshot;
        !           947:        m_lOpen = AFX_RECORDSET_STATUS_UNKNOWN;
        !           948:        m_nEditMode = noMode;
        !           949:        m_nDefaultType = snapshot;
        !           950:        m_dwOptions = none;
        !           951: 
        !           952:        m_bAppendable = FALSE;
        !           953:        m_bUpdatable = FALSE;
        !           954:        m_bScrollable = FALSE;
        !           955:        m_bRecordsetDb = FALSE;
        !           956:        m_bRebindParams = FALSE;
        !           957:        m_bLongBinaryColumns = FALSE;
        !           958:        m_nLockMode = optimistic;
        !           959:        m_dwInitialGetDataLen = 0;
        !           960:        m_rgODBCFieldInfos = NULL;
        !           961:        m_rgFieldInfos = NULL;
        !           962:        m_rgRowStatus = NULL;
        !           963:        m_dwRowsetSize = 25;
        !           964:        m_dwAllocatedRowsetSize = 0;
        !           965: 
        !           966:        m_nFields = 0;
        !           967:        m_nParams = 0;
        !           968:        m_nFieldsBound = 0;
        !           969:        m_lCurrentRecord = AFX_CURRENT_RECORD_UNDEFINED;
        !           970:        m_lRecordCount = 0;
        !           971:        m_bUseUpdateSQL = FALSE;
        !           972:        m_bUseODBCCursorLib = FALSE;
        !           973:        m_nResultCols = -1;
        !           974:        m_bCheckCacheForDirtyFields = TRUE;
        !           975: 
        !           976:        m_pbFieldFlags = NULL;
        !           977:        m_pbParamFlags = NULL;
        !           978:        m_plParamLength = NULL;
        !           979:        m_pvFieldProxy = NULL;
        !           980:        m_pvParamProxy = NULL;
        !           981:        m_nProxyFields = 0;
        !           982:        m_nProxyParams = 0;
        !           983: 
        !           984:        m_hstmtUpdate = SQL_NULL_HSTMT;
        !           985:        m_hstmt = SQL_NULL_HSTMT;
        !           986:        if (m_pDatabase != NULL && m_pDatabase->IsOpen())
        !           987:        {
        !           988:                ASSERT_VALID(m_pDatabase);
        !           989:                TRY
        !           990:                {
        !           991:                        RETCODE nRetCode;
        !           992:                        AFX_SQL_SYNC(::SQLAllocStmt(m_pDatabase->m_hdbc, &m_hstmt));
        !           993:                        if (!Check(nRetCode))
        !           994:                                ThrowDBException(SQL_INVALID_HANDLE);
        !           995: 
        !           996:                        // Add to list of CRecordsets with alloced hstmts
        !           997:                        AfxLockGlobals(CRIT_ODBC);
        !           998:                        TRY
        !           999:                        {
        !          1000:                                m_pDatabase->m_listRecordsets.AddHead(this);
        !          1001:                        }
        !          1002:                        CATCH_ALL(e)
        !          1003:                        {
        !          1004:                                AfxUnlockGlobals(CRIT_ODBC);
        !          1005:                                THROW_LAST();
        !          1006:                        }
        !          1007:                        END_CATCH_ALL
        !          1008:                        AfxUnlockGlobals(CRIT_ODBC);
        !          1009:                }
        !          1010:                CATCH_ALL(e)
        !          1011:                {
        !          1012:                        ASSERT(m_hstmt == SQL_NULL_HSTMT);
        !          1013:                        DELETE_EXCEPTION(e);
        !          1014:                }
        !          1015:                END_CATCH_ALL
        !          1016:        }
        !          1017: }
        !          1018: 
        !          1019: CRecordset::~CRecordset()
        !          1020: {
        !          1021:        ASSERT_VALID(this);
        !          1022: 
        !          1023:        TRY
        !          1024:        {
        !          1025:                if (m_hstmt != NULL)
        !          1026:                {
        !          1027: #ifdef _DEBUG
        !          1028:                        if (m_dwOptions & useMultiRowFetch)
        !          1029:                        {
        !          1030:                                TRACE(traceDatabase, 0, _T("\nWARNING: Close called implicitly from destructor."));
        !          1031:                                TRACE(traceDatabase, 0, _T("\nUse of multi row fetch requires explicit call"));
        !          1032:                                TRACE(traceDatabase, 0, _T("\nto Close or memory leaks will result.\n"));
        !          1033:                        }
        !          1034: #endif
        !          1035:                        Close();
        !          1036:                }
        !          1037:                if (m_bRecordsetDb)
        !          1038:                        delete m_pDatabase;
        !          1039:                m_pDatabase = NULL;
        !          1040:        }
        !          1041:        CATCH_ALL(e)
        !          1042:        {
        !          1043:                // Nothing we can do
        !          1044:                TRACE(traceDatabase, 0, _T("Error: Exception ignored in ~CRecordset().\n"));
        !          1045:                DELETE_EXCEPTION(e);
        !          1046:        }
        !          1047:        END_CATCH_ALL
        !          1048: }
        !          1049: 
        !          1050: BOOL CRecordset::Open(UINT nOpenType, LPCTSTR lpszSQL, DWORD dwOptions)
        !          1051: {
        !          1052:        ASSERT(!IsOpen());
        !          1053:        ASSERT_VALID(this);
        !          1054:        ASSERT(lpszSQL == NULL || AfxIsValidString(lpszSQL));
        !          1055:        ASSERT(nOpenType == AFX_DB_USE_DEFAULT_TYPE ||
        !          1056:                nOpenType == dynaset || nOpenType == snapshot ||
        !          1057:                nOpenType == forwardOnly || nOpenType == dynamic);
        !          1058:        ASSERT(!(dwOptions & readOnly && dwOptions & appendOnly));
        !          1059: 
        !          1060:        // Can only use optimizeBulkAdd with appendOnly recordsets
        !          1061:        ASSERT((dwOptions & optimizeBulkAdd && dwOptions & appendOnly) ||
        !          1062:                !(dwOptions & optimizeBulkAdd));
        !          1063: 
        !          1064:        // forwardOnly recordsets have limited functionality
        !          1065:        ASSERT(!(nOpenType == forwardOnly && dwOptions & skipDeletedRecords));
        !          1066: 
        !          1067:        // Cache state info and allocate hstmt
        !          1068:        SetState(nOpenType, lpszSQL, dwOptions);
        !          1069:        if(!AllocHstmt())
        !          1070:                return FALSE;
        !          1071: 
        !          1072:        // Check if bookmarks upported (CanBookmark depends on open DB)
        !          1073:        ASSERT(dwOptions & useBookmarks ? CanBookmark() : TRUE);
        !          1074: 
        !          1075:        TRY
        !          1076:        {
        !          1077:                OnSetOptions(m_hstmt);
        !          1078: 
        !          1079:                // Allocate the field/param status arrays, if necessary
        !          1080:                BOOL bUnbound = FALSE;
        !          1081:                if (m_nFields > 0 || m_nParams > 0)
        !          1082:                        AllocStatusArrays();
        !          1083:                else
        !          1084:                        bUnbound = TRUE;
        !          1085: 
        !          1086:                // Build SQL and prep/execute or just execute direct
        !          1087:                BuildSQL(lpszSQL);
        !          1088:                PrepareAndExecute();
        !          1089: 
        !          1090:                // Cache some field info and prepare the rowset
        !          1091:                AllocAndCacheFieldInfo();
        !          1092:                AllocRowset();
        !          1093: 
        !          1094:                // If late binding, still need to allocate status arrays
        !          1095:                if (bUnbound && (m_nFields > 0 || m_nParams > 0))
        !          1096:                        AllocStatusArrays();
        !          1097: 
        !          1098:                // Give derived classes a call before binding
        !          1099:                PreBindFields();
        !          1100: 
        !          1101:                // Fetch the first row of data
        !          1102:                MoveNext();
        !          1103: 
        !          1104:                // If EOF, then result set empty, so set BOF as well
        !          1105:                m_bBOF = m_bEOF;
        !          1106:        }
        !          1107:        CATCH_ALL(e)
        !          1108:        {
        !          1109:                Close();
        !          1110:                THROW_LAST();
        !          1111:        }
        !          1112:        END_CATCH_ALL
        !          1113: 
        !          1114:        return TRUE;
        !          1115: }
        !          1116: 
        !          1117: void CRecordset::Close()
        !          1118: {
        !          1119:        ASSERT_VALID(this);
        !          1120:        // Can't close if database has been deleted
        !          1121:        ASSERT(m_pDatabase != NULL);
        !          1122: 
        !          1123:        // This will force a requery for cursor name if reopened.
        !          1124:        m_strCursorName.Empty();
        !          1125: 
        !          1126:        if (m_rgFieldInfos != NULL &&
        !          1127:                m_nFields > 0 && m_bCheckCacheForDirtyFields)
        !          1128:        {
        !          1129:                FreeDataCache();
        !          1130:        }
        !          1131: 
        !          1132:        FreeRowset();
        !          1133: 
        !          1134:        m_nEditMode = noMode;
        !          1135: 
        !          1136:        delete [] m_rgFieldInfos;
        !          1137:        m_rgFieldInfos = NULL;
        !          1138: 
        !          1139:        delete [] m_rgODBCFieldInfos;
        !          1140:        m_rgODBCFieldInfos = NULL;
        !          1141: 
        !          1142:        delete [] m_pbFieldFlags;
        !          1143:        m_pbFieldFlags = NULL;
        !          1144: 
        !          1145:        delete [] m_pbParamFlags;
        !          1146:        m_pbParamFlags = NULL;
        !          1147: 
        !          1148:        if (m_pvFieldProxy != NULL)
        !          1149:        {
        !          1150:                for (UINT nField = 0; nField < m_nProxyFields; nField++)
        !          1151:                        delete m_pvFieldProxy[nField];
        !          1152: 
        !          1153:                delete [] m_pvFieldProxy;
        !          1154:                m_pvFieldProxy = NULL;
        !          1155:                m_nProxyFields = 0;
        !          1156:        }
        !          1157: 
        !          1158:        if (m_pvParamProxy != NULL)
        !          1159:        {
        !          1160:                for (UINT nParam = 0; nParam < m_nProxyParams; nParam++)
        !          1161:                        delete m_pvParamProxy[nParam];
        !          1162: 
        !          1163:                delete [] m_pvParamProxy;
        !          1164:                m_pvParamProxy = NULL;
        !          1165:                m_nProxyParams = 0;
        !          1166:        }
        !          1167: 
        !          1168:        delete [] m_plParamLength;
        !          1169:        m_plParamLength = NULL;
        !          1170: 
        !          1171:        RETCODE nRetCode;
        !          1172:        if (m_hstmt != SQL_NULL_HSTMT)
        !          1173:        {
        !          1174:                AFX_SQL_SYNC(::SQLFreeStmt(m_hstmt, SQL_DROP));
        !          1175:                m_hstmt = SQL_NULL_HSTMT;
        !          1176:        }
        !          1177: 
        !          1178:        if (m_hstmtUpdate != SQL_NULL_HSTMT)
        !          1179:        {
        !          1180:                AFX_SQL_SYNC(::SQLFreeStmt(m_hstmtUpdate, SQL_DROP));
        !          1181:                m_hstmtUpdate = SQL_NULL_HSTMT;
        !          1182:        }
        !          1183: 
        !          1184:        // Remove CRecordset from CDatabase's list
        !          1185:        AfxLockGlobals(CRIT_ODBC);
        !          1186:        TRY
        !          1187:        {
        !          1188:                POSITION pos = m_pDatabase->m_listRecordsets.Find(this);
        !          1189:                if (pos != NULL)
        !          1190:                        m_pDatabase->m_listRecordsets.RemoveAt(pos);
        !          1191: #ifdef _DEBUG
        !          1192:                else
        !          1193:                        TRACE(traceDatabase, 0, _T("WARNING: CRecordset not found in m_pDatabase->m_listRecordsets.\n"));
        !          1194: #endif
        !          1195:        }
        !          1196:        CATCH_ALL(e)
        !          1197:        {
        !          1198:                AfxUnlockGlobals(CRIT_ODBC);
        !          1199:                THROW_LAST();
        !          1200:        }
        !          1201:        END_CATCH_ALL
        !          1202:        AfxUnlockGlobals(CRIT_ODBC);
        !          1203: 
        !          1204:        m_lOpen = AFX_RECORDSET_STATUS_CLOSED;
        !          1205:        m_bBOF = TRUE;
        !          1206:        m_bEOF = TRUE;
        !          1207:        m_bDeleted = FALSE;
        !          1208:        m_bAppendable = FALSE;
        !          1209:        m_bUpdatable = FALSE;
        !          1210:        m_bScrollable = FALSE;
        !          1211:        m_bRebindParams = FALSE;
        !          1212:        m_bLongBinaryColumns = FALSE;
        !          1213:        m_nLockMode = optimistic;
        !          1214: 
        !          1215:        m_nFieldsBound = 0;
        !          1216:        m_nResultCols = -1;
        !          1217: }
        !          1218: 
        !          1219: BOOL CRecordset::IsOpen() const
        !          1220:        // Note: assumes base class CRecordset::Close called
        !          1221: {
        !          1222:        if (m_hstmt == NULL)
        !          1223:                return FALSE;
        !          1224: 
        !          1225:        if (m_lOpen == AFX_RECORDSET_STATUS_OPEN)
        !          1226:                return TRUE;
        !          1227: 
        !          1228:        RETCODE nRetCode;
        !          1229:        SWORD nCols;
        !          1230: 
        !          1231:        AFX_ODBC_CALL(::SQLNumResultCols(m_hstmt, &nCols));
        !          1232: 
        !          1233:        if (!Check(nRetCode))
        !          1234:        {
        !          1235:                // If function sequence error, CRecordset not open
        !          1236:                CDBException* e = new CDBException(nRetCode);
        !          1237:                e->BuildErrorString(m_pDatabase, m_hstmt, FALSE);
        !          1238:                if (e->m_strStateNativeOrigin.Find(_afxOutOfSequence) >= 0)
        !          1239:                {
        !          1240:                        e->Delete();
        !          1241:                        return FALSE;
        !          1242:                }
        !          1243:                else
        !          1244:                {
        !          1245: #ifdef _DEBUG
        !          1246:                        TRACE(traceDatabase, 0, _T("Error: SQLNumResultCols failed during IsOpen().\n"));
        !          1247:                        e->TraceErrorMessage(e->m_strError);
        !          1248:                        e->TraceErrorMessage(e->m_strStateNativeOrigin);
        !          1249: #endif
        !          1250:                        THROW(e);
        !          1251:                }
        !          1252:        }
        !          1253: 
        !          1254:        BOOL bOpen = FALSE;
        !          1255: 
        !          1256:        if (nCols != 0)
        !          1257:                bOpen = TRUE;
        !          1258: 
        !          1259:        return bOpen;
        !          1260: }
        !          1261: 
        !          1262: BOOL CRecordset::IsFieldDirty(void* pv)
        !          1263: {
        !          1264:        ASSERT_VALID(this);
        !          1265:        ASSERT(!(m_dwOptions & useMultiRowFetch));
        !          1266: 
        !          1267:        if (m_nFields <= 0)
        !          1268:        {
        !          1269:                ASSERT(FALSE);
        !          1270:                return FALSE;
        !          1271:        }
        !          1272: 
        !          1273:        // If not in update op fields can't be dirty
        !          1274:        // must compare saved and current values
        !          1275:        if (m_nEditMode == noMode)
        !          1276:                return FALSE;
        !          1277: 
        !          1278:        // Must compare values to find dirty fields if necessary
        !          1279:        if (m_bCheckCacheForDirtyFields)
        !          1280:        {
        !          1281:                if (m_nEditMode == edit)
        !          1282:                        MarkForUpdate();
        !          1283:                else
        !          1284:                        MarkForAddNew();
        !          1285:        }
        !          1286: 
        !          1287:        int nIndex = 0, nIndexEnd;
        !          1288: 
        !          1289:        if (pv == NULL)
        !          1290:                nIndexEnd = m_nFields - 1;
        !          1291:        else
        !          1292:        {
        !          1293:                // GetBoundFieldIndex returns 1-based index
        !          1294:                nIndex = nIndexEnd = GetBoundFieldIndex(pv) - 1;
        !          1295: 
        !          1296:                // must be address of field member
        !          1297:                ASSERT(nIndex >= 0);
        !          1298:        }
        !          1299: 
        !          1300:        BOOL bDirty = FALSE;
        !          1301: 
        !          1302:        while (nIndex <= nIndexEnd && !bDirty)
        !          1303:                bDirty = IsFieldStatusDirty(nIndex++);
        !          1304: 
        !          1305:        return bDirty;
        !          1306: }
        !          1307: 
        !          1308: BOOL CRecordset::IsFieldNull(void* pv)
        !          1309: {
        !          1310:        ASSERT_VALID(this);
        !          1311:        ASSERT(!(m_dwOptions & useMultiRowFetch));
        !          1312: 
        !          1313:        int nIndex;
        !          1314:        BOOL bRetVal;
        !          1315: 
        !          1316:        if (pv == NULL)
        !          1317:        {
        !          1318:                bRetVal = FALSE;
        !          1319:                for (nIndex = 0; !bRetVal && nIndex <= int(m_nFields-1); nIndex++)
        !          1320:                        bRetVal = IsFieldStatusNull((DWORD) nIndex);
        !          1321:        }
        !          1322:        else
        !          1323:        {
        !          1324:                nIndex = GetBoundFieldIndex(pv) - 1;
        !          1325:                if (nIndex < 0)
        !          1326:                        ThrowDBException(AFX_SQL_ERROR_FIELD_NOT_FOUND);
        !          1327:                bRetVal = IsFieldStatusNull((DWORD) nIndex);
        !          1328:        }
        !          1329: 
        !          1330:        return bRetVal;
        !          1331: }
        !          1332: 
        !          1333: BOOL CRecordset::IsFieldNullable(void* pv)
        !          1334: {
        !          1335:        ASSERT_VALID(this);
        !          1336: 
        !          1337:        if (pv == NULL)
        !          1338:        {
        !          1339:                // Must specify valid column name
        !          1340:                ASSERT(FALSE);
        !          1341:                return FALSE;
        !          1342:        }
        !          1343: 
        !          1344:        int nIndex = GetBoundFieldIndex(pv) - 1;
        !          1345:        if (nIndex < 0)
        !          1346:                ThrowDBException(AFX_SQL_ERROR_FIELD_NOT_FOUND);
        !          1347: 
        !          1348:        return IsFieldNullable((DWORD)nIndex);
        !          1349: }
        !          1350: 
        !          1351: BOOL CRecordset::CanBookmark() const
        !          1352: {
        !          1353:        ASSERT_VALID(this);
        !          1354:        ASSERT(m_pDatabase->IsOpen());
        !          1355: 
        !          1356:        if (!(m_dwOptions & useBookmarks) ||
        !          1357:                (m_nOpenType == forwardOnly && !(m_dwOptions & useExtendedFetch)))
        !          1358:                return FALSE;
        !          1359: 
        !          1360:        return m_pDatabase->GetBookmarkPersistence() & SQL_BP_SCROLL;
        !          1361: }
        !          1362: 
        !          1363: void CRecordset::Move(long nRows, WORD wFetchType)
        !          1364: {
        !          1365:        ASSERT_VALID(this);
        !          1366:        ASSERT(m_hstmt != SQL_NULL_HSTMT);
        !          1367: 
        !          1368:        // First call - fields haven't been bound (m_nFieldsBound will change)
        !          1369:        if (m_nFieldsBound == 0)
        !          1370:        {
        !          1371:                InitRecord();
        !          1372:                ResetCursor();
        !          1373:        }
        !          1374: 
        !          1375:        if (m_nFieldsBound > 0)
        !          1376:        {
        !          1377:                // Reset field flags - mark all clean, all non-null
        !          1378:                memset(m_pbFieldFlags, 0, m_nFields);
        !          1379: 
        !          1380:                // Clear any edit mode that was set
        !          1381:                m_nEditMode = noMode;
        !          1382:        }
        !          1383: 
        !          1384:        // Check scrollability, EOF/BOF status
        !          1385:        CheckRowsetCurrencyStatus(wFetchType, nRows);
        !          1386: 
        !          1387:        RETCODE nRetCode;
        !          1388: 
        !          1389:        // Fetch the data, skipping deleted records if necessary
        !          1390:        if ((wFetchType == SQL_FETCH_FIRST ||
        !          1391:                wFetchType == SQL_FETCH_LAST ||
        !          1392:                wFetchType == SQL_FETCH_NEXT ||
        !          1393:                wFetchType == SQL_FETCH_PRIOR ||
        !          1394:                wFetchType == SQL_FETCH_RELATIVE) &&
        !          1395:                m_dwOptions & skipDeletedRecords)
        !          1396:        {
        !          1397:                SkipDeletedRecords(wFetchType, nRows, &m_dwRowsFetched, &nRetCode);
        !          1398:        }
        !          1399:        else
        !          1400:                // Fetch the data and check for errors
        !          1401:                nRetCode = FetchData(wFetchType, nRows, &m_dwRowsFetched);
        !          1402: 
        !          1403:        // Set currency status and increment the record counters
        !          1404:        SetRowsetCurrencyStatus(nRetCode, wFetchType, nRows, m_dwRowsFetched);
        !          1405: 
        !          1406:        // Need to fixup bound fields in some cases
        !          1407:        if (m_nFields > 0 && !IsEOF() && !IsBOF() &&
        !          1408:                !(m_dwOptions & useMultiRowFetch))
        !          1409:        {
        !          1410:                Fixups();
        !          1411:        }
        !          1412: }
        !          1413: 
        !          1414: void CRecordset::CheckRowsetError(RETCODE nRetCode)
        !          1415: {
        !          1416:        if (nRetCode == SQL_SUCCESS_WITH_INFO)
        !          1417:        {
        !          1418:                CDBException e(nRetCode);
        !          1419:                // Build the error string but don't send nuisance output to TRACE window
        !          1420:                e.BuildErrorString(m_pDatabase, m_hstmt, FALSE);
        !          1421: 
        !          1422:                if (e.m_strStateNativeOrigin.Find(_afxDataTruncated) >= 0)
        !          1423:                {
        !          1424:                        // Ignore data truncated warning if binding long binary columns
        !          1425:                        // (may mask non-long binary truncation warnings or other warnings)
        !          1426:                        if (!((m_pDatabase->m_dwUpdateOptions & AFX_SQL_SETPOSUPDATES) &&
        !          1427:                                m_bLongBinaryColumns))
        !          1428:                        {
        !          1429:                                NO_CPP_EXCEPTION(e.Empty());
        !          1430:                                TRACE(traceDatabase, 0, _T("Error: field data truncated during data fetch.\n"));
        !          1431:                                ThrowDBException(AFX_SQL_ERROR_DATA_TRUNCATED);
        !          1432:                        }
        !          1433:                }
        !          1434:                else if (e.m_strStateNativeOrigin.Find(_afxRowFetch) >= 0)
        !          1435:                {
        !          1436: #ifdef _DEBUG
        !          1437:                        TRACE(traceDatabase, 0, _T("Error: fetching row from server.\n"));
        !          1438:                        e.TraceErrorMessage(e.m_strError);
        !          1439:                        e.TraceErrorMessage(e.m_strStateNativeOrigin);
        !          1440: #endif
        !          1441:                        NO_CPP_EXCEPTION(e.Empty());
        !          1442:                        ThrowDBException(AFX_SQL_ERROR_ROW_FETCH);
        !          1443:                }
        !          1444: #ifdef _DEBUG
        !          1445:                else
        !          1446:                {
        !          1447:                        // Not a truncation or row fetch warning so send debug output
        !          1448:                        TRACE(traceDatabase, 0, _T("Warning: ODBC Success With Info,\n"));
        !          1449:                        e.TraceErrorMessage(e.m_strError);
        !          1450:                        e.TraceErrorMessage(e.m_strStateNativeOrigin);
        !          1451:                }
        !          1452: #endif // _DEBUG
        !          1453:        }
        !          1454:        else if (!Check(nRetCode))
        !          1455:                ThrowDBException(nRetCode);
        !          1456: }
        !          1457: 
        !          1458: void CRecordset::GetBookmark(CDBVariant& varBookmark)
        !          1459: {
        !          1460:        ASSERT_VALID(this);
        !          1461: 
        !          1462:        // Validate bookmarks are usable
        !          1463:        if (!(m_dwOptions & useBookmarks))
        !          1464:                ThrowDBException(AFX_SQL_ERROR_BOOKMARKS_NOT_ENABLED);
        !          1465:        else if (!CanBookmark())
        !          1466:                ThrowDBException(AFX_SQL_ERROR_BOOKMARKS_NOT_SUPPORTED);
        !          1467: 
        !          1468:        // Currently ODBC only supports 4 byte bookmarks
        !          1469:        // Initialize the variant to a long
        !          1470:        if (varBookmark.m_dwType != DBVT_LONG)
        !          1471:        {
        !          1472:                varBookmark.Clear();
        !          1473:                varBookmark.m_dwType = DBVT_LONG;
        !          1474:                varBookmark.m_lVal = 0;
        !          1475:        }
        !          1476: 
        !          1477:        RETCODE nRetCode;
        !          1478:        SQLLEN nActualSize;
        !          1479: 
        !          1480:        // Retrieve the bookmark (column 0) data
        !          1481:        AFX_ODBC_CALL(::SQLGetData(m_hstmt, 0, SQL_C_BOOKMARK,
        !          1482:                &varBookmark.m_lVal, sizeof(varBookmark.m_lVal), &nActualSize));
        !          1483:        if (!Check(nRetCode))
        !          1484:        {
        !          1485:                TRACE(traceDatabase, 0, _T("Error: GetBookmark operation failed.\n"));
        !          1486:                ThrowDBException(nRetCode);
        !          1487:        }
        !          1488: }
        !          1489: 
        !          1490: void CRecordset::SetBookmark(const CDBVariant& varBookmark)
        !          1491: {
        !          1492:        ASSERT_VALID(this);
        !          1493: 
        !          1494:        // Validate bookmarks are usable
        !          1495:        if (!(m_dwOptions & useBookmarks))
        !          1496:                ThrowDBException(AFX_SQL_ERROR_BOOKMARKS_NOT_ENABLED);
        !          1497:        else if (!CanBookmark())
        !          1498:                ThrowDBException(AFX_SQL_ERROR_BOOKMARKS_NOT_SUPPORTED);
        !          1499: 
        !          1500:        // Currently ODBC only supports 4 byte bookmarks
        !          1501:        ASSERT(varBookmark.m_dwType == DBVT_LONG);
        !          1502: 
        !          1503:        Move(varBookmark.m_lVal, SQL_FETCH_BOOKMARK);
        !          1504: }
        !          1505: 
        !          1506: void CRecordset::SetRowsetSize(DWORD dwNewRowsetSize)
        !          1507: {
        !          1508:        ASSERT_VALID(this);
        !          1509:        ASSERT(dwNewRowsetSize > 0);
        !          1510: 
        !          1511:        // If not yet open, only set expected length
        !          1512:        if (!IsOpen())
        !          1513:        {
        !          1514:                m_dwRowsetSize = dwNewRowsetSize;
        !          1515:                return;
        !          1516:        }
        !          1517: 
        !          1518:        if (!(m_dwOptions & useMultiRowFetch))
        !          1519:        {
        !          1520:                // Only works if bulk row fetching!
        !          1521:                ASSERT(FALSE);
        !          1522:                return;
        !          1523:        }
        !          1524: 
        !          1525:        // Need to reallocate some memory if rowset size grows
        !          1526:        if (m_dwAllocatedRowsetSize == 0 ||
        !          1527:                (m_dwAllocatedRowsetSize < dwNewRowsetSize))
        !          1528:        {
        !          1529:                // If rowset already allocated, delete old and reallocate
        !          1530:                FreeRowset();
        !          1531:                m_rgRowStatus = new WORD[dwNewRowsetSize];
        !          1532: 
        !          1533:                // If not a user allocated buffer grow the data buffers
        !          1534:                if (!(m_dwOptions & userAllocMultiRowBuffers))
        !          1535:                {
        !          1536:                        // Allocate the rowset field buffers
        !          1537:                        m_dwRowsetSize = dwNewRowsetSize;
        !          1538:                        CFieldExchange fx(CFieldExchange::AllocMultiRowBuffer, this);
        !          1539:                        DoBulkFieldExchange(&fx);
        !          1540: 
        !          1541:                        m_dwAllocatedRowsetSize = dwNewRowsetSize;
        !          1542: 
        !          1543:                        // Set bound fields to zero, rebind and reset bound field count
        !          1544:                        int nOldFieldsBound = m_nFieldsBound;
        !          1545:                        m_nFieldsBound = 0;
        !          1546:                        InitRecord();
        !          1547:                        m_nFieldsBound = nOldFieldsBound;
        !          1548:                }
        !          1549:        }
        !          1550:        else
        !          1551:        {
        !          1552:                // Just reset the new rowset size
        !          1553:                m_dwRowsetSize = dwNewRowsetSize;
        !          1554:        }
        !          1555: 
        !          1556:        RETCODE nRetCode;
        !          1557:        AFX_SQL_SYNC(::SQLSetStmtOption(m_hstmt, SQL_ROWSET_SIZE,
        !          1558:                m_dwRowsetSize));
        !          1559: }
        !          1560: 
        !          1561: void CRecordset::AddNew()
        !          1562: {
        !          1563:        ASSERT_VALID(this);
        !          1564:        ASSERT(m_hstmt != SQL_NULL_HSTMT);
        !          1565:        // we can't construct an INSERT statement w/o any columns
        !          1566:        ASSERT(m_nFields != 0);
        !          1567: 
        !          1568:        if (!m_bAppendable)
        !          1569:        {
        !          1570:                ThrowDBException(AFX_SQL_ERROR_RECORDSET_READONLY);
        !          1571:        }
        !          1572: 
        !          1573:        if (m_dwOptions & useMultiRowFetch)
        !          1574:        {
        !          1575:                // Can't use update methods on multi-row rowset
        !          1576:                ASSERT(FALSE);
        !          1577:                return;
        !          1578:        }
        !          1579: 
        !          1580:        if (m_bCheckCacheForDirtyFields && m_nFields > 0)
        !          1581:        {
        !          1582:                if (m_nEditMode == noMode)
        !          1583:                {
        !          1584:                        // First addnew call, cache record values
        !          1585:                        StoreFields();
        !          1586:                }
        !          1587:                else
        !          1588:                {
        !          1589:                        // subsequent Edit/AddNew call.  Restore values, save them again
        !          1590:                        LoadFields();
        !          1591:                        StoreFields();
        !          1592:                }
        !          1593:        }
        !          1594: 
        !          1595:        SetFieldNull(NULL);
        !          1596:        SetFieldDirty(NULL, FALSE);
        !          1597: 
        !          1598:        m_nEditMode = addnew;
        !          1599: }
        !          1600: 
        !          1601: void CRecordset::Edit()
        !          1602: {
        !          1603:        ASSERT_VALID(this);
        !          1604:        ASSERT(m_hstmt != SQL_NULL_HSTMT);
        !          1605:        // we can't construct an UPDATE statement w/o any columns
        !          1606:        ASSERT(m_nFields != 0);
        !          1607: 
        !          1608:        if (!m_bUpdatable)
        !          1609:                ThrowDBException(AFX_SQL_ERROR_RECORDSET_READONLY);
        !          1610: 
        !          1611:        if (m_dwOptions & useMultiRowFetch)
        !          1612:        {
        !          1613:                // Can't use update methods on multi-row rowset
        !          1614:                ASSERT(FALSE);
        !          1615:                return;
        !          1616:        }
        !          1617: 
        !          1618:        if (m_bEOF || m_bBOF || m_bDeleted)
        !          1619:        {
        !          1620:                TRACE(traceDatabase, 0, _T("Error: Edit attempt failed - not on a record.\n"));
        !          1621:                ThrowDBException(AFX_SQL_ERROR_NO_CURRENT_RECORD);
        !          1622:        }
        !          1623: 
        !          1624:        if ((m_nOpenType == dynaset || m_nOpenType == dynamic) &&
        !          1625:                m_nLockMode == pessimistic)
        !          1626:        {
        !          1627:                RETCODE nRetCode;
        !          1628:                AFX_ODBC_CALL(::SQLSetPos(m_hstmt, 1, SQL_POSITION,
        !          1629:                        SQL_LCK_EXCLUSIVE));
        !          1630:                if (!Check(nRetCode))
        !          1631:                {
        !          1632:                        TRACE(traceDatabase, 0, _T("Error: attempt to lock record failed during Edit function.\n"));
        !          1633:                        ThrowDBException(nRetCode);
        !          1634:                }
        !          1635:        }
        !          1636: 
        !          1637:        if (m_bCheckCacheForDirtyFields && m_nFields > 0)
        !          1638:        {
        !          1639:                if (m_nEditMode == noMode)
        !          1640:                        // First edit call, cache record values
        !          1641:                        StoreFields();
        !          1642:                else
        !          1643:                {
        !          1644:                        // subsequent Edit/AddNew call.  Restore values, save them again
        !          1645:                        LoadFields();
        !          1646:                        StoreFields();
        !          1647:                }
        !          1648:        }
        !          1649: 
        !          1650:        m_nEditMode = edit;
        !          1651: }
        !          1652: 
        !          1653: BOOL CRecordset::Update()
        !          1654: {
        !          1655:        ASSERT_VALID(this);
        !          1656:        ASSERT(m_hstmt != SQL_NULL_HSTMT);
        !          1657: 
        !          1658:        if (m_dwOptions & useMultiRowFetch)
        !          1659:        {
        !          1660:                // Can't use update methods on multi-row rowset
        !          1661:                ASSERT(FALSE);
        !          1662:                return FALSE;
        !          1663:        }
        !          1664: 
        !          1665:        if (m_nEditMode != addnew && m_nEditMode != edit)
        !          1666:        {
        !          1667:                TRACE(traceDatabase, 0, _T("Error: must enter Edit or AddNew mode before updating.\n"));
        !          1668:                ThrowDBException(AFX_SQL_ERROR_ILLEGAL_MODE);
        !          1669:        }
        !          1670:        return UpdateInsertDelete();
        !          1671: }
        !          1672: 
        !          1673: void CRecordset::Delete()
        !          1674: {
        !          1675:        ASSERT_VALID(this);
        !          1676:        ASSERT(m_hstmt != SQL_NULL_HSTMT);
        !          1677: 
        !          1678:        if (m_dwOptions & useMultiRowFetch)
        !          1679:        {
        !          1680:                // Can't use update methods on multi-row rowset
        !          1681:                ASSERT(FALSE);
        !          1682:                return;
        !          1683:        }
        !          1684: 
        !          1685:        if (m_nEditMode != noMode)
        !          1686:        {
        !          1687:                TRACE(traceDatabase, 0, _T("Error: attempted to delete while still in Edit or AddNew mode.\n"));
        !          1688:                ThrowDBException(AFX_SQL_ERROR_ILLEGAL_MODE);
        !          1689:        }
        !          1690:        UpdateInsertDelete();   // This call can't fail in delete mode (noMode)
        !          1691: }
        !          1692: 
        !          1693: void CRecordset::CancelUpdate()
        !          1694: {
        !          1695:        ASSERT_VALID(this);
        !          1696:        ASSERT(IsOpen());
        !          1697: 
        !          1698:        if (m_nEditMode == noMode)
        !          1699:                // Do nothing if not in edit mode
        !          1700:                return;
        !          1701:        else
        !          1702:                // Reset the edit mode
        !          1703:                m_nEditMode = noMode;
        !          1704: 
        !          1705:        // Restore cache if necessary
        !          1706:        if (m_bCheckCacheForDirtyFields && m_nFields > 0)
        !          1707:                LoadFields();
        !          1708: }
        !          1709: 
        !          1710: BOOL CRecordset::FlushResultSet()
        !          1711: {
        !          1712:        RETCODE nRetCode;
        !          1713:        AFX_ODBC_CALL(::SQLMoreResults(m_hstmt));
        !          1714: 
        !          1715:        if (!Check(nRetCode))
        !          1716:        {
        !          1717:                TRACE(traceDatabase, 0, _T("Error: attempt FlushResultSet failed.\n"));
        !          1718:                AfxThrowDBException(nRetCode, m_pDatabase, m_hstmt);
        !          1719:        }
        !          1720: 
        !          1721:        // Reset state of cursor
        !          1722:        ResetCursor();
        !          1723: 
        !          1724:        return nRetCode != SQL_NO_DATA_FOUND;
        !          1725: }
        !          1726: 
        !          1727: void CRecordset::GetODBCFieldInfo(LPCTSTR lpszName,
        !          1728:        CODBCFieldInfo& fieldinfo)
        !          1729: {
        !          1730:        ASSERT_VALID(this);
        !          1731:        ASSERT(IsOpen());
        !          1732:        ASSERT(lpszName != NULL);
        !          1733: 
        !          1734:        // No data or no column info fetched yet
        !          1735:        if (GetODBCFieldCount() <= 0)
        !          1736:        {
        !          1737:                ASSERT(FALSE);
        !          1738:                return;
        !          1739:        }
        !          1740: 
        !          1741:        // Get the index of the field corresponding to name
        !          1742:        short nField = GetFieldIndexByName(lpszName);
        !          1743: 
        !          1744:        GetODBCFieldInfo(nField, fieldinfo);
        !          1745: }
        !          1746: 
        !          1747: void CRecordset::GetODBCFieldInfo(short nIndex,
        !          1748:        CODBCFieldInfo& fieldinfo)
        !          1749: {
        !          1750:        ASSERT_VALID(this);
        !          1751:        ASSERT(IsOpen());
        !          1752: 
        !          1753:        // No data or no column info fetched yet
        !          1754:        if (GetODBCFieldCount() <= 0)
        !          1755:        {
        !          1756:                ASSERT(FALSE);
        !          1757:                return;
        !          1758:        }
        !          1759: 
        !          1760:        // Just copy the data into the field info
        !          1761:        CODBCFieldInfo* pInfo = &m_rgODBCFieldInfos[nIndex];
        !          1762:        fieldinfo.m_strName = pInfo->m_strName;
        !          1763:        fieldinfo.m_nSQLType = pInfo->m_nSQLType;
        !          1764:        fieldinfo.m_nPrecision = pInfo->m_nPrecision;
        !          1765:        fieldinfo.m_nScale = pInfo->m_nScale;
        !          1766:        fieldinfo.m_nNullability = pInfo->m_nNullability;
        !          1767: }
        !          1768: 
        !          1769: void CRecordset::GetFieldValue(LPCTSTR lpszName,
        !          1770:        CDBVariant& varValue, short nFieldType)
        !          1771: {
        !          1772:        ASSERT_VALID(this);
        !          1773:        ASSERT(IsOpen());
        !          1774:        ASSERT(lpszName != NULL);
        !          1775: 
        !          1776:        // No data or no column info fetched yet
        !          1777:        if (GetODBCFieldCount() <= 0)
        !          1778:        {
        !          1779:                ASSERT(FALSE);
        !          1780:                varValue.Clear();
        !          1781:                return;
        !          1782:        }
        !          1783: 
        !          1784:        // Get the index of the field corresponding to name
        !          1785:        short nField = GetFieldIndexByName(lpszName);
        !          1786: 
        !          1787:        GetFieldValue(nField, varValue, nFieldType);
        !          1788: }
        !          1789: 
        !          1790: void CRecordset::GetFieldValue(short nIndex,
        !          1791:        CDBVariant& varValue, short nFieldType)
        !          1792: {
        !          1793:        ASSERT_VALID(this);
        !          1794:        ASSERT(IsOpen());
        !          1795: 
        !          1796:        // Clear the previous variant
        !          1797:        varValue.Clear();
        !          1798: 
        !          1799:        // No data or no column info fetched yet
        !          1800:        if (GetODBCFieldCount() <= 0)
        !          1801:        {
        !          1802:                ASSERT(FALSE);
        !          1803:                return;
        !          1804:        }
        !          1805: 
        !          1806:        // Convert index to 1-based and check range
        !          1807:        nIndex++;
        !          1808:        if (nIndex < 1 || nIndex > GetODBCFieldCount())
        !          1809:        {
        !          1810:                ThrowDBException(AFX_SQL_ERROR_FIELD_NOT_FOUND);
        !          1811:        }
        !          1812: 
        !          1813:        void* pvData = NULL;
        !          1814:        SQLLEN nLen = 0;
        !          1815: 
        !          1816:        // Determine the default field type and get the data buffer
        !          1817:        if (nFieldType == DEFAULT_FIELD_TYPE)
        !          1818:        {
        !          1819:                nFieldType =
        !          1820:                        GetDefaultFieldType(m_rgODBCFieldInfos[nIndex - 1].m_nSQLType);
        !          1821:        }
        !          1822:        pvData = GetDataBuffer(varValue, nFieldType, &nLen,
        !          1823:                m_rgODBCFieldInfos[nIndex - 1].m_nSQLType,
        !          1824:                m_rgODBCFieldInfos[nIndex - 1].m_nPrecision);
        !          1825: 
        !          1826:        // Now can actually get the data
        !          1827:        SQLLEN nActualSize = GetData(m_pDatabase, m_hstmt, nIndex,
        !          1828:                nFieldType, pvData,
        !          1829:                (SQLLEN) (varValue.m_dwType == DBVT_WSTRING ? nLen * sizeof(WCHAR) : nLen),
        !          1830:                m_rgODBCFieldInfos[nIndex - 1].m_nSQLType);
        !          1831: 
        !          1832:        // Handle NULL data separately
        !          1833:        if (nActualSize == SQL_NULL_DATA)
        !          1834:        {
        !          1835:                // Clear value and set the value NULL
        !          1836:                varValue.Clear();
        !          1837:        }
        !          1838:        else
        !          1839:        {
        !          1840:                // May need to cleanup and call SQLGetData again if LONG_VAR data
        !          1841:                switch(nFieldType)
        !          1842:                {
        !          1843:                case SQL_C_CHAR:
        !          1844:                        GetLongCharDataAndCleanup(m_pDatabase, m_hstmt, nIndex,
        !          1845:                                nActualSize, &pvData, nLen, *varValue.m_pstringA,
        !          1846:                                m_rgODBCFieldInfos[nIndex - 1].m_nSQLType, SQL_C_CHAR);
        !          1847:                        break;
        !          1848: 
        !          1849:                case SQL_C_WCHAR:
        !          1850:                        GetLongCharDataAndCleanup(m_pDatabase, m_hstmt, nIndex,
        !          1851:                                nActualSize, &pvData, nLen, *varValue.m_pstringW,
        !          1852:                                m_rgODBCFieldInfos[nIndex - 1].m_nSQLType, SQL_C_WCHAR);
        !          1853:                        break;
        !          1854: 
        !          1855:                case SQL_C_BINARY:
        !          1856:                        GetLongBinaryDataAndCleanup(m_pDatabase, m_hstmt, nIndex,
        !          1857:                                nActualSize, &pvData, nLen, varValue,
        !          1858:                                m_rgODBCFieldInfos[nIndex - 1].m_nSQLType);
        !          1859:                        break;
        !          1860:                }
        !          1861:        }
        !          1862: }
        !          1863: 
        !          1864: void CRecordset::GetFieldValue(LPCTSTR lpszName, CStringW& strValue)
        !          1865: {
        !          1866:        ASSERT_VALID(this);
        !          1867:        ASSERT(IsOpen());
        !          1868:        ASSERT(lpszName != NULL);
        !          1869: 
        !          1870:        // No data or no column info fetched yet
        !          1871:        if (GetODBCFieldCount() <= 0)
        !          1872:        {
        !          1873:                ASSERT(FALSE);
        !          1874:                return;
        !          1875:        }
        !          1876: 
        !          1877:        // Get the index of the field corresponding to name
        !          1878:        short nField = GetFieldIndexByName(lpszName);
        !          1879: 
        !          1880:        GetFieldValue(nField, strValue);
        !          1881: }
        !          1882: 
        !          1883: void CRecordset::GetFieldValue(LPCTSTR lpszName, CStringA& strValue)
        !          1884: {
        !          1885:        ASSERT_VALID(this);
        !          1886:        ASSERT(IsOpen());
        !          1887:        ASSERT(lpszName != NULL);
        !          1888: 
        !          1889:        // No data or no column info fetched yet
        !          1890:        if (GetODBCFieldCount() <= 0)
        !          1891:        {
        !          1892:                ASSERT(FALSE);
        !          1893:                return;
        !          1894:        }
        !          1895: 
        !          1896:        // Get the index of the field corresponding to name
        !          1897:        short nField = GetFieldIndexByName(lpszName);
        !          1898: 
        !          1899:        GetFieldValue(nField, strValue);
        !          1900: }
        !          1901: 
        !          1902: void CRecordset::GetFieldValue(short nIndex, CStringW &strValue)
        !          1903: {
        !          1904:        GetFieldValueEx(nIndex, strValue, SQL_C_WCHAR);
        !          1905: }
        !          1906: 
        !          1907: void CRecordset::GetFieldValue(short nIndex, CStringA &strValue)
        !          1908: {
        !          1909:        GetFieldValueEx(nIndex, strValue, SQL_C_CHAR);
        !          1910: }
        !          1911: 
        !          1912: void CRecordset::SetFieldDirty(void* pv, BOOL bDirty)
        !          1913: {
        !          1914:        ASSERT_VALID(this);
        !          1915: 
        !          1916:        int nIndex, nIndexEnd;
        !          1917: 
        !          1918:        // If not setting all NULL, check simple case
        !          1919:        if (pv != NULL)
        !          1920:        {
        !          1921:                // GetBoundFieldIndex returns 1-based index
        !          1922:                nIndex = GetBoundFieldIndex(pv) - 1;
        !          1923: 
        !          1924:                if (nIndex < 0)
        !          1925:                {
        !          1926:                        // pv must be address of field member
        !          1927:                        ASSERT(FALSE);
        !          1928:                        return;
        !          1929:                }
        !          1930:                else
        !          1931:                {
        !          1932:                        nIndexEnd = nIndex;
        !          1933:                }
        !          1934:        }
        !          1935:        else
        !          1936:        {
        !          1937:                nIndex = 0;
        !          1938:                nIndexEnd = m_nFields - 1;
        !          1939:        }
        !          1940: 
        !          1941:        while (nIndex <= nIndexEnd)
        !          1942:        {
        !          1943:                if (bDirty)
        !          1944:                        SetDirtyFieldStatus((DWORD)nIndex);
        !          1945:                else
        !          1946:                        ClearDirtyFieldStatus((DWORD)nIndex);
        !          1947: 
        !          1948:                nIndex++;
        !          1949:        }
        !          1950: }
        !          1951: 
        !          1952: void CRecordset::SetFieldNull(void* pv, BOOL bNull)
        !          1953: {
        !          1954:        ASSERT_VALID(this);
        !          1955:        ASSERT(IsOpen());
        !          1956:        ASSERT(!(m_dwOptions & useMultiRowFetch));
        !          1957: 
        !          1958:        // If not setting all fields NULL, check simple case (param) first
        !          1959:        if (pv != NULL)
        !          1960:        {
        !          1961:                // Cached index is 1-based
        !          1962:                int nIndex = GetBoundParamIndex(pv) - 1;
        !          1963:                if (nIndex >= 0)
        !          1964:                {
        !          1965:                        if (bNull)
        !          1966:                                SetNullParamStatus(nIndex);
        !          1967:                        else
        !          1968:                                ClearNullParamStatus(nIndex);
        !          1969:                        return;
        !          1970:                }
        !          1971:        }
        !          1972: 
        !          1973:        // Not a param, must be a field
        !          1974:        if (m_nFields <= 0)
        !          1975:        {
        !          1976:                TRACE(traceDatabase, 0, _T("Warning: SetFieldNull() called with no bound fields.\n"));
        !          1977:                return;
        !          1978:        }
        !          1979: 
        !          1980:        // Need field exchange mechanism to set PSEUDO NULL values
        !          1981:        // and to reset data lengths (especially for RFX_LongBinary)
        !          1982:        CFieldExchange fx(CFieldExchange::SetFieldNull, this, pv);
        !          1983:        fx.m_nFieldFound = 0;
        !          1984:        fx.m_bField = bNull;
        !          1985:        DoFieldExchange(&fx);
        !          1986: 
        !          1987:        // If no field found, m_nFieldFound will still be zero
        !          1988:        ASSERT(fx.m_nFieldFound != 0);
        !          1989: }
        !          1990: 
        !          1991: void CRecordset::SetParamNull(int nIndex, BOOL bNull)
        !          1992: {
        !          1993:        ASSERT_VALID(this);
        !          1994:        ASSERT((DWORD)nIndex < m_nParams);
        !          1995: 
        !          1996:        // Can be called before Open, but need to alloc status arrays first
        !          1997:        if (!IsOpen())
        !          1998:                AllocStatusArrays();
        !          1999: 
        !          2000:        if (bNull)
        !          2001:                SetNullParamStatus(nIndex);
        !          2002:        else
        !          2003:                ClearNullParamStatus(nIndex);
        !          2004: 
        !          2005:        return;
        !          2006: }
        !          2007: 
        !          2008: void CRecordset::SetLockingMode(UINT nLockMode)
        !          2009: {
        !          2010:        if (nLockMode == pessimistic)
        !          2011:        {
        !          2012:                RETCODE nRetCode;
        !          2013:                UDWORD dwTypes;
        !          2014:                SWORD nResult;
        !          2015:                AFX_SQL_SYNC(::SQLGetInfo(m_pDatabase->m_hdbc, SQL_LOCK_TYPES,
        !          2016:                        &dwTypes, sizeof(dwTypes), &nResult));
        !          2017:                if (!Check(nRetCode) || !(dwTypes & SQL_LCK_EXCLUSIVE))
        !          2018:                        ThrowDBException(AFX_SQL_ERROR_LOCK_MODE_NOT_SUPPORTED);
        !          2019:        }
        !          2020:        m_nLockMode = nLockMode;
        !          2021: }
        !          2022: 
        !          2023: BOOL CRecordset::Requery()
        !          2024: {
        !          2025:        RETCODE nRetCode;
        !          2026: 
        !          2027:        ASSERT_VALID(this);
        !          2028:        ASSERT(IsOpen());
        !          2029: 
        !          2030:        // Can't requery if using direct execution
        !          2031:        if (m_dwOptions & executeDirect)
        !          2032:                return FALSE;
        !          2033: 
        !          2034:        TRY
        !          2035:        {
        !          2036:                // Detect changes to filter and sort
        !          2037:                if ((m_strFilter != m_strRequeryFilter) || (m_strSort != m_strRequerySort))
        !          2038:                {
        !          2039:                        m_strRequeryFilter = m_strFilter;
        !          2040:                        m_strRequerySort = m_strSort;
        !          2041:                        Close();
        !          2042:                        if (m_strRequerySQL.IsEmpty())
        !          2043:                                return Open(m_nOpenType, NULL, m_dwOptions);
        !          2044:                        else
        !          2045:                                return Open(m_nOpenType, m_strRequerySQL, m_dwOptions);
        !          2046:                }
        !          2047:                else
        !          2048:                {
        !          2049:                        // Shutdown current query, preserving buffers for performance
        !          2050:                        AFX_SQL_SYNC(::SQLFreeStmt(m_hstmt, SQL_CLOSE));
        !          2051:                        m_lOpen = AFX_RECORDSET_STATUS_CLOSED;
        !          2052: 
        !          2053:                        // Rebind date/time parameters
        !          2054:                        RebindParams(m_hstmt);
        !          2055: 
        !          2056:                        // now attempt to re-execute the SQL Query
        !          2057:                        AFX_ODBC_CALL(::SQLExecute(m_hstmt));
        !          2058:                        if (!Check(nRetCode))
        !          2059:                        {
        !          2060:                                TRACE(traceDatabase, 0, _T("Error: Requery attempt failed.\n"));
        !          2061:                                ThrowDBException(nRetCode);
        !          2062:                        }
        !          2063: 
        !          2064:                        m_lOpen = AFX_RECORDSET_STATUS_OPEN;
        !          2065: 
        !          2066:                        // Reset some cursor properties and fetch first record
        !          2067:                        ResetCursor();
        !          2068:                        MoveNext();
        !          2069: 
        !          2070:                        // If EOF, then result set empty, so set BOF as well
        !          2071:                        m_bBOF = m_bEOF;
        !          2072:                }
        !          2073:        }
        !          2074:        CATCH_ALL(e)
        !          2075:        {
        !          2076:                Close();
        !          2077:                THROW_LAST();
        !          2078:        }
        !          2079:        END_CATCH_ALL
        !          2080: 
        !          2081:        return TRUE;    // all set
        !          2082: }
        !          2083: 
        !          2084: // Shutdown any pending query for CRecordset's hstmt's
        !          2085: void CRecordset::Cancel()
        !          2086: {
        !          2087:        ASSERT_VALID(this);
        !          2088:        ASSERT(m_hstmt != SQL_NULL_HSTMT);
        !          2089: 
        !          2090:        ::SQLCancel(m_hstmt);
        !          2091: 
        !          2092:        // If Update hstmt has been allocated, shut it down also
        !          2093:        if (m_hstmtUpdate != SQL_NULL_HSTMT)
        !          2094:                ::SQLCancel(m_hstmtUpdate);
        !          2095: }
        !          2096: 
        !          2097: CString CRecordset::GetDefaultConnect()
        !          2098: {
        !          2099:        ASSERT_VALID(this);
        !          2100: 
        !          2101:        return _afxODBCTrail;
        !          2102: }
        !          2103: 
        !          2104: CString CRecordset::GetDefaultSQL()
        !          2105: {
        !          2106:        ASSERT_VALID(this);
        !          2107: 
        !          2108:        // Override and add table name or entire SQL SELECT statement
        !          2109:        return _T("");
        !          2110: }
        !          2111: 
        !          2112: void CRecordset::DoFieldExchange(CFieldExchange* /* pFX */)
        !          2113: {
        !          2114:        ASSERT_VALID(this);
        !          2115: 
        !          2116:        // Do nothing if dynamically retrieving unbound fields,
        !          2117:        // otherwise override CRecordset and add RFX calls
        !          2118: }
        !          2119: 
        !          2120: void CRecordset::DoBulkFieldExchange(CFieldExchange* /* pFX */)
        !          2121: {
        !          2122:        ASSERT_VALID(this);
        !          2123: 
        !          2124:        // To use multi-record data fetching, you must use
        !          2125:        // a derived CRecordset class and call Close explicitly.
        !          2126:        ASSERT(FALSE);
        !          2127: }
        !          2128: 
        !          2129: void CRecordset::OnSetUpdateOptions(HSTMT hstmt)
        !          2130: {
        !          2131:        ASSERT_VALID(this);
        !          2132:        ASSERT(hstmt != SQL_NULL_HSTMT);
        !          2133:        UNUSED(hstmt);
        !          2134: 
        !          2135:        // user will override
        !          2136: 
        !          2137:        return;
        !          2138: }
        !          2139: 
        !          2140: void CRecordset::OnSetOptions(HSTMT hstmt)
        !          2141: {
        !          2142:        ASSERT_VALID(this);
        !          2143:        ASSERT(hstmt != SQL_NULL_HSTMT);
        !          2144: 
        !          2145:        // Inherit options settings from CDatabase
        !          2146:        m_pDatabase->OnSetOptions(hstmt);
        !          2147: 
        !          2148:        // If fowardOnly recordset and not using SQLExtendedFetch, quit now
        !          2149:        if (m_nOpenType == forwardOnly && !(m_dwOptions & useExtendedFetch))
        !          2150:                return;
        !          2151: 
        !          2152:        // Turn on bookmark support if necessary
        !          2153:        EnableBookmarks();
        !          2154: 
        !          2155:        // If using forwardOnly and extended fetch, quit now
        !          2156:        if (m_nOpenType == forwardOnly)
        !          2157:                return;
        !          2158: 
        !          2159:        // Make sure driver supports extended fetch, ODBC 2.0 and requested cursor type
        !          2160:        VerifyDriverBehavior();
        !          2161:        DWORD dwScrollType = VerifyCursorSupport();
        !          2162: 
        !          2163:        // Set the update method, concurrency and cursor type
        !          2164:        SetUpdateMethod();
        !          2165:        SetConcurrencyAndCursorType(hstmt, dwScrollType);
        !          2166: }
        !          2167: 
        !          2168: // Screen for errors.
        !          2169: BOOL CRecordset::Check(RETCODE nRetCode) const
        !          2170: {
        !          2171:        ASSERT_VALID(this);
        !          2172: 
        !          2173:        switch (nRetCode)
        !          2174:        {
        !          2175:        case SQL_SUCCESS_WITH_INFO:
        !          2176: #ifdef _DEBUG
        !          2177:        {
        !          2178:                CDBException e(nRetCode);
        !          2179:                TRACE(traceDatabase, 0, _T("Warning: ODBC Success With Info, "));
        !          2180:                e.BuildErrorString(m_pDatabase, m_hstmt);
        !          2181:        }
        !          2182: #endif
        !          2183: 
        !          2184:                // Fall through
        !          2185: 
        !          2186:        case SQL_SUCCESS:
        !          2187:        case SQL_NO_DATA_FOUND:
        !          2188:        case SQL_NEED_DATA:
        !          2189:                return TRUE;
        !          2190:        }
        !          2191: 
        !          2192:        return FALSE;
        !          2193: }
        !          2194: 
        !          2195: void CRecordset::PreBindFields()
        !          2196: {
        !          2197:        // Do nothing
        !          2198: }
        !          2199: 
        !          2200: //////////////////////////////////////////////////////////////////////////////
        !          2201: // CRecordset internal functions
        !          2202: 
        !          2203: // Cache state information internally in CRecordset
        !          2204: void CRecordset::SetState(int nOpenType, LPCTSTR lpszSQL, DWORD dwOptions)
        !          2205: {
        !          2206:        if (nOpenType == AFX_DB_USE_DEFAULT_TYPE)
        !          2207:                m_nOpenType = m_nDefaultType;
        !          2208:        else
        !          2209:                m_nOpenType = nOpenType;
        !          2210: 
        !          2211:        m_bAppendable = (dwOptions & appendOnly) != 0 ||
        !          2212:                (dwOptions & readOnly) == 0;
        !          2213:        m_bUpdatable = (dwOptions & readOnly) == 0 &&
        !          2214:                (dwOptions & appendOnly) == 0;
        !          2215: 
        !          2216:        // Can turn off dirty field checking via dwOptions
        !          2217:        if (dwOptions & noDirtyFieldCheck || dwOptions & useMultiRowFetch)
        !          2218:                m_bCheckCacheForDirtyFields = FALSE;
        !          2219: 
        !          2220:        // Set recordset readOnly if forwardOnly
        !          2221:        if (m_nOpenType == forwardOnly && !(dwOptions & readOnly))
        !          2222:        {
        !          2223:                TRACE(traceDatabase, 0, _T("Warning: Setting forwardOnly recordset readOnly.\n"));
        !          2224: 
        !          2225:                dwOptions |= readOnly;
        !          2226: 
        !          2227:                // If using multiRowFetch also set useExtendFetch
        !          2228:                if (dwOptions & useMultiRowFetch)
        !          2229:                        dwOptions |= useExtendedFetch;
        !          2230:        }
        !          2231: 
        !          2232:        // Archive info for use in Requery
        !          2233:        m_dwOptions = dwOptions;
        !          2234:        m_strRequerySQL = lpszSQL;
        !          2235:        m_strRequeryFilter = m_strFilter;
        !          2236:        m_strRequerySort = m_strSort;
        !          2237: }
        !          2238: 
        !          2239: // Allocate the Hstmt and implicitly create and open Database if necessary
        !          2240: BOOL CRecordset::AllocHstmt()
        !          2241: {
        !          2242:        RETCODE nRetCode;
        !          2243:        if (m_hstmt == SQL_NULL_HSTMT)
        !          2244:        {
        !          2245:                CString strDefaultConnect;
        !          2246:                TRY
        !          2247:                {
        !          2248:                        if (m_pDatabase == NULL)
        !          2249:                        {
        !          2250:                                m_pDatabase = new CDatabase();
        !          2251:                                m_bRecordsetDb = TRUE;
        !          2252:                        }
        !          2253: 
        !          2254:                        strDefaultConnect = GetDefaultConnect();
        !          2255: 
        !          2256:                        // If not already opened, attempt to open
        !          2257:                        if (!m_pDatabase->IsOpen())
        !          2258:                        {
        !          2259:                                BOOL bUseCursorLib = m_bUseODBCCursorLib;
        !          2260: 
        !          2261:                                // If non-readOnly snapshot request must use cursor lib
        !          2262:                                if (m_nOpenType == snapshot && !(m_dwOptions & readOnly))
        !          2263:                                {
        !          2264:                                        // This assumes drivers only support readOnly snapshots
        !          2265:                                        bUseCursorLib = TRUE;
        !          2266:                                }
        !          2267: 
        !          2268:                                if (!m_pDatabase->Open(_T(""), FALSE, FALSE,
        !          2269:                                        strDefaultConnect, bUseCursorLib))
        !          2270:                                {
        !          2271:                                        return FALSE;
        !          2272:                                }
        !          2273: 
        !          2274:                                // If snapshot cursor requested and not supported, load cursor lib
        !          2275:                                if (m_nOpenType == snapshot && !bUseCursorLib)
        !          2276:                                {
        !          2277:                                        // Get the supported cursor types
        !          2278:                                        RETCODE nResult;
        !          2279:                                        UDWORD dwDriverScrollOptions;
        !          2280:                                        AFX_SQL_SYNC(::SQLGetInfo(m_pDatabase->m_hdbc, SQL_SCROLL_OPTIONS,
        !          2281:                                                &dwDriverScrollOptions, sizeof(dwDriverScrollOptions), &nResult));
        !          2282:                                        if (!Check(nRetCode))
        !          2283:                                        {
        !          2284:                                                TRACE(traceDatabase, 0, _T("Error: ODBC failure checking for driver capabilities.\n"));
        !          2285:                                                ThrowDBException(nRetCode);
        !          2286:                                        }
        !          2287: 
        !          2288:                                        // Check for STATIC cursor support and load cursor lib
        !          2289:                                        if (!(dwDriverScrollOptions & SQL_SO_STATIC))
        !          2290:                                        {
        !          2291:                                                m_pDatabase->Close();
        !          2292:                                                if (!m_pDatabase->Open(_T(""), FALSE, FALSE,
        !          2293:                                                        strDefaultConnect, TRUE))
        !          2294:                                                        return FALSE;
        !          2295:                                        }
        !          2296:                                }
        !          2297:                        }
        !          2298: 
        !          2299:                        AFX_SQL_SYNC(::SQLAllocStmt(m_pDatabase->m_hdbc, &m_hstmt));
        !          2300:                        if (!Check(nRetCode))
        !          2301:                                ThrowDBException(SQL_INVALID_HANDLE);
        !          2302: 
        !          2303:                        // Add to list of CRecordsets with alloced hstmts
        !          2304:                        AfxLockGlobals(CRIT_ODBC);
        !          2305:                        TRY
        !          2306:                        {
        !          2307:                                m_pDatabase->m_listRecordsets.AddHead(this);
        !          2308:                        }
        !          2309:                        CATCH_ALL(e)
        !          2310:                        {
        !          2311:                                AfxUnlockGlobals(CRIT_ODBC);
        !          2312:                                THROW_LAST();
        !          2313:                        }
        !          2314:                        END_CATCH_ALL
        !          2315:                        AfxUnlockGlobals(CRIT_ODBC);
        !          2316:                }
        !          2317:                CATCH_ALL(e)
        !          2318:                {
        !          2319:                        TRACE(traceDatabase, 0, _T("Error: CDatabase create for CRecordset failed.\n"));
        !          2320: 
        !          2321:                        NO_CPP_EXCEPTION(strDefaultConnect.Empty());
        !          2322:                        if (m_bRecordsetDb)
        !          2323:                        {
        !          2324:                                delete m_pDatabase;
        !          2325:                                m_pDatabase = NULL;
        !          2326:                        }
        !          2327:                        ASSERT(m_hstmt == SQL_NULL_HSTMT);
        !          2328:                        THROW_LAST();
        !          2329:                }
        !          2330:                END_CATCH_ALL
        !          2331:        }
        !          2332: 
        !          2333:        return TRUE;
        !          2334: }
        !          2335: 
        !          2336: // Initialize the status arrays and create the SQL
        !          2337: void CRecordset::BuildSQL(LPCTSTR lpszSQL)
        !          2338: {
        !          2339:        if (lpszSQL == NULL)
        !          2340:                m_strSQL = GetDefaultSQL();
        !          2341:        else
        !          2342:                m_strSQL = lpszSQL;
        !          2343: 
        !          2344:        // Set any supplied params
        !          2345:        if (m_nParams != 0)
        !          2346:        {
        !          2347:                UINT nParams = BindParams(m_hstmt);
        !          2348:                ASSERT(nParams == m_nParams);
        !          2349:        }
        !          2350: 
        !          2351:        // Construct the SQL string
        !          2352:        BuildSelectSQL();
        !          2353:        AppendFilterAndSortSQL();
        !          2354: 
        !          2355:        // Do some extra checking if trying to set recordset updatable or appendable
        !          2356:        if ((m_bUpdatable || m_bAppendable) && !IsRecordsetUpdatable())
        !          2357:                m_bUpdatable = m_bAppendable = FALSE;
        !          2358: 
        !          2359:        if (m_bUpdatable && m_bUseUpdateSQL && m_pDatabase->m_bAddForUpdate)
        !          2360:                m_strSQL += _afxForUpdate;
        !          2361: 
        !          2362:        // Replace brackets with SQL_IDENTIFIER_QUOTE_CHAR
        !          2363:        m_pDatabase->ReplaceBrackets(m_strSQL.GetBuffer(0));
        !          2364:        m_strSQL.ReleaseBuffer();
        !          2365: }
        !          2366: 
        !          2367: // Prepare and Execute the SQL or simple call SQLExecDirect, resetting concurrency if necessary
        !          2368: void CRecordset::PrepareAndExecute()
        !          2369: {
        !          2370:        RETCODE nRetCode = 0;
        !          2371:        BOOL bConcurrency = FALSE;
        !          2372:        TCHAR *pszSQL = const_cast<LPTSTR>(static_cast<LPCTSTR>(m_strSQL));
        !          2373: 
        !          2374:        while (!bConcurrency)
        !          2375:        {
        !          2376:                // Prepare or execute the query
        !          2377:                if (m_dwOptions & executeDirect)
        !          2378:                        AFX_ODBC_CALL(::SQLExecDirect(m_hstmt, reinterpret_cast<SQLTCHAR *>(pszSQL), SQL_NTS));
        !          2379:                else
        !          2380:                        AFX_ODBC_CALL(::SQLPrepare(m_hstmt, reinterpret_cast<SQLTCHAR *>(pszSQL), SQL_NTS));
        !          2381:                if (Check(nRetCode))
        !          2382:                        bConcurrency = TRUE;
        !          2383:                else
        !          2384:                {
        !          2385:                        // If "Driver Not Capable" error, assume cursor type doesn't
        !          2386:                        // support requested concurrency and try alternate concurrency.
        !          2387:                        CDBException* e = new CDBException(nRetCode);
        !          2388:                        e->BuildErrorString(m_pDatabase, m_hstmt);
        !          2389:                        if (m_dwConcurrency != SQL_CONCUR_READ_ONLY &&
        !          2390:                                e->m_strStateNativeOrigin.Find(_afxDriverNotCapable) >= 0)
        !          2391:                        {
        !          2392:                                TRACE(traceDatabase, 0, _T("Warning: Driver does not support requested concurrency.\n"));
        !          2393: 
        !          2394:                                // Don't need exception to persist while attempting to reset concurrency
        !          2395:                                e->Delete();
        !          2396: 
        !          2397:                                // ODBC will automatically attempt to set alternate concurrency if
        !          2398:                                // request fails, but it won't try LOCK even if driver supports it.
        !          2399:                                if ((m_dwDriverConcurrency & SQL_SCCO_LOCK) &&
        !          2400:                                        (m_dwConcurrency == SQL_CONCUR_ROWVER ||
        !          2401:                                        m_dwConcurrency == SQL_CONCUR_VALUES))
        !          2402:                                {
        !          2403:                                        m_dwConcurrency = SQL_CONCUR_LOCK;
        !          2404:                                }
        !          2405:                                else
        !          2406:                                {
        !          2407:                                        m_dwConcurrency = SQL_CONCUR_READ_ONLY;
        !          2408:                                        m_bUpdatable = m_bAppendable = FALSE;
        !          2409: 
        !          2410:                                        TRACE(traceDatabase, 0, _T("Warning: Setting recordset read only.\n"));
        !          2411:                                }
        !          2412: 
        !          2413:                                // Attempt to reset the concurrency model.
        !          2414:                                AFX_SQL_SYNC(::SQLSetStmtOption(m_hstmt, SQL_CONCURRENCY,
        !          2415:                                        m_dwConcurrency));
        !          2416:                                if (!Check(nRetCode))
        !          2417:                                {
        !          2418:                                        TRACE(traceDatabase, 0, _T("Error: ODBC failure setting recordset concurrency.\n"));
        !          2419:                                        ThrowDBException(nRetCode);
        !          2420:                                }
        !          2421:                        }
        !          2422:                        else
        !          2423:                        {
        !          2424:                                TRACE(traceDatabase, 0, _T("Error: ODBC failure on SQLPrepare or SQLExecDirect\n"));
        !          2425:                                THROW(e);
        !          2426:                        }
        !          2427:                }
        !          2428:        }
        !          2429: 
        !          2430: 
        !          2431:        // now attempt to execute the SQL Query if not executed already
        !          2432:        if (!(m_dwOptions & executeDirect))
        !          2433:        {
        !          2434:                AFX_ODBC_CALL(::SQLExecute(m_hstmt));
        !          2435:                if (!Check(nRetCode))
        !          2436:                        ThrowDBException(nRetCode);
        !          2437:        }
        !          2438:        m_lOpen = AFX_RECORDSET_STATUS_OPEN;
        !          2439: 
        !          2440:        // SQLExecute or SQLExecDirect may have changed an option value
        !          2441:        if (nRetCode == SQL_SUCCESS_WITH_INFO)
        !          2442:        {
        !          2443:                // Check if concurrency was changed in order to mark
        !          2444:                // recordset non-updatable if necessary
        !          2445:                DWORD dwConcurrency;
        !          2446:                AFX_SQL_SYNC(::SQLGetStmtOption(m_hstmt, SQL_CONCURRENCY, &dwConcurrency));
        !          2447:                if (!Check(nRetCode))
        !          2448:                        ThrowDBException(nRetCode);
        !          2449: 
        !          2450:                if (dwConcurrency == SQL_CONCUR_READ_ONLY && (m_bUpdatable || m_bAppendable))
        !          2451:                {
        !          2452:                        m_bUpdatable = FALSE;
        !          2453:                        m_bAppendable = FALSE;
        !          2454: 
        !          2455:                        TRACE(traceDatabase, 0, _T("Warning: Concurrency changed by driver.\n"));
        !          2456:                        TRACE(traceDatabase, 0, _T("\tMarking CRecordset as not updatable.\n"));
        !          2457:                }
        !          2458:        }
        !          2459: }
        !          2460: 
        !          2461: // Ensure that driver supports extended fetch and ODBC 2.0 if necessary
        !          2462: void CRecordset::VerifyDriverBehavior()
        !          2463: {
        !          2464:        RETCODE nRetCode;
        !          2465:        UWORD wScrollable;
        !          2466:        // If SQLExtendedFetch not supported, use SQLFetch
        !          2467:        AFX_SQL_SYNC(::SQLGetFunctions(m_pDatabase->m_hdbc,
        !          2468:                SQL_API_SQLEXTENDEDFETCH, &wScrollable));
        !          2469:        if (!Check(nRetCode))
        !          2470:        {
        !          2471:                TRACE(traceDatabase, 0, _T("Error: ODBC failure determining whether recordset is scrollable.\n"));
        !          2472:                ThrowDBException(nRetCode);
        !          2473:        }
        !          2474:        m_bScrollable = wScrollable;
        !          2475:        if (!m_bScrollable)
        !          2476:        {
        !          2477:                TRACE(traceDatabase, 0, _T("Warning: SQLExtendedFetch not supported by driver\n"));
        !          2478:                TRACE(traceDatabase, 0, _T("and/or cursor library not loaded. Opening forwardOnly.\n"));
        !          2479:                TRACE(traceDatabase, 0, _T("for use with SQLFetch.\n"));
        !          2480: 
        !          2481:                m_bUpdatable = FALSE;
        !          2482:                return;
        !          2483:        }
        !          2484: 
        !          2485:        TCHAR szResult[30];
        !          2486:        SWORD nResult;
        !          2487:        // require ODBC v2.0
        !          2488:        AFX_SQL_SYNC(::SQLGetInfo(m_pDatabase->m_hdbc, SQL_ODBC_VER,
        !          2489:                szResult, sizeof(szResult), &nResult));
        !          2490:        if (!Check(nRetCode))
        !          2491:        {
        !          2492:                TRACE(traceDatabase, 0, _T("Error: ODBC failure checking for driver capabilities.\n"));
        !          2493:                ThrowDBException(nRetCode);
        !          2494:        }
        !          2495:        if (szResult[0] == '0' && szResult[1] < '2')
        !          2496:                ThrowDBException(AFX_SQL_ERROR_ODBC_V2_REQUIRED);
        !          2497: }
        !          2498: 
        !          2499: // Check that driver supports requested cursor type
        !          2500: DWORD CRecordset::VerifyCursorSupport()
        !          2501: {
        !          2502:        RETCODE nRetCode;
        !          2503:        SWORD nResult;
        !          2504:        UDWORD dwDriverScrollOptions;
        !          2505:        AFX_SQL_SYNC(::SQLGetInfo(m_pDatabase->m_hdbc, SQL_SCROLL_OPTIONS,
        !          2506:                &dwDriverScrollOptions, sizeof(dwDriverScrollOptions), &nResult));
        !          2507:        if (!Check(nRetCode))
        !          2508:        {
        !          2509:                TRACE(traceDatabase, 0, _T("Error: ODBC failure checking for driver capabilities.\n"));
        !          2510:                ThrowDBException(nRetCode);
        !          2511:        }
        !          2512: 
        !          2513:        SDWORD dwScrollOptions = SQL_CURSOR_FORWARD_ONLY;
        !          2514:        if (m_nOpenType == dynaset)
        !          2515:        {
        !          2516:                // Dynaset support requires ODBC's keyset driven cursor model
        !          2517:                if (!(dwDriverScrollOptions & SQL_SO_KEYSET_DRIVEN))
        !          2518:                        ThrowDBException(AFX_SQL_ERROR_DYNASET_NOT_SUPPORTED);
        !          2519:                dwScrollOptions = SQL_CURSOR_KEYSET_DRIVEN;
        !          2520:        }
        !          2521:        else if (m_nOpenType == snapshot)
        !          2522:        {
        !          2523:                // Snapshot support requires ODBC's static cursor model
        !          2524:                if (!(dwDriverScrollOptions & SQL_SO_STATIC))
        !          2525:                        ThrowDBException(AFX_SQL_ERROR_SNAPSHOT_NOT_SUPPORTED);
        !          2526:                dwScrollOptions = SQL_CURSOR_STATIC;
        !          2527:        }
        !          2528:        else
        !          2529:        {
        !          2530:                // Dynamic cursor support requires ODBC's dynamic cursor model
        !          2531:                if (!(dwDriverScrollOptions & SQL_SO_DYNAMIC))
        !          2532:                        ThrowDBException(AFX_SQL_ERROR_DYNAMIC_CURSOR_NOT_SUPPORTED);
        !          2533:                dwScrollOptions = SQL_CURSOR_DYNAMIC;
        !          2534:        }
        !          2535: 
        !          2536:        return dwScrollOptions;
        !          2537: }
        !          2538: 
        !          2539: void CRecordset::AllocAndCacheFieldInfo()
        !          2540: {
        !          2541:        ASSERT(GetODBCFieldCount() < 0);
        !          2542:        ASSERT(m_rgODBCFieldInfos == NULL);
        !          2543: 
        !          2544:        RETCODE nRetCode;
        !          2545:        SWORD nActualLen;
        !          2546: 
        !          2547:        // Cache the number of result columns
        !          2548:        AFX_ODBC_CALL(::SQLNumResultCols(m_hstmt, &m_nResultCols));
        !          2549:        if (!Check(nRetCode))
        !          2550:        {
        !          2551:                TRACE(traceDatabase, 0, _T("Error: Can't get field info.\n"));
        !          2552:                ThrowDBException(nRetCode);
        !          2553:        }
        !          2554: 
        !          2555:        // If there are no fields quit now
        !          2556:        if (m_nResultCols == 0)
        !          2557:                return;
        !          2558: 
        !          2559:        // Allocate buffer and get the ODBC meta data
        !          2560:        m_rgODBCFieldInfos = new CODBCFieldInfo[m_nResultCols];
        !          2561:        TCHAR *lpszFieldName;
        !          2562: 
        !          2563:        // Get the field info each field
        !          2564:        for (WORD n = 1; n <= GetODBCFieldCount(); n++)
        !          2565:        {
        !          2566:                // Reset the buffer to point to next element
        !          2567:                lpszFieldName =
        !          2568:                        static_cast<LPTSTR>(m_rgODBCFieldInfos[n - 1].m_strName.GetBuffer(MAX_FNAME_LEN + 1));
        !          2569: 
        !          2570:                AFX_ODBC_CALL(::SQLDescribeCol(m_hstmt, n,
        !          2571:                        reinterpret_cast<SQLTCHAR *>(lpszFieldName), MAX_FNAME_LEN, &nActualLen,
        !          2572:                        &m_rgODBCFieldInfos[n - 1].m_nSQLType,
        !          2573:                        &m_rgODBCFieldInfos[n - 1].m_nPrecision,
        !          2574:                        &m_rgODBCFieldInfos[n - 1].m_nScale,
        !          2575:                        &m_rgODBCFieldInfos[n - 1].m_nNullability));
        !          2576:                m_rgODBCFieldInfos[n - 1].m_strName.ReleaseBuffer(nActualLen);
        !          2577: 
        !          2578:                if (!Check(nRetCode))
        !          2579:                {
        !          2580:                        TRACE(traceDatabase, 0, _T("Error: ODBC failure getting field #%d info.\n"), n);
        !          2581:                        ThrowDBException(nRetCode);
        !          2582:                }
        !          2583:        }
        !          2584: }
        !          2585: 
        !          2586: void CRecordset::AllocRowset()
        !          2587: {
        !          2588:        if (m_dwOptions & useMultiRowFetch)
        !          2589:                SetRowsetSize(m_dwRowsetSize);
        !          2590:        else
        !          2591:        {
        !          2592:                // Not using bulk row fetch, set rowset size to 1
        !          2593:                m_rgRowStatus = new WORD[1];
        !          2594:                m_dwRowsetSize = 1;
        !          2595:        }
        !          2596: }
        !          2597: 
        !          2598: void CRecordset::FreeRowset()
        !          2599: {
        !          2600:        // Delete the rowset status
        !          2601:        delete [] m_rgRowStatus;
        !          2602:        m_rgRowStatus = NULL;
        !          2603: 
        !          2604:        if (m_dwOptions & useMultiRowFetch &&
        !          2605:                !(m_dwOptions & userAllocMultiRowBuffers))
        !          2606:        {
        !          2607:                // Calling virtual function, DoBulkFieldExchange, here is bad
        !          2608:                // because Close then FreeRowset may get called from destructor.
        !          2609:                // There is no simple choice however if RFX_Bulk functions do
        !          2610:                // a memory allocation. The net result is that users MUST call
        !          2611:                // Close explicitly (rather than relying on destructor) if
        !          2612:                // using multi row fetches, otherwise they will get a memory leak.
        !          2613:                // If rowset already allocated, delete old rowset buffers
        !          2614:                if (m_dwAllocatedRowsetSize != 0)
        !          2615:                {
        !          2616:                        CFieldExchange fx(CFieldExchange::DeleteMultiRowBuffer, this);
        !          2617:                        DoBulkFieldExchange(&fx);
        !          2618:                }
        !          2619:        }
        !          2620: 
        !          2621:        m_dwAllocatedRowsetSize = 0;
        !          2622: }
        !          2623: 
        !          2624: void CRecordset::EnableBookmarks()
        !          2625: {
        !          2626:        // Turn on bookmark support if necessary
        !          2627:        if (m_dwOptions & useBookmarks)
        !          2628:        {
        !          2629:                RETCODE nRetCode;
        !          2630: 
        !          2631:                // Set stmt option if bookmarks supported by driver
        !          2632:                if (m_pDatabase->GetBookmarkPersistence() & SQL_BP_SCROLL)
        !          2633:                {
        !          2634:                        AFX_SQL_SYNC(::SQLSetStmtOption(m_hstmt, SQL_USE_BOOKMARKS,
        !          2635:                                SQL_UB_ON));
        !          2636:                        if (!Check(nRetCode))
        !          2637:                        {
        !          2638:                                TRACE(traceDatabase, 0, _T("Error: Can't enable bookmark support.\n"));
        !          2639:                                ThrowDBException(nRetCode);
        !          2640:                        }
        !          2641:                }
        !          2642:        }
        !          2643: }
        !          2644: 
        !          2645: // Determine whether to use SQLSetPos or positioned update SQL
        !          2646: void CRecordset::SetUpdateMethod()
        !          2647: {
        !          2648:        // Determine update method
        !          2649:        if (m_pDatabase->m_dwUpdateOptions & AFX_SQL_SETPOSUPDATES)
        !          2650:                m_bUseUpdateSQL = FALSE;
        !          2651:        else if (m_pDatabase->m_dwUpdateOptions & AFX_SQL_POSITIONEDSQL)
        !          2652:                m_bUseUpdateSQL = TRUE;
        !          2653:        else
        !          2654:                m_bUpdatable = FALSE;
        !          2655:  }
        !          2656: 
        !          2657: // Determine which type of concurrency to set, set it and cursor type
        !          2658: void CRecordset::SetConcurrencyAndCursorType(HSTMT hstmt, DWORD dwScrollOptions)
        !          2659: {
        !          2660:        RETCODE nRetCode;
        !          2661:        SWORD nResult;
        !          2662: 
        !          2663:        m_dwConcurrency = SQL_CONCUR_READ_ONLY;
        !          2664:        if ((m_bUpdatable || m_bAppendable) && m_pDatabase->m_bUpdatable)
        !          2665:        {
        !          2666:                AFX_SQL_SYNC(::SQLGetInfo(m_pDatabase->m_hdbc, SQL_SCROLL_CONCURRENCY,
        !          2667:                        &m_dwDriverConcurrency, sizeof(m_dwDriverConcurrency), &nResult));
        !          2668:                if (!Check(nRetCode))
        !          2669:                {
        !          2670:                        TRACE(traceDatabase, 0, _T("Error: ODBC failure checking recordset updatability.\n"));
        !          2671:                        ThrowDBException(nRetCode);
        !          2672:                }
        !          2673: 
        !          2674:                if (m_nLockMode == pessimistic)
        !          2675:                {
        !          2676:                        if (m_dwDriverConcurrency & SQL_SCCO_LOCK)
        !          2677:                                m_dwConcurrency = SQL_CONCUR_LOCK;
        !          2678: #ifdef _DEBUG
        !          2679:                        else
        !          2680:                                TRACE(traceDatabase, 0, _T("Warning: locking not supported, setting recordset read only.\n"));
        !          2681: #endif
        !          2682:                }
        !          2683:                else
        !          2684:                {
        !          2685:                        // Use cheapest, most concurrent model
        !          2686:                        if (m_dwDriverConcurrency & SQL_SCCO_OPT_ROWVER)
        !          2687:                                m_dwConcurrency = SQL_CONCUR_ROWVER;
        !          2688:                        else if (m_dwDriverConcurrency & SQL_SCCO_OPT_VALUES)
        !          2689:                                m_dwConcurrency = SQL_CONCUR_VALUES;
        !          2690:                        else if (m_dwDriverConcurrency & SQL_SCCO_LOCK)
        !          2691:                                m_dwConcurrency = SQL_CONCUR_LOCK;
        !          2692:                }
        !          2693:        }
        !          2694: 
        !          2695:        // Set cursor type (Let rowset size default to 1).
        !          2696:        AFX_SQL_SYNC(::SQLSetStmtOption(hstmt, SQL_CURSOR_TYPE, dwScrollOptions));
        !          2697:        if (!Check(nRetCode))
        !          2698:        {
        !          2699:                TRACE(traceDatabase, 0, _T("Error: ODBC failure setting recordset cursor type.\n"));
        !          2700:                ThrowDBException(nRetCode);
        !          2701:        }
        !          2702: 
        !          2703:        // Set the concurrency model (NOTE: may have to reset concurrency later).
        !          2704:        AFX_SQL_SYNC(::SQLSetStmtOption(hstmt, SQL_CONCURRENCY, m_dwConcurrency));
        !          2705:        if (!Check(nRetCode))
        !          2706:        {
        !          2707:                TRACE(traceDatabase, 0, _T("Error: ODBC failure setting recordset concurrency.\n"));
        !          2708:                ThrowDBException(nRetCode);
        !          2709:        }
        !          2710: }
        !          2711: 
        !          2712: // Is there a join, stored proc call, GROUP BY, UNION or missing FROM?
        !          2713: BOOL CRecordset::IsSQLUpdatable(LPCTSTR lpszSQL)
        !          2714: {
        !          2715:        // Parse for query procedure call keyword or return param
        !          2716:        if (!(_tcsnicmp(lpszSQL, _afxCall, lstrlen(_afxCall)-1) == 0 ||
        !          2717:                _tcsnicmp(lpszSQL, _afxParamCall, lstrlen(_afxParamCall)-1) == 0))
        !          2718:                // Assume this is a select query
        !          2719:                return IsSelectQueryUpdatable(lpszSQL);
        !          2720:        else
        !          2721:                // Don't know the table name to update in procedure call
        !          2722:                return FALSE;
        !          2723: }
        !          2724: 
        !          2725: BOOL CRecordset::IsSelectQueryUpdatable(LPCTSTR lpszSQL)
        !          2726: {
        !          2727:        LPCTSTR lpchTokenFrom;
        !          2728:        LPCTSTR lpchToken;
        !          2729:        LPCTSTR lpchTokenNext;
        !          2730:        LPTSTR lpszSQLStart;
        !          2731:        CString strSQL = lpszSQL;
        !          2732: 
        !          2733:        lpchTokenFrom = FindSQLToken(strSQL, _afxFrom);
        !          2734:        if (lpchTokenFrom == NULL)
        !          2735:        {
        !          2736:                TRACE(traceDatabase, 0, _T("Warning: Missing ' FROM ', recordset not updatable \n"));
        !          2737:                return FALSE;
        !          2738:        }
        !          2739: 
        !          2740:        lpchToken = FindSQLToken(strSQL, _T(" GROUP BY "));
        !          2741:        if (lpchToken != NULL)
        !          2742:        {
        !          2743:                TRACE(traceDatabase, 0, _T("Warning: SQL contains ' GROUP BY ', recordset not updatable \n"));
        !          2744:                return FALSE;
        !          2745:        }
        !          2746: 
        !          2747:        lpchToken = FindSQLToken(strSQL, _T(" UNION "));
        !          2748:        if (lpchToken != NULL)
        !          2749:        {
        !          2750:                TRACE(traceDatabase, 0, _T("Warning: SQL contains ' UNION ', recordset not updatable \n"));
        !          2751:                return FALSE;
        !          2752:        }
        !          2753: 
        !          2754:        // Find next token after FROM (can't have HAVING clause without GROUP BY)
        !          2755:        lpchToken = FindSQLToken(strSQL, _afxWhere);
        !          2756:        lpchTokenNext = FindSQLToken(strSQL, _afxOrderBy);
        !          2757: 
        !          2758:        lpszSQLStart = strSQL.GetBuffer(0);
        !          2759: 
        !          2760:        if (lpchTokenNext == NULL)
        !          2761:                lpchTokenNext = lpchToken;
        !          2762:        else if (lpchToken != NULL && lpchToken < lpchTokenNext)
        !          2763:                lpchTokenNext = lpchToken;
        !          2764: 
        !          2765:        if (lpchTokenNext != NULL)
        !          2766:        {
        !          2767:          //IA64: Assume max query length < 2G chars
        !          2768:                int nFromLength = int(lpchTokenNext - lpchTokenFrom);
        !          2769:                memmove(lpszSQLStart, lpchTokenFrom, nFromLength*sizeof(TCHAR));
        !          2770:                lpszSQLStart[nFromLength] = '\0';
        !          2771:        }
        !          2772:        else
        !          2773:                memmove(lpszSQLStart, lpchTokenFrom, (lstrlen(lpchTokenFrom) + 1) * sizeof(TCHAR));
        !          2774: 
        !          2775:        strSQL.ReleaseBuffer();
        !          2776: 
        !          2777:        if (IsJoin(strSQL))
        !          2778:        {
        !          2779:                TRACE(traceDatabase, 0, _T("Warning: SQL contains join, recordset not updatable \n"));
        !          2780:                return FALSE;
        !          2781:        }
        !          2782: 
        !          2783:        // Cache table name (skip over " FROM ")
        !          2784:        m_strTableName = strSQL.Right(strSQL.GetLength()-6);
        !          2785: 
        !          2786:        return TRUE;
        !          2787: }
        !          2788: 
        !          2789: 
        !          2790: // Check FROM clause for join syntax
        !          2791: BOOL PASCAL CRecordset::IsJoin(LPCTSTR lpszJoinClause)
        !          2792: {
        !          2793:        // Look for comma in join clause
        !          2794:        if (FindSQLToken(lpszJoinClause, _afxComma) != NULL)
        !          2795:                return TRUE;
        !          2796: 
        !          2797:        // Look for outer join clause
        !          2798:        if (FindSQLToken(lpszJoinClause, _T(" JOIN ")) != NULL)
        !          2799:                return TRUE;
        !          2800: 
        !          2801:        return FALSE;
        !          2802: }
        !          2803: 
        !          2804: // Searches string for given token not in single quotes or brackets
        !          2805: LPCTSTR PASCAL CRecordset::FindSQLToken(LPCTSTR lpszSQL, LPCTSTR lpszSQLToken)
        !          2806: {
        !          2807:        BOOL bInLiteral;
        !          2808:        BOOL bInBrackets;
        !          2809:        int nLeftBrackets;
        !          2810:        int nRightBrackets;
        !          2811:        LPCTSTR lpch;
        !          2812:        LPCTSTR lpchSQLStart;
        !          2813:        LPCTSTR lpszFoundToken;
        !          2814:        int nTokenOffset = 0;
        !          2815:        CString strSQL = lpszSQL;
        !          2816: 
        !          2817:        strSQL.MakeUpper();
        !          2818:        lpszFoundToken = strSQL.GetBuffer(0);
        !          2819:        lpchSQLStart = lpszFoundToken;
        !          2820: 
        !          2821:        do
        !          2822:        {
        !          2823:                lpszFoundToken = _tcsstr(lpszFoundToken + nTokenOffset, lpszSQLToken);
        !          2824:                if (lpszFoundToken == NULL)
        !          2825:                {
        !          2826:                        strSQL.ReleaseBuffer();
        !          2827:                        return NULL;
        !          2828:                }
        !          2829: 
        !          2830:                bInLiteral = bInBrackets = FALSE;
        !          2831:                nLeftBrackets = nRightBrackets = 0;
        !          2832: 
        !          2833:                // Check if embedded in literal or brackets
        !          2834:                for (lpch = lpchSQLStart; lpch < lpszFoundToken; lpch = _tcsinc(lpch))
        !          2835:                {
        !          2836:                        if (*lpch == _afxLiteralSeparator)
        !          2837:                        {
        !          2838:                                // Skip if escape literal
        !          2839:                                if (*_tcsinc(lpch) == _afxLiteralSeparator)
        !          2840:                                        lpch = _tcsinc(lpch);
        !          2841:                                else
        !          2842:                                        bInLiteral = !bInLiteral;
        !          2843:                        }
        !          2844:                        else if (!bInLiteral && (*lpch == '['))
        !          2845:                        {
        !          2846:                                // Skip if escape left bracket
        !          2847:                                if (*_tcsinc(lpch) == '[')
        !          2848:                                        lpch = _tcsinc(lpch);
        !          2849:                                else
        !          2850:                                {
        !          2851:                                        nLeftBrackets++;
        !          2852:                                        if ((nLeftBrackets - nRightBrackets) > 0)
        !          2853:                                                bInBrackets = TRUE;
        !          2854:                                        else
        !          2855:                                                bInBrackets = FALSE;
        !          2856:                                }
        !          2857:                        }
        !          2858:                        else if (!bInLiteral && (*lpch == ']'))
        !          2859:                        {
        !          2860:                                // Skip if escape right bracket
        !          2861:                                if (*_tcsinc(lpch) == ']')
        !          2862:                                        lpch = _tcsinc(lpch);
        !          2863:                                else
        !          2864:                                {
        !          2865:                                        nRightBrackets++;
        !          2866:                                        if ((nLeftBrackets - nRightBrackets) > 0)
        !          2867:                                                bInBrackets = TRUE;
        !          2868:                                        else
        !          2869:                                                bInBrackets = FALSE;
        !          2870:                                }
        !          2871:                        }
        !          2872:                }
        !          2873: 
        !          2874:                // If first iteration, reset the offset to jump over found token
        !          2875:                if (nTokenOffset == 0)
        !          2876:                        nTokenOffset = lstrlen(lpszSQLToken);
        !          2877: 
        !          2878:        } while (bInLiteral || bInBrackets);
        !          2879: 
        !          2880:        lpszFoundToken = lpszSQL + (lpszFoundToken - lpchSQLStart);
        !          2881:        strSQL.ReleaseBuffer();
        !          2882:        return lpszFoundToken;
        !          2883: }
        !          2884: 
        !          2885: // Bind fields (if not already bound), then retrieve 1st record
        !          2886: void CRecordset::InitRecord()
        !          2887: {
        !          2888:        // fields to bind
        !          2889:        if (m_nFields != 0)
        !          2890:        {
        !          2891:                m_nFieldsBound = BindFieldsToColumns();
        !          2892:                // m_nFields doesn't reflect number of
        !          2893:                // RFX_ output column calls in Do[Bulk]FieldExchange
        !          2894:                ASSERT((int)m_nFields == m_nFieldsBound);
        !          2895: 
        !          2896:                // Allocate the data cache if necessary
        !          2897:                if (m_nFields > 0 && m_bCheckCacheForDirtyFields)
        !          2898:                        AllocDataCache();
        !          2899:        }
        !          2900:        else
        !          2901:                // No fields to bind, don't attempt to bind again
        !          2902:                m_nFieldsBound = -1;
        !          2903: }
        !          2904: 
        !          2905: void CRecordset::ResetCursor()
        !          2906: {
        !          2907:        m_bEOFSeen = m_bBOF = m_bEOF = FALSE;
        !          2908:        m_bDeleted = FALSE;
        !          2909:        m_lCurrentRecord = AFX_CURRENT_RECORD_BOF;
        !          2910:        m_lRecordCount = 0;
        !          2911: }
        !          2912: 
        !          2913: void CRecordset::CheckRowsetCurrencyStatus(UWORD wFetchType, long nRows)
        !          2914: {
        !          2915:        if (!m_bScrollable && wFetchType != SQL_FETCH_NEXT)
        !          2916:        {
        !          2917:                TRACE(traceDatabase, 0, _T("Error: forward-only recordsets only support MoveNext.\n"));
        !          2918:                ThrowDBException(AFX_SQL_ERROR_RECORDSET_FORWARD_ONLY);
        !          2919:        }
        !          2920: 
        !          2921:        if (IsEOF() && IsBOF())
        !          2922:        {
        !          2923:                // Can't position cursor if recordset empty
        !          2924:                TRACE(traceDatabase, 0, _T("Error: attempted to position cursor on empty recordset.\n"));
        !          2925:                ThrowDBException(AFX_SQL_ERROR_NO_DATA_FOUND);
        !          2926:        }
        !          2927: 
        !          2928:        if (m_nOpenType != dynamic)
        !          2929:        {
        !          2930:                if (IsEOF() && (wFetchType == SQL_FETCH_NEXT ||
        !          2931:                (wFetchType == SQL_FETCH_RELATIVE && nRows > 0)))
        !          2932:                {
        !          2933:                        // if already at EOF, throw an exception
        !          2934:                        TRACE(traceDatabase, 0, _T("Error: attempted to move past EOF.\n"));
        !          2935:                        ThrowDBException(AFX_SQL_ERROR_NO_DATA_FOUND);
        !          2936:                }
        !          2937:                else if (IsBOF() && (wFetchType == SQL_FETCH_PRIOR ||
        !          2938:                (wFetchType == SQL_FETCH_RELATIVE && nRows < 0)))
        !          2939:                {
        !          2940:                        // if already at BOF, throw an exception
        !          2941:                        TRACE(traceDatabase, 0, _T("Error: attempted to move before BOF.\n"));
        !          2942:                        ThrowDBException(AFX_SQL_ERROR_NO_DATA_FOUND);
        !          2943:                }
        !          2944:        }
        !          2945: }
        !          2946: 
        !          2947: RETCODE CRecordset::FetchData(UWORD wFetchType, SDWORD nRow,
        !          2948:        DWORD* pdwRowsFetched)
        !          2949: {
        !          2950:        RETCODE nRetCode;
        !          2951: 
        !          2952:        if (m_nOpenType == forwardOnly && !(m_dwOptions & useExtendedFetch))
        !          2953:        {
        !          2954:                ASSERT(wFetchType == SQL_FETCH_NEXT);
        !          2955: 
        !          2956:                AFX_ODBC_CALL(::SQLFetch(m_hstmt));
        !          2957:                *pdwRowsFetched = 1;
        !          2958: 
        !          2959:                m_bDeleted = FALSE;
        !          2960:        }
        !          2961:        else
        !          2962:        {
        !          2963:                AFX_ODBC_CALL(::SQLExtendedFetch(m_hstmt, wFetchType,
        !          2964:                        nRow, pdwRowsFetched, m_rgRowStatus));
        !          2965: 
        !          2966:                // Set deleted status
        !          2967:                m_bDeleted = GetRowStatus(1) == SQL_ROW_DELETED;
        !          2968:        }
        !          2969: 
        !          2970:        CheckRowsetError(nRetCode);
        !          2971: 
        !          2972:        return nRetCode;
        !          2973: }
        !          2974: 
        !          2975: void CRecordset::SkipDeletedRecords(UWORD wFetchType, long nRows,
        !          2976:        DWORD* pdwRowsFetched, RETCODE* pnRetCode)
        !          2977: {
        !          2978:        ASSERT(!(m_dwOptions & useMultiRowFetch));
        !          2979:        ASSERT(wFetchType == SQL_FETCH_RELATIVE ||
        !          2980:                wFetchType == SQL_FETCH_FIRST ||
        !          2981:                wFetchType == SQL_FETCH_NEXT ||
        !          2982:                wFetchType == SQL_FETCH_LAST ||
        !          2983:                wFetchType == SQL_FETCH_PRIOR);
        !          2984:        ASSERT(nRows != 0);
        !          2985: 
        !          2986:        UWORD wDeletedFetchType = wFetchType;
        !          2987:        DWORD dwDeletedRows = abs(nRows);
        !          2988:        BOOL m_bDone;
        !          2989: 
        !          2990:        switch (wFetchType)
        !          2991:        {
        !          2992:        case SQL_FETCH_FIRST:
        !          2993:                wDeletedFetchType = SQL_FETCH_NEXT;
        !          2994:                break;
        !          2995: 
        !          2996:        case SQL_FETCH_LAST:
        !          2997:                wDeletedFetchType = SQL_FETCH_PRIOR;
        !          2998:                break;
        !          2999: 
        !          3000:        case SQL_FETCH_RELATIVE:
        !          3001:                if (nRows > 0)
        !          3002:                        wDeletedFetchType = SQL_FETCH_NEXT;
        !          3003:                else
        !          3004:                        wDeletedFetchType = SQL_FETCH_PRIOR;
        !          3005:                break;
        !          3006:        }
        !          3007: 
        !          3008:        // First fetch is as expected unless relative fetch
        !          3009:        if (wFetchType != SQL_FETCH_RELATIVE)
        !          3010:        {
        !          3011:                *pnRetCode = FetchData(wFetchType, 1, pdwRowsFetched);
        !          3012:                m_bDone = !m_bDeleted;
        !          3013:        }
        !          3014:        else
        !          3015:        {
        !          3016:                // Since deleted records must be skipped Move(n)
        !          3017:                // must be turned into n MoveNext/MovePrev calls
        !          3018:                *pnRetCode = FetchData(wDeletedFetchType, 1, pdwRowsFetched);
        !          3019:                if (!m_bDeleted)
        !          3020:                {
        !          3021:                        dwDeletedRows--;
        !          3022:                        m_bDone = dwDeletedRows == 0;
        !          3023:                }
        !          3024:                else
        !          3025:                        m_bDone = FALSE;
        !          3026:        }
        !          3027: 
        !          3028:        // Continue fetching until all req'd deleted records skipped
        !          3029:        while (*pnRetCode != SQL_NO_DATA_FOUND && !m_bDone)
        !          3030:        {
        !          3031:                *pnRetCode = FetchData(wDeletedFetchType, 1, pdwRowsFetched);
        !          3032: 
        !          3033:                if (wFetchType == SQL_FETCH_RELATIVE)
        !          3034:                {
        !          3035:                        if (!m_bDeleted)
        !          3036:                        {
        !          3037:                                dwDeletedRows--;
        !          3038:                                m_bDone = dwDeletedRows == 0;
        !          3039:                        }
        !          3040:                        else
        !          3041:                                m_bDone = FALSE;
        !          3042:                }
        !          3043:                else
        !          3044:                        m_bDone = !m_bDeleted;
        !          3045:        }
        !          3046: }
        !          3047: 
        !          3048: void CRecordset::SetRowsetCurrencyStatus(RETCODE nRetCode,
        !          3049:        UWORD wFetchType, long nRows, DWORD dwRowsFetched)
        !          3050: {
        !          3051:        UNUSED_ALWAYS(dwRowsFetched);
        !          3052: 
        !          3053:        // Set the fetch direction
        !          3054:        int nDirection = 0;
        !          3055: 
        !          3056:        switch (wFetchType)
        !          3057:        {
        !          3058:        case SQL_FETCH_FIRST:
        !          3059:                nDirection = 1;
        !          3060:                if (nRetCode == SQL_NO_DATA_FOUND)
        !          3061:                {
        !          3062:                        m_lCurrentRecord = AFX_CURRENT_RECORD_UNDEFINED;
        !          3063:                        m_lRecordCount = 0;
        !          3064:                }
        !          3065:                else
        !          3066:                        m_lCurrentRecord = 0;
        !          3067:                break;
        !          3068: 
        !          3069:        case SQL_FETCH_NEXT:
        !          3070:                nDirection = 1;
        !          3071:                _AfxSetCurrentRecord(&m_lCurrentRecord, nRows, nRetCode);
        !          3072:                _AfxSetRecordCount(&m_lRecordCount, m_lCurrentRecord,
        !          3073:                        m_bEOFSeen, nRetCode);
        !          3074: 
        !          3075:                // This is the only way to know you've hit the end (m_bEOFSeen)
        !          3076:                if (!m_bEOFSeen && nRetCode == SQL_NO_DATA_FOUND &&
        !          3077:                        m_lRecordCount == m_lCurrentRecord + 1)
        !          3078:                {
        !          3079:                        m_bEOFSeen = TRUE;
        !          3080:                }
        !          3081:                break;
        !          3082: 
        !          3083:        case SQL_FETCH_LAST:
        !          3084:                nDirection = -1;
        !          3085:                if (nRetCode == SQL_NO_DATA_FOUND)
        !          3086:                {
        !          3087:                        m_lCurrentRecord = AFX_CURRENT_RECORD_UNDEFINED;
        !          3088:                        m_lRecordCount = 0;
        !          3089:                }
        !          3090:                else if (m_bEOFSeen)
        !          3091:                        m_lCurrentRecord = m_lRecordCount - 1;
        !          3092:                else
        !          3093:                        m_lCurrentRecord = AFX_CURRENT_RECORD_UNDEFINED;
        !          3094:                break;
        !          3095: 
        !          3096:        case SQL_FETCH_PRIOR:
        !          3097:                nDirection = -1;
        !          3098:                // If doing MovePrev after m_bEOF, don't increment current rec
        !          3099:                if (!m_bEOF)
        !          3100:                        _AfxSetCurrentRecord(&m_lCurrentRecord, nRows, nRetCode);
        !          3101:                break;
        !          3102: 
        !          3103:        case SQL_FETCH_RELATIVE:
        !          3104:                nDirection = nRows;
        !          3105:                _AfxSetCurrentRecord(&m_lCurrentRecord, nRows, nRetCode);
        !          3106:                _AfxSetRecordCount(&m_lRecordCount, m_lCurrentRecord,
        !          3107:                        m_bEOFSeen, nRetCode);
        !          3108:                break;
        !          3109: 
        !          3110:        case SQL_FETCH_ABSOLUTE:
        !          3111:                nDirection = nRows;
        !          3112:                if (nRetCode != SQL_NO_DATA_FOUND)
        !          3113:                {
        !          3114:                        if (nRows > 0)
        !          3115:                                m_lCurrentRecord = nRows - 1;
        !          3116:                        else if (m_bEOFSeen)
        !          3117:                                m_lCurrentRecord = m_lRecordCount + nRows;
        !          3118:                        else
        !          3119:                                m_lCurrentRecord = AFX_CURRENT_RECORD_UNDEFINED;
        !          3120:                }
        !          3121:                else
        !          3122:                        m_lCurrentRecord = AFX_CURRENT_RECORD_UNDEFINED;
        !          3123: 
        !          3124:                _AfxSetRecordCount(&m_lRecordCount, m_lCurrentRecord,
        !          3125:                        m_bEOFSeen, nRetCode);
        !          3126:                break;
        !          3127: 
        !          3128:        case SQL_FETCH_BOOKMARK:
        !          3129:                nDirection = 0;
        !          3130:                m_lCurrentRecord = AFX_CURRENT_RECORD_UNDEFINED;
        !          3131:                break;
        !          3132:        }
        !          3133: 
        !          3134:        // Set the BOF/EOF flags
        !          3135:        if (nRetCode == SQL_NO_DATA_FOUND)
        !          3136:        {
        !          3137:                if (wFetchType == SQL_FETCH_FIRST || wFetchType == SQL_FETCH_LAST ||
        !          3138:                        wFetchType == SQL_FETCH_BOOKMARK)
        !          3139:                {
        !          3140:                        // If MoveFirst/MoveLast fails, result set is empty
        !          3141:                        // If SetBookmark fails, currency undefined
        !          3142:                        m_bEOF = m_bBOF = TRUE;
        !          3143:                }
        !          3144:                else
        !          3145:                {
        !          3146:                        m_bEOF = nDirection >= 0;
        !          3147:                        m_bBOF = !m_bEOF;
        !          3148:                }
        !          3149:        }
        !          3150:        else
        !          3151:        {
        !          3152:                m_bEOF = m_bBOF = FALSE;
        !          3153:        }
        !          3154: }
        !          3155: 
        !          3156: void CRecordset::RefreshRowset(WORD wRow, WORD wLockType)
        !          3157: {
        !          3158:        ASSERT(IsOpen());
        !          3159:        ASSERT(m_dwOptions & useMultiRowFetch);
        !          3160: 
        !          3161:        RETCODE nRetCode;
        !          3162: 
        !          3163:        AFX_ODBC_CALL(::SQLSetPos(m_hstmt, wRow, SQL_REFRESH, wLockType));
        !          3164: 
        !          3165:        // Need to fixup bound fields in some cases
        !          3166:        if (m_nFields > 0 && !IsEOF() && !IsBOF() &&
        !          3167:                !(m_dwOptions & useMultiRowFetch))
        !          3168:        {
        !          3169:                Fixups();
        !          3170:        }
        !          3171: }
        !          3172: 
        !          3173: void CRecordset::SetRowsetCursorPosition(WORD wRow, WORD wLockType)
        !          3174: {
        !          3175:        ASSERT(IsOpen());
        !          3176:        ASSERT(m_dwOptions & useMultiRowFetch);
        !          3177: 
        !          3178:        RETCODE nRetCode;
        !          3179: 
        !          3180:        AFX_ODBC_CALL(::SQLSetPos(m_hstmt, wRow, SQL_POSITION, wLockType));
        !          3181: }
        !          3182: 
        !          3183: // "SELECT <user column name list> FROM <table name>"
        !          3184: void CRecordset::BuildSelectSQL()
        !          3185: {
        !          3186:        ASSERT_VALID(this);
        !          3187:        ASSERT(m_hstmt != SQL_NULL_HSTMT);
        !          3188: 
        !          3189:        // Ignore queries with procedure call keyword or output param
        !          3190:        if (!(_tcsnicmp(m_strSQL, _afxCall, lstrlen(_afxCall)-1) == 0 ||
        !          3191:                _tcsnicmp(m_strSQL, _afxParamCall, lstrlen(_afxParamCall)-1) == 0))
        !          3192:        {
        !          3193:                // Ignore queries already built
        !          3194:                if (_tcsnicmp(m_strSQL, _afxSelect, lstrlen(_afxSelect)-1) != 0)
        !          3195:                {
        !          3196:                        // Assume m_strSQL specifies table name
        !          3197:                        ASSERT(m_nFields != 0);
        !          3198: 
        !          3199:                        CString strTableName;
        !          3200:                        strTableName = m_strSQL;
        !          3201:                        m_strSQL.Empty();
        !          3202:                        m_strSQL = _afxSelect;
        !          3203: 
        !          3204:                        // Set all fields dirty. AppendNames only outputs dirty field names
        !          3205:                        SetFieldDirty(NULL);
        !          3206:                        if (AppendNames(&m_strSQL, _T(",")) == 0)
        !          3207:                        {
        !          3208:                                TRACE(traceDatabase, 0, _T("Error: no field names - at least 1 required.\n"));
        !          3209:                                ThrowDBException(AFX_SQL_ERROR_EMPTY_COLUMN_LIST);
        !          3210:                        }
        !          3211: 
        !          3212:                        // Overwrite final ',' separator with ' '
        !          3213:                        ASSERT(m_strSQL[m_strSQL.GetLength()-1] == ',');
        !          3214:                        m_strSQL.SetAt(m_strSQL.GetLength()-1, ' ');
        !          3215: 
        !          3216:                        m_strSQL += _afxFrom;
        !          3217:                        m_strSQL += strTableName;
        !          3218:                }
        !          3219:        }
        !          3220: }
        !          3221: 
        !          3222: // Add the filter and sort strings to query SQL
        !          3223: void CRecordset::AppendFilterAndSortSQL()
        !          3224: {
        !          3225:        if (!m_strFilter.IsEmpty())
        !          3226:        {
        !          3227:                m_strSQL += _afxWhere;
        !          3228:                m_strSQL += m_strFilter;
        !          3229:        }
        !          3230: 
        !          3231:        if (!m_strSort.IsEmpty())
        !          3232:        {
        !          3233:                m_strSQL += _afxOrderBy;
        !          3234:                m_strSQL += m_strSort;
        !          3235:        }
        !          3236: }
        !          3237: 
        !          3238: // Check for required SQLGetData support and do limited SQL parsing
        !          3239: BOOL CRecordset::IsRecordsetUpdatable()
        !          3240: {
        !          3241:        // Do limited SQL parsing to determine if SQL updatable
        !          3242:        if (!IsSQLUpdatable(m_strSQL))
        !          3243:                return FALSE;
        !          3244: 
        !          3245:        // Updatable recordsets with long binary columns must support
        !          3246:        // SQL_GD_BOUND to use SQLSetPos, otherwise must use SQL updates
        !          3247:        BOOL bUpdatable = TRUE;
        !          3248:        if (m_bLongBinaryColumns && !m_bUseUpdateSQL)
        !          3249:        {
        !          3250:                // Set non-updatable if you can't use SQLGetData on bound columns
        !          3251:                if (!(m_pDatabase->m_dwUpdateOptions & AFX_SQL_GDBOUND))
        !          3252:                {
        !          3253:                        // Okay can't use SetPos, try and use positioned update SQL
        !          3254:                        if (m_pDatabase->m_dwUpdateOptions & AFX_SQL_POSITIONEDSQL)
        !          3255:                        {
        !          3256:                                m_bUseUpdateSQL = TRUE;
        !          3257:                                TRACE(traceDatabase, 0, _T("Warning: Can't use SQLSetPos due to lack of SQLGetData support.\n"));
        !          3258:                                TRACE(traceDatabase, 0, _T("\tWill use positioned update SQL.\n"));
        !          3259:                        }
        !          3260:                        else
        !          3261:                        {
        !          3262:                                TRACE(traceDatabase, 0, _T("Warning: Setting recordset read only due to lack of SQLGetData support.\n"));
        !          3263:                                bUpdatable = FALSE;
        !          3264:                        }
        !          3265:                }
        !          3266:        }
        !          3267: 
        !          3268:        return bUpdatable;
        !          3269: }
        !          3270: 
        !          3271: // Execute the update (or delete) using SQLSetPos
        !          3272: void CRecordset::ExecuteSetPosUpdate()
        !          3273: {
        !          3274:        UWORD wExpectedRowStatus;
        !          3275:        UWORD wPosOption;
        !          3276:        if (m_nEditMode == noMode)
        !          3277:        {
        !          3278:                wPosOption = SQL_DELETE;
        !          3279:                wExpectedRowStatus = SQL_ROW_DELETED;
        !          3280:        }
        !          3281:        else
        !          3282:        {
        !          3283:                if (m_nEditMode == edit)
        !          3284:                {
        !          3285:                        wPosOption = SQL_UPDATE;
        !          3286:                        wExpectedRowStatus = SQL_ROW_UPDATED;
        !          3287:                }
        !          3288:                else
        !          3289:                {
        !          3290:                        wPosOption = SQL_ADD;
        !          3291:                        wExpectedRowStatus = SQL_ROW_ADDED;
        !          3292:                }
        !          3293:        }
        !          3294: 
        !          3295:        BindFieldsForUpdate();
        !          3296: 
        !          3297:        RETCODE nRetCode;
        !          3298:        AFX_ODBC_CALL(::SQLSetPos(m_hstmt, 1, wPosOption, SQL_LOCK_NO_CHANGE));
        !          3299:        if (!Check(nRetCode))
        !          3300:        {
        !          3301:                TRACE(traceDatabase, 0, _T("Error: failure updating record.\n"));
        !          3302:                AfxThrowDBException(nRetCode, m_pDatabase, m_hstmt);
        !          3303:        }
        !          3304:        // Only have data-at-execution columns for CLongBinary columns
        !          3305:        if (nRetCode == SQL_NEED_DATA)
        !          3306:                SendLongBinaryData(m_hstmt);
        !          3307:        // This should only fail if SQLSetPos returned SQL_SUCCESS_WITH_INFO explaining why
        !          3308:        if (nRetCode == SQL_SUCCESS_WITH_INFO && GetRowStatus(1) != wExpectedRowStatus)
        !          3309:                ThrowDBException(AFX_SQL_ERROR_UPDATE_DELETE_FAILED);
        !          3310: 
        !          3311:        UnbindFieldsForUpdate();
        !          3312: }
        !          3313: 
        !          3314: // Prepare for sending update SQL by initializing m_hstmtUpdate
        !          3315: void CRecordset::PrepareUpdateHstmt()
        !          3316: {
        !          3317:        RETCODE nRetCode;
        !          3318:        if (m_hstmtUpdate == SQL_NULL_HSTMT)
        !          3319:        {
        !          3320:                AFX_SQL_SYNC(::SQLAllocStmt(m_pDatabase->m_hdbc, &m_hstmtUpdate));
        !          3321:                if (!Check(nRetCode))
        !          3322:                {
        !          3323:                        TRACE(traceDatabase, 0, _T("Error: failure to allocate update statement.\n"));
        !          3324:                        AfxThrowDBException(nRetCode, m_pDatabase, m_hstmtUpdate);
        !          3325:                }
        !          3326: 
        !          3327:                OnSetUpdateOptions(m_hstmtUpdate);
        !          3328:        }
        !          3329:        else
        !          3330:        {
        !          3331:                AFX_SQL_SYNC(::SQLFreeStmt(m_hstmtUpdate, SQL_CLOSE));
        !          3332:                if (!Check(nRetCode))
        !          3333:                        goto LErrRetCode;
        !          3334: 
        !          3335:                // Re-use (prepared) hstmt & param binding if optimizeBulkAdd option
        !          3336:                if(!(m_dwOptions & optimizeBulkAdd))
        !          3337:                {
        !          3338:                        AFX_SQL_SYNC(::SQLFreeStmt(m_hstmtUpdate, SQL_RESET_PARAMS));
        !          3339:                        if (!Check(nRetCode))
        !          3340:                        {
        !          3341:        LErrRetCode:
        !          3342:                                // Bad hstmt, free it and allocate another one
        !          3343:                                AFX_SQL_SYNC(::SQLFreeStmt(m_hstmtUpdate, SQL_DROP));
        !          3344:                                m_hstmtUpdate = SQL_NULL_HSTMT;
        !          3345: 
        !          3346:                                AFX_SQL_SYNC(::SQLAllocStmt(m_pDatabase->m_hdbc, &m_hstmtUpdate));
        !          3347:                                if (!Check(nRetCode))
        !          3348:                                {
        !          3349:                                        TRACE(traceDatabase, 0, _T("Error: failure to allocate update statement.\n"));
        !          3350:                                        AfxThrowDBException(nRetCode, m_pDatabase, m_hstmtUpdate);
        !          3351:                                }
        !          3352: 
        !          3353:                                OnSetUpdateOptions(m_hstmtUpdate);
        !          3354:                        }
        !          3355:                }
        !          3356:        }
        !          3357: }
        !          3358: 
        !          3359: // Build the UPDATE, INSERT or DELETE SQL
        !          3360: void CRecordset::BuildUpdateSQL()
        !          3361: {
        !          3362:        switch (m_nEditMode)
        !          3363:        {
        !          3364:        case noMode:
        !          3365:                // DELETE FROM <tablename> WHERE CURRENT OF
        !          3366:                {
        !          3367:                        m_strUpdateSQL = _T("DELETE FROM ");
        !          3368:                        m_strUpdateSQL += m_strTableName;
        !          3369:                }
        !          3370:                break;
        !          3371: 
        !          3372:        case addnew:
        !          3373:                // INSERT INTO <tablename> (<colname1>[,<colname2>]) VALUES (?[,?])
        !          3374:                {
        !          3375:                        m_strUpdateSQL = _T("INSERT INTO ");
        !          3376:                        m_strUpdateSQL += m_strTableName;
        !          3377: 
        !          3378:                        m_strUpdateSQL += _T(" (");
        !          3379: 
        !          3380:                        // Append column names
        !          3381:                        AppendNames(&m_strUpdateSQL, _afxComma);
        !          3382: 
        !          3383:                        // overwrite last ',' with ')'
        !          3384:                        ASSERT(m_strUpdateSQL[m_strUpdateSQL.GetLength()-1] == ',');
        !          3385:                        m_strUpdateSQL.SetAt(m_strUpdateSQL.GetLength()-1, ')');
        !          3386: 
        !          3387:                        // Append values
        !          3388:                        m_strUpdateSQL += _T(" VALUES (");
        !          3389:                        AppendValues(m_hstmtUpdate, &m_strUpdateSQL, _afxComma);
        !          3390: 
        !          3391:                        // overwrite last ',' with ')'
        !          3392:                        ASSERT(m_strUpdateSQL[m_strUpdateSQL.GetLength()-1] == ',');
        !          3393:                        m_strUpdateSQL.SetAt(m_strUpdateSQL.GetLength()-1, ')');
        !          3394:                }
        !          3395:                break;
        !          3396: 
        !          3397:        case edit:
        !          3398:                // UPDATE <tablename> SET <colname1>=?[,<colname2>=?] WHERE CURRENT OF
        !          3399:                {
        !          3400:                        m_strUpdateSQL = _T("UPDATE ");
        !          3401:                        m_strUpdateSQL += m_strTableName;
        !          3402: 
        !          3403:                        m_strUpdateSQL += _T(" SET ");
        !          3404:                        AppendNamesValues(m_hstmtUpdate, &m_strUpdateSQL, _afxComma);
        !          3405: 
        !          3406:                        // overwrite last ',' with ' '
        !          3407:                        ASSERT(m_strUpdateSQL[m_strUpdateSQL.GetLength()-1] == ',');
        !          3408:                        m_strUpdateSQL.SetAt(m_strUpdateSQL.GetLength()-1, ' ');
        !          3409:                }
        !          3410:                break;
        !          3411:        }
        !          3412: 
        !          3413:        // Update and Delete need "WHERE CURRENT OF <cursorname>"
        !          3414:        if (m_nEditMode == edit || m_nEditMode == noMode)
        !          3415:        {
        !          3416:                m_strUpdateSQL += _T(" WHERE CURRENT OF ");
        !          3417: 
        !          3418:                // Cache cursor name assigned by ODBC
        !          3419:                if (m_strCursorName.IsEmpty())
        !          3420:                {
        !          3421:                        // Get predefined cursor name from datasource
        !          3422:                        RETCODE nRetCode;
        !          3423:                        TCHAR szCursorName[MAX_CURSOR_NAME+1];
        !          3424:                        SWORD nLength = sizeof(szCursorName)-1;
        !          3425:                        AFX_SQL_SYNC(::SQLGetCursorName(m_hstmt,
        !          3426:                                reinterpret_cast<SQLTCHAR *>(szCursorName), sizeof(szCursorName), &nLength));
        !          3427:                        if (!Check(nRetCode))
        !          3428:                                ThrowDBException(nRetCode);
        !          3429:                        m_strCursorName = szCursorName;
        !          3430:                }
        !          3431:                m_strUpdateSQL += m_strCursorName;
        !          3432:        }
        !          3433: 
        !          3434:        m_pDatabase->ReplaceBrackets(m_strUpdateSQL.GetBuffer(0));
        !          3435:        m_strUpdateSQL.ReleaseBuffer();
        !          3436: 
        !          3437:        // Must prepare the hstmt on first optimized bulk add
        !          3438:        if(m_dwOptions & firstBulkAdd)
        !          3439:        {
        !          3440:                RETCODE nRetCode;
        !          3441:                TCHAR *pszSQL = const_cast<LPTSTR>(static_cast<LPCTSTR>(m_strUpdateSQL));
        !          3442:                AFX_ODBC_CALL(::SQLPrepare(m_hstmtUpdate, reinterpret_cast<SQLTCHAR *>(pszSQL), SQL_NTS));
        !          3443:                if (!Check(nRetCode))
        !          3444:                        ThrowDBException(nRetCode, m_hstmtUpdate);
        !          3445:        }
        !          3446: }
        !          3447: 
        !          3448: void CRecordset::ExecuteUpdateSQL()
        !          3449: {
        !          3450:        RETCODE nRetCode;
        !          3451: 
        !          3452:        if(!(m_dwOptions & optimizeBulkAdd))
        !          3453:        {
        !          3454:                TCHAR *pszSQL = const_cast<LPTSTR>(static_cast<LPCTSTR>(m_strUpdateSQL));
        !          3455:                AFX_ODBC_CALL(::SQLExecDirect(m_hstmtUpdate, reinterpret_cast<SQLTCHAR *>(pszSQL), SQL_NTS));
        !          3456:                if (!Check(nRetCode))
        !          3457:                        ThrowDBException(nRetCode, m_hstmtUpdate);
        !          3458:        }
        !          3459:        else
        !          3460:        {
        !          3461:                AFX_ODBC_CALL(::SQLExecute(m_hstmtUpdate));
        !          3462:                if (!Check(nRetCode))
        !          3463:                        ThrowDBException(nRetCode, m_hstmtUpdate);
        !          3464:        }
        !          3465: 
        !          3466:        // Only have data-at-execution parameters for CLongBinary columns
        !          3467:        if (nRetCode == SQL_NEED_DATA)
        !          3468:                SendLongBinaryData(m_hstmtUpdate);
        !          3469: 
        !          3470: #ifdef _WIN64
        !          3471:        SQLROWCOUNT lRowsAffected = 0;
        !          3472: #else
        !          3473:    LONG lRowsAffected = 0;
        !          3474: #endif
        !          3475: 
        !          3476:        AFX_SQL_SYNC(::SQLRowCount(m_hstmtUpdate, &lRowsAffected));
        !          3477:        if (!Check(nRetCode) || lRowsAffected == -1)
        !          3478:        {
        !          3479:                // Assume 1 row affected if # rows affected can't be determined
        !          3480:                lRowsAffected = 1;
        !          3481:        }
        !          3482:        else
        !          3483:        {
        !          3484:                if (lRowsAffected != 1)
        !          3485:                {
        !          3486:                        TRACE(traceDatabase, 0, _T("Warning: %u rows affected by update operation (expected 1).\n"),
        !          3487:                                lRowsAffected);
        !          3488: 
        !          3489:                        ThrowDBException((RETCODE)(lRowsAffected == 0 ?
        !          3490:                                AFX_SQL_ERROR_NO_ROWS_AFFECTED :
        !          3491:                                AFX_SQL_ERROR_MULTIPLE_ROWS_AFFECTED));
        !          3492:                }
        !          3493:        }
        !          3494:        m_strUpdateSQL.Empty();
        !          3495: }
        !          3496: 
        !          3497: 
        !          3498: void CRecordset::SendLongBinaryData(HSTMT hstmt)
        !          3499: {
        !          3500:        RETCODE nRetCode;
        !          3501:        void* pv;
        !          3502:        AFX_ODBC_CALL(::SQLParamData(hstmt, &pv));
        !          3503:        if (!Check(nRetCode))
        !          3504:        {
        !          3505:                // cache away error
        !          3506:                CDBException* pException = new CDBException(nRetCode);
        !          3507:                pException->BuildErrorString(m_pDatabase, hstmt);
        !          3508: 
        !          3509:                // then cancel Execute operation
        !          3510:                Cancel();
        !          3511:                THROW(pException);
        !          3512:        }
        !          3513: 
        !          3514:        while (nRetCode == SQL_NEED_DATA)
        !          3515:        {
        !          3516:                CLongBinary* pLongBinary = (CLongBinary*)pv;
        !          3517:                ASSERT_VALID(pLongBinary);
        !          3518: 
        !          3519:                const BYTE* lpData = (const BYTE*)::GlobalLock(pLongBinary->m_hData);
        !          3520:                ASSERT(lpData != NULL);
        !          3521: 
        !          3522:                AFX_ODBC_CALL(::SQLPutData(hstmt, (PTR)lpData,
        !          3523:                        (SQLLEN)pLongBinary->m_dwDataLength));
        !          3524: 
        !          3525:                ::GlobalUnlock(pLongBinary->m_hData);
        !          3526: 
        !          3527:                if (!Check(nRetCode))
        !          3528:                {
        !          3529:                        // cache away error
        !          3530:                        CDBException* pException = new CDBException(nRetCode);
        !          3531:                        pException->BuildErrorString(m_pDatabase, hstmt);
        !          3532: 
        !          3533:                        // then cancel Execute operation
        !          3534:                        Cancel();
        !          3535:                        THROW(pException);
        !          3536:                }
        !          3537: 
        !          3538:                // Check for another DATA_AT_EXEC
        !          3539:                AFX_ODBC_CALL(::SQLParamData(hstmt, &pv));
        !          3540:                if (!Check(nRetCode))
        !          3541:                {
        !          3542:                        TRACE(traceDatabase, 0, _T("Error: failure handling long binary value during update.\n"));
        !          3543:                        ThrowDBException(nRetCode, hstmt);
        !          3544:                }
        !          3545:        }
        !          3546: }
        !          3547: 
        !          3548: //////////////////////////////////////////////////////////////////////////////
        !          3549: // CRecordset RFX implementations
        !          3550: 
        !          3551: void CRecordset::AllocStatusArrays()
        !          3552: {
        !          3553:        TRY
        !          3554:        {
        !          3555:                if (m_nFields != 0)
        !          3556:                {
        !          3557:                        // Allocate buffers to hold field info
        !          3558:                        if (m_rgFieldInfos == NULL)
        !          3559:                        {
        !          3560:                                m_rgFieldInfos = new CFieldInfo[m_nFields];
        !          3561:                                memset(m_rgFieldInfos, 0, sizeof(CFieldInfo) * m_nFields);
        !          3562:                        }
        !          3563: 
        !          3564:                        if (m_pbFieldFlags == NULL)
        !          3565:                        {
        !          3566:                                m_pbFieldFlags = new BYTE[m_nFields];
        !          3567:                                memset(m_pbFieldFlags, 0, m_nFields);
        !          3568:                        }
        !          3569:                }
        !          3570: 
        !          3571:                if (m_nParams != 0)
        !          3572:                {
        !          3573:                        // Allocate buffers to hold param info
        !          3574:                        if (m_pbParamFlags == NULL)
        !          3575:                        {
        !          3576:                                m_pbParamFlags = new BYTE[m_nParams];
        !          3577:                                memset(m_pbParamFlags, 0, m_nParams);
        !          3578:                        }
        !          3579: 
        !          3580:                        if (m_plParamLength == NULL)
        !          3581:                        {
        !          3582:                                m_plParamLength = new LONG_PTR[m_nParams];
        !          3583:                                memset(m_plParamLength, 0, m_nParams*sizeof(LONG_PTR));
        !          3584:                        }
        !          3585:                }
        !          3586:        }
        !          3587:        CATCH_ALL(e)
        !          3588:        {
        !          3589:                Close();
        !          3590:                THROW_LAST();
        !          3591:        }
        !          3592:        END_CATCH_ALL
        !          3593: }
        !          3594: 
        !          3595: int CRecordset::GetBoundFieldIndex(void* pv)
        !          3596: {
        !          3597:        void* pvIndex;
        !          3598: 
        !          3599:        if (!m_mapFieldIndex.Lookup(pv, pvIndex))
        !          3600:                return -1;
        !          3601:        else
        !          3602:                // Cached value is short not ptr
        !          3603:                return (int)(INT_PTR)pvIndex;
        !          3604: }
        !          3605: 
        !          3606: int CRecordset::GetBoundParamIndex(void* pv)
        !          3607: {
        !          3608:        void* pvIndex;
        !          3609: 
        !          3610:        if (!m_mapParamIndex.Lookup(pv, pvIndex))
        !          3611:                return -1;
        !          3612:        else
        !          3613:                // Cached value in data not ptr
        !          3614:                return (int)(INT_PTR)pvIndex;
        !          3615: }
        !          3616: 
        !          3617: short CRecordset::GetFieldIndexByName(LPCTSTR lpszFieldName)
        !          3618: {
        !          3619:        short nIndex;
        !          3620:        for (nIndex = 0; nIndex < GetODBCFieldCount(); nIndex++)
        !          3621:        {
        !          3622:                if (m_rgODBCFieldInfos[nIndex].m_strName == lpszFieldName)
        !          3623:                        break;
        !          3624:        }
        !          3625: 
        !          3626:        // Check if field name found
        !          3627:        if (nIndex == GetODBCFieldCount())
        !          3628:                ThrowDBException(AFX_SQL_ERROR_FIELD_NOT_FOUND);
        !          3629: 
        !          3630:        return nIndex;
        !          3631: }
        !          3632: 
        !          3633: LONG_PTR* CRecordset::GetFieldLengthBuffer(DWORD nField, int nFieldType)
        !          3634: {
        !          3635:        if (nFieldType == CFieldExchange::outputColumn)
        !          3636:        {
        !          3637:                ASSERT(nField < m_nFields);
        !          3638:                if(nField >= m_nFields)
        !          3639:                        AfxThrowUserException();
        !          3640:                return &m_rgFieldInfos[nField].m_nLength;
        !          3641:        }
        !          3642:        else
        !          3643:        {
        !          3644:                ASSERT(nField < m_nParams);
        !          3645:                if(nField >= m_nParams)
        !          3646:                        AfxThrowUserException();
        !          3647:                return &m_plParamLength[nField];
        !          3648:        }
        !          3649: }
        !          3650: 
        !          3651: BYTE CRecordset::GetFieldStatus(DWORD nField)
        !          3652: {
        !          3653:        ASSERT(nField < m_nFields);
        !          3654:        if(nField >= m_nFields)
        !          3655:                AfxThrowUserException();
        !          3656: 
        !          3657:        return m_pbFieldFlags[nField];
        !          3658: }
        !          3659: 
        !          3660: void CRecordset::SetFieldStatus(DWORD nField, BYTE bFlags)
        !          3661: {
        !          3662:        ASSERT(nField < m_nFields);
        !          3663:        if(nField >= m_nFields)
        !          3664:                AfxThrowUserException();
        !          3665: 
        !          3666:        m_pbFieldFlags[nField] |= bFlags;
        !          3667: }
        !          3668: 
        !          3669: void CRecordset::ClearFieldStatus()
        !          3670: {
        !          3671:        memset(m_pbFieldFlags, 0, m_nFields);
        !          3672: }
        !          3673: 
        !          3674: BOOL CRecordset::IsFieldStatusDirty(DWORD nField) const
        !          3675: {
        !          3676:        ASSERT(nField < m_nFields);
        !          3677:        if(nField >= m_nFields)
        !          3678:                AfxThrowUserException();
        !          3679: 
        !          3680:        return m_pbFieldFlags[nField] & AFX_SQL_FIELD_FLAG_DIRTY;
        !          3681: }
        !          3682: 
        !          3683: void CRecordset::SetDirtyFieldStatus(DWORD nField)
        !          3684: {
        !          3685:        ASSERT(nField < m_nFields);
        !          3686:        if(nField >= m_nFields)
        !          3687:                AfxThrowUserException();
        !          3688: 
        !          3689:        m_pbFieldFlags[nField] |= AFX_SQL_FIELD_FLAG_DIRTY;
        !          3690: }
        !          3691: 
        !          3692: void CRecordset::ClearDirtyFieldStatus(DWORD nField)
        !          3693: {
        !          3694:        ASSERT(nField < m_nFields);
        !          3695:        if(nField >= m_nFields)
        !          3696:                AfxThrowUserException();
        !          3697: 
        !          3698:        m_pbFieldFlags[nField] &= ~AFX_SQL_FIELD_FLAG_DIRTY;
        !          3699: }
        !          3700: 
        !          3701: BOOL CRecordset::IsFieldStatusNull(DWORD nField) const
        !          3702: {
        !          3703:        ASSERT(nField < m_nFields);
        !          3704:        if(nField >= m_nFields)
        !          3705:                AfxThrowUserException();
        !          3706: 
        !          3707:        return m_pbFieldFlags[nField] & AFX_SQL_FIELD_FLAG_NULL;
        !          3708: }
        !          3709: 
        !          3710: void CRecordset::SetNullFieldStatus(DWORD nField)
        !          3711: {
        !          3712:        ASSERT(nField < m_nFields);
        !          3713:        if(nField >= m_nFields)
        !          3714:                AfxThrowUserException();
        !          3715: 
        !          3716:        m_pbFieldFlags[nField] |= AFX_SQL_FIELD_FLAG_NULL;
        !          3717: }
        !          3718: 
        !          3719: void CRecordset::ClearNullFieldStatus(DWORD nField)
        !          3720: {
        !          3721:        ASSERT(nField < m_nFields);
        !          3722:        if(nField >= m_nFields)
        !          3723:                AfxThrowUserException();
        !          3724: 
        !          3725:        m_pbFieldFlags[nField] &= ~AFX_SQL_FIELD_FLAG_NULL;
        !          3726: }
        !          3727: 
        !          3728: BOOL CRecordset::IsParamStatusNull(DWORD nParam) const
        !          3729: {
        !          3730:        ASSERT(nParam < m_nParams);
        !          3731:        if(nParam >= m_nParams)
        !          3732:                AfxThrowUserException();
        !          3733: 
        !          3734:        return m_pbParamFlags[nParam] & AFX_SQL_FIELD_FLAG_NULL;
        !          3735: }
        !          3736: 
        !          3737: void CRecordset::SetNullParamStatus(DWORD nParam)
        !          3738: {
        !          3739:        ASSERT(nParam < m_nParams);
        !          3740:        if(nParam >= m_nParams)
        !          3741:                AfxThrowUserException();
        !          3742: 
        !          3743:        m_pbParamFlags[nParam] |= AFX_SQL_FIELD_FLAG_NULL;
        !          3744: }
        !          3745: 
        !          3746: void CRecordset::ClearNullParamStatus(DWORD nParam)
        !          3747: {
        !          3748:        ASSERT(nParam < m_nParams);
        !          3749:        if(nParam >= m_nParams)
        !          3750:                AfxThrowUserException();
        !          3751: 
        !          3752:        m_pbParamFlags[nParam] &= ~AFX_SQL_FIELD_FLAG_NULL;
        !          3753: }
        !          3754: 
        !          3755: BOOL CRecordset::IsFieldNullable(DWORD nField) const
        !          3756: {
        !          3757:        ASSERT(nField <= INT_MAX);
        !          3758:        if(nField > INT_MAX)
        !          3759:                AfxThrowUserException();
        !          3760:        ASSERT((long)nField < GetODBCFieldCount());
        !          3761:        if((long)nField >= GetODBCFieldCount())
        !          3762:                AfxThrowUserException();
        !          3763: 
        !          3764:        // return TRUE if nulls allowed or if not known
        !          3765:        return m_rgODBCFieldInfos[nField].m_nNullability != SQL_NO_NULLS;
        !          3766: }
        !          3767: 
        !          3768: UINT CRecordset::BindParams(HSTMT hstmt)
        !          3769: {
        !          3770:        ASSERT_VALID(this);
        !          3771:        ASSERT(m_hstmt != SQL_NULL_HSTMT);
        !          3772: 
        !          3773:        CFieldExchange fx(CFieldExchange::BindParam, this);
        !          3774:        fx.m_hstmt = hstmt;
        !          3775: 
        !          3776:        DoFieldExchange(&fx);
        !          3777: 
        !          3778:        return fx.m_nParams;
        !          3779: }
        !          3780: 
        !          3781: void CRecordset::RebindParams(HSTMT hstmt)
        !          3782: {
        !          3783:        ASSERT_VALID(this);
        !          3784:        ASSERT(m_hstmt != SQL_NULL_HSTMT);
        !          3785: 
        !          3786:        if (m_bRebindParams)
        !          3787:        {
        !          3788:                CFieldExchange fx(CFieldExchange::RebindParam, this);
        !          3789:                fx.m_hstmt = hstmt;
        !          3790: 
        !          3791:                DoFieldExchange(&fx);
        !          3792:        }
        !          3793: }
        !          3794: 
        !          3795: UINT CRecordset::BindFieldsToColumns()
        !          3796: {
        !          3797:        ASSERT_VALID(this);
        !          3798:        ASSERT(m_hstmt != SQL_NULL_HSTMT);
        !          3799: 
        !          3800:        ASSERT(m_nFieldsBound == 0);
        !          3801:        ASSERT(m_nFields != 0 && m_nFields <= 255);
        !          3802: 
        !          3803:        CFieldExchange fx(CFieldExchange::BindFieldToColumn, this);
        !          3804:        fx.m_hstmt = m_hstmt;
        !          3805: 
        !          3806:        // Binding depends on fetch type
        !          3807:        if (m_dwOptions & useMultiRowFetch)
        !          3808:                DoBulkFieldExchange(&fx);
        !          3809:        else
        !          3810:                DoFieldExchange(&fx);
        !          3811: 
        !          3812:        return fx.m_nFields;
        !          3813: }
        !          3814: 
        !          3815: void CRecordset::BindFieldsForUpdate()
        !          3816: {
        !          3817:        ASSERT_VALID(this);
        !          3818: 
        !          3819:        if (m_nEditMode == edit || m_nEditMode == addnew)
        !          3820:        {
        !          3821:                CFieldExchange fx(CFieldExchange::BindFieldForUpdate, this);
        !          3822:                fx.m_hstmt = m_hstmt;
        !          3823:                DoFieldExchange(&fx);
        !          3824:        }
        !          3825: }
        !          3826: 
        !          3827: void CRecordset::UnbindFieldsForUpdate()
        !          3828: {
        !          3829:        ASSERT_VALID(this);
        !          3830: 
        !          3831:        if (m_nEditMode == edit || m_nEditMode == addnew)
        !          3832:        {
        !          3833:                CFieldExchange fx(CFieldExchange::UnbindFieldForUpdate, this);
        !          3834:                fx.m_hstmt = m_hstmt;
        !          3835:                DoFieldExchange(&fx);
        !          3836:        }
        !          3837: }
        !          3838: 
        !          3839: // After Move operation, reflect status and lengths of columns in RFX fields
        !          3840: void CRecordset::Fixups()
        !          3841: {
        !          3842:        ASSERT_VALID(this);
        !          3843:        ASSERT(m_hstmt != SQL_NULL_HSTMT);
        !          3844:        ASSERT(m_nFieldsBound != 0);
        !          3845: 
        !          3846:        CFieldExchange fx(CFieldExchange::Fixup, this);
        !          3847:        fx.m_hstmt = m_hstmt;
        !          3848:        DoFieldExchange(&fx);
        !          3849: }
        !          3850: 
        !          3851: UINT CRecordset::AppendNames(CString* pstr, LPCTSTR lpszSeparator)
        !          3852: {
        !          3853:        ASSERT_VALID(this);
        !          3854:        ASSERT(AfxIsValidAddress(pstr, sizeof(CString)));
        !          3855:        ASSERT(AfxIsValidString(lpszSeparator));
        !          3856:        ASSERT(m_hstmt != SQL_NULL_HSTMT);
        !          3857: 
        !          3858:        CFieldExchange fx(CFieldExchange::Name, this);
        !          3859:        fx.m_pstr = pstr;
        !          3860:        fx.m_lpszSeparator = lpszSeparator;
        !          3861: 
        !          3862:        if (m_dwOptions & useMultiRowFetch)
        !          3863:                DoBulkFieldExchange(&fx);
        !          3864:        else
        !          3865:                DoFieldExchange(&fx);
        !          3866: 
        !          3867:        return fx.m_nFields;
        !          3868: }
        !          3869: 
        !          3870: // For each "changed" column, append <column name>=<column value>,
        !          3871: UINT CRecordset::AppendNamesValues(HSTMT hstmt, CString* pstr,
        !          3872:        LPCTSTR lpszSeparator)
        !          3873: {
        !          3874:        ASSERT_VALID(this);
        !          3875:        ASSERT(AfxIsValidAddress(pstr, sizeof(CString)));
        !          3876:        ASSERT(AfxIsValidString(lpszSeparator));
        !          3877:        ASSERT(m_hstmt != SQL_NULL_HSTMT);
        !          3878:        ASSERT(hstmt != SQL_NULL_HSTMT);
        !          3879: 
        !          3880:        CFieldExchange fx(CFieldExchange::NameValue, this);
        !          3881:        fx.m_pstr = pstr;
        !          3882:        fx.m_lpszSeparator = lpszSeparator;
        !          3883:        fx.m_hstmt = hstmt;
        !          3884: 
        !          3885:        DoFieldExchange(&fx);
        !          3886: 
        !          3887:        return fx.m_nFields;
        !          3888: }
        !          3889: 
        !          3890: // For each "changed" column, append <column value>,
        !          3891: UINT CRecordset::AppendValues(HSTMT hstmt, CString* pstr,
        !          3892:        LPCTSTR lpszSeparator)
        !          3893: {
        !          3894:        ASSERT_VALID(this);
        !          3895:        ASSERT(AfxIsValidAddress(pstr, sizeof(CString)));
        !          3896:        ASSERT(AfxIsValidString(lpszSeparator));
        !          3897:        ASSERT(m_hstmt != SQL_NULL_HSTMT);
        !          3898:        ASSERT(hstmt != SQL_NULL_HSTMT);
        !          3899: 
        !          3900:        CFieldExchange fx(CFieldExchange::Value, this);
        !          3901:        fx.m_pstr = pstr;
        !          3902:        fx.m_lpszSeparator = lpszSeparator;
        !          3903:        fx.m_hstmt = hstmt;
        !          3904: 
        !          3905:        DoFieldExchange(&fx);
        !          3906: 
        !          3907:        return fx.m_nFields;
        !          3908: }
        !          3909: 
        !          3910: 
        !          3911: // Cache fields of copy buffer in a CMemFile with CArchive
        !          3912: void CRecordset::StoreFields()
        !          3913: {
        !          3914:        ASSERT_VALID(this);
        !          3915:        ASSERT(m_nFieldsBound != 0);
        !          3916: 
        !          3917:        CFieldExchange fx(CFieldExchange::StoreField, this);
        !          3918:        DoFieldExchange(&fx);
        !          3919: }
        !          3920: 
        !          3921: // Restore fields of copy buffer from archived memory file
        !          3922: void CRecordset::LoadFields()
        !          3923: {
        !          3924:        ASSERT_VALID(this);
        !          3925:        ASSERT(m_nFieldsBound != 0);
        !          3926: 
        !          3927:        // Must clear out the old status
        !          3928:        ClearFieldStatus();
        !          3929: 
        !          3930:        CFieldExchange fx(CFieldExchange::LoadField, this);
        !          3931:        DoFieldExchange(&fx);
        !          3932: }
        !          3933: 
        !          3934: void CRecordset::MarkForUpdate()
        !          3935: {
        !          3936:        ASSERT_VALID(this);
        !          3937: 
        !          3938:        CFieldExchange fx(CFieldExchange::MarkForUpdate, this);
        !          3939:        DoFieldExchange(&fx);
        !          3940: }
        !          3941: 
        !          3942: void CRecordset::MarkForAddNew()
        !          3943: {
        !          3944:        ASSERT_VALID(this);
        !          3945: 
        !          3946:        CFieldExchange fx(CFieldExchange::MarkForAddNew, this);
        !          3947:        DoFieldExchange(&fx);
        !          3948: }
        !          3949: 
        !          3950: void CRecordset::AllocDataCache()
        !          3951: {
        !          3952:        ASSERT_VALID(this);
        !          3953: 
        !          3954:        CFieldExchange fx(CFieldExchange::AllocCache, this);
        !          3955:        DoFieldExchange(&fx);
        !          3956: }
        !          3957: 
        !          3958: void CRecordset::FreeDataCache()
        !          3959: {
        !          3960:        ASSERT_VALID(this);
        !          3961: 
        !          3962:        CFieldInfo* pInfo;
        !          3963: 
        !          3964:        for (DWORD nField = 0; nField < m_nFields; nField++)
        !          3965:        {
        !          3966:                pInfo = &m_rgFieldInfos[nField];
        !          3967: 
        !          3968:                switch(pInfo->m_nDataType)
        !          3969:                {
        !          3970:                default:
        !          3971:                        ASSERT(FALSE);
        !          3972:                        // fall through
        !          3973: 
        !          3974:                // Data not cached
        !          3975:                case AFX_RFX_NO_TYPE:
        !          3976:                        break;
        !          3977: 
        !          3978:                // Types cached by value (sizeof(TYPE) <= sizeof(void*))
        !          3979:                case AFX_RFX_BOOL:
        !          3980:                case AFX_RFX_BYTE:
        !          3981:                case AFX_RFX_INT:
        !          3982:                case AFX_RFX_LONG:
        !          3983:                case AFX_RFX_SINGLE:
        !          3984:                        pInfo->m_pvDataCache = NULL;
        !          3985:                        break;
        !          3986: 
        !          3987:                case AFX_RFX_WTEXT:
        !          3988:                        delete reinterpret_cast<CStringW *>(pInfo->m_pvDataCache);
        !          3989:                        pInfo->m_pvDataCache = NULL;
        !          3990:                        break;
        !          3991: 
        !          3992:                case AFX_RFX_ATEXT:
        !          3993:                        delete reinterpret_cast<CStringA *>(pInfo->m_pvDataCache);
        !          3994:                        pInfo->m_pvDataCache = NULL;
        !          3995:                        break;
        !          3996: 
        !          3997:                case AFX_RFX_LPWSTR:
        !          3998:                        delete [] LPWSTR(pInfo->m_pvDataCache);
        !          3999:                        pInfo->m_pvDataCache = NULL;
        !          4000:                        break;
        !          4001: 
        !          4002:                case AFX_RFX_LPASTR:
        !          4003:                        delete [] LPSTR(pInfo->m_pvDataCache);
        !          4004:                        pInfo->m_pvDataCache = NULL;
        !          4005:                        break;
        !          4006: 
        !          4007:                case AFX_RFX_DOUBLE:
        !          4008:                        delete (double*)pInfo->m_pvDataCache;
        !          4009:                        pInfo->m_pvDataCache = NULL;
        !          4010:                        break;
        !          4011: 
        !          4012:                case AFX_RFX_BIGINT:
        !          4013:                        delete (LONGLONG*)pInfo->m_pvDataCache;
        !          4014:                        pInfo->m_pvDataCache = NULL;
        !          4015:                        break;
        !          4016: 
        !          4017:                case AFX_RFX_TIMESTAMP:
        !          4018:                        delete (TIMESTAMP_STRUCT*)pInfo->m_pvDataCache;
        !          4019:                        pInfo->m_pvDataCache = NULL;
        !          4020:                        break;
        !          4021: 
        !          4022:                case AFX_RFX_OLEDATE:
        !          4023:                        delete (COleDateTime*)pInfo->m_pvDataCache;
        !          4024:                        pInfo->m_pvDataCache = NULL;
        !          4025:                        break;
        !          4026: 
        !          4027:                case AFX_RFX_DATE:
        !          4028:                        delete (CTime*)pInfo->m_pvDataCache;
        !          4029:                        pInfo->m_pvDataCache = NULL;
        !          4030:                        break;
        !          4031: 
        !          4032:                case AFX_RFX_BINARY:
        !          4033:                        delete (CByteArray*)pInfo->m_pvDataCache;
        !          4034:                        pInfo->m_pvDataCache = NULL;
        !          4035:                        break;
        !          4036:                }
        !          4037:        }
        !          4038: }
        !          4039: 
        !          4040: #ifdef _DEBUG
        !          4041: void CRecordset::DumpFields(CDumpContext& dc) const
        !          4042: {
        !          4043:        CFieldExchange fx(CFieldExchange::DumpField, (CRecordset *)this);
        !          4044:        fx.m_pdcDump = &dc;
        !          4045:        ((CRecordset *)this)->DoFieldExchange(&fx);
        !          4046: }
        !          4047: #endif // _DEBUG
        !          4048: 
        !          4049: 
        !          4050: // Perform Update (m_nModeEdit == edit), Insert (addnew),
        !          4051: // or Delete (noMode)
        !          4052: BOOL CRecordset::UpdateInsertDelete()
        !          4053: {
        !          4054:        ASSERT_VALID(this);
        !          4055:        ASSERT(m_hstmt != SQL_NULL_HSTMT);
        !          4056: 
        !          4057:        // Delete mode
        !          4058:        if (m_nEditMode == addnew)
        !          4059:        {
        !          4060:                if (!m_bAppendable)
        !          4061:                {
        !          4062:                        TRACE(traceDatabase, 0, _T("Error: attempted to add a record to a read only recordset.\n"));
        !          4063:                        ThrowDBException(AFX_SQL_ERROR_RECORDSET_READONLY);
        !          4064:                }
        !          4065:        }
        !          4066:        else
        !          4067:        {
        !          4068:                if (!m_bUpdatable)
        !          4069:                {
        !          4070:                        TRACE(traceDatabase, 0, _T("Error: attempted to update a read only recordset.\n"));
        !          4071:                        ThrowDBException(AFX_SQL_ERROR_RECORDSET_READONLY);
        !          4072:                }
        !          4073: 
        !          4074:                // Requires currency
        !          4075:                if (m_bEOF || m_bBOF || m_bDeleted)
        !          4076:                {
        !          4077:                        TRACE(traceDatabase, 0, _T("Error: attempting to update recordset - but no record is current.\n"));
        !          4078:                        ThrowDBException(AFX_SQL_ERROR_NO_CURRENT_RECORD);
        !          4079:                }
        !          4080:        }
        !          4081: 
        !          4082:        // Update or AddNew is NOP w/o at least 1 changed field
        !          4083:        if (m_nEditMode != noMode && !IsFieldDirty(NULL))
        !          4084:                return FALSE;
        !          4085: 
        !          4086:        if (!m_bUseUpdateSQL)
        !          4087:        {
        !          4088:                // Most efficient update method
        !          4089:                ExecuteSetPosUpdate();
        !          4090:        }
        !          4091:        else
        !          4092:        {
        !          4093: 
        !          4094:                BOOL bNullHstmt = (m_hstmtUpdate == NULL);
        !          4095: 
        !          4096:                // Make sure m_hstmtUpdate allocated
        !          4097:                PrepareUpdateHstmt();
        !          4098: 
        !          4099:                // Build update SQL unless optimizing bulk adds and hstmt not NULL
        !          4100:                if(!(m_dwOptions & optimizeBulkAdd) || bNullHstmt)
        !          4101:                {
        !          4102:                        // Mark as first bulk add if optimizing
        !          4103:                        if(m_dwOptions & optimizeBulkAdd)
        !          4104:                        {
        !          4105:                                m_dwOptions &= ~optimizeBulkAdd;
        !          4106:                                m_dwOptions |= firstBulkAdd;
        !          4107:                        }
        !          4108:                        BuildUpdateSQL();
        !          4109: 
        !          4110:                        // Reset flag marking first optimization
        !          4111:                        if(m_dwOptions & firstBulkAdd)
        !          4112:                        {
        !          4113:                                m_dwOptions &= ~firstBulkAdd;
        !          4114:                                m_dwOptions |= optimizeBulkAdd;
        !          4115:                        }
        !          4116:                }
        !          4117:                else
        !          4118:                {
        !          4119:                        // Just reset the data lengths and datetime proxies
        !          4120:                        AppendValues(m_hstmtUpdate, &m_strUpdateSQL, _afxComma);
        !          4121:                }
        !          4122: 
        !          4123:                ExecuteUpdateSQL();
        !          4124:        }
        !          4125: 
        !          4126:        TRY
        !          4127:        {
        !          4128:                // Delete
        !          4129:                switch (m_nEditMode)
        !          4130:                {
        !          4131:                case noMode:
        !          4132:                        // Decrement record count
        !          4133:                        if (m_lCurrentRecord >= 0)
        !          4134:                        {
        !          4135:                                if (m_lRecordCount > 0)
        !          4136:                                        m_lRecordCount--;
        !          4137:                                m_lCurrentRecord--;
        !          4138:                        }
        !          4139: 
        !          4140:                        // indicate on a deleted record
        !          4141:                        m_bDeleted = TRUE;
        !          4142:                        // Set all fields to NULL
        !          4143:                        SetFieldNull(NULL);
        !          4144:                        break;
        !          4145: 
        !          4146:                case addnew:
        !          4147:                        // The recordset may no longer be empty (depending on driver behavior)
        !          4148:                        // reset m_bEOF/m_bBOF so Move can be called
        !          4149:                        m_bEOF = m_bBOF = FALSE;
        !          4150: 
        !          4151:                        if (m_pDatabase->m_bIncRecordCountOnAdd && m_lCurrentRecord >= 0)
        !          4152:                        {
        !          4153:                                if (m_lRecordCount != -1)
        !          4154:                                        m_lRecordCount++;
        !          4155:                                m_lCurrentRecord++;
        !          4156:                        }
        !          4157: 
        !          4158:                        // Reset the data cache if necessary
        !          4159:                        if (m_bCheckCacheForDirtyFields && m_nFields > 0)
        !          4160:                                LoadFields();
        !          4161:                        break;
        !          4162: 
        !          4163:                case edit:
        !          4164:                        break;
        !          4165:                }
        !          4166: 
        !          4167:                // Reset the edit mode
        !          4168:                m_nEditMode = noMode;
        !          4169:        }
        !          4170:        END_TRY
        !          4171: 
        !          4172:        // Unless doing a bulk AddNew, reset the dirty flags
        !          4173:        if (m_bCheckCacheForDirtyFields && !(m_dwOptions & optimizeBulkAdd))
        !          4174:                SetFieldDirty(NULL, FALSE);
        !          4175: 
        !          4176:        // Must return TRUE since record updated
        !          4177:        return TRUE;
        !          4178: }
        !          4179: 
        !          4180: // Fetch and alloc algorithms for CLongBinary data when length unknown
        !          4181: SQLLEN CRecordset::GetLBFetchSize(SQLLEN lOldSize)
        !          4182: {
        !          4183:        // Make it twice as big
        !          4184:        return lOldSize << 1;
        !          4185: }
        !          4186: 
        !          4187: SQLLEN CRecordset::GetLBReallocSize(SQLLEN lOldSize)
        !          4188: {
        !          4189:        // Make it twice as big (no effect if less than fetch size)
        !          4190:        return lOldSize << 1;
        !          4191: }
        !          4192: 
        !          4193: short PASCAL CRecordset::GetDefaultFieldType(short nSQLType)
        !          4194: {
        !          4195:        short nFieldType = 0;
        !          4196: 
        !          4197:        switch (nSQLType)
        !          4198:        {
        !          4199:        case SQL_BIT:
        !          4200:                nFieldType = SQL_C_BIT;
        !          4201:                break;
        !          4202: 
        !          4203:        case SQL_TINYINT:
        !          4204:                nFieldType = SQL_C_UTINYINT;
        !          4205:                break;
        !          4206: 
        !          4207:        case SQL_SMALLINT:
        !          4208:                nFieldType = SQL_C_SSHORT;
        !          4209:                break;
        !          4210: 
        !          4211:        case SQL_INTEGER:
        !          4212:                nFieldType = SQL_C_SLONG;
        !          4213:                break;
        !          4214: 
        !          4215:        case SQL_REAL:
        !          4216:                nFieldType = SQL_C_FLOAT;
        !          4217:                break;
        !          4218: 
        !          4219:        case SQL_FLOAT:
        !          4220:        case SQL_DOUBLE:
        !          4221:                nFieldType = SQL_C_DOUBLE;
        !          4222:                break;
        !          4223: 
        !          4224:        case SQL_DATE:
        !          4225:        case SQL_TIME:
        !          4226:        case SQL_TIMESTAMP:
        !          4227:                nFieldType = SQL_C_TIMESTAMP;
        !          4228:                break;
        !          4229: 
        !          4230:        case SQL_NUMERIC:
        !          4231:        case SQL_DECIMAL:
        !          4232:        case SQL_BIGINT:
        !          4233:                nFieldType = SQL_C_TCHAR;
        !          4234:                break;
        !          4235: 
        !          4236:        case SQL_CHAR:
        !          4237:        case SQL_VARCHAR:
        !          4238:        case SQL_LONGVARCHAR:
        !          4239:                nFieldType = SQL_C_CHAR;
        !          4240:                break;
        !          4241: 
        !          4242:        case SQL_WCHAR:
        !          4243:        case SQL_WVARCHAR:
        !          4244:        case SQL_WLONGVARCHAR:
        !          4245:                nFieldType = SQL_C_WCHAR;
        !          4246:                break;
        !          4247: 
        !          4248:        case SQL_BINARY:
        !          4249:        case SQL_VARBINARY:
        !          4250:        case SQL_LONGVARBINARY:
        !          4251:                nFieldType = SQL_C_BINARY;
        !          4252:                break;
        !          4253: 
        !          4254:        default:
        !          4255:                ASSERT(FALSE);
        !          4256:        }
        !          4257: 
        !          4258:        return nFieldType;
        !          4259: }
        !          4260: 
        !          4261: void* PASCAL CRecordset::GetDataBuffer(CDBVariant& varValue,
        !          4262:        short nFieldType, SQLLEN* pnLen, short nSQLType, SQLULEN nPrecision)
        !          4263: {
        !          4264:        void* pvData = NULL;
        !          4265: 
        !          4266:        switch (nFieldType)
        !          4267:        {
        !          4268:        case SQL_C_BIT:
        !          4269:                pvData = &varValue.m_boolVal;
        !          4270:                varValue.m_dwType = DBVT_BOOL;
        !          4271:                varValue.m_boolVal = 0;  // SQL will only set the low byte, so we need
        !          4272:                        // to initialize the whole thing to zero.
        !          4273:                *pnLen = sizeof(varValue.m_boolVal);
        !          4274:                break;
        !          4275: 
        !          4276:        case SQL_C_UTINYINT:
        !          4277:                pvData = &varValue.m_chVal;
        !          4278:                varValue.m_dwType = DBVT_UCHAR;
        !          4279:                *pnLen = sizeof(varValue.m_chVal);
        !          4280:                break;
        !          4281: 
        !          4282:        case SQL_C_SSHORT:
        !          4283:                pvData = &varValue.m_iVal;
        !          4284:                varValue.m_dwType = DBVT_SHORT;
        !          4285:                *pnLen = sizeof(varValue.m_iVal);
        !          4286:                break;
        !          4287: 
        !          4288:        case SQL_C_SLONG:
        !          4289:                pvData = &varValue.m_lVal;
        !          4290:                varValue.m_dwType = DBVT_LONG;
        !          4291:                *pnLen = sizeof(varValue.m_lVal);
        !          4292:                break;
        !          4293: 
        !          4294:        case SQL_C_FLOAT:
        !          4295:                pvData = &varValue.m_fltVal;
        !          4296:                varValue.m_dwType = DBVT_SINGLE;
        !          4297:                *pnLen = sizeof(varValue.m_fltVal);
        !          4298:                break;
        !          4299: 
        !          4300:        case SQL_C_DOUBLE:
        !          4301:                pvData = &varValue.m_dblVal;
        !          4302:                varValue.m_dwType = DBVT_DOUBLE;
        !          4303:                *pnLen = sizeof(varValue.m_dblVal);
        !          4304:                break;
        !          4305: 
        !          4306:        case SQL_C_TIMESTAMP:
        !          4307:                pvData = varValue.m_pdate = new TIMESTAMP_STRUCT;
        !          4308:                varValue.m_dwType = DBVT_DATE;
        !          4309:                *pnLen = sizeof(*varValue.m_pdate);
        !          4310:                break;
        !          4311: 
        !          4312:        case SQL_C_CHAR:
        !          4313:                varValue.m_pstringA = new CStringA;
        !          4314:                varValue.m_dwType = DBVT_ASTRING;
        !          4315: 
        !          4316:                *pnLen = GetTextLen(nSQLType, nPrecision);
        !          4317:                if (*pnLen > INT_MAX)
        !          4318:                        AfxThrowMemoryException();
        !          4319:                pvData = varValue.m_pstringA->GetBufferSetLength(int(*pnLen));  
        !          4320:                break;
        !          4321: 
        !          4322:        case SQL_C_WCHAR:
        !          4323:                varValue.m_pstringW = new CStringW;
        !          4324:                varValue.m_dwType = DBVT_WSTRING;
        !          4325: 
        !          4326:                *pnLen = GetTextLen(nSQLType, nPrecision);
        !          4327:                if (*pnLen > INT_MAX)
        !          4328:                        AfxThrowMemoryException();
        !          4329: 
        !          4330:                pvData = varValue.m_pstringW->GetBufferSetLength(int(*pnLen)); 
        !          4331:                break;
        !          4332: 
        !          4333:        case SQL_C_BINARY:
        !          4334:                varValue.m_pbinary = new CLongBinary;
        !          4335:                varValue.m_dwType = DBVT_BINARY;
        !          4336: 
        !          4337:                if (nSQLType == SQL_LONGVARBINARY)
        !          4338:                {
        !          4339:                        // pvData can't be NULL, so nLen must be at least 1
        !          4340:                        *pnLen = 1;
        !          4341:                }
        !          4342:                else
        !          4343:                {
        !          4344:                        // better know the length!
        !          4345:                        ASSERT(nPrecision != 0);
        !          4346:                        *pnLen = nPrecision;
        !          4347:                }
        !          4348: 
        !          4349:                varValue.m_pbinary->m_hData = ::GlobalAlloc(GMEM_MOVEABLE, *pnLen);
        !          4350:                varValue.m_pbinary->m_dwDataLength = *pnLen;
        !          4351: 
        !          4352:                pvData = ::GlobalLock(varValue.m_pbinary->m_hData);
        !          4353:                break;
        !          4354: 
        !          4355:        default:
        !          4356:                ASSERT(FALSE);
        !          4357:        }
        !          4358: 
        !          4359:        return pvData;
        !          4360: }
        !          4361: 
        !          4362: SQLLEN PASCAL CRecordset::GetTextLen(short nSQLType, SQLULEN nPrecision)
        !          4363: {
        !          4364:        SQLLEN nLen;
        !          4365: 
        !          4366:        if (nSQLType == SQL_LONGVARCHAR || nSQLType == SQL_LONGVARBINARY)
        !          4367:        {
        !          4368:                // Use a dummy length of 1 (will just get NULL terminator)
        !          4369:                nLen = 1;
        !          4370:        }
        !          4371:        else if(nSQLType == SQL_WLONGVARCHAR)
        !          4372:        {
        !          4373:                // Use a dummy length of DBCS 1
        !          4374:                nLen = sizeof(WCHAR);
        !          4375:        }
        !          4376:        else
        !          4377:        {
        !          4378:                // better know the length
        !          4379:                //ASSERT(nPrecision >= 0);
        !          4380: 
        !          4381:                nLen = nPrecision + 1;
        !          4382: 
        !          4383:                switch (nSQLType)
        !          4384:                {
        !          4385:                case SQL_INTEGER:
        !          4386:                case SQL_SMALLINT:
        !          4387:                        nLen++;  // '-' sign
        !          4388:                        break;
        !          4389: 
        !          4390:                case SQL_NUMERIC:
        !          4391:                case SQL_DECIMAL:
        !          4392:                case SQL_FLOAT:
        !          4393:                case SQL_REAL:
        !          4394:                case SQL_DOUBLE:
        !          4395:                        nLen += 2;  // '-' sign and decimal point
        !          4396:                        break;
        !          4397:                }
        !          4398:        }
        !          4399: 
        !          4400:        return nLen;
        !          4401: }
        !          4402: 
        !          4403: SQLLEN PASCAL CRecordset::GetData(CDatabase* pdb, HSTMT hstmt,
        !          4404:        short nFieldIndex, short nFieldType, LPVOID pvData, SQLLEN nLen,
        !          4405:        short nSQLType)
        !          4406: {
        !          4407:        UNUSED(nSQLType);
        !          4408: 
        !          4409:        SQLLEN nActualSize;
        !          4410:        RETCODE nRetCode;
        !          4411: 
        !          4412:        // Retrieve the column in question
        !          4413:        AFX_ODBC_CALL(::SQLGetData(hstmt, nFieldIndex,
        !          4414:                nFieldType, pvData, nLen, &nActualSize));
        !          4415: 
        !          4416:        // Ignore data truncated warnings for long data
        !          4417:        if (nRetCode == SQL_SUCCESS_WITH_INFO)
        !          4418:        {
        !          4419: #ifdef _DEBUG
        !          4420:                CDBException e(nRetCode);
        !          4421: 
        !          4422:                // Build the error string but don't send nuisance output to TRACE window
        !          4423:                e.BuildErrorString(pdb, hstmt, FALSE);
        !          4424: 
        !          4425:                // If not a data truncated warning on long var column,
        !          4426:                // then send debug output
        !          4427:                if ((nSQLType != SQL_LONGVARCHAR &&
        !          4428:                        nSQLType != SQL_WLONGVARCHAR &&
        !          4429:                        nSQLType != SQL_LONGVARBINARY) ||
        !          4430:                        (e.m_strStateNativeOrigin.Find(_afxDataTruncated) < 0))
        !          4431:                {
        !          4432:                        TRACE(traceDatabase, 0, _T("Warning: ODBC Success With Info on field %d.\n"),
        !          4433:                                nFieldIndex - 1);
        !          4434:                        e.TraceErrorMessage(e.m_strError);
        !          4435:                        e.TraceErrorMessage(e.m_strStateNativeOrigin);
        !          4436:                }
        !          4437: #endif // _DEBUG
        !          4438:        }
        !          4439:        else if (nRetCode == SQL_NO_DATA_FOUND)
        !          4440:        {
        !          4441:                TRACE(traceDatabase, 0, _T("Error: GetFieldValue operation failed on field %d.\n"));
        !          4442:                TRACE(traceDatabase, 0, _T("\tData already fetched for this field.\n"),
        !          4443:                        nFieldIndex - 1);
        !          4444:                AfxThrowDBException(nRetCode, pdb, hstmt);
        !          4445:        }
        !          4446:        else if (nRetCode != SQL_SUCCESS)
        !          4447:        {
        !          4448:                TRACE(traceDatabase, 0, _T("Error: GetFieldValue operation failed on field %d.\n"),
        !          4449:                        nFieldIndex - 1);
        !          4450:                AfxThrowDBException(nRetCode, pdb, hstmt);
        !          4451:        }
        !          4452: 
        !          4453:        return nActualSize;
        !          4454: }
        !          4455: 
        !          4456: template<typename StringType>
        !          4457: inline static void PASCAL GetLongCharDataAndCleanup(CDatabase* pdb,
        !          4458:        HSTMT hstmt, short nFieldIndex, SQLLEN nActualSize, LPVOID* ppvData,
        !          4459:        SQLLEN nLen, StringType& strValue, short nSQLType, short nSQLCType)
        !          4460: {
        !          4461:        RETCODE nRetCode;
        !          4462: 
        !          4463:        // Release the buffer now that data has been fetched
        !          4464:        strValue.ReleaseBuffer((int)(nActualSize < nLen ? nActualSize : nLen));
        !          4465: 
        !          4466:        // If long data, may need to call SQLGetData again
        !          4467:        if (nLen <= nActualSize &&
        !          4468:                (nSQLType == SQL_WLONGVARCHAR || nSQLType == SQL_LONGVARCHAR ||
        !          4469:                nSQLType == SQL_LONGVARBINARY))
        !          4470:        {
        !          4471:                // Reallocate the size (this will copy the data)
        !          4472:                if (nActualSize > (INT_MAX-1))
        !          4473:                        AfxThrowMemoryException();
        !          4474:                *ppvData = strValue.GetBufferSetLength((int)nActualSize + 1);
        !          4475: 
        !          4476:                // Get pointer, skipping over original data, but not the NULL
        !          4477:                SQLLEN nOldLen = nLen - 1;
        !          4478:                *ppvData = (BYTE*)*ppvData + nOldLen;
        !          4479:                nLen = nActualSize + 1 - nOldLen;
        !          4480: 
        !          4481:                // Retrieve the column in question
        !          4482:                AFX_ODBC_CALL(::SQLGetData(hstmt, nFieldIndex,
        !          4483:                        nSQLCType, *ppvData, nLen, &nActualSize));
        !          4484:                if (nRetCode == SQL_SUCCESS_WITH_INFO)
        !          4485:                {
        !          4486: #ifdef _DEBUG
        !          4487:                        TRACE(traceDatabase, 0, _T("Warning: ODBC Success With Info on field %d.\n"),
        !          4488:                                nFieldIndex - 1);
        !          4489:                        CDBException e(nRetCode);
        !          4490:                        e.BuildErrorString(pdb, hstmt);
        !          4491: #endif // _DEBUG
        !          4492:                }
        !          4493:                else if (nRetCode != SQL_SUCCESS)
        !          4494:                {
        !          4495:                        TRACE(traceDatabase, 0, _T("Error: GetFieldValue operation failed on field %d.\n"),
        !          4496:                                nFieldIndex - 1);
        !          4497:                        AfxThrowDBException(nRetCode, pdb, hstmt);
        !          4498:                }
        !          4499: 
        !          4500:                // Release the buffer now that data has been fetched
        !          4501:                strValue.ReleaseBuffer((int)(nActualSize + nOldLen));
        !          4502:        }
        !          4503: }
        !          4504: 
        !          4505: void PASCAL CRecordset::GetLongCharDataAndCleanup(CDatabase* pdb, HSTMT hstmt,
        !          4506:        short nFieldIndex, SQLLEN nActualSize, LPVOID* ppvData, SQLLEN nLen,
        !          4507:        CStringW& strValue, short nSQLType, short nSQLCType)
        !          4508: {
        !          4509:        ::GetLongCharDataAndCleanup(pdb, hstmt, nFieldIndex, nActualSize, ppvData, nLen,
        !          4510:                strValue, nSQLType, nSQLCType);
        !          4511: }
        !          4512: void PASCAL CRecordset::GetLongCharDataAndCleanup(CDatabase* pdb, HSTMT hstmt,
        !          4513:        short nFieldIndex, SQLLEN nActualSize, LPVOID* ppvData, SQLLEN nLen,
        !          4514:        CStringA& strValue, short nSQLType, short nSQLCType)
        !          4515: {
        !          4516:        ::GetLongCharDataAndCleanup(pdb, hstmt, nFieldIndex, nActualSize, ppvData, nLen,
        !          4517:                strValue, nSQLType, nSQLCType);
        !          4518: }
        !          4519: 
        !          4520: void PASCAL CRecordset::GetLongBinaryDataAndCleanup(CDatabase* pdb, HSTMT hstmt,
        !          4521:        short nFieldIndex, SQLLEN nActualSize, LPVOID* ppvData, SQLLEN nLen,
        !          4522:        CDBVariant& varValue, short nSQLType)
        !          4523: {
        !          4524:        RETCODE nRetCode;
        !          4525: 
        !          4526:        ::GlobalUnlock(varValue.m_pbinary->m_hData);
        !          4527: 
        !          4528:        // If long data, may need to call SQLGetData again
        !          4529:        if (nLen < nActualSize && nSQLType == SQL_LONGVARBINARY)
        !          4530:        {
        !          4531:                // Reallocate a bigger buffer
        !          4532:                HGLOBAL hOldData = varValue.m_pbinary->m_hData;
        !          4533:                varValue.m_pbinary->m_hData = ::GlobalReAlloc(hOldData,
        !          4534:                        nActualSize, GMEM_MOVEABLE);
        !          4535: 
        !          4536:                // Validate the memory was allocated and can be locked
        !          4537:                if (varValue.m_pbinary->m_hData == NULL)
        !          4538:                {
        !          4539:                        // Restore the old handle (not NULL if Realloc failed)
        !          4540:                        varValue.m_pbinary->m_hData = hOldData;
        !          4541:                        AfxThrowMemoryException();
        !          4542:                }
        !          4543:                varValue.m_pbinary->m_dwDataLength = nActualSize;
        !          4544: 
        !          4545:                // Get pointer, skipping over original data
        !          4546:                *ppvData = (BYTE*)::GlobalLock(varValue.m_pbinary->m_hData) + nLen;
        !          4547:                INT_PTR nOldLen = nLen;
        !          4548:                nLen = nActualSize - nOldLen;
        !          4549: 
        !          4550:                // Retrieve the column in question
        !          4551:                AFX_ODBC_CALL(::SQLGetData(hstmt, nFieldIndex,
        !          4552:                        SQL_C_BINARY, *ppvData, nLen, &nActualSize));
        !          4553:                if (nRetCode == SQL_SUCCESS_WITH_INFO)
        !          4554:                {
        !          4555: #ifdef _DEBUG
        !          4556:                        TRACE(traceDatabase, 0, _T("Warning: ODBC Success With Info on field %d.\n"),
        !          4557:                                nFieldIndex - 1);
        !          4558:                        CDBException e(nRetCode);
        !          4559:                        e.BuildErrorString(pdb, hstmt);
        !          4560: #endif // _DEBUG
        !          4561:                }
        !          4562:                else if (nRetCode != SQL_SUCCESS)
        !          4563:                {
        !          4564:                        TRACE(traceDatabase, 0, _T("Error: GetFieldValue operation failed on field %d.\n"),
        !          4565:                                nFieldIndex - 1);
        !          4566:                        AfxThrowDBException(nRetCode, pdb, hstmt);
        !          4567:                }
        !          4568: 
        !          4569:                ASSERT((int)varValue.m_pbinary->m_dwDataLength ==
        !          4570:                        nActualSize + nOldLen);
        !          4571: 
        !          4572:                // Release the buffer now that data has been fetched
        !          4573:                ::GlobalUnlock(varValue.m_pbinary->m_hData);
        !          4574:        }
        !          4575: }
        !          4576: 
        !          4577: //////////////////////////////////////////////////////////////////////////////
        !          4578: // CRecordset diagnostics
        !          4579: 
        !          4580: #ifdef _DEBUG
        !          4581: void CRecordset::AssertValid() const
        !          4582: {
        !          4583:        CObject::AssertValid();
        !          4584:        if (m_pDatabase != NULL)
        !          4585:                m_pDatabase->AssertValid();
        !          4586: }
        !          4587: 
        !          4588: void CRecordset::Dump(CDumpContext& dc) const
        !          4589: {
        !          4590:        CObject::Dump(dc);
        !          4591: 
        !          4592:        dc << _T("m_nOpenType = ") << m_nOpenType;
        !          4593:        dc << _T("\nm_strSQL = ") << m_strSQL;
        !          4594:        dc << _T("\nm_hstmt = ") << m_hstmt;
        !          4595:        dc << _T("\nm_bRecordsetDb = ") << m_bRecordsetDb;
        !          4596: 
        !          4597:        dc << _T("\nm_lOpen = ") << m_lOpen;
        !          4598:        dc << _T("\nm_bScrollable = ") << m_bScrollable;
        !          4599:        dc << _T("\nm_bUpdatable = ") << m_bUpdatable;
        !          4600:        dc << _T("\nm_bAppendable = ") << m_bAppendable;
        !          4601: 
        !          4602:        dc << _T("\nm_nFields = ") << m_nFields;
        !          4603:        dc << _T("\nm_nFieldsBound = ") << m_nFieldsBound;
        !          4604:        dc << _T("\nm_nParams = ") << m_nParams;
        !          4605: 
        !          4606:        dc << _T("\nm_bEOF = ") << m_bEOF;
        !          4607:        dc << _T("\nm_bBOF = ") << m_bBOF;
        !          4608:        dc << _T("\nm_bDeleted = ") << m_bEOF;
        !          4609: 
        !          4610:        dc << _T("\nm_bLockMode = ") << m_nLockMode;
        !          4611:        dc << _T("\nm_nEditMode = ") << m_nEditMode;
        !          4612:        dc << _T("\nm_strCursorName = ") << m_strCursorName;
        !          4613:        dc << _T("\nm_hstmtUpdate = ") << m_hstmtUpdate;
        !          4614: 
        !          4615:        dc << _T("\nDump values for each field in current record.");
        !          4616:        DumpFields(dc);
        !          4617: 
        !          4618:        if (dc.GetDepth() > 0)
        !          4619:        {
        !          4620:                if (m_pDatabase == NULL)
        !          4621:                        dc << _T("with no database\n");
        !          4622:                else
        !          4623:                        dc << _T("with database: ") << m_pDatabase;
        !          4624:        }
        !          4625: }
        !          4626: #endif // _DEBUG
        !          4627: 
        !          4628: //////////////////////////////////////////////////////////////////////////////
        !          4629: // Helpers
        !          4630: 
        !          4631: void AFXAPI _AfxSetCurrentRecord(long* plCurrentRecord, long nRows, RETCODE nRetCode)
        !          4632: {
        !          4633:        if (*plCurrentRecord != AFX_CURRENT_RECORD_UNDEFINED &&
        !          4634:                nRetCode != SQL_NO_DATA_FOUND)
        !          4635:                *plCurrentRecord += nRows;
        !          4636: }
        !          4637: 
        !          4638: void AFXAPI _AfxSetRecordCount(long* plRecordCount, long lCurrentRecord,
        !          4639:        BOOL bEOFSeen, RETCODE nRetCode)
        !          4640: {
        !          4641:        // If not at the end and haven't yet been to the end, incr count
        !          4642:        if (nRetCode != SQL_NO_DATA_FOUND && !bEOFSeen &&
        !          4643:                lCurrentRecord != AFX_CURRENT_RECORD_UNDEFINED)
        !          4644:        {
        !          4645:                // lCurrentRecord 0-based and it's already been set
        !          4646:                *plRecordCount =
        !          4647:                        __max(*plRecordCount, lCurrentRecord + 1);
        !          4648:        }
        !          4649: }
        !          4650: 
        !          4651: //////////////////////////////////////////////////////////////////////////////
        !          4652: // Inline function declarations expanded out-of-line
        !          4653: 
        !          4654: #ifndef _AFX_ENABLE_INLINES
        !          4655: 
        !          4656: #define _AFXDBCORE_INLINE
        !          4657: #include "afxdb.inl"
        !          4658: 
        !          4659: #endif
        !          4660: 
        !          4661: #ifdef AFX_INIT_SEG
        !          4662: #pragma code_seg(AFX_INIT_SEG)
        !          4663: #endif
        !          4664: 
        !          4665: IMPLEMENT_DYNAMIC(CDBException, CException)
        !          4666: IMPLEMENT_DYNAMIC(CDatabase, CObject)
        !          4667: IMPLEMENT_DYNAMIC(CRecordset, CObject)
        !          4668: 
        !          4669: #pragma warning(disable: 4074)
        !          4670: #pragma init_seg(lib)
        !          4671: 
        !          4672: PROCESS_LOCAL(_AFX_DB_STATE, _afxDbState)
        !          4673: 
        !          4674: /////////////////////////////////////////////////////////////////////////////
        !          4675: 
        !          4676: #endif  // !_WIN64

E-mail: