GithubHelp home page GithubHelp logo

nanodbc / nanodbc Goto Github PK

View Code? Open in Web Editor NEW
318.0 17.0 80.0 10.88 MB

A small C++ wrapper for the native C ODBC API | Requires C++14 since v2.12

Home Page: http://nanodbc.io

License: MIT License

CMake 0.93% C++ 98.32% Batchfile 0.21% Shell 0.38% Dockerfile 0.16%
odbc database-access database-access-library odbc-api sqlite3 mysql sqlserver postgresql vertica cplusplus

nanodbc's Introduction

nanodbc

nanodbc-banner

A small C++ wrapper for the native C ODBC API. Please see the online documentation for user information, example usage, propaganda, and detailed source level documentation.

GitHub release GitHub commits License

Gitter

Build Status

Branch Linux/OSX Windows Coverage Coverity
main main main codecov coverity_scan

Note: The Coverity status uses the coverity_scan branch. When main has had a significant amount of work pushed to it, merge those changes into coverity_scan as well to keep the status up to date.

Philosophy

The native C API for working with ODBC is exorbitantly verbose, ridiculously complicated, and fantastically brittle. nanodbc addresses these frustrations! The goal for nanodbc is to make developers happy. Common tasks should be easy, requiring concise and simple code.

The latest C++ standards and best practices are enthusiastically incorporated to make the library as future-proof as possible. To accommodate users who can not use the latest and greatest, semantic versioning and release notes will clarify required C++ features and/or standards for particular versions.

Design Decisions

All complex objects in nanodbc follow the pimpl (Pointer to IMPLementation) idiom to provide separation between interface and implementation, value semantics, and a clean nanodbc.h header file that includes nothing but standard C++ headers.

nanodbc wraps ODBC code, providing a simpler way to do the same thing. We try to be as featureful as possible, but I can't guarantee you'll never have to write supporting ODBC code. Personally, I have never had to do so.

Major features beyond what's already supported by ODBC are not within the scope of nanodbc. This is where the nano part of nanodbc becomes relevant: This library is as minimal as possible. That means no dependencies beyond standard C++ and typical ODBC headers and libraries to link against. No features unsupported by existing ODBC API calls.

Building

nanodbc is intentionally small enough that you can drag and drop the header and implementation files into your project and run with it. For those that want it, I have also provided CMake files which build a library object, or build and run the included tests. The CMake files will also support out of source builds.

Tests use the Catch test framework, and CMake will automatically fetch the latest version of Catch for you at build time. To build the nanodbc and the tests you will also need to have either unixODBC or iODBC installed and discoverable by CMake. This is easy on OS X where you can use Homebrew to install unixODBC with brew install unixodbc, or use the system provided iODBC if you have OS X 10.9 or earlier.

The tests attempt to connect to a SQLite database, so you will have to have that and a SQLite ODBC driver installed. At the time of this writing, there happens to be a nice SQLite ODBC driver available from Christian Werner's website, also available via Homebrew as sqliteobdc! The tests expect to find a data source named sqlite on *nix systems and SQLite3 ODBC Driver on Windows systems. For example, your odbcinst.ini file on OS X must have a section like the following.

[sqlite]
Description             = SQLite3 ODBC Driver
Setup                   = /usr/lib/libsqlite3odbc-0.93.dylib
Driver                  = /usr/lib/libsqlite3odbc-0.93.dylib
Threading               = 2

Example Build Process

It's most convenient to create a build directory for an out of source build, but this isn't required. After you've used cmake to generate your Makefiles, make nanodbc will build your shared object. make check will build and run the tests. You can also install nanodbc to your system using make install.

If the tests fail, please don't hesitate to report it by creating an issue with your detailed test log (prepend your make command with env CTEST_OUTPUT_ON_FAILURE=1 to enable verbose output please).

cd path/to/nanodbc/repository
mkdir build
cd build
cmake [Build Options] ..
make # creates shared library
make nanodbc # creates shared library
make tests # builds the tests
make test # runs the tests
make check # builds and then runs tests
make examples # builds all the example programs
make install # installs nanodbc.h and shared library

Build Options

The following build options are available via CMake command-line option -D. If you are not using CMake to build nanodbc, you will need to set the corresponding -D compile define flags yourself.

All boolean options follow the CMake OPTION default value convention: if no initial value is provided, OFF is used.

Use the standard CMake option -DBUILD_SHARED_LIBS=ON to build nanodbc as shared library.

If you need to use the NANODBC_ENABLE_BOOST=ON option, you will have to configure your environment to use Boost.

CMake Option Possible Values Details
NANODBC_DISABLE_ASYNC OFF or ON Disable all async features. May resolve build issues in older ODBC versions.
NANODBC_DISABLE_EXAMPLES OFF or ON Do not build examples.
NANODBC_DISABLE_INSTALL OFF or ON Do not generate install target.
NANODBC_DISABLE_LIBCXX OFF or ON Do not use libc++, if available on the system.
NANODBC_DISABLE_TESTS OFF or ON Do not build tests.
NANODBC_ENABLE_BOOST OFF or ON Use Boost for Unicode string convertions (requires Boost.Locale). Workaround to issue #24.
NANODBC_ENABLE_UNICODE OFF or ON Enable Unicode support. nanodbc::string becomes std::u16string or std::u32string.
NANODBC_ENABLE_WORKAROUND_NODATA OFF or ON Enable SQL_NO_DATA workaround to issue #43.
NANODBC_OVERALLOCATE_CHAR OFF or ON Overallocate auto-bound n/var/char buffers to accomodate retrieving Unicode data in VARCHAR columns #219.
NANODBC_ODBC_VERSION SQL_OV_ODBC3[...] Forces ODBC version to use. Default is SQL_OV_ODBC3_80 if available, otherwise SQL_OV_ODBC3.

Note About iODBC

Under Windows sizeof(wchar_t) == sizeof(SQLWCHAR) == 2, yet on Unix systems sizeof(wchar_t) == 4. On unixODBC, sizeof(SQLWCHAR) == 2 while on iODBC, sizeof(SQLWCHAR) == sizeof(wchar_t) == 4. This leads to incompatible ABIs between applications and drivers. If building against iODBC and the build option NANODBC_USE_UNICODE is ON, then nanodbc::string will be std::u32string. In ALL other cases it will be std::u16string.

The CI builds do not exercise a Unicode-enabled iODBC driver. As such there is no guarantee that tests will pass in entirety on a system using iODBC. My recommendation is to use unixODBC. If you must use iODBC, consider disabling unicode mode to avoid wchar_t issues.


Contributing

Code Style

clang-format version 15 handles all C++ code formatting for nanodbc. See our .clang-format configuration file for details on the style and currently required version of clang-format specified in the comment at the top of the file The script utility/style.sh formats all code in the repository automatically.

To run clang-format against the whole nanodbc codebase:

./utility/style.sh

To run clang-format on a single file use the following.

clang-format -i /path/to/file

Please auto-format all code submitted in Pull Requests.

Source Level Documentation

Source level documentation provided via GitHub's gh-pages is available at nanodbc.io. To re-build and update it, preform the following steps from the doc/README.md file.

Quick Setup for Testing or Development Environments

To get up and running with nanodbc as fast as possible consider using the provided Dockerfile and docker-compose.yml or Vagrantfile.

