0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Linux上のプログラムからODBCでDBMSに接続する

Last updated at Posted at 2025-01-26

はじめに

ここではLinux上のユーザープログラムが、同じくLinux上で稼働するDBMSにODBC経由で接続するための手順を示す。前提とするプラットフォームおよびDBMSは以下のとおり。

  • ユーザープログラムが稼働するOS : Alma Linux 9.5 DVD ISO
  • ユーザープログラムを構成する技術 : gccで作成したネイティブアプリ
  • DBMSが稼働するOS : Alma Linux 9.5 DVD ISO
  • DBMS : PostgreSQL 17.2
  • ODBC Driver : psqlODBC 17.00
  • ODBC Driver Manager : unixODBC 2.3.9

ユーザープログラムとDBMSの関係

ユーザープログラムは、何らかのConnectorを介してDBMSに接続する。DBMSごとに多種のConnectorが提供されており、ODBCはそのひとつであり、代表的なものとなる。
ODBC経由でDBMSに接続するには、DBMSの開発元から提供されているODBC Driverを予めクライアント側(ユーザープログラム側の環境)に組み込んでおく必要がある。また、ユーザープログラムは、ODBC Driver Managerを介してODBC Driverに接続する。
image.png
Windowsの場合、ODBC Driver Managerは、OSに組み込まれているため、その存在を特別意識しなくても良かったが、Linuxの場合、ODBC Driverと同様に、ODBC Driver Managerもクライアント側に組み込んでおく必要がある。
Linux用のODBC Driver Managerとしては、下記のものがよく知られている。

unixODBC

  • 概要: 最も一般的で広く使用されているオープンソースのODBC Driver Manager。
  • 特徴: 多くのODBC Driverやアプリケーションでサポートされている。
  • 利用シーン: 多くのLinuxディストリビューションにおける標準のODBC Driver Manager。
  • ライセンス: GNU General Public License (GPL) および GNU Lesser General Public License (LGPL)

iODBC (Independent Open DataBase Connectivity)

  • 概要: unixODBCと同様にオープンソースのODBC Driver Manager。最初にリリースされたODBC Driver Managerひとつで、クロスプラットフォーム対応を目指してる。
  • 特徴: macOSや他のUnix系システムでも広く使用されている。
  • 利用シーン: 特定の商用アプリケーションやiODBCに特化した環境で使われることがある。
  • ライセンス: BSD License

環境構築

DBMS、ODBC Driver/Driver Managerのインストール

DBMSのインストールについては、数多くの情報源があり、その設定方法も様々であるため、ここでは詳細を事細かに示すことはせず、PostgreSQLに絞り、最低限の記載にとどめる。また、ODBC Driver Managerについては、unixODBCを使用する。

PostgreSQLの最低限の動作に必要な、下記のパッケージをダウンロードする。
postgresql17-libs-17.2-1PGDG.rhel9.x86_64.rpm
postgresql17-17.2-1PGDG.rhel9.x86_64.rpm
postgresql17-server-17.2-1PGDG.rhel9.x86_64.rpm
postgresql17-odbc-17.00.0004-1PGDG.rhel9.x86_64.rpm

上記は、PostgreSQLの公式サイトからダウンロード可能である。
https://download.postgresql.org/pub/repos/yum/17/redhat/rhel-9-x86_64/

また、unixODBCは、RPMのダウンロードサイトからダウンロード可能である。
unixODBC-devel-2.3.9-4.el9.x86_64.rpm
unixODBC-devel-2.3.9-4-devel.el9.x86_64.rpm

ダウンロード後、下記のコマンドでインストールする。インストールする順番を間違えるとエラーとなる。

$ sudo rpm -ivh postgresql17-libs-17.2-1PGDG.rhel9.x86_64.rpm
$ sudo rpm -ivh postgresql17-17.2-1PGDG.rhel9.x86_64.rpm
$ sudo rpm -ivh postgresql17-server-17.2-1PGDG.rhel9.x86_64.rpm
$ sudo rpm -ivh unixODBC-devel-2.3.9-4.el9.x86_64.rpm
$ sudo rpm -ivh unixODBC-devel-2.3.9-4-devel.el9.x86_64.rpm
$ sudo rpm -ivh postgresql17-odbc-17.00.0004-1PGDG.rhel9.x86_64.rpm

PostgreSQL、ODBC Driver Managerの設定

PostgreSQLおよびunixODBCのインストール後、PostgreSQLおよびunixODBCの初期設定を行う。postgresユーザーでinitdbを実行する。コマンド実行時にpostgresユーザーのパスワードの設定を求められる。ここではパスワード=postgresとする。

$sudu su - postgres
$ /usr/pgsql-17/bin/initdb -E UTF8 --locale=C -A scram-sha-256 -W

postgresユーザーのまま、/var/lib/pgsql/17/data/pg_hba.confが下記のようになっていることを確認する。

local all all scram-sha-256
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256

同じく/var/lib/pgsql/17/data/postgresql.confを下記のように編集する。

listen_address='localhost'
Port=5432

このあと、postgresユーザーをexitで抜けて下記を実行する。

$ sudo systemctl start postgresql-17.service
$ sudo systemctl enable postgresql-17.service

postgresユーザーとして、psqlでデータベース、テーブル、レコードを追加する。

$ psql -U postgres
# create database testdb;
# \l
... testdbを含むデータベース一覧が表示される ...
# \c testdb
# create table abc (id integer, name varchar(255));
# insert into abc values (0, 'hello');

上記で、\l はデータベース一覧を表示するコマンドで、\cはデータベースに接続するコマンドである。
postgresユーザーでのpsqlの実行後、psqlを抜けて、/etc/odbc.iniおよび/etc/odbcinst.iniを変更する。

# odbcinst.ini:
[PostgreSQL]
Driver=/usr/pgsql-17/lib/psqlodbcw.so
Setup=/usr/lib64/unixODBC/libodbcpsqlS.so
Driver64=/usr/pgsql-17/lib/psqlodbcw.so
Setup64=/usr/lib64/unixODBC/libodbcpsqlS.so
FileUsage=1
# odbc.ini:
[PostgreSQL_DS]
Driver=PostgreSQL
Database=testdb
Servername=localhost
Port=5432
UserName=postgres
Password=postgres

下記isqlでtestdbに接続し、abcテーブルの情報を参照できることを確認する。

$ isql PostgreSQL_DS
SQL> select * from abc;

ユーザープログラム

下記のプログラムは、DBMSに接続し、切断するだけのサンプルコードである。下記でコンパイルおよび実行することができる。

$ gcc testdb.cpp -lodbc -lodbcinst -o testdb
$ ./testdb
#include <cstdlib>
#include <cstdio>
#include <sql.h>
#include <sqlext.h>

SQLHENV  henv;
SQLHDBC  hdbc;
SQLHSTMT hstmt;

// Return 0: Success, -1:Error
int open_database(SQLCHAR* connect_str, SQLCHAR statemsg[10], SQLCHAR msg[1024])
{
	SQLINTEGER native; // This will not be refered from anywhere
	SQLSMALLINT actual_msg_len; // This will not be refered from anywhere

	// Alloc environment handle
	if (SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv) == SQL_ERROR) {
		if (henv != SQL_NULL_HENV) {
			SQLGetDiagRec(SQL_HANDLE_ENV, henv, 1, statemsg, &native, msg, 1024, &actual_msg_len);
		}
		return -1;
	}
	SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, reinterpret_cast<SQLPOINTER>(SQL_OV_ODBC3), 0);

	// Alloc DB connection handle
	if (SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc) == SQL_ERROR) {
		SQLGetDiagRec(SQL_HANDLE_ENV, henv, 1, statemsg, &native, msg, 1024, &actual_msg_len);
		return -1;
	}

	// SQLDriverConnect
	SQLCHAR conn_out[255]; // This will not be refered from anywhere
	SQLSMALLINT conn_out_len; // This will not be refered from anywhere
	SQLRETURN Ret = SQLDriverConnect(hdbc, NULL, connect_str, SQL_NTS, conn_out, 255, &conn_out_len, SQL_DRIVER_COMPLETE);
	if (Ret == SQL_ERROR || Ret == SQL_SUCCESS_WITH_INFO) {
		SQLGetDiagRec(SQL_HANDLE_DBC, hdbc, 1, statemsg, &native, msg, 1024, &actual_msg_len);
		return -1;
	}

	// Alloc statement handle 
	if (SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt) == SQL_ERROR) {
		SQLGetDiagRec(SQL_HANDLE_DBC, hdbc, 1, statemsg, &native, msg, 1024, &actual_msg_len);
		return -1;
	}

	return 0;
}

// Return 0: Success, -1:Error
int close_database(SQLCHAR statemsg[10], SQLCHAR msg[1024])
{
	SQLINTEGER native; // This will not be refered from anywhere
	SQLSMALLINT actual_msg_len; // This will not be refered from anywhere

	// Free statement handle
	if (SQLFreeHandle(SQL_HANDLE_STMT, hstmt) == SQL_ERROR) {
		SQLGetDiagRec(SQL_HANDLE_DBC, hdbc, 1, statemsg, &native, msg, 1024, &actual_msg_len);
		return -1;
	}

	// SQLDisconnect
	SQLRETURN Ret = SQLDisconnect(hdbc);
	if (Ret == SQL_ERROR || Ret == SQL_SUCCESS_WITH_INFO) {
		SQLGetDiagRec(SQL_HANDLE_DBC, hdbc, 1, statemsg, &native, msg, 1024, &actual_msg_len);
		return -1;
	}

	// Free DB connection handle
	if (SQLFreeHandle(SQL_HANDLE_DBC, hdbc) == SQL_ERROR) {
		SQLGetDiagRec(SQL_HANDLE_ENV, henv, 1, statemsg, &native, msg, 1024, &actual_msg_len);
		return -1;
	}

	// Free environment handle
	if (SQLFreeHandle(SQL_HANDLE_ENV, henv) == SQL_ERROR) {
		SQLGetDiagRec(SQL_HANDLE_ENV, henv, 1, statemsg, &native, msg, 1024, &actual_msg_len);
		return -1;
	}

	return 0;
}

int main(int argc, char* argv[])
{
	SQLCHAR statemsg[10];
	SQLCHAR msg[1024];
	SQLCHAR connect_str[64] = "DSN=PostgreSQL_DS;";
	if (open_database(connect_str, statemsg, msg) == -1) {
		printf("statemsg=%s, msg=%s\n", (char*)statemsg, (char*)msg);
		return -1;
	}
	if (close_database(statemsg, msg) == -1) {
		printf("statemsg=%s, msg=%s\n", (char*)statemsg, (char*)msg);
		return -1;
	}
	printf("success!\n");
	return 0;
}

接続文字列の指定では、前述isqlの実行と同様にodbc.iniで設定したデータソース名'PostgreSQL_DS'を指定しているが、odbc.iniおよびodbcinst.iniに記載した個別設定をconnet_strに指定することもできる。

SQLCHAR connect_str[128] = "Driver=/usr/pgsql-17/lib/psqlodbcw.so;Server=127.0.0.1;Database=testdb;UID=postgres;PWD=postgres;Port=5432;";
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?