aGrUM  0.21.0
a C++ library for (probabilistic) graphical models
nanodbc.cpp
Go to the documentation of this file.
1 #ifndef DOXYGEN_SHOULD_SKIP_THIS
2 
3 #ifdef _ODBC // Enable Nanodbc only if an odbc driver was found
4 
5 //! \file nanodbc.cpp Implementation details.
6 #ifndef DOXYGEN
7 
8 // ASCII art banners are helpful for code editors with a minimap display.
9 // Generated with http://patorjk.com/software/taag/#p=display&v=0&f=Colossal
10 
11 #include "nanodbc.h"
12 
13 #include <algorithm>
14 #include <clocale>
15 #include <cstdio>
16 #include <cstring>
17 #include <ctime>
18 #include <iomanip>
19 #include <map>
20 
21 #ifndef __clang__
22  #include <cstdint>
23 #endif
24 
25 // User may redefine NANODBC_ASSERT macro in nanodbc.h
26 #ifndef NANODBC_ASSERT
27  #include <cassert>
28  #define NANODBC_ASSERT(expr) assert(expr)
29 #endif
30 
31 #ifdef NANODBC_USE_BOOST_CONVERT
32  #include <boost/locale/encoding_utf.hpp>
33 #else
34  #include <codecvt>
35 #endif
36 
37 #if defined(_MSC_VER) && _MSC_VER <= 1800
38  // silence spurious Visual C++ warnings
39  #pragma warning(disable:4244) // warning about integer conversion issues.
40  #pragma warning(disable:4312) // warning about 64-bit portability issues.
41  #pragma warning(disable:4996) // warning about snprintf() deprecated.
42 #endif
43 
44 #ifdef __APPLE__
45  // silence spurious OS X deprecation warnings
46  #define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_6
47 #endif
48 
49 #ifdef _WIN32
50  // needs to be included above sql.h for windows
51  #define NOMINMAX
52  #include <windows.h>
53 #endif
54 
55 #include <sql.h>
56 #include <sqlext.h>
57 
58 // Default to ODBC version defined by NANODBC_ODBC_VERSION if provided.
59 #ifndef NANODBC_ODBC_VERSION
60  #ifdef SQL_OV_ODBC3_80
61  // Otherwise, use ODBC v3.8 if it's available...
62  #define NANODBC_ODBC_VERSION SQL_OV_ODBC3_80
63  #else
64  // or fallback to ODBC v3.x.
65  #define NANODBC_ODBC_VERSION SQL_OV_ODBC3
66  #endif
67 #endif
68 
69 // 888 888 d8b 888
70 // 888 888 Y8P 888
71 // 888 888 888
72 // 888 888 88888b. 888 .d8888b .d88b. .d88888 .d88b.
73 // 888 888 888 "88b 888 d88P" d88""88b d88" 888 d8P Y8b
74 // 888 888 888 888 888 888 888 888 888 888 88888888
75 // Y88b. .d88P 888 888 888 Y88b. Y88..88P Y88b 888 Y8b.
76 // "Y88888P" 888 888 888 "Y8888P "Y88P" "Y88888 "Y8888
77 // MARK: Unicode -
78 
79 #ifdef NANODBC_USE_UNICODE
80  #ifdef NANODBC_USE_IODBC_WIDE_STRINGS
81  #define NANODBC_TEXT(s) U ## s
82  #else
83  #define NANODBC_TEXT(s) u ## s
84  #endif
85  #define NANODBC_FUNC(f) f ## W
86  #define NANODBC_SQLCHAR SQLWCHAR
87 #else
88  #define NANODBC_TEXT(s) s
89  #define NANODBC_FUNC(f) f
90  #define NANODBC_SQLCHAR SQLCHAR
91 #endif
92 
93 #ifdef NANODBC_USE_IODBC_WIDE_STRINGS
94  typedef std::u32string wide_string_type;
95  #define NANODBC_CODECVT_TYPE std::codecvt_utf8
96 #else
97  typedef std::u16string wide_string_type;
98  #define NANODBC_CODECVT_TYPE std::codecvt_utf8_utf16
99 #endif
100 typedef wide_string_type::value_type wide_char_t;
101 
102 #if defined(_MSC_VER)
103  #ifndef NANODBC_USE_UNICODE
104  // Disable unicode in sqlucode.h on Windows when NANODBC_USE_UNICODE
105  // is not defined. This is required because unicode is enabled by
106  // default on many Windows systems.
107  #define SQL_NOUNICODEMAP
108  #endif
109 #endif
110 
111 // .d88888b. 8888888b. 888888b. .d8888b. 888b d888
112 // d88P" "Y88b 888 "Y88b 888 "88b d88P Y88b 8888b d8888
113 // 888 888 888 888 888 .88P 888 888 88888b.d88888
114 // 888 888 888 888 8888888K. 888 888Y88888P888 8888b. .d8888b 888d888 .d88b. .d8888b
115 // 888 888 888 888 888 "Y88b 888 888 Y888P 888 "88b d88P" 888P" d88""88b 88K
116 // 888 888 888 888 888 888 888 888 888 Y8P 888 .d888888 888 888 888 888 "Y8888b.
117 // Y88b. .d88P 888 .d88P 888 d88P Y88b d88P 888 " 888 888 888 Y88b. 888 Y88..88P X88
118 // "Y88888P" 8888888P" 8888888P" "Y8888P" 888 888 "Y888888 "Y8888P 888 "Y88P" 88888P'
119 // MARK: ODBC Macros -
120 
121 #define NANODBC_STRINGIZE_I(text) #text
122 #define NANODBC_STRINGIZE(text) NANODBC_STRINGIZE_I(text)
123 
124 // By making all calls to ODBC functions through this macro, we can easily get
125 // runtime debugging information of which ODBC functions are being called,
126 // in what order, and with what parameters by defining NANODBC_ODBC_API_DEBUG.
127 #ifdef NANODBC_ODBC_API_DEBUG
128  #include <iostream>
129  #define NANODBC_CALL_RC(FUNC, RC, ...)
130  do
131  {
132  std::cerr << __FILE__ ":" NANODBC_STRINGIZE( __LINE__) " "
133  NANODBC_STRINGIZE(FUNC) "(" # __VA_ARGS__ ")" << std::endl;
134  RC = FUNC( __VA_ARGS__);
135  } while(false)
136  /**/
137  #define NANODBC_CALL(FUNC, ...)
138  do
139  {
140  std::cerr << __FILE__ ":" NANODBC_STRINGIZE( __LINE__) " "
141  NANODBC_STRINGIZE(FUNC) "(" # __VA_ARGS__ ")" << std::endl;
142  FUNC( __VA_ARGS__);
143  } while(false)
144  /**/
145 #else
146  #define NANODBC_CALL_RC(FUNC, RC, ...) RC = FUNC( __VA_ARGS__)
147  #define NANODBC_CALL(FUNC, ...) FUNC( __VA_ARGS__)
148 #endif
149 
150 // 8888888888 888 888 888 888 d8b
151 // 888 888 888 888 888 Y8P
152 // 888 888 888 888 888
153 // 8888888 888d888 888d888 .d88b. 888d888 8888888888 8888b. 88888b. .d88888 888 888 88888b. .d88b.
154 // 888 888P" 888P" d88""88b 888P" 888 888 "88b 888 "88b d88" 888 888 888 888 "88b d88P"88b
155 // 888 888 888 888 888 888 888 888 .d888888 888 888 888 888 888 888 888 888 888 888
156 // 888 888 888 Y88..88P 888 888 888 888 888 888 888 Y88b 888 888 888 888 888 Y88b 888
157 // 8888888888 888 888 "Y88P" 888 888 888 "Y888888 888 888 "Y88888 888 888 888 888 "Y88888
158 // 888
159 // Y8b d88P
160 // "Y88P"
161 // MARK: Error Handling -
162 
163 namespace
164 {
165  #ifdef NANODBC_ODBC_API_DEBUG
166  inline nanodbc::string_type return_code(RETCODE rc)
167  {
168  switch(rc)
169  {
170  case SQL_SUCCESS: return NANODBC_TEXT("SQL_SUCCESS");
171  case SQL_SUCCESS_WITH_INFO: return NANODBC_TEXT("SQL_SUCCESS_WITH_INFO");
172  case SQL_ERROR: return NANODBC_TEXT("SQL_ERROR");
173  case SQL_INVALID_HANDLE: return NANODBC_TEXT("SQL_INVALID_HANDLE");
174  case SQL_NO_DATA: return NANODBC_TEXT("SQL_NO_DATA");
175  case SQL_NEED_DATA: return NANODBC_TEXT("SQL_NEED_DATA");
176  case SQL_STILL_EXECUTING: return NANODBC_TEXT("SQL_STILL_EXECUTING");
177  }
178  NANODBC_ASSERT(0);
179  return "unknown"; // should never make it here
180  }
181  #endif
182 
183  // Easy way to check if a return code signifies success.
184  inline bool success(RETCODE rc)
185  {
186  #ifdef NANODBC_ODBC_API_DEBUG
187  std::cerr << "<-- rc: " << return_code(rc) << " | ";
188  #endif
189  return rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO;
190  }
191 
192  // Returns the array size.
193  template<typename T, std::size_t N>
194  inline std::size_t arrlen(T(&)[N])
195  {
196  return N;
197  }
198 
199  // Operates like strlen() on a character array.
200  template<typename T, std::size_t N>
201  inline std::size_t strarrlen(T(&a)[N])
202  {
203  const T* s = &a[0];
204  std::size_t i = 0;
205  while(*s++ && i < N) i++;
206  return i;
207  }
208 
209  inline void convert(const wide_string_type& in, std::string& out)
210  {
211  #ifdef NANODBC_USE_BOOST_CONVERT
212  using boost::locale::conv::utf_to_utf;
213  out = utf_to_utf<char>(in.c_str(), in.c_str() + in.size());
214  #else
215  #if defined(_MSC_VER) && (_MSC_VER == 1900)
216  // Workaround for confirmed bug in VS2015.
217  // See: https://social.msdn.microsoft.com/Forums/en-US/8f40dcd8-c67f-4eba-9134-a19b9178e481/vs-2015-rc-linker-stdcodecvt-error
218  auto p = reinterpret_cast<wide_char_t const*>(in.data());
219  out = std::wstring_convert<NANODBC_CODECVT_TYPE<wide_char_t>, wide_char_t>().to_bytes(p, p + in.size());
220  #else
221  out = std::wstring_convert<NANODBC_CODECVT_TYPE<wide_char_t>, wide_char_t>().to_bytes(in);
222  #endif
223  #endif
224  }
225 
226  #ifdef NANODBC_USE_UNICODE
227  inline void convert(const std::string& in, wide_string_type& out)
228  {
229  #ifdef NANODBC_USE_BOOST_CONVERT
230  using boost::locale::conv::utf_to_utf;
231  out = utf_to_utf<wide_char_t>(in.c_str(), in.c_str() + in.size());
232  #elif defined(_MSC_VER) && (_MSC_VER == 1900)
233  // Workaround for confirmed bug in VS2015.
234  // See: https://social.msdn.microsoft.com/Forums/en-US/8f40dcd8-c67f-4eba-9134-a19b9178e481/vs-2015-rc-linker-stdcodecvt-error
235  auto s = std::wstring_convert<NANODBC_CODECVT_TYPE<wide_char_t>, wide_char_t>().from_bytes(in);
236  auto p = reinterpret_cast<wide_char_t const*>(s.data());
237  out.assign(p, p + s.size());
238  #else
239  out = std::wstring_convert<NANODBC_CODECVT_TYPE<wide_char_t>, wide_char_t>().from_bytes(in);
240  #endif
241  }
242 
243  inline void convert(const wide_string_type& in, wide_string_type& out)
244  {
245  out = in;
246  }
247  #else
248  inline void convert(const std::string& in, std::string& out)
249  {
250  out = in;
251  }
252  #endif
253 
254  // Attempts to get the most recent ODBC error as a string.
255  // Always returns std::string, even in unicode mode.
256  inline std::string recent_error(
257  SQLHANDLE handle
258  , SQLSMALLINT handle_type
259  , long &native
260  , std::string &state)
261  {
262  nanodbc::string_type result;
263  std::string rvalue;
264  std::vector<NANODBC_SQLCHAR> sql_message(SQL_MAX_MESSAGE_LENGTH);
265  sql_message[0] = '\0';
266 
267  SQLINTEGER i = 1;
268  SQLINTEGER native_error;
269  SQLSMALLINT total_bytes;
270  NANODBC_SQLCHAR sql_state[6];
271  RETCODE rc;
272 
273  do
274  {
275  NANODBC_CALL_RC(
276  NANODBC_FUNC(SQLGetDiagRec)
277  , rc
278  , handle_type
279  , handle
280  , (SQLSMALLINT)i
281  , sql_state
282  , &native_error
283  , 0
284  , 0
285  , &total_bytes);
286 
287  if(success(rc) && total_bytes > 0)
288  sql_message.resize(total_bytes + 1);
289 
290  if(rc == SQL_NO_DATA)
291  break;
292 
293  NANODBC_CALL_RC(
294  NANODBC_FUNC(SQLGetDiagRec)
295  , rc
296  , handle_type
297  , handle
298  , (SQLSMALLINT)i
299  , sql_state
300  , &native_error
301  , sql_message.data()
302  , (SQLSMALLINT)sql_message.size()
303  , &total_bytes);
304 
305  if(!success(rc))
306  {
307  convert(result, rvalue);
308  return rvalue;
309  }
310 
311  if(!result.empty())
312  result += ' ';
313 
314  result += nanodbc::string_type(sql_message.begin(), sql_message.end());
315  i++;
316 
317  // NOTE: unixODBC using PostgreSQL and SQLite drivers crash if you call SQLGetDiagRec()
318  // more than once. So as a (terrible but the best possible) workaround just exit
319  // this loop early on non-Windows systems.
320  #ifndef _MSC_VER
321  break;
322  #endif
323  } while(rc != SQL_NO_DATA);
324 
325  convert(result, rvalue);
326  state = std::string(&sql_state[0], &sql_state[arrlen(sql_state) - 1]);
327  native = native_error;
328  std::string status = state;
329  status += ": ";
330  status += rvalue;
331 
332  // some drivers insert \0 into error messages for unknown reasons
333  using std::replace;
334  replace(status.begin(), status.end(), '\0', ' ');
335 
336  return status;
337  }
338 } // namespace
339 
340 namespace nanodbc
341 {
342  type_incompatible_error::type_incompatible_error()
343  : std::runtime_error("type incompatible") { }
344 
345  const char* type_incompatible_error::what() const NANODBC_NOEXCEPT
346  {
347  return std::runtime_error::what();
348  }
349 
350  null_access_error::null_access_error()
351  : std::runtime_error("null access") { }
352 
353  const char* null_access_error::what() const NANODBC_NOEXCEPT
354  {
355  return std::runtime_error::what();
356  }
357 
358  index_range_error::index_range_error()
359  : std::runtime_error("index out of range") { }
360 
361  const char* index_range_error::what() const NANODBC_NOEXCEPT
362  {
363  return std::runtime_error::what();
364  }
365 
366  programming_error::programming_error(const std::string& info)
367  : std::runtime_error(info.c_str()) { }
368 
369  const char* programming_error::what() const NANODBC_NOEXCEPT
370  {
371  return std::runtime_error::what();
372  }
373 
374  database_error::database_error(void* handle, short handle_type, const std::string& info)
375  : std::runtime_error(info), native_error(0), sql_state("00000")
376  {
377  message = std::string(std::runtime_error::what()) + recent_error(handle, handle_type, native_error, sql_state);
378  }
379 
380  const char* database_error::what() const NANODBC_NOEXCEPT
381  {
382  return message.c_str();
383  }
384 
385  const long database_error::native() const NANODBC_NOEXCEPT
386  {
387  return native_error;
388  }
389 
390  const std::string database_error::state() const NANODBC_NOEXCEPT
391  {
392  return sql_state;
393  }
394 
395 } // namespace nanodbc
396 
397 // Throwing exceptions using NANODBC_THROW_DATABASE_ERROR enables file name
398 // and line numbers to be inserted into the error message. Useful for debugging.
399 #define NANODBC_THROW_DATABASE_ERROR(handle, handle_type)
400  throw nanodbc::database_error(
401  handle
402  , handle_type
403  , __FILE__ ":" NANODBC_STRINGIZE( __LINE__) ": ")
404  /**/
405 
406 // 8888888b. 888 d8b 888
407 // 888 "Y88b 888 Y8P 888
408 // 888 888 888 888
409 // 888 888 .d88b. 888888 8888b. 888 888 .d8888b
410 // 888 888 d8P Y8b 888 "88b 888 888 88K
411 // 888 888 88888888 888 .d888888 888 888 "Y8888b.
412 // 888 .d88P Y8b. Y88b. 888 888 888 888 X88
413 // 8888888P" "Y8888 "Y888 "Y888888 888 888 88888P'
414 // MARK: Details -
415 
416 namespace
417 {
418  using namespace std; // if int64_t is in std namespace (in c++11)
419 
420  // A utility for calculating the ctype from the given type T.
421  // I essentially create a lookup table based on the MSDN ODBC documentation.
422  // See http://msdn.microsoft.com/en-us/library/windows/desktop/ms714556(v=vs.85).aspx
423  template<class T>
424  struct sql_ctype { };
425 
426  template<>
427  struct sql_ctype<nanodbc::string_type::value_type>
428  {
429  #ifdef NANODBC_USE_UNICODE
430  static const SQLSMALLINT value = SQL_C_WCHAR;
431  #else
432  static const SQLSMALLINT value = SQL_C_CHAR;
433  #endif
434  };
435 
436  template<>
437  struct sql_ctype<short>
438  {
439  static const SQLSMALLINT value = SQL_C_SSHORT;
440  };
441 
442  template<>
443  struct sql_ctype<unsigned short>
444  {
445  static const SQLSMALLINT value = SQL_C_USHORT;
446  };
447 
448  template<>
449  struct sql_ctype<int32_t>
450  {
451  static const SQLSMALLINT value = SQL_C_SLONG;
452  };
453 
454  template<>
455  struct sql_ctype<uint32_t>
456  {
457  static const SQLSMALLINT value = SQL_C_ULONG;
458  };
459 
460  template<>
461  struct sql_ctype<int64_t>
462  {
463  static const SQLSMALLINT value = SQL_C_SBIGINT;
464  };
465 
466  template<>
467  struct sql_ctype<uint64_t>
468  {
469  static const SQLSMALLINT value = SQL_C_UBIGINT;
470  };
471 
472  template<>
473  struct sql_ctype<float>
474  {
475  static const SQLSMALLINT value = SQL_C_FLOAT;
476  };
477 
478  template<>
479  struct sql_ctype<double>
480  {
481  static const SQLSMALLINT value = SQL_C_DOUBLE;
482  };
483 
484  template<>
485  struct sql_ctype<nanodbc::string_type>
486  {
487  #ifdef NANODBC_USE_UNICODE
488  static const SQLSMALLINT value = SQL_C_WCHAR;
489  #else
490  static const SQLSMALLINT value = SQL_C_CHAR;
491  #endif
492  };
493 
494  template<>
495  struct sql_ctype<nanodbc::date>
496  {
497  static const SQLSMALLINT value = SQL_C_DATE;
498  };
499 
500  template<>
501  struct sql_ctype<nanodbc::timestamp>
502  {
503  static const SQLSMALLINT value = SQL_C_TIMESTAMP;
504  };
505 
506  // Encapsulates resources needed for column binding.
507  class bound_column
508  {
509  public:
510  bound_column(const bound_column& rhs) =delete;
511  bound_column& operator=(bound_column rhs) =delete;
512 
513  bound_column()
514  : name_()
515  , column_(0)
516  , sqltype_(0)
517  , sqlsize_(0)
518  , scale_(0)
519  , ctype_(0)
520  , clen_(0)
521  , blob_(false)
522  , cbdata_(0)
523  , pdata_(0)
524  {
525 
526  }
527 
528  ~bound_column()
529  {
530  delete[] cbdata_;
531  delete[] pdata_;
532  }
533 
534  public:
535  nanodbc::string_type name_;
536  short column_;
537  SQLSMALLINT sqltype_;
538  SQLULEN sqlsize_;
539  SQLSMALLINT scale_;
540  SQLSMALLINT ctype_;
541  SQLULEN clen_;
542  bool blob_;
543  nanodbc::null_type* cbdata_;
544  char* pdata_;
545  };
546 
547  // Allocates the native ODBC handles.
548  inline void allocate_handle(SQLHENV& env, SQLHDBC& conn)
549  {
550  RETCODE rc;
551  NANODBC_CALL_RC(
552  SQLAllocHandle
553  , rc
554  , SQL_HANDLE_ENV
555  , SQL_NULL_HANDLE
556  , &env);
557  if(!success(rc))
558  NANODBC_THROW_DATABASE_ERROR(env, SQL_HANDLE_ENV);
559 
560  try
561  {
562  NANODBC_CALL_RC(
563  SQLSetEnvAttr
564  , rc
565  , env
566  , SQL_ATTR_ODBC_VERSION
567  , (SQLPOINTER)NANODBC_ODBC_VERSION
568  , SQL_IS_UINTEGER);
569  if(!success(rc))
570  NANODBC_THROW_DATABASE_ERROR(env, SQL_HANDLE_ENV);
571 
572  NANODBC_CALL_RC(
573  SQLAllocHandle
574  , rc
575  , SQL_HANDLE_DBC
576  , env
577  , &conn);
578  if(!success(rc))
579  NANODBC_THROW_DATABASE_ERROR(env, SQL_HANDLE_ENV);
580  }
581  catch(...)
582  {
583  NANODBC_CALL(
584  SQLFreeHandle
585  , SQL_HANDLE_ENV
586  , env);
587  throw;
588  }
589  }
590 } // namespace
591 
592 // .d8888b. 888 d8b 8888888 888
593 // d88P Y88b 888 Y8P 888 888
594 // 888 888 888 888 888
595 // 888 .d88b. 88888b. 88888b. .d88b. .d8888b 888888 888 .d88b. 88888b. 888 88888b.d88b. 88888b. 888
596 // 888 d88""88b 888 "88b 888 "88b d8P Y8b d88P" 888 888 d88""88b 888 "88b 888 888 "888 "88b 888 "88b 888
597 // 888 888 888 888 888 888 888 888 88888888 888 888 888 888 888 888 888 888 888 888 888 888 888 888
598 // Y88b d88P Y88..88P 888 888 888 888 Y8b. Y88b. Y88b. 888 Y88..88P 888 888 888 888 888 888 888 d88P 888
599 // "Y8888P" "Y88P" 888 888 888 888 "Y8888 "Y8888P "Y888 888 "Y88P" 888 888 8888888 888 888 888 88888P" 888
600 // 888
601 // 888
602 // 888
603 // MARK: Connection Impl -
604 
605 namespace nanodbc
606 {
607 
608 class connection::connection_impl
609 {
610 public:
611  connection_impl(const connection_impl&) =delete;
612  connection_impl& operator=(const connection_impl&) =delete;
613 
614  connection_impl()
615  : env_(0)
616  , conn_(0)
617  , connected_(false)
618  , transactions_(0)
619  , rollback_(false)
620  {
621  allocate_handle(env_, conn_);
622  }
623 
624  connection_impl(
625  const string_type& dsn
626  , const string_type& user
627  , const string_type& pass
628  , long timeout)
629  : env_(0)
630  , conn_(0)
631  , connected_(false)
632  , transactions_(0)
633  , rollback_(false)
634  {
635  allocate_handle(env_, conn_);
636  try
637  {
638  connect(dsn, user, pass, timeout);
639  }
640  catch(...)
641  {
642  NANODBC_CALL(
643  SQLFreeHandle
644  , SQL_HANDLE_DBC
645  , conn_);
646  NANODBC_CALL(
647  SQLFreeHandle
648  , SQL_HANDLE_ENV
649  , env_);
650  throw;
651  }
652  }
653 
654  connection_impl(const string_type& connection_string, long timeout)
655  : env_(0)
656  , conn_(0)
657  , connected_(false)
658  , transactions_(0)
659  , rollback_(false)
660  {
661  allocate_handle(env_, conn_);
662  try
663  {
664  connect(connection_string, timeout);
665  }
666  catch(...)
667  {
668  NANODBC_CALL(
669  SQLFreeHandle
670  , SQL_HANDLE_DBC
671  , conn_);
672  NANODBC_CALL(
673  SQLFreeHandle
674  , SQL_HANDLE_ENV
675  , env_);
676  throw;
677  }
678  }
679 
680  ~connection_impl() NANODBC_NOEXCEPT
681  {
682  try
683  {
684  disconnect();
685  }
686  catch(...)
687  {
688  // ignore exceptions thrown during disconnect
689  }
690  NANODBC_CALL(
691  SQLFreeHandle
692  , SQL_HANDLE_DBC
693  , conn_);
694  NANODBC_CALL(
695  SQLFreeHandle
696  , SQL_HANDLE_ENV
697  , env_);
698  }
699 
700 #ifdef SQL_ATTR_ASYNC_DBC_EVENT
701  void enable_async(void* event_handle)
702  {
703  RETCODE rc;
704  NANODBC_CALL_RC(
705  SQLSetConnectAttr
706  , rc
707  , conn_
708  , SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE
709  , (SQLPOINTER)SQL_ASYNC_DBC_ENABLE_ON
710  , SQL_IS_INTEGER);
711  if(!success(rc))
712  NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
713 
714  NANODBC_CALL_RC(
715  SQLSetConnectAttr
716  , rc
717  , conn_
718  , SQL_ATTR_ASYNC_DBC_EVENT
719  , event_handle
720  , SQL_IS_POINTER);
721  if(!success(rc))
722  NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
723  }
724 
725  void async_complete()
726  {
727  RETCODE rc, arc;
728  NANODBC_CALL_RC(
729  SQLCompleteAsync
730  , rc
731  , SQL_HANDLE_DBC
732  , conn_
733  , &arc);
734  if(!success(rc) || !success(arc))
735  NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
736 
737  NANODBC_CALL_RC(
738  SQLSetConnectAttr
739  , rc
740  , conn_
741  , SQL_ATTR_ASYNC_ENABLE
742  , (SQLPOINTER)SQL_ASYNC_ENABLE_OFF
743  , SQL_IS_INTEGER);
744  if(!success(rc))
745  NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
746 
747  connected_ = success(rc);
748  }
749 #endif // SQL_ATTR_ASYNC_DBC_EVENT
750 
751  void connect(
752  const string_type& dsn
753  , const string_type& user
754  , const string_type& pass
755  , long timeout
756  , void* event_handle = NULL)
757  {
758  disconnect();
759 
760  RETCODE rc;
761  NANODBC_CALL_RC(
762  SQLFreeHandle
763  , rc
764  , SQL_HANDLE_DBC
765  , conn_);
766  if(!success(rc))
767  NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
768 
769  NANODBC_CALL_RC(
770  SQLAllocHandle
771  , rc
772  , SQL_HANDLE_DBC
773  , env_
774  , &conn_);
775  if(!success(rc))
776  NANODBC_THROW_DATABASE_ERROR(env_, SQL_HANDLE_ENV);
777 
778  NANODBC_CALL_RC(
779  SQLSetConnectAttr
780  , rc
781  , conn_
782  , SQL_LOGIN_TIMEOUT
783  , (SQLPOINTER)(std::intptr_t)timeout
784  , 0);
785  if(!success(rc))
786  NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
787 
788  #ifdef SQL_ATTR_ASYNC_DBC_EVENT
789  if(event_handle != NULL)
790  enable_async(event_handle);
791  #endif
792 
793  NANODBC_CALL_RC(
794  NANODBC_FUNC(SQLConnect)
795  , rc
796  , conn_
797  , (NANODBC_SQLCHAR*)dsn.c_str(), SQL_NTS
798  , !user.empty() ? (NANODBC_SQLCHAR*)user.c_str() : 0, SQL_NTS
799  , !pass.empty() ? (NANODBC_SQLCHAR*)pass.c_str() : 0, SQL_NTS);
800  if(!success(rc) && (event_handle == NULL || rc != SQL_STILL_EXECUTING))
801  NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
802 
803  connected_ = success(rc);
804  }
805 
806  void connect(const string_type& connection_string, long timeout, void* event_handle = NULL)
807  {
808  disconnect();
809 
810  RETCODE rc;
811  NANODBC_CALL_RC(
812  SQLFreeHandle
813  , rc
814  , SQL_HANDLE_DBC
815  , conn_);
816  if(!success(rc))
817  NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
818 
819  NANODBC_CALL_RC(
820  SQLAllocHandle
821  , rc
822  , SQL_HANDLE_DBC
823  , env_
824  , &conn_);
825  if(!success(rc))
826  NANODBC_THROW_DATABASE_ERROR(env_, SQL_HANDLE_ENV);
827 
828  NANODBC_CALL_RC(
829  SQLSetConnectAttr
830  , rc
831  , conn_
832  , SQL_LOGIN_TIMEOUT
833  , (SQLPOINTER)(std::intptr_t)timeout
834  , 0);
835  if(!success(rc))
836  NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
837 
838  #ifdef SQL_ATTR_ASYNC_DBC_EVENT
839  if(event_handle != NULL)
840  enable_async(event_handle);
841  #endif
842 
843  NANODBC_SQLCHAR dsn[1024];
844  SQLSMALLINT dsn_size = 0;
845  NANODBC_CALL_RC(
846  NANODBC_FUNC(SQLDriverConnect)
847  , rc
848  , conn_
849  , 0
850  , (NANODBC_SQLCHAR*)connection_string.c_str(), SQL_NTS
851  , dsn
852  , sizeof(dsn) / sizeof(NANODBC_SQLCHAR)
853  , &dsn_size
854  , SQL_DRIVER_NOPROMPT);
855  if(!success(rc) && (event_handle == NULL || rc != SQL_STILL_EXECUTING))
856  NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
857 
858  connected_ = success(rc);
859  }
860 
861  bool connected() const
862  {
863  return connected_;
864  }
865 
866  void disconnect()
867  {
868  if(connected())
869  {
870  RETCODE rc;
871  NANODBC_CALL_RC(
872  SQLDisconnect
873  , rc
874  , conn_);
875  if(!success(rc))
876  NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
877  }
878  connected_ = false;
879  }
880 
881  std::size_t transactions() const
882  {
883  return transactions_;
884  }
885 
886  void* native_dbc_handle() const
887  {
888  return conn_;
889  }
890 
891  void* native_env_handle() const
892  {
893  return env_;
894  }
895 
896  string_type dbms_name() const
897  {
898  NANODBC_SQLCHAR name[255] = { 0 };
899  SQLSMALLINT length(0);
900  RETCODE rc;
901  NANODBC_CALL_RC(
902  NANODBC_FUNC(SQLGetInfo)
903  , rc
904  , conn_
905  , SQL_DBMS_NAME
906  , name
907  , sizeof(name) / sizeof(NANODBC_SQLCHAR)
908  , &length);
909  if (!success(rc))
910  NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
911  return string_type(&name[0], &name[strarrlen(name)]);
912  }
913 
914  string_type dbms_version() const
915  {
916  NANODBC_SQLCHAR version[255] = { 0 };
917  SQLSMALLINT length(0);
918  RETCODE rc;
919  NANODBC_CALL_RC(
920  NANODBC_FUNC(SQLGetInfo)
921  , rc
922  , conn_
923  , SQL_DBMS_VER
924  , version
925  , sizeof(version) / sizeof(NANODBC_SQLCHAR)
926  , &length);
927  if (!success(rc))
928  NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
929  return string_type(&version[0], &version[strarrlen(version)]);
930  }
931 
932  string_type driver_name() const
933  {
934  NANODBC_SQLCHAR name[1024];
935  SQLSMALLINT length;
936  RETCODE rc;
937  NANODBC_CALL_RC(
938  NANODBC_FUNC(SQLGetInfo)
939  , rc
940  , conn_
941  , SQL_DRIVER_NAME
942  , name
943  , sizeof(name) / sizeof(NANODBC_SQLCHAR)
944  , &length);
945  if(!success(rc))
946  NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
947  return string_type(&name[0], &name[strarrlen(name)]);
948  }
949 
950  string_type database_name() const
951  {
952  // Allocate buffer of dynamic size as drivers do not agree on universal size
953  // MySQL driver limits MAX_NAME_LEN=255
954  // PostgreSQL driver MAX_INFO_STIRNG=128
955  // MFC CDatabase allocates buffer dynamically.
956  NANODBC_SQLCHAR name[255] = { 0 };
957  SQLSMALLINT length(0);
958  RETCODE rc;
959  NANODBC_CALL_RC(
960  NANODBC_FUNC(SQLGetInfo)
961  , rc
962  , conn_
963  , SQL_DATABASE_NAME
964  , name
965  , sizeof(name) / sizeof(NANODBC_SQLCHAR)
966  , &length);
967  if(!success(rc))
968  NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
969  return string_type(&name[0], &name[strarrlen(name)]);
970  }
971 
972  string_type catalog_name() const
973  {
974  NANODBC_SQLCHAR name[SQL_MAX_OPTION_STRING_LENGTH] = { 0 };
975  SQLINTEGER length(0);
976  RETCODE rc;
977  NANODBC_CALL_RC(
978  NANODBC_FUNC(SQLGetConnectAttr)
979  , rc
980  , conn_
981  , SQL_ATTR_CURRENT_CATALOG
982  , name
983  , sizeof(name) / sizeof(NANODBC_SQLCHAR)
984  , &length);
985  if(!success(rc))
986  NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
987  return string_type(&name[0], &name[strarrlen(name)]);
988  }
989 
990 
991  std::size_t ref_transaction()
992  {
993  return --transactions_;
994  }
995 
996  std::size_t unref_transaction()
997  {
998  return ++transactions_;
999  }
1000 
1001  bool rollback() const
1002  {
1003  return rollback_;
1004  }
1005 
1006  void rollback(bool onoff)
1007  {
1008  rollback_ = onoff;
1009  }
1010 
1011 private:
1012  HENV env_;
1013  HDBC conn_;
1014  bool connected_;
1015  std::size_t transactions_;
1016  bool rollback_; // if true, this connection is marked for eventual transaction rollback
1017 };
1018 
1019 } // namespace nanodbc
1020 
1021 // 88888888888 888 d8b 8888888 888
1022 // 888 888 Y8P 888 888
1023 // 888 888 888 888
1024 // 888 888d888 8888b. 88888b. .d8888b 8888b. .d8888b 888888 888 .d88b. 88888b. 888 88888b.d88b. 88888b. 888
1025 // 888 888P" "88b 888 "88b 88K "88b d88P" 888 888 d88""88b 888 "88b 888 888 "888 "88b 888 "88b 888
1026 // 888 888 .d888888 888 888 "Y8888b. .d888888 888 888 888 888 888 888 888 888 888 888 888 888 888 888
1027 // 888 888 888 888 888 888 X88 888 888 Y88b. Y88b. 888 Y88..88P 888 888 888 888 888 888 888 d88P 888
1028 // 888 888 "Y888888 888 888 88888P' "Y888888 "Y8888P "Y888 888 "Y88P" 888 888 8888888 888 888 888 88888P" 888
1029 // 888
1030 // 888
1031 // 888
1032 // MARK: Transaction Impl -
1033 
1034 namespace nanodbc
1035 {
1036 
1037 class transaction::transaction_impl
1038 {
1039 public:
1040  transaction_impl(const transaction_impl&) =delete;
1041  transaction_impl& operator=(const transaction_impl&) =delete;
1042 
1043  transaction_impl(const class connection& conn)
1044  : conn_(conn)
1045  , committed_(false)
1046  {
1047  if(conn_.transactions() == 0 && conn_.connected())
1048  {
1049  RETCODE rc;
1050  NANODBC_CALL_RC(
1051  SQLSetConnectAttr
1052  , rc
1053  , conn_.native_dbc_handle()
1054  , SQL_ATTR_AUTOCOMMIT
1055  , (SQLPOINTER)SQL_AUTOCOMMIT_OFF
1056  , SQL_IS_UINTEGER);
1057  if(!success(rc))
1058  NANODBC_THROW_DATABASE_ERROR(conn_.native_dbc_handle(), SQL_HANDLE_DBC);
1059  }
1060  conn_.ref_transaction();
1061  }
1062 
1063  ~transaction_impl() NANODBC_NOEXCEPT
1064  {
1065  if(!committed_)
1066  {
1067  conn_.rollback(true);
1068  conn_.unref_transaction();
1069  }
1070 
1071  if(conn_.transactions() == 0 && conn_.connected())
1072  {
1073  if(conn_.rollback())
1074  {
1075  NANODBC_CALL(
1076  SQLEndTran
1077  , SQL_HANDLE_DBC
1078  , conn_.native_dbc_handle()
1079  , SQL_ROLLBACK);
1080  conn_.rollback(false);
1081  }
1082 
1083  NANODBC_CALL(
1084  SQLSetConnectAttr
1085  , conn_.native_dbc_handle()
1086  , SQL_ATTR_AUTOCOMMIT
1087  , (SQLPOINTER)SQL_AUTOCOMMIT_ON
1088  , SQL_IS_UINTEGER);
1089  }
1090  }
1091 
1092  void commit()
1093  {
1094  if(committed_)
1095  return;
1096  committed_ = true;
1097  if(conn_.unref_transaction() == 0 && conn_.connected())
1098  {
1099  RETCODE rc;
1100  NANODBC_CALL_RC(
1101  SQLEndTran
1102  , rc
1103  , SQL_HANDLE_DBC
1104  , conn_.native_dbc_handle()
1105  , SQL_COMMIT);
1106  if(!success(rc))
1107  NANODBC_THROW_DATABASE_ERROR(conn_.native_dbc_handle(), SQL_HANDLE_DBC);
1108  }
1109  }
1110 
1111  void rollback() NANODBC_NOEXCEPT
1112  {
1113  if(committed_)
1114  return;
1115  conn_.rollback(true);
1116  }
1117 
1118  class connection& connection()
1119  {
1120  return conn_;
1121  }
1122 
1123  const class connection& connection() const
1124  {
1125  return conn_;
1126  }
1127 
1128 private:
1129  class connection conn_;
1130  bool committed_;
1131 };
1132 
1133 } // namespace nanodbc
1134 
1135 // .d8888b. 888 888 888 8888888 888
1136 // d88P Y88b 888 888 888 888 888
1137 // Y88b. 888 888 888 888 888
1138 // "Y888b. 888888 8888b. 888888 .d88b. 88888b.d88b. .d88b. 88888b. 888888 888 88888b.d88b. 88888b. 888
1139 // "Y88b. 888 "88b 888 d8P Y8b 888 "888 "88b d8P Y8b 888 "88b 888 888 888 "888 "88b 888 "88b 888
1140 // "888 888 .d888888 888 88888888 888 888 888 88888888 888 888 888 888 888 888 888 888 888 888
1141 // Y88b d88P Y88b. 888 888 Y88b. Y8b. 888 888 888 Y8b. 888 888 Y88b. 888 888 888 888 888 d88P 888
1142 // "Y8888P" "Y888 "Y888888 "Y888 "Y8888 888 888 888 "Y8888 888 888 "Y888 8888888 888 888 888 88888P" 888
1143 // 888
1144 // 888
1145 // 888
1146 // MARK: Statement Impl -
1147 
1148 namespace nanodbc
1149 {
1150 
1151 class statement::statement_impl
1152 {
1153 public:
1154  statement_impl(const statement_impl&) =delete;
1155  statement_impl& operator=(const statement_impl&) =delete;
1156 
1157  statement_impl()
1158  : stmt_(0)
1159  , open_(false)
1160  , conn_()
1161  , bind_len_or_null_()
1162  {
1163 
1164  }
1165 
1166  statement_impl(class connection& conn)
1167  : stmt_(0)
1168  , open_(false)
1169  , conn_()
1170  , bind_len_or_null_()
1171  {
1172  open(conn);
1173  }
1174 
1175  statement_impl(class connection& conn, const string_type& query, long timeout)
1176  : stmt_(0)
1177  , open_(false)
1178  , conn_()
1179  , bind_len_or_null_()
1180  {
1181  prepare(conn, query, timeout);
1182  }
1183 
1184  ~statement_impl() NANODBC_NOEXCEPT
1185  {
1186  if(open() && connected())
1187  {
1188  NANODBC_CALL(
1189  SQLCancel
1190  , stmt_);
1191  reset_parameters();
1192  NANODBC_CALL(
1193  SQLFreeHandle
1194  , SQL_HANDLE_STMT
1195  , stmt_);
1196  }
1197  }
1198 
1199  void open(class connection& conn)
1200  {
1201  close();
1202  RETCODE rc;
1203  NANODBC_CALL_RC(
1204  SQLAllocHandle
1205  , rc
1206  , SQL_HANDLE_STMT
1207  , conn.native_dbc_handle()
1208  , &stmt_);
1209  open_ = success(rc);
1210  if(!open_)
1211  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1212  conn_ = conn;
1213  }
1214 
1215  bool open() const
1216  {
1217  return open_;
1218  }
1219 
1220  bool connected() const
1221  {
1222  return conn_.connected();
1223  }
1224 
1225  const class connection& connection() const
1226  {
1227  return conn_;
1228  }
1229 
1230  class connection& connection()
1231  {
1232  return conn_;
1233  }
1234 
1235  void* native_statement_handle() const
1236  {
1237  return stmt_;
1238  }
1239 
1240  void close()
1241  {
1242  if(open() && connected())
1243  {
1244  RETCODE rc;
1245  NANODBC_CALL_RC(
1246  SQLCancel
1247  , rc
1248  , stmt_);
1249  if(!success(rc))
1250  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1251 
1252  reset_parameters();
1253 
1254  NANODBC_CALL_RC(
1255  SQLFreeHandle
1256  , rc
1257  , SQL_HANDLE_STMT
1258  , stmt_);
1259  if(!success(rc))
1260  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1261  }
1262 
1263  open_ = false;
1264  stmt_ = 0;
1265  }
1266 
1267  void cancel()
1268  {
1269  RETCODE rc;
1270  NANODBC_CALL_RC(
1271  SQLCancel
1272  , rc
1273  , stmt_);
1274  if(!success(rc))
1275  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1276  }
1277 
1278  void prepare(class connection& conn, const string_type& query, long timeout)
1279  {
1280  open(conn);
1281  prepare(query, timeout);
1282  }
1283 
1284  void prepare(const string_type& query, long timeout)
1285  {
1286  if(!open())
1287  throw programming_error("statement has no associated open connection");
1288 
1289  RETCODE rc;
1290  NANODBC_CALL_RC(
1291  NANODBC_FUNC(SQLPrepare)
1292  , rc
1293  , stmt_
1294  , (NANODBC_SQLCHAR*)query.c_str()
1295  , (SQLINTEGER)query.size());
1296  if(!success(rc))
1297  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1298 
1299  this->timeout(timeout);
1300  }
1301 
1302  void timeout(long timeout)
1303  {
1304  RETCODE rc;
1305  NANODBC_CALL_RC(
1306  SQLSetStmtAttr
1307  , rc
1308  , stmt_
1309  , SQL_ATTR_QUERY_TIMEOUT
1310  , (SQLPOINTER)(std::intptr_t)timeout,
1311  0);
1312 
1313  // some drivers don't support timeout for statements,
1314  // so only raise the error if a non-default timeout was requested.
1315  if(!success(rc) && (timeout != 0))
1316  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1317  }
1318 
1319 #if defined(SQL_ATTR_ASYNC_STMT_EVENT) && defined(SQL_API_SQLCOMPLETEASYNC)
1320  void enable_async(void* event_handle)
1321  {
1322  RETCODE rc;
1323  NANODBC_CALL_RC(
1324  SQLSetStmtAttr
1325  , rc
1326  , stmt_
1327  , SQL_ATTR_ASYNC_ENABLE
1328  , (SQLPOINTER)SQL_ASYNC_ENABLE_ON
1329  , SQL_IS_INTEGER);
1330  if(!success(rc))
1331  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1332 
1333  NANODBC_CALL_RC(
1334  SQLSetStmtAttr
1335  , rc
1336  , stmt_
1337  , SQL_ATTR_ASYNC_STMT_EVENT
1338  , event_handle
1339  , SQL_IS_POINTER);
1340  if(!success(rc))
1341  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1342  }
1343 
1344  void async_execute_direct(
1345  class connection& conn
1346  , void* event_handle
1347  , const string_type& query
1348  , long batch_operations
1349  , long timeout
1350  , statement& statement)
1351  {
1352  RETCODE rc = just_execute_direct(
1353  conn
1354  , query
1355  , batch_operations
1356  , timeout
1357  , statement
1358  , event_handle);
1359 
1360  if(rc != SQL_STILL_EXECUTING)
1361  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1362  }
1363 
1364  result async_complete(long batch_operations, statement& statement)
1365  {
1366  RETCODE rc, arc;
1367  NANODBC_CALL_RC(
1368  SQLCompleteAsync
1369  , rc
1370  , SQL_HANDLE_STMT
1371  , stmt_
1372  , &arc);
1373  if(!success(rc) || !success(arc))
1374  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1375 
1376  NANODBC_CALL_RC(
1377  SQLSetStmtAttr
1378  , rc
1379  , stmt_
1380  , SQL_ATTR_ASYNC_ENABLE
1381  , (SQLPOINTER)SQL_ASYNC_ENABLE_OFF
1382  , SQL_IS_INTEGER);
1383  if(!success(rc))
1384  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1385 
1386  return result(statement, batch_operations);
1387  }
1388 #endif // SQL_ATTR_ASYNC_STMT_EVENT && SQL_API_SQLCOMPLETEASYNC
1389 
1390  result execute_direct(
1391  class connection& conn
1392  , const string_type& query
1393  , long batch_operations
1394  , long timeout
1395  , statement& statement)
1396  {
1397  #ifdef NANODBC_HANDLE_NODATA_BUG
1398  const RETCODE rc = just_execute_direct(conn, query, batch_operations, timeout, statement);
1399  if(rc == SQL_NO_DATA)
1400  return result();
1401  #else
1402  just_execute_direct(conn, query, batch_operations, timeout, statement);
1403  #endif
1404  return result(statement, batch_operations);
1405  }
1406 
1407  RETCODE just_execute_direct(
1408  class connection& conn
1409  , const string_type& query
1410  , long batch_operations
1411  , long timeout
1412  , statement& /*statement*/
1413  , void* event_handle = NULL)
1414  {
1415  open(conn);
1416 
1417  #if defined(SQL_ATTR_ASYNC_STMT_EVENT) && defined(SQL_API_SQLCOMPLETEASYNC)
1418  if(event_handle != NULL)
1419  enable_async(event_handle);
1420  #endif
1421 
1422  RETCODE rc;
1423  NANODBC_CALL_RC(
1424  SQLSetStmtAttr
1425  , rc
1426  , stmt_
1427  , SQL_ATTR_PARAMSET_SIZE
1428  , (SQLPOINTER)(std::intptr_t)batch_operations
1429  , 0);
1430  if(!success(rc))
1431  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1432 
1433  this->timeout(timeout);
1434 
1435  NANODBC_CALL_RC(
1436  NANODBC_FUNC(SQLExecDirect)
1437  , rc
1438  , stmt_
1439  , (NANODBC_SQLCHAR*)query.c_str()
1440  , SQL_NTS);
1441  if(!success(rc) && rc != SQL_NO_DATA && rc != SQL_STILL_EXECUTING)
1442  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1443 
1444  return rc;
1445  }
1446 
1447  result execute(long batch_operations, long timeout, statement& statement)
1448  {
1449  #ifdef NANODBC_HANDLE_NODATA_BUG
1450  const RETCODE rc = just_execute(batch_operations, timeout, statement);
1451  if(rc == SQL_NO_DATA)
1452  return result();
1453  #else
1454  just_execute(batch_operations, timeout, statement);
1455  #endif
1456  return result(statement, batch_operations);
1457  }
1458 
1459  RETCODE just_execute(long batch_operations, long timeout, statement& /*statement*/)
1460  {
1461  RETCODE rc;
1462 
1463  if(open())
1464  {
1465  // The ODBC cursor must be closed before subsequent executions, as described
1466  // here http://msdn.microsoft.com/en-us/library/windows/desktop/ms713584%28v=vs.85%29.aspx
1467  //
1468  // However, we don't necessarily want to call SQLCloseCursor() because that
1469  // will cause an invalid cursor state in the case that no cursor is currently open.
1470  // A better solution is to use SQLFreeStmt() with the SQL_CLOSE option, which has
1471  // the same effect without the undesired limitations.
1472  NANODBC_CALL_RC(
1473  SQLFreeStmt
1474  , rc
1475  , stmt_
1476  , SQL_CLOSE);
1477  if(!success(rc))
1478  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1479  }
1480 
1481  NANODBC_CALL_RC(
1482  SQLSetStmtAttr
1483  , rc
1484  , stmt_
1485  , SQL_ATTR_PARAMSET_SIZE
1486  , (SQLPOINTER)(std::intptr_t)batch_operations
1487  , 0);
1488  if(!success(rc) && rc != SQL_NO_DATA)
1489  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1490 
1491  this->timeout(timeout);
1492 
1493  NANODBC_CALL_RC(
1494  SQLExecute
1495  , rc
1496  , stmt_);
1497  if(!success(rc) && rc != SQL_NO_DATA)
1498  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1499 
1500  return rc;
1501  }
1502 
1503  result procedure_columns(
1504  const string_type& catalog
1505  , const string_type& schema
1506  , const string_type& procedure
1507  , const string_type& column
1508  , statement& statement)
1509  {
1510  if(!open())
1511  throw programming_error("statement has no associated open connection");
1512 
1513  RETCODE rc;
1514  NANODBC_CALL_RC(
1515  NANODBC_FUNC(SQLProcedureColumns)
1516  , rc
1517  , stmt_
1518  , (NANODBC_SQLCHAR*)(catalog.empty() ? NULL : catalog.c_str())
1519  , (catalog.empty() ? 0 : SQL_NTS)
1520  , (NANODBC_SQLCHAR*)(schema.empty() ? NULL : schema.c_str())
1521  , (schema.empty() ? 0 : SQL_NTS)
1522  , (NANODBC_SQLCHAR*)procedure.c_str()
1523  , SQL_NTS
1524  , (NANODBC_SQLCHAR*)(column.empty() ? NULL : column.c_str())
1525  , (column.empty() ? 0 : SQL_NTS));
1526 
1527  if(!success(rc))
1528  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1529 
1530  return result(statement, 1);
1531  }
1532 
1533  long affected_rows() const
1534  {
1535  SQLLEN rows;
1536  RETCODE rc;
1537  NANODBC_CALL_RC(
1538  SQLRowCount
1539  , rc
1540  , stmt_
1541  , &rows);
1542  if(!success(rc))
1543  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1544  NANODBC_ASSERT(rows <= static_cast<SQLLEN>(std::numeric_limits<long>::max()));
1545  return static_cast<long>(rows);
1546  }
1547 
1548  short columns() const
1549  {
1550  SQLSMALLINT cols;
1551  RETCODE rc;
1552  NANODBC_CALL_RC(
1553  SQLNumResultCols
1554  , rc
1555  , stmt_
1556  , &cols);
1557  if(!success(rc))
1558  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1559  return cols;
1560  }
1561 
1562  void reset_parameters() NANODBC_NOEXCEPT
1563  {
1564  NANODBC_CALL(
1565  SQLFreeStmt
1566  , stmt_
1567  , SQL_RESET_PARAMS);
1568  }
1569 
1570  unsigned long parameter_size(short param) const
1571  {
1572  RETCODE rc;
1573  SQLSMALLINT data_type;
1574  SQLSMALLINT nullable;
1575  SQLULEN parameter_size;
1576  NANODBC_CALL_RC(
1577  SQLDescribeParam
1578  , rc
1579  , stmt_
1580  , param + 1
1581  , &data_type
1582  , &parameter_size
1583  , 0
1584  , &nullable);
1585  if(!success(rc))
1586  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1587  NANODBC_ASSERT(parameter_size <= static_cast<SQLULEN>(std::numeric_limits<unsigned long>::max()));
1588  return static_cast<unsigned long>(parameter_size);
1589  }
1590 
1591  static SQLSMALLINT param_type_from_direction(param_direction direction)
1592  {
1593  switch(direction)
1594  {
1595  case PARAM_IN:
1596  return SQL_PARAM_INPUT;
1597  break;
1598  case PARAM_OUT:
1599  return SQL_PARAM_OUTPUT;
1600  break;
1601  case PARAM_INOUT:
1602  return SQL_PARAM_INPUT_OUTPUT;
1603  break;
1604  case PARAM_RETURN:
1605  return SQL_PARAM_OUTPUT;
1606  break;
1607  default:
1608  NANODBC_ASSERT(false);
1609  throw programming_error("unrecognized param_direction value");
1610  }
1611  }
1612 
1613  // initializes bind_len_or_null_ and gets information for bind
1614  void prepare_bind(
1615  short param
1616  , std::size_t elements
1617  , param_direction direction
1618  , SQLSMALLINT& data_type
1619  , SQLSMALLINT& param_type
1620  , SQLULEN& parameter_size
1621  , SQLSMALLINT& scale)
1622  {
1623  RETCODE rc;
1624  SQLSMALLINT nullable;
1625  NANODBC_CALL_RC(
1626  SQLDescribeParam
1627  , rc
1628  , stmt_
1629  , param + 1
1630  , &data_type
1631  , &parameter_size
1632  , &scale
1633  , &nullable);
1634  if(!success(rc))
1635  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1636 
1637  param_type = param_type_from_direction(direction);
1638 
1639  if(!bind_len_or_null_.count(param))
1640  bind_len_or_null_[param] = std::vector<null_type>();
1641  std::vector<null_type>().swap(bind_len_or_null_[param]);
1642 
1643  // ODBC weirdness: this must be at least 8 elements in size
1644  const std::size_t indicator_size = elements > 8 ? elements : 8;
1645 
1646  bind_len_or_null_[param].reserve(indicator_size);
1647  bind_len_or_null_[param].assign(indicator_size, SQL_NULL_DATA);
1648  }
1649 
1650  // calls actual ODBC bind parameter function
1651  template<class T>
1652  void bind_parameter(
1653  short param
1654  , const T* data
1655  , std::size_t /*elements*/
1656  , SQLSMALLINT data_type
1657  , SQLSMALLINT param_type
1658  , SQLULEN parameter_size
1659  , SQLSMALLINT scale)
1660  {
1661  RETCODE rc;
1662  NANODBC_CALL_RC(
1663  SQLBindParameter
1664  , rc
1665  , stmt_ // handle
1666  , param + 1 // parameter number
1667  , param_type // input or output type
1668  , sql_ctype<T>::value // value type
1669  , data_type // parameter type
1670  , parameter_size // column size ignored for many types, but needed for strings
1671  , scale // decimal digits
1672  , (SQLPOINTER)data // parameter value
1673  , parameter_size // buffer length
1674  , bind_len_or_null_[param].data());
1675 
1676  if(!success(rc))
1677  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1678  }
1679 
1680  // handles a single value (possibly a single string value), or multiple non-string values
1681  template<class T>
1682  void bind(short param, const T* values, std::size_t elements, param_direction direction);
1683 
1684  // handles multiple string values
1685  void bind_strings(
1686  short param
1687  , const string_type::value_type* values
1688  , std::size_t /*length*/
1689  , std::size_t elements
1690  , param_direction direction)
1691  {
1692  bind(param, values, elements, direction);
1693  }
1694 
1695  // handles multiple null values
1696  void bind_null(short param, std::size_t elements)
1697  {
1698  SQLSMALLINT data_type;
1699  SQLSMALLINT param_type;
1700  SQLULEN parameter_size;
1701  SQLSMALLINT scale;
1702  prepare_bind(param, elements, PARAM_IN, data_type, param_type, parameter_size, scale);
1703 
1704  RETCODE rc;
1705  NANODBC_CALL_RC(
1706  SQLBindParameter
1707  , rc
1708  , stmt_
1709  , param + 1
1710  , param_type
1711  , SQL_C_CHAR
1712  , data_type
1713  , parameter_size // column size ignored for many types, but needed for strings
1714  , 0
1715  , (SQLPOINTER)0 // null value
1716  , 0 // parameter_size
1717  , bind_len_or_null_[param].data());
1718  if(!success(rc))
1719  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1720  }
1721 
1722  // comparator for null sentry values
1723  template<class T>
1724  bool equals(const T& lhs, const T& rhs)
1725  {
1726  return lhs == rhs;
1727  }
1728 
1729  // handles multiple non-string values with a null sentry
1730  template<class T>
1731  void bind(
1732  short param
1733  , const T* values
1734  , std::size_t elements
1735  , const bool* nulls
1736  , const T* null_sentry
1737  , param_direction direction);
1738 
1739  // handles multiple string values
1740  void bind_strings(
1741  short param
1742  , const string_type::value_type* values
1743  , std::size_t length
1744  , std::size_t elements
1745  , const bool* nulls
1746  , const string_type::value_type* null_sentry
1747  , param_direction direction);
1748 
1749 private:
1750  HSTMT stmt_;
1751  bool open_;
1752  class connection conn_;
1753  std::map<short, std::vector<null_type> > bind_len_or_null_;
1754 };
1755 
1756 // Supports code like: query.bind(0, std_string.c_str())
1757 // In this case, we need to pass NULL to the final parameter of SQLBindParameter().
1758 template<>
1759 void statement::statement_impl::bind_parameter<string_type::value_type>(
1760  short param
1761  , const string_type::value_type* data
1762  , std::size_t elements
1763  , SQLSMALLINT data_type
1764  , SQLSMALLINT param_type
1765  , SQLULEN parameter_size
1766  , SQLSMALLINT scale)
1767 {
1768  RETCODE rc;
1769  NANODBC_CALL_RC(
1770  SQLBindParameter
1771  , rc
1772  , stmt_ // handle
1773  , param + 1 // parameter number
1774  , param_type // input or output type
1775  , sql_ctype<string_type::value_type>::value // value type
1776  , data_type // parameter type
1777  , parameter_size // column size ignored for many types, but needed for strings
1778  , scale // decimal digits
1779  , (SQLPOINTER)data // parameter value
1780  , parameter_size // buffer length
1781  , (elements <= 1 ? NULL : bind_len_or_null_[param].data()));
1782 
1783  if(!success(rc))
1784  NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
1785 }
1786 
1787 template<class T>
1788 void statement::statement_impl::bind(
1789  short param
1790  , const T* values
1791  , std::size_t elements
1792  , param_direction direction)
1793 {
1794  SQLSMALLINT data_type;
1795  SQLSMALLINT param_type;
1796  SQLULEN parameter_size;
1797  SQLSMALLINT scale;
1798  prepare_bind(param, elements, direction, data_type, param_type, parameter_size, scale);
1799 
1800  for(std::size_t i = 0; i < elements; ++i)
1801  bind_len_or_null_[param][i] = parameter_size;
1802 
1803  bind_parameter(param, values, elements, data_type, param_type, parameter_size, scale);
1804 }
1805 
1806 template<class T>
1807 void statement::statement_impl::bind(
1808  short param
1809  , const T* values
1810  , std::size_t elements
1811  , const bool* nulls
1812  , const T* null_sentry
1813  , param_direction direction)
1814 {
1815  SQLSMALLINT data_type;
1816  SQLSMALLINT param_type;
1817  SQLULEN parameter_size;
1818  SQLSMALLINT scale;
1819  prepare_bind(param, elements, direction, data_type, param_type, parameter_size, scale);
1820 
1821  for(std::size_t i = 0; i < elements; ++i)
1822  if((null_sentry && !equals(values[i], *null_sentry)) || (nulls && !nulls[i]))
1823  bind_len_or_null_[param][i] = parameter_size;
1824 
1825  bind_parameter(param, values, elements, data_type, param_type, parameter_size, scale);
1826 }
1827 
1828 void statement::statement_impl::bind_strings(
1829  short param
1830  , const string_type::value_type* values
1831  , std::size_t length
1832  , std::size_t elements
1833  , const bool* nulls
1834  , const string_type::value_type* null_sentry
1835  , param_direction direction)
1836 {
1837  SQLSMALLINT data_type;
1838  SQLSMALLINT param_type;
1839  SQLULEN parameter_size;
1840  SQLSMALLINT scale;
1841  prepare_bind(param, elements, direction, data_type, param_type, parameter_size, scale);
1842 
1843  if(null_sentry)
1844  {
1845  for(std::size_t i = 0; i < elements; ++i)
1846  {
1847  const string_type s_lhs(values + i * length, values + (i + 1) * length);
1848  const string_type s_rhs(null_sentry);
1849  #if NANODBC_USE_UNICODE
1850  std::string narrow_lhs;
1851  narrow_lhs.reserve(s_lhs.size());
1852  convert(s_lhs, narrow_lhs);
1853  std::string narrow_rhs;
1854  narrow_rhs.reserve(s_rhs.size());
1855  convert(s_rhs, narrow_lhs);
1856  if(std::strncmp(narrow_lhs.c_str(), narrow_rhs.c_str(), length))
1857  bind_len_or_null_[param][i] = parameter_size;
1858  #else
1859  if(std::strncmp(s_lhs.c_str(), s_rhs.c_str(), length))
1860  bind_len_or_null_[param][i] = parameter_size;
1861  #endif
1862  }
1863  }
1864  else if(nulls)
1865  {
1866  for(std::size_t i = 0; i < elements; ++i)
1867  {
1868  if(!nulls[i])
1869  bind_len_or_null_[param][i] = SQL_NTS; // null terminated
1870  }
1871  }
1872 
1873  bind_parameter(param, values, elements, data_type, param_type, parameter_size, scale);
1874 }
1875 
1876 template<>
1877 bool statement::statement_impl::equals(const date& lhs, const date& rhs)
1878 {
1879  return lhs.year == rhs.year
1880  && lhs.month == rhs.month
1881  && lhs.day == rhs.day;
1882 }
1883 
1884 template<>
1885 bool statement::statement_impl::equals(const timestamp& lhs, const timestamp& rhs)
1886 {
1887  return lhs.year == rhs.year
1888  && lhs.month == rhs.month
1889  && lhs.day == rhs.day
1890  && lhs.hour == rhs.hour
1891  && lhs.min == rhs.min
1892  && lhs.sec == rhs.sec
1893  && lhs.fract == rhs.fract;
1894 }
1895 
1896 } // namespace nanodbc
1897 
1898 // 8888888b. 888 888 8888888 888
1899 // 888 Y88b 888 888 888 888
1900 // 888 888 888 888 888 888
1901 // 888 d88P .d88b. .d8888b 888 888 888 888888 888 88888b.d88b. 88888b. 888
1902 // 8888888P" d8P Y8b 88K 888 888 888 888 888 888 "888 "88b 888 "88b 888
1903 // 888 T88b 88888888 "Y8888b. 888 888 888 888 888 888 888 888 888 888 888
1904 // 888 T88b Y8b. X88 Y88b 888 888 Y88b. 888 888 888 888 888 d88P 888
1905 // 888 T88b "Y8888 88888P' "Y88888 888 "Y888 8888888 888 888 888 88888P" 888
1906 // 888
1907 // 888
1908 // 888
1909 // MARK: Result Impl -
1910 
1911 namespace nanodbc
1912 {
1913 
1914 class result::result_impl
1915 {
1916 public:
1917  result_impl(const result_impl&) =delete;
1918  result_impl& operator=(const result_impl&) =delete;
1919 
1920  result_impl(statement stmt, long rowset_size)
1921  : stmt_(stmt)
1922  , rowset_size_(rowset_size)
1923  , row_count_(0)
1924  , bound_columns_(0)
1925  , bound_columns_size_(0)
1926  , rowset_position_(0)
1927  , bound_columns_by_name_()
1928  , at_end_(false)
1929  {
1930  RETCODE rc;
1931  NANODBC_CALL_RC(
1932  SQLSetStmtAttr
1933  , rc
1934  , stmt_.native_statement_handle()
1935  , SQL_ATTR_ROW_ARRAY_SIZE
1936  , (SQLPOINTER)(std::intptr_t)rowset_size_
1937  , 0);
1938  if(!success(rc))
1939  NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
1940 
1941  NANODBC_CALL_RC(
1942  SQLSetStmtAttr
1943  , rc
1944  , stmt_.native_statement_handle()
1945  , SQL_ATTR_ROWS_FETCHED_PTR
1946  , &row_count_
1947  , 0);
1948  if(!success(rc))
1949  NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
1950 
1951  auto_bind();
1952  }
1953 
1954  ~result_impl() NANODBC_NOEXCEPT
1955  {
1956  cleanup_bound_columns();
1957  }
1958 
1959  void* native_statement_handle() const
1960  {
1961  return stmt_.native_statement_handle();
1962  }
1963 
1964  long rowset_size() const
1965  {
1966  return rowset_size_;
1967  }
1968 
1969  long affected_rows() const
1970  {
1971  return stmt_.affected_rows();
1972  }
1973 
1974  long rows() const NANODBC_NOEXCEPT
1975  {
1976  NANODBC_ASSERT(row_count_ <= static_cast<SQLULEN>(std::numeric_limits<long>::max()));
1977  return static_cast<long>(row_count_);
1978  }
1979 
1980  short columns() const
1981  {
1982  return stmt_.columns();
1983  }
1984 
1985  bool first()
1986  {
1987  rowset_position_ = 0;
1988  return fetch(0, SQL_FETCH_FIRST);
1989  }
1990 
1991  bool last()
1992  {
1993  rowset_position_ = 0;
1994  return fetch(0, SQL_FETCH_LAST);
1995  }
1996 
1997  bool next()
1998  {
1999  if(rows() && ++rowset_position_ < rowset_size_)
2000  return rowset_position_ < rows();
2001  rowset_position_ = 0;
2002  return fetch(0, SQL_FETCH_NEXT);
2003  }
2004 
2005  bool prior()
2006  {
2007  if(rows() && --rowset_position_ >= 0)
2008  return true;
2009  rowset_position_ = 0;
2010  return fetch(0, SQL_FETCH_PRIOR);
2011  }
2012 
2013  bool move(long row)
2014  {
2015  rowset_position_ = 0;
2016  return fetch(row, SQL_FETCH_ABSOLUTE);
2017  }
2018 
2019  bool skip(long rows)
2020  {
2021  rowset_position_ += rows;
2022  if(this->rows() && rowset_position_ < rowset_size_)
2023  return rowset_position_ < this->rows();
2024  rowset_position_ = 0;
2025  return fetch(rows, SQL_FETCH_RELATIVE);
2026  }
2027 
2028  unsigned long position() const
2029  {
2030  SQLULEN pos = 0; // necessary to initialize to 0
2031  RETCODE rc;
2032  NANODBC_CALL_RC(
2033  SQLGetStmtAttr
2034  , rc
2035  , stmt_.native_statement_handle()
2036  , SQL_ATTR_ROW_NUMBER
2037  , &pos
2038  , SQL_IS_UINTEGER
2039  , 0);
2040  if(!success(rc))
2041  NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
2042 
2043 
2044  // MSDN (https://msdn.microsoft.com/en-us/library/ms712631.aspx):
2045  // If the number of the current row cannot be determined or
2046  // there is no current row, the driver returns 0.
2047  // Otherwise, valid row number is returned, starting at 1.
2048  //
2049  // NOTE: We try to address incorrect implementation in some drivers (e.g. SQLite ODBC)
2050  // which instead of 0 return SQL_ROW_NUMBER_UNKNOWN(-2) .
2051  if (pos == 0 || pos == static_cast<SQLULEN>(SQL_ROW_NUMBER_UNKNOWN))
2052  return 0;
2053 
2054  NANODBC_ASSERT(pos <= static_cast<SQLULEN>(std::numeric_limits<unsigned long>::max()));
2055  return static_cast<unsigned long>(pos) + rowset_position_;
2056  }
2057 
2058  bool end() const NANODBC_NOEXCEPT
2059  {
2060  if(at_end_)
2061  return true;
2062  SQLULEN pos = 0; // necessary to initialize to 0
2063  RETCODE rc;
2064  NANODBC_CALL_RC(
2065  SQLGetStmtAttr
2066  , rc
2067  , stmt_.native_statement_handle()
2068  , SQL_ATTR_ROW_NUMBER
2069  , &pos
2070  , SQL_IS_UINTEGER
2071  , 0);
2072  return (!success(rc) || rows() < 0 || pos - 1 > static_cast<unsigned long>(rows()));
2073  }
2074 
2075  bool is_null(short column) const
2076  {
2077  if(column >= bound_columns_size_)
2078  throw index_range_error();
2079  bound_column& col = bound_columns_[column];
2080  if(rowset_position_ >= rows())
2081  throw index_range_error();
2082  return col.cbdata_[rowset_position_] == SQL_NULL_DATA;
2083  }
2084 
2085  bool is_null(const string_type& column_name) const
2086  {
2087  const short column = this->column(column_name);
2088  return is_null(column);
2089  }
2090 
2091  string_type column_name(short column) const
2092  {
2093  if(column >= bound_columns_size_)
2094  throw index_range_error();
2095  return bound_columns_[column].name_;
2096  }
2097 
2098  long column_size(short column) const
2099  {
2100  if(column >= bound_columns_size_)
2101  throw index_range_error();
2102  bound_column& col = bound_columns_[column];
2103  NANODBC_ASSERT(col.sqlsize_ <= static_cast<SQLULEN>(std::numeric_limits<long>::max()));
2104  return static_cast<long>(col.sqlsize_);
2105  }
2106 
2107  short column(const string_type& column_name) const
2108  {
2109  typedef std::map<string_type, bound_column*>::const_iterator iter;
2110  iter i = bound_columns_by_name_.find(column_name);
2111  if(i == bound_columns_by_name_.end())
2112  throw index_range_error();
2113  return i->second->column_;
2114  }
2115 
2116  int column_datatype(short column) const
2117  {
2118  if(column >= bound_columns_size_)
2119  throw index_range_error();
2120  bound_column& col = bound_columns_[column];
2121  return col.sqltype_;
2122  }
2123 
2124  int column_datatype(const string_type& column_name) const
2125  {
2126  const short column = this->column(column_name);
2127  bound_column& col = bound_columns_[column];
2128  return col.sqltype_;
2129  }
2130 
2131  int column_c_datatype(short column) const
2132  {
2133  if(column >= bound_columns_size_)
2134  throw index_range_error();
2135  bound_column& col = bound_columns_[column];
2136  return col.ctype_;
2137  }
2138 
2139  int column_c_datatype(const string_type& column_name) const
2140  {
2141  const short column = this->column(column_name);
2142  bound_column& col = bound_columns_[column];
2143  return col.ctype_;
2144  }
2145 
2146  bool next_result()
2147  {
2148  RETCODE rc;
2149  NANODBC_CALL_RC(
2150  SQLMoreResults
2151  , rc
2152  , stmt_.native_statement_handle());
2153  if(rc == SQL_NO_DATA)
2154  return false;
2155  if(!success(rc))
2156  NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
2157  auto_bind();
2158  return true;
2159  }
2160 
2161  template<class T>
2162  void get_ref(short column, T& result) const
2163  {
2164  if(column >= bound_columns_size_)
2165  throw index_range_error();
2166  if(is_null(column))
2167  throw null_access_error();
2168  get_ref_impl<T>(column, result);
2169  }
2170 
2171  template<class T>
2172  void get_ref(short column, const T& fallback, T& result) const
2173  {
2174  if(column >= bound_columns_size_)
2175  throw index_range_error();
2176  if(is_null(column))
2177  {
2178  result = fallback;
2179  return;
2180  }
2181  get_ref_impl<T>(column, result);
2182  }
2183 
2184  template<class T>
2185  void get_ref(const string_type& column_name, T& result) const
2186  {
2187  const short column = this->column(column_name);
2188  if(is_null(column))
2189  throw null_access_error();
2190  get_ref_impl<T>(column, result);
2191  }
2192 
2193  template<class T>
2194  void get_ref(const string_type& column_name, const T& fallback, T& result) const
2195  {
2196  const short column = this->column(column_name);
2197  if(is_null(column))
2198  {
2199  result = fallback;
2200  return;
2201  }
2202  get_ref_impl<T>(column, result);
2203  }
2204 
2205  template<class T>
2206  T get(short column) const
2207  {
2208  T result;
2209  get_ref(column, result);
2210  return result;
2211  }
2212 
2213  template<class T>
2214  T get(short column, const T& fallback) const
2215  {
2216  T result;
2217  get_ref(column, fallback, result);
2218  return result;
2219  }
2220 
2221  template<class T>
2222  T get(const string_type& column_name) const
2223  {
2224  T result;
2225  get_ref(column_name, result);
2226  return result;
2227  }
2228 
2229  template<class T>
2230  T get(const string_type& column_name, const T& fallback) const
2231  {
2232  T result;
2233  get_ref(column_name, fallback, result);
2234  return result;
2235  }
2236 
2237 private:
2238  template<class T>
2239  void get_ref_impl(short column, T& result) const;
2240 
2241  void before_move() NANODBC_NOEXCEPT
2242  {
2243  for(short i = 0; i < bound_columns_size_; ++i)
2244  {
2245  bound_column& col = bound_columns_[i];
2246  for(long j = 0; j < rowset_size_; ++j)
2247  col.cbdata_[j] = 0;
2248  if(col.blob_ && col.pdata_)
2249  release_bound_resources(i);
2250  }
2251  }
2252 
2253  void release_bound_resources(short column) NANODBC_NOEXCEPT
2254  {
2255  NANODBC_ASSERT(column < bound_columns_size_);
2256  bound_column& col = bound_columns_[column];
2257  delete[] col.pdata_;
2258  col.pdata_ = 0;
2259  col.clen_ = 0;
2260  }
2261 
2262  void cleanup_bound_columns() NANODBC_NOEXCEPT
2263  {
2264  before_move();
2265  delete[] bound_columns_;
2266  bound_columns_ = NULL;
2267  bound_columns_size_ = 0;
2268  bound_columns_by_name_.clear();
2269  }
2270 
2271  bool fetch(long rows, SQLUSMALLINT orientation)
2272  {
2273  before_move();
2274  RETCODE rc;
2275  NANODBC_CALL_RC(
2276  SQLFetchScroll
2277  , rc
2278  , stmt_.native_statement_handle()
2279  , orientation
2280  , rows);
2281  if(rc == SQL_NO_DATA)
2282  {
2283  at_end_ = true;
2284  return false;
2285  }
2286  if(!success(rc))
2287  NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
2288  return true;
2289  }
2290 
2291  void auto_bind()
2292  {
2293  cleanup_bound_columns();
2294 
2295  const short n_columns = columns();
2296  if(n_columns < 1)
2297  return;
2298 
2299  NANODBC_ASSERT(!bound_columns_);
2300  NANODBC_ASSERT(!bound_columns_size_);
2301  bound_columns_ = new bound_column[n_columns];
2302  bound_columns_size_ = n_columns;
2303 
2304  RETCODE rc;
2305  NANODBC_SQLCHAR column_name[1024];
2306  SQLSMALLINT sqltype, scale, nullable, len;
2307  SQLULEN sqlsize;
2308 
2309  for(SQLSMALLINT i = 0; i < n_columns; ++i)
2310  {
2311  NANODBC_CALL_RC(
2312  NANODBC_FUNC(SQLDescribeCol)
2313  , rc
2314  , stmt_.native_statement_handle()
2315  , i + 1
2316  , (NANODBC_SQLCHAR*)column_name
2317  , sizeof(column_name)/sizeof(NANODBC_SQLCHAR)
2318  , &len
2319  , &sqltype
2320  , &sqlsize
2321  , &scale
2322  , &nullable);
2323  if(!success(rc))
2324  NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
2325 
2326  // Adjust the sqlsize parameter in case of "unlimited" data (varchar(max), nvarchar(max)).
2327  bool is_blob = false;
2328 
2329  if(sqlsize == 0)
2330  {
2331  switch (sqltype)
2332  {
2333  case SQL_VARCHAR:
2334  case SQL_WVARCHAR:
2335  {
2336  // Divide in half, due to sqlsize being 32-bit in Win32 (and 64-bit in x64)
2337  //sqlsize = std::numeric_limits<int32_t>::max() / 2 - 1;
2338  is_blob = true;
2339  }
2340  }
2341  }
2342 
2343  bound_column& col = bound_columns_[i];
2344  col.name_ = reinterpret_cast<string_type::value_type*>(column_name);
2345  col.column_ = i;
2346  col.sqltype_ = sqltype;
2347  col.sqlsize_ = sqlsize;
2348  col.scale_ = scale;
2349  bound_columns_by_name_[col.name_] = &col;
2350 
2351  using namespace std; // if int64_t is in std namespace (in c++11)
2352  switch(col.sqltype_)
2353  {
2354  case SQL_BIT:
2355  case SQL_TINYINT:
2356  case SQL_SMALLINT:
2357  case SQL_INTEGER:
2358  col.ctype_ = SQL_C_LONG;
2359  col.clen_ = sizeof(int32_t);
2360  break;
2361  case SQL_BIGINT:
2362  col.ctype_ = SQL_C_SBIGINT;
2363  col.clen_ = sizeof(int64_t);
2364  break;
2365  case SQL_DOUBLE:
2366  case SQL_FLOAT:
2367  case SQL_DECIMAL:
2368  case SQL_REAL:
2369  case SQL_NUMERIC:
2370  col.ctype_ = SQL_C_DOUBLE;
2371  col.clen_ = sizeof(double);
2372  break;
2373  case SQL_DATE:
2374  case SQL_TYPE_DATE:
2375  col.ctype_ = SQL_C_DATE;
2376  col.clen_ = sizeof(date);
2377  break;
2378  case SQL_TIMESTAMP:
2379  case SQL_TYPE_TIMESTAMP:
2380  col.ctype_ = SQL_C_TIMESTAMP;
2381  col.clen_ = sizeof(timestamp);
2382  break;
2383  case SQL_CHAR:
2384  case SQL_VARCHAR:
2385  col.ctype_ = SQL_C_CHAR;
2386  col.clen_ = (col.sqlsize_ + 1) * sizeof(SQLCHAR);
2387  if(is_blob)
2388  {
2389  col.clen_ = 0;
2390  col.blob_ = true;
2391  }
2392  break;
2393  case SQL_WCHAR:
2394  case SQL_WVARCHAR:
2395  col.ctype_ = SQL_C_WCHAR;
2396  col.clen_ = (col.sqlsize_ + 1) * sizeof(SQLWCHAR);
2397  if(is_blob)
2398  {
2399  col.clen_ = 0;
2400  col.blob_ = true;
2401  }
2402  break;
2403  case SQL_LONGVARCHAR:
2404  col.ctype_ = SQL_C_CHAR;
2405  col.blob_ = true;
2406  col.clen_ = 0;
2407  break;
2408  case SQL_BINARY:
2409  case SQL_VARBINARY:
2410  case SQL_LONGVARBINARY:
2411  col.ctype_ = SQL_C_BINARY;
2412  col.blob_ = true;
2413  col.clen_ = 0;
2414  break;
2415  default:
2416  col.ctype_ = sql_ctype<string_type>::value;
2417  col.clen_ = 128;
2418  break;
2419  }
2420  }
2421 
2422  for(SQLSMALLINT i = 0; i < n_columns; ++i)
2423  {
2424  bound_column& col = bound_columns_[i];
2425  col.cbdata_ = new null_type[rowset_size_];
2426  if(col.blob_)
2427  {
2428  NANODBC_CALL_RC(
2429  SQLBindCol
2430  , rc
2431  , stmt_.native_statement_handle()
2432  , i + 1
2433  , col.ctype_
2434  , 0
2435  , 0
2436  , col.cbdata_);
2437  if(!success(rc))
2438  NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
2439  }
2440  else
2441  {
2442  col.pdata_ = new char[rowset_size_ * col.clen_];
2443  NANODBC_CALL_RC(
2444  SQLBindCol
2445  , rc
2446  , stmt_.native_statement_handle()
2447  , i + 1 // ColumnNumber
2448  , col.ctype_ // TargetType
2449  , col.pdata_ // TargetValuePtr
2450  , col.clen_ // BufferLength
2451  , col.cbdata_); // StrLen_or_Ind
2452  if(!success(rc))
2453  NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
2454  }
2455  }
2456  }
2457 
2458 private:
2459  statement stmt_;
2460  const long rowset_size_;
2461  SQLULEN row_count_;
2462  bound_column* bound_columns_;
2463  short bound_columns_size_;
2464  long rowset_position_;
2465  std::map<string_type, bound_column*> bound_columns_by_name_;
2466  bool at_end_;
2467 };
2468 
2469 template<>
2470 inline void result::result_impl::get_ref_impl<date>(short column, date& result) const
2471 {
2472  bound_column& col = bound_columns_[column];
2473  switch(col.ctype_)
2474  {
2475  case SQL_C_DATE:
2476  result = *reinterpret_cast<date*>(col.pdata_ + rowset_position_ * col.clen_);
2477  return;
2478  case SQL_C_TIMESTAMP:
2479  {
2480  timestamp stamp = *reinterpret_cast<timestamp*>(col.pdata_ + rowset_position_ * col.clen_ );
2481  date d = { stamp.year, stamp.month, stamp.day };
2482  result = d;
2483  return;
2484  }
2485  }
2486  throw type_incompatible_error();
2487 }
2488 
2489 template<>
2490 inline void result::result_impl::get_ref_impl<timestamp>(short column, timestamp& result) const
2491 {
2492  bound_column& col = bound_columns_[column];
2493  switch(col.ctype_)
2494  {
2495  case SQL_C_DATE:
2496  {
2497  date d = *reinterpret_cast<date*>(col.pdata_ + rowset_position_ * col.clen_);
2498  timestamp stamp = { d.year, d.month, d.day, 0, 0, 0, 0 };
2499  result = stamp;
2500  return;
2501  }
2502  case SQL_C_TIMESTAMP:
2503  result = *reinterpret_cast<timestamp*>(col.pdata_ + rowset_position_ * col.clen_);
2504  return;
2505  }
2506  throw type_incompatible_error();
2507 }
2508 
2509 template<>
2510 inline void result::result_impl::get_ref_impl<string_type>(short column, string_type& result) const
2511 {
2512  bound_column& col = bound_columns_[column];
2513  const SQLULEN column_size = col.sqlsize_;
2514 
2515  switch(col.ctype_)
2516  {
2517  case SQL_C_CHAR:
2518  case SQL_C_BINARY:
2519  {
2520  if(col.blob_)
2521  {
2522  // Input is always std::string, while output may be std::string or wide_string_type
2523  std::string out;
2524  SQLLEN ValueLenOrInd;
2525  SQLRETURN rc;
2526  void* handle = native_statement_handle();
2527  do
2528  {
2529  char buffer[1024] = {0};
2530  const std::size_t buffer_size = sizeof(buffer);
2531  NANODBC_CALL_RC(
2532  SQLGetData
2533  , rc
2534  , handle // StatementHandle
2535  , column + 1 // Col_or_Param_Num
2536  , col.ctype_ // TargetType
2537  , buffer // TargetValuePtr
2538  , buffer_size - 1 // BufferLength
2539  , &ValueLenOrInd); // StrLen_or_IndPtr
2540  if(ValueLenOrInd > 0)
2541  out.append(buffer);
2542  else if(ValueLenOrInd == SQL_NULL_DATA)
2543  *col.cbdata_ = (SQLINTEGER) SQL_NULL_DATA;
2544  // Sequence of successful calls is:
2545  // SQL_NO_DATA or SQL_SUCCESS_WITH_INFO followed by SQL_SUCCESS.
2546  } while(rc == SQL_SUCCESS_WITH_INFO);
2547  if (rc == SQL_SUCCESS || rc == SQL_NO_DATA)
2548  convert(out, result);
2549  }
2550  else
2551  {
2552  const char* s = col.pdata_ + rowset_position_ * col.clen_;
2553  const std::string::size_type str_size = std::strlen(s);
2554  result.assign(s, s + str_size);
2555  }
2556  return;
2557  }
2558 
2559  case SQL_C_WCHAR:
2560  {
2561  if(col.blob_)
2562  {
2563  // Input is always wide_string_type, output might be std::string or wide_string_type.
2564  // Use a string builder to build the output string.
2565  wide_string_type out;
2566  SQLLEN ValueLenOrInd;
2567  SQLRETURN rc;
2568  void* handle = native_statement_handle();
2569  do
2570  {
2571  wide_char_t buffer[512] = {0};
2572  const std::size_t buffer_size = sizeof(buffer);
2573  NANODBC_CALL_RC(
2574  SQLGetData
2575  , rc
2576  , handle // StatementHandle
2577  , column + 1 // Col_or_Param_Num
2578  , col.ctype_ // TargetType
2579  , buffer // TargetValuePtr
2580  , buffer_size - 1 // BufferLength
2581  , &ValueLenOrInd); // StrLen_or_IndPtr
2582  if(ValueLenOrInd > 0)
2583  out.append(buffer);
2584  else if(ValueLenOrInd == SQL_NULL_DATA)
2585  *col.cbdata_ = (SQLINTEGER) SQL_NULL_DATA;
2586  // Sequence of successful calls is:
2587  // SQL_NO_DATA or SQL_SUCCESS_WITH_INFO followed by SQL_SUCCESS.
2588  } while(rc == SQL_SUCCESS_WITH_INFO);
2589  if (rc == SQL_SUCCESS || rc == SQL_NO_DATA)
2590  convert(out, result);
2591  }
2592  else
2593  {
2594  // Type is unicode in the database, convert if necessary
2595  const SQLWCHAR* s = reinterpret_cast<SQLWCHAR*>(col.pdata_ + rowset_position_ * col.clen_);
2596  const string_type::size_type str_size = *col.cbdata_ / sizeof(SQLWCHAR);
2597  wide_string_type temp(s, s + str_size);
2598  convert(temp, result);
2599  }
2600  return;
2601  }
2602 
2603  case SQL_C_GUID:
2604  {
2605  const char* s = col.pdata_ + rowset_position_ * col.clen_;
2606  result.assign(s, s + column_size);
2607  return;
2608  }
2609 
2610  case SQL_C_LONG:
2611  {
2612  std::string buffer;
2613  buffer.reserve(column_size + 1); // ensure terminating null
2614  buffer.resize(buffer.capacity());
2615  using std::fill;
2616  fill(buffer.begin(), buffer.end(), '\0');
2617  const wide_char_t data = *reinterpret_cast<wide_char_t*>(col.pdata_ + rowset_position_ * col.clen_);
2618  const int bytes = std::snprintf(const_cast<char*>(buffer.data()), column_size, "%d", data);
2619  if(bytes == -1)
2620  throw type_incompatible_error();
2621  else if((SQLULEN)bytes < column_size)
2622  buffer.resize(bytes);
2623  buffer.resize(std::strlen(buffer.data())); // drop any trailing nulls
2624  result.reserve(buffer.size() * sizeof(string_type::value_type));
2625  convert(buffer, result);
2626  return;
2627  }
2628 
2629  case SQL_C_SBIGINT:
2630  {
2631  using namespace std; // in case intmax_t is in namespace std
2632  std::string buffer;
2633  buffer.reserve(column_size + 1); // ensure terminating null
2634  buffer.resize(buffer.capacity());
2635  using std::fill;
2636  fill(buffer.begin(), buffer.end(), '\0');
2637  const intmax_t data = (intmax_t)*reinterpret_cast<int64_t*>(col.pdata_ + rowset_position_ * col.clen_);
2638  const int bytes = std::snprintf(const_cast<char*>(buffer.data()), column_size, "%jd", data);
2639  if(bytes == -1)
2640  throw type_incompatible_error();
2641  else if((SQLULEN)bytes < column_size)
2642  buffer.resize(bytes);
2643  buffer.resize(std::strlen(buffer.data())); // drop any trailing nulls
2644  result.reserve(buffer.size() * sizeof(string_type::value_type));
2645  convert(buffer, result);
2646  return;
2647  }
2648 
2649  case SQL_C_FLOAT:
2650  {
2651  std::string buffer;
2652  buffer.reserve(column_size + 1); // ensure terminating null
2653  buffer.resize(buffer.capacity());
2654  using std::fill;
2655  fill(buffer.begin(), buffer.end(), '\0');
2656  const float data = *reinterpret_cast<float*>(col.pdata_ + rowset_position_ * col.clen_);
2657  const int bytes = std::snprintf(const_cast<char*>(buffer.data()), column_size, "%f", data);
2658  if(bytes == -1)
2659  throw type_incompatible_error();
2660  else if((SQLULEN)bytes < column_size)
2661  buffer.resize(bytes);
2662  buffer.resize(std::strlen(buffer.data())); // drop any trailing nulls
2663  result.reserve(buffer.size() * sizeof(string_type::value_type));
2664  convert(buffer, result);
2665  return;
2666  }
2667 
2668  case SQL_C_DOUBLE:
2669  {
2670  std::string buffer;
2671  const SQLULEN width = column_size + 2; // account for decimal mark and sign
2672  buffer.reserve(width + 1); // ensure terminating null
2673  buffer.resize(buffer.capacity());
2674  using std::fill;
2675  fill(buffer.begin(), buffer.end(), '\0');
2676  const double data = *reinterpret_cast<double*>(col.pdata_ + rowset_position_ * col.clen_);
2677  const int bytes = std::snprintf(
2678  const_cast<char*>(buffer.data())
2679  , width
2680  , "%.*lf" // restrict the number of digits
2681  , col.scale_ // number of digits after the decimal point
2682  , data);
2683  if(bytes == -1)
2684  throw type_incompatible_error();
2685  else if((SQLULEN)bytes < column_size)
2686  buffer.resize(bytes);
2687  buffer.resize(std::strlen(buffer.data())); // drop any trailing nulls
2688  result.reserve(buffer.size() * sizeof(string_type::value_type));
2689  convert(buffer, result);
2690  return;
2691  }
2692 
2693  case SQL_C_DATE:
2694  {
2695  const date d = *reinterpret_cast<date*>(col.pdata_ + rowset_position_ * col.clen_);
2696  std::tm st = { 0 };
2697  st.tm_year = d.year - 1900;
2698  st.tm_mon = d.month - 1;
2699  st.tm_mday = d.day;
2700  char* old_lc_time = std::setlocale(LC_TIME, NULL);
2701  std::setlocale(LC_TIME, "");
2702  char date_str[512];
2703  std::strftime(date_str, sizeof(date_str), "%Y-%m-%d", &st);
2704  std::setlocale(LC_TIME, old_lc_time);
2705  convert(date_str, result);
2706  return;
2707  }
2708 
2709  case SQL_C_TIMESTAMP:
2710  {
2711  const timestamp stamp = *reinterpret_cast<timestamp*>(col.pdata_ + rowset_position_ * col.clen_);
2712  std::tm st = { 0 };
2713  st.tm_year = stamp.year - 1900;
2714  st.tm_mon = stamp.month - 1;
2715  st.tm_mday = stamp.day;
2716  st.tm_hour = stamp.hour;
2717  st.tm_min = stamp.min;
2718  st.tm_sec = stamp.sec;
2719  char* old_lc_time = std::setlocale(LC_TIME, NULL);
2720  std::setlocale(LC_TIME, "");
2721  char date_str[512];
2722  std::strftime(date_str, sizeof(date_str), "%Y-%m-%d %H:%M:%S %z", &st);
2723  std::setlocale(LC_TIME, old_lc_time);
2724  convert(date_str, result);
2725  return;
2726  }
2727  }
2728  throw type_incompatible_error();
2729 }
2730 
2731 template<class T>
2732 void result::result_impl::get_ref_impl(short column, T& result) const
2733 {
2734  bound_column& col = bound_columns_[column];
2735  using namespace std; // if int64_t is in std namespace (in c++11)
2736  const char* s = col.pdata_ + rowset_position_ * col.clen_;
2737  switch(col.ctype_)
2738  {
2739  case SQL_C_CHAR: result = (T)*(char*)(s); return;
2740  case SQL_C_SSHORT: result = (T)*(short*)(s); return;
2741  case SQL_C_USHORT: result = (T)*(unsigned short*)(s); return;
2742  case SQL_C_LONG: result = (T)*(int32_t*)(s); return;
2743  case SQL_C_SLONG: result = (T)*(int32_t*)(s); return;
2744  case SQL_C_ULONG: result = (T)*(uint32_t*)(s); return;
2745  case SQL_C_FLOAT: result = (T)*(float*)(s); return;
2746  case SQL_C_DOUBLE: result = (T)*(double*)(s); return;
2747  case SQL_C_SBIGINT: result = (T)*(int64_t*)(s); return;
2748  case SQL_C_UBIGINT: result = (T)*(uint64_t*)(s); return;
2749  }
2750  throw type_incompatible_error();
2751 }
2752 
2753 } // namespace nanodbc
2754 
2755 // 8888888888 8888888888 888 d8b
2756 // 888 888 888 Y8P
2757 // 888 888 888
2758 // 8888888 888d888 .d88b. .d88b. 8888888 888 888 88888b. .d8888b 888888 888 .d88b. 88888b. .d8888b
2759 // 888 888P" d8P Y8b d8P Y8b 888 888 888 888 "88b d88P" 888 888 d88""88b 888 "88b 88K
2760 // 888 888 88888888 88888888 888 888 888 888 888 888 888 888 888 888 888 888 "Y8888b.
2761 // 888 888 Y8b. Y8b. 888 Y88b 888 888 888 Y88b. Y88b. 888 Y88..88P 888 888 X88
2762 // 888 888 "Y8888 "Y8888 888 "Y88888 888 888 "Y8888P "Y888 888 "Y88P" 888 888 88888P'
2763 // MARK: Free Functions -
2764 
2765 namespace nanodbc
2766 {
2767 
2768 result execute(connection& conn, const string_type& query, long batch_operations, long timeout)
2769 {
2770  class statement statement;
2771  return statement.execute_direct(conn, query, batch_operations, timeout);
2772 }
2773 
2774 void just_execute(connection& conn, const string_type& query, long batch_operations, long timeout) {
2775  class statement statement;
2776  statement.just_execute_direct(conn, query, batch_operations, timeout);
2777 }
2778 
2779 result execute(statement& stmt, long batch_operations)
2780 {
2781  return stmt.execute(batch_operations);
2782 }
2783 
2784 void just_execute(statement& stmt, long batch_operations)
2785 {
2786  return stmt.just_execute(batch_operations);
2787 }
2788 
2789 result transact(statement& stmt, long batch_operations)
2790 {
2791  class transaction transaction(stmt.connection());
2792  result rvalue = stmt.execute(batch_operations);
2793  transaction.commit();
2794  return rvalue;
2795 }
2796 
2797 void just_transact(statement& stmt, long batch_operations)
2798 {
2799  class transaction transaction(stmt.connection());
2800  stmt.just_execute(batch_operations);
2801  transaction.commit();
2802 }
2803 
2804 void prepare(statement& stmt, const string_type& query, long timeout)
2805 {
2806  stmt.prepare(stmt.connection(), query, timeout);
2807 }
2808 
2809 } // namespace nanodbc
2810 
2811 // .d8888b. 888 d8b 8888888888 888
2812 // d88P Y88b 888 Y8P 888 888
2813 // 888 888 888 888 888
2814 // 888 .d88b. 88888b. 88888b. .d88b. .d8888b 888888 888 .d88b. 88888b. 8888888 888 888 888 .d88888
2815 // 888 d88""88b 888 "88b 888 "88b d8P Y8b d88P" 888 888 d88""88b 888 "88b 888 888 888 888 d88" 888
2816 // 888 888 888 888 888 888 888 888 88888888 888 888 888 888 888 888 888 888 888 888 888 888 888
2817 // Y88b d88P Y88..88P 888 888 888 888 Y8b. Y88b. Y88b. 888 Y88..88P 888 888 888 Y88b 888 d88P Y88b 888
2818 // "Y8888P" "Y88P" 888 888 888 888 "Y8888 "Y8888P "Y888 888 "Y88P" 888 888 888 "Y8888888P" "Y88888
2819 // MARK: Connection Fwd -
2820 
2821 namespace nanodbc
2822 {
2823 
2824 connection::connection()
2825 : impl_(new connection_impl())
2826 {
2827 
2828 }
2829 
2830 connection::connection(const connection& rhs)
2831 : impl_(rhs.impl_)
2832 {
2833 
2834 }
2835 
2836 #ifndef NANODBC_NO_MOVE_CTOR
2837  connection::connection(connection&& rhs) NANODBC_NOEXCEPT
2838  : impl_(std::move(rhs.impl_))
2839  {
2840 
2841  }
2842 #endif
2843 
2844 connection& connection::operator=(connection rhs)
2845 {
2846  swap(rhs);
2847  return *this;
2848 }
2849 
2850 void connection::swap(connection& rhs) NANODBC_NOEXCEPT
2851 {
2852  using std::swap;
2853  swap(impl_, rhs.impl_);
2854 }
2855 
2856 connection::connection(
2857  const string_type& dsn
2858  , const string_type& user
2859  , const string_type& pass
2860  , long timeout)
2861 : impl_(new connection_impl(dsn, user, pass, timeout))
2862 {
2863 
2864 }
2865 
2866 connection::connection(const string_type& connection_string, long timeout)
2867 : impl_(new connection_impl(connection_string, timeout))
2868 {
2869 
2870 }
2871 
2872 connection::~connection() NANODBC_NOEXCEPT
2873 {
2874 
2875 }
2876 
2877 void connection::connect(
2878  const string_type& dsn
2879  , const string_type& user
2880  , const string_type& pass
2881  , long timeout)
2882 {
2883  impl_->connect(dsn, user, pass, timeout);
2884 }
2885 
2886 void connection::connect(const string_type& connection_string, long timeout)
2887 {
2888  impl_->connect(connection_string, timeout);
2889 }
2890 
2891 #ifdef SQL_ATTR_ASYNC_DBC_EVENT
2892 void connection::async_connect(
2893  const string_type& dsn
2894  , const string_type& user
2895  , const string_type& pass
2896  , void* event_handle
2897  , long timeout)
2898 {
2899  impl_->connect(dsn, user, pass, timeout, event_handle);
2900 }
2901 
2902 void connection::async_connect(const string_type& connection_string, void* event_handle, long timeout)
2903 {
2904  impl_->connect(connection_string, timeout, event_handle);
2905 }
2906 
2907 void connection::async_complete()
2908 {
2909  impl_->async_complete();
2910 }
2911 #endif // SQL_ATTR_ASYNC_DBC_EVENT
2912 
2913 bool connection::connected() const
2914 {
2915  return impl_->connected();
2916 }
2917 
2918 void connection::disconnect()
2919 {
2920  impl_->disconnect();
2921 }
2922 
2923 std::size_t connection::transactions() const
2924 {
2925  return impl_->transactions();
2926 }
2927 
2928 void* connection::native_dbc_handle() const
2929 {
2930  return impl_->native_dbc_handle();
2931 }
2932 
2933 void* connection::native_env_handle() const
2934 {
2935  return impl_->native_env_handle();
2936 }
2937 
2938 string_type connection::dbms_name() const
2939 {
2940  return impl_->dbms_name();
2941 }
2942 
2943 string_type connection::dbms_version() const
2944 {
2945  return impl_->dbms_version();
2946 }
2947 
2948 string_type connection::driver_name() const
2949 {
2950  return impl_->driver_name();
2951 }
2952 
2953 string_type connection::database_name() const
2954 {
2955  return impl_->database_name();
2956 }
2957 
2958 string_type connection::catalog_name() const
2959 {
2960  return impl_->catalog_name();
2961 }
2962 
2963 std::size_t connection::ref_transaction()
2964 {
2965  return impl_->ref_transaction();
2966 }
2967 
2968 std::size_t connection::unref_transaction()
2969 {
2970  return impl_->unref_transaction();
2971 }
2972 
2973 bool connection::rollback() const
2974 {
2975  return impl_->rollback();
2976 }
2977 
2978 void connection::rollback(bool onoff)
2979 {
2980  impl_->rollback(onoff);
2981 }
2982 
2983 } // namespace nanodbc
2984 
2985 // 88888888888 888 d8b 8888888888 888
2986 // 888 888 Y8P 888 888
2987 // 888 888 888 888
2988 // 888 888d888 8888b. 88888b. .d8888b 8888b. .d8888b 888888 888 .d88b. 88888b. 8888888 888 888 888 .d88888 .d8888b
2989 // 888 888P" "88b 888 "88b 88K "88b d88P" 888 888 d88""88b 888 "88b 888 888 888 888 d88" 888 88K
2990 // 888 888 .d888888 888 888 "Y8888b. .d888888 888 888 888 888 888 888 888 888 888 888 888 888 888 "Y8888b.
2991 // 888 888 888 888 888 888 X88 888 888 Y88b. Y88b. 888 Y88..88P 888 888 888 Y88b 888 d88P Y88b 888 X88
2992 // 888 888 "Y888888 888 888 88888P' "Y888888 "Y8888P "Y888 888 "Y88P" 888 888 888 "Y8888888P" "Y88888 88888P'
2993 // MARK: Transaction Fwd -
2994 
2995 namespace nanodbc
2996 {
2997 
2998 transaction::transaction(const class connection& conn)
2999 : impl_(new transaction_impl(conn))
3000 {
3001 
3002 }
3003 
3004 transaction::transaction(const transaction& rhs)
3005 : impl_(rhs.impl_)
3006 {
3007 
3008 }
3009 
3010 #ifndef NANODBC_NO_MOVE_CTOR
3011  transaction::transaction(transaction&& rhs) NANODBC_NOEXCEPT
3012  : impl_(std::move(rhs.impl_))
3013  {
3014 
3015  }
3016 #endif
3017 
3018 transaction& transaction::operator=(transaction rhs)
3019 {
3020  swap(rhs);
3021  return *this;
3022 }
3023 
3024 void transaction::swap(transaction& rhs) NANODBC_NOEXCEPT
3025 {
3026  using std::swap;
3027  swap(impl_, rhs.impl_);
3028 }
3029 
3030 transaction::~transaction() NANODBC_NOEXCEPT
3031 {
3032 
3033 }
3034 
3035 void transaction::commit()
3036 {
3037  impl_->commit();
3038 }
3039 
3040 void transaction::rollback() NANODBC_NOEXCEPT
3041 {
3042  impl_->rollback();
3043 }
3044 
3045 class connection& transaction::connection()
3046 {
3047  return impl_->connection();
3048 }
3049 
3050 const class connection& transaction::connection() const
3051 {
3052  return impl_->connection();
3053 }
3054 
3055 transaction::operator class connection&()
3056 {
3057  return impl_->connection();
3058 }
3059 
3060 transaction::operator const class connection&() const
3061 {
3062  return impl_->connection();
3063 }
3064 
3065 } // namespace nanodbc
3066 
3067 // .d8888b. 888 888 888 8888888888 888
3068 // d88P Y88b 888 888 888 888 888
3069 // Y88b. 888 888 888 888 888
3070 // "Y888b. 888888 8888b. 888888 .d88b. 88888b.d88b. .d88b. 88888b. 888888 8888888 888 888 888 .d88888
3071 // "Y88b. 888 "88b 888 d8P Y8b 888 "888 "88b d8P Y8b 888 "88b 888 888 888 888 888 d88" 888
3072 // "888 888 .d888888 888 88888888 888 888 888 88888888 888 888 888 888 888 888 888 888 888
3073 // Y88b d88P Y88b. 888 888 Y88b. Y8b. 888 888 888 Y8b. 888 888 Y88b. 888 Y88b 888 d88P Y88b 888
3074 // "Y8888P" "Y888 "Y888888 "Y888 "Y8888 888 888 888 "Y8888 888 888 "Y888 888 "Y8888888P" "Y88888
3075 // MARK: Statement Fwd -
3076 
3077 namespace nanodbc
3078 {
3079 
3080 statement::statement()
3081 : impl_(new statement_impl())
3082 {
3083 
3084 }
3085 
3086 statement::statement(class connection& conn)
3087 : impl_(new statement_impl(conn))
3088 {
3089 
3090 }
3091 
3092 #ifndef NANODBC_NO_MOVE_CTOR
3093  statement::statement(statement&& rhs) NANODBC_NOEXCEPT
3094  : impl_(std::move(rhs.impl_))
3095  {
3096 
3097  }
3098 #endif
3099 
3100 statement::statement(class connection& conn, const string_type& query, long timeout)
3101 : impl_(new statement_impl(conn, query, timeout))
3102 {
3103 
3104 }
3105 
3106 statement::statement(const statement& rhs)
3107 : impl_(rhs.impl_)
3108 {
3109 
3110 }
3111 
3112 statement& statement::operator=(statement rhs)
3113 {
3114  swap(rhs);
3115  return *this;
3116 }
3117 
3118 void statement::swap(statement& rhs) NANODBC_NOEXCEPT
3119 {
3120  using std::swap;
3121  swap(impl_, rhs.impl_);
3122 }
3123 
3124 statement::~statement() NANODBC_NOEXCEPT
3125 {
3126 
3127 }
3128 
3129 void statement::open(class connection& conn)
3130 {
3131  impl_->open(conn);
3132 }
3133 
3134 bool statement::open() const
3135 {
3136  return impl_->open();
3137 }
3138 
3139 bool statement::connected() const
3140 {
3141  return impl_->connected();
3142 }
3143 
3144 const class connection& statement::connection() const
3145 {
3146  return impl_->connection();
3147 }
3148 
3149 class connection& statement::connection()
3150 {
3151  return impl_->connection();
3152 }
3153 
3154 void* statement::native_statement_handle() const
3155 {
3156  return impl_->native_statement_handle();
3157 }
3158 
3159 void statement::close()
3160 {
3161  impl_->close();
3162 }
3163 
3164 void statement::cancel()
3165 {
3166  impl_->cancel();
3167 }
3168 
3169 void statement::prepare(class connection& conn, const string_type& query, long timeout)
3170 {
3171  impl_->prepare(conn, query, timeout);
3172 }
3173 
3174 void statement::prepare(const string_type& query, long timeout)
3175 {
3176  impl_->prepare(query, timeout);
3177 }
3178 
3179 void statement::timeout(long timeout)
3180 {
3181  impl_->timeout(timeout);
3182 }
3183 
3184 result statement::execute_direct(
3185  class connection& conn
3186  , const string_type& query
3187  , long batch_operations
3188  , long timeout)
3189 {
3190  return impl_->execute_direct(conn, query, batch_operations, timeout, *this);
3191 }
3192 
3193 #if defined(SQL_ATTR_ASYNC_STMT_EVENT) && defined(SQL_API_SQLCOMPLETEASYNC)
3194  void statement::async_execute_direct(
3195  class connection& conn
3196  , void* event_handle
3197  , const string_type& query
3198  , long batch_operations
3199  , long timeout)
3200  {
3201  impl_->async_execute_direct(conn, event_handle, query, batch_operations, timeout, *this);
3202  }
3203 
3204  result statement::async_complete(long batch_operations)
3205  {
3206  return impl_->async_complete(batch_operations, *this);
3207  }
3208 #endif
3209 
3210 void statement::just_execute_direct(
3211  class connection& conn
3212  , const string_type& query
3213  , long batch_operations
3214  , long timeout)
3215 {
3216  impl_->just_execute_direct(conn, query, batch_operations, timeout, *this);
3217 }
3218 
3219 result statement::execute(long batch_operations, long timeout)
3220 {
3221  return impl_->execute(batch_operations, timeout, *this);
3222 }
3223 
3224 void statement::just_execute(long batch_operations, long timeout)
3225 {
3226  impl_->just_execute(batch_operations, timeout, *this);
3227 }
3228 
3229 result statement::procedure_columns(
3230  const string_type& catalog
3231  , const string_type& schema
3232  , const string_type& procedure
3233  , const string_type& column)
3234 {
3235  return impl_->procedure_columns(catalog, schema, procedure, column, *this);
3236 }
3237 
3238 long statement::affected_rows() const
3239 {
3240  return impl_->affected_rows();
3241 }
3242 
3243 short statement::columns() const
3244 {
3245  return impl_->columns();
3246 }
3247 
3248 void statement::reset_parameters() NANODBC_NOEXCEPT
3249 {
3250  impl_->reset_parameters();
3251 }
3252 
3253 unsigned long statement::parameter_size(short param) const
3254 {
3255  return impl_->parameter_size(param);
3256 }
3257 
3258 // We need to instantiate each form of bind() for each of our supported data types.
3259 #define NANODBC_INSTANTIATE_BINDS(type)
3260  template void statement::bind(short, const type*, param_direction); /* 1-ary */
3261  template void statement::bind(short, const type*, std::size_t, param_direction); /* n-ary */
3262  template void statement::bind(short, const type*, std::size_t, const type*, param_direction); /* n-ary, sentry */
3263  template void statement::bind(short, const type*, std::size_t, const bool*, param_direction) /* n-ary, flags */
3264  /**/
3265 
3266 // The following are the only supported instantiations of statement::bind().
3267 NANODBC_INSTANTIATE_BINDS(string_type::value_type);
3268 NANODBC_INSTANTIATE_BINDS(short);
3269 NANODBC_INSTANTIATE_BINDS(unsigned short);
3270 NANODBC_INSTANTIATE_BINDS(int32_t);
3271 NANODBC_INSTANTIATE_BINDS(uint32_t);
3272 NANODBC_INSTANTIATE_BINDS(int64_t);
3273 NANODBC_INSTANTIATE_BINDS(uint64_t);
3274 NANODBC_INSTANTIATE_BINDS(float);
3275 NANODBC_INSTANTIATE_BINDS(double);
3276 NANODBC_INSTANTIATE_BINDS(date);
3277 NANODBC_INSTANTIATE_BINDS(timestamp);
3278 
3279 #undef NANODBC_INSTANTIATE_BINDS
3280 
3281 template<class T>
3282 void statement::bind(short param, const T* value, param_direction direction)
3283 {
3284  impl_->bind(param, value, 1, direction);
3285 }
3286 
3287 template<class T>
3288 void statement::bind(short param, const T* values, std::size_t elements, param_direction direction)
3289 {
3290  impl_->bind(param, values, elements, direction);
3291 }
3292 
3293 template<class T>
3294 void statement::bind(
3295  short param
3296  , const T* values
3297  , std::size_t elements
3298  , const T* null_sentry
3299  , param_direction direction)
3300 {
3301  impl_->bind(param, values, elements, 0, null_sentry, direction);
3302 }
3303 
3304 template<class T>
3305 void statement::bind(
3306  short param
3307  , const T* values
3308  , std::size_t elements
3309  , const bool* nulls
3310  , param_direction direction)
3311 {
3312  impl_->bind(param, values, elements, nulls, (T*)0, direction);
3313 }
3314 
3315 void statement::bind_strings(
3316  short param
3317  , const string_type::value_type* values
3318  , std::size_t length
3319  , std::size_t elements
3320  , param_direction direction)
3321 {
3322  impl_->bind_strings(param, values, length, elements, direction);
3323 }
3324 
3325 void statement::bind_strings(
3326  short param
3327  , const string_type::value_type* values
3328  , std::size_t length
3329  , std::size_t elements
3330  , const string_type::value_type* null_sentry
3331  , param_direction direction)
3332 {
3333  impl_->bind_strings(param, values, length, elements, (bool*)0, null_sentry, direction);
3334 }
3335 
3336 void statement::bind_strings(
3337  short param
3338  , const string_type::value_type* values
3339  , std::size_t length
3340  , std::size_t elements
3341  , const bool* nulls
3342  , param_direction direction)
3343 {
3344  impl_->bind_strings(
3345  param
3346  , values
3347  , length
3348  , elements
3349  , nulls
3350  , (string_type::value_type*)0
3351  , direction);
3352 }
3353 
3354 void statement::bind_null(short param, std::size_t elements)
3355 {
3356  impl_->bind_null(param, elements);
3357 }
3358 
3359 } // namespace nanodbc
3360 
3361 namespace nanodbc
3362 {
3363 
3364 catalog::tables::tables(result& find_result)
3365 : result_(find_result)
3366 {
3367 }
3368 
3369 bool catalog::tables::next()
3370 {
3371  return result_.next();
3372 }
3373 
3374 string_type catalog::tables::table_catalog() const
3375 {
3376  // TABLE_CAT might be NULL
3377  return result_.get<string_type>(0, string_type());
3378 }
3379 
3380 string_type catalog::tables::table_schema() const
3381 {
3382  // TABLE_SCHEM might be NULL
3383  return result_.get<string_type>(1, string_type());
3384 }
3385 
3386 string_type catalog::tables::table_name() const
3387 {
3388  // TABLE_NAME column is never NULL
3389  return result_.get<string_type>(2);
3390 }
3391 
3392 string_type catalog::tables::table_type() const
3393 {
3394  // TABLE_TYPE column is never NULL
3395  return result_.get<string_type>(3);
3396 }
3397 
3398 string_type catalog::tables::table_remarks() const
3399 {
3400  // REMARKS might be NULL
3401  return result_.get<string_type>(4, string_type());
3402 }
3403 
3404 catalog::primary_keys::primary_keys(result& find_result)
3405 : result_(find_result)
3406 {
3407 }
3408 
3409 bool catalog::primary_keys::next()
3410 {
3411  return result_.next();
3412 }
3413 
3414 string_type catalog::primary_keys::table_catalog() const
3415 {
3416  // TABLE_CAT might be NULL
3417  return result_.get<string_type>(0, string_type());
3418 }
3419 
3420 string_type catalog::primary_keys::table_schema() const
3421 {
3422  // TABLE_SCHEM might be NULL
3423  return result_.get<string_type>(1, string_type());
3424 }
3425 
3426 string_type catalog::primary_keys::table_name() const
3427 {
3428  // TABLE_NAME is never NULL
3429  return result_.get<string_type>(2);
3430 }
3431 
3432 string_type catalog::primary_keys::column_name() const
3433 {
3434  // COLUMN_NAME is never NULL
3435  return result_.get<string_type>(3);
3436 }
3437 
3438 short catalog::primary_keys::column_number() const
3439 {
3440  // KEY_SEQ is never NULL
3441  return result_.get<short>(4);
3442 }
3443 
3444 string_type catalog::primary_keys::primary_key_name() const
3445 {
3446  // PK_NAME might be NULL
3447  return result_.get<string_type>(5);
3448 }
3449 
3450 catalog::columns::columns(result& find_result)
3451 : result_(find_result)
3452 {
3453 }
3454 
3455 bool catalog::columns::next()
3456 {
3457  return result_.next();
3458 }
3459 
3460 string_type catalog::columns::table_catalog() const
3461 {
3462  // TABLE_CAT might be NULL
3463  return result_.get<string_type>(0, string_type());
3464 }
3465 
3466 string_type catalog::columns::table_schema() const
3467 {
3468  // TABLE_SCHEM might be NULL
3469  return result_.get<string_type>(1, string_type());
3470 }
3471 
3472 string_type catalog::columns::table_name() const
3473 {
3474  // TABLE_NAME is never NULL
3475  return result_.get<string_type>(2);
3476 }
3477 
3478 string_type catalog::columns::column_name() const
3479 {
3480  // COLUMN_NAME is never NULL
3481  return result_.get<string_type>(3);
3482 }
3483 
3484 short catalog::columns::data_type() const
3485 {
3486  // DATA_TYPE is never NULL
3487  return result_.get<short>(4);
3488 }
3489 
3490 string_type catalog::columns::type_name() const
3491 {
3492  // TYPE_NAME is never NULL
3493  return result_.get<string_type>(5);
3494 }
3495 
3496 long catalog::columns::column_size() const
3497 {
3498  // COLUMN_SIZE
3499  return result_.get<long>(6);
3500 }
3501 
3502 long catalog::columns::buffer_length() const
3503 {
3504  // BUFFER_LENGTH
3505  return result_.get<long>(7);
3506 }
3507 
3508 short catalog::columns::decimal_digits() const
3509 {
3510  // DECIMAL_DIGITS might be NULL
3511  return result_.get<short>(8, 0);
3512 }
3513 
3514 short catalog::columns::numeric_precision_radix() const
3515 {
3516  // NUM_PREC_RADIX might be NULL
3517  return result_.get<short>(9, 0);
3518 }
3519 
3520 short catalog::columns::nullable() const
3521 {
3522  // NULLABLE is never NULL
3523  return result_.get<short>(10);
3524 }
3525 
3526 string_type catalog::columns::remarks() const
3527 {
3528  // REMARKS might be NULL
3529  return result_.get<string_type>(11, string_type());
3530 }
3531 
3532 string_type catalog::columns::column_default() const
3533 {
3534  // COLUMN_DEF might be NULL, if no default value is specified.
3535  return result_.get<string_type>(12, string_type());
3536 }
3537 
3538 short catalog::columns::sql_data_type() const
3539 {
3540  // SQL_DATA_TYPE is never NULL
3541  return result_.get<short>(13);
3542 }
3543 
3544 short catalog::columns::sql_datetime_subtype() const
3545 {
3546  // SQL_DATETIME_SUB might be NULL
3547  return result_.get<short>(14, 0);
3548 }
3549 
3550 long catalog::columns::char_octed_length() const
3551 {
3552  // CHAR_OCTET_LENGTH might be nULL
3553  return result_.get<long>(15, 0);
3554 }
3555 
3556 long catalog::columns::ordinal_position() const
3557 {
3558  // ORDINAL_POSITION is never NULL
3559  return result_.get<long>(16);
3560 }
3561 
3562 string_type catalog::columns::is_nullable() const
3563 {
3564  // IS_NULLABLE might be NULL
3565 
3566  // MSDN: This column returns a zero-length string if nullability is unknown.
3567  // ISO rules are followed to determine nullability.
3568  // An ISO SQL-compliant DBMS cannot return an empty string.
3569  return result_.get<string_type>(17, string_type());
3570 }
3571 
3572 catalog::catalog(connection& conn)
3573 : conn_(conn)
3574 {
3575 }
3576 
3577 catalog::tables catalog::find_tables(
3578  const string_type& table
3579  , const string_type& type
3580  , const string_type& schema
3581  , const string_type& catalog)
3582 {
3583  statement stmt(conn_);
3584  RETCODE rc;
3585  NANODBC_CALL_RC(
3586  NANODBC_FUNC(SQLTables)
3587  , rc
3588  , stmt.native_statement_handle()
3589  , (NANODBC_SQLCHAR*)(catalog.empty() ? NULL : catalog.c_str())
3590  , (catalog.empty() ? 0 : SQL_NTS)
3591  , (NANODBC_SQLCHAR*)(schema.empty() ? NULL : schema.c_str())
3592  , (schema.empty() ? 0 : SQL_NTS)
3593  , (NANODBC_SQLCHAR*)(table.empty() ? NULL : table.c_str())
3594  , (table.empty() ? 0 : SQL_NTS)
3595  , (NANODBC_SQLCHAR*)(type.empty() ? NULL : type.c_str())
3596  , (type.empty() ? 0 : SQL_NTS));
3597  if(!success(rc))
3598  NANODBC_THROW_DATABASE_ERROR(stmt.native_statement_handle(), SQL_HANDLE_STMT);
3599 
3600  result find_result(stmt, 1);
3601  return catalog::tables(find_result);
3602 }
3603 
3604 catalog::columns catalog::find_columns(
3605  const string_type& column
3606  , const string_type& table
3607  , const string_type& schema
3608  , const string_type& catalog)
3609 {
3610  statement stmt(conn_);
3611  RETCODE rc;
3612  NANODBC_CALL_RC(
3613  NANODBC_FUNC(SQLColumns)
3614  , rc
3615  , stmt.native_statement_handle()
3616  , (NANODBC_SQLCHAR*)(catalog.empty() ? NULL : catalog.c_str())
3617  , (catalog.empty() ? 0 : SQL_NTS)
3618  , (NANODBC_SQLCHAR*)(schema.empty() ? NULL : schema.c_str())
3619  , (schema.empty() ? 0 : SQL_NTS)
3620  , (NANODBC_SQLCHAR*)(table.empty() ? NULL : table.c_str())
3621  , (table.empty() ? 0 : SQL_NTS)
3622  , (NANODBC_SQLCHAR*)(column.empty() ? NULL : column.c_str())
3623  , (column.empty() ? 0 : SQL_NTS));
3624  if(!success(rc))
3625  NANODBC_THROW_DATABASE_ERROR(stmt.native_statement_handle(), SQL_HANDLE_STMT);
3626 
3627  result find_result(stmt, 1);
3628  return catalog::columns(find_result);
3629 }
3630 
3631 catalog::primary_keys catalog::find_primary_keys(
3632  const string_type& table
3633  , const string_type& schema
3634  , const string_type& catalog)
3635 {
3636  statement stmt(conn_);
3637  RETCODE rc;
3638  NANODBC_CALL_RC(
3639  NANODBC_FUNC(SQLPrimaryKeys)
3640  , rc
3641  , stmt.native_statement_handle()
3642  , (NANODBC_SQLCHAR*)(catalog.empty() ? NULL : catalog.c_str())
3643  , (catalog.empty() ? 0 : SQL_NTS)
3644  , (NANODBC_SQLCHAR*)(schema.empty() ? NULL : schema.c_str())
3645  , (schema.empty() ? 0 : SQL_NTS)
3646  , (NANODBC_SQLCHAR*)(table.empty() ? NULL : table.c_str())
3647  , (table.empty() ? 0 : SQL_NTS));
3648  if(!success(rc))
3649  NANODBC_THROW_DATABASE_ERROR(stmt.native_statement_handle(), SQL_HANDLE_STMT);
3650 
3651  result find_result(stmt, 1);
3652  return catalog::primary_keys(find_result);
3653 }
3654 
3655 } // namespace nanodbc
3656 
3657 // 8888888b. 888 888 8888888888 888
3658 // 888 Y88b 888 888 888 888
3659 // 888 888 888 888 888 888
3660 // 888 d88P .d88b. .d8888b 888 888 888 888888 8888888 888 888 888 .d88888
3661 // 8888888P" d8P Y8b 88K 888 888 888 888 888 888 888 888 d88" 888
3662 // 888 T88b 88888888 "Y8888b. 888 888 888 888 888 888 888 888 888 888
3663 // 888 T88b Y8b. X88 Y88b 888 888 Y88b. 888 Y88b 888 d88P Y88b 888
3664 // 888 T88b "Y8888 88888P' "Y88888 888 "Y888 888 "Y8888888P" "Y88888
3665 // MARK: Result Fwd -
3666 
3667 namespace nanodbc
3668 {
3669 
3670 result::result()
3671 : impl_()
3672 {
3673 
3674 }
3675 
3676 result::~result() NANODBC_NOEXCEPT
3677 {
3678 
3679 }
3680 
3681 result::result(statement stmt, long rowset_size)
3682 : impl_(new result_impl(stmt, rowset_size))
3683 {
3684 
3685 }
3686 
3687 #ifndef NANODBC_NO_MOVE_CTOR
3688  result::result(result&& rhs) NANODBC_NOEXCEPT
3689  : impl_(std::move(rhs.impl_))
3690  {
3691 
3692  }
3693 #endif
3694 
3695 result::result(const result& rhs)
3696 : impl_(rhs.impl_)
3697 {
3698 
3699 }
3700 
3701 result& result::operator=(result rhs)
3702 {
3703  swap(rhs);
3704  return *this;
3705 }
3706 
3707 void result::swap(result& rhs) NANODBC_NOEXCEPT
3708 {
3709  using std::swap;
3710  swap(impl_, rhs.impl_);
3711 }
3712 
3713 void* result::native_statement_handle() const
3714 {
3715  return impl_->native_statement_handle();
3716 }
3717 
3718 long result::rowset_size() const NANODBC_NOEXCEPT
3719 {
3720  return impl_->rowset_size();
3721 }
3722 
3723 long result::affected_rows() const
3724 {
3725  return impl_->affected_rows();
3726 }
3727 
3728 long result::rows() const NANODBC_NOEXCEPT
3729 {
3730  return impl_->rows();
3731 }
3732 
3733 short result::columns() const
3734 {
3735  return impl_->columns();
3736 }
3737 
3738 bool result::first()
3739 {
3740  return impl_->first();
3741 }
3742 
3743 bool result::last()
3744 {
3745  return impl_->last();
3746 }
3747 
3748 bool result::next()
3749 {
3750  return impl_->next();
3751 }
3752 
3753 bool result::prior()
3754 {
3755  return impl_->prior();
3756 }
3757 
3758 bool result::move(long row)
3759 {
3760  return impl_->move(row);
3761 }
3762 
3763 bool result::skip(long rows)
3764 {
3765  return impl_->skip(rows);
3766 }
3767 
3768 unsigned long result::position() const
3769 {
3770  return impl_->position();
3771 }
3772 
3773 bool result::end() const NANODBC_NOEXCEPT
3774 {
3775  return impl_->end();
3776 }
3777 
3778 bool result::is_null(short column) const
3779 {
3780  return impl_->is_null(column);
3781 }
3782 
3783 bool result::is_null(const string_type& column_name) const
3784 {
3785  return impl_->is_null(column_name);
3786 }
3787 
3788 string_type result::column_name(short column) const
3789 {
3790  return impl_->column_name(column);
3791 }
3792 
3793 long result::column_size(short column) const
3794 {
3795  return impl_->column_size(column);
3796 }
3797 
3798 short result::column(const string_type& column_name) const
3799 {
3800  return impl_->column(column_name);
3801 }
3802 
3803 int result::column_datatype(short column) const
3804 {
3805  return impl_->column_datatype(column);
3806 }
3807 
3808 int result::column_datatype(const string_type& column_name) const
3809 {
3810  return impl_->column_datatype(column_name);
3811 }
3812 
3813 int result::column_c_datatype(short column) const
3814 {
3815  return impl_->column_c_datatype(column);
3816 }
3817 
3818 int result::column_c_datatype(const string_type& column_name) const
3819 {
3820  return impl_->column_c_datatype(column_name);
3821 }
3822 
3823 bool result::next_result()
3824 {
3825  return impl_->next_result();
3826 }
3827 
3828 template<class T>
3829 void result::get_ref(short column, T& result) const
3830 {
3831  return impl_->get_ref<T>(column, result);
3832 }
3833 
3834 template<class T>
3835 void result::get_ref(short column, const T& fallback, T& result) const
3836 {
3837  return impl_->get_ref<T>(column, fallback, result);
3838 }
3839 
3840 template<class T>
3841 void result::get_ref(const string_type& column_name, T& result) const
3842 {
3843  return impl_->get_ref<T>(column_name, result);
3844 }
3845 
3846 template<class T>
3847 void result::get_ref(const string_type& column_name, const T& fallback, T& result) const
3848 {
3849  return impl_->get_ref<T>(column_name, fallback, result);
3850 }
3851 
3852 template<class T>
3853 T result::get(short column) const
3854 {
3855  return impl_->get<T>(column);
3856 }
3857 
3858 template<class T>
3859 T result::get(short column, const T& fallback) const
3860 {
3861  return impl_->get<T>(column, fallback);
3862 }
3863 
3864 template<class T>
3865 T result::get(const string_type& column_name) const
3866 {
3867  return impl_->get<T>(column_name);
3868 }
3869 
3870 template<class T>
3871 T result::get(const string_type& column_name, const T& fallback) const
3872 {
3873  return impl_->get<T>(column_name, fallback);
3874 }
3875 
3876 result::operator bool() const
3877 {
3878  return static_cast<bool>(impl_);
3879 }
3880 
3881 // The following are the only supported instantiations of result::get_ref().
3882 template void result::get_ref(short, string_type::value_type&) const;
3883 template void result::get_ref(short, short&) const;
3884 template void result::get_ref(short, unsigned short&) const;
3885 template void result::get_ref(short, int32_t&) const;
3886 template void result::get_ref(short, uint32_t&) const;
3887 template void result::get_ref(short, int64_t&) const;
3888 template void result::get_ref(short, uint64_t&) const;
3889 template void result::get_ref(short, float&) const;
3890 template void result::get_ref(short, double&) const;
3891 template void result::get_ref(short, string_type&) const;
3892 template void result::get_ref(short, date&) const;
3893 template void result::get_ref(short, timestamp&) const;
3894 
3895 template void result::get_ref(const string_type&, string_type::value_type&) const;
3896 template void result::get_ref(const string_type&, short&) const;
3897 template void result::get_ref(const string_type&, unsigned short&) const;
3898 template void result::get_ref(const string_type&, int32_t&) const;
3899 template void result::get_ref(const string_type&, uint32_t&) const;
3900 template void result::get_ref(const string_type&, int64_t&) const;
3901 template void result::get_ref(const string_type&, uint64_t&) const;
3902 template void result::get_ref(const string_type&, float&) const;
3903 template void result::get_ref(const string_type&, double&) const;
3904 template void result::get_ref(const string_type&, string_type&) const;
3905 template void result::get_ref(const string_type&, date&) const;
3906 template void result::get_ref(const string_type&, timestamp&) const;
3907 
3908 // The following are the only supported instantiations of result::get_ref() with fallback.
3909 template void result::get_ref(short, const string_type::value_type&, string_type::value_type&) const;
3910 template void result::get_ref(short, const short&, short&) const;
3911 template void result::get_ref(short, const unsigned short&, unsigned short&) const;
3912 template void result::get_ref(short, const int32_t&, int32_t&) const;
3913 template void result::get_ref(short, const uint32_t&, uint32_t&) const;
3914 template void result::get_ref(short, const int64_t&, int64_t&) const;
3915 template void result::get_ref(short, const uint64_t&, uint64_t&) const;
3916 template void result::get_ref(short, const float&, float&) const;
3917 template void result::get_ref(short, const double&, double&) const;
3918 template void result::get_ref(short, const string_type&, string_type&) const;
3919 template void result::get_ref(short, const date&, date&) const;
3920 template void result::get_ref(short, const timestamp&, timestamp&) const;
3921 
3922 template void result::get_ref(const string_type&, const string_type::value_type&, string_type::value_type&) const;
3923 template void result::get_ref(const string_type&, const short&, short&) const;
3924 template void result::get_ref(const string_type&, const unsigned short&, unsigned short&) const;
3925 template void result::get_ref(const string_type&, const int32_t&, int32_t&) const;
3926 template void result::get_ref(const string_type&, const uint32_t&, uint32_t&) const;
3927 template void result::get_ref(const string_type&, const int64_t&, int64_t&) const;
3928 template void result::get_ref(const string_type&, const uint64_t&, uint64_t&) const;
3929 template void result::get_ref(const string_type&, const float&, float&) const;
3930 template void result::get_ref(const string_type&, const double&, double&) const;
3931 template void result::get_ref(const string_type&, const string_type&, string_type&) const;
3932 template void result::get_ref(const string_type&, const date&, date&) const;
3933 template void result::get_ref(const string_type&, const timestamp&, timestamp&) const;
3934 
3935 // The following are the only supported instantiations of result::get().
3936 template string_type::value_type result::get(short) const;
3937 template short result::get(short) const;
3938 template unsigned short result::get(short) const;
3939 template int32_t result::get(short) const;
3940 template uint32_t result::get(short) const;
3941 template int64_t result::get(short) const;
3942 template uint64_t result::get(short) const;
3943 template float result::get(short) const;
3944 template double result::get(short) const;
3945 template string_type result::get(short) const;
3946 template date result::get(short) const;
3947 template timestamp result::get(short) const;
3948 
3949 template string_type::value_type result::get(const string_type&) const;
3950 template short result::get(const string_type&) const;
3951 template unsigned short result::get(const string_type&) const;
3952 template int32_t result::get(const string_type&) const;
3953 template uint32_t result::get(const string_type&) const;
3954 template int64_t result::get(const string_type&) const;
3955 template uint64_t result::get(const string_type&) const;
3956 template float result::get(const string_type&) const;
3957 template double result::get(const string_type&) const;
3958 template string_type result::get(const string_type&) const;
3959 template date result::get(const string_type&) const;
3960 template timestamp result::get(const string_type&) const;
3961 
3962 // The following are the only supported instantiations of result::get() with fallback.
3963 template string_type::value_type result::get(short, const string_type::value_type&) const;
3964 template short result::get(short, const short&) const;
3965 template unsigned short result::get(short, const unsigned short&) const;
3966 template int32_t result::get(short, const int32_t&) const;
3967 template uint32_t result::get(short, const uint32_t&) const;
3968 template int64_t result::get(short, const int64_t&) const;
3969 template uint64_t result::get(short, const uint64_t&) const;
3970 template float result::get(short, const float&) const;
3971 template double result::get(short, const double&) const;
3972 template string_type result::get(short, const string_type&) const;
3973 template date result::get(short, const date&) const;
3974 template timestamp result::get(short, const timestamp&) const;
3975 
3976 template string_type::value_type result::get(const string_type&, const string_type::value_type&) const;
3977 template short result::get(const string_type&, const short&) const;
3978 template unsigned short result::get(const string_type&, const unsigned short&) const;
3979 template int32_t result::get(const string_type&, const int32_t&) const;
3980 template uint32_t result::get(const string_type&, const uint32_t&) const;
3981 template int64_t result::get(const string_type&, const int64_t&) const;
3982 template uint64_t result::get(const string_type&, const uint64_t&) const;
3983 template float result::get(const string_type&, const float&) const;
3984 template double result::get(const string_type&, const double&) const;
3985 template string_type result::get(const string_type&, const string_type&) const;
3986 template date result::get(const string_type&, const date&) const;
3987 template timestamp result::get(const string_type&, const timestamp&) const;
3988 
3989 } // namespace nanodbc
3990 
3991 #undef NANODBC_THROW_DATABASE_ERROR
3992 #undef NANODBC_STRINGIZE
3993 #undef NANODBC_STRINGIZE_I
3994 #undef NANODBC_CALL_RC
3995 #undef NANODBC_CALL
3996 
3997 #endif // DOXYGEN
3998 
3999 #endif // DOXYGEN_SHOULD_SKIP_THIS
4000 
4001 #endif // _ODBC