home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / dbrfx.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-06-16  |  89.1 KB  |  3,460 lines

  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. // CDBByteArray db specific class for holding byte array data
  26. class CDBByteArray : public CByteArray
  27. {
  28.     DECLARE_DYNAMIC(CDBByteArray)
  29.  
  30. // Operations
  31.     void SetLength(int nNewSize);
  32. };
  33.  
  34. inline void CDBByteArray::SetLength(int nNewSize)
  35. {
  36.     // Can't grow buffer since ODBC has been SQLBindCol'd on it.
  37.     ASSERT(nNewSize <= m_nMaxSize);
  38.     m_nSize = nNewSize;
  39. }
  40.  
  41. //////////////////////////////////////////////////////////////////////////////
  42. // CFieldExchange
  43.  
  44. CFieldExchange::CFieldExchange(UINT nOperation, CRecordset* prs, void* pvField)
  45. {
  46. #ifdef _DEBUG
  47.     ASSERT(nOperation >= BindParam && nOperation <= DumpField);
  48. #endif
  49.     ASSERT_VALID(prs);
  50.     ASSERT(prs->m_hstmt != SQL_NULL_HSTMT);
  51.  
  52.     m_nFieldType = (UINT) noFieldType;
  53.     m_nOperation = nOperation;
  54.     m_prs = prs;
  55.     m_pvField = pvField;
  56.  
  57.     m_nFields = 0;
  58.     m_nParams = 0;
  59.     m_nParamFields = 0;
  60.     m_bField = FALSE;
  61.     m_pstr = NULL;
  62.     m_hstmt = SQL_NULL_HSTMT;
  63.  
  64.     m_lDefaultLBFetchSize = 0x00010000;
  65.     m_lDefaultLBReallocSize = 0x00010000;
  66. }
  67.  
  68. BOOL CFieldExchange::IsFieldType(UINT* pnField)
  69. {
  70.     if (m_nFieldType == outputColumn)
  71.     {
  72.         *pnField = ++m_nFields;
  73.         // Recordset's m_nFields must match number of Fields!
  74.         ASSERT(m_nFields <= m_prs->m_nFields);
  75.     }
  76.     else
  77.     {
  78.         // Make sure SetFieldType was called
  79.         ASSERT(m_nFieldType == inputParam ||
  80.             m_nFieldType == outputParam ||
  81.             m_nFieldType == inoutParam);
  82.  
  83.         *pnField = ++m_nParams;
  84.         // Recordset's m_nParams must match number of Params!
  85.         ASSERT(m_nParams <= m_prs->m_nParams);
  86.     }
  87.  
  88.     if (m_nOperation == BindParam || m_nOperation == RebindParam)
  89.     {
  90.         // only valid on a param field type
  91.         return m_nFieldType != outputColumn;
  92.     }
  93.     else
  94.     {
  95.         // valid only on an outputColumn field type
  96.         return m_nFieldType == outputColumn;
  97.     }
  98. }
  99.  
  100. // Default implementation for RFX functions
  101. void CFieldExchange::Default(LPCTSTR szName,
  102.     void* pv, LONG* plLength, int nCType, UINT cbValue, UINT cbPrecision)
  103. {
  104.     RETCODE nRetCode;
  105.     UINT nField = (m_nFieldType == outputColumn)? m_nFields: m_nParams;
  106.     switch (m_nOperation)
  107.     {
  108.     case BindParam:
  109.         if (m_prs->IsParamStatusNull(nField - 1))
  110.             *plLength = SQL_NULL_DATA;
  111.         else
  112.             *plLength = cbValue;
  113.         // For params, CType is same as SQL type
  114.         AFX_SQL_SYNC(::SQLBindParameter(m_hstmt, (UWORD)nField,
  115.             (SWORD)m_nFieldType, (SWORD)nCType, (SWORD)nCType, cbPrecision, 0,
  116.             pv, 0, plLength));
  117.         if (nRetCode != SQL_SUCCESS)
  118.             m_prs->ThrowDBException(nRetCode, m_hstmt);
  119.  
  120.         // Add the member address to the param map
  121.         m_prs->m_mapParamIndex.SetAt(pv, (void*)nField);
  122.         return;
  123.  
  124.     case RebindParam:
  125.         // Only need to reset param length
  126.         *plLength = m_prs->IsParamStatusNull(nField - 1) ? SQL_NULL_DATA : cbValue;
  127.         return;
  128.  
  129.     case BindFieldForUpdate:
  130.         if (!m_prs->IsFieldStatusDirty(nField - 1))
  131.         {
  132.             // If not dirty, set length to SQL_IGNORE for SQLSetPos updates
  133.             *plLength = SQL_IGNORE;
  134.         }
  135.         else if (!m_prs->IsFieldStatusNull(nField - 1))
  136.         {
  137.             // Reset the length as it may have changed for var length fields
  138.             *plLength = cbValue;
  139.         }
  140.         return;
  141.  
  142.  
  143.     case UnbindFieldForUpdate:
  144.         // Reset bound length to actual length to clear SQL_IGNOREs
  145.         if (!m_prs->IsFieldStatusDirty(nField - 1))
  146.             *plLength = cbValue;
  147.         return;
  148.  
  149.     case BindFieldToColumn:
  150.         AFX_SQL_SYNC(::SQLBindCol(m_prs->m_hstmt, (UWORD)nField, (SWORD)nCType,
  151.             pv, cbValue, plLength));
  152.         if (!m_prs->Check(nRetCode))
  153.             m_prs->ThrowDBException(nRetCode);
  154.  
  155.         // Add the member address to the field map
  156.         m_prs->m_mapFieldIndex.SetAt(pv, (void*)nField);
  157.         return;
  158.  
  159.     case Name:
  160.         if (m_prs->IsFieldStatusDirty(nField - 1))
  161.         {
  162.             // We require a name
  163.             ASSERT(lstrlen(szName) != 0);
  164.  
  165.             *m_pstr += szName;
  166.             *m_pstr += m_lpszSeparator;
  167.         }
  168.         return;
  169.  
  170.     case NameValue:
  171.         if (m_prs->IsFieldStatusDirty(nField - 1))
  172.         {
  173.             *m_pstr += szName;
  174.             *m_pstr += '=';
  175.         }
  176.  
  177.         // Fall through
  178.     case Value:
  179.         if (m_prs->IsFieldStatusDirty(nField - 1))
  180.         {
  181.             // If user marked column NULL, reflect this in length
  182.             if (m_prs->IsFieldStatusNull(nField - 1))
  183.                 *plLength = SQL_NULL_DATA;
  184.             else
  185.                 *plLength = cbValue;
  186.  
  187.             // If optimizing for bulk add, only need lengths set correctly
  188.             if(!(m_prs->m_dwOptions & CRecordset::optimizeBulkAdd))
  189.             {
  190.                 *m_pstr += '?';
  191.                 *m_pstr += m_lpszSeparator;
  192.                 m_nParamFields++;
  193.  
  194.                 // Assumes all bound fields BEFORE unbound fields
  195.                 CODBCFieldInfo* pODBCInfo =
  196.                     &m_prs->m_rgODBCFieldInfos[nField - 1];
  197.  
  198.                 AFX_SQL_SYNC(::SQLBindParameter(m_hstmt,
  199.                     (UWORD)m_nParamFields, SQL_PARAM_INPUT,
  200.                     (SWORD)nCType, pODBCInfo->m_nSQLType,
  201.                     pODBCInfo->m_nPrecision, pODBCInfo->m_nScale,
  202.                     pv, 0, plLength));
  203.                 if (nRetCode != SQL_SUCCESS)
  204.                     m_prs->ThrowDBException(nRetCode, m_hstmt);
  205.             }
  206.         }
  207.         return;
  208.  
  209.     case MarkForUpdate:
  210.         {
  211.             // Get the field data
  212.             CFieldInfo* pInfo = &m_prs->m_rgFieldInfos[nField - 1];
  213.  
  214.             // If user changed field value from previous value, mark field dirty
  215.             if ((pInfo->m_bStatus & AFX_SQL_FIELD_FLAG_NULL))
  216.             {
  217.                 if (!m_prs->IsFieldStatusNull(nField - 1))
  218.                     m_prs->SetDirtyFieldStatus(nField - 1);
  219.             }
  220.             else
  221.             {
  222.                 // Saved field is not NULL. current field null, so field dirty
  223.                 BOOL bDirty = m_prs->IsFieldStatusNull(nField - 1);
  224.  
  225.                 // If values differ, then field dirty
  226.                 void* pvDataCache;
  227.  
  228.                 if (pInfo->m_nDataType == AFX_RFX_BOOL ||
  229.                     pInfo->m_nDataType == AFX_RFX_BYTE ||
  230.                     pInfo->m_nDataType == AFX_RFX_INT ||
  231.                     pInfo->m_nDataType == AFX_RFX_LONG ||
  232.                     pInfo->m_nDataType == AFX_RFX_SINGLE)
  233.                 {
  234.                     // If caching data by value, pass a ref
  235.                     pvDataCache = &pInfo->m_pvDataCache;
  236.                 }
  237.                 else
  238.                     pvDataCache = pInfo->m_pvDataCache;
  239.  
  240.                 if (bDirty || !AfxCompareValueByRef(pv, pvDataCache, pInfo->m_nDataType))
  241.                     m_prs->SetDirtyFieldStatus(nField - 1);
  242.             }
  243.  
  244. #ifdef _DEBUG
  245.             // Field address must not change - ODBC's SQLBindCol depends upon this
  246.             void* pvBind;
  247.  
  248.             switch (pInfo->m_nDataType)
  249.             {
  250.             default:
  251.                 pvBind = pv;
  252.                 break;
  253.  
  254.             case AFX_RFX_LPTSTR:
  255. #ifdef _UNICODE
  256.                 pvBind = m_prs->m_pvFieldProxy[nField-1];
  257. #else // !_UNICODE
  258.                 pvBind = pv;
  259. #endif
  260.                 break;
  261.  
  262.             case AFX_RFX_TEXT:
  263. #ifdef _UNICODE
  264.                 pvBind = m_prs->m_pvFieldProxy[nField-1];
  265. #else // !_UNICODE
  266.                 {
  267.                     pvBind = ((CString*)pv)->GetBuffer(0);
  268.                     ((CString*)pv)->ReleaseBuffer();
  269.                 }
  270. #endif
  271.                 break;
  272.  
  273.             case AFX_RFX_OLEDATE:
  274.             case AFX_RFX_DATE:
  275.                 pvBind = m_prs->m_pvFieldProxy[nField-1];
  276.                 break;
  277.  
  278.             case AFX_RFX_BINARY:
  279.                 pvBind = ((CByteArray*)pv)->GetData();
  280.                 break;
  281.             }
  282.  
  283.             if (pInfo->m_pvBindAddress != pvBind)
  284.             {
  285.                 TRACE1("Error: field address (column %u) has changed!\n",
  286.                     nField);
  287.                 ASSERT(FALSE);
  288.             }
  289. #endif // _DEBUG
  290.  
  291.             if ((m_pvField == NULL  || m_pvField == pv) &&
  292.                 m_prs->IsFieldStatusDirty(nField - 1))
  293.             {
  294.                 m_bField = TRUE;
  295.             }
  296.         }
  297.         return;
  298.  
  299.     case StoreField:
  300.         AfxStoreField(*m_prs, nField, pv);
  301.         return;
  302.  
  303.     case LoadField:
  304.         AfxLoadField(*m_prs, nField, pv, plLength);
  305.         return;
  306.  
  307.     default:
  308.         ASSERT(FALSE);
  309.     }
  310. }
  311.  
  312. void AFXAPI RFX_Text(CFieldExchange* pFX, LPCTSTR szName,
  313.     LPTSTR value, int nMaxLength, int nColumnType, short nScale)
  314. {
  315.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  316.     ASSERT(AfxIsValidString(szName));
  317.     ASSERT(AfxIsValidAddress(value, nMaxLength));
  318.  
  319.     RETCODE nRetCode;
  320.     UINT nField;
  321.     if (!pFX->IsFieldType(&nField))
  322.         return;
  323.  
  324.     LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
  325.         nField - 1, pFX->m_nFieldType);
  326.     switch (pFX->m_nOperation)
  327.     {
  328.     default:
  329.         pFX->Default(szName, value, plLength,
  330.             SQL_C_CHAR, lstrlen(value), nMaxLength);
  331.         return;
  332.  
  333.     case CFieldExchange::BindParam:
  334.         {
  335.             void* pvParam = value; // will be overwritten if UNICODE
  336.  
  337. #ifdef _UNICODE
  338.             // Must use proxy to translate unicode data into non-unicode param
  339.             pFX->m_prs->m_bRebindParams = TRUE;
  340.  
  341.             // Allocate proxy array if necessary
  342.             if (pFX->m_prs->m_pvParamProxy == NULL)
  343.             {
  344.                 pFX->m_prs->m_pvParamProxy = new void*[pFX->m_prs->m_nParams];
  345.                 memset(pFX->m_prs->m_pvParamProxy, 0,
  346.                     pFX->m_prs->m_nParams*sizeof(void*));
  347.                 pFX->m_prs->m_nProxyParams = pFX->m_prs->m_nParams;
  348.             }
  349.  
  350.             // Allocate non-unicode string to nMaxLength if necessary for SQLBindParameter
  351.             if (pFX->m_prs->m_pvParamProxy[nField-1] == NULL)
  352.             {
  353.                 pvParam = new CHAR[nMaxLength+1];
  354.                 pFX->m_prs->m_pvParamProxy[nField-1] = pvParam;
  355.             }
  356.             else
  357.                 pvParam = pFX->m_prs->m_pvParamProxy[nField-1];
  358.  
  359.             // Now fill in the data value
  360.             USES_CONVERSION;
  361.             lstrcpyA((char*)pvParam, T2A((LPCTSTR)value));
  362. #endif // _UNICODE
  363.  
  364.             *plLength = pFX->m_prs->IsParamStatusNull(nField - 1) ?
  365.                 SQL_NULL_DATA : SQL_NTS;
  366.  
  367.             AFX_SQL_SYNC(::SQLBindParameter(pFX->m_hstmt, (UWORD)nField,
  368.                  (SWORD)pFX->m_nFieldType, SQL_C_CHAR, (SWORD)nColumnType,
  369.                  nMaxLength, nScale, pvParam, nMaxLength, plLength));
  370.  
  371.             if (nRetCode != SQL_SUCCESS)
  372.                 pFX->m_prs->ThrowDBException(nRetCode, pFX->m_hstmt);
  373.  
  374.             // Add the member address to the param map
  375.             pFX->m_prs->m_mapParamIndex.SetAt(value, (void*)nField);
  376.         }
  377.         return;
  378.  
  379. #ifdef _UNICODE
  380.     case CFieldExchange::RebindParam:
  381.         *plLength = pFX->m_prs->IsParamStatusNull(nField - 1) ?
  382.             SQL_NULL_DATA : SQL_NTS;
  383.         if (pFX->m_prs->m_nProxyParams != 0)
  384.         {
  385.             // Fill buffer (expected by SQLBindParameter) with new param data
  386.             USES_CONVERSION;
  387.             LPSTR lpszParam = (LPSTR)pFX->m_prs->m_pvParamProxy[nField-1];
  388.             lstrcpyA(lpszParam, T2A((LPCTSTR)value));
  389.         }
  390.         return;
  391. #endif // _UNICODE
  392.  
  393.     case CFieldExchange::BindFieldToColumn:
  394.         {
  395.             // Assumes all bound fields BEFORE unbound fields
  396.             CODBCFieldInfo* pODBCInfo =
  397.                 &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  398.             UINT cbColumn = pODBCInfo->m_nPrecision;
  399.  
  400.             switch (pODBCInfo->m_nSQLType)
  401.             {
  402.             default:
  403. #ifdef _DEBUG
  404.                 // Warn of possible field schema mismatch
  405.                 if (afxTraceFlags & traceDatabase)
  406.                     TRACE1("Warning: string converted from SQL type %ld.\n",
  407.                         pODBCInfo->m_nSQLType);
  408. #endif // _DEBUG
  409.  
  410.                 // Add room for extra information like sign, decimal point, etc.
  411.                 cbColumn += 10;
  412.                 break;
  413.  
  414.             case SQL_LONGVARCHAR:
  415.             case SQL_CHAR:
  416.             case SQL_VARCHAR:
  417.                 break;
  418.  
  419.             case SQL_FLOAT:
  420.             case SQL_REAL:
  421.             case SQL_DOUBLE:
  422.                 // Add room for sign, decimal point and " E +XXX"
  423.                 cbColumn += 10;
  424.                 break;
  425.  
  426.             case SQL_DECIMAL:
  427.             case SQL_NUMERIC:
  428.                 // Add room for sign and decimal point
  429.                 cbColumn += 2;
  430.                 break;
  431.  
  432.             case SQL_TIMESTAMP:
  433.             case SQL_DATE:
  434.             case SQL_TIME:
  435.                 // May need extra space, i.e. "{TS mm/dd/yyyy hh:mm:ss}"
  436.                 cbColumn += 10;
  437.                 break;
  438.  
  439.             case SQL_TINYINT:
  440.             case SQL_SMALLINT:
  441.             case SQL_INTEGER:
  442.             case SQL_BIGINT:
  443.                 // Add room for sign
  444.                 cbColumn += 1;
  445.                 break;
  446.             }
  447.  
  448.             // Constrain to user specified max length, subject to 256 byte min
  449.             if (cbColumn > (UINT)nMaxLength || cbColumn < 256)
  450.                 cbColumn = nMaxLength;
  451.  
  452.             // Set up binding addres
  453.  
  454.             void* pvData = value;   // overwritten if _UNICODE
  455.             value[cbColumn] = '\0';
  456.  
  457. #ifdef _UNICODE
  458.             // Allocate proxy array if necessary
  459.             if (pFX->m_prs->m_pvFieldProxy == NULL)
  460.             {
  461.                 pFX->m_prs->m_pvFieldProxy = new void*[pFX->m_prs->m_nFields];
  462.                 memset(pFX->m_prs->m_pvFieldProxy, 0,
  463.                     pFX->m_prs->m_nFields*sizeof(void*));
  464.                 pFX->m_prs->m_nProxyFields = pFX->m_prs->m_nFields;
  465.             }
  466.  
  467.             // Allocate non-unicode string for SQLBindCol (not necessary on Requery)
  468.             if (pFX->m_prs->m_pvFieldProxy[nField-1] == NULL)
  469.                 pFX->m_prs->m_pvFieldProxy[nField-1] = new CHAR[cbColumn+2];
  470.  
  471.             pvData = pFX->m_prs->m_pvFieldProxy[nField-1];
  472. #endif // _UNICODE
  473.  
  474.             AFX_SQL_SYNC(::SQLBindCol(pFX->m_prs->m_hstmt, (UWORD)nField,
  475.                 SQL_C_CHAR, pvData, cbColumn+1, plLength));
  476.             if (!pFX->m_prs->Check(nRetCode))
  477.                 pFX->m_prs->ThrowDBException(nRetCode);
  478.  
  479.             // Add the member address to the field map
  480.             pFX->m_prs->m_mapFieldIndex.SetAt(value, (void*)nField);
  481.         }
  482.         return;
  483.  
  484. #ifdef _UNICODE
  485.     case CFieldExchange::BindFieldForUpdate:
  486.         if (pFX->m_prs->m_nProxyFields != 0)
  487.         {
  488.             // Fill buffer (expected by SQLSetPos) with new field data
  489.             USES_CONVERSION;
  490.             LPSTR lpszData = (LPSTR)pFX->m_prs->m_pvFieldProxy[nField-1];
  491.             lstrcpyA(lpszData, T2A((LPCTSTR)value));
  492.  
  493.             pFX->Default(szName, (void *)lpszData, plLength, SQL_C_CHAR,
  494.                 lstrlenA(lpszData), nMaxLength);
  495.         }
  496.         return;
  497. #endif // _UNICODE
  498.  
  499.     case CFieldExchange::Fixup:
  500.             if (*plLength == SQL_NULL_DATA)
  501.         {
  502.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  503.             value[0] = '\0';
  504.         }
  505.         else
  506.         {
  507. #ifdef _UNICODE
  508.             // Copy value out of the proxy
  509.             value = (LPTSTR)pFX->m_prs->m_pvFieldProxy[nField-1];
  510. #endif
  511.  
  512.             LPTSTR lpsz = value;
  513.             if (pFX->m_prs->m_pDatabase->m_bStripTrailingSpaces)
  514.             {
  515.                 // find first trailing space
  516.                 LPTSTR lpszFirstTrailing = NULL;
  517.                 while (*lpsz != '\0')
  518.                 {
  519.                     if (*lpsz != ' ')
  520.                         lpszFirstTrailing = NULL;
  521.                     else
  522.                     {
  523.                         if (lpszFirstTrailing == NULL)
  524.                             lpszFirstTrailing = lpsz;
  525.                     }
  526.                     lpsz = _tcsinc(lpsz);
  527.                 }
  528.                 // truncate
  529.                 if (lpszFirstTrailing != NULL)
  530.                     *lpszFirstTrailing = '\0';
  531.  
  532.             }
  533.             *plLength = lstrlen(value);
  534.         }
  535.         return;
  536.  
  537.     case CFieldExchange::SetFieldNull:
  538.         if ((pFX->m_pvField == NULL &&
  539.             pFX->m_nFieldType == CFieldExchange::outputColumn) ||
  540.             pFX->m_pvField == value)
  541.         {
  542.             if (pFX->m_bField)
  543.             {
  544.                 // Mark fields null
  545.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  546.                 // Set string 0 length
  547.                 *plLength = SQL_NULL_DATA;
  548.             }
  549.             else
  550.             {
  551.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  552.                 *plLength = SQL_NTS;
  553.             }
  554. #ifdef _DEBUG
  555.             pFX->m_nFieldFound = nField;
  556. #endif
  557.         }
  558.         return;
  559.  
  560.  
  561. #ifdef _UNICODE
  562.     case CFieldExchange::NameValue:
  563.         if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
  564.         {
  565.             *pFX->m_pstr += szName;
  566.             *pFX->m_pstr += '=';
  567.         }
  568.         // Fall through
  569.  
  570.     case CFieldExchange::Value:
  571.         if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
  572.         {
  573.             // Get the field data
  574.             CODBCFieldInfo* pODBCInfo =
  575.                     &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  576.  
  577.             LPSTR lpszData = (LPSTR)pFX->m_prs->m_pvFieldProxy[nField-1];
  578.             if (pFX->m_prs->IsFieldStatusNull(nField - 1))
  579.             {
  580.                 *plLength = SQL_NULL_DATA;
  581.             }
  582.             else
  583.             {
  584.                 USES_CONVERSION;
  585.                 lstrcpyA(lpszData, T2A((LPCTSTR)value));
  586.                 *plLength = lstrlen(value);
  587.             }
  588.  
  589.             // If optimizing for bulk add, only need lengths & proxy set correctly
  590.             if(!(pFX->m_prs->m_dwOptions & CRecordset::optimizeBulkAdd))
  591.             {
  592.                 *pFX->m_pstr += '?';
  593.                 *pFX->m_pstr += pFX->m_lpszSeparator;
  594.                 pFX->m_nParamFields++;
  595.                 AFX_SQL_SYNC(::SQLBindParameter(pFX->m_hstmt,
  596.                     (UWORD)pFX->m_nParamFields, SQL_PARAM_INPUT,
  597.                     SQL_C_CHAR, pODBCInfo->m_nSQLType, nMaxLength,
  598.                     pODBCInfo->m_nScale, lpszData, 0, plLength));
  599.             }
  600.         }
  601.         return;
  602. #endif // _UNICODE
  603.  
  604.     case CFieldExchange::MarkForAddNew:
  605.         // can force writing of psuedo-null value (as a non-null) by setting field dirty
  606.         if (*value != '\0')
  607.         {
  608.             pFX->m_prs->SetDirtyFieldStatus(nField - 1);
  609.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  610.         }
  611.         return;
  612.  
  613.     case CFieldExchange::MarkForUpdate:
  614.         if (*value == '\0')
  615.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  616.         else
  617.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  618.         pFX->Default(szName, value, plLength,
  619.             SQL_C_CHAR, lstrlen(value), nMaxLength);
  620.         return;
  621.  
  622.     case CFieldExchange::LoadField:
  623.         {
  624.             // Get the field data
  625.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  626.  
  627.             // Restore the status
  628.             pFX->m_prs->SetFieldStatus(nField - 1, pInfo->m_bStatus);
  629.  
  630.             // If not NULL, restore the value and length
  631.             if (!pFX->m_prs->IsFieldStatusNull(nField - 1))
  632.             {
  633.                 value = LPTSTR(pInfo->m_pvDataCache);
  634.                 *plLength = lstrlen(value);
  635.  
  636. #ifdef _UNICODE
  637.                 // Must restore proxy for correct WHERE CURRENT OF operation
  638.                 USES_CONVERSION;
  639.                 LPSTR lpszData = (LPSTR)pFX->m_prs->m_pvFieldProxy[nField-1];
  640.                 lstrcpyA(lpszData, T2A((LPCTSTR)value));
  641. #endif // _UNICODE
  642.             }
  643.             else
  644.             {
  645.                 *plLength = SQL_NULL_DATA;
  646.             }
  647.  
  648. #ifdef _DEBUG
  649.             // Buffer address must not change - ODBC's SQLBindCol depends upon this
  650.             void* pvBind;
  651.  
  652. #ifdef _UNICODE
  653.             pvBind = pFX->m_prs->m_pvFieldProxy[nField-1];
  654. #else // !_UNICODE
  655.             pvBind = value;
  656. #endif
  657.  
  658.             if (pvBind != pInfo->m_pvBindAddress)
  659.             {
  660.                 TRACE1("Error: buffer (column %u) address has changed!\n",
  661.                     nField);
  662.                 ASSERT(FALSE);
  663.             }
  664. #endif // _DEBUG
  665.         }
  666.         return;
  667.  
  668.     case CFieldExchange::StoreField:
  669.         AfxStoreField(*pFX->m_prs, nField, value);
  670.         return;
  671.  
  672.     case CFieldExchange::AllocCache:
  673.         {
  674.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  675.             pInfo->m_pvDataCache = new TCHAR[nMaxLength];
  676.             pInfo->m_nDataType = AFX_RFX_LPTSTR;
  677.         }
  678.         return;
  679.  
  680. #ifdef _DEBUG
  681.     case CFieldExchange::DumpField:
  682.         *pFX->m_pdcDump << "\n" << szName << " = " << value;
  683.         return;
  684. #endif // _DEBUG
  685.  
  686.     }
  687. }
  688.  
  689. // Note: CString.m_pchData must not be changed.  This address is registered
  690. // with ODBC and must remain valid until the recordset is released.
  691. void AFXAPI RFX_Text(CFieldExchange* pFX, LPCTSTR szName,
  692.     CString& value, int nMaxLength, int nColumnType, short nScale)
  693. {
  694.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  695.     ASSERT(AfxIsValidString(szName));
  696.     ASSERT(AfxIsValidAddress(&value, sizeof(CString)));
  697.  
  698.     RETCODE nRetCode;
  699.     UINT nField;
  700.     if (!pFX->IsFieldType(&nField))
  701.         return;
  702.  
  703.     LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
  704.         nField - 1, pFX->m_nFieldType);
  705.     switch (pFX->m_nOperation)
  706.     {
  707.     default:
  708.         pFX->Default(szName, value.GetBuffer(0), plLength,
  709.             SQL_C_CHAR, value.GetLength(), nMaxLength);
  710.         value.ReleaseBuffer();
  711.         return;
  712.  
  713.     case CFieldExchange::BindParam:
  714.         {
  715.             // Preallocate to nMaxLength and setup binding address
  716.             value.GetBufferSetLength(nMaxLength);
  717.             void* pvParam = value.LockBuffer(); // will be overwritten if UNICODE
  718.  
  719. #ifdef _UNICODE
  720.             // Must use proxy to translate unicode data into non-unicode param
  721.             pFX->m_prs->m_bRebindParams = TRUE;
  722.  
  723.             // Allocate proxy array if necessary
  724.             if (pFX->m_prs->m_pvParamProxy == NULL)
  725.             {
  726.                 pFX->m_prs->m_pvParamProxy = new void*[pFX->m_prs->m_nParams];
  727.                 memset(pFX->m_prs->m_pvParamProxy, 0,
  728.                     pFX->m_prs->m_nParams*sizeof(void*));
  729.                 pFX->m_prs->m_nProxyParams = pFX->m_prs->m_nParams;
  730.             }
  731.  
  732.             // Allocate non-unicode string to nMaxLength if necessary for SQLBindParameter
  733.             if (pFX->m_prs->m_pvParamProxy[nField-1] == NULL)
  734.             {
  735.                 pvParam = new CHAR[nMaxLength+1];
  736.                 pFX->m_prs->m_pvParamProxy[nField-1] = pvParam;
  737.             }
  738.             else
  739.                 pvParam = pFX->m_prs->m_pvParamProxy[nField-1];
  740.  
  741.             // Now fill in the data value
  742.             USES_CONVERSION;
  743.             lstrcpyA((char*)pvParam, T2A((LPCTSTR)value));
  744. #endif // _UNICODE
  745.  
  746.             *plLength = pFX->m_prs->IsParamStatusNull(nField - 1) ?
  747.                 SQL_NULL_DATA : SQL_NTS;
  748.  
  749.             AFX_SQL_SYNC(::SQLBindParameter(pFX->m_hstmt, (UWORD)nField,
  750.                  (SWORD)pFX->m_nFieldType, SQL_C_CHAR, (SWORD)nColumnType,
  751.                  nMaxLength, nScale, pvParam, nMaxLength, plLength));
  752.  
  753.             value.ReleaseBuffer();
  754.  
  755.             if (nRetCode != SQL_SUCCESS)
  756.                 pFX->m_prs->ThrowDBException(nRetCode, pFX->m_hstmt);
  757.  
  758.             // Add the member address to the param map
  759.             pFX->m_prs->m_mapParamIndex.SetAt(&value, (void*)nField);
  760.         }
  761.         return;
  762.  
  763. #ifdef _UNICODE
  764.     case CFieldExchange::RebindParam:
  765.         *plLength = pFX->m_prs->IsParamStatusNull(nField - 1) ?
  766.             SQL_NULL_DATA : SQL_NTS;
  767.         if (pFX->m_prs->m_nProxyParams != 0)
  768.         {
  769.             // Fill buffer (expected by SQLBindParameter) with new param data
  770.             USES_CONVERSION;
  771.             LPSTR lpszParam = (LPSTR)pFX->m_prs->m_pvParamProxy[nField-1];
  772.             lstrcpyA(lpszParam, T2A((LPCTSTR)value));
  773.         }
  774.         return;
  775. #endif // _UNICODE
  776.  
  777.     case CFieldExchange::BindFieldToColumn:
  778.         {
  779.             // Assumes all bound fields BEFORE unbound fields
  780.             CODBCFieldInfo* pODBCInfo =
  781.                 &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  782.             UINT cbColumn = pODBCInfo->m_nPrecision;
  783.  
  784.             switch (pODBCInfo->m_nSQLType)
  785.             {
  786.             default:
  787. #ifdef _DEBUG
  788.                 // Warn of possible field schema mismatch
  789.                 if (afxTraceFlags & traceDatabase)
  790.                     TRACE1("Warning: CString converted from SQL type %ld.\n",
  791.                         pODBCInfo->m_nSQLType);
  792. #endif // _DEBUG
  793.  
  794.                 // Add room for extra information like sign, decimal point, etc.
  795.                 cbColumn += 10;
  796.                 break;
  797.  
  798.             case SQL_LONGVARCHAR:
  799.             case SQL_CHAR:
  800.             case SQL_VARCHAR:
  801.                 break;
  802.  
  803.             case SQL_FLOAT:
  804.             case SQL_REAL:
  805.             case SQL_DOUBLE:
  806.                 // Add room for sign, decimal point and " E +XXX"
  807.                 cbColumn += 10;
  808.                 break;
  809.  
  810.             case SQL_DECIMAL:
  811.             case SQL_NUMERIC:
  812.                 // Add room for sign and decimal point
  813.                 cbColumn += 2;
  814.                 break;
  815.  
  816.             case SQL_TIMESTAMP:
  817.             case SQL_DATE:
  818.             case SQL_TIME:
  819.                 // May need extra space, i.e. "{TS mm/dd/yyyy hh:mm:ss}"
  820.                 cbColumn += 10;
  821.                 break;
  822.  
  823.             case SQL_TINYINT:
  824.             case SQL_SMALLINT:
  825.             case SQL_INTEGER:
  826.             case SQL_BIGINT:
  827.                 // Add room for sign
  828.                 cbColumn += 1;
  829.                 break;
  830.             }
  831.  
  832.             // Constrain to user specified max length, subject to 256 byte min
  833.             if (cbColumn > (UINT)nMaxLength || cbColumn < 256)
  834.                 cbColumn = nMaxLength;
  835.  
  836.             // Set up binding addres
  837.             void* pvData;
  838.             value.GetBufferSetLength(cbColumn+1);
  839.             pvData = value.LockBuffer();    // will be overwritten if UNICODE
  840.  
  841. #ifdef _UNICODE
  842.             // Allocate proxy array if necessary
  843.             if (pFX->m_prs->m_pvFieldProxy == NULL)
  844.             {
  845.                 pFX->m_prs->m_pvFieldProxy = new void*[pFX->m_prs->m_nFields];
  846.                 memset(pFX->m_prs->m_pvFieldProxy, 0,
  847.                     pFX->m_prs->m_nFields*sizeof(void*));
  848.                 pFX->m_prs->m_nProxyFields = pFX->m_prs->m_nFields;
  849.             }
  850.  
  851.             // Allocate non-unicode string for SQLBindCol (not necessary on Requery)
  852.             if (pFX->m_prs->m_pvFieldProxy[nField-1] == NULL)
  853.                 pFX->m_prs->m_pvFieldProxy[nField-1] = new CHAR[cbColumn+2];
  854.  
  855.             pvData = pFX->m_prs->m_pvFieldProxy[nField-1];
  856. #endif // _UNICODE
  857.  
  858.             AFX_SQL_SYNC(::SQLBindCol(pFX->m_prs->m_hstmt, (UWORD)nField,
  859.                 SQL_C_CHAR, pvData, cbColumn+1, plLength));
  860.             value.ReleaseBuffer();
  861.             if (!pFX->m_prs->Check(nRetCode))
  862.                 pFX->m_prs->ThrowDBException(nRetCode);
  863.  
  864.             // Add the member address to the field map
  865.             pFX->m_prs->m_mapFieldIndex.SetAt(&value, (void*)nField);
  866.         }
  867.         return;
  868.  
  869. #ifdef _UNICODE
  870.     case CFieldExchange::BindFieldForUpdate:
  871.         if (pFX->m_prs->m_nProxyFields != 0)
  872.         {
  873.             // Fill buffer (expected by SQLSetPos) with new field data
  874.             USES_CONVERSION;
  875.             LPSTR lpszData = (LPSTR)pFX->m_prs->m_pvFieldProxy[nField-1];
  876.             lstrcpyA(lpszData, T2A((LPCTSTR)value));
  877.  
  878.             pFX->Default(szName, (void *)lpszData, plLength, SQL_C_CHAR,
  879.                 value.GetLength(), nMaxLength);
  880.         }
  881.         return;
  882. #endif // _UNICODE
  883.  
  884.     case CFieldExchange::Fixup:
  885.         if (*plLength == SQL_NULL_DATA)
  886.         {
  887.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  888.             value.GetBufferSetLength(0);
  889.             value.ReleaseBuffer();
  890.         }
  891.         else
  892.         {
  893. #ifdef _UNICODE
  894.             // Copy value out of the proxy
  895.             value = (LPSTR)pFX->m_prs->m_pvFieldProxy[nField-1];
  896. #endif
  897.  
  898.             LPTSTR lpsz = value.GetBuffer(0);
  899.             if (pFX->m_prs->m_pDatabase->m_bStripTrailingSpaces)
  900.             {
  901.                 // find first trailing space
  902.                 LPTSTR lpszFirstTrailing = NULL;
  903.                 while (*lpsz != '\0')
  904.                 {
  905.                     if (*lpsz != ' ')
  906.                         lpszFirstTrailing = NULL;
  907.                     else
  908.                     {
  909.                         if (lpszFirstTrailing == NULL)
  910.                             lpszFirstTrailing = lpsz;
  911.                     }
  912.                     lpsz = _tcsinc(lpsz);
  913.                 }
  914.                 // truncate
  915.                 if (lpszFirstTrailing != NULL)
  916.                     *lpszFirstTrailing = '\0';
  917.  
  918.             }
  919.             value.ReleaseBuffer();
  920.             *plLength = value.GetLength();
  921.         }
  922.         return;
  923.  
  924.     case CFieldExchange::SetFieldNull:
  925.         if ((pFX->m_pvField == NULL &&
  926.             pFX->m_nFieldType == CFieldExchange::outputColumn) ||
  927.             pFX->m_pvField == &value)
  928.         {
  929.             if (pFX->m_bField)
  930.             {
  931.                 // Mark fields null
  932.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  933.                 // Set string 0 length
  934.                 value.GetBufferSetLength(0);
  935.                 value.ReleaseBuffer();
  936.                 *plLength = SQL_NULL_DATA;
  937.             }
  938.             else
  939.             {
  940.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  941.                 *plLength = SQL_NTS;
  942.             }
  943. #ifdef _DEBUG
  944.             pFX->m_nFieldFound = nField;
  945. #endif
  946.         }
  947.         return;
  948.  
  949.  
  950. #ifdef _UNICODE
  951.     case CFieldExchange::NameValue:
  952.         if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
  953.         {
  954.             *pFX->m_pstr += szName;
  955.             *pFX->m_pstr += '=';
  956.         }
  957.         // Fall through
  958.  
  959.     case CFieldExchange::Value:
  960.         if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
  961.         {
  962.             // Get the field data
  963.             CODBCFieldInfo* pODBCInfo =
  964.                     &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  965.  
  966.             LPSTR lpszData = (LPSTR)pFX->m_prs->m_pvFieldProxy[nField-1];
  967.             if (pFX->m_prs->IsFieldStatusNull(nField - 1))
  968.             {
  969.                 *plLength = SQL_NULL_DATA;
  970.             }
  971.             else
  972.             {
  973.                 USES_CONVERSION;
  974.                 lstrcpyA(lpszData, T2A((LPCTSTR)value));
  975.                 *plLength = value.GetLength();
  976.             }
  977.  
  978.             // If optimizing for bulk add, only need lengths & proxy set correctly
  979.             if(!(pFX->m_prs->m_dwOptions & CRecordset::optimizeBulkAdd))
  980.             {
  981.                 *pFX->m_pstr += '?';
  982.                 *pFX->m_pstr += pFX->m_lpszSeparator;
  983.                 pFX->m_nParamFields++;
  984.                 AFX_SQL_SYNC(::SQLBindParameter(pFX->m_hstmt,
  985.                     (UWORD)pFX->m_nParamFields, SQL_PARAM_INPUT,
  986.                     SQL_C_CHAR, pODBCInfo->m_nSQLType, nMaxLength,
  987.                     pODBCInfo->m_nScale, lpszData, 0, plLength));
  988.             }
  989.         }
  990.         return;
  991. #endif // _UNICODE
  992.  
  993.     case CFieldExchange::MarkForAddNew:
  994.         // can force writing of psuedo-null value (as a non-null) by setting field dirty
  995.         if (!value.IsEmpty())
  996.         {
  997.             pFX->m_prs->SetDirtyFieldStatus(nField - 1);
  998.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  999.         }
  1000.         return;
  1001.  
  1002.     case CFieldExchange::MarkForUpdate:
  1003.         if (value.IsEmpty())
  1004.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  1005.         else
  1006.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1007.         pFX->Default(szName, &value, plLength,
  1008.             SQL_C_CHAR, value.GetLength(), nMaxLength);
  1009.         return;
  1010.  
  1011.     case CFieldExchange::LoadField:
  1012.         {
  1013.             // Get the field data
  1014.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  1015.             CString* pStrCachedValue = (CString*)pInfo->m_pvDataCache;
  1016.  
  1017.             // Restore the status
  1018.             pFX->m_prs->SetFieldStatus(nField - 1, pInfo->m_bStatus);
  1019.  
  1020.             // If not NULL, restore the value and length
  1021.             if (!pFX->m_prs->IsFieldStatusNull(nField - 1))
  1022.             {
  1023.                 value = *pStrCachedValue;
  1024.                 *plLength = value.GetLength();
  1025.  
  1026. #ifdef _UNICODE
  1027.                 // Must restore proxy for correct WHERE CURRENT OF operation
  1028.                 USES_CONVERSION;
  1029.                 LPSTR lpszData = (LPSTR)pFX->m_prs->m_pvFieldProxy[nField-1];
  1030.                 lstrcpyA(lpszData, T2A((LPCTSTR)value));
  1031. #endif // _UNICODE
  1032.             }
  1033.             else
  1034.             {
  1035.                 *plLength = SQL_NULL_DATA;
  1036.             }
  1037.  
  1038. #ifdef _DEBUG
  1039.             // Buffer address must not change - ODBC's SQLBindCol depends upon this
  1040.             void* pvBind;
  1041.  
  1042.     #ifdef _UNICODE
  1043.             pvBind = pFX->m_prs->m_pvFieldProxy[nField-1];
  1044.     #else // !_UNICODE
  1045.             pvBind = value.GetBuffer(0);
  1046.             value.ReleaseBuffer();
  1047.     #endif
  1048.  
  1049.             if (pvBind != pInfo->m_pvBindAddress)
  1050.             {
  1051.                 TRACE1("Error: CString buffer (column %u) address has changed!\n",
  1052.                     nField);
  1053.                 ASSERT(FALSE);
  1054.             }
  1055. #endif // _DEBUG
  1056.         }
  1057.         return;
  1058.  
  1059.     case CFieldExchange::StoreField:
  1060.         AfxStoreField(*pFX->m_prs, nField, &value);
  1061.         return;
  1062.  
  1063.     case CFieldExchange::AllocCache:
  1064.         {
  1065.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  1066.             pInfo->m_pvDataCache = new CString;
  1067.             pInfo->m_nDataType = AFX_RFX_TEXT;
  1068.         }
  1069.         return;
  1070.  
  1071. #ifdef _DEBUG
  1072.     case CFieldExchange::DumpField:
  1073.         *pFX->m_pdcDump << "\n" << szName << " = " << value;
  1074.         return;
  1075. #endif // _DEBUG
  1076.  
  1077.     }
  1078. }
  1079.  
  1080. void AFXAPI RFX_Int(CFieldExchange* pFX, LPCTSTR szName, int& value)
  1081. {
  1082.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  1083.     ASSERT(AfxIsValidString(szName));
  1084.  
  1085.     UINT nField;
  1086.     if (!pFX->IsFieldType(&nField))
  1087.         return;
  1088.  
  1089.     LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
  1090.         nField - 1, pFX->m_nFieldType);
  1091.     switch (pFX->m_nOperation)
  1092.     {
  1093.     case CFieldExchange::BindFieldToColumn:
  1094.         {
  1095. #ifdef _DEBUG
  1096.             // Assumes all bound fields BEFORE unbound fields
  1097.             CODBCFieldInfo* pODBCInfo =
  1098.                 &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  1099.  
  1100.             if (pODBCInfo->m_nSQLType != SQL_C_SHORT)
  1101.             {
  1102.                 // Warn of possible field schema mismatch
  1103.                 if (afxTraceFlags & traceDatabase)
  1104.                     TRACE1("Warning: int converted from SQL type %ld.\n",
  1105.                         pODBCInfo->m_nSQLType);
  1106.             }
  1107. #endif // _DEBUG
  1108.         }
  1109.         // fall through
  1110.  
  1111.     default:
  1112. LDefault:
  1113.         pFX->Default(szName, &value, plLength, SQL_C_LONG,
  1114.             sizeof(value), 5);
  1115.         return;
  1116.  
  1117.     case CFieldExchange::Fixup:
  1118.         if (*plLength == SQL_NULL_DATA)
  1119.         {
  1120.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  1121.             value = AFX_RFX_INT_PSEUDO_NULL;
  1122.         }
  1123.         return;
  1124.  
  1125.     case CFieldExchange::SetFieldNull:
  1126.         if ((pFX->m_pvField == NULL &&
  1127.             pFX->m_nFieldType == CFieldExchange::outputColumn) ||
  1128.             pFX->m_pvField == &value)
  1129.         {
  1130.             if (pFX->m_bField)
  1131.             {
  1132.                 // Mark fields null
  1133.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  1134.                 value = AFX_RFX_INT_PSEUDO_NULL;
  1135.                 *plLength = SQL_NULL_DATA;
  1136.             }
  1137.             else
  1138.             {
  1139.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1140.                 *plLength = sizeof(value);
  1141.             }
  1142. #ifdef _DEBUG
  1143.             pFX->m_nFieldFound = nField;
  1144. #endif
  1145.         }
  1146.         return;
  1147.  
  1148.     case CFieldExchange::MarkForAddNew:
  1149.         // can force writing of psuedo-null value (as a non-null) by setting field dirty
  1150.         if (value != AFX_RFX_INT_PSEUDO_NULL)
  1151.         {
  1152.             pFX->m_prs->SetDirtyFieldStatus(nField - 1);
  1153.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1154.         }
  1155.         return;
  1156.  
  1157.     case CFieldExchange::MarkForUpdate:
  1158.         if (value != AFX_RFX_INT_PSEUDO_NULL)
  1159.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1160.         goto LDefault;
  1161.  
  1162.     case CFieldExchange::AllocCache:
  1163.         {
  1164.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  1165.  
  1166.             // Data cached by value, no allocation necessary
  1167.             pInfo->m_nDataType = AFX_RFX_INT;
  1168.         }
  1169.         return;
  1170.  
  1171. #ifdef _DEBUG
  1172.     case CFieldExchange::DumpField:
  1173.         *pFX->m_pdcDump << "\n" << szName << " = " << value;
  1174.         return;
  1175. #endif // _DEBUG
  1176.  
  1177.     }
  1178. }
  1179.  
  1180. void AFXAPI RFX_Long(CFieldExchange* pFX, LPCTSTR szName, long& value)
  1181. {
  1182.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  1183.     ASSERT(AfxIsValidString(szName));
  1184.  
  1185.     UINT nField;
  1186.     if (!pFX->IsFieldType(&nField))
  1187.         return;
  1188.  
  1189.     LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
  1190.         nField - 1, pFX->m_nFieldType);
  1191.     switch (pFX->m_nOperation)
  1192.     {
  1193.     case CFieldExchange::BindFieldToColumn:
  1194.         {
  1195. #ifdef _DEBUG
  1196.             // Assumes all bound fields BEFORE unbound fields
  1197.             CODBCFieldInfo* pODBCInfo =
  1198.                 &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  1199.  
  1200.             if (pODBCInfo->m_nSQLType != SQL_C_LONG)
  1201.             {
  1202.                 // Warn of possible field schema mismatch
  1203.                 if (afxTraceFlags & traceDatabase)
  1204.                     TRACE1("Warning: long converted from SQL type %ld.\n",
  1205.                         pODBCInfo->m_nSQLType);
  1206.             }
  1207. #endif // _DEBUG
  1208.         }
  1209.         // fall through
  1210.  
  1211.     default:
  1212. LDefault:
  1213.         pFX->Default(szName, &value, plLength, SQL_C_LONG,
  1214.             sizeof(value), 10);
  1215.         return;
  1216.  
  1217.     case CFieldExchange::Fixup:
  1218.         if (*plLength == SQL_NULL_DATA)
  1219.         {
  1220.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  1221.             value = AFX_RFX_LONG_PSEUDO_NULL;
  1222.         }
  1223.         return;
  1224.  
  1225.     case CFieldExchange::SetFieldNull:
  1226.         if ((pFX->m_pvField == NULL &&
  1227.             pFX->m_nFieldType == CFieldExchange::outputColumn) ||
  1228.             pFX->m_pvField == &value)
  1229.         {
  1230.             if (pFX->m_bField)
  1231.             {
  1232.                 // Mark fields null
  1233.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  1234.                 value = AFX_RFX_LONG_PSEUDO_NULL;
  1235.                 *plLength = SQL_NULL_DATA;
  1236.             }
  1237.             else
  1238.             {
  1239.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1240.                 *plLength = sizeof(value);
  1241.             }
  1242. #ifdef _DEBUG
  1243.             pFX->m_nFieldFound = nField;
  1244. #endif
  1245.         }
  1246.         return;
  1247.  
  1248.     case CFieldExchange::MarkForAddNew:
  1249.         // can force writing of psuedo-null value (as a non-null) by setting field dirty
  1250.         if (value != AFX_RFX_LONG_PSEUDO_NULL)
  1251.         {
  1252.             pFX->m_prs->SetDirtyFieldStatus(nField - 1);
  1253.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1254.         }
  1255.         return;
  1256.  
  1257.     case CFieldExchange::MarkForUpdate:
  1258.         if (value != AFX_RFX_LONG_PSEUDO_NULL)
  1259.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1260.         goto LDefault;
  1261.  
  1262.     case CFieldExchange::AllocCache:
  1263.         {
  1264.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  1265.  
  1266.             // Data cached by value, no allocation necessary
  1267.             pInfo->m_nDataType = AFX_RFX_LONG;
  1268.         }
  1269.         return;
  1270.  
  1271. #ifdef _DEBUG
  1272.     case CFieldExchange::DumpField:
  1273.         *pFX->m_pdcDump << "\n" << szName << " = " << value;
  1274.         return;
  1275. #endif // _DEBUG
  1276.  
  1277.     }
  1278. }
  1279.  
  1280. void AFXAPI RFX_Byte(CFieldExchange* pFX, LPCTSTR szName, BYTE& value)
  1281. {
  1282.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  1283.     ASSERT(AfxIsValidString(szName));
  1284.  
  1285.     UINT nField;
  1286.     if (!pFX->IsFieldType(&nField))
  1287.         return;
  1288.  
  1289.     LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
  1290.         nField - 1, pFX->m_nFieldType);
  1291.     switch (pFX->m_nOperation)
  1292.     {
  1293.     case CFieldExchange::BindFieldToColumn:
  1294.         {
  1295. #ifdef _DEBUG
  1296.             // Assumes all bound fields BEFORE unbound fields
  1297.             CODBCFieldInfo* pODBCInfo =
  1298.                 &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  1299.  
  1300.             if (pODBCInfo->m_nSQLType != SQL_TINYINT)
  1301.             {
  1302.                 // Warn of possible field schema mismatch
  1303.                 if (afxTraceFlags & traceDatabase)
  1304.                     TRACE1("Warning: BYTE converted from SQL type %ld.\n",
  1305.                         pODBCInfo->m_nSQLType);
  1306.             }
  1307. #endif // _DEBUG
  1308.         }
  1309.         // fall through
  1310.  
  1311.     default:
  1312. LDefault:
  1313.         pFX->Default(szName, &value, plLength, SQL_TINYINT,
  1314.             sizeof(value), 3);
  1315.         break;
  1316.  
  1317.     case CFieldExchange::Fixup:
  1318.         if (*plLength == SQL_NULL_DATA)
  1319.         {
  1320.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  1321.             value = AFX_RFX_BYTE_PSEUDO_NULL;
  1322.         }
  1323.         return;
  1324.  
  1325.     case CFieldExchange::SetFieldNull:
  1326.         if ((pFX->m_pvField == NULL &&
  1327.             pFX->m_nFieldType == CFieldExchange::outputColumn) ||
  1328.             pFX->m_pvField == &value)
  1329.         {
  1330.             if (pFX->m_bField)
  1331.             {
  1332.                 // Mark fields null
  1333.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  1334.                 value = AFX_RFX_BYTE_PSEUDO_NULL;
  1335.                 *plLength = SQL_NULL_DATA;
  1336.             }
  1337.             else
  1338.             {
  1339.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1340.                 *plLength = sizeof(value);
  1341.             }
  1342. #ifdef _DEBUG
  1343.             pFX->m_nFieldFound = nField;
  1344. #endif
  1345.         }
  1346.         return;
  1347.  
  1348.     case CFieldExchange::MarkForAddNew:
  1349.         // can force writing of psuedo-null value (as a non-null) by setting field dirty
  1350.         if (value != AFX_RFX_BYTE_PSEUDO_NULL)
  1351.         {
  1352.             pFX->m_prs->SetDirtyFieldStatus(nField - 1);
  1353.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1354.         }
  1355.         return;
  1356.  
  1357.     case CFieldExchange::MarkForUpdate:
  1358.         if (value != AFX_RFX_BYTE_PSEUDO_NULL)
  1359.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1360.         goto LDefault;
  1361.  
  1362.     case CFieldExchange::AllocCache:
  1363.         {
  1364.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  1365.  
  1366.             // Data cached by value, no allocation necessary
  1367.             pInfo->m_nDataType = AFX_RFX_BYTE;
  1368.         }
  1369.         return;
  1370.  
  1371. #ifdef _DEBUG
  1372.     case CFieldExchange::DumpField:
  1373.         *pFX->m_pdcDump << "\n" << szName << " = " << value;
  1374.         return;
  1375. #endif // _DEBUG
  1376.  
  1377.     }
  1378. }
  1379.  
  1380. void AFXAPI RFX_Bool(CFieldExchange* pFX, LPCTSTR szName, BOOL& value)
  1381. {
  1382.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  1383.     ASSERT(AfxIsValidString(szName));
  1384.  
  1385.     UINT nField;
  1386.     if (!pFX->IsFieldType(&nField))
  1387.         return;
  1388.  
  1389.     LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
  1390.         nField - 1, pFX->m_nFieldType);
  1391.     switch (pFX->m_nOperation)
  1392.     {
  1393.     case CFieldExchange::BindFieldToColumn:
  1394.         {
  1395. #ifdef _DEBUG
  1396.             // Assumes all bound fields BEFORE unbound fields
  1397.             CODBCFieldInfo* pODBCInfo =
  1398.                 &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  1399.  
  1400.             if (pODBCInfo->m_nSQLType != SQL_BIT)
  1401.             {
  1402.                 // Warn of possible field schema mismatch
  1403.                 if (afxTraceFlags & traceDatabase)
  1404.                     TRACE1("Warning: BOOL converted from SQL type %ld.\n",
  1405.                         pODBCInfo->m_nSQLType);
  1406.             }
  1407. #endif // _DEBUG
  1408.         }
  1409.         // Fall through
  1410.  
  1411.     default:
  1412. LDefault:
  1413.         pFX->Default(szName, &value, plLength, SQL_BIT,
  1414.             sizeof(value), 1);
  1415.         return;
  1416.  
  1417.     case CFieldExchange::Fixup:
  1418.         if (*plLength == SQL_NULL_DATA)
  1419.         {
  1420.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  1421.             value = AFX_RFX_BOOL_PSEUDO_NULL;
  1422.         }
  1423.         else
  1424.             // Cast BYTE into BOOL (int)
  1425.             value = *(BYTE *)&value;
  1426.         return;
  1427.  
  1428.     case CFieldExchange::SetFieldNull:
  1429.         if ((pFX->m_pvField == NULL &&
  1430.             pFX->m_nFieldType == CFieldExchange::outputColumn) ||
  1431.             pFX->m_pvField == &value)
  1432.         {
  1433.             if (pFX->m_bField)
  1434.             {
  1435.                 // Mark fields null
  1436.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  1437.                 value = AFX_RFX_BOOL_PSEUDO_NULL;
  1438.                 *plLength = SQL_NULL_DATA;
  1439.             }
  1440.             else
  1441.             {
  1442.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1443.                 *plLength = sizeof(value);
  1444.             }
  1445. #ifdef _DEBUG
  1446.             pFX->m_nFieldFound = nField;
  1447. #endif
  1448.         }
  1449.         return;
  1450.  
  1451.     case CFieldExchange::MarkForAddNew:
  1452.         // can force writing of psuedo-null value (as a non-null) by setting field dirty
  1453.         if (value != AFX_RFX_BOOL_PSEUDO_NULL)
  1454.         {
  1455.             pFX->m_prs->SetDirtyFieldStatus(nField - 1);
  1456.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1457.         }
  1458.         return;
  1459.  
  1460.     case CFieldExchange::MarkForUpdate:
  1461.         if (value != AFX_RFX_BOOL_PSEUDO_NULL)
  1462.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1463.         goto LDefault;
  1464.  
  1465.     case CFieldExchange::AllocCache:
  1466.         {
  1467.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  1468.  
  1469.             // Data cached by value, no allocation necessary
  1470.             pInfo->m_nDataType = AFX_RFX_BOOL;
  1471.         }
  1472.         return;
  1473.  
  1474. #ifdef _DEBUG
  1475.     case CFieldExchange::DumpField:
  1476.         *pFX->m_pdcDump << "\n" << szName << " = " << value;
  1477.         return;
  1478. #endif // _DEBUG
  1479.  
  1480.     }
  1481. }
  1482.  
  1483. // Note: CByteArray.m_pData must not be changed.  This address is registered
  1484. // with ODBC and must remain valid until the recordset is released.
  1485. void AFXAPI RFX_Binary(CFieldExchange* pFX, LPCTSTR szName,
  1486.     CByteArray& value, int nMaxLength)
  1487. {
  1488.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  1489.     ASSERT(AfxIsValidString(szName));
  1490.  
  1491.     RETCODE nRetCode;
  1492.     UINT nField;
  1493.     if (!pFX->IsFieldType(&nField))
  1494.         return;
  1495.  
  1496.     LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
  1497.         nField - 1, pFX->m_nFieldType);
  1498.  
  1499.     BOOL bByRef = FALSE;
  1500.     switch (pFX->m_nOperation)
  1501.     {
  1502.     default:
  1503. LDefault:
  1504.         {
  1505.             void* pvData = NULL;
  1506.             if (value.GetSize() > 0)
  1507.             {
  1508.                 if (bByRef)
  1509.                     pvData = &value;
  1510.                 else
  1511.                     pvData = &value[0];
  1512.             }
  1513.  
  1514.             pFX->Default(szName, pvData, plLength, SQL_C_BINARY,
  1515.                 (int)value.GetSize(), (UINT)value.GetSize());
  1516.         }
  1517.         return;
  1518.  
  1519.     case CFieldExchange::BindFieldToColumn:
  1520.         {
  1521.             // Assumes all bound fields BEFORE unbound fields
  1522.             CODBCFieldInfo* pODBCInfo =
  1523.                 &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  1524.             UDWORD cbColumn = pODBCInfo->m_nPrecision;
  1525.  
  1526. #ifdef _DEBUG
  1527.             if (pODBCInfo->m_nSQLType != SQL_BINARY &&
  1528.                 pODBCInfo->m_nSQLType != SQL_VARBINARY &&
  1529.                 pODBCInfo->m_nSQLType != SQL_LONGVARBINARY)
  1530.             {
  1531.                 // Warn of possible field schema mismatch
  1532.                 if (afxTraceFlags & traceDatabase)
  1533.                     TRACE1("Warning: CByteArray converted from SQL type %ld.\n",
  1534.                         pODBCInfo->m_nSQLType);
  1535.             }
  1536. #endif // _DEBUG
  1537.  
  1538.             // Constrain to user specified max length
  1539.             if (cbColumn > (UINT)nMaxLength)
  1540.                 cbColumn = nMaxLength;
  1541.             value.SetSize(cbColumn);
  1542.             AFX_SQL_SYNC(::SQLBindCol(pFX->m_prs->m_hstmt, (UWORD)nField,
  1543.                 SQL_C_BINARY, &value[0], (LONG)cbColumn, plLength));
  1544.             if (!pFX->m_prs->Check(nRetCode))
  1545.                 pFX->m_prs->ThrowDBException(nRetCode);
  1546.  
  1547.             // Add the member address to the field map
  1548.             pFX->m_prs->m_mapFieldIndex.SetAt(&value, (void*)nField);
  1549.         }
  1550.         return;
  1551.  
  1552.     case CFieldExchange::Fixup:
  1553.         if (*plLength == SQL_NULL_DATA)
  1554.         {
  1555.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  1556.             value.SetSize(1);
  1557.             value[0] = AFX_RFX_BYTE_PSEUDO_NULL;
  1558.         }
  1559.         else
  1560.         {
  1561.             ASSERT(*plLength <= (LONG)nMaxLength);
  1562.             ((CDBByteArray&)value).SetLength((UINT)*plLength);
  1563.         }
  1564.         return;
  1565.  
  1566.     case CFieldExchange::SetFieldNull:
  1567.         if ((pFX->m_pvField == NULL &&
  1568.             pFX->m_nFieldType == CFieldExchange::outputColumn) ||
  1569.             pFX->m_pvField == &value)
  1570.         {
  1571.             if (pFX->m_bField)
  1572.             {
  1573.                 // Mark fields null
  1574.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  1575.                 value.SetSize(1);
  1576.                 value[0] = AFX_RFX_BYTE_PSEUDO_NULL;
  1577.                 *plLength = SQL_NULL_DATA;
  1578.             }
  1579.             else
  1580.             {
  1581.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1582.                 *plLength = value.GetSize();
  1583.             }
  1584. #ifdef _DEBUG
  1585.             pFX->m_nFieldFound = nField;
  1586. #endif
  1587.         }
  1588.         return;
  1589.  
  1590.     case CFieldExchange::MarkForAddNew:
  1591.         // can force writing of psuedo-null value (as a non-null) by setting field dirty
  1592.         if (value.GetSize() != 1 || value[0] != AFX_RFX_BYTE_PSEUDO_NULL)
  1593.         {
  1594.             pFX->m_prs->SetDirtyFieldStatus(nField - 1);
  1595.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1596.         }
  1597.         return;
  1598.  
  1599.     case CFieldExchange::MarkForUpdate:
  1600.         if (value.GetSize() != 1 || value[0] != AFX_RFX_BYTE_PSEUDO_NULL)
  1601.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1602.         bByRef = TRUE;
  1603.         goto LDefault;
  1604.  
  1605.     case CFieldExchange::StoreField:
  1606.         AfxStoreField(*pFX->m_prs, nField, &value);
  1607.         return;
  1608.  
  1609.     case CFieldExchange::LoadField:
  1610.         AfxLoadField(*pFX->m_prs, nField, &value, plLength);
  1611.         return;
  1612.  
  1613.     case CFieldExchange::AllocCache:
  1614.         {
  1615.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  1616.             pInfo->m_pvDataCache = new CByteArray;
  1617.             pInfo->m_nDataType = AFX_RFX_BINARY;
  1618.         }
  1619.         return;
  1620.  
  1621. #ifdef _DEBUG
  1622.     case CFieldExchange::DumpField:
  1623.         *pFX->m_pdcDump << "\n" << szName << ":";
  1624.         value.Dump(*pFX->m_pdcDump);
  1625.         return;
  1626. #endif // _DEBUG
  1627.  
  1628.     }
  1629. }
  1630.  
  1631. void AFXAPI RFX_Date(CFieldExchange* pFX, LPCTSTR szName, CTime& value)
  1632. {
  1633.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  1634.     ASSERT(AfxIsValidString(szName));
  1635.  
  1636.     RETCODE nRetCode;
  1637.     UINT nField;
  1638.     if (!pFX->IsFieldType(&nField))
  1639.         return;
  1640.  
  1641.     LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
  1642.         nField - 1, pFX->m_nFieldType);
  1643.     switch (pFX->m_nOperation)
  1644.     {
  1645.     default:
  1646. LDefault:
  1647.         pFX->Default(szName, &value, plLength, SQL_C_TIMESTAMP,
  1648.             sizeof(value), TIMESTAMP_PRECISION);
  1649.         return;
  1650.  
  1651.     case CFieldExchange::BindParam:
  1652.         {
  1653.             TIMESTAMP_STRUCT* pts;
  1654.             pFX->m_prs->m_bRebindParams = TRUE;
  1655.  
  1656.             if (pFX->m_prs->IsParamStatusNull(nField - 1))
  1657.             {
  1658.                 pts = NULL;
  1659.                 *plLength = SQL_NULL_DATA;
  1660.             }
  1661.             else
  1662.             {
  1663.                 // Allocate proxy array if necessary
  1664.                 if (pFX->m_prs->m_pvParamProxy == NULL)
  1665.                 {
  1666.                     pFX->m_prs->m_pvParamProxy = new void*[pFX->m_prs->m_nParams];
  1667.                     memset(pFX->m_prs->m_pvParamProxy, 0,
  1668.                         pFX->m_prs->m_nParams*sizeof(void*));
  1669.                     pFX->m_prs->m_nProxyParams = pFX->m_prs->m_nParams;
  1670.                 }
  1671.  
  1672.                 // Allocate TIMESTAMP_STRUCT if necessary for SQLBindParameter
  1673.                 if (pFX->m_prs->m_pvParamProxy[nField-1] == NULL)
  1674.                 {
  1675.                     pts = new TIMESTAMP_STRUCT;
  1676.                     pFX->m_prs->m_pvParamProxy[nField-1] = pts;
  1677.                 }
  1678.                 else
  1679.                     pts = (TIMESTAMP_STRUCT *)pFX->m_prs->m_pvParamProxy[nField-1];
  1680.  
  1681.                 pts->year = (SWORD)value.GetYear();
  1682.                 pts->month = (UWORD)value.GetMonth();
  1683.                 pts->day = (UWORD)value.GetDay();
  1684.                 pts->hour = (UWORD)value.GetHour();
  1685.                 pts->minute = (UWORD)value.GetMinute();
  1686.                 pts->second = (UWORD)value.GetSecond();
  1687.                 pts->fraction = 0;
  1688.                 *plLength = sizeof(TIMESTAMP_STRUCT);
  1689.             }
  1690.  
  1691.             AFX_SQL_SYNC(::SQLBindParameter(pFX->m_hstmt, (UWORD)nField,
  1692.                 (SWORD)pFX->m_nFieldType, SQL_C_TIMESTAMP, SQL_C_TIMESTAMP,
  1693.                 TIMESTAMP_PRECISION, 0, pts, 0, plLength));
  1694.             if (nRetCode != SQL_SUCCESS)
  1695.                 pFX->m_prs->ThrowDBException(nRetCode, pFX->m_hstmt);
  1696.  
  1697.             // Add the member address to the param map
  1698.             pFX->m_prs->m_mapParamIndex.SetAt(&value, (void*)nField);
  1699.         }
  1700.         return;
  1701.  
  1702.     case CFieldExchange::RebindParam:
  1703.         {
  1704.             *plLength = pFX->m_prs->IsParamStatusNull(nField - 1) ?
  1705.                 SQL_NULL_DATA : sizeof(TIMESTAMP_STRUCT);
  1706.             if (pFX->m_prs->m_nProxyParams != 0)
  1707.             {
  1708.                 // Fill buffer (expected by SQLBindParameter) with new param data
  1709.                 TIMESTAMP_STRUCT* pts;
  1710.                 pts = (TIMESTAMP_STRUCT *)pFX->m_prs->m_pvParamProxy[nField-1];
  1711.                 pts->year = (SWORD)value.GetYear();
  1712.                 pts->month = (UWORD)value.GetMonth();
  1713.                 pts->day = (UWORD)value.GetDay();
  1714.                 pts->hour = (UWORD)value.GetHour();
  1715.                 pts->minute = (UWORD)value.GetMinute();
  1716.                 pts->second = (UWORD)value.GetSecond();
  1717.                 pts->fraction = 0;
  1718.             }
  1719.         }
  1720.         return;
  1721.  
  1722.     case CFieldExchange::BindFieldToColumn:
  1723.         {
  1724. #ifdef _DEBUG
  1725.             // Assumes all bound fields BEFORE unbound fields
  1726.             CODBCFieldInfo* pODBCInfo =
  1727.                 &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  1728.  
  1729.             if (pODBCInfo->m_nSQLType != SQL_DATE &&
  1730.                 pODBCInfo->m_nSQLType != SQL_TIME &&
  1731.                 pODBCInfo->m_nSQLType != SQL_TIMESTAMP)
  1732.             {
  1733.                 // Warn of possible field schema mismatch
  1734.                 if (afxTraceFlags & traceDatabase)
  1735.                     TRACE1("Warning: CTime converted from SQL type %ld.\n",
  1736.                         pODBCInfo->m_nSQLType);
  1737.             }
  1738. #endif // _DEBUG
  1739.  
  1740.             // Allocate proxy array if necessary
  1741.             if (pFX->m_prs->m_pvFieldProxy == NULL)
  1742.             {
  1743.                 pFX->m_prs->m_pvFieldProxy = new void*[pFX->m_prs->m_nFields];
  1744.                 memset(pFX->m_prs->m_pvFieldProxy, 0,
  1745.                     pFX->m_prs->m_nFields*sizeof(void*));
  1746.                 pFX->m_prs->m_nProxyFields = pFX->m_prs->m_nFields;
  1747.             }
  1748.  
  1749.             // Allocate TIMESTAMP_STRUCT for SQLBindCol (not necessary on Requery)
  1750.             if (pFX->m_prs->m_pvFieldProxy[nField-1] == NULL)
  1751.                 pFX->m_prs->m_pvFieldProxy[nField-1] = new TIMESTAMP_STRUCT;
  1752.  
  1753.             AFX_SQL_SYNC(::SQLBindCol(pFX->m_prs->m_hstmt, (UWORD)nField,
  1754.                 SQL_C_TIMESTAMP, pFX->m_prs->m_pvFieldProxy[nField-1],
  1755.                 sizeof(TIMESTAMP_STRUCT), plLength));
  1756.             if (!pFX->m_prs->Check(nRetCode))
  1757.                 pFX->m_prs->ThrowDBException(nRetCode);
  1758.  
  1759.             // Add the member address to the field map
  1760.             pFX->m_prs->m_mapFieldIndex.SetAt(&value, (void*)nField);
  1761.         }
  1762.         return;
  1763.  
  1764.     case CFieldExchange::BindFieldForUpdate:
  1765.         if (pFX->m_prs->m_nProxyFields != 0)
  1766.         {
  1767.             // Fill buffer (expected by SQLSetPos) with new field data
  1768.             TIMESTAMP_STRUCT* pts;
  1769.             pts = (TIMESTAMP_STRUCT *)pFX->m_prs->m_pvFieldProxy[nField-1];
  1770.             pts->year = (SWORD)value.GetYear();
  1771.             pts->month = (UWORD)value.GetMonth();
  1772.             pts->day = (UWORD)value.GetDay();
  1773.             pts->hour = (UWORD)value.GetHour();
  1774.             pts->minute = (UWORD)value.GetMinute();
  1775.             pts->second = (UWORD)value.GetSecond();
  1776.             pts->fraction = 0;
  1777.  
  1778.             pFX->Default(szName, (void *)pts, plLength, SQL_C_TIMESTAMP,
  1779.                 sizeof(TIMESTAMP_STRUCT), TIMESTAMP_PRECISION);
  1780.         }
  1781.         return;
  1782.  
  1783.     case CFieldExchange::Fixup:
  1784.         if (*plLength == SQL_NULL_DATA)
  1785.         {
  1786.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  1787.             value = AFX_RFX_DATE_PSEUDO_NULL;
  1788.         }
  1789.         else
  1790.         {
  1791.             TIMESTAMP_STRUCT* pts =
  1792.                 (TIMESTAMP_STRUCT*)pFX->m_prs->m_pvFieldProxy[nField-1];
  1793.             if (pts->year < 1970 || pts->year > 2038)
  1794.             {
  1795.                 // Time value out of range, return NULL
  1796. #ifdef _DEBUG
  1797.                 if (afxTraceFlags & traceDatabase)
  1798.                     TRACE0("Warning: date value out of range, returning NULL value.\n");
  1799. #endif
  1800.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  1801.                 value = AFX_RFX_DATE_PSEUDO_NULL;
  1802.             }
  1803.             else
  1804.             {
  1805. #ifdef _DEBUG
  1806.                 if ((afxTraceFlags & traceDatabase) && pts->fraction != 0)
  1807.                     TRACE0("Warning: ignoring milliseconds.\n");
  1808. #endif
  1809.                 value = CTime(pts->year, pts->month, pts->day,
  1810.                     pts->hour, pts->minute, pts->second);
  1811.             }
  1812.         }
  1813.         return;
  1814.  
  1815.     case CFieldExchange::NameValue:
  1816.         if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
  1817.         {
  1818.             *pFX->m_pstr += szName;
  1819.             *pFX->m_pstr += '=';
  1820.         }
  1821.         // Fall through
  1822.  
  1823.     case CFieldExchange::Value:
  1824.         if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
  1825.         {
  1826.             TIMESTAMP_STRUCT* pts =
  1827.                 (TIMESTAMP_STRUCT*)pFX->m_prs->m_pvFieldProxy[nField-1];
  1828.             if (pFX->m_prs->IsFieldStatusNull(nField - 1))
  1829.             {
  1830.                 *plLength = SQL_NULL_DATA;
  1831.             }
  1832.             else
  1833.             {
  1834.                 pts->year = (SWORD)value.GetYear();
  1835.                 pts->month = (UWORD)value.GetMonth();
  1836.                 pts->day = (UWORD)value.GetDay();
  1837.                 pts->hour = (UWORD)value.GetHour();
  1838.                 pts->minute = (UWORD)value.GetMinute();
  1839.                 pts->second = (UWORD)value.GetSecond();
  1840.                 pts->fraction = 0;
  1841.                 *plLength = sizeof(TIMESTAMP_STRUCT);
  1842.             }
  1843.  
  1844.             // If optimizing for bulk add, only need lengths & proxy set correctly
  1845.             if(!(pFX->m_prs->m_dwOptions & CRecordset::optimizeBulkAdd))
  1846.             {
  1847.                 *pFX->m_pstr += '?';
  1848.                 *pFX->m_pstr += pFX->m_lpszSeparator;
  1849.                 pFX->m_nParamFields++;
  1850.  
  1851.                 // Assumes all bound fields BEFORE unbound fields
  1852.                 CODBCFieldInfo* pODBCInfo =
  1853.                     &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  1854.  
  1855.                 AFX_SQL_SYNC(::SQLBindParameter(pFX->m_hstmt,
  1856.                     (UWORD)pFX->m_nParamFields, SQL_PARAM_INPUT,
  1857.                     SQL_C_TIMESTAMP, pODBCInfo->m_nSQLType,
  1858.                     TIMESTAMP_PRECISION, 0, pts, 0, plLength));
  1859.             }
  1860.         }
  1861.         return;
  1862.  
  1863.     case CFieldExchange::SetFieldNull:
  1864.         if ((pFX->m_pvField == NULL &&
  1865.             pFX->m_nFieldType == CFieldExchange::outputColumn) ||
  1866.             pFX->m_pvField == &value)
  1867.         {
  1868.             if (pFX->m_bField)
  1869.             {
  1870.                 // Mark fields null
  1871.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  1872.                 value = AFX_RFX_DATE_PSEUDO_NULL;
  1873.                 *plLength = SQL_NULL_DATA;
  1874.             }
  1875.             else
  1876.             {
  1877.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1878.                 *plLength = sizeof(TIMESTAMP_STRUCT);
  1879.             }
  1880. #ifdef _DEBUG
  1881.             pFX->m_nFieldFound = nField;
  1882. #endif
  1883.         }
  1884.         return;
  1885.  
  1886.     case CFieldExchange::MarkForAddNew:
  1887.         {
  1888.             // can force writing of psuedo-null value (as a non-null) by setting field dirty
  1889.             CTime timeNull = AFX_RFX_DATE_PSEUDO_NULL;
  1890.             if (value != timeNull)
  1891.             {
  1892.                 pFX->m_prs->SetDirtyFieldStatus(nField - 1);
  1893.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1894.             }
  1895.         }
  1896.         return;
  1897.  
  1898.     case CFieldExchange::MarkForUpdate:
  1899.         {
  1900.             CTime timeNull = AFX_RFX_DATE_PSEUDO_NULL;
  1901.             if (value != timeNull)
  1902.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1903.         }
  1904.         goto LDefault;
  1905.  
  1906.     case CFieldExchange::LoadField:
  1907.         {
  1908.             // Get the field data
  1909.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  1910.  
  1911.             // Restore the status
  1912.             pFX->m_prs->SetFieldStatus(nField - 1, pInfo->m_bStatus);
  1913.  
  1914.             // If not NULL, restore the value, length and proxy
  1915.             if (!pFX->m_prs->IsFieldStatusNull(nField - 1))
  1916.             {
  1917.                 AfxCopyValueByRef(pInfo->m_pvDataCache, &value,
  1918.                     plLength, pInfo->m_nDataType);
  1919.  
  1920.                 // Restore proxy for correct WHERE CURRENT OF operations
  1921.                 TIMESTAMP_STRUCT* pts =
  1922.                     (TIMESTAMP_STRUCT*)pFX->m_prs->m_pvFieldProxy[nField-1];
  1923.  
  1924.                 pts->year = (SWORD)value.GetYear();
  1925.                 pts->month = (UWORD)value.GetMonth();
  1926.                 pts->day = (UWORD)value.GetDay();
  1927.                 pts->hour = (UWORD)value.GetHour();
  1928.                 pts->minute = (UWORD)value.GetMinute();
  1929.                 pts->second = (UWORD)value.GetSecond();
  1930.                 pts->fraction = 0;
  1931.             }
  1932.             else
  1933.                 *plLength = SQL_NULL_DATA;
  1934.  
  1935. #ifdef _DEBUG
  1936.             // Buffer address must not change - ODBC's SQLBindCol depends upon this
  1937.             if (pInfo->m_pvBindAddress != pFX->m_prs->m_pvFieldProxy[nField-1])
  1938.             {
  1939.                 TRACE1("Error: CString buffer (column %u) address has changed!\n",
  1940.                     nField);
  1941.                 ASSERT(FALSE);
  1942.             }
  1943. #endif // _DEBUG
  1944.         }
  1945.         return;
  1946.  
  1947.     case CFieldExchange::AllocCache:
  1948.         {
  1949.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  1950.             pInfo->m_pvDataCache = new CTime;
  1951.             pInfo->m_nDataType = AFX_RFX_DATE;
  1952.         }
  1953.         return;
  1954.  
  1955. #ifdef _DEBUG
  1956.     case CFieldExchange::DumpField:
  1957.         *pFX->m_pdcDump << "\n" << szName << " = " << value;
  1958.         return;
  1959. #endif // _DEBUG
  1960.  
  1961.     }
  1962. }
  1963.  
  1964. void AFXAPI RFX_Date(CFieldExchange* pFX, LPCTSTR szName,
  1965.     TIMESTAMP_STRUCT& value)
  1966. {
  1967.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  1968.     ASSERT(AfxIsValidString(szName));
  1969.  
  1970.     UINT nField;
  1971.     if (!pFX->IsFieldType(&nField))
  1972.         return;
  1973.  
  1974.     LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
  1975.         nField - 1, pFX->m_nFieldType);
  1976.     switch (pFX->m_nOperation)
  1977.     {
  1978.     case CFieldExchange::BindFieldToColumn:
  1979.         {
  1980. #ifdef _DEBUG
  1981.             // Assumes all bound fields BEFORE unbound fields
  1982.             CODBCFieldInfo* pODBCInfo =
  1983.                 &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  1984.  
  1985.             if (pODBCInfo->m_nSQLType != SQL_DATE &&
  1986.                 pODBCInfo->m_nSQLType != SQL_TIME &&
  1987.                 pODBCInfo->m_nSQLType != SQL_TIMESTAMP)
  1988.             {
  1989.                 // Warn of possible field schema mismatch
  1990.                 if (afxTraceFlags & traceDatabase)
  1991.                     TRACE1("Warning: TIMESTAMP_STRUCT converted from SQL type %ld.\n",
  1992.                         pODBCInfo->m_nSQLType);
  1993.             }
  1994. #endif // _DEBUG
  1995.             // fall through
  1996.         }
  1997.  
  1998.     default:
  1999. LDefault:
  2000.         pFX->Default(szName, &value, plLength, SQL_C_TIMESTAMP,
  2001.             sizeof(value), TIMESTAMP_PRECISION);
  2002.         return;
  2003.  
  2004.     case CFieldExchange::Fixup:
  2005.         if (*plLength == SQL_NULL_DATA)
  2006.         {
  2007.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  2008.             value.year = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  2009.             value.month = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  2010.             value.day = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  2011.             value.hour = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  2012.             value.minute = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  2013.             value.second = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  2014.             value.fraction = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  2015.         }
  2016.         return;
  2017.  
  2018.     case CFieldExchange::SetFieldNull:
  2019.         if ((pFX->m_pvField == NULL &&
  2020.             pFX->m_nFieldType == CFieldExchange::outputColumn) ||
  2021.             pFX->m_pvField == &value)
  2022.         {
  2023.             if (pFX->m_bField)
  2024.             {
  2025.                 // Mark fields null
  2026.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  2027.                 value.year = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  2028.                 value.month = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  2029.                 value.day = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  2030.                 value.hour = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  2031.                 value.minute = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  2032.                 value.second = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  2033.                 value.fraction = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  2034.                 *plLength = SQL_NULL_DATA;
  2035.             }
  2036.             else
  2037.             {
  2038.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  2039.                 *plLength = sizeof(TIMESTAMP_STRUCT);
  2040.             }
  2041. #ifdef _DEBUG
  2042.             pFX->m_nFieldFound = nField;
  2043. #endif
  2044.         }
  2045.         return;
  2046.  
  2047.     case CFieldExchange::MarkForAddNew:
  2048.         // can force writing of psuedo-null value (as a non-null) by setting field dirty
  2049.         if (!(value.year == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  2050.             value.month == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  2051.             value.day == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  2052.             value.hour == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  2053.             value.minute == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  2054.             value.second == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  2055.             value.fraction == AFX_RFX_TIMESTAMP_PSEUDO_NULL ))
  2056.         {
  2057.             pFX->m_prs->SetDirtyFieldStatus(nField - 1);
  2058.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  2059.         }
  2060.         return;
  2061.  
  2062.     case CFieldExchange::MarkForUpdate:
  2063.         if (!(value.year == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  2064.             value.month == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  2065.             value.day == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  2066.             value.hour == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  2067.             value.minute == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  2068.             value.second == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  2069.             value.fraction == AFX_RFX_TIMESTAMP_PSEUDO_NULL ))
  2070.         {
  2071.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  2072.         }
  2073.         goto LDefault;
  2074.  
  2075.     case CFieldExchange::AllocCache:
  2076.         {
  2077.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  2078.             pInfo->m_pvDataCache = new TIMESTAMP_STRUCT;
  2079.             pInfo->m_nDataType = AFX_RFX_TIMESTAMP;
  2080.         }
  2081.         return;
  2082.  
  2083. #ifdef _DEBUG
  2084.     case CFieldExchange::DumpField:
  2085.         *pFX->m_pdcDump << "\n" << szName << ".year = " << (int)value.year;
  2086.         *pFX->m_pdcDump << "\n" << szName << ".month = " << value.month;
  2087.         *pFX->m_pdcDump << "\n" << szName << ".day = " << value.day;
  2088.         *pFX->m_pdcDump << "\n" << szName << ".hour = " << value.hour;
  2089.         *pFX->m_pdcDump << "\n" << szName << ".minute = " << value.minute;
  2090.         *pFX->m_pdcDump << "\n" << szName << ".second = " << value.second;
  2091.         *pFX->m_pdcDump << "\n" << szName << ".fraction = " << value.fraction;
  2092.         return;
  2093. #endif // _DEBUG
  2094.  
  2095.     }
  2096. }
  2097.  
  2098. void AFXAPI RFX_Date(CFieldExchange* pFX, LPCTSTR szName,
  2099.     COleDateTime& value)
  2100. {
  2101.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  2102.     ASSERT(AfxIsValidString(szName));
  2103.  
  2104.     UINT nField;
  2105.     if (!pFX->IsFieldType(&nField))
  2106.         return;
  2107.  
  2108.     RETCODE nRetCode;
  2109.     LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
  2110.         nField - 1, pFX->m_nFieldType);
  2111.     switch (pFX->m_nOperation)
  2112.     {
  2113.     case CFieldExchange::BindParam:
  2114.         {
  2115.             TIMESTAMP_STRUCT* pts;
  2116.             pFX->m_prs->m_bRebindParams = TRUE;
  2117.  
  2118.             if (pFX->m_prs->IsParamStatusNull(nField - 1))
  2119.             {
  2120.                 pts = NULL;
  2121.                 *plLength = SQL_NULL_DATA;
  2122.             }
  2123.             else
  2124.             {
  2125.                 // Allocate proxy array if necessary
  2126.                 if (pFX->m_prs->m_pvParamProxy == NULL)
  2127.                 {
  2128.                     pFX->m_prs->m_pvParamProxy = new void*[pFX->m_prs->m_nParams];
  2129.                     memset(pFX->m_prs->m_pvParamProxy, 0,
  2130.                         pFX->m_prs->m_nParams*sizeof(void*));
  2131.                     pFX->m_prs->m_nProxyParams = pFX->m_prs->m_nParams;
  2132.                 }
  2133.  
  2134.                 // Allocate TIMESTAMP_STRUCT if necessary for SQLBindParameter
  2135.                 if (pFX->m_prs->m_pvParamProxy[nField-1] == NULL)
  2136.                 {
  2137.                     pts = new TIMESTAMP_STRUCT;
  2138.                     pFX->m_prs->m_pvParamProxy[nField-1] = pts;
  2139.                 }
  2140.                 else
  2141.                     pts = (TIMESTAMP_STRUCT *)pFX->m_prs->m_pvParamProxy[nField-1];
  2142.  
  2143.                 pts->year = (SWORD)value.GetYear();
  2144.                 pts->month = (UWORD)value.GetMonth();
  2145.                 pts->day = (UWORD)value.GetDay();
  2146.                 pts->hour = (UWORD)value.GetHour();
  2147.                 pts->minute = (UWORD)value.GetMinute();
  2148.                 pts->second = (UWORD)value.GetSecond();
  2149.                 pts->fraction = 0;
  2150.                 *plLength = sizeof(TIMESTAMP_STRUCT);
  2151.             }
  2152.  
  2153.             AFX_SQL_SYNC(::SQLBindParameter(pFX->m_hstmt, (UWORD)nField,
  2154.                 (SWORD)pFX->m_nFieldType, SQL_C_TIMESTAMP, SQL_C_TIMESTAMP,
  2155.                 TIMESTAMP_PRECISION, 0, pts, 0, plLength));
  2156.             if (nRetCode != SQL_SUCCESS)
  2157.                 pFX->m_prs->ThrowDBException(nRetCode, pFX->m_hstmt);
  2158.  
  2159.             // Add the member address to the param map
  2160.             pFX->m_prs->m_mapParamIndex.SetAt(&value, (void*)nField);
  2161.         }
  2162.         return;
  2163.  
  2164.     case CFieldExchange::NameValue:
  2165.         if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
  2166.         {
  2167.             *pFX->m_pstr += szName;
  2168.             *pFX->m_pstr += '=';
  2169.         }
  2170.         // Fall through
  2171.  
  2172.     case CFieldExchange::Value:
  2173.         if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
  2174.         {
  2175.             TIMESTAMP_STRUCT* pts =
  2176.                 (TIMESTAMP_STRUCT*)pFX->m_prs->m_pvFieldProxy[nField-1];
  2177.             if (pFX->m_prs->IsFieldStatusNull(nField - 1))
  2178.             {
  2179.                 *plLength = SQL_NULL_DATA;
  2180.             }
  2181.             else
  2182.             {
  2183.                 pts->year = (SWORD)value.GetYear();
  2184.                 pts->month = (UWORD)value.GetMonth();
  2185.                 pts->day = (UWORD)value.GetDay();
  2186.                 pts->hour = (UWORD)value.GetHour();
  2187.                 pts->minute = (UWORD)value.GetMinute();
  2188.                 pts->second = (UWORD)value.GetSecond();
  2189.                 pts->fraction = 0;
  2190.                 *plLength = sizeof(TIMESTAMP_STRUCT);
  2191.             }
  2192.  
  2193.             // If optimizing for bulk add, only need lengths & proxy set correctly
  2194.             if(!(pFX->m_prs->m_dwOptions & CRecordset::optimizeBulkAdd))
  2195.             {
  2196.                 *pFX->m_pstr += '?';
  2197.                 *pFX->m_pstr += pFX->m_lpszSeparator;
  2198.                 pFX->m_nParamFields++;
  2199.  
  2200.                 // Assumes all bound fields BEFORE unbound fields
  2201.                 CODBCFieldInfo* pODBCInfo =
  2202.                     &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  2203.  
  2204.                 AFX_SQL_SYNC(::SQLBindParameter(pFX->m_hstmt,
  2205.                     (UWORD)pFX->m_nParamFields, SQL_PARAM_INPUT,
  2206.                     SQL_C_TIMESTAMP, pODBCInfo->m_nSQLType,
  2207.                     TIMESTAMP_PRECISION, 0, pts, 0, plLength));
  2208.             }
  2209.         }
  2210.         return;
  2211.  
  2212.     case CFieldExchange::RebindParam:
  2213.         {
  2214.             *plLength = pFX->m_prs->IsParamStatusNull(nField - 1) ?
  2215.                 SQL_NULL_DATA : sizeof(TIMESTAMP_STRUCT);
  2216.             if (pFX->m_prs->m_nProxyParams != 0)
  2217.             {
  2218.                 // Fill buffer (expected by SQLBindParameter) with new param data
  2219.                 TIMESTAMP_STRUCT* pts;
  2220.                 pts = (TIMESTAMP_STRUCT *)pFX->m_prs->m_pvParamProxy[nField-1];
  2221.                 pts->year = (SWORD)value.GetYear();
  2222.                 pts->month = (UWORD)value.GetMonth();
  2223.                 pts->day = (UWORD)value.GetDay();
  2224.                 pts->hour = (UWORD)value.GetHour();
  2225.                 pts->minute = (UWORD)value.GetMinute();
  2226.                 pts->second = (UWORD)value.GetSecond();
  2227.                 pts->fraction = 0;
  2228.             }
  2229.         }
  2230.         return;
  2231.  
  2232.     case CFieldExchange::BindFieldForUpdate:
  2233.         if (pFX->m_prs->m_nProxyFields != 0)
  2234.         {
  2235.             // Fill buffer (expected by SQLSetPos) with new field data
  2236.             TIMESTAMP_STRUCT* pts;
  2237.             pts = (TIMESTAMP_STRUCT *)pFX->m_prs->m_pvFieldProxy[nField-1];
  2238.             pts->year = (SWORD)value.GetYear();
  2239.             pts->month = (UWORD)value.GetMonth();
  2240.             pts->day = (UWORD)value.GetDay();
  2241.             pts->hour = (UWORD)value.GetHour();
  2242.             pts->minute = (UWORD)value.GetMinute();
  2243.             pts->second = (UWORD)value.GetSecond();
  2244.             pts->fraction = 0;
  2245.  
  2246.             pFX->Default(szName, (void *)pts, plLength, SQL_C_TIMESTAMP,
  2247.                 sizeof(TIMESTAMP_STRUCT), TIMESTAMP_PRECISION);
  2248.         }
  2249.         return;
  2250.  
  2251.     case CFieldExchange::LoadField:
  2252.         {
  2253.             // Get the field data
  2254.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  2255.  
  2256.             // Restore the status
  2257.             pFX->m_prs->SetFieldStatus(nField - 1, pInfo->m_bStatus);
  2258.  
  2259.             // If not NULL, restore the value, length and proxy
  2260.             if (!pFX->m_prs->IsFieldStatusNull(nField - 1))
  2261.             {
  2262.                 AfxCopyValueByRef(pInfo->m_pvDataCache, &value,
  2263.                     plLength, pInfo->m_nDataType);
  2264.  
  2265.                 // Restore proxy for correct WHERE CURRENT OF operations
  2266.                 TIMESTAMP_STRUCT* pts =
  2267.                     (TIMESTAMP_STRUCT*)pFX->m_prs->m_pvFieldProxy[nField-1];
  2268.  
  2269.                 pts->year = (SWORD)value.GetYear();
  2270.                 pts->month = (UWORD)value.GetMonth();
  2271.                 pts->day = (UWORD)value.GetDay();
  2272.                 pts->hour = (UWORD)value.GetHour();
  2273.                 pts->minute = (UWORD)value.GetMinute();
  2274.                 pts->second = (UWORD)value.GetSecond();
  2275.                 pts->fraction = 0;
  2276.             }
  2277.             else
  2278.                 *plLength = SQL_NULL_DATA;
  2279.  
  2280. #ifdef _DEBUG
  2281.             // Buffer address must not change - ODBC's SQLBindCol depends upon this
  2282.             if (pInfo->m_pvBindAddress != pFX->m_prs->m_pvFieldProxy[nField-1])
  2283.             {
  2284.                 TRACE1("Error: CString buffer (column %u) address has changed!\n",
  2285.                     nField);
  2286.                 ASSERT(FALSE);
  2287.             }
  2288. #endif // _DEBUG
  2289.         }
  2290.         return;
  2291.  
  2292.     case CFieldExchange::BindFieldToColumn:
  2293.         {
  2294. #ifdef _DEBUG
  2295.             // Assumes all bound fields BEFORE unbound fields
  2296.             CODBCFieldInfo* pODBCInfo =
  2297.                 &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  2298.  
  2299.             if (pODBCInfo->m_nSQLType != SQL_DATE &&
  2300.                 pODBCInfo->m_nSQLType != SQL_TIME &&
  2301.                 pODBCInfo->m_nSQLType != SQL_TIMESTAMP)
  2302.             {
  2303.                 // Warn of possible field schema mismatch
  2304.                 if (afxTraceFlags & traceDatabase)
  2305.                     TRACE1("Warning: COleDateTime converted from SQL type %ld.\n",
  2306.                         pODBCInfo->m_nSQLType);
  2307.             }
  2308. #endif // _DEBUG
  2309.  
  2310.             // Allocate proxy array if necessary
  2311.             if (pFX->m_prs->m_pvFieldProxy == NULL)
  2312.             {
  2313.                 pFX->m_prs->m_pvFieldProxy = new void*[pFX->m_prs->m_nFields];
  2314.                 memset(pFX->m_prs->m_pvFieldProxy, 0,
  2315.                     pFX->m_prs->m_nFields*sizeof(void*));
  2316.                 pFX->m_prs->m_nProxyFields = pFX->m_prs->m_nFields;
  2317.             }
  2318.  
  2319.             // Allocate TIMESTAMP_STRUCT for SQLBindCol (not necessary on Requery)
  2320.             if (pFX->m_prs->m_pvFieldProxy[nField-1] == NULL)
  2321.                 pFX->m_prs->m_pvFieldProxy[nField-1] = new TIMESTAMP_STRUCT;
  2322.  
  2323.             AFX_SQL_SYNC(::SQLBindCol(pFX->m_prs->m_hstmt, (UWORD)nField,
  2324.                 SQL_C_TIMESTAMP, pFX->m_prs->m_pvFieldProxy[nField-1],
  2325.                 sizeof(TIMESTAMP_STRUCT), plLength));
  2326.             if (!pFX->m_prs->Check(nRetCode))
  2327.                 pFX->m_prs->ThrowDBException(nRetCode);
  2328.  
  2329.             // Add the member address to the field map
  2330.             pFX->m_prs->m_mapFieldIndex.SetAt(&value, (void*)nField);
  2331.         }
  2332.         return;
  2333.  
  2334.     default:
  2335. LDefault:
  2336.         pFX->Default(szName, &value, plLength, SQL_C_TIMESTAMP,
  2337.             sizeof(value), TIMESTAMP_PRECISION);
  2338.         return;
  2339.  
  2340.     case CFieldExchange::AllocCache:
  2341.         {
  2342.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  2343.             pInfo->m_pvDataCache = new COleDateTime;
  2344.             pInfo->m_nDataType = AFX_RFX_OLEDATE;
  2345.         }
  2346.         return;
  2347.  
  2348.     case CFieldExchange::Fixup:
  2349.         if (*plLength == SQL_NULL_DATA)
  2350.         {
  2351.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  2352.             value.SetStatus(COleDateTime::null);
  2353.         }
  2354.         else
  2355.         {
  2356.             TIMESTAMP_STRUCT* pts =
  2357.                 (TIMESTAMP_STRUCT*)pFX->m_prs->m_pvFieldProxy[nField-1];
  2358. #ifdef _DEBUG
  2359.             if ((afxTraceFlags & traceDatabase) && pts->fraction != 0)
  2360.                 TRACE0("Warning: ignoring milliseconds.\n");
  2361. #endif
  2362.             value = COleDateTime(pts->year, pts->month, pts->day,
  2363.                 pts->hour, pts->minute, pts->second);
  2364.         }
  2365.         return;
  2366.  
  2367.     case CFieldExchange::SetFieldNull:
  2368.         if ((pFX->m_pvField == NULL &&
  2369.             pFX->m_nFieldType == CFieldExchange::outputColumn) ||
  2370.             pFX->m_pvField == &value)
  2371.         {
  2372.             if (pFX->m_bField)
  2373.             {
  2374.                 // Mark fields null
  2375.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  2376.                 value.SetStatus(COleDateTime::null);
  2377.                 *plLength = SQL_NULL_DATA;
  2378.             }
  2379.             else
  2380.             {
  2381.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  2382.                 *plLength = sizeof(TIMESTAMP_STRUCT);
  2383.             }
  2384. #ifdef _DEBUG
  2385.             pFX->m_nFieldFound = nField;
  2386. #endif
  2387.         }
  2388.         return;
  2389.  
  2390.     case CFieldExchange::MarkForAddNew:
  2391.         // can force writing of psuedo-null value (as a non-null) by setting field dirty
  2392.         if (value.GetStatus() != COleDateTime::null)
  2393.         {
  2394.             pFX->m_prs->SetDirtyFieldStatus(nField - 1);
  2395.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  2396.         }
  2397.         return;
  2398.  
  2399.     case CFieldExchange::MarkForUpdate:
  2400.         if (value.GetStatus() != COleDateTime::null)
  2401.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  2402.         goto LDefault;
  2403.  
  2404. #ifdef _DEBUG
  2405.     case CFieldExchange::DumpField:
  2406.         CString str;
  2407.         str = value.Format();
  2408.         *pFX->m_pdcDump << "\n" << str;
  2409.         return;
  2410. #endif // _DEBUG
  2411.  
  2412.     }
  2413. }
  2414.  
  2415. void AFXAPI RFX_LongBinary(CFieldExchange* pFX, LPCTSTR szName,
  2416.     CLongBinary& value)
  2417. {
  2418.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  2419.     ASSERT(AfxIsValidString(szName));
  2420.  
  2421.     RETCODE nRetCode;
  2422.     UINT nField;
  2423.     if (!pFX->IsFieldType(&nField))
  2424.         return;
  2425.  
  2426.     LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
  2427.         nField - 1, pFX->m_nFieldType);
  2428.     switch (pFX->m_nOperation)
  2429.     {
  2430.     case CFieldExchange::Name:
  2431.         pFX->m_prs->m_bLongBinaryColumns = TRUE;
  2432.         pFX->Default(szName, &value, plLength, SQL_C_DEFAULT, 0, 0);
  2433.         return;
  2434.  
  2435.     case CFieldExchange::BindFieldToColumn:
  2436.         // Don't bind if using update SQL, simply do SQLGetData on Fixup
  2437.         if (!pFX->m_prs->m_bUseUpdateSQL && pFX->m_prs->CanUpdate())
  2438.         {
  2439.             // Bind for updates with cb=0 now. Driver may not support post Execute or ExtendedFetch binding
  2440.             AFX_SQL_SYNC(::SQLBindCol(pFX->m_prs->m_hstmt, (UWORD)nField, SQL_C_DEFAULT,
  2441.                 &value, 0, plLength));
  2442.             if (!pFX->m_prs->Check(nRetCode))
  2443.                 pFX->m_prs->ThrowDBException(nRetCode);
  2444.         }
  2445.  
  2446.         // Add the member address to the field map
  2447.         pFX->m_prs->m_mapFieldIndex.SetAt(&value, (void*)nField);
  2448.         return;
  2449.  
  2450. #ifdef _DEBUG
  2451.     case CFieldExchange::BindParam:
  2452.         // CLongBinary parameters are not supported
  2453.         ASSERT(FALSE);
  2454.  
  2455.     case CFieldExchange::MarkForAddNew:
  2456.     case CFieldExchange::MarkForUpdate:
  2457.         // We do not archive LongBinary values
  2458.     case CFieldExchange::StoreField:
  2459.     case CFieldExchange::LoadField:
  2460.         // We do not archive LongBinary values
  2461. #endif // _DEBUG
  2462.     default:
  2463.         return;
  2464.  
  2465.     case CFieldExchange::Fixup:
  2466.         // Get the size of the long binary field
  2467.         *plLength = pFX->GetLongBinarySize(nField);
  2468.  
  2469.         // Get the data if necessary
  2470.         if (*plLength != SQL_NULL_DATA)
  2471.             pFX->GetLongBinaryData(nField, value, plLength);
  2472.  
  2473.         // Set the status and length
  2474.         if (*plLength == SQL_NULL_DATA)
  2475.         {
  2476.             // Field NULL, set length and status
  2477.             value.m_dwDataLength = 0;
  2478.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  2479.         }
  2480.         else
  2481.         {
  2482.             // Field not NULL, clear the status (length already set)
  2483.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  2484.         }
  2485.  
  2486.         return;
  2487.  
  2488.     case CFieldExchange::NameValue:
  2489.         if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
  2490.         {
  2491.             *pFX->m_pstr += szName;
  2492.             *pFX->m_pstr += '=';
  2493.         }
  2494.  
  2495.         // Fall through
  2496.     case CFieldExchange::Value:
  2497.         if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
  2498.         {
  2499.             // If user marked column NULL, reflect this in length
  2500.             if (pFX->m_prs->IsFieldStatusNull(nField - 1))
  2501.                 *plLength = SQL_NULL_DATA;
  2502.             else
  2503.             {
  2504.                 // Indicate data will be sent after SQLExecute
  2505.                 // Length is signed value, it's limited by LONG_MAX
  2506.                 if (value.m_dwDataLength >
  2507.                     (ULONG)(LONG_MAX - labs(SQL_LEN_DATA_AT_EXEC_OFFSET)))
  2508.                 {
  2509.                     ASSERT(FALSE);
  2510.                     *plLength = LONG_MAX - labs(SQL_LEN_DATA_AT_EXEC_OFFSET);
  2511.                 }
  2512.                 else
  2513.                     *plLength = value.m_dwDataLength;
  2514.  
  2515.                 *plLength = SQL_LEN_DATA_AT_EXEC(*plLength);
  2516.             }
  2517.  
  2518.             // If optimizing for bulk add, only need lengths set correctly
  2519.             if(!(pFX->m_prs->m_dwOptions & CRecordset::optimizeBulkAdd))
  2520.             {
  2521.                 *pFX->m_pstr += '?';
  2522.                 *pFX->m_pstr += pFX->m_lpszSeparator;
  2523.                 pFX->m_nParamFields++;
  2524.  
  2525.                 // Assumes all bound fields BEFORE unbound fields
  2526.                 CODBCFieldInfo* pODBCInfo =
  2527.                     &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  2528.  
  2529.                 AFX_SQL_SYNC(::SQLBindParameter(pFX->m_hstmt,
  2530.                     (UWORD)pFX->m_nParamFields, SQL_PARAM_INPUT,
  2531.                     SQL_C_DEFAULT, pODBCInfo->m_nSQLType,
  2532.                     value.m_dwDataLength, 0, &value, 0, plLength));
  2533.                 if (nRetCode != SQL_SUCCESS)
  2534.                     pFX->m_prs->ThrowDBException(nRetCode, pFX->m_hstmt);
  2535.             }
  2536.         }
  2537.         return;
  2538.  
  2539.     case CFieldExchange::BindFieldForUpdate:
  2540.         if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
  2541.         {
  2542.             // If user marked column NULL, reflect this in length
  2543.             if (pFX->m_prs->IsFieldStatusNull(nField - 1))
  2544.                 *plLength = SQL_NULL_DATA;
  2545.             else
  2546.             {
  2547.                 // Length is signed value, it's limited by LONG_MAX
  2548.                 if (value.m_dwDataLength >
  2549.                     (ULONG)(LONG_MAX - labs(SQL_LEN_DATA_AT_EXEC_OFFSET)))
  2550.                 {
  2551.                     ASSERT(FALSE);
  2552.                     *plLength = LONG_MAX - labs(SQL_LEN_DATA_AT_EXEC_OFFSET);
  2553.                 }
  2554.                 else
  2555.                     *plLength = value.m_dwDataLength;
  2556.  
  2557.                 *plLength = SQL_LEN_DATA_AT_EXEC(*plLength);
  2558.             }
  2559.         }
  2560.         else
  2561.             *plLength = SQL_IGNORE;
  2562.  
  2563.         return;
  2564.  
  2565.     case CFieldExchange::UnbindFieldForUpdate:
  2566.         *plLength = value.m_dwDataLength;
  2567.         return;
  2568.  
  2569.     case CFieldExchange::SetFieldNull:
  2570.         if ((pFX->m_pvField == NULL &&
  2571.             pFX->m_nFieldType == CFieldExchange::outputColumn) ||
  2572.             pFX->m_pvField == &value)
  2573.         {
  2574.             if (pFX->m_bField)
  2575.             {
  2576.                 // Mark fields null
  2577.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  2578.                 value.m_dwDataLength = 0;
  2579.                 *plLength = SQL_NULL_DATA;
  2580.             }
  2581.             else
  2582.             {
  2583.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  2584.  
  2585.                 // Length is signed value, it's limited by LONG_MAX
  2586.                 if (value.m_dwDataLength >
  2587.                     (ULONG)(LONG_MAX - labs(SQL_LEN_DATA_AT_EXEC_OFFSET)))
  2588.                 {
  2589.                     ASSERT(FALSE);
  2590.                     *plLength = LONG_MAX - labs(SQL_LEN_DATA_AT_EXEC_OFFSET);
  2591.                 }
  2592.                 else
  2593.                     *plLength = value.m_dwDataLength;
  2594.  
  2595.                 *plLength = SQL_LEN_DATA_AT_EXEC(*plLength);
  2596.             }
  2597. #ifdef _DEBUG
  2598.             pFX->m_nFieldFound = nField;
  2599. #endif
  2600.         }
  2601.         return;
  2602.  
  2603.     case CFieldExchange::AllocCache:
  2604.         // Caching not supported for long binary
  2605.         return;
  2606.  
  2607. #ifdef _DEBUG
  2608.     case CFieldExchange::DumpField:
  2609.         *pFX->m_pdcDump << "\n" << szName << " = ";
  2610.         value.Dump(*pFX->m_pdcDump);
  2611.         return;
  2612. #endif // _DEBUG
  2613.  
  2614.     }
  2615. }
  2616.  
  2617. long CFieldExchange::GetLongBinarySize(int nField)
  2618. {
  2619.     RETCODE nRetCode;
  2620.     int nDummy;
  2621.     long lSize;
  2622.  
  2623.     // Give empty buffer to find size of entire LongBinary
  2624.     AFX_ODBC_CALL(::SQLGetData(m_prs->m_hstmt,
  2625.         (UWORD)nField, SQL_C_DEFAULT, &nDummy, 0, &lSize));
  2626.  
  2627.     switch (nRetCode)
  2628.     {
  2629.         case SQL_SUCCESS:
  2630.             break;
  2631.  
  2632.         case SQL_SUCCESS_WITH_INFO:
  2633. #ifdef _DEBUG
  2634.             if (afxTraceFlags & traceDatabase)
  2635.             {
  2636.                 CDBException* e = new CDBException(nRetCode);
  2637.                 e->BuildErrorString(m_prs->m_pDatabase,
  2638.                     m_prs->m_hstmt, FALSE);
  2639.  
  2640.                 // Ignore data truncated messages
  2641.                 if (e->m_strStateNativeOrigin.Find(_T("State:01004")) < 0)
  2642.                 {
  2643.                     TRACE0("Warning: ODBC Success With Info, ");
  2644.                     e->TraceErrorMessage(e->m_strError);
  2645.                     e->TraceErrorMessage(e->m_strStateNativeOrigin);
  2646.                 }
  2647.                 e->Delete();
  2648.             }
  2649. #endif // _DEBUG
  2650.             break;
  2651.  
  2652.         default:
  2653.             m_prs->ThrowDBException(nRetCode);
  2654.     }
  2655.  
  2656.     return lSize;
  2657. }
  2658.  
  2659. void CFieldExchange::GetLongBinaryData(int nField, CLongBinary& lb,
  2660.     long* plSize)
  2661. {
  2662.     RETCODE nRetCode;
  2663.     long lActualDataSize = 0;
  2664.     long lChunkDataSize;
  2665.     long lReallocSize;
  2666.     const BYTE* lpLongBinary;
  2667.  
  2668.     lb.m_dwDataLength = 0;
  2669.  
  2670.     // Determine initial chunk sizes
  2671.     if (*plSize == SQL_NO_TOTAL)
  2672.     {
  2673.         lChunkDataSize = m_lDefaultLBFetchSize;
  2674.         lReallocSize = m_lDefaultLBReallocSize;
  2675.     }
  2676.     else
  2677.     {
  2678.         lChunkDataSize = *plSize;
  2679.         lReallocSize = *plSize;
  2680.     }
  2681.  
  2682.     do
  2683.     {
  2684.         // Check if CLongBianary is big enough
  2685.         lpLongBinary = ReallocLongBinary(lb,
  2686.             (long)lb.m_dwDataLength + lChunkDataSize,
  2687.             (long)lb.m_dwDataLength + lReallocSize);
  2688.  
  2689.         // Adjust the pointer so that data added at end
  2690.         lpLongBinary += lb.m_dwDataLength;
  2691.  
  2692.         AFX_ODBC_CALL(::SQLGetData(m_prs->m_hstmt, (UWORD)nField,
  2693.             SQL_C_BINARY, (UCHAR*)lpLongBinary, lChunkDataSize, &lActualDataSize));
  2694.         ::GlobalUnlock(lb.m_hData);
  2695.  
  2696.         switch (nRetCode)
  2697.         {
  2698.             case SQL_NO_DATA_FOUND:
  2699.                 m_prs->SetNullFieldStatus(nField - 1);
  2700.                 *plSize = SQL_NULL_DATA;
  2701.                 break;
  2702.  
  2703.             case SQL_SUCCESS:
  2704.                 // All data fetched
  2705.                 lb.m_dwDataLength += lActualDataSize;
  2706.                 *plSize = (long)lb.m_dwDataLength;
  2707.                 return;
  2708.  
  2709.             case SQL_SUCCESS_WITH_INFO:
  2710.                 {
  2711.                     CDBException* e = new CDBException(nRetCode);
  2712.                     e->BuildErrorString(m_prs->m_pDatabase, m_prs->m_hstmt,
  2713.                         FALSE);
  2714.  
  2715.                     // Ignore data truncated messages
  2716.                     if (e->m_strStateNativeOrigin.Find(_T("State:01004")) < 0)
  2717.                     {
  2718. #ifdef _DEBUG
  2719.                         if (afxTraceFlags & traceDatabase)
  2720.                         {
  2721.                             TRACE0("Warning: ODBC Success With Info, ");
  2722.                             e->TraceErrorMessage(e->m_strError);
  2723.                             e->TraceErrorMessage(e->m_strStateNativeOrigin);
  2724.                         }
  2725. #endif // _DEBUG
  2726.  
  2727.                         // Must be some other warning, should be finished
  2728.                         lb.m_dwDataLength += lActualDataSize;
  2729.                     }
  2730.                     else
  2731.                     {
  2732.                         // Should only happen if further calls to SQLGetData necessary
  2733.  
  2734.                         // Increment the length by the chunk size for subsequent SQLGetData call
  2735.                         lb.m_dwDataLength += lChunkDataSize;
  2736.  
  2737.                         // Recalculate chunk and alloc sizes
  2738.                         lChunkDataSize = m_prs->GetLBFetchSize(lChunkDataSize);
  2739.                         lReallocSize = m_prs->GetLBReallocSize(lReallocSize);
  2740.                         // force loop to repeat
  2741.                         lActualDataSize = SQL_NO_TOTAL;
  2742.                     }
  2743.  
  2744.                     *plSize = (long)lb.m_dwDataLength;
  2745.                     e->Delete();
  2746.                 }
  2747.                 break;
  2748.  
  2749.             default:
  2750.                 m_prs->ThrowDBException(nRetCode);
  2751.         }
  2752.  
  2753.     } while (lActualDataSize == SQL_NO_TOTAL);
  2754. }
  2755.  
  2756. BYTE* CFieldExchange::ReallocLongBinary(CLongBinary& lb, long lSizeRequired,
  2757.     long lReallocSize)
  2758. {
  2759.     // realloc max of lSizeRequired, lReallocSize (m_dwDataLength untouched)
  2760.  
  2761.     if (lSizeRequired < 0)
  2762.     {
  2763.         ASSERT(FALSE);
  2764.         lSizeRequired = 0;
  2765.     }
  2766.  
  2767.     HGLOBAL hOldData = NULL;
  2768.  
  2769.     // Allocate or Realloc as req'd
  2770.     if (lb.m_hData == NULL)
  2771.         lb.m_hData = ::GlobalAlloc(GMEM_MOVEABLE, lReallocSize);
  2772.     else
  2773.     {
  2774.         DWORD dwSize = ::GlobalSize(lb.m_hData);
  2775.         if (dwSize < (DWORD)lSizeRequired)
  2776.         {
  2777.             // Save the old handle in case ReAlloc fails
  2778.             hOldData = lb.m_hData;
  2779.  
  2780.             // Allocate more memory if necessary
  2781.             lb.m_hData = ::GlobalReAlloc(lb.m_hData,
  2782.                 __max(lSizeRequired, lReallocSize), GMEM_MOVEABLE);
  2783.         }
  2784.     }
  2785.  
  2786.     // Validate the memory was allocated and can be locked
  2787.     if (lb.m_hData == NULL)
  2788.     {
  2789.         // Restore the old handle (not NULL if Realloc failed)
  2790.         lb.m_hData = hOldData;
  2791.         AfxThrowMemoryException();
  2792.     }
  2793.  
  2794.     BYTE* lpLongBinary = (BYTE*)::GlobalLock(lb.m_hData);
  2795.     if (lpLongBinary == NULL)
  2796.     {
  2797.         ::GlobalFree(lb.m_hData);
  2798.         lb.m_hData = NULL;
  2799.         AfxThrowMemoryException();
  2800.     }
  2801.  
  2802.     return lpLongBinary;
  2803. }
  2804.  
  2805. //////////////////////////////////////////////////////////////////////////////
  2806. // Recordset Field Exchange Helpers
  2807.  
  2808.  
  2809. void AFXAPI AfxStoreField(CRecordset& rs, UINT nField, void* pvField)
  2810. {
  2811.     // Get the field data
  2812.     CFieldInfo* pInfo = &rs.m_rgFieldInfos[nField - 1];
  2813.  
  2814.     // Cache the status
  2815.     pInfo->m_bStatus = rs.GetFieldStatus(nField - 1);
  2816.  
  2817.     // Save the data
  2818.     if (!rs.IsFieldStatusNull(nField - 1))
  2819.     {
  2820.         // Don't need to save length for variable len
  2821.         // objects as CString and CByteArray track len internally
  2822.         long nDummyLength;
  2823.  
  2824.         if (pInfo->m_nDataType == AFX_RFX_BOOL ||
  2825.             pInfo->m_nDataType == AFX_RFX_BYTE ||
  2826.             pInfo->m_nDataType == AFX_RFX_INT ||
  2827.             pInfo->m_nDataType == AFX_RFX_LONG ||
  2828.             pInfo->m_nDataType == AFX_RFX_SINGLE)
  2829.         {
  2830.             // If caching data by value, pass a ref
  2831.             AfxCopyValueByRef(pvField, &pInfo->m_pvDataCache,
  2832.                 &nDummyLength, pInfo->m_nDataType);
  2833.         }
  2834.         else
  2835.         {
  2836.             AfxCopyValueByRef(pvField, pInfo->m_pvDataCache,
  2837.                 &nDummyLength, pInfo->m_nDataType);
  2838.         }
  2839.     }
  2840.  
  2841. #ifdef _DEBUG
  2842.     // Cache the bind address expected by ODBC
  2843.     switch(pInfo->m_nDataType)
  2844.     {
  2845.     default:
  2846.         // All types that are bound directly
  2847.         pInfo->m_pvBindAddress = pvField;
  2848.         break;
  2849.  
  2850.     case AFX_RFX_TEXT:
  2851. #ifdef _UNICODE
  2852.         pInfo->m_pvBindAddress = rs.m_pvFieldProxy[nField-1];
  2853. #else
  2854.         pInfo->m_pvBindAddress = ((CString*)pvField)->GetBuffer(0);
  2855.         ((CString*)pvField)->ReleaseBuffer();
  2856. #endif
  2857.         break;
  2858.  
  2859.     case AFX_RFX_LPTSTR:
  2860. #ifdef _UNICODE
  2861.         pInfo->m_pvBindAddress = rs.m_pvFieldProxy[nField-1];
  2862. #else
  2863.         pInfo->m_pvBindAddress = pvField;
  2864. #endif
  2865.         break;
  2866.  
  2867.     case AFX_RFX_DATE:
  2868.     case AFX_RFX_OLEDATE:
  2869.         pInfo->m_pvBindAddress = rs.m_pvFieldProxy[nField-1];
  2870.         break;
  2871.  
  2872.     case AFX_RFX_BINARY:
  2873.         pInfo->m_pvBindAddress = ((CByteArray*)pvField)->GetData();
  2874.         break;
  2875.     }
  2876. #endif
  2877. }
  2878.  
  2879. void AFXAPI AfxLoadField(CRecordset& rs, UINT nField,
  2880.     void* pvField, long* plLength)
  2881. {
  2882.     // Get the field data
  2883.     CFieldInfo* pInfo = &rs.m_rgFieldInfos[nField - 1];
  2884.  
  2885.     // Assumes old field status cleared out
  2886.     rs.SetFieldStatus(nField - 1, pInfo->m_bStatus);
  2887.  
  2888.     // If not NULL, restore the value and the length
  2889.     if (!rs.IsFieldStatusNull(nField - 1))
  2890.     {
  2891.         if (pInfo->m_nDataType == AFX_RFX_BOOL ||
  2892.             pInfo->m_nDataType == AFX_RFX_BYTE ||
  2893.             pInfo->m_nDataType == AFX_RFX_INT ||
  2894.             pInfo->m_nDataType == AFX_RFX_LONG ||
  2895.             pInfo->m_nDataType == AFX_RFX_SINGLE)
  2896.         {
  2897.             // If caching data by value, pass a ref
  2898.             AfxCopyValueByRef(&pInfo->m_pvDataCache, pvField,
  2899.                 plLength, pInfo->m_nDataType);
  2900.         }
  2901.         else
  2902.         {
  2903.             AfxCopyValueByRef(pInfo->m_pvDataCache, pvField,
  2904.                 plLength, pInfo->m_nDataType);
  2905.         }
  2906.     }
  2907.     else
  2908.         *plLength = SQL_NULL_DATA;
  2909.  
  2910. #ifdef _DEBUG
  2911.     // Buffer address must not change - ODBC's SQLBindCol depends upon this
  2912.     if (pInfo->m_nDataType == AFX_RFX_BINARY)
  2913.     {
  2914.         // Change pvField to point to the data of the CByteArray
  2915.         pvField = ((CByteArray*)pvField)->GetData();
  2916.     }
  2917.  
  2918.     if (pInfo->m_pvBindAddress != pvField)
  2919.     {
  2920.         TRACE1("Error: field address (column %u) has changed!\n",
  2921.             nField);
  2922.         ASSERT(FALSE);
  2923.     }
  2924. #endif // _DEBUG
  2925. }
  2926.  
  2927. BOOL AFXAPI AfxCompareValueByRef(void* pvSrc, void* pvDest, int nSrcType)
  2928. {
  2929.     ASSERT(pvSrc != NULL);
  2930.     ASSERT(pvDest != NULL);
  2931.  
  2932.     BOOL bCompare = FALSE;
  2933.  
  2934.     switch(nSrcType)
  2935.     {
  2936.  
  2937.     case AFX_RFX_LONGBINARY:
  2938.         // Caching long binary Src not supported
  2939.     default:
  2940.         ASSERT(FALSE);
  2941.         break;
  2942.  
  2943.     case AFX_RFX_LPTSTR:
  2944.         if (lstrcmp((LPTSTR) pvDest, (LPTSTR) pvSrc) == 0)
  2945.             bCompare = TRUE;
  2946.         break;
  2947.  
  2948.     case AFX_RFX_TEXT:
  2949.         if (*(CString*)pvDest == *(CString*)pvSrc)
  2950.             bCompare = TRUE;
  2951.         break;
  2952.  
  2953.     case AFX_RFX_BINARY:
  2954.         {
  2955.             CByteArray* pByteArraySrc = (CByteArray*)pvSrc;
  2956.             CByteArray* pByteArrayDest = (CByteArray*)pvDest;
  2957.  
  2958.             // If sizes compare, compare the Src
  2959.             int nSize = pByteArraySrc->GetSize();
  2960.             if (nSize == pByteArrayDest->GetSize())
  2961.             {
  2962.                 if (memcmp(&pByteArrayDest[0], &pByteArraySrc[0], nSize) == 0)
  2963.                     bCompare = TRUE;
  2964.             }
  2965.         }
  2966.         break;
  2967.     case AFX_RFX_BOOL:
  2968.         if (*(BOOL*)pvDest == *(BOOL*)pvSrc)
  2969.             bCompare = TRUE;
  2970.         break;
  2971.  
  2972.     case AFX_RFX_BYTE:
  2973.         if (*(BYTE*)pvDest == *(BYTE*)pvSrc)
  2974.             bCompare = TRUE;
  2975.         break;
  2976.  
  2977.     case AFX_RFX_INT:
  2978.         if (*(int*)pvDest == *(int*)pvSrc)
  2979.             bCompare = TRUE;
  2980.         break;
  2981.  
  2982.     case AFX_RFX_LONG:
  2983.         if (*(long*)pvDest == *(long*)pvSrc)
  2984.             bCompare = TRUE;
  2985.         break;
  2986.  
  2987.     case AFX_RFX_SINGLE:
  2988.         if (*(float*)pvDest == *(float*)pvSrc)
  2989.             bCompare = TRUE;
  2990.         break;
  2991.  
  2992.     case AFX_RFX_DOUBLE:
  2993.         if (*(double*)pvDest == *(double*)pvSrc)
  2994.             bCompare = TRUE;
  2995.         break;
  2996.  
  2997.     case AFX_RFX_OLEDATE:
  2998.         if (*(COleDateTime*)pvDest == *(COleDateTime*)pvSrc)
  2999.             bCompare = TRUE;
  3000.         break;
  3001.  
  3002.     case AFX_RFX_DATE:
  3003.         if (*(CTime*)pvDest == *(CTime*)pvSrc)
  3004.             bCompare = TRUE;
  3005.         break;
  3006.  
  3007.     case AFX_RFX_TIMESTAMP:
  3008.         {
  3009.             TIMESTAMP_STRUCT* pSrc = (TIMESTAMP_STRUCT*)pvSrc;
  3010.             TIMESTAMP_STRUCT* pDest = (TIMESTAMP_STRUCT*)pvDest;
  3011.             if ((pSrc->year == pDest->year &&
  3012.                 pSrc->month == pDest->month &&
  3013.                 pSrc->day == pDest->day &&
  3014.                 pSrc->hour == pDest->hour &&
  3015.                 pSrc->minute == pDest->minute &&
  3016.                 pSrc->second == pDest->second &&
  3017.                 pSrc->fraction == pDest->fraction))
  3018.             {
  3019.                 bCompare = TRUE;
  3020.             }
  3021.         }
  3022.         break;
  3023.     }
  3024.  
  3025.     return bCompare;
  3026. }
  3027.  
  3028. void AFXAPI AfxCopyValueByRef(void* pvSrc, void* pvDest, long* plLength, int nDestType)
  3029. {
  3030.     ASSERT(pvSrc != NULL);
  3031.     ASSERT(pvDest != NULL);
  3032.     ASSERT(plLength != NULL);
  3033.  
  3034.     switch (nDestType)
  3035.     {
  3036.     case AFX_RFX_LONGBINARY:
  3037.         // Caching long binary Dest not supported
  3038.     default:
  3039.         ASSERT(FALSE);
  3040.         break;
  3041.  
  3042.     case AFX_RFX_LPTSTR:
  3043.         lstrcpy((LPTSTR) pvDest, (LPTSTR) pvSrc);
  3044.         *plLength = lstrlen((LPTSTR) pvDest);
  3045.         break;
  3046.  
  3047.     case AFX_RFX_TEXT:
  3048.         *(CString*)pvDest = *(CString*)pvSrc;
  3049.         *plLength = ((CString*)pvSrc)->GetLength();
  3050.         break;
  3051.  
  3052.     case AFX_RFX_BINARY:
  3053.         ((CByteArray*)pvDest)->Copy(*(CByteArray*)pvSrc);
  3054.         *plLength = ((CByteArray*)pvSrc)->GetSize();
  3055.         break;
  3056.  
  3057.     case AFX_RFX_BOOL:
  3058.         *(BOOL*)pvDest = *(BOOL*)pvSrc;
  3059.         *plLength = sizeof(BOOL);
  3060.         break;
  3061.  
  3062.     case AFX_RFX_BYTE:
  3063.         *(BYTE*)pvDest = *(BYTE*)pvSrc;
  3064.         *plLength = sizeof(BYTE);
  3065.         break;
  3066.  
  3067.     case AFX_RFX_INT:
  3068.         *(int*)pvDest = *(int*)pvSrc;
  3069.         *plLength = sizeof(int);
  3070.         break;
  3071.  
  3072.     case AFX_RFX_LONG:
  3073.         *(long*)pvDest = *(long*)pvSrc;
  3074.         *plLength = sizeof(long);
  3075.         break;
  3076.  
  3077.     case AFX_RFX_SINGLE:
  3078.         *(float*)pvDest = *(float*)pvSrc;
  3079.         *plLength = sizeof(float);
  3080.         break;
  3081.  
  3082.     case AFX_RFX_DOUBLE:
  3083.         *(double*)pvDest = *(double*)pvSrc;
  3084.         *plLength = sizeof(double);
  3085.         break;
  3086.  
  3087.     case AFX_RFX_DATE:
  3088.         *(CTime*)pvDest = *(CTime*)pvSrc;
  3089.         *plLength = sizeof(TIMESTAMP_STRUCT);
  3090.         break;
  3091.  
  3092.     case AFX_RFX_OLEDATE:
  3093.         *(COleDateTime*)pvDest = *(COleDateTime*)pvSrc;
  3094.         *plLength = sizeof(TIMESTAMP_STRUCT);
  3095.         break;
  3096.  
  3097.     case AFX_RFX_TIMESTAMP:
  3098.         {
  3099.             TIMESTAMP_STRUCT* pDest = (TIMESTAMP_STRUCT*)pvDest;
  3100.             TIMESTAMP_STRUCT* pSrc = (TIMESTAMP_STRUCT*)pvSrc;
  3101.  
  3102.             pDest->year = pSrc->year;
  3103.             pDest->month = pSrc->month;
  3104.             pDest->day = pSrc->day;
  3105.             pDest->hour = pSrc->hour;
  3106.             pDest->minute = pSrc->minute;
  3107.             pDest->second = pSrc->second;
  3108.             pDest->fraction = pSrc->fraction;
  3109.  
  3110.             *plLength = sizeof(TIMESTAMP_STRUCT);
  3111.         }
  3112.         break;
  3113.     }
  3114. }
  3115.  
  3116. //////////////////////////////////////////////////////////////////////////////
  3117. // Bulk Recordset Field Exchange
  3118.  
  3119. void AFXAPI RFX_Text_Bulk(CFieldExchange* pFX, LPCTSTR szName,
  3120.     LPSTR* prgStrVals, long** prgLengths, int nMaxLength)
  3121. {
  3122.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  3123.     ASSERT(AfxIsValidString(szName));
  3124.  
  3125.     UINT nField;
  3126.     if (!pFX->IsFieldType(&nField))
  3127.         return;
  3128.  
  3129.     switch (pFX->m_nOperation)
  3130.     {
  3131.     case CFieldExchange::AllocMultiRowBuffer:
  3132.         {
  3133.             // The buffer pointer better be initialized to NULL
  3134.             // or cleanup in exceptional cases mail fail
  3135.             ASSERT(*prgStrVals == NULL);
  3136.             ASSERT(*prgLengths == NULL);
  3137.  
  3138.             int nRowsetSize = pFX->m_prs->GetRowsetSize();
  3139.  
  3140.             // Allocate buffers to hold data and length
  3141.             *prgStrVals = new char[nRowsetSize * nMaxLength];
  3142.             *prgLengths = new long[nRowsetSize];
  3143.         }
  3144.         break;
  3145.  
  3146.     case CFieldExchange::DeleteMultiRowBuffer:
  3147.         delete [] *prgStrVals;
  3148.         *prgStrVals = NULL;
  3149.  
  3150.         delete [] *prgLengths;
  3151.         *prgLengths = NULL;
  3152.         break;
  3153.  
  3154.     default:
  3155.         AfxRFXBulkDefault(pFX, szName, *prgStrVals, *prgLengths,
  3156.             SQL_C_CHAR, nMaxLength);
  3157.         break;
  3158.     }
  3159. }
  3160.  
  3161. void AFXAPI RFX_Bool_Bulk(CFieldExchange* pFX, LPCTSTR szName,
  3162.     BOOL** prgBoolVals, long** prgLengths)
  3163. {
  3164.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  3165.     ASSERT(AfxIsValidString(szName));
  3166.  
  3167.     UINT nField;
  3168.     if (!pFX->IsFieldType(&nField))
  3169.         return;
  3170.  
  3171.     switch (pFX->m_nOperation)
  3172.     {
  3173.     case CFieldExchange::AllocMultiRowBuffer:
  3174.         {
  3175.             // The buffer pointer better be initialized to NULL
  3176.             // or cleanup in exceptional cases mail fail
  3177.             ASSERT(*prgBoolVals == NULL);
  3178.             ASSERT(*prgLengths == NULL);
  3179.  
  3180.             int nRowsetSize = pFX->m_prs->GetRowsetSize();
  3181.  
  3182.             // Allocate buffers to hold data and length
  3183.             *prgBoolVals = new BOOL[nRowsetSize];
  3184.             *prgLengths = new long[nRowsetSize];
  3185.         }
  3186.         break;
  3187.  
  3188.     case CFieldExchange::DeleteMultiRowBuffer:
  3189.         delete [] *prgBoolVals;
  3190.         *prgBoolVals = NULL;
  3191.  
  3192.         delete [] *prgLengths;
  3193.         *prgLengths = NULL;
  3194.         break;
  3195.  
  3196.     default:
  3197.         AfxRFXBulkDefault(pFX, szName, *prgBoolVals, *prgLengths,
  3198.             SQL_C_LONG, sizeof(BOOL));
  3199.         break;
  3200.     }
  3201. }
  3202.  
  3203. void AFXAPI RFX_Int_Bulk(CFieldExchange* pFX, LPCTSTR szName,
  3204.     int** prgIntVals, long** prgLengths)
  3205. {
  3206.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  3207.     ASSERT(AfxIsValidString(szName));
  3208.  
  3209.     UINT nField;
  3210.     if (!pFX->IsFieldType(&nField))
  3211.         return;
  3212.  
  3213.     switch (pFX->m_nOperation)
  3214.     {
  3215.     case CFieldExchange::AllocMultiRowBuffer:
  3216.         {
  3217.             // The buffer pointer better be initialized to NULL
  3218.             // or cleanup in exceptional cases mail fail
  3219.             ASSERT(*prgIntVals == NULL);
  3220.             ASSERT(*prgLengths == NULL);
  3221.  
  3222.             int nRowsetSize = pFX->m_prs->GetRowsetSize();
  3223.  
  3224.             // Allocate buffers to hold data and length
  3225.             *prgIntVals = new int[nRowsetSize];
  3226.             *prgLengths = new long[nRowsetSize];
  3227.         }
  3228.         break;
  3229.  
  3230.     case CFieldExchange::DeleteMultiRowBuffer:
  3231.         delete [] *prgIntVals;
  3232.         *prgIntVals = NULL;
  3233.  
  3234.         delete [] *prgLengths;
  3235.         *prgLengths = NULL;
  3236.         break;
  3237.  
  3238.     default:
  3239.         AfxRFXBulkDefault(pFX, szName, *prgIntVals, *prgLengths,
  3240.             SQL_C_LONG, sizeof(int));
  3241.         break;
  3242.     }
  3243. }
  3244.  
  3245. void AFXAPI RFX_Long_Bulk(CFieldExchange* pFX, LPCTSTR szName,
  3246.     long** prgLongVals, long** prgLengths)
  3247. {
  3248.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  3249.     ASSERT(AfxIsValidString(szName));
  3250.  
  3251.     UINT nField;
  3252.     if (!pFX->IsFieldType(&nField))
  3253.         return;
  3254.  
  3255.     switch (pFX->m_nOperation)
  3256.     {
  3257.     case CFieldExchange::AllocMultiRowBuffer:
  3258.         {
  3259.             // The buffer pointer better be initialized to NULL
  3260.             // or cleanup in exceptional cases mail fail
  3261.             ASSERT(*prgLongVals == NULL);
  3262.             ASSERT(*prgLengths == NULL);
  3263.  
  3264.             int nRowsetSize = pFX->m_prs->GetRowsetSize();
  3265.  
  3266.             // Allocate buffers to hold data and length
  3267.             *prgLongVals = new long[nRowsetSize];
  3268.             *prgLengths = new long[nRowsetSize];
  3269.         }
  3270.         break;
  3271.  
  3272.     case CFieldExchange::DeleteMultiRowBuffer:
  3273.         delete [] *prgLongVals;
  3274.         *prgLongVals = NULL;
  3275.  
  3276.         delete [] *prgLengths;
  3277.         *prgLengths = NULL;
  3278.         break;
  3279.  
  3280.     default:
  3281.         AfxRFXBulkDefault(pFX, szName, *prgLongVals, *prgLengths,
  3282.             SQL_C_LONG, sizeof(long));
  3283.         break;
  3284.     }
  3285. }
  3286.  
  3287. void AFXAPI RFX_Date_Bulk(CFieldExchange* pFX, LPCTSTR szName,
  3288.     TIMESTAMP_STRUCT** prgTSVals, long** prgLengths)
  3289. {
  3290.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  3291.     ASSERT(AfxIsValidString(szName));
  3292.  
  3293.     UINT nField;
  3294.     if (!pFX->IsFieldType(&nField))
  3295.         return;
  3296.  
  3297.     switch (pFX->m_nOperation)
  3298.     {
  3299.     case CFieldExchange::AllocMultiRowBuffer:
  3300.         {
  3301.             // The buffer pointer better be initialized to NULL
  3302.             // or cleanup in exceptional cases mail fail
  3303.             ASSERT(*prgTSVals == NULL);
  3304.             ASSERT(*prgLengths == NULL);
  3305.  
  3306.             int nRowsetSize = pFX->m_prs->GetRowsetSize();
  3307.  
  3308.             // Allocate buffers to hold data and length
  3309.             *prgTSVals = new TIMESTAMP_STRUCT[nRowsetSize];
  3310.             *prgLengths = new long[nRowsetSize];
  3311.         }
  3312.         break;
  3313.  
  3314.     case CFieldExchange::DeleteMultiRowBuffer:
  3315.         delete [] *prgTSVals;
  3316.         *prgTSVals = NULL;
  3317.  
  3318.         delete [] *prgLengths;
  3319.         *prgLengths = NULL;
  3320.         break;
  3321.  
  3322.     default:
  3323.         AfxRFXBulkDefault(pFX, szName, *prgTSVals, *prgLengths,
  3324.             SQL_C_TIMESTAMP, sizeof(TIMESTAMP_STRUCT));
  3325.         break;
  3326.     }
  3327. }
  3328.  
  3329. void AFXAPI RFX_Byte_Bulk(CFieldExchange* pFX, LPCTSTR szName,
  3330.     BYTE** prgByteVals, long** prgLengths)
  3331. {
  3332.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  3333.     ASSERT(AfxIsValidString(szName));
  3334.  
  3335.     UINT nField;
  3336.     if (!pFX->IsFieldType(&nField))
  3337.         return;
  3338.  
  3339.     switch (pFX->m_nOperation)
  3340.     {
  3341.     case CFieldExchange::AllocMultiRowBuffer:
  3342.         {
  3343.             // The buffer pointer better be initialized to NULL
  3344.             // or cleanup in exceptional cases mail fail
  3345.             ASSERT(*prgByteVals == NULL);
  3346.             ASSERT(*prgLengths == NULL);
  3347.  
  3348.             int nRowsetSize = pFX->m_prs->GetRowsetSize();
  3349.  
  3350.             // Allocate buffers to hold data and length
  3351.             *prgByteVals = new BYTE[nRowsetSize];
  3352.             *prgLengths = new long[nRowsetSize];
  3353.         }
  3354.         break;
  3355.  
  3356.     case CFieldExchange::DeleteMultiRowBuffer:
  3357.         delete [] *prgByteVals;
  3358.         *prgByteVals = NULL;
  3359.  
  3360.         delete [] *prgLengths;
  3361.         *prgLengths = NULL;
  3362.         break;
  3363.  
  3364.     default:
  3365.         AfxRFXBulkDefault(pFX, szName, *prgByteVals, *prgLengths,
  3366.             SQL_C_TINYINT, sizeof(BYTE));
  3367.         break;
  3368.     }
  3369. }
  3370.  
  3371. void AFXAPI RFX_Binary_Bulk(CFieldExchange* pFX, LPCTSTR szName,
  3372.     BYTE** prgByteVals, long** prgLengths, int nMaxLength)
  3373. {
  3374.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  3375.     ASSERT(AfxIsValidString(szName));
  3376.  
  3377.     UINT nField;
  3378.     if (!pFX->IsFieldType(&nField))
  3379.         return;
  3380.  
  3381.     switch (pFX->m_nOperation)
  3382.     {
  3383.     case CFieldExchange::AllocMultiRowBuffer:
  3384.         {
  3385.             // The buffer pointer better be initialized to NULL
  3386.             // or cleanup in exceptional cases mail fail
  3387.             ASSERT(*prgByteVals == NULL);
  3388.             ASSERT(*prgLengths == NULL);
  3389.  
  3390.             int nRowsetSize = pFX->m_prs->GetRowsetSize();
  3391.  
  3392.             // Allocate buffers to hold data and length
  3393.             *prgByteVals = new BYTE[nRowsetSize * nMaxLength];
  3394.             *prgLengths = new long[nRowsetSize];
  3395.         }
  3396.         break;
  3397.  
  3398.     case CFieldExchange::DeleteMultiRowBuffer:
  3399.         delete [] *prgByteVals;
  3400.         *prgByteVals = NULL;
  3401.  
  3402.         delete [] *prgLengths;
  3403.         *prgLengths = NULL;
  3404.         break;
  3405.  
  3406.     default:
  3407.         AfxRFXBulkDefault(pFX, szName, *prgByteVals, *prgLengths,
  3408.             SQL_C_BINARY, nMaxLength);
  3409.         break;
  3410.     }
  3411. }
  3412.  
  3413. void AFXAPI AfxRFXBulkDefault(CFieldExchange* pFX, LPCTSTR szName,
  3414.     void* pv, long* rgLengths, int nCType, UINT cbValue)
  3415. {
  3416.     RETCODE nRetCode;
  3417.  
  3418.     switch(pFX->m_nOperation)
  3419.     {
  3420.     default:
  3421.         // Operation not valid for bulk fetch
  3422.         ASSERT(FALSE);
  3423.         return;
  3424.  
  3425.     case CFieldExchange::Name:
  3426.         // We require a name
  3427.         ASSERT(lstrlen(szName) != 0);
  3428.  
  3429.         *pFX->m_pstr += szName;
  3430.         *pFX->m_pstr += pFX->m_lpszSeparator;
  3431.         break;
  3432.  
  3433.     case CFieldExchange::BindFieldToColumn:
  3434.         AFX_SQL_SYNC(::SQLBindCol(pFX->m_prs->m_hstmt,
  3435.             (UWORD)pFX->m_nFields, (SWORD)nCType, pv, cbValue, rgLengths));
  3436.         if (!pFX->m_prs->Check(nRetCode))
  3437.             pFX->m_prs->ThrowDBException(nRetCode);
  3438.         break;
  3439.     }
  3440. }
  3441.  
  3442. //////////////////////////////////////////////////////////////////////////////
  3443. // Inline function declarations expanded out-of-line
  3444.  
  3445. #ifndef _AFX_ENABLE_INLINES
  3446.  
  3447. static char _szAfxDbInl[] = "afxdb.inl";
  3448. #undef THIS_FILE
  3449. #define THIS_FILE _szAfxDbInl
  3450. #define _AFXDBRFX_INLINE
  3451. #include "afxdb.inl"
  3452.  
  3453. #endif
  3454.  
  3455. #ifdef AFX_INIT_SEG
  3456. #pragma code_seg(AFX_INIT_SEG)
  3457. #endif
  3458.  
  3459. /////////////////////////////////////////////////////////////////////////////
  3460.