For example, to spin up a docker container suitable for testing and development of nanodbc:

cd /path/to/nanodbc
docker build -t nanodbc .

# Use container local nanodbc repository
docker run -it nanodbc /bin/bash
root@hash:/# mkdir -p /opt/nanodbc/build && cd /opt/nanodbc-host/build

# Alternatively, bind host repository as container volume
docker run -v "$(pwd)":"/opt/$(basename $(pwd))-host" -it nanodbc /bin/bash
root@hash:/# mkdir -p /opt/nanodbc-host/build && cd /opt/nanodbc-host/build

root@hash:/opt/nanodbc-host/build# cmake ..
root@hash:/opt/nanodbc-host/build# make nanodbc

Or, spin up the complete multi-container environment with database services:

cd /path/to/nanodbc
docker-compose build
docker-compose up -d
docker exec -it nanodbc /bin/bash

Or, to build and ssh into a vagrant VM (using VirtualBox for example) use:

cd /path/to/nanodbc
vagrant up
vagrant ssh
vagrant@vagrant-ubuntu-precise-64:~$ git clone https://github.com/nanodbc/nanodbc.git
vagrant@vagrant-ubuntu-precise-64:~$ mkdir -p nanodbc/build && cd nanodbc/build
vagrant@vagrant-ubuntu-precise-64:~$ CXX=g++-5 cmake ..
vagrant@vagrant-ubuntu-precise-64:~$ make nanodbc

Tests

One of important objectives is to maintain nanodbc covered with tests. New contributions submitted via Pull Requests must include corresponding tests. This is important to ensure the quality of new features.

The good news is that adding tests is easy!

The tests structure:

  • tests/base_test_fixture.h includes a set of common test cases.
  • tests/<database>_test.cpp is a source code for an independent test program that includes both, common and database-specific test cases.

To add new test case:

  1. In tests/base_test_fixture.h file, add a new test case method to base_test_fixture class (e.g. void my_feature_test()).
  2. In each tests/<database>_test.cpp file, copy and paste the TEST_CASE_METHOD boilerplate, updating name, tags, etc.

If a feature requires a database-specific test case for each database, then skip the tests/base_test_fixture.h step and write a dedicated test case directly in tests/<database>_test.cpp file.

Publish and Release Process

Once your local main branch is ready for publishing (i.e. semantic versioning), use the utility/publish.sh script. This script bumps the major, minor, or patch version, then updates the repository's VERSION.txt file, adds a "Preparing" commit, and creates git tags appropriately. For example to make a minor update you would run ./utility/publish.sh minor. Review files of CMake configuration, documentation and Sphinx configuration, and update version number wherever necessary.

Important: Always update CHANGELOG.md with information about new changes, bug fixes, and features when making a new release. Use the ./utility/changes.sh script to aid in your composition of this document. The publish script itself will attempt to verify that the changelog file has been properly updated.

To do this manually instead, use the following steps — for example a minor update from 2.9.x to 2.10.0:

  1. echo "2.10.0" > VERSION.txt
  2. git add VERSION.txt
  3. git commit -m "Preparing 2.10.0 release."
  4. git tag -f "v2.10.0"
  5. git push -f origin "v2.10.0"

Next, switch to gh-pages branch, build latest documentation, commit and push.

Finally, announce the new release to the public.

Future work

Good to Have / Want Someday

  • Refactor tests to follow BDD pattern.
  • Update codebase to use more C++14 idioms and patterns.
  • Write more tests with the goal to have much higher code coverage.
  • More tests for a large variety of drivers. Include performance tests.
  • Clean up bind_* family of functions, reduce any duplication.
  • Improve documentation: The main website and API docs should be more responsive.
  • Provide more examples in documentation, more details, and point out any gotchas.
  • Versioned generated source level API documentation for matesr and previous releases.
  • Add "HOWTO Build" documentation for Windows, OS X, and Linux.

MIT © lexicalunit, mloskot and contributors.

nanodbc's People

Contributors

1015onasaturdaynight avatar angelcarro avatar billyoneal avatar ctrychta avatar dand-oss avatar detule avatar disog avatar gabm avatar h1x4dev avatar jimhester avatar jon-v avatar kentf avatar lexicalunit avatar manwithahammer avatar matejbrumen avatar mcg1969 avatar michalpalkamobica avatar mkaes avatar mloskot avatar mpalka31 avatar nimish avatar pepr avatar rafeememon avatar shelnutt2 avatar sphawk avatar thermox360 avatar tolonuk avatar traceon avatar weitjong avatar wimobiwan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nanodbc's Issues

Add tests for SQL integer types

From @mloskot on April 27, 2017 7:5

There is need for better tests coverage for SQL integer types, standard and vendor-specific too.

Each of the types like TINYINT, SMALLINT, INT, BIGINT etc. should be covered with complete SQL statements chain: CREATAE TABLE, INSERT INTO and SELECT together with C++ to/from SQL integer conversions (eg. bind/get int to TINYINT).

Copied from original issue: lexicalunit/nanodbc#263

auto_bind corrections, additional warning suppression

From @mcg1969 on January 18, 2014 23:50

I think this is a bug fix I offered up before, but I got focused on other things and didn't get it done.

First, I added an additional disabled warning (4996, about _snprintf), and bumped up the version to _MSC_VER <= 1600, so Visual C++ 2010 would benefit from it. I still have to use that old clunky version.

Now the big one:, in auto_bind, the character data needs to be allocated in SQL data sizes. So SQL_CHAR needs to be size SQLCHAR, even if you're ultimately going to Unicode, since you're setting col.ctype_ = SQL_C_CHAR. Likewise, for SQL_WCHAR, the size needs to be SQLWCHAR, since col.ctype_ = SQL_C_WCHAR.

The way you had it before, you weren't allocating enough space for SQLWCHAR data, and therefore string data was getting overlaid on top of each other, corrupting the results.

Now, I think you could do it another way, if you wanted, and let SQL do all the translation. To do that, you would actually do something like this, I think.

case SQL_CHAR:
case SQL_VARCHAR:
case SQL_WCHAR:
case SQL_WVARCHAR:
    #ifdef NANODBC_USE_UNICODE
        col.ctype_ = SQL_C_WCHAR;
    #else
        col.ctype_ = SQL_C_CHAR;
    #endif
    col.clen_ = ( col.sqlsize_ + 1 ) * sizeof(NANODBC_SQLCHAR);

I don't know this for sure; I haven't tried it! I might though. If I do I'll let you know. The disadvantage here is that you're committing to one string type in your code at compile time. In theory, your current setup, combined with my pull request, lets you mix and match narrow and wide char strings in the same application, as you choose.

Copied from original issue: lexicalunit/nanodbc/pull/18

GCC 4.9 - codecvt not found

From @kowalikm on August 27, 2015 13:55

Hello,

i have problem with compilation on Debian GCC 4.9:

fatal error: codecvt: Not found
#include <codecvt>
                   ^
compilation terminated.

AFAIK codecvt isn't supported by GCC yet.

Copied from original issue: lexicalunit/nanodbc#44

Add support for DLL build on Windows

From @mloskot on March 19, 2017 0:57

ATM, building shared library (DLL) on Windows is not really supported: public interfaces are not exported properly so no import library is generated for DLL.


Request for feedback: Any Windows users interested in nanodbc as DLL, please speak/thumb up.


Meanwhile, I updated CMake configuration to disable SHARED target on Windows (in 83013b3, w/ silly mistake corrected in 046f0db).

Copied from original issue: lexicalunit/nanodbc#246

long* and 64 bit issues

From @kmehltretter on November 25, 2013 12:24

long* is 64bit on Linux.
so nanodbc accesses uninitialized memory in some cases.
Tested on 64 bit Linux.

Fix:

--- a/nanodbc.cpp
+++ b/nanodbc.cpp
@@ -1559,7 +1559,8 @@
 {
     const bound_column& col = bound_columns_[column];
     const SQLULEN column_size = col.sqlsize_;
-    
+
+    using namespace std; // if int64_t is in std namespace (in c++11)
     switch(col.ctype_)
     {
         case SQL_C_CHAR:
@@ -1591,6 +1592,17 @@
         }

         case SQL_C_LONG:
+        {
+                   string_type buffer(column_size, 0);
+                   if(NANODBC_SNPRINTF(
+                           const_cast<string_type::value_type*>(buffer.data())
+                           , column_size
+                           , NANODBC_TEXT("%d")
+                           , *(int32_t*)(col.pdata_ + rowset_position_ * col.clen_)) == -1)
+                       throw type_incompatible_error();
+                   buffer.resize(NANODBC_STRLEN(buffer.c_str()));
+                   return buffer;
+        }
         case SQL_C_SBIGINT:
         {
             string_type buffer(column_size, 0);
@@ -1598,7 +1610,7 @@
                     const_cast<string_type::value_type*>(buffer.data())
                     , column_size
                     , NANODBC_TEXT("%ld")
-                    , *(long*)(col.pdata_ + rowset_position_ * col.clen_)) == -1)
+                    , *(int64_t*)(col.pdata_ + rowset_position_ * col.clen_)) == -1)
                 throw type_incompatible_error();
             buffer.resize(NANODBC_STRLEN(buffer.c_str()));
             return buffer;
@@ -1677,9 +1689,9 @@
         case SQL_C_CHAR: return (T)*(char*)(s);
         case SQL_C_SSHORT: return (T)*(short*)(s);
         case SQL_C_USHORT: return (T)*(unsigned short*)(s);
-        case SQL_C_LONG: return (T)*(long*)(s);
-        case SQL_C_SLONG: return (T)*(long*)(s);
-        case SQL_C_ULONG: return (T)*(unsigned long*)(s);
+        case SQL_C_LONG: return (T)*(int32_t*)(s);
+        case SQL_C_SLONG: return (T)*(int32_t*)(s);
+        case SQL_C_ULONG: return (T)*(uint32_t*)(s);
         case SQL_C_FLOAT: return (T)*(float*)(s);
         case SQL_C_DOUBLE: return (T)*(double*)(s);
         case SQL_C_SBIGINT: return (T)*(int64_t*)(s);

Copied from original issue: lexicalunit/nanodbc#16

[MariaDB] SELECT text FROM database returns wrong value

From @itisscan on March 21, 2016 13:11

I use MariaDB 5.5 (x64) on Windows 7 and MariaDB ODBC driver 1.0 in order to make connection. Also I use Urho3D library which just wraps nanodbc, in order to get database's data from the program.

I have table 'location' with 4 columns (Id, World, Location, Scene). 'Scene' column saves text as LONGTEXT datatype. Let's say it contains this value-

http://pastebin.com/h70h83gf

When Urho3D try to get this value from the program with this call -
nanodbc::execute(connectionImpl_, "SELECT A.Scene FROM location A WHERE A.Name = "EarthBaseStation";")

I get wrong value, like this one -

http://pastebin.com/21ZitRnV

It seems that result was splitted into two chunks. First chunk was duplicated from the 29 line . In the result i get 2 the same chunks.

I have debugged this nanodbc::execute method, could not find any issue.

But when Urho3D wants to retrieve value from the result class, it calls this method -

result.resultImpl_.get<nanodbc::string_type>((short)i).c_str();

in the result we step into below method, where nanodbc reads row's data in the buffer, look -

template<>
inline void result::result_impl::get_ref_impl<string_type>(short column, string_type& result) const
{
    const bound_column& col = bound_columns_[column];
    const SQLULEN column_size = col.sqlsize_;

    switch(col.ctype_)
    {
        case SQL_C_CHAR:
        {
            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);
                SQLLEN ValueLenOrInd;
                SQLRETURN rc;
                void* handle = native_statement_handle();
                do
                {
                    NANODBC_CALL_RC(
                        SQLGetData
                        , rc
                        , handle            // StatementHandle
                        , column + 1        // Col_or_Param_Num
                        , SQL_C_CHAR        // TargetType
                        , buff              // TargetValuePtr
                        , buff_size         // BufferLength
                        , &ValueLenOrInd);  // StrLen_or_IndPtr
                    if (ValueLenOrInd > 0)
                        ss << buff;
                } while(rc > 0);
                convert(ss.str(), result);
            }
            else
            {
                const char* s = col.pdata_ + rowset_position_ * col.clen_;
                const std::string::size_type str_size = std::strlen(s);
                result.assign(s, s + str_size);
            }
            return;
        }

I have checked the do { ... } while(rc > 0); loop. I noticed that if text's size is 1787 character, then in the first iteration we get 1024 first characters and remains to read 715 characters.
However it looks like in the second iteration we do not start reading from 1025 character, but start reading from the beginning. In the result we get the wrong result that I have described previous.

I suppose the bug is how nanodbc reads the row's data in the buffer or it just consequence to wrong executing of query.

Copied from original issue: lexicalunit/nanodbc#117

Example fixes

From @reubenhwk on March 5, 2014 19:48

example.cpp should return a proper value in main and it should also print usage info.

Without the usage info, and the check of the command line parameters, users will get a segfault unless they already know how to use example.cpp.

Copied from original issue: lexicalunit/nanodbc/pull/21

build error with unicode(visual studio 2013)

From @icedac on March 31, 2014 8:22

env:
Windows 7 x64
Visual Studio 2013 ver 12.0.30110.00 Update 1(sp1)
Unicode/x64 build.

    // Attempts to get the most recent ODBC error as a string.
    inline std::string recent_error(SQLHANDLE handle, SQLSMALLINT handle_type)
    {
        std::string result; // always return string, even in unicode mode
        std::vector sql_message( SQL_MAX_MESSAGE_LENGTH ); // change SQLCHAR to SQLTCHAR
        sql_message[0] = '\0';

        SQLINTEGER i = 1;
        SQLINTEGER native_error = 0;
        SQLSMALLINT total_bytes;
        SQLTCHAR sql_state[6];  // change SQLCHAR to SQLTCHAR
        RETCODE rc;

the two code change it works.
I'm not sure SQLTCHAR is standard or not.

Copied from original issue: lexicalunit/nanodbc#23

converting column data to string function exception(visual studio 2013/x64)

From @icedac on March 31, 2014 7:51

env:
Windows 7 x64
Visual Studio 2013 ver 12.0.30110.00 Update 1(sp1)
MBCS/x64 build.

the example.cpp program throwed a exception with 'type incompatible', and i checked whats wrong.

template<>
inline string_type result::result_impl::get_impl(short column) const
{
    const bound_column& col = bound_columns_[column];
    const SQLULEN column_size = col.sqlsize_;
    switch(col.ctype_)
    {
        case SQL_C_DOUBLE:
        {
            string_type buffer(column_size*2, 0);                   // in here the column_size is 7
            if(NANODBC_SNPRINTF(
                    const_cast(buffer.data())
                    , column_size*2
                    , NANODBC_TEXT("%lf")
                    , *(double*)(col.pdata_ + rowset_position_ * col.clen_)) == -1) // but the _snprintf needs 8 bytes for buffer, so it reutrn -1
                throw type_incompatible_error();
            buffer.resize(NANODBC_STRLEN(buffer.c_str()));
            return buffer;
        }
}

so i just temporarily fixed by doubling the buffer size.
any idea?

Copied from original issue: lexicalunit/nanodbc#22

Simplify parameters combinations of bind functions

From @mloskot on November 2, 2016 16:3

Problem

Currently, the family of bind functions can be summarised this way:

  • Bind single value
void bind(short param_index, T value, param_direction direction = PARAM_IN);
  • Bind multiple values (assume bind below denotes both, bind and bind_strings):
void bind(short param_index, T values, std::size_t batch_size, param_direction direction = PARAM_IN);
void bind(short param_index, T values, std::size_t batch_size, T const* null_sentry, param_direction direction = PARAM_IN);
void bind(short param_index, T values, std::size_t batch_size, bool const* nulls, param_direction direction = PARAM_IN);

where T denotes one from the set of types:

  • T const*
  • string_type::value_type const*
  • string_type::value_type[BatchSize][ValueSize]
  • std::vector<string_type> const&
  • std::vector<std::vector<uint8_t>> const&

That makes at least 3 x 5 ways of binding values.

I think, burden of optional parameters makes the number of combinations high.
Every introduction of new T adds new batch of bind functions.
I think, we may expect more T to come in future. In fact, I may submit such proposal with bind overloaded for new kind of T.

Proposal

I'm looking for options to further simplify the bind family.

One way is to apply parameter object pattern and wrap the optional parameters with single argument, but then the burden would shift to client who can no longer ignore those parameters. I'm not convinced this is the best alternative.

Another way is to extract the optional step of setting null values into dedicated two methods:

void set_bind_nulls(short param_index, bool const* nulls);
void set_bind_null_sentry(short param_index, T const* null_sentry);

Or, better just set_nulls and set_null_sentry, for brevity?

Then, client would use it this way:

statement.set_bind_null_sentry(0, "X"); // optional step
statement.bind(0, values, 10);

Advantages:

  • only 5 bind functions
  • cleaner and intuitive interface
  • easier to maintain and extend

Disadvantages:

  • memory occupied by statement object grows to accommodate array of null flags or single null sentry value.

Questions

  1. Does the second proposal sound compelling?
  2. Does anyone see a better way to simplify the bind functions family?

Copied from original issue: lexicalunit/nanodbc#222

TIME / TIMESTAMP with timezone and fractional seconds

From @mloskot on November 16, 2016 17:13

  • SQL Server has DateTimeOffset SQL type (ODBC slang: SQL_SS_TIMESTAMPOFFSET) which supports fractional seconds and time zone offset (see Data Type Support for ODBC Date and Time Improvements).
  • PostgreSQL has time(precision) with time zone and timestamp(precision) with time zone where precision indicates fractional seconds.
  • MySQL does not store time zone in time types, it recognises fractional seconds but does not store it.
  • I'm not sure about other popular databases.

I'd like to propose small update to nanodbc::time

struct time
{
    std::int16_t hour; ///< Hours since midnight [0-23].
    std::int16_t min;  ///< Minutes after the hour [0-59].
    std::int16_t sec;  ///< Seconds after the minute.
    std::int16_t fract;///< Fractional second digits.
}

and nanodbc::timestamp

struct timestamp
{
    std::int16_t year;  ///< Year [0-inf).
    std::int16_t month; ///< Month of the year [1-12].
    std::int16_t day;   ///< Day of the month [1-31].
    std::int16_t hour;  ///< Hours since midnight [0-23].
    std::int16_t min;   ///< Minutes after the hour [0-59].
    std::int16_t sec;   ///< Seconds after the minute.
    std::int32_t fract; ///< Fractional seconds.
    std::int32_t timezone_hour; ///< Timezone hour offset (negative or positive).
    std::int32_t timezone_min;  ///< Timezone minutes offset (sign as timezone_hour).
};

to support fractional seconds and time zone offset.

What do you think about such changes?

Copied from original issue: lexicalunit/nanodbc#224

Several changes to improve wide string support.

From @mcg1969 on October 1, 2013 22:10

First, I noticed that get_impl<string_type> was computing the wrong string length for wide strings, so the calls to buffer.resize() were incorrect. I created a NANODBC_STRLEN macro so that wcslen is called instead of strlen for wide strings.
There was also a problem with last_error with wide strings. It's C++'s fault, really, because it's not convenient to have wide string exceptions. So I reworked it to retrieve the wide string from SQLGetDiagRec and crudely convert the result to a narrow string for the exception message. I'm just truncating the 16-bit characters to 8 bits. Conversion to UTF-8 is probably a better choice.
Finally, I changed the parameter number from "long" to "short" to reflect what SQLBindParameter, etc. expect to see.

Copied from original issue: lexicalunit/nanodbc/pull/7

recent_error truncate gbk strings

From @ddmly on September 13, 2017 14:0

Environment

  • nanodbc version: master/20170912
  • DBMS name/version: sql server 2008
  • ODBC connection string: Driver=ODBC Driver 13 for SQL Server;Server=x;Uid=sa;Pwd=x;database=test
  • OS and Compiler: windows, vs2013, gbk
  • CMake settings: None, compile nanodbc together with test.cpp

Actual behavior

truncate gbk string shows [....?]

Expected behavior

show all error message [...........]

Minimal Working Example

sql = "drop table if exists test.simple_test"; // sql server not support, will throw error

Temporary fixed

- if (success(rc) && total_bytes > 0)
+ if (success(rc) && total_bytes > SQL_MAX_MESSAGE_LENGTH)
            sql_message.resize(static_cast<std::size_t>(total_bytes) + 1);
- result += nanodbc::string(sql_message.begin(), sql_message.end());
+ result += nanodbc::string(sql_message.begin(), sql_message.begin()+ total_bytes);

Copied from original issue: lexicalunit/nanodbc#284

Added 64-bit support

From @WimObiwan on August 26, 2013 13:20

The "long *" pointers (which specify whether or not parameter values are NULL) resulted in compiler errors when using x64 compilation on VC8. The odbc API expects a SQLLEN which is __int64 on x64 compilations.

To fix this, in nanodbc, I replaced the long with a new null_type that is long on 32-bit, and __int64 on 64-bit.

Copied from original issue: lexicalunit/nanodbc/pull/6

connect with user and password not working

From @kmehltretter on November 25, 2013 12:20

connect with username and password does not work.
Fix:

--- a/nanodbc.cpp
+++ b/nanodbc.cpp
@@ -522,8 +522,8 @@
             , rc
             , conn_
             , (NANODBC_SQLCHAR*)dsn.c_str(), SQL_NTS
-            , user.empty() ? (NANODBC_SQLCHAR*)user.c_str() : 0, SQL_NTS
-            , pass.empty() ? (NANODBC_SQLCHAR*)pass.c_str() : 0, SQL_NTS);
+            , !user.empty() ? (NANODBC_SQLCHAR*)user.c_str() : 0, SQL_NTS
+            , !pass.empty() ? (NANODBC_SQLCHAR*)pass.c_str() : 0, SQL_NTS);
         if(!success(rc))
             NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);

Copied from original issue: lexicalunit/nanodbc#15

No data results hides subsequent result sets.

From @aquasync on December 30, 2014 0:48

I'm using nanodbc to run a chunk of sql containing multiple statements (dml/ddl along with selects), and iterating through the results with something like the following:

nanodbc::connection db("...");
nanodbc::result res = execute(db, sql);
do {
    if (bool(res) && res.columns() > 0) {
        // process resultset
    }
} while (bool(res) && res.next_result());

I ran into the situation where queries which return no data return a default-constructed result object, as the SQL_NO_DATA path in execute/execute_direct is taken. This causes it to have no underlying result_impl or reference to the statement, so you are unable to advance to subsequent result sets. Adding a preceding dummy query seems to fix this.

For example, in "select top 0 1 as a into #temp; select 2 as b", data from the latter select is not reachable, but prepending "select 1 as a into #dummy; " returns a valid result object allowing you to iterate to the final result set.

Commenting out the above-mentioned SQL_NO_DATA path in both execute & execute_direct fixes this for me by returning a valid result object always. Could this be deleted upstream or is it needed in some other situtation?

Copied from original issue: lexicalunit/nanodbc#33

bind to null example

From @DraconPern on November 11, 2013 6:20

Can you put in an example of binding to null for parameters? Thanks!

Copied from original issue: lexicalunit/nanodbc#12

result::is_null returns incorrect value for unbound columns

From @AndrewJD79 on February 13, 2017 17:43

Hi,
I’ve found that result::is_null returns incorrect value for unbound columns (content is read by SQLGetData but not binded to static buffer – all blobs) until value itself is retrieved. The issue can be easily reproduced when returning NULL binary column. I’ve reproduced behavior on MSSQL and OpenEdge Progress ODBC driver.
Investigation shows that in case of unbound column (parameters of SQLBindCol TargetValuePtr=nullptr, BufferLength =0) value of StrLen_or_Ind(bound_column:: cbdata_) is never updated during SQLFetchScroll and has zero value. On the other hand implementation of result_impl:: is_null relies on value from bound_column::cbdata_. After a value is read form the column by SQLGetData then bound_column:: cbdata_ is correctly initialized in get_ref_impl function.
It seems interface doesn’t provide way to reliably check column for NULL until data is accessed.

I've found mention of the same issue here: Using SQLBindCol to get column length of unbound long data column

Code example:

   nanodbc::statement statement(connection);
  prepare(statement, NANODBC_TEXT("{ CALL test_odbc_BinaryTypeNull() }"));
  nanodbc::result results = execute(statement);
  nanodbc::string_type const null_value = NANODBC_TEXT("null");
  short col = 0;
  while (results.next())
  {
     bool isValNull = results.is_null(col);
     auto const value = results.get<nanodbc::string_type>(col, null_value);
     cout << results.column_name(col) << " -  isNullBefore:" << isValNull << "; isNullAfter: " << results.is_null(col) << "\n";
  }

SQL code:

IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[test_odbc_BinaryTypeNull]') AND OBJECTPROPERTY(id, N'IsProcedure') = 1)
EXEC ('CREATE PROCEDURE [dbo].[test_odbc_BinaryTypeNull] AS RETURN -1')
GO

ALTER PROCEDURE [dbo].[test_odbc_BinaryTypeNull]
AS
SET NOCOUNT ON
    select CAST(null as varbinary(max)) as null_bin_column
GO

Output:

null_bin_column - isNullBefore:0; isNullAfter: 1

Copied from original issue: lexicalunit/nanodbc#239

Binary column binding on static buffer is not implemented

From @AndrewJD79 on February 14, 2017 22:58

Hi,

I've notice that binary columns can be bind to std::vector<uint8_t> only as BLOBs.
result_impl::auto_bind():

      case SQL_BINARY:
        case SQL_VARBINARY:
        case SQL_LONGVARBINARY:
        case SQL_SS_UDT: // MSDN: Essentially, UDT is a varbinary type with additional metadata.
            col.ctype_ = SQL_C_BINARY;
            **col.blob_ = true;**
            col.clen_ = 0;

On the other hand function result_impl::get_ref_impl<std::vectorstd::uint8_t> has code for reading from static buffer:

    else // if (col.blob_)
    {
        // Read fixed-length binary data
        const char* s = col.pdata_ + rowset_position_ * col.clen_;
        result.assign(s, s + column_size);
    }

Does such reading in BLOB only mode was done for purpose or just left for future enhancement?

Copied from original issue: lexicalunit/nanodbc#240

explicit keyword usage

From @DraconPern on November 4, 2013 2:30

I suggest removing 'explicit' from operator bool() const; in the .h file (ling 731, result::operator bool() const) so that at least the file will work on vs2012 where it's not supported. The affect on this on client code should be minimal.

FYI, looking at the bool() operator, I am not quite sure what it's functionality is. The implementation is a static_cast from a shared_ptr... Which basically says, return true if result is assigned.

Copied from original issue: lexicalunit/nanodbc#11

[Vertica] Unicode issue under Windows

From @wildpackets-ma on November 6, 2015 23:6

The code below is excerpted nearly verbatim from the nanodbc home page example.

The Vertica database is running on a linux machine, while the test code is running under Windows 7 x64 and compiled with VS2013. If the code is compiled with NANODBC_USE_UNICODE undefined, I get the expected output:
Displaying 4 rows (3 fetched at a time):
row x
0 (this )
1 (is )
2 (a )
3 (test )

However, defining NANODBC_USE_UNICODE causes the following output error:
Displaying 4 rows (3 fetched at a time):
row x
0 (this )
1 ( )
2 (is )
3 ( )

Nanodbc version was pulled from github source today, so it's the latest

#include <iostream>
#include <algorithm>
#include <cstring>
#include <iostream>

#include "nanodbc.h"

using str = nanodbc::string_type;
using namespace std;

#ifdef NANODBC_USE_UNICODE
#define UNI(a) L ## a
    str encoding = UNI("Unicode");
    auto & outr = std::wcout;
    using char_t = wchar_t;
#else
#define UNI(a) a
    str encoding = UNI("Ascii");
    auto & outr = std::cout;
    using char_t = char;
#endif

void show(nanodbc::result& results)
{
    const short columns = results.columns();
    long rows_displayed = 0;

    outr << UNI("\nDisplaying ") << results.affected_rows() << UNI(" rows ")
        << UNI("(") << results.rowset_size() << UNI(" fetched at a time):") << endl;

    // show the column names
    outr << UNI("row\t");
    for (short i = 0; i < columns; ++i)
        outr << results.column_name(i) << UNI("\t");
    outr << endl;

    // show the column data for each row
    while (results.next())
    {
        outr << rows_displayed++ << UNI("\t");
        for (short col = 0; col < columns; ++col)
            outr << UNI("(") << results.get<str>(col, UNI("nul")) << UNI(")\t");
        outr << endl;
    }
}

int main(int argc, char * argv[])
{
    str dsn = UNI("test-dsn");
    str user = UNI("foo");
    str pass = UNI("bar");

    nanodbc::connection connection(dsn, user, pass);
    if (!connection.connected()) return -1;

    // Batch inserting
    nanodbc::result results;
    nanodbc::statement statement(connection);
    execute(connection, UNI("drop table if exists public.batch_test;"));
    execute(connection, UNI("create table public.batch_test (x varchar(10));"));
    prepare(statement, UNI("insert into public.batch_test (x) values (?);"));

    const std::size_t elements = 4;

    char_t xdata[elements][10] = { UNI("this"), UNI("is"), UNI("a"), UNI("test") };
    statement.bind_strings(0, xdata);

    transact(statement, elements);

    results = execute(connection, UNI("select * from public.batch_test;"), 3);
    show(results);

    return 0;
}

Copied from original issue: lexicalunit/nanodbc#54

Caching SQL text of prepared statement

From @mloskot on September 20, 2017 15:8

Currently, once statement object has been prepared, that is SQLPrepare was called (either via statement constructor or explicit statement::prepare call later), there is no way to retrieve SQL text of such prepared statement.

ODBC API does not offer any means to do that. There only are database-specific solutions like SQL Servers's SELECT * FROM sys.dm_exec_sql_text(@sql_handle). See also my SO question Retrieve SQL text of ODBC prepared statement.


I think it would be useful to cache the SQL text (eg. for logging, re-creating statements etc.)

I'm thinking about adding private nanodbc::string query_ to statement class accompanied with nanodbc::query const& query() const member function.

Any thoughts, better ideas?

Copied from original issue: lexicalunit/nanodbc#291

Unable to set string parameter to NULL using bind overload

From @AndrewJD79 on July 3, 2017 20:40

Environment

  • nanodbc version: 2.12.4, master
  • DBMS name/version: Progress/ MSSQL

Actual behavior

Unable to set string parameter to NULL using following bind overload:
void bind( short param_index, T const* values, std::size_t batch_size, bool const* nulls, param_direction direction = PARAM_IN);

The overload eventually calls
void statement::statement_impl::bind_parameter<string::value_type>( bound_parameter const& param, bound_buffer<string::value_type>& buffer)

For some reasons last parameter (parameter length or status indicator) passed into SQLBindParameter is
(buffer.size_ <= 1 ? nullptr : bind_len_or_null_[param.index_].data()

I'm not sure why length_or_status is depending from batch size (buffer.size_). If the line changed to
bind_len_or_null_[param.index_].data()
everything works as expected.

Expected behavior

parameter has to have NULL value

Minimal Working Example

statement stat(conn);

size_t batch_size = 1;
char buffer [] = "";
bool isNull = true;
stat.bind(0, buffer, batch_size , &isNull);

Copied from original issue: lexicalunit/nanodbc#277

Feature: Support for retrieving output/return parameters when calling stored procedure

From @WimObiwan on August 21, 2013 12:44

...dures. You need to retrieve all returned result sets before the output parameters are filled in (using next_result(), provided by separate commit).

Example:
nanodbc::statement stmt(conn);
stmt.prepare(conn, "{? = CALL dbo.spMyProc(?,?,?)}");
stmt.bind_parameter(0, &result, NULL, nanodbc::statement::Return);
stmt.bind_parameter(1, str.c_str());
stmt.bind_parameter(2, &i);
stmt.bind_parameter(3, &j, nanodbc::statement::Out);
nanodbc::result resultset = stmt.execute();
while (resultset.next_result()) ;
conn.disconnect();

Copied from original issue: lexicalunit/nanodbc/pull/4

Large Objects: String data, right truncation

From @mloskot on September 18, 2017 16:57

Environment

  • nanodbc version: master
  • DBMS name/version: SQL Server (w/ 2014, 2016, 2017)
  • OS and Compiler: Windows 10 + VS2017

Actual behavior

Attempt to execute prepared statement with bound large object (varchar, varbinary, UDT) with size larger than a database/driver-specific limit fails due to truncation.
That is despite schema of table column in database is defined without such limit, becuase nanodbc does not specify "unlimited" size input.

For example, SQL Server has 8K limit, https://docs.microsoft.com/en-us/sql/relational-databases/native-client/features/using-large-value-types:

Large value data types can have a maximum size between 1 and 8 KB, or they can be specified as unlimited.

So, inserting (bound) object larger than 8K will fail due to trancation.

The issue is that parameter size reported by SQLDescribeParam for Large Objects is at at most the upper bound of size limit.:

  • For SQL VARBINARY(MAX), it is Zero which actually means SQL_SS_LENGTH_UNLIMITED.
  • For SQL UDT (eg. GEOMETRY), it may be driver-specific max limit (eg. SQL Server is DBMAXCHAR=8000 bytes defined in msodbcsql.h of Microsoft ODBC Driver for SQL Server).

Expected behavior

Adjust parameter size for SQLBindParameter to avoid truncation, according to this table at https://docs.microsoft.com/en-us/sql/relational-databases/native-client/odbc/large-clr-user-defined-types-odbc:

sqlserver-size

Minimal Working Example

connection c1("...");
statement s1(c1);
prepare(s1, INSERT INTO t (g) VALUES (geometry::STGeomFromWKB(?, 0)));

// blob is binary object with size larger than 8K (SQL Server limit)
std::vector<std::vector<std::uint8_t>> rows = {blob};

s1.bind(0, rows);
execute(s1);
// 22001: [Microsoft][ODBC Driver 11for SQL Server] String data, right truncation

Copied from original issue: lexicalunit/nanodbc#287

many fixes for windows 64bit unicode

From @duhlin on October 4, 2013 14:20

This pull request fixes many issues on win64 unicode with visual studio 2008.
This commit includes the pull request from mcg1969 which solves compilation issue on this plateform.

It has been tested with sqlite3 odbc driver.

Copied from original issue: lexicalunit/nanodbc/pull/8

SQLGetData error then Read ValueLenOrInd directly

From @superzmy on September 8, 2017 14:42

Environment

  • nanodbc version: trunk
  • DBMS name/version:
  • ODBC connection string: ODBC 13 for SQL SERVER
  • OS and Compiler: WIN7 64 VS2017
  • CMake settings:

Actual behavior

get_ref_impl : SQLGetData(...) error on a varbinary field(and it's value is NULL). code didn't test success before use ValueLenOrInd and ValueLenOrInd is uninit.

Expected behavior

don't throw error ? why SQLGetData error by using show() of usage.cpp

Minimal Working Example

results.getnanodbc::string(col, null_value);

Copied from original issue: lexicalunit/nanodbc#281

Not maintaining transaction isolation level when set via query

From @NickLarsen on June 13, 2017 16:8

Environment

  • nanodbc version: 2.12.4
  • DBMS name/version: SQL Server 2016
  • ODBC connection string: driver=SQL Server;server=localhost;database=mydb;uid=test;pwd=test1
  • OS and Compiler: windows 10, visual studio 2017
  • CMake settings: ?

Actual behavior

When I execute the state SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED, then check the transaction isolation level of the session, it stays at read committed (2) when what I want is UN committed (1).

Expected behavior

It should maintain the transaction isolation level when set via query, like it does when using other tools (e.g. the .net sql server tooling).

Minimal Working Example

#include "nanodbc.h"

int main()
{
    nanodbc::connection *conn = new nanodbc::connection(NANODBC_TEXT("driver=SQL Server;server=localhost;database=mydb;uid=test;pwd=test1"), 100);
    nanodbc::statement *statement = new nanodbc::statement(*conn, NANODBC_TEXT("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED"), 100);
    statement->execute();
    // set a break point here and run the query below in some other tool
    return 0;
}

requires admin privs but lets you see the transaction isolation level of all active sessions

SELECT   
    c.session_id, s.program_name, s.transaction_isolation_level,
    s.login_name, s.original_login_name, c.connect_time
FROM sys.dm_exec_connections AS c  
JOIN sys.dm_exec_sessions AS s  
    ON c.session_id = s.session_id
group by 
    c.session_id, s.program_name, s.transaction_isolation_level,
    s.login_name, s.original_login_name, c.connect_time

Fixing it

In terms of fixing this, the query method shown above is preferable, but I have not been able to debug why that's not working. I did find a workaround by setting connection attributes however.

Adding the following code in the connect function (nanodbc.cpp line 926) around line 943 will set the transaction isolation level on the connection prior to connecting and maintain it throughout the session.

NANODBC_CALL_RC(SQLSetConnectAttr, rc, conn_, SQL_ATTR_TXN_ISOLATION, (SQLPOINTER)(std::intptr_t)SQL_TXN_READ_UNCOMMITTED, 0);
if (!success(rc))
    NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);

I think this would be some sort of optional thing you set on the connection prior to connecting instead of just jamming it in there, but of course I'd still much prefer setting it with the sql statement.

Copied from original issue: lexicalunit/nanodbc#276

Binding of nanodbc::timestamp fraction field fails

From @bangusi on September 19, 2017 1:58

Environment

  • nanodbc version: Latest
  • DBMS name/version: SQL Server 14
  • ODBC connection string: Driver SQL Server 13
  • OS and Compiler: MSVC 2017 ( v 15.3
  • CMake settings:

Actual behavior

It seems nanodbc::timestamp fractional field does not store nanoseconds like the odbc timestaruct
Or may be it does but the value is truncated

Runtime exception:
[Microsoft][ODBC Driver 13 for SQL Server]Datetime field overflow. Fractional second precision exceeds the scale specified in the parameter binding.

Expected behavior

Minimal Working Example

CREATE TABLE [dbo].[z_tests2](
	[time_t] [datetime2](7) NULL
)
void foo(nanodbc::connection& conn)
{
	nanodbc::statement statement(conn);
	std::wstring sql = L"insert into dbo.z_tests2(time_t) values(?)";
	prepare(statement, sql);

	nanodbc::timestamp dt;
	dt.year = 2009;
	dt.month = 6;
	dt.day = 9;
	dt.hour = 23;
	dt.min = 59;
	dt.sec = 59;
	dt.fract = 9999999; // we should be able to specify up to 9 digits see tagTIMESTAMP_STRUCT defined here https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/c-data-types
	nanodbc::transaction trx(conn);
	statement.bind(0, &dt);
	statement.execute();
	trx.commit();
}

Copied from original issue: lexicalunit/nanodbc#290

Set connection attributes before connection is made

From @bangusi on September 21, 2017 0:39

Environment

UPDATE: replaced Latest with master ~mloskot

  • nanodbc version: Latest master
  • DBMS name/version: SQL Server 2014
  • ODBC connection string:
  • OS and Compiler: MSVC 2017 ( v 15.3)
  • CMake settings:

Actual behavior

In order to enable bulk copy ( really fast data loads ) using the Microsoft odbc driver you have to enable bcp in the connection attributes.

My attempt was

auto const connection_string = L"Driver={ODBC Driver 13 for SQL Server}; Server=myServer;Database=myDB;Trusted_Connection=Yes";
nanodbc::connection conn(connection_string);
SQLRETURN rc = SQLSetConnectAttr(conn.native_dbc_handle, SQL_COPT_SS_BCP, (SQLPOINTER)SQL_BCP_ON, SQL_IS_INTEGER);

Expected behavior

This does not work. You have to be in bcp mode before you establish the connection.
Here is the documentation and sample code
https://docs.microsoft.com/en-us/sql/relational-databases/native-client-odbc-extensions-bulk-copy-functions/bcp-init

 // Allocate ODBC connection handle, set BCP mode, and connect.  
   retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc1);  
   if ( (retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) {  
      printf("SQLAllocHandle(hdbc1) Failed\n\n");  
      Cleanup();  
      return(9);  
   }  

   retcode = SQLSetConnectAttr(hdbc1, SQL_COPT_SS_BCP, (void *)SQL_BCP_ON, SQL_IS_INTEGER);  
   if ( (retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) {  
      printf("SQLSetConnectAttr(hdbc1) Failed\n\n");  
      Cleanup();  
      return(9);  
   }  

   // Sample uses Integrated Security. Create SQL Server DSN using Windows NT authentication.  
   retcode = SQLConnect(hdbc1, (UCHAR*)"Test", SQL_NTS, (UCHAR*)"", SQL_NTS, (UCHAR*)"", SQL_NTS);  
   if ( (retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO) ) {  
      printf("SQLConnect() Failed\n\n");  
      Cleanup();  
      return(9);  
   }  

   // Initialize the bulk copy.  
   retcode = bcp_init(hdbc1, "BCPDate", "BCPODBC.bcp", NULL, DB_IN);  
   if ( (retcode != SUCCEED) ) {  
      printf("bcp_init(hdbc1) Failed\n\n");  
      Cleanup();  
      return(9);  

Copied from original issue: lexicalunit/nanodbc#292

Equals statement comparing same thing 3 times

From @MrMikeJJ on March 5, 2014 16:28

Hi,
I noticed that the
bool statement::statement_impl::equals
for dates compares the year 3 times (currently line 1429 of the .cpp).
Shouldn't it compare the year, month, day ?
thanks
Mike

Copied from original issue: lexicalunit/nanodbc#20

What is MySQL DSN syntax on windows?

From @changtj on September 27, 2017 1:41

Environment

windows 10
MySQL ODBC 5.3 ANSI Driver

  • nanodbc version: 2.12.4
  • DBMS name/version: mysql 5.7
  • ODBC connection string:
    Driver=MySQL ODBC 5.3 ANSI Driver;Server=192.168.123.56;Port=3306
  • OS and Compiler:
    windows 10 gcc
  • CMake settings:
    used vcpkg

Actual behavior

can't connect.
database_error ,HY090, character is invalidate.

Expected behavior

Minimal Working Example

std::string str_conn = "Driver=MySQL ODBC 5.3 ANSI Driver;Server=192.168.123.56;Port=3306";
std::string str_user = "root";
std::string str_pwd = "123456";
nanodbc::connection _conn;
_conn.connect( str_conn, str_user, str_pwd);

comment:

when i set dsn on windows datasource management ,
std::string str_conn = "MySQL ODBC ANSI";
and other is same. it can connect correctly.

Copied from original issue: lexicalunit/nanodbc#295

Compiling with MinGW 64 fails

From @bangusi on March 17, 2017 14:16

Is MinGW supported? I am using this distro of MinGW https://nuwen.net/mingw.html.
Compiling with

NANODBC_USE_UNICODE=on 
NANODBC_DISABLE_ASYNC=off

Some of the errors I am getting:

nanodbc.cpp: In member function 'void nanodbc::statement::statement_impl::bindtrings(nanodbc::statement::param_direction, short int, const value_type*, std:
ize_t, std::size_t, const bool*, const value_type*)':
nanodbc.cpp:2033:70: error: cannot convert 'const char16_t*' to 'const char*'
r argument '1' to 'int strncmp(const char*, const char*, size_t)'
             if (std::strncmp(s_lhs.c_str(), s_rhs.c_str(), value_size) != 0)
                                                                      ^
main.cpp: In function 'void f0(nanodbc::connection&)':
main.cpp:7:53: error: no matching function for call to 'execute(nanodbc::conneion&, std::__cxx11::wstring&)'
  nanodbc::result results = nanodbc::execute(conn,sql);

My sql is like std::wstring sql = L"select x from some_table"

Copied from original issue: lexicalunit/nanodbc#244

cannot execute prepared SELECT more than once

From @FernandoCorradi on April 23, 2014 18:40

If I prepare a simple SELECT command and try to execute it more than once, an exception will be thrown.

According to MS, "To execute a SELECT statement more than once, the application must call SQLCloseCursor before reexecuting the SELECT statement."
http://msdn.microsoft.com/en-us/library/windows/desktop/ms713584%28v=vs.85%29.aspx

Currently there is no way to call SQLCloseCursor using the nanobdc API. I can bypass the problem if I execute a statement::cancel() before the next statement::execute(). Is this the recommended way?

Here is a sample code:

[code]
nanodbc::connection connection("my server connection string");

nanodbc::statement statement(connection);
nanodbc::prepare(statement, "SELECT GETDATE() as x");

nanodbc::result rs = statement.execute();
rs.next();
std::cout << rs.get<nanodbc::timestamp>(0) << std::endl;

    // statement.cancel();
    // only by issuing this cancel() the next execute will work
    // but shouldn't we be calling SQLCloseCursor(), as recommended here:
    // http://msdn.microsoft.com/en-us/library/windows/desktop/ms713584%28v=vs.85%29.aspx   

rs = statement.execute();
rs.next();
std::cout << rs.get<nanodbc::timestamp>(0) << std::endl;

[/code]

Copied from original issue: lexicalunit/nanodbc#24

Test integer to string conversions with all DBMS

From @mloskot on June 30, 2016 8:27

Such test has been added for SQLite only in #190.
It should be moved to generic tests to cover all databases we test on CI services.

It also should be checked if the column size and value truncation which occur with SQLite is a common issue, see lexicalunit/nanodbc#176 (comment), then probably we want to improve the implementation of conversions.

Copied from original issue: lexicalunit/nanodbc#191

[MSSQL] SQL_TIME or SQL_TYPE_TIME not supported but SQL_SS_TIME2

From @mloskot on November 21, 2016 10:16

While using the ODBC Driver 11 for SQL Server, I noticed it is not possible to get data from TIME column.

TL:TR; nanodbc expects SQL_TIME while the SQL Server driver reports TIME as SQL Server-specific (SS) type SQL_SS_TIME2 (-154).

Problem

Currently, nanodbc handles SQL_TIME (ODBC 2.x) or SQL_TYPE_TIME (ODBC 3.x).

The SQL Server 2008 added new date and time types , namely SQL_SS_TIME2. In theory, uses of old SQL_TIME should work without modifications required:

The new SQL Server time data type has fractional seconds accurate to 100 nanoseconds (...)
Existing applications written to use times with no fractional seconds can use time(0) columns.
The existing ODBC SQL_TYPE_TIME types and their corresponding structs should work correctly,
unless the applications rely on the type returned in metadata.

AFAICT, it does not. I tried to create table with time(0) hoping to get it reported as SQL_TIME or SQL_TYPE_TIME, but the driver still reports SQL_SS_TIME2 which nanodbc falls back to bind it as SQL_(W)CHAR which in turn fails to translate to nanodbc::time.

Interestingly, Data Type Support for ODBC Date and Time Improvements does not even list SQL_TIME or SQL_TYPE_TIME type as supported anymore. Mess squared!

BTW, since, the date and time data types that were added in SQL Server 2008, users of other drivers like
SQL Server Native Client are likely affected too.

Possible solution

Simplest, would be to handle all SQL Server-specific SQL_SS_* codes

Complete list is defined in

// Driver specific SQL data type defines.
// Microsoft has -150 thru -199 reserved for Microsoft SQL Server Native Client driver usage.
#define SQL_SS_VARIANT                      (-150)
#define SQL_SS_UDT                          (-151)
#define SQL_SS_XML                          (-152)
#define SQL_SS_TABLE                        (-153)
#define SQL_SS_TIME2                        (-154)
#define SQL_SS_TIMESTAMPOFFSET              (-155)

along with the corresponding ODBC C type constants:

// Extended C Types range 4000 and above. Range of -100 thru 200 is reserved by Driver Manager.
#define SQL_C_TYPES_EXTENDED                0x04000L
#define SQL_C_SS_TIME2                         (SQL_C_TYPES_EXTENDED+0)
#define SQL_C_SS_TIMESTAMPOFFSET               (SQL_C_TYPES_EXTENDED+1

and time-related structures:

  • SQL_SS_TIME2_STRUCT
  • SQL_SS_TIMESTAMPOFFSET_STRUCT

Note, the structures (and their sizeof) are used in binding, similarly to their simpler counterparts:

  • SQL_TIME_STRUCT
  • SQL_TIMESTAMP_STRUCT:

In nanodbc, we may need to provide additional equivalent types, e.g.

  • nanodbc::time2
  • nanodbc::timestamp_tz

Question

Can we be certain that no other drivers use the same codes like -154 for their ODBC DBMS-specific data types?

I think we can't but there seem to be no better option than to take the risk, release nanodbc to the wild find out.

Any feedback is highly appreciated.

Copied from original issue: lexicalunit/nanodbc#226

Problem binding string parameters in insert

From @PhilLello on January 18, 2014 19:13

Using a simple insert with strings is buggy. In the code fragment:

nanodbc::statement query(_connection);
nanodbc::prepare(query, "INSERT INTO accounts(name) VALUES(?)");
query.bind(0, name.c_str());
nanodbc::execute(query);

The value taken for c_str is wrong; it looks like the bind is taking bytes from name.c_str() instead of using the string length. This results in garbage after the inserted value.

Copied from original issue: lexicalunit/nanodbc#17

error of using setlocale when translate datetime to string (switch case SQL_C_TIMESTAMP)

From @superzmy on September 25, 2017 3:26

debug in VS2017
nanodbc.cpp

case SQL_C_TIMESTAMP:   
.... 
char* old_lc_time = std::setlocale(LC_TIME, NULL);  //when step over this line,string of old_lc_time  will be some English words
std::setlocale(LC_TIME, "");   //when step over this line, string of old_lc_time will be 0xdddddddd....
...
std::setlocale(LC_TIME, old_lc_time); //error here

Copied from original issue: lexicalunit/nanodbc#293

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.