RecordsetEx.cpp
上传用户:jsxglz
上传日期:2007-01-03
资源大小:117k
文件大小:8k
源码类别:

SQL Server

开发平台:

Visual C++

  1. // RecordsetEx.cpp : implementation file
  2. //
  3. #include "stdafx.h"
  4. #include "RecordsetEx.h"
  5. #ifdef _DEBUG
  6. #define new DEBUG_NEW
  7. #undef THIS_FILE
  8. static char THIS_FILE[] = __FILE__;
  9. #endif
  10. static const TCHAR _lpszDataTruncated[] = _T("State:01004");
  11. static const TCHAR _lpszDataNumericValueOutOfRange[] =  _T("State:22003");
  12. /////////////////////////////////////////////////////////////////////////////
  13. // CRecordsetEx
  14. IMPLEMENT_DYNAMIC(CRecordsetEx, CRecordset)
  15. CRecordsetEx::CRecordsetEx(CDatabase* pdb)
  16. : CRecordset(pdb)
  17. {
  18. //{{AFX_FIELD_INIT(CRecordsetEx)
  19. //}}AFX_FIELD_INIT
  20. m_nDefaultType = snapshot;
  21. }
  22. BOOL CRecordsetEx::ExecDirect(const CString& sSQL)
  23. {
  24. return Open(CRecordset::forwardOnly, sSQL, CRecordset::executeDirect);
  25. }
  26. BOOL CRecordsetEx::Open(UINT nOpenType, LPCTSTR lpszSQL, DWORD dwOptions)
  27. {
  28. ASSERT(!IsOpen());
  29. ASSERT_VALID(this);
  30. ASSERT(lpszSQL == NULL || AfxIsValidString(lpszSQL));
  31. ASSERT(nOpenType == AFX_DB_USE_DEFAULT_TYPE ||
  32. nOpenType == dynaset || nOpenType == snapshot ||
  33. nOpenType == forwardOnly || nOpenType == dynamic);
  34. ASSERT(!(dwOptions & readOnly && dwOptions & appendOnly));
  35. // Can only use optimizeBulkAdd with appendOnly recordsets
  36. ASSERT((dwOptions & optimizeBulkAdd && dwOptions & appendOnly) ||
  37. !(dwOptions & optimizeBulkAdd));
  38. // forwardOnly recordsets have limited functionality
  39. ASSERT(!(nOpenType == forwardOnly && dwOptions & skipDeletedRecords));
  40. // Cache state info and allocate hstmt
  41. SetState(nOpenType, lpszSQL, dwOptions);
  42. if(!AllocHstmt())
  43. return FALSE;
  44. // Check if bookmarks upported (CanBookmark depends on open DB)
  45. ASSERT(dwOptions & useBookmarks ? CanBookmark() : TRUE);
  46. TRY
  47. {
  48. OnSetOptions(m_hstmt);
  49. // Allocate the field/param status arrays, if necessary
  50. BOOL bUnbound = FALSE;
  51. if (m_nFields > 0 || m_nParams > 0)
  52. AllocStatusArrays();
  53. else
  54. bUnbound = TRUE;
  55. // Build SQL and prep/execute or just execute direct
  56. m_strSQL = lpszSQL;
  57. PrepareAndExecute();
  58. // Cache some field info and prepare the rowset
  59. AllocAndCacheFieldInfo();
  60. AllocRowset();
  61. // If late binding, still need to allocate status arrays
  62. if (bUnbound && (m_nFields > 0 || m_nParams > 0))
  63. AllocStatusArrays();
  64. // Give derived classes a call before binding
  65. PreBindFields();
  66. if(m_nResultCols)
  67. MoveNext();
  68. // If EOF, then result set empty, so set BOF as well
  69. m_bBOF = m_bEOF;
  70. }
  71. CATCH_ALL(e)
  72. {
  73. Close();
  74. THROW_LAST();
  75. }
  76. END_CATCH_ALL
  77. return TRUE;
  78. }
  79. void CRecordsetEx::AllocAndCacheFieldInfo()
  80. {
  81. m_nResultCols = -1;
  82. ASSERT(GetODBCFieldCount() < 0);
  83. if(m_rgODBCFieldInfos != NULL)
  84. {
  85. delete [] m_rgODBCFieldInfos;
  86. m_rgODBCFieldInfos = NULL;
  87. }
  88. RETCODE nRetCode;
  89. SWORD nActualLen;    // Cache the number of result columns.
  90. AFX_ODBC_CALL(::SQLNumResultCols(m_hstmt, &m_nResultCols));
  91. if(!Check(nRetCode))
  92. {
  93. TRACE("Error: Can't get field info.n");
  94. AfxThrowDBException(nRetCode, m_pDatabase,m_hstmt);
  95. }
  96. // If there are no fields, quit now
  97. if(m_nResultCols == 0)
  98.  return;
  99.   
  100. // Allocate buffer and get the ODBC meta data.
  101. m_rgODBCFieldInfos = new CODBCFieldInfo[m_nResultCols];
  102. LPSTR lpszFieldName;
  103. #ifdef _UNICODE
  104.   // Need proxy to temporarily store non-UNICODE name.
  105.   lpszFieldName = new char[MAX_FNAME_LEN + 1];
  106. #endif
  107.       // Get the field info for each field.
  108.    for(WORD n = 1; n <= GetODBCFieldCount(); n++)
  109.    {
  110. #ifndef _UNICODE  // Reset the buffer to point to next element.
  111. lpszFieldName = m_rgODBCFieldInfos[n - 1].m_strName.GetBuffer(MAX_FNAME_LEN + 1);
  112. #endif
  113. AFX_ODBC_CALL(::SQLDescribeCol(m_hstmt, n,
  114.  (UCHAR*)lpszFieldName, MAX_FNAME_LEN, &nActualLen,
  115.  &m_rgODBCFieldInfos[n - 1].m_nSQLType,
  116.  &m_rgODBCFieldInfos[n - 1].m_nPrecision,
  117.  &m_rgODBCFieldInfos[n - 1].m_nScale,
  118.  &m_rgODBCFieldInfos[n - 1].m_nNullability));
  119.    
  120. #ifndef _UNICODE
  121. m_rgODBCFieldInfos[n - 1].m_strName.ReleaseBuffer(nActualLen);
  122. #else  // Copy the proxy data to correct element.
  123. m_rgODBCFieldInfos[n - 1].m_strName = lpszFieldName;
  124. #endif
  125. if(!Check(nRetCode))
  126. {
  127. TRACE1("Error: ODBC failure getting field #%d info.n", n);
  128. AfxThrowDBException(nRetCode, m_pDatabase, m_hstmt);
  129. }
  130.   }
  131. #ifdef _UNICODE
  132. delete[] lpszFieldName;
  133. #endif
  134. }
  135. void CRecordsetEx::GetFieldValue(short nIndex, CString& strValue)
  136. {
  137. ASSERT_VALID(this);
  138. ASSERT(IsOpen());
  139. // No data or no column info fetched yet
  140. if (GetODBCFieldCount() <= 0)
  141. {
  142. ASSERT(FALSE);
  143. return;
  144. }
  145. // Convert index to 1-based and check range
  146. nIndex++;
  147. if (nIndex < 1 || nIndex > GetODBCFieldCount())
  148. {
  149. ThrowDBException(AFX_SQL_ERROR_FIELD_NOT_FOUND);
  150. }
  151. int nLen = GetTextLen(m_rgODBCFieldInfos[nIndex - 1].m_nSQLType,
  152. m_rgODBCFieldInfos[nIndex - 1].m_nPrecision);
  153. #ifndef _UNICODE
  154. CString& strData = strValue;
  155. #else
  156. CString strProxy;
  157. CString& strData = strProxy;
  158. #endif
  159. void* pvData = strData.GetBufferSetLength(nLen);
  160. // Now can actually get the data
  161. long nActualSize = GetData(m_pDatabase, m_hstmt, nIndex,
  162. SQL_C_CHAR, pvData, nLen,
  163. m_rgODBCFieldInfos[nIndex - 1].m_nSQLType);
  164. // Handle NULL data separately
  165. if (nActualSize == SQL_NULL_DATA)
  166. {
  167. // Clear value
  168. strValue.Empty();
  169. }
  170. else
  171. {
  172. // May need to cleanup and call SQLGetData again if necessary
  173. GetLongCharDataAndCleanup(m_pDatabase, m_hstmt, nIndex,
  174. nActualSize, &pvData, nLen, strData,
  175. m_rgODBCFieldInfos[nIndex - 1].m_nSQLType);
  176. #ifdef _UNICODE
  177. // Now must convert string to UNICODE
  178. strValue = (LPCSTR)strData.GetBuffer(0);
  179. #endif // _UNIOCDE
  180. }
  181. }
  182. long CRecordsetEx::GetData(CDatabase* pdb, HSTMT hstmt,
  183. short nFieldIndex, short nFieldType, LPVOID pvData, int nLen,
  184. short nSQLType)
  185. {
  186. UNUSED(nSQLType);
  187. long nActualSize;
  188. RETCODE nRetCode;
  189. // Retrieve the column in question
  190. AFX_ODBC_CALL(::SQLGetData(hstmt, nFieldIndex,
  191. nFieldType, pvData, nLen, &nActualSize));
  192. // Ignore data truncated warnings for long data
  193. if (nRetCode == SQL_SUCCESS_WITH_INFO)
  194. {
  195. #ifdef _DEBUG
  196. if (afxTraceFlags & traceDatabase)
  197. {
  198. CDBException e(nRetCode);
  199. // Build the error string but don't send nuisance output to TRACE window
  200. e.BuildErrorString(pdb, hstmt, FALSE);
  201. // If not a data truncated warning on long var column,
  202. // then send debug output
  203. if ((nSQLType != SQL_LONGVARCHAR &&
  204. nSQLType != SQL_LONGVARBINARY) ||
  205. (e.m_strStateNativeOrigin.Find(_lpszDataTruncated) < 0))
  206. {
  207. TRACE1("Warning: ODBC Success With Info on field %d.n",
  208. nFieldIndex - 1);
  209. e.TraceErrorMessage(e.m_strError);
  210. e.TraceErrorMessage(e.m_strStateNativeOrigin);
  211. }
  212. }
  213. #endif // _DEBUG
  214. }
  215. else if (nRetCode == SQL_NO_DATA_FOUND)
  216. {
  217. TRACE0("Error: GetFieldValue operation failed on field %d.n");
  218. TRACE1("tData already fetched for this field.n",
  219. nFieldIndex - 1);
  220. AfxThrowDBException(nRetCode, pdb, hstmt);
  221. }
  222. else if (nRetCode != SQL_SUCCESS)
  223. {
  224. TRACE1("Error: GetFieldValue operation failed on field %d.n",
  225. nFieldIndex - 1);
  226. CDBException e(nRetCode);
  227. e.BuildErrorString(pdb, hstmt, FALSE);
  228. if(e.m_strStateNativeOrigin.Find(_lpszDataNumericValueOutOfRange) == -1)
  229. AfxThrowDBException(nRetCode, pdb, hstmt);
  230. else
  231. {
  232. #ifdef _DEBUG
  233. e.TraceErrorMessage(e.m_strError);
  234. e.TraceErrorMessage(e.m_strStateNativeOrigin);
  235. #endif
  236. }
  237. }
  238. return nActualSize;
  239. }
  240. /////////////////////////////////////////////////////////////////////////////
  241. // CRecordsetEx diagnostics
  242. #ifdef _DEBUG
  243. void CRecordsetEx::AssertValid() const
  244. {
  245. CRecordset::AssertValid();
  246. }
  247. void CRecordsetEx::Dump(CDumpContext& dc) const
  248. {
  249. CRecordset::Dump(dc);
  250. }
  251. #endif //_DEBUG