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