Skip to content

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

License

Notifications You must be signed in to change notification settings

nanodbc/nanodbc

Repository files navigation

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 .

# To build using the nanodbc already source within the container
$ docker run -it nanodbc /bin/bash

# Alternatively, mount the nanodbc repository into the container as a volume
$ docker run -v "$(pwd)":"/opt/$(basename $(pwd))" -it nanodbc /bin/bash

# Then, enter the source directory and build nanodbc:
root@hash:/# mkdir -p /opt/nanodbc/build
root@hash:/# cd /opt/nanodbc/build
root@hash:/opt/nanodbc/build# cmake ..
root@hash:/opt/nanodbc/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 master and previous releases.
  • Add "HOWTO Build" documentation for Windows, OS X, and Linux.

MIT © lexicalunit, mloskot and contributors.