AlmaLinux9でMySQL connector for unixODBCをビルドする

2022/08/17に公開

はじめに

AlmaLinux9でunixODBCを利用する必要が発生しました。
しかしrpmではel8までしかなかったのでソースからビルドすることにしました。
その際の手順となります。

ソースのダウンロード

  • mysql-connector-odbc-8.0.30-src.tar.gz
  • 以下のURLから取得

https://dev.mysql.com/downloads/connector/odbc/

  • Oracleからダウンロードなのでアカウントが必要
  • 「Source Code」を選択
  • 「All Operating Systems (Generic) (Architecture Independent), Compressed TAR Archive」を選択してダウンロードする。
  • インストールするサーバ上に配置して展開する。
  • 以下の例では/tmpに配置
mkdir -p /root/working
cd /root/working
dnf install -y tar
tar zxvf /tmp/mysql-connector-odbc-8.0.30-src.tar.gz
cd mysql-connector-odbc-8.0.30-src

ビルド手順

追加パッケージ

  • 一部過不足あるかも?試行錯誤しながら実施したため
dnf install -y gcc g++ make
dnf --enablerepo=crb install -y mysql-devel
dnf --enablerepo=crb install -y unixODBC-devel
dnf --enablerepo=crb install -y libzstd-devel
dnf install -y zlib-devel
dnf install -y openssl-devel
dnf --enablerepo=crb install -y mysql-libs

ビルド & インストール

  • インストール先は/usr/local/lib
  • 参考ページ

https://dev.mysql.com/doc/connector-odbc/en/connector-odbc-installation-source-unix.html

cmake -G "Unix Makefiles" -DWITH_UNIXODBC=1 -DDISABLE_GUI=1
make
make install

unixODBCの設定

/etc/odbcinst.ini

  • 次の内容で更新
[ODBC Drivers]
MyODBCw=Installed
MyODBCa=Installed
/usr/local/lib/libmyodbc8w.so=Installed
/usr/local/lib/libmyodbc8a.so=Installed
UsageCount=2

[MyODBCw]
Driver=/usr/local/lib/libmyodbc8w.so
Description=Unicode Driver for connecting to MySQL database server
Threading=0
UsageCount=2

[/usr/local/lib/libmyodbc8w.so]
Driver=/usr/local/lib/libmyodbc8w.so
Description=Unicode Driver for connecting to MySQL database server
Threading=0
UsageCount=2

[MyODBCa]
Driver=/usr/local/lib/libmyodbc8a.so
Description=ANSI Driver for connecting to MySQL database server
Threading=0
UsageCount=2

[/usr/local/lib/libmyodbc8a.so]
Driver=/usr/local/lib/libmyodbc8a.so
Description=ANSI Driver for connecting to MySQL database server
Threading=0
UsageCount=2

確認

odbcinst -q -d

/etc/odbc.ini

  • 次の内容で更新
[MySQL_TEST1]
Driver = MyODBCw
Description = Test for MySQL 1
User = dbuser
Password = Pass@word1
Database = test_db
Server = xxx.xxx.xxx.xxx
Port = 3306
  • ユーザ/パスワード/データベースは適宜変更
  • 最初の[MySQL_TEST1]はDSNという名前付けで任意に変更可能
  • DSNは利用する際に設定を選択するための文字列
  • DSNを使用しない接続設定方法もある

確認

odbcinst -q -s

確認用MySQLの内容

  • database名は「test_db」
  • ユーザ名は「dbuser」
  • パスワードは「Pass@word1」
  • 参考ページ

https://self-development.info/【コピペだけでok】mysqlへの外部接続を許可する方法/

ユーザとDATABASE作成

CREATE USER 'dbuser'@'%' IDENTIFIED BY 'Pass@word1';
CREATE DATABASE test_db;
GRANT ALL PRIVILEGES ON test_db.* TO 'dbuser'@'%';
FLUSH PRIVILEGES;
  • mysql-connector-odbc-8.0.30では「WITH mysql_native_password」は不要

