Annotation of parser3/src/sql/odbc/MFCpatches/DBCORE.CPP, revision 1.1

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

E-mail: