|
@@ -13,7 +13,6 @@
|
|
|
#include <ctime>
|
|
#include <ctime>
|
|
|
#include <iomanip>
|
|
#include <iomanip>
|
|
|
#include <map>
|
|
#include <map>
|
|
|
-#include <sstream>
|
|
|
|
|
|
|
|
|
|
#ifndef __clang__
|
|
#ifndef __clang__
|
|
|
#include <cstdint>
|
|
#include <cstdint>
|
|
@@ -21,8 +20,8 @@
|
|
|
|
|
|
|
|
// User may redefine NANODBC_ASSERT macro in nanodbc.h
|
|
// User may redefine NANODBC_ASSERT macro in nanodbc.h
|
|
|
#ifndef NANODBC_ASSERT
|
|
#ifndef NANODBC_ASSERT
|
|
|
-#include <cassert>
|
|
|
|
|
-#define NANODBC_ASSERT(expr) assert(expr)
|
|
|
|
|
|
|
+ #include <cassert>
|
|
|
|
|
+ #define NANODBC_ASSERT(expr) assert(expr)
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef NANODBC_USE_BOOST_CONVERT
|
|
#ifdef NANODBC_USE_BOOST_CONVERT
|
|
@@ -73,47 +72,36 @@
|
|
|
// "Y88888P" 888 888 888 "Y8888P "Y88P" "Y88888 "Y8888
|
|
// "Y88888P" 888 888 888 "Y8888P "Y88P" "Y88888 "Y8888
|
|
|
// MARK: Unicode -
|
|
// MARK: Unicode -
|
|
|
|
|
|
|
|
-#if defined(_MSC_VER)
|
|
|
|
|
- #ifdef NANODBC_USE_UNICODE
|
|
|
|
|
- #define NANODBC_TEXT(s) L ## s
|
|
|
|
|
- #define NANODBC_SNPRINTF std::swprintf
|
|
|
|
|
- #define NANODBC_STRFTIME std::wcsftime
|
|
|
|
|
- #define NANODBC_STRLEN std::wcslen
|
|
|
|
|
- #define NANADBC_STRNCMP std::wcsncmp
|
|
|
|
|
- #define NANODBC_UNICODE(f) f ## W
|
|
|
|
|
- #define NANODBC_SQLCHAR SQLWCHAR
|
|
|
|
|
|
|
+#ifdef NANODBC_USE_UNICODE
|
|
|
|
|
+ #ifdef NANODBC_USE_IODBC_WIDE_STRINGS
|
|
|
|
|
+ #define NANODBC_TEXT(s) U ## s
|
|
|
#else
|
|
#else
|
|
|
- #define NANODBC_TEXT(s) s
|
|
|
|
|
- #define NANODBC_SNPRINTF(buffer, count, format, ...) _snprintf_s(buffer, count, _TRUNCATE, format, __VA_ARGS__)
|
|
|
|
|
- #define NANODBC_STRFTIME std::strftime
|
|
|
|
|
- #define NANODBC_STRLEN std::strlen
|
|
|
|
|
- #define NANADBC_STRNCMP std::strncmp
|
|
|
|
|
- #define NANODBC_UNICODE(f) f
|
|
|
|
|
- #define NANODBC_SQLCHAR SQLCHAR
|
|
|
|
|
|
|
+ #define NANODBC_TEXT(s) u ## s
|
|
|
|
|
+ #endif
|
|
|
|
|
+ #define NANODBC_FUNC(f) f ## W
|
|
|
|
|
+ #define NANODBC_SQLCHAR SQLWCHAR
|
|
|
|
|
+#else
|
|
|
|
|
+ #define NANODBC_TEXT(s) s
|
|
|
|
|
+ #define NANODBC_FUNC(f) f
|
|
|
|
|
+ #define NANODBC_SQLCHAR SQLCHAR
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+#ifdef NANODBC_USE_IODBC_WIDE_STRINGS
|
|
|
|
|
+ typedef std::u32string wide_string_type;
|
|
|
|
|
+ #define NANODBC_CODECVT_TYPE std::codecvt_utf8
|
|
|
|
|
+#else
|
|
|
|
|
+ typedef std::u16string wide_string_type;
|
|
|
|
|
+ #define NANODBC_CODECVT_TYPE std::codecvt_utf8_utf16
|
|
|
|
|
+#endif
|
|
|
|
|
+typedef wide_string_type::value_type wide_char_t;
|
|
|
|
|
|
|
|
|
|
+#if defined(_MSC_VER)
|
|
|
|
|
+ #ifndef NANODBC_USE_UNICODE
|
|
|
// Disable unicode in sqlucode.h on Windows when NANODBC_USE_UNICODE
|
|
// Disable unicode in sqlucode.h on Windows when NANODBC_USE_UNICODE
|
|
|
// is not defined. This is required because unicode is enabled by
|
|
// is not defined. This is required because unicode is enabled by
|
|
|
// default on many Windows systems.
|
|
// default on many Windows systems.
|
|
|
#define SQL_NOUNICODEMAP
|
|
#define SQL_NOUNICODEMAP
|
|
|
#endif
|
|
#endif
|
|
|
-#else
|
|
|
|
|
- #ifdef NANODBC_USE_UNICODE
|
|
|
|
|
- #define NANODBC_TEXT(s) L ## s
|
|
|
|
|
- #define NANODBC_SNPRINTF std::swprintf
|
|
|
|
|
- #define NANODBC_STRFTIME std::wcsftime
|
|
|
|
|
- #define NANODBC_STRLEN std::wcslen
|
|
|
|
|
- #define NANADBC_STRNCMP std::wcsncmp
|
|
|
|
|
- #define NANODBC_UNICODE(f) f ## W
|
|
|
|
|
- #define NANODBC_SQLCHAR SQLWCHAR
|
|
|
|
|
- #else
|
|
|
|
|
- #define NANODBC_TEXT(s) s
|
|
|
|
|
- #define NANODBC_SNPRINTF std::snprintf
|
|
|
|
|
- #define NANODBC_STRFTIME std::strftime
|
|
|
|
|
- #define NANODBC_STRLEN std::strlen
|
|
|
|
|
- #define NANADBC_STRNCMP std::strncmp
|
|
|
|
|
- #define NANODBC_UNICODE(f) f
|
|
|
|
|
- #define NANODBC_SQLCHAR SQLCHAR
|
|
|
|
|
- #endif
|
|
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
// .d88888b. 8888888b. 888888b. .d8888b. 888b d888
|
|
// .d88888b. 8888888b. 888888b. .d8888b. 888b d888
|
|
@@ -214,28 +202,41 @@ namespace
|
|
|
return i;
|
|
return i;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- inline void convert(const std::wstring& in, std::string& out)
|
|
|
|
|
|
|
+ inline void convert(const wide_string_type& in, std::string& out)
|
|
|
{
|
|
{
|
|
|
#ifdef NANODBC_USE_BOOST_CONVERT
|
|
#ifdef NANODBC_USE_BOOST_CONVERT
|
|
|
using boost::locale::conv::utf_to_utf;
|
|
using boost::locale::conv::utf_to_utf;
|
|
|
out = utf_to_utf<char>(in.c_str(), in.c_str() + in.size());
|
|
out = utf_to_utf<char>(in.c_str(), in.c_str() + in.size());
|
|
|
#else
|
|
#else
|
|
|
- out = std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(in);
|
|
|
|
|
|
|
+ #if defined(_MSC_VER) && (_MSC_VER == 1900)
|
|
|
|
|
+ // Workaround for confirmed bug in VS2015.
|
|
|
|
|
+ // See: https://social.msdn.microsoft.com/Forums/en-US/8f40dcd8-c67f-4eba-9134-a19b9178e481/vs-2015-rc-linker-stdcodecvt-error
|
|
|
|
|
+ auto p = reinterpret_cast<wide_char_t const*>(in.data());
|
|
|
|
|
+ out = std::wstring_convert<NANODBC_CODECVT_TYPE<wide_char_t>, wide_char_t>().to_bytes(p, p + in.size());
|
|
|
|
|
+ #else
|
|
|
|
|
+ out = std::wstring_convert<NANODBC_CODECVT_TYPE<wide_char_t>, wide_char_t>().to_bytes(in);
|
|
|
|
|
+ #endif
|
|
|
#endif
|
|
#endif
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#ifdef NANODBC_USE_UNICODE
|
|
#ifdef NANODBC_USE_UNICODE
|
|
|
- inline void convert(const std::string& in, std::wstring& out)
|
|
|
|
|
|
|
+ inline void convert(const std::string& in, wide_string_type& out)
|
|
|
{
|
|
{
|
|
|
#ifdef NANODBC_USE_BOOST_CONVERT
|
|
#ifdef NANODBC_USE_BOOST_CONVERT
|
|
|
using boost::locale::conv::utf_to_utf;
|
|
using boost::locale::conv::utf_to_utf;
|
|
|
- out = utf_to_utf<wchar_t>(in.c_str(), in.c_str() + in.size());
|
|
|
|
|
|
|
+ out = utf_to_utf<wide_char_t>(in.c_str(), in.c_str() + in.size());
|
|
|
|
|
+ #elif defined(_MSC_VER) && (_MSC_VER == 1900)
|
|
|
|
|
+ // Workaround for confirmed bug in VS2015.
|
|
|
|
|
+ // See: https://social.msdn.microsoft.com/Forums/en-US/8f40dcd8-c67f-4eba-9134-a19b9178e481/vs-2015-rc-linker-stdcodecvt-error
|
|
|
|
|
+ auto s = std::wstring_convert<NANODBC_CODECVT_TYPE<wide_char_t>, wide_char_t>().from_bytes(in);
|
|
|
|
|
+ auto p = reinterpret_cast<wide_char_t const*>(s.data());
|
|
|
|
|
+ out.assign(p, p + s.size());
|
|
|
#else
|
|
#else
|
|
|
- out = std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(in);
|
|
|
|
|
|
|
+ out = std::wstring_convert<NANODBC_CODECVT_TYPE<wide_char_t>, wide_char_t>().from_bytes(in);
|
|
|
#endif
|
|
#endif
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- inline void convert(const std::wstring& in, std::wstring & out)
|
|
|
|
|
|
|
+ inline void convert(const wide_string_type& in, wide_string_type& out)
|
|
|
{
|
|
{
|
|
|
out = in;
|
|
out = in;
|
|
|
}
|
|
}
|
|
@@ -268,7 +269,7 @@ namespace
|
|
|
do
|
|
do
|
|
|
{
|
|
{
|
|
|
NANODBC_CALL_RC(
|
|
NANODBC_CALL_RC(
|
|
|
- NANODBC_UNICODE(SQLGetDiagRec)
|
|
|
|
|
|
|
+ NANODBC_FUNC(SQLGetDiagRec)
|
|
|
, rc
|
|
, rc
|
|
|
, handle_type
|
|
, handle_type
|
|
|
, handle
|
|
, handle
|
|
@@ -286,7 +287,7 @@ namespace
|
|
|
break;
|
|
break;
|
|
|
|
|
|
|
|
NANODBC_CALL_RC(
|
|
NANODBC_CALL_RC(
|
|
|
- NANODBC_UNICODE(SQLGetDiagRec)
|
|
|
|
|
|
|
+ NANODBC_FUNC(SQLGetDiagRec)
|
|
|
, rc
|
|
, rc
|
|
|
, handle_type
|
|
, handle_type
|
|
|
, handle
|
|
, handle
|
|
@@ -786,7 +787,7 @@ public:
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
NANODBC_CALL_RC(
|
|
NANODBC_CALL_RC(
|
|
|
- NANODBC_UNICODE(SQLConnect)
|
|
|
|
|
|
|
+ NANODBC_FUNC(SQLConnect)
|
|
|
, rc
|
|
, rc
|
|
|
, conn_
|
|
, conn_
|
|
|
, (NANODBC_SQLCHAR*)dsn.c_str(), SQL_NTS
|
|
, (NANODBC_SQLCHAR*)dsn.c_str(), SQL_NTS
|
|
@@ -838,7 +839,7 @@ public:
|
|
|
NANODBC_SQLCHAR dsn[1024];
|
|
NANODBC_SQLCHAR dsn[1024];
|
|
|
SQLSMALLINT dsn_size = 0;
|
|
SQLSMALLINT dsn_size = 0;
|
|
|
NANODBC_CALL_RC(
|
|
NANODBC_CALL_RC(
|
|
|
- NANODBC_UNICODE(SQLDriverConnect)
|
|
|
|
|
|
|
+ NANODBC_FUNC(SQLDriverConnect)
|
|
|
, rc
|
|
, rc
|
|
|
, conn_
|
|
, conn_
|
|
|
, 0
|
|
, 0
|
|
@@ -888,13 +889,49 @@ public:
|
|
|
return env_;
|
|
return env_;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ string_type dbms_name() const
|
|
|
|
|
+ {
|
|
|
|
|
+ NANODBC_SQLCHAR name[255] = { 0 };
|
|
|
|
|
+ SQLSMALLINT length(0);
|
|
|
|
|
+ RETCODE rc;
|
|
|
|
|
+ NANODBC_CALL_RC(
|
|
|
|
|
+ NANODBC_FUNC(SQLGetInfo)
|
|
|
|
|
+ , rc
|
|
|
|
|
+ , conn_
|
|
|
|
|
+ , SQL_DBMS_NAME
|
|
|
|
|
+ , name
|
|
|
|
|
+ , sizeof(name) / sizeof(NANODBC_SQLCHAR)
|
|
|
|
|
+ , &length);
|
|
|
|
|
+ if (!success(rc))
|
|
|
|
|
+ NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
|
|
|
|
|
+ return string_type(&name[0], &name[strarrlen(name)]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ string_type dbms_version() const
|
|
|
|
|
+ {
|
|
|
|
|
+ NANODBC_SQLCHAR version[255] = { 0 };
|
|
|
|
|
+ SQLSMALLINT length(0);
|
|
|
|
|
+ RETCODE rc;
|
|
|
|
|
+ NANODBC_CALL_RC(
|
|
|
|
|
+ NANODBC_FUNC(SQLGetInfo)
|
|
|
|
|
+ , rc
|
|
|
|
|
+ , conn_
|
|
|
|
|
+ , SQL_DBMS_VER
|
|
|
|
|
+ , version
|
|
|
|
|
+ , sizeof(version) / sizeof(NANODBC_SQLCHAR)
|
|
|
|
|
+ , &length);
|
|
|
|
|
+ if (!success(rc))
|
|
|
|
|
+ NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
|
|
|
|
|
+ return string_type(&version[0], &version[strarrlen(version)]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
string_type driver_name() const
|
|
string_type driver_name() const
|
|
|
{
|
|
{
|
|
|
NANODBC_SQLCHAR name[1024];
|
|
NANODBC_SQLCHAR name[1024];
|
|
|
SQLSMALLINT length;
|
|
SQLSMALLINT length;
|
|
|
RETCODE rc;
|
|
RETCODE rc;
|
|
|
NANODBC_CALL_RC(
|
|
NANODBC_CALL_RC(
|
|
|
- NANODBC_UNICODE(SQLGetInfo)
|
|
|
|
|
|
|
+ NANODBC_FUNC(SQLGetInfo)
|
|
|
, rc
|
|
, rc
|
|
|
, conn_
|
|
, conn_
|
|
|
, SQL_DRIVER_NAME
|
|
, SQL_DRIVER_NAME
|
|
@@ -916,7 +953,7 @@ public:
|
|
|
SQLSMALLINT length(0);
|
|
SQLSMALLINT length(0);
|
|
|
RETCODE rc;
|
|
RETCODE rc;
|
|
|
NANODBC_CALL_RC(
|
|
NANODBC_CALL_RC(
|
|
|
- NANODBC_UNICODE(SQLGetInfo)
|
|
|
|
|
|
|
+ NANODBC_FUNC(SQLGetInfo)
|
|
|
, rc
|
|
, rc
|
|
|
, conn_
|
|
, conn_
|
|
|
, SQL_DATABASE_NAME
|
|
, SQL_DATABASE_NAME
|
|
@@ -934,7 +971,7 @@ public:
|
|
|
SQLINTEGER length(0);
|
|
SQLINTEGER length(0);
|
|
|
RETCODE rc;
|
|
RETCODE rc;
|
|
|
NANODBC_CALL_RC(
|
|
NANODBC_CALL_RC(
|
|
|
- NANODBC_UNICODE(SQLGetConnectAttr)
|
|
|
|
|
|
|
+ NANODBC_FUNC(SQLGetConnectAttr)
|
|
|
, rc
|
|
, rc
|
|
|
, conn_
|
|
, conn_
|
|
|
, SQL_ATTR_CURRENT_CATALOG
|
|
, SQL_ATTR_CURRENT_CATALOG
|
|
@@ -1247,7 +1284,7 @@ public:
|
|
|
|
|
|
|
|
RETCODE rc;
|
|
RETCODE rc;
|
|
|
NANODBC_CALL_RC(
|
|
NANODBC_CALL_RC(
|
|
|
- NANODBC_UNICODE(SQLPrepare)
|
|
|
|
|
|
|
+ NANODBC_FUNC(SQLPrepare)
|
|
|
, rc
|
|
, rc
|
|
|
, stmt_
|
|
, stmt_
|
|
|
, (NANODBC_SQLCHAR*)query.c_str()
|
|
, (NANODBC_SQLCHAR*)query.c_str()
|
|
@@ -1392,7 +1429,7 @@ public:
|
|
|
this->timeout(timeout);
|
|
this->timeout(timeout);
|
|
|
|
|
|
|
|
NANODBC_CALL_RC(
|
|
NANODBC_CALL_RC(
|
|
|
- NANODBC_UNICODE(SQLExecDirect)
|
|
|
|
|
|
|
+ NANODBC_FUNC(SQLExecDirect)
|
|
|
, rc
|
|
, rc
|
|
|
, stmt_
|
|
, stmt_
|
|
|
, (NANODBC_SQLCHAR*)query.c_str()
|
|
, (NANODBC_SQLCHAR*)query.c_str()
|
|
@@ -1471,7 +1508,7 @@ public:
|
|
|
|
|
|
|
|
RETCODE rc;
|
|
RETCODE rc;
|
|
|
NANODBC_CALL_RC(
|
|
NANODBC_CALL_RC(
|
|
|
- NANODBC_UNICODE(SQLProcedureColumns)
|
|
|
|
|
|
|
+ NANODBC_FUNC(SQLProcedureColumns)
|
|
|
, rc
|
|
, rc
|
|
|
, stmt_
|
|
, stmt_
|
|
|
, (NANODBC_SQLCHAR*)(catalog.empty() ? NULL : catalog.c_str())
|
|
, (NANODBC_SQLCHAR*)(catalog.empty() ? NULL : catalog.c_str())
|
|
@@ -1530,6 +1567,7 @@ public:
|
|
|
{
|
|
{
|
|
|
RETCODE rc;
|
|
RETCODE rc;
|
|
|
SQLSMALLINT data_type;
|
|
SQLSMALLINT data_type;
|
|
|
|
|
+ SQLSMALLINT nullable;
|
|
|
SQLULEN parameter_size;
|
|
SQLULEN parameter_size;
|
|
|
NANODBC_CALL_RC(
|
|
NANODBC_CALL_RC(
|
|
|
SQLDescribeParam
|
|
SQLDescribeParam
|
|
@@ -1539,7 +1577,7 @@ public:
|
|
|
, &data_type
|
|
, &data_type
|
|
|
, ¶meter_size
|
|
, ¶meter_size
|
|
|
, 0
|
|
, 0
|
|
|
- , 0);
|
|
|
|
|
|
|
+ , &nullable);
|
|
|
if(!success(rc))
|
|
if(!success(rc))
|
|
|
NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
|
|
NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
|
|
|
NANODBC_ASSERT(parameter_size <= static_cast<SQLULEN>(std::numeric_limits<unsigned long>::max()));
|
|
NANODBC_ASSERT(parameter_size <= static_cast<SQLULEN>(std::numeric_limits<unsigned long>::max()));
|
|
@@ -1579,6 +1617,7 @@ public:
|
|
|
, SQLSMALLINT& scale)
|
|
, SQLSMALLINT& scale)
|
|
|
{
|
|
{
|
|
|
RETCODE rc;
|
|
RETCODE rc;
|
|
|
|
|
+ SQLSMALLINT nullable;
|
|
|
NANODBC_CALL_RC(
|
|
NANODBC_CALL_RC(
|
|
|
SQLDescribeParam
|
|
SQLDescribeParam
|
|
|
, rc
|
|
, rc
|
|
@@ -1587,7 +1626,7 @@ public:
|
|
|
, &data_type
|
|
, &data_type
|
|
|
, ¶meter_size
|
|
, ¶meter_size
|
|
|
, &scale
|
|
, &scale
|
|
|
- , 0);
|
|
|
|
|
|
|
+ , &nullable);
|
|
|
if(!success(rc))
|
|
if(!success(rc))
|
|
|
NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
|
|
NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
|
|
|
|
|
|
|
@@ -1799,12 +1838,23 @@ void statement::statement_impl::bind_strings(
|
|
|
|
|
|
|
|
if(null_sentry)
|
|
if(null_sentry)
|
|
|
{
|
|
{
|
|
|
- const string_type rhs(null_sentry);
|
|
|
|
|
for(std::size_t i = 0; i < elements; ++i)
|
|
for(std::size_t i = 0; i < elements; ++i)
|
|
|
{
|
|
{
|
|
|
- const string_type lhs(values + i * length, values + (i + 1) * length);
|
|
|
|
|
- if(NANADBC_STRNCMP(lhs.c_str(), rhs.c_str(), length))
|
|
|
|
|
- bind_len_or_null_[param][i] = parameter_size;
|
|
|
|
|
|
|
+ const string_type s_lhs(values + i * length, values + (i + 1) * length);
|
|
|
|
|
+ const string_type s_rhs(null_sentry);
|
|
|
|
|
+ #if NANODBC_USE_UNICODE
|
|
|
|
|
+ std::string narrow_lhs;
|
|
|
|
|
+ narrow_lhs.reserve(s_lhs.size());
|
|
|
|
|
+ convert(s_lhs, narrow_lhs);
|
|
|
|
|
+ std::string narrow_rhs;
|
|
|
|
|
+ narrow_rhs.reserve(s_rhs.size());
|
|
|
|
|
+ convert(s_rhs, narrow_lhs);
|
|
|
|
|
+ if(std::strncmp(narrow_lhs.c_str(), narrow_rhs.c_str(), length))
|
|
|
|
|
+ bind_len_or_null_[param][i] = parameter_size;
|
|
|
|
|
+ #else
|
|
|
|
|
+ if(std::strncmp(s_lhs.c_str(), s_rhs.c_str(), length))
|
|
|
|
|
+ bind_len_or_null_[param][i] = parameter_size;
|
|
|
|
|
+ #endif
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
else if(nulls)
|
|
else if(nulls)
|
|
@@ -2255,7 +2305,7 @@ private:
|
|
|
for(SQLSMALLINT i = 0; i < n_columns; ++i)
|
|
for(SQLSMALLINT i = 0; i < n_columns; ++i)
|
|
|
{
|
|
{
|
|
|
NANODBC_CALL_RC(
|
|
NANODBC_CALL_RC(
|
|
|
- NANODBC_UNICODE(SQLDescribeCol)
|
|
|
|
|
|
|
+ NANODBC_FUNC(SQLDescribeCol)
|
|
|
, rc
|
|
, rc
|
|
|
, stmt_.native_statement_handle()
|
|
, stmt_.native_statement_handle()
|
|
|
, i + 1
|
|
, i + 1
|
|
@@ -2353,9 +2403,6 @@ private:
|
|
|
break;
|
|
break;
|
|
|
case SQL_BINARY:
|
|
case SQL_BINARY:
|
|
|
case SQL_VARBINARY:
|
|
case SQL_VARBINARY:
|
|
|
- col.ctype_ = SQL_C_BINARY;
|
|
|
|
|
- col.clen_ = col.sqlsize_ + sizeof(NANODBC_SQLCHAR);
|
|
|
|
|
- break;
|
|
|
|
|
case SQL_LONGVARBINARY:
|
|
case SQL_LONGVARBINARY:
|
|
|
col.ctype_ = SQL_C_BINARY;
|
|
col.ctype_ = SQL_C_BINARY;
|
|
|
col.blob_ = true;
|
|
col.blob_ = true;
|
|
@@ -2422,11 +2469,11 @@ inline void result::result_impl::get_ref_impl<date>(short column, date& result)
|
|
|
switch(col.ctype_)
|
|
switch(col.ctype_)
|
|
|
{
|
|
{
|
|
|
case SQL_C_DATE:
|
|
case SQL_C_DATE:
|
|
|
- result = *((date*)(col.pdata_ + rowset_position_ * col.clen_));
|
|
|
|
|
|
|
+ result = *reinterpret_cast<date*>(col.pdata_ + rowset_position_ * col.clen_);
|
|
|
return;
|
|
return;
|
|
|
case SQL_C_TIMESTAMP:
|
|
case SQL_C_TIMESTAMP:
|
|
|
{
|
|
{
|
|
|
- timestamp stamp = *( (timestamp*)( col.pdata_ + rowset_position_ * col.clen_ ) );
|
|
|
|
|
|
|
+ timestamp stamp = *reinterpret_cast<timestamp*>(col.pdata_ + rowset_position_ * col.clen_ );
|
|
|
date d = { stamp.year, stamp.month, stamp.day };
|
|
date d = { stamp.year, stamp.month, stamp.day };
|
|
|
result = d;
|
|
result = d;
|
|
|
return;
|
|
return;
|
|
@@ -2443,13 +2490,13 @@ inline void result::result_impl::get_ref_impl<timestamp>(short column, timestamp
|
|
|
{
|
|
{
|
|
|
case SQL_C_DATE:
|
|
case SQL_C_DATE:
|
|
|
{
|
|
{
|
|
|
- date d = *((date*)(col.pdata_ + rowset_position_ * col.clen_));
|
|
|
|
|
|
|
+ date d = *reinterpret_cast<date*>(col.pdata_ + rowset_position_ * col.clen_);
|
|
|
timestamp stamp = { d.year, d.month, d.day, 0, 0, 0, 0 };
|
|
timestamp stamp = { d.year, d.month, d.day, 0, 0, 0, 0 };
|
|
|
result = stamp;
|
|
result = stamp;
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
case SQL_C_TIMESTAMP:
|
|
case SQL_C_TIMESTAMP:
|
|
|
- result = *((timestamp*)(col.pdata_ + rowset_position_ * col.clen_));
|
|
|
|
|
|
|
+ result = *reinterpret_cast<timestamp*>(col.pdata_ + rowset_position_ * col.clen_);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
throw type_incompatible_error();
|
|
throw type_incompatible_error();
|
|
@@ -2464,33 +2511,37 @@ inline void result::result_impl::get_ref_impl<string_type>(short column, string_
|
|
|
switch(col.ctype_)
|
|
switch(col.ctype_)
|
|
|
{
|
|
{
|
|
|
case SQL_C_CHAR:
|
|
case SQL_C_CHAR:
|
|
|
|
|
+ case SQL_C_BINARY:
|
|
|
{
|
|
{
|
|
|
if(col.blob_)
|
|
if(col.blob_)
|
|
|
{
|
|
{
|
|
|
- // Input is always std::string, while output may be std::string or std::wstring
|
|
|
|
|
- std::stringstream ss;
|
|
|
|
|
- char buff[1024] = {0};
|
|
|
|
|
- std::size_t buff_size = sizeof(buff);
|
|
|
|
|
|
|
+ // Input is always std::string, while output may be std::string or wide_string_type
|
|
|
|
|
+ std::string out;
|
|
|
SQLLEN ValueLenOrInd;
|
|
SQLLEN ValueLenOrInd;
|
|
|
SQLRETURN rc;
|
|
SQLRETURN rc;
|
|
|
void* handle = native_statement_handle();
|
|
void* handle = native_statement_handle();
|
|
|
do
|
|
do
|
|
|
{
|
|
{
|
|
|
|
|
+ char buffer[1024] = {0};
|
|
|
|
|
+ const std::size_t buffer_size = sizeof(buffer);
|
|
|
NANODBC_CALL_RC(
|
|
NANODBC_CALL_RC(
|
|
|
SQLGetData
|
|
SQLGetData
|
|
|
, rc
|
|
, rc
|
|
|
, handle // StatementHandle
|
|
, handle // StatementHandle
|
|
|
, column + 1 // Col_or_Param_Num
|
|
, column + 1 // Col_or_Param_Num
|
|
|
- , SQL_C_CHAR // TargetType
|
|
|
|
|
- , buff // TargetValuePtr
|
|
|
|
|
- , buff_size // BufferLength
|
|
|
|
|
|
|
+ , col.ctype_ // TargetType
|
|
|
|
|
+ , buffer // TargetValuePtr
|
|
|
|
|
+ , buffer_size - 1 // BufferLength
|
|
|
, &ValueLenOrInd); // StrLen_or_IndPtr
|
|
, &ValueLenOrInd); // StrLen_or_IndPtr
|
|
|
if(ValueLenOrInd > 0)
|
|
if(ValueLenOrInd > 0)
|
|
|
- ss << buff;
|
|
|
|
|
|
|
+ out.append(buffer);
|
|
|
else if(ValueLenOrInd == SQL_NULL_DATA)
|
|
else if(ValueLenOrInd == SQL_NULL_DATA)
|
|
|
*col.cbdata_ = (SQLINTEGER) SQL_NULL_DATA;
|
|
*col.cbdata_ = (SQLINTEGER) SQL_NULL_DATA;
|
|
|
- } while(rc > 0);
|
|
|
|
|
- convert(ss.str(), result);
|
|
|
|
|
|
|
+ // Sequence of successful calls is:
|
|
|
|
|
+ // SQL_NO_DATA or SQL_SUCCESS_WITH_INFO followed by SQL_SUCCESS.
|
|
|
|
|
+ } while(rc == SQL_SUCCESS_WITH_INFO);
|
|
|
|
|
+ if (rc == SQL_SUCCESS || rc == SQL_NO_DATA)
|
|
|
|
|
+ convert(out, result);
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
@@ -2505,48 +2556,48 @@ inline void result::result_impl::get_ref_impl<string_type>(short column, string_
|
|
|
{
|
|
{
|
|
|
if(col.blob_)
|
|
if(col.blob_)
|
|
|
{
|
|
{
|
|
|
- // Input is always std::wstring, output might be std::string or std::wstring.
|
|
|
|
|
|
|
+ // Input is always wide_string_type, output might be std::string or wide_string_type.
|
|
|
// Use a string builder to build the output string.
|
|
// Use a string builder to build the output string.
|
|
|
- std::wstringstream ss;
|
|
|
|
|
- wchar_t buffer[512] = {0};
|
|
|
|
|
- std::size_t buffer_size = sizeof(buffer);
|
|
|
|
|
|
|
+ wide_string_type out;
|
|
|
SQLLEN ValueLenOrInd;
|
|
SQLLEN ValueLenOrInd;
|
|
|
SQLRETURN rc;
|
|
SQLRETURN rc;
|
|
|
void* handle = native_statement_handle();
|
|
void* handle = native_statement_handle();
|
|
|
do
|
|
do
|
|
|
{
|
|
{
|
|
|
|
|
+ wide_char_t buffer[512] = {0};
|
|
|
|
|
+ const std::size_t buffer_size = sizeof(buffer);
|
|
|
NANODBC_CALL_RC(
|
|
NANODBC_CALL_RC(
|
|
|
SQLGetData
|
|
SQLGetData
|
|
|
, rc
|
|
, rc
|
|
|
, handle // StatementHandle
|
|
, handle // StatementHandle
|
|
|
, column + 1 // Col_or_Param_Num
|
|
, column + 1 // Col_or_Param_Num
|
|
|
- , SQL_C_WCHAR // TargetType
|
|
|
|
|
|
|
+ , col.ctype_ // TargetType
|
|
|
, buffer // TargetValuePtr
|
|
, buffer // TargetValuePtr
|
|
|
- , buffer_size // BufferLength
|
|
|
|
|
|
|
+ , buffer_size - 1 // BufferLength
|
|
|
, &ValueLenOrInd); // StrLen_or_IndPtr
|
|
, &ValueLenOrInd); // StrLen_or_IndPtr
|
|
|
if(ValueLenOrInd > 0)
|
|
if(ValueLenOrInd > 0)
|
|
|
- ss << buffer;
|
|
|
|
|
|
|
+ out.append(buffer);
|
|
|
else if(ValueLenOrInd == SQL_NULL_DATA)
|
|
else if(ValueLenOrInd == SQL_NULL_DATA)
|
|
|
*col.cbdata_ = (SQLINTEGER) SQL_NULL_DATA;
|
|
*col.cbdata_ = (SQLINTEGER) SQL_NULL_DATA;
|
|
|
- } while(rc > 0);
|
|
|
|
|
- convert(ss.str(), result);
|
|
|
|
|
|
|
+ // Sequence of successful calls is:
|
|
|
|
|
+ // SQL_NO_DATA or SQL_SUCCESS_WITH_INFO followed by SQL_SUCCESS.
|
|
|
|
|
+ } while(rc == SQL_SUCCESS_WITH_INFO);
|
|
|
|
|
+ if (rc == SQL_SUCCESS || rc == SQL_NO_DATA)
|
|
|
|
|
+ convert(out, result);
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
// Type is unicode in the database, convert if necessary
|
|
// Type is unicode in the database, convert if necessary
|
|
|
const SQLWCHAR* s = reinterpret_cast<SQLWCHAR*>(col.pdata_ + rowset_position_ * col.clen_);
|
|
const SQLWCHAR* s = reinterpret_cast<SQLWCHAR*>(col.pdata_ + rowset_position_ * col.clen_);
|
|
|
const string_type::size_type str_size = *col.cbdata_ / sizeof(SQLWCHAR);
|
|
const string_type::size_type str_size = *col.cbdata_ / sizeof(SQLWCHAR);
|
|
|
- std::wstring temp(s, s + str_size);
|
|
|
|
|
|
|
+ wide_string_type temp(s, s + str_size);
|
|
|
convert(temp, result);
|
|
convert(temp, result);
|
|
|
}
|
|
}
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
case SQL_C_GUID:
|
|
case SQL_C_GUID:
|
|
|
- case SQL_C_BINARY:
|
|
|
|
|
{
|
|
{
|
|
|
- if(col.blob_)
|
|
|
|
|
- throw std::runtime_error("blob not implemented yet");
|
|
|
|
|
const char* s = col.pdata_ + rowset_position_ * col.clen_;
|
|
const char* s = col.pdata_ + rowset_position_ * col.clen_;
|
|
|
result.assign(s, s + column_size);
|
|
result.assign(s, s + column_size);
|
|
|
return;
|
|
return;
|
|
@@ -2554,81 +2605,106 @@ inline void result::result_impl::get_ref_impl<string_type>(short column, string_
|
|
|
|
|
|
|
|
case SQL_C_LONG:
|
|
case SQL_C_LONG:
|
|
|
{
|
|
{
|
|
|
- result.resize(column_size);
|
|
|
|
|
- if(NANODBC_SNPRINTF(
|
|
|
|
|
- const_cast<string_type::value_type*>(result.data())
|
|
|
|
|
- , column_size
|
|
|
|
|
- , NANODBC_TEXT("%d")
|
|
|
|
|
- , *(int32_t*)(col.pdata_ + rowset_position_ * col.clen_)) == -1)
|
|
|
|
|
- throw type_incompatible_error();
|
|
|
|
|
- result.resize(NANODBC_STRLEN(result.c_str()));
|
|
|
|
|
- return;
|
|
|
|
|
|
|
+ std::string buffer;
|
|
|
|
|
+ buffer.reserve(column_size + 1); // ensure terminating null
|
|
|
|
|
+ buffer.resize(buffer.capacity());
|
|
|
|
|
+ using std::fill;
|
|
|
|
|
+ fill(buffer.begin(), buffer.end(), '\0');
|
|
|
|
|
+ const wide_char_t data = *reinterpret_cast<wide_char_t*>(col.pdata_ + rowset_position_ * col.clen_);
|
|
|
|
|
+ const int bytes = std::snprintf(const_cast<char*>(buffer.data()), column_size, "%d", data);
|
|
|
|
|
+ if(bytes == -1)
|
|
|
|
|
+ throw type_incompatible_error();
|
|
|
|
|
+ else if((SQLULEN)bytes < column_size)
|
|
|
|
|
+ buffer.resize(bytes);
|
|
|
|
|
+ buffer.resize(std::strlen(buffer.data())); // drop any trailing nulls
|
|
|
|
|
+ result.reserve(buffer.size() * sizeof(string_type::value_type));
|
|
|
|
|
+ convert(buffer, result);
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
case SQL_C_SBIGINT:
|
|
case SQL_C_SBIGINT:
|
|
|
{
|
|
{
|
|
|
using namespace std; // in case intmax_t is in namespace std
|
|
using namespace std; // in case intmax_t is in namespace std
|
|
|
- result.resize(column_size);
|
|
|
|
|
- if(NANODBC_SNPRINTF(
|
|
|
|
|
- const_cast<string_type::value_type*>(result.data())
|
|
|
|
|
- , column_size
|
|
|
|
|
- , NANODBC_TEXT("%jd")
|
|
|
|
|
- , (intmax_t) *(int64_t*)(col.pdata_ + rowset_position_ * col.clen_)) == -1)
|
|
|
|
|
|
|
+ std::string buffer;
|
|
|
|
|
+ buffer.reserve(column_size + 1); // ensure terminating null
|
|
|
|
|
+ buffer.resize(buffer.capacity());
|
|
|
|
|
+ using std::fill;
|
|
|
|
|
+ fill(buffer.begin(), buffer.end(), '\0');
|
|
|
|
|
+ const intmax_t data = (intmax_t)*reinterpret_cast<int64_t*>(col.pdata_ + rowset_position_ * col.clen_);
|
|
|
|
|
+ const int bytes = std::snprintf(const_cast<char*>(buffer.data()), column_size, "%jd", data);
|
|
|
|
|
+ if(bytes == -1)
|
|
|
throw type_incompatible_error();
|
|
throw type_incompatible_error();
|
|
|
- result.resize(NANODBC_STRLEN(result.c_str()));
|
|
|
|
|
|
|
+ else if((SQLULEN)bytes < column_size)
|
|
|
|
|
+ buffer.resize(bytes);
|
|
|
|
|
+ buffer.resize(std::strlen(buffer.data())); // drop any trailing nulls
|
|
|
|
|
+ result.reserve(buffer.size() * sizeof(string_type::value_type));
|
|
|
|
|
+ convert(buffer, result);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
case SQL_C_FLOAT:
|
|
case SQL_C_FLOAT:
|
|
|
{
|
|
{
|
|
|
- result.resize(column_size);
|
|
|
|
|
- if(NANODBC_SNPRINTF(
|
|
|
|
|
- const_cast<string_type::value_type*>(result.data())
|
|
|
|
|
- , column_size
|
|
|
|
|
- , NANODBC_TEXT("%f")
|
|
|
|
|
- , *(float*)(col.pdata_ + rowset_position_ * col.clen_)) == -1)
|
|
|
|
|
|
|
+ std::string buffer;
|
|
|
|
|
+ buffer.reserve(column_size + 1); // ensure terminating null
|
|
|
|
|
+ buffer.resize(buffer.capacity());
|
|
|
|
|
+ using std::fill;
|
|
|
|
|
+ fill(buffer.begin(), buffer.end(), '\0');
|
|
|
|
|
+ const float data = *reinterpret_cast<float*>(col.pdata_ + rowset_position_ * col.clen_);
|
|
|
|
|
+ const int bytes = std::snprintf(const_cast<char*>(buffer.data()), column_size, "%f", data);
|
|
|
|
|
+ if(bytes == -1)
|
|
|
throw type_incompatible_error();
|
|
throw type_incompatible_error();
|
|
|
- result.resize(NANODBC_STRLEN(result.c_str()));
|
|
|
|
|
|
|
+ else if((SQLULEN)bytes < column_size)
|
|
|
|
|
+ buffer.resize(bytes);
|
|
|
|
|
+ buffer.resize(std::strlen(buffer.data())); // drop any trailing nulls
|
|
|
|
|
+ result.reserve(buffer.size() * sizeof(string_type::value_type));
|
|
|
|
|
+ convert(buffer, result);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
case SQL_C_DOUBLE:
|
|
case SQL_C_DOUBLE:
|
|
|
{
|
|
{
|
|
|
- result.resize(column_size + 2); // account for decimal mark and sign
|
|
|
|
|
- if(NANODBC_SNPRINTF(
|
|
|
|
|
- const_cast<string_type::value_type*>(result.data())
|
|
|
|
|
- , column_size + 2
|
|
|
|
|
- , NANODBC_TEXT("%.*lf") // restrict the number of digits
|
|
|
|
|
|
|
+ std::string buffer;
|
|
|
|
|
+ const SQLULEN width = column_size + 2; // account for decimal mark and sign
|
|
|
|
|
+ buffer.reserve(width + 1); // ensure terminating null
|
|
|
|
|
+ buffer.resize(buffer.capacity());
|
|
|
|
|
+ using std::fill;
|
|
|
|
|
+ fill(buffer.begin(), buffer.end(), '\0');
|
|
|
|
|
+ const double data = *reinterpret_cast<double*>(col.pdata_ + rowset_position_ * col.clen_);
|
|
|
|
|
+ const int bytes = std::snprintf(
|
|
|
|
|
+ const_cast<char*>(buffer.data())
|
|
|
|
|
+ , width
|
|
|
|
|
+ , "%.*lf" // restrict the number of digits
|
|
|
, col.scale_ // number of digits after the decimal point
|
|
, col.scale_ // number of digits after the decimal point
|
|
|
- , *(double*)(col.pdata_ + rowset_position_ * col.clen_)) == -1)
|
|
|
|
|
- throw type_incompatible_error();
|
|
|
|
|
- result.resize(NANODBC_STRLEN(result.c_str()));
|
|
|
|
|
|
|
+ , data);
|
|
|
|
|
+ if(bytes == -1)
|
|
|
|
|
+ throw type_incompatible_error();
|
|
|
|
|
+ else if((SQLULEN)bytes < column_size)
|
|
|
|
|
+ buffer.resize(bytes);
|
|
|
|
|
+ buffer.resize(std::strlen(buffer.data())); // drop any trailing nulls
|
|
|
|
|
+ result.reserve(buffer.size() * sizeof(string_type::value_type));
|
|
|
|
|
+ convert(buffer, result);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
case SQL_C_DATE:
|
|
case SQL_C_DATE:
|
|
|
{
|
|
{
|
|
|
- date d = *((date*)(col.pdata_ + rowset_position_ * col.clen_));
|
|
|
|
|
|
|
+ const date d = *reinterpret_cast<date*>(col.pdata_ + rowset_position_ * col.clen_);
|
|
|
std::tm st = { 0 };
|
|
std::tm st = { 0 };
|
|
|
st.tm_year = d.year - 1900;
|
|
st.tm_year = d.year - 1900;
|
|
|
st.tm_mon = d.month - 1;
|
|
st.tm_mon = d.month - 1;
|
|
|
st.tm_mday = d.day;
|
|
st.tm_mday = d.day;
|
|
|
char* old_lc_time = std::setlocale(LC_TIME, NULL);
|
|
char* old_lc_time = std::setlocale(LC_TIME, NULL);
|
|
|
std::setlocale(LC_TIME, "");
|
|
std::setlocale(LC_TIME, "");
|
|
|
- string_type::value_type date_str[512];
|
|
|
|
|
- NANODBC_STRFTIME(
|
|
|
|
|
- date_str
|
|
|
|
|
- , sizeof(date_str) / sizeof(string_type::value_type)
|
|
|
|
|
- , NANODBC_TEXT("%Y-%m-%d")
|
|
|
|
|
- , &st);
|
|
|
|
|
|
|
+ char date_str[512];
|
|
|
|
|
+ std::strftime(date_str, sizeof(date_str), "%Y-%m-%d", &st);
|
|
|
std::setlocale(LC_TIME, old_lc_time);
|
|
std::setlocale(LC_TIME, old_lc_time);
|
|
|
- result.assign(date_str);
|
|
|
|
|
|
|
+ convert(date_str, result);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
case SQL_C_TIMESTAMP:
|
|
case SQL_C_TIMESTAMP:
|
|
|
{
|
|
{
|
|
|
- timestamp stamp = *((timestamp*)(col.pdata_ + rowset_position_ * col.clen_));
|
|
|
|
|
|
|
+ const timestamp stamp = *reinterpret_cast<timestamp*>(col.pdata_ + rowset_position_ * col.clen_);
|
|
|
std::tm st = { 0 };
|
|
std::tm st = { 0 };
|
|
|
st.tm_year = stamp.year - 1900;
|
|
st.tm_year = stamp.year - 1900;
|
|
|
st.tm_mon = stamp.month - 1;
|
|
st.tm_mon = stamp.month - 1;
|
|
@@ -2638,15 +2714,11 @@ inline void result::result_impl::get_ref_impl<string_type>(short column, string_
|
|
|
st.tm_sec = stamp.sec;
|
|
st.tm_sec = stamp.sec;
|
|
|
char* old_lc_time = std::setlocale(LC_TIME, NULL);
|
|
char* old_lc_time = std::setlocale(LC_TIME, NULL);
|
|
|
std::setlocale(LC_TIME, "");
|
|
std::setlocale(LC_TIME, "");
|
|
|
- string_type::value_type date_str[512];
|
|
|
|
|
- NANODBC_STRFTIME(
|
|
|
|
|
- date_str
|
|
|
|
|
- , sizeof(date_str) / sizeof(string_type::value_type)
|
|
|
|
|
- , NANODBC_TEXT("%Y-%m-%d %H:%M:%S %z")
|
|
|
|
|
- , &st);
|
|
|
|
|
|
|
+ char date_str[512];
|
|
|
|
|
+ std::strftime(date_str, sizeof(date_str), "%Y-%m-%d %H:%M:%S %z", &st);
|
|
|
std::setlocale(LC_TIME, old_lc_time);
|
|
std::setlocale(LC_TIME, old_lc_time);
|
|
|
- result.assign(date_str);
|
|
|
|
|
- return;
|
|
|
|
|
|
|
+ convert(date_str, result);
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
throw type_incompatible_error();
|
|
throw type_incompatible_error();
|
|
@@ -2859,6 +2931,16 @@ void* connection::native_env_handle() const
|
|
|
return impl_->native_env_handle();
|
|
return impl_->native_env_handle();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+string_type connection::dbms_name() const
|
|
|
|
|
+{
|
|
|
|
|
+ return impl_->dbms_name();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+string_type connection::dbms_version() const
|
|
|
|
|
+{
|
|
|
|
|
+ return impl_->dbms_version();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
string_type connection::driver_name() const
|
|
string_type connection::driver_name() const
|
|
|
{
|
|
{
|
|
|
return impl_->driver_name();
|
|
return impl_->driver_name();
|
|
@@ -3475,8 +3557,12 @@ long catalog::columns::ordinal_position() const
|
|
|
|
|
|
|
|
string_type catalog::columns::is_nullable() const
|
|
string_type catalog::columns::is_nullable() const
|
|
|
{
|
|
{
|
|
|
- // IS_NULLABLE is never NULL
|
|
|
|
|
- return result_.get<string_type>(17);
|
|
|
|
|
|
|
+ // IS_NULLABLE might be NULL
|
|
|
|
|
+
|
|
|
|
|
+ // MSDN: This column returns a zero-length string if nullability is unknown.
|
|
|
|
|
+ // ISO rules are followed to determine nullability.
|
|
|
|
|
+ // An ISO SQL-compliant DBMS cannot return an empty string.
|
|
|
|
|
+ return result_.get<string_type>(17, string_type());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
catalog::catalog(connection& conn)
|
|
catalog::catalog(connection& conn)
|
|
@@ -3493,7 +3579,7 @@ catalog::tables catalog::find_tables(
|
|
|
statement stmt(conn_);
|
|
statement stmt(conn_);
|
|
|
RETCODE rc;
|
|
RETCODE rc;
|
|
|
NANODBC_CALL_RC(
|
|
NANODBC_CALL_RC(
|
|
|
- NANODBC_UNICODE(SQLTables)
|
|
|
|
|
|
|
+ NANODBC_FUNC(SQLTables)
|
|
|
, rc
|
|
, rc
|
|
|
, stmt.native_statement_handle()
|
|
, stmt.native_statement_handle()
|
|
|
, (NANODBC_SQLCHAR*)(catalog.empty() ? NULL : catalog.c_str())
|
|
, (NANODBC_SQLCHAR*)(catalog.empty() ? NULL : catalog.c_str())
|
|
@@ -3520,7 +3606,7 @@ catalog::columns catalog::find_columns(
|
|
|
statement stmt(conn_);
|
|
statement stmt(conn_);
|
|
|
RETCODE rc;
|
|
RETCODE rc;
|
|
|
NANODBC_CALL_RC(
|
|
NANODBC_CALL_RC(
|
|
|
- NANODBC_UNICODE(SQLColumns)
|
|
|
|
|
|
|
+ NANODBC_FUNC(SQLColumns)
|
|
|
, rc
|
|
, rc
|
|
|
, stmt.native_statement_handle()
|
|
, stmt.native_statement_handle()
|
|
|
, (NANODBC_SQLCHAR*)(catalog.empty() ? NULL : catalog.c_str())
|
|
, (NANODBC_SQLCHAR*)(catalog.empty() ? NULL : catalog.c_str())
|
|
@@ -3546,7 +3632,7 @@ catalog::primary_keys catalog::find_primary_keys(
|
|
|
statement stmt(conn_);
|
|
statement stmt(conn_);
|
|
|
RETCODE rc;
|
|
RETCODE rc;
|
|
|
NANODBC_CALL_RC(
|
|
NANODBC_CALL_RC(
|
|
|
- NANODBC_UNICODE(SQLPrimaryKeys)
|
|
|
|
|
|
|
+ NANODBC_FUNC(SQLPrimaryKeys)
|
|
|
, rc
|
|
, rc
|
|
|
, stmt.native_statement_handle()
|
|
, stmt.native_statement_handle()
|
|
|
, (NANODBC_SQLCHAR*)(catalog.empty() ? NULL : catalog.c_str())
|
|
, (NANODBC_SQLCHAR*)(catalog.empty() ? NULL : catalog.c_str())
|