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

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: 
1.2     ! parser   4676: //IMPLEMENT_DYNAMIC(CDBException, CException)
        !          4677: //IMPLEMENT_DYNAMIC(CDatabase, CObject)
        !          4678: //IMPLEMENT_DYNAMIC(CRecordset, CObject)
1.1       parser   4679: 
                   4680: #pragma warning(disable: 4074)
                   4681: #pragma init_seg(lib)
                   4682: 
                   4683: PROCESS_LOCAL(_AFX_DB_STATE, _afxDbState)
                   4684: 
                   4685: /////////////////////////////////////////////////////////////////////////////

E-mail: