刹那(せつな)の瞬き

Willkömmen! Ich heiße Setsuna. Haben Sie etwas Zeit für mich?

Ubuntu20.10でnanodbcからODBCの対応状況を確認する

Ubuntu というか Linux 環境で RDBMS 、特に SQL ServerODBC 接続していると、ついつい Windows 環境と同等な動作を期待してしまいます。

そもそも前提が異なるので完全互換は期待してません。
それでも、どの程度対応してるのかな?と思い立ったので、C++ ライブラリの nanodbc を利用して確認してみました。

環境

  • OS: Ubuntu 20.10
  • g++ (Ubuntu 10.2.0-13ubuntu1) 10.2.0
  • nanodbc v2.13.0
  • unixODBC 2.3.7

ODBC ドライバ

私の手元にある ODBC ドライバについて調査してみました。

ドライバ SQL_DRIVER_NAME SQL_DRIVER_VER パッケージ
msodbc libmsodbcsql-17.6.so.1.1 17.06.0001 公式サイト
freetds libtdsodbc.so 01.02.0003 tdsodbc
mysql libmyodbc8w.so 08.00.0022 公式サイト
maria libmaodbc.so 03.01.0009 odbc-mariadb
pg psqlodbcw.so 12.02.0000 odbc-postgresql

※表現の都合上、勝手にドライバに略名をつけてます。

調査結果

各ドライバの調査結果の一部を載せておきます。

パラメータ (sqlext.h) msodbc freetds mysql maria pg
SQL_DRIVER_ODBC_VER 03.52 03.50 03.80 03.51 03.51
SQL_ASYNC_MODE 2 0 0 0 0
SQL_ACTIVE_CONNECTIONS 0 0 0 0 0
SQL_ACTIVE_STATEMENTS 1 1 0 0 0
SQL_ODBC_API_CONFORMANCE 2 2 1 1 1
SQL_ODBC_SQL_CONFORMANCE 1 1 1 1 1
SQL_MULT_RESULT_SETS Y Y Y Y Y
SQL_ACTIVE_ENVIRONMENTS 0 0 0 0 0
SQL_SQL_CONFORMANCE 1 1 4 4 1
SQL_MAX_ASYNC_CONCURRENT _STATEMENTS 1 1 0 0 core dump
SQL_ODBC_INTERFACE
_CONFORMANCE
3 2 2 1 1
SQL_ATTR_ODBC_VERSION 380 380 380 380 380
SQL_ATTR_AUTOCOMMIT 1 1 1 4 1

unixODBC の SQL_ODBC_VER は 03.52 です。
SQL_ATTR_ODBC_VERSION が 3 ではなく 380 なのは、nanodbc の設定です。

残念ながら ODBC 3.8 の Driver-Aware Connection Pooling や 非同期通知等は使えないようです。

ソースコード

nanodbc::connection の get_info<T>() を利用すると手軽に情報を取得できます。

get_info<T>() は ODBC API の SQLGetInfo() を呼び出してます。
環境属性や接続属性を調査する場合は、ハンドルが用意されているので、

  • SQLGetEnvAttr() には native_env_handle()
  • SQLGetConnectAttr() には native_dbc_handle()

を指定すると情報を取得できます。

以下は、冗長な部分を省いたコードのサンプルです。

・ソースファイル (main.cpp)

#include <iostream>
#include <sql.h>
#include <sqlext.h>
#include <nanodbc/nanodbc.h>

int main() {
    std::locale::global(std::locale(""));
    std::string conn_str = "Driver={ODBC Driver 17 for SQL Server};Server=localhost;UID=sa;PWD=abcd1234$;Database=my_test_db;";
    //std::string conn_str = "Driver={FreeTDS};ServerName=mssql-server;UID=sa;PWD=abcd1234$;Database=my_test_db;";
    //std::string conn_str = "Driver={MySQL ODBC 8.0 Driver};Server=localhost;UID=dareka;PWD=dareda;Database=my_test_db;";
    //std::string conn_str = "Driver={MariaDB Unicode};Server=localhost;UID=dareka;PWD=dareda;Database=my_test_db;";
    //std::string conn_str = "Driver={PostgreSQL Unicode};Server=localhost;UID=dareka;PWD=dareda;Database=my_test_db;";
    try
    {
        nanodbc::connection conn(conn_str);
        std::cout << "get_info()" << std::endl;
        {
            auto ret_ver = conn.get_info<std::string>(SQL_DRIVER_ODBC_VER);
            std::cout << "-- get_info(SQL_DRIVER_ODBC_VER) --> " << ret_ver << std::endl;
            auto ret_am = conn.get_info<SQLUINTEGER>(SQL_ASYNC_MODE);
            std::cout << "-- get_info(SQL_ASYNC_MODE) --> " << ret_am  << std::endl;
        }
        std::cout << "SQLGetEnvAttr()" << std::endl;
        {
            SQLINTEGER val = 0;
            SQLRETURN ret = SQLGetEnvAttr(conn.native_env_handle(), SQL_ATTR_ODBC_VERSION, &val, sizeof(val), nullptr);
            std::cout << "-- SQL_ATTR_ODBC_VERSION : ret = " << ret << " / val = "  << val  << std::endl;
        }
        std::cout << "SQLGetConnectAttr()" << std::endl;
        {
            SQLINTEGER val = 0;
            SQLRETURN ret = SQLGetConnectAttr(conn.native_dbc_handle(), SQL_ATTR_ASYNC_ENABLE, &val, sizeof(val), nullptr);
            std::cout << "-- SQL_ATTR_ASYNC_ENABLE : ret = " << ret << " / val = "  << val  << std::endl;
        }
    }
    catch (const std::exception& e)
    {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

・CMakeLists.txt

cmake_minimum_required(VERSION 3.0)
project(odbc_getinfo CXX)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
find_package(nanodbc REQUIRED)
add_executable(odbc_getinfo main.cpp)
target_link_libraries(odbc_getinfo PRIVATE pthread nanodbc)

所感 

当初、昔を思い出しながら SQLAllocHandle() で環境ハンドル用意して、SQLSetEnvAttr() で ODBC バージョン指定して、SQLAllocHandle() で接続ハンドル用意して...と、C で簡単なプログラムを書いて環境を調べました。
とりあえず目的は達成できたのですが、煩雑すぎて不満が募ります。

そこで、以前調査した nanodbc を使ってみたのですが、やっぱりお手軽です。
Poco や Qt5 より軽量と言うか薄い分、Win32API っぽいコーディングはやりやすいかも。