テーブル作成

test1_create.sql
CREATE TABLE `test1` (
	`id` INT(10) NOT NULL AUTO_INCREMENT,
	`v` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8mb4_bin',
	PRIMARY KEY (`id`) USING BTREE
)
COLLATE='utf8mb4_bin'
ENGINE=InnoDB
AUTO_INCREMENT=0
;

データのINSERT

test1_insert.sql
INSERT INTO `test1` (`id`, `v`) VALUES (1, 'TEST1');
INSERT INTO `test1` (`id`, `v`) VALUES (4, 'あああ');
INSERT INTO `test1` (`id`, `v`) VALUES (2, 'TEST2');
INSERT INTO `test1` (`id`, `v`) VALUES (3, 'TEST333');

動作確認 ・・・ python3

test1.py
import pyodbc
 
cnxn = pyodbc.connect('DSN=MySQL_TEST1')
cursor = cnxn.cursor()
cursor.execute("SELECT id,v FROM test1")
rows = cursor.fetchall()
for row in rows:
    print(row.id, row.v)

実行結果

1 TEST1
2 TEST2
3 TEST333
4 あああ

動作確認 ・・・ c++

  • pythonと同じSQLを実行
  • 文字はutf8mb4_bin前提
test2.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sql.h>
#include <sqlext.h>

int main( int argc, char* argv[] ) 
{
    SQLHENV env;
    SQLHDBC dbc;
    SQLHSTMT stmt;
    SQLRETURN ret;
    SQLSMALLINT columns;
    int row = 0;

    char sql[1024] = "";
    sprintf(sql, "SELECT id,v FROM test1");

    SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
    SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
    SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);

    SQLDriverConnect(dbc, NULL, (SQLCHAR*)"DSN=MySQL_TEST1", SQL_NTS,NULL, 0, NULL, SQL_DRIVER_COMPLETE);
    SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
    SQLExecDirect(stmt,(SQLCHAR*) sql, SQL_NTS);
    SQLNumResultCols(stmt, &columns);

    while (SQL_SUCCEEDED(ret = SQLFetch(stmt))) {
        SQLUSMALLINT i;
        printf("row[%d]\n", row);

        for (i = 1; i <= columns; i++) {
            SQLLEN len;
            char buf[512];
            memset(buf,0,sizeof(buf));
            ret = SQLGetData(stmt, i, SQL_C_BINARY,buf, sizeof(buf), &len);

            if (SQL_SUCCEEDED(ret)) {
                printf("  items[%d]=`%s`\n", i - 1, buf);
            }
        }
        row+=1;
    }
    return 0;
}

ビルド方法

gcc test2.cpp -lodbc -o test2

実行結果

row[0]
  items[0]=`1`
  items[1]=`TEST1`
row[1]
  items[0]=`2`
  items[1]=`TEST2`
row[2]
  items[0]=`3`
  items[1]=`TEST333`
row[3]
  items[0]=`4`
  items[1]=`あああ`

その他①・MySQL8で外部接続可能なrootの作成

  • パスワードは適宜変更
CREATE USER 'root'@'%' IDENTIFIED BY 'kKv2z7x2pFHVA4h3sJTiFeKr';
GRANT all PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;

その他②・crb

  • enablerepo=crbのcrbは「codeready-builder」の略

https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/8/html/package_manifest/codereadylinuxbuilder-repository

dnf config-manager --set-enabled crb
dnf install -y epel-release

その他③・SQL_C_BINARY

C++でマルチバイト文字を扱うときには常に悩みどころが存在します。
今回はutf8mb4_binで統一しているので、SQLGetDataのTargetType指定をSQL_C_BINARYとすることで、UTF-8のバイトデータが取得しています。

その他④・ODBCでSQLを発行するCLI

iusql MySQL_TEST1

Discussion