Use
In order to use the nanodbc library, add nanodbc/nanodbc.h
and nanodbc/nanodbc.cpp
source files to your project.
Alternatively, you can build the library with CMake as static or shared library and add it to your project as linker input.
Add #include <nanodbc/nanodbc.h>
in source files where you wish to use
nanodbc functions and classes.
The entirety of nanodbc can be found within the single nanodbc namespace.
Quickstart
#include <nanodbc/nanodbc.h>
#include <iostream>
#include <exception>
int main() try
{
auto const connstr = NANODBC_TEXT("..."); // an ODBC connection string to your database
nanodbc::connection conn(connstr);
nanodbc::execute(conn, NANODBC_TEXT("create table t (i int)"));
nanodbc::execute(conn, NANODBC_TEXT("insert into t (1)"));
auto result = nanodbc::execute(conn, NANODBC_TEXT("SELECT i FROM t"));
while (result.next())
{
auto i = result.get<int>(0);
}
return EXIT_SUCCESS;
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
iODBC and unixODBC
Notes about using nanodbc with iODBC and unixODBC in Unix systems.
On Windows, sizeof(wchar_t) == sizeof(SQLWCHAR) == 2
.
On Unix, sizeof(wchar_t) == 4
.
On unixODBC, sizeof(SQLWCHAR) == 2
.
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_type
will be std::u32string
.
In ALL other cases it will be std::u16string
.
The nanodbc continuous integration tests run on Travis CI. The build platform does not make available a Unicode-enabled iODBC driver. As such there is no guarantee that tests will pass in entirety on a system using iODBC. Our recommendation is to use unixODBC.
If you must use iODBC, consider disabling Unicode mode in nanodbc build
configuration to avoid wchar_t
issues, see Build.
Example
#include <algorithm>
#include <cstring>
#include <iostream>
#include <nanodbc/nanodbc.h>
using namespace std;
void show(nanodbc::result& results);
void run_test(const char* connection_string)
{
// Establishing connections
nanodbc::connection connection(connection_string);
// or connection(connection_string, timeout_seconds);
// or connection("data source name", "username", "password");
// or connection("data source name", "username", "password", timeout_seconds);
cout << "Connected with driver " << connection.driver_name() << endl;
// Setup
execute(connection, "drop table if exists public.simple_test;");
execute(connection, "create table public.simple_test (a int, b varchar(10));");
nanodbc::result results;
// Direct execution
{
execute(connection, "insert into public.simple_test values (1, 'one');");
execute(connection, "insert into public.simple_test values (2, 'two');");
execute(connection, "insert into public.simple_test values (3, 'tri');");
execute(connection, "insert into public.simple_test (b) values ('z');");
results = execute(connection, "select * from public.simple_test;");
show(results);
}
// Accessing results by name, or column number
{
results = execute(
connection, "select a as first, b as second from public.simple_test where a = 1;");
results.next();
cout << endl << results.get<int>("first") << ", " << results.get<string>(1) << endl;
}
// Binding parameters
{
nanodbc::statement statement(connection);
// Inserting values
prepare(statement, "insert into public.simple_test (a, b) values (?, ?);");
const int eight_int = 8;
statement.bind(0, &eight_int);
const string eight_str = "eight";
statement.bind(1, eight_str.c_str());
execute(statement);
// Inserting null values
prepare(statement, "insert into public.simple_test (a, b) values (?, ?);");
statement.bind_null(0);
statement.bind_null(1);
execute(statement);
// Inserting multiple null values
prepare(statement, "insert into public.simple_test (a, b) values (?, ?);");
statement.bind_null(0, 2);
statement.bind_null(1, 2);
execute(statement, 2);
prepare(statement, "select * from public.simple_test;");
results = execute(statement);
show(results);
}
// Transactions
{
{
cout << "\ndeleting all rows ... " << flush;
nanodbc::transaction transaction(connection);
execute(connection, "delete from public.simple_test;");
// transaction will be rolled back if we don't call transaction.commit()
}
results = execute(connection, "select count(1) from public.simple_test;");
results.next();
cout << "still have " << results.get<int>(0) << " rows!" << endl;
}
// Batch inserting
{
nanodbc::statement statement(connection);
execute(connection, "drop table if exists public.batch_test;");
execute(connection, "create table public.batch_test (x varchar(10), y int, z float);");
prepare(statement, "insert into public.batch_test (x, y, z) values (?, ?, ?);");
const std::size_t elements = 4;
char xdata[elements][10] = {"this", "is", "a", "test"};
statement.bind_strings(0, xdata);
int ydata[elements] = {1, 2, 3, 4};
statement.bind(1, ydata, elements);
float zdata[elements] = {1.1, 2.2, 3.3, 4.4};
statement.bind(2, zdata, elements);
transact(statement, elements);
results = execute(connection, "select * from public.batch_test;", 3);
show(results);
execute(connection, "drop table if exists public.batch_test;");
}
// Dates and Times
{
execute(connection, "drop table if exists public.date_test;");
execute(connection, "create table public.date_test (x datetime);");
execute(connection, "insert into public.date_test values (current_timestamp);");
results = execute(connection, "select * from public.date_test;");
results.next();
nanodbc::date date = results.get<nanodbc::date>(0);
cout << endl << date.year << "-" << date.month << "-" << date.day << endl;
results = execute(connection, "select * from public.date_test;");
show(results);
execute(connection, "drop table if exists public.date_test;");
}
// Inserting NULL values with a sentry
{
nanodbc::statement statement(connection);
prepare(statement, "insert into public.simple_test (a, b) values (?, ?);");
const int elements = 5;
const int a_null = 0;
const char* b_null = "";
int a_data[elements] = {0, 88, 0, 0, 0};
char b_data[elements][10] = {"", "non-null", "", "", ""};
statement.bind(0, a_data, elements, &a_null);
statement.bind_strings(1, b_data, b_null);
execute(statement, elements);
nanodbc::result results = execute(connection, "select * from public.simple_test;");
show(results);
}
// Inserting NULL values with flags
{
nanodbc::statement statement(connection);
prepare(statement, "insert into public.simple_test (a, b) values (?, ?);");
const int elements = 2;
int a_data[elements] = {0, 42};
char b_data[elements][10] = {"", "every"};
bool nulls[elements] = {true, false};
statement.bind(0, a_data, elements, nulls);
statement.bind_strings(1, b_data, nulls);
execute(statement, elements);
nanodbc::result results = execute(connection, "select * from public.simple_test;");
show(results);
}
// Cleanup
execute(connection, "drop table if exists public.simple_test;");
}
void show(nanodbc::result& results)
{
const short columns = results.columns();
long rows_displayed = 0;
cout << "\nDisplaying " << results.affected_rows() << " rows "
<< "(" << results.rowset_size() << " fetched at a time):" << endl;
// show the column names
cout << "row\t";
for (short i = 0; i < columns; ++i)
cout << results.column_name(i) << "\t";
cout << endl;
// show the column data for each row
while (results.next())
{
cout << rows_displayed++ << "\t";
for (short col = 0; col < columns; ++col)
cout << "(" << results.get<string>(col, "null") << ")\t";
cout << endl;
}
}
void usage(std::ostream& out, std::string const& binary_name)
{
out << "usage: " << binary_name << " connection_string" << std::endl;
}
int main(int argc, char* argv[])
{
if (argc != 2)
{
char* app_name = std::strrchr(argv[0], '/');
app_name = app_name ? app_name + 1 : argv[0];
if (0 == std::strncmp(app_name, "lt-", 3))
app_name += 3; // remove libtool prefix
usage(std::cerr, app_name);
return 1;
}
try
{
run_test(argv[1]);
}
catch (const exception& e)
{
cerr << e.what() << endl;
return 1;
}
}