Version: 1.0.0
odbc_resultset.cpp
Go to the documentation of this file.
1 #include "wx/database/wxprec.h"
2 
3 #if wxUSE_DATABASE_ODBC
4 
5 #include <wx/arrimpl.cpp> // this is a magic incantation which must be done!
6 WX_DEFINE_OBJARRAY(ValuesArray);
7 
8 #define ODBC_FIELD_NAME_LEN 71
9 
10 // ctor
12 {
13  m_pInterface = pInterface;
14  m_pStatement = NULL;
15  m_pOdbcStatement = NULL;
16  m_bManageStatement = false;
17 }
18 
19 wxOdbcResultSet::wxOdbcResultSet(wxOdbcInterface* pInterface, wxOdbcPreparedStatement* pStatement, bool bManageStatement, int nCol)
21 {
22  m_pInterface = pInterface;
23  m_pStatement = pStatement;
24  m_pOdbcStatement = m_pStatement->GetLastStatement();
25  m_bManageStatement = bManageStatement;
26 
27  // Populate field lookup map
28  SQLTCHAR field_name[ODBC_FIELD_NAME_LEN];
29  SQLSMALLINT colnamelen;
30 
31  for (int i=0; i<nCol; i++)
32  {
33  UWORD col = i+1;
34  long nReturn = m_pInterface->GetSQLColAttributes()(m_pOdbcStatement, col, SQL_COLUMN_NAME,
35  field_name,
36  ODBC_FIELD_NAME_LEN, &colnamelen, 0);
37  if ( nReturn != SQL_SUCCESS && nReturn != SQL_SUCCESS_WITH_INFO )
38  {
39  InterpretErrorCodes(nReturn, m_pOdbcStatement);
41  return;
42  }
43 
44 #if wxUSE_UNICODE
45  wxString strField = ConvertFromUnicodeStream((const char*)(wxChar*)field_name);
46 #else
47  wxString strField((wxChar*)field_name);
48 #endif
49  m_FieldLookupMap[strField.Upper()] = i;
50  }
51 }
52 
53 // dtor
55 {
56  Close();
57 
58  m_FieldLookupMap.clear();
59  m_fieldValues.Clear();
60 
61  m_RetrievedValues.clear();
62  m_NullValues.clear();
63 
64  m_BlobMap.clear();
65 }
66 
67 
69 {
70  CloseMetaData();
71 
73  {
74  if (m_pStatement != NULL)
75  {
76  //m_pStatement->Close();
77  wxDELETE(m_pStatement);
78  }
79  }
80 }
81 
82 
84 {
85  m_RetrievedValues.clear();
86  m_NullValues.clear();
87 
88  m_BlobMap.clear();
89 
90  if (m_pOdbcStatement == NULL)
92 
93  long nReturn = m_pInterface->GetSQLFetch()( m_pOdbcStatement );
94 
95  if ( nReturn != SQL_SUCCESS && nReturn != SQL_SUCCESS_WITH_INFO )
96  {
97  if ( nReturn == SQL_NO_DATA )
98  return false;
99 
102  return false;
103  }
104 
105  m_fieldValues.Clear();
106  for ( int i=0; i<(int)m_FieldLookupMap.size(); i++ )
107  m_fieldValues.push_back( wxVariant() );
108 
109  return true;
110 }
111 
112 
113 // get field
114 bool wxOdbcResultSet::IsFieldNull(int nField)
115 {
116  // Some ODBC drivers (i.e. MS SQL SERVER) need the fields to be retrieved in order
117  for (int ctr = 1; ctr < nField; ctr++)
118  {
119  if (m_fieldValues[ctr-1].IsNull())
120  {
121  if (IsBlob(ctr))
122  {
123  wxMemoryBuffer buffer;
124  GetResultBlob(ctr, buffer);
125  }
126  else
127  {
128  RetrieveFieldData(ctr);
129  }
130  }
131  }
132 
133  if (!IsBlob(nField))
134  {
135  if (m_RetrievedValues.find(nField) == m_RetrievedValues.end())
136  {
137  RetrieveFieldData(nField);
138  }
139  }
140  else
141  {
142  wxMemoryBuffer buffer;
143  void* pBlob = GetResultBlob(nField, buffer);
144  if (pBlob == NULL)
145  m_NullValues.insert(nField);
146  }
147 
148  return (m_NullValues.find(nField) != m_NullValues.end());
149 }
150 
151 int wxOdbcResultSet::GetFieldLength(int nField)
152 {
153  // Some ODBC drivers (i.e. MS SQL SERVER) need the fields to be retrieved in order
154  for (int ctr = 1; ctr <= nField; ctr++)
155  {
156  if (m_fieldValues[ctr-1].IsNull())
157  {
158  if (!IsBlob(ctr))
159  {
160  RetrieveFieldData(ctr);
161  }
162  else
163  {
164  wxMemoryBuffer buffer;
165  GetResultBlob(ctr, buffer);
166  }
167  }
168  }
169  /*
170  if (m_fieldValues[nField-1].IsNull())
171  RetrieveFieldData(nField);
172  */
173 
174  wxString strValue = m_fieldValues[nField-1].GetString();
175 
176  strValue = strValue.Trim();
177  size_t real_size = strValue.Length();
178 
179  return real_size;
180 }
181 
182 void wxOdbcResultSet::RetrieveFieldData(int nField)
183 {
184  if (nField != -1)
185  {
186  SQLRETURN rc;
187  SQLSMALLINT buflen;
188  unsigned long int colType;
189  rc = m_pInterface->GetSQLColAttribute()(m_pOdbcStatement, nField, SQL_DESC_TYPE, NULL, 0,
190  &buflen, &colType);
191 
192  if (SQL_FLOAT == colType || SQL_DOUBLE == colType)
193  {
194  SQLFLOAT ret;
195  SQLLEN sqlPtr;
196  rc = m_pInterface->GetSQLGetData()(m_pOdbcStatement, nField, SQL_C_DOUBLE, &ret, 0, &sqlPtr);
197  if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
198  {
201  return;
202  }
203  // Mark this field as retrieved
204  m_RetrievedValues.insert(nField);
205  // Record whether this field is NULL
206  if (sqlPtr == SQL_NULL_DATA)
207  {
208  m_NullValues.insert(nField);
209  ret = 0;
210  m_fieldValues[nField-1] = ret;
211  }
212  else
213  {
214  m_fieldValues[nField-1] = ret;
215  }
216  }
217  else if (SQL_DATETIME == colType)
218  {
219  TIMESTAMP_STRUCT ret;
220  SQLLEN sqlPtr;
221  rc = m_pInterface->GetSQLGetData()(m_pOdbcStatement, nField, SQL_C_TIMESTAMP, &ret, sizeof(ret),
222  &sqlPtr);
223 
224  if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
225  {
228  return;
229  }
230  // Mark this field as retrieved
231  m_RetrievedValues.insert(nField);
232  // Record whether this field is NULL
233  if (sqlPtr == SQL_NULL_DATA)
234  {
235  m_NullValues.insert(nField);
236  //m_fieldValues[nField-1] = wxInvalidDateTime;
237  }
238  else
239  {
240  /*
241  wxPrintf(_T("day = %d, month = %d, year = %d, hour = %d, minute = %d, second = %d, fraction = %d\n"),
242  ret.day, ret.month, ret.year, ret.hour, ret.minute, ret.second, ret.fraction);*/
243  wxDateTime dt(ret.day, wxDateTime::Month(ret.month-1), ret.year, ret.hour,
244  ret.minute, ret.second, ret.fraction);
245  m_fieldValues[nField-1] = dt;
246  }
247  }
248  else
249  {
250  wxString strValue;
251  SQLPOINTER buff[8192];
252 
253  memset(buff, 0, 8192*sizeof(SQLTCHAR));
254 
255  SQLINTEGER col_size = 8192;
256  SQLLEN real_size = 0;
257 
258  if (m_pOdbcStatement == NULL)
260 
261  SQLRETURN nRet = m_pInterface->GetSQLGetData()( m_pOdbcStatement, nField, SQL_C_TCHAR, buff, col_size, &real_size ); // should SQL_C_CHAR be SQL_C_TCHAR?
262  if ( nRet != SQL_SUCCESS && nRet != SQL_SUCCESS_WITH_INFO )
263  {
266  return;
267  }
268  strValue += ConvertFromUnicodeStream((const wxChar*)/*(const char*)*/buff);
269  // Mark this field as retrieved
270  m_RetrievedValues.insert(nField);
271  // Record whether this field is NULL
272  if (real_size == SQL_NULL_DATA)
273  m_NullValues.insert(nField);
274 
275  if ( real_size > col_size )
276  {
277  while ( nRet != SQL_NO_DATA )
278  {
279  nRet = m_pInterface->GetSQLGetData()( m_pOdbcStatement, nField, SQL_C_TCHAR, buff, col_size, &real_size ); // should SQL_C_CHAR be SQL_C_TCHAR?
280  if ( nRet != SQL_SUCCESS && nRet != SQL_SUCCESS_WITH_INFO && nRet != SQL_NO_DATA )
281  {
284  return;
285  }
286  strValue += ConvertFromUnicodeStream((const wxChar*)/*(const char*)*/buff);
287  }
288  }
289 
290  m_fieldValues[nField-1] = strValue;//.Trim();
291  }
292  }
293 }
294 
295 int wxOdbcResultSet::GetFieldLength(const wxString& strField)
296 {
297  int nIndex = LookupField(strField);
298  if ( nIndex == -1 )
299  return -1;
300 
301  return GetFieldLength( nIndex );
302 }
303 
304 int wxOdbcResultSet::GetResultInt(int nField)
305 {
306  if ( m_fieldValues[nField-1].IsNull() )
307  {
308  if ( GetFieldLength(nField) < 0 )
309  return 0;
310  }
311 
312  return m_fieldValues[nField-1].GetLong();
313 }
314 
315 wxString wxOdbcResultSet::GetResultString(int nField)
316 {
317  if ( m_fieldValues[nField-1].IsNull() )
318  {
319  if ( GetFieldLength(nField) < 0 )
320  return wxEmptyString;
321  }
322 
323  return m_fieldValues[nField-1].GetString();
324 }
325 
326 long wxOdbcResultSet::GetResultLong(int nField)
327 {
328  if ( m_fieldValues[nField-1].IsNull() )
329  {
330  if ( GetFieldLength(nField) < 0 )
331  return 0;
332  }
333 
334  return m_fieldValues[nField-1].GetLong();
335 }
336 
337 bool wxOdbcResultSet::GetResultBool(int nField)
338 {
339  if ( m_fieldValues[nField-1].IsNull() )
340  {
341  if ( GetFieldLength(nField) < 0 )
342  return false;
343  }
344 
345  return m_fieldValues[nField-1].GetBool();
346 }
347 
348 wxDateTime wxOdbcResultSet::GetResultDate(int nField)
349 {
350  if ( m_fieldValues[nField-1].IsNull() )
351  {
352  if ( GetFieldLength(nField) <= 0 )
353  return wxInvalidDateTime;
354  }
355 
356  return m_fieldValues[nField-1].GetDateTime();
357 }
358 
359 double wxOdbcResultSet::GetResultDouble(int nField)
360 {
361  if ( m_fieldValues[nField-1].IsNull() )
362  {
363  if ( GetFieldLength(nField) < 0 )
364  return 0;
365  }
366 
367  return m_fieldValues[nField-1].GetDouble();
368 }
369 
370 void* wxOdbcResultSet::GetResultBlob(int nField, wxMemoryBuffer& Buffer)
371 {
372  if (m_BlobMap.find(nField) == m_BlobMap.end())
373  {
374  if (m_pOdbcStatement == NULL)
376 
377  if (m_NullValues.find(nField) != m_NullValues.end())
378  return NULL;
379 
380  SQLINTEGER iLength = 8192;
381  SQLLEN iSize = 0;
382  unsigned char buff[8193];
383 
384  memset(buff, 0, 8193*sizeof(unsigned char));
385 
386  long nReturn = m_pInterface->GetSQLBindParameter()(m_pOdbcStatement, nField, SQL_PARAM_OUTPUT,
387  SQL_C_BINARY, SQL_BINARY, iLength, 0, &buff, iLength, &iSize);
388 
389  // Mark this field as retrieved
390  m_RetrievedValues.insert(nField);
391  // Record whether this field is NULL
392  if (iSize == SQL_NULL_DATA)
393  {
394  m_NullValues.insert(nField);
395  return NULL;
396  }
397 
398  nReturn = m_pInterface->GetSQLGetData()( m_pOdbcStatement, nField, SQL_C_BINARY, &buff, iLength, &iSize );
399  if ( nReturn != SQL_SUCCESS && nReturn != SQL_SUCCESS_WITH_INFO )
400  {
401  wxLogError(_T("Error with RunQueryWithResults - 1\n"));
404  }
405 
406  // NULL data
407  if (iSize < 0)
408  {
409  wxMemoryBuffer tempBuffer(0);
410  tempBuffer.SetDataLen(0);
411  tempBuffer.SetBufSize(0);
412  Buffer = tempBuffer;
413 
414  // Add null blobs to the map as well
415  m_BlobMap[nField] = tempBuffer;
416  return NULL;
417  }
418 
419  size_t dataLength = (iLength < iSize) ? iLength : iSize;
420  size_t bufferSize = dataLength;
421  wxMemoryBuffer tempBuffer(dataLength);
422 
423  tempBuffer.AppendData( buff, dataLength );
424 
425  while ( iSize > iLength )
426  {
427  nReturn = m_pInterface->GetSQLGetData()( m_pOdbcStatement, nField, SQL_C_BINARY, &buff, iLength, &iSize );
428  if ( nReturn != SQL_SUCCESS && nReturn != SQL_SUCCESS_WITH_INFO )
429  {
430  wxLogError(_T("Error with RunQueryWithResults - 2\n"));
433  }
434 
435  dataLength = (iLength < iSize) ? iLength : iSize;
436  tempBuffer.AppendData( buff, dataLength );
437  bufferSize += dataLength;
438  }
439 
440  wxMemoryBuffer tempBufferExactSize(bufferSize);
441  void* pData = tempBufferExactSize.GetWriteBuf(bufferSize);
442  memcpy(pData, tempBuffer.GetData(), bufferSize);
443  tempBufferExactSize.UngetWriteBuf(bufferSize);
444  tempBufferExactSize.SetDataLen(bufferSize);
445  tempBufferExactSize.SetBufSize(bufferSize);
446  Buffer = tempBufferExactSize;
447 
448  wxMemoryBuffer localCopy(Buffer);
449  m_BlobMap[nField] = localCopy;
450 
451  return Buffer.GetData();
452  }
453  else
454  {
455  BlobMap::iterator it = m_BlobMap.find(nField);
456  if (it == m_BlobMap.end())
457  {
458  wxMemoryBuffer tempBuffer(0);
459  tempBuffer.SetDataLen(0);
460  tempBuffer.SetBufSize(0);
461  Buffer = tempBuffer;
462  return NULL;
463  }
464  else
465  {
466  Buffer = it->second;
467  return Buffer.GetData();
468  }
469  }
470 }
471 
472 int wxOdbcResultSet::LookupField(const wxString& strField)
473 {
474  StringToIntMap::iterator SearchIterator = m_FieldLookupMap.find(strField.Upper());
475  if (SearchIterator == m_FieldLookupMap.end())
476  {
477  wxString msg(_("Field '") + strField + _("' not found in the resultset"));
478 #if wxUSE_DATABASE_EXCEPTIONS
479  wxDatabaseException error(wxDATABASE_FIELD_NOT_IN_RESULTSET, msg);
480  throw error;
481 #else
482  wxLogError(msg);
483 #endif
484  //return -1;
485  }
486  else
487  {
488  return ((*SearchIterator).second+1); // Add +1 to make the result set 1-based rather than 0-based
489  }
490 }
491 
493 {
495  LogMetaDataForCleanup(pMetaData);
496  return pMetaData;
497 }
498 
499 void wxOdbcResultSet::InterpretErrorCodes( long WXUNUSED(nCode), SQLHSTMT stmth_ptr )
500 {
501  //if ((nCode != SQL_SUCCESS) ) // && (nCode != SQL_SUCCESS_WITH_INFO))
502  {
503  SQLINTEGER iNativeCode;
504  SQLTCHAR strState[ERR_STATE_LEN];
505  SQLTCHAR strBuffer[ERR_BUFFER_LEN];
506  SQLSMALLINT iMsgLen;
507 
508  memset(strState, 0, ERR_STATE_LEN*sizeof(SQLTCHAR));
509  memset(strBuffer, 0, ERR_BUFFER_LEN*sizeof(SQLTCHAR));
510 
511  m_pInterface->GetSQLGetDiagRec()(SQL_HANDLE_STMT, stmth_ptr, 1, strState, &iNativeCode,
512  strBuffer, ERR_BUFFER_LEN, &iMsgLen);
513 
514  SetErrorCode((int)iNativeCode);
515  SetErrorMessage(ConvertFromUnicodeStream((char*)strBuffer)); // AML
516  //AML SetErrorMessage(wxString((wxChar*)strBuffer));
517  }
518 }
519 
520 bool wxOdbcResultSet::IsBlob(int nField)
521 {
522  SQLTCHAR col_name[8192];
523  SQLSMALLINT col_name_length;
524  SQLSMALLINT col_data_type;
525  SQLULEN col_size;
526  SQLSMALLINT col_decimal_digits;
527  SQLSMALLINT col_nullable;
528 
529  memset(col_name, 0, 8192);
530 
531  /*SQLRETURN nRet = */m_pInterface->GetSQLDescribeCol()( m_pOdbcStatement, nField, col_name,
532  8192, &col_name_length, &col_data_type, &col_size, &col_decimal_digits, &col_nullable );
533  //not used so mark it so
534 
535  return (col_data_type == SQL_BIT || col_data_type == SQL_BINARY ||
536  col_data_type == SQL_VARBINARY || col_data_type == SQL_LONGVARBINARY);
537 }
538 
539 #endif//wxUSE_DATABASE_ODBC
StringToIntMap m_FieldLookupMap
virtual void Close()
Close the result set (call wxDatabase::CloseResultSet() instead on the result set)
IntegerSet m_RetrievedValues
virtual wxResultSetMetaData * GetMetaData()
Retrieve the MetaData associated with this result set.
SQLDescribeColType GetSQLDescribeCol()
Definition: odbc_inteface.h:78
virtual bool IsFieldNull(int nField)
Check if a field in the current result set record is NULL.
virtual long GetResultLong(int nField)
Retrieve a long from the result set by the 1-based field index.
virtual void * GetResultBlob(int nField, wxMemoryBuffer &Buffer)
Retrieve a BLOB from the result set by the 1-based field index.
void LogMetaDataForCleanup(wxResultSetMetaData *pMetaData)
Add meta data object pointer to the list for "garbage collection".
Definition: resultset.h:70
wxOdbcPreparedStatement * m_pStatement
void RetrieveFieldData(int nField)
ValuesArray m_fieldValues
#define ERR_BUFFER_LEN
Definition: odbc_database.h:8
#define wxDATABASE_FIELD_NOT_IN_RESULTSET
Definition: errorcodes.h:12
SQLBindParameterType GetSQLBindParameter()
Definition: odbc_inteface.h:75
virtual bool Next()
Move to the next record in the result set.
IntegerSet m_NullValues
SQLColAttributeType GetSQLColAttribute()
Definition: odbc_inteface.h:77
wxOdbcInterface * m_pInterface
void CloseMetaData()
Close all meta data objects that have been generated but not yet closed.
Definition: resultset.cpp:96
void SetErrorMessage(const wxString &strErrorMessage)
SQLColAttributesType GetSQLColAttributes()
Definition: odbc_inteface.h:76
virtual double GetResultDouble(int nField)
Retrieve a double from the result set by the 1-based field index.
virtual wxString GetResultString(int nField)
Retrieve a wxString from the result set by the 1-based field index.
virtual bool GetResultBool(int nField)
Retrieve a boolean from the result set by the 1-based field index.
virtual wxDateTime GetResultDate(int nField)
Retrieve a wxDateTime from the result set by the 1-based field index.
virtual int LookupField(const wxString &strField)
SQLFetchType GetSQLFetch()
Definition: odbc_inteface.h:63
virtual int GetResultInt(int nField)
Retrieve an integer from the result set by the 1-based field index.
void SetErrorCode(int nErrorCode)
SQLGetDiagRecType GetSQLGetDiagRec()
Definition: odbc_inteface.h:67
SQLHSTMT m_pOdbcStatement
virtual wxString ConvertFromUnicodeStream(const char *inputBuffer)
wxOdbcResultSet(wxOdbcInterface *pInterface)
virtual ~wxOdbcResultSet()
SQLGetDataType GetSQLGetData()
Definition: odbc_inteface.h:64
void InterpretErrorCodes(long nCode, SQLHSTMT stmth_ptr=NULL)
virtual int GetFieldLength(int nField)
bool IsBlob(int nField)
void InterpretErrorCodes(long nCode, SQLHSTMT stmth_ptr=NULL)
#define ERR_STATE_LEN
Definition: odbc_database.h:9