IBMi db2へ、PHP ODBCから接続する

### 導入
こんにちは。phpでお仕事してます。
お仕事の傍、php-srcのコントリビュータもしています。

今回、phpの`odbc_connect()`および`odbc_pconnect()`の改修をするにあたり、いろんなODBCドライバの挙動を見る必要が出てきてIBMi db2に初めて触ることになりました。

日本語にしろ英語にしろまとまった情報源がなく、かなり手探りで接続までたどり着いたので、備忘録を兼ねて記事にすることにしました

なお、この記事では

- DBにPUB400のIBMi db2を使用
- phpは8.4（執筆時点のphp-src masterブランチのビルド）、localのdocker ubuntu22.04環境

での操作の備忘録となります（ただし、php-srcのビルドは本記事の趣旨と逸れるので、割愛します）。

### IBMi db2とは
IBMiに搭載されているdb2のことです。IBMiについての説明はwikipedia先生に任せます。

[IBMi(Wikipedia)](https://ja.wikipedia.org/wiki/IBM_i)

Linuxなどとは全然違うので、同じdb2でもドライバが違っていたりします。

### IBMiを利用する

IBMiの実機を気軽に手に入れるのは現実的ではないので、IBMiの学習用サービスを利用します。

[PUB400.COM - Your public IBM i server](https://www.pub400.com/)

こちらのサービスでは、400MBまでですが無料でIBMiを使用することができます（学習用です）。

#### PUB400に登録する

登録すると、結構経ってから登録確認メールが届きます。メールアドレスの認証を終えると初期ログイン情報その他諸々が記載されたメールが届きます。

#### 初期設定を行なう

初期設定とは言っても、初期パスワードを変更するだけです。初期パスワードの変更をした後なら、SSH接続もできるようになります。

では最初の接続はどうするか？というと、[IBM 5250](https://ja.wikipedia.org/wiki/IBM_5250)のターミナルエミュレータからTelnetで接続します。
~~IBM 5250の実機をお持ちの方はそちらを使用してもいいと思います。~~

私はMacOSなので、「Mocha TN5250 Lite」を利用しました。

##### Mocha TN5250 Liteの設定

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570710/91d3f351-4b90-9d10-48c8-ecaf5c32b49d.png" alt="Mocha TN5250 Liteの設定例" width="500">

設定項目が色々ありますが、

- IPに「PUB400.COM」
- Userに、メールに記載されているユーザ名
- Passwordに、初期パスワード

を入力する以外はデフォルトのままで問題ありません。

「設定を保存する」というようなボタンなどないロックな仕様なので、「×」で設定画面を閉じます。

##### いざIBMiに接続

上部メニューの「Open」を押下すると、接続できます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570710/14f64119-c2cc-c6c7-8067-4fb74f00e2f7.png" alt="IBMiのパスワード変更画面" width="500">

初手、パスワードの変更を求められます。

ちなみに、入力欄はEnterやTABで移動するのではなく普通に十字キーで移動します。画像のカーソル位置が文字入力開始位置なのですが、**普通に後ろ方向へもカーソルが移動できます**。

**そして、開始位置ピッタリからちゃんと入力しないとエラーで怒られます。なんてロック。**

最後のドットの真横の位置から、カーソル2つ分右に移動したところが文字入力開始位置です。

全部入力したら、Enter押下でパスワードが更新されます。~~怒られる場合は大体カーソルの位置が間違っています。~~

以上で、db2に接続するためのIBMi側の操作は終了です。

### ubuntu側の準備をする

php本体とodbc extをインストールした状態だとします。
他、curl, unixodbc, unixodbc-devあたりが入っていると良いと思います（php odbcが入っている時点でunixodbc系は多分入っているのではないかと思います）。

#### IBMのリポジトリを追加する

ibm-iaccessというパッケージをインストールすることで、IBMi db2用のODBCドライバが手に入ります。
そのために、IBMのリポジトリを追加します。

ubuntuでは次のコマンドで追加できます。apt系なら同じなので、Debianもこれでいけます。dockerなのでsudoはつけません。環境によってはつけてください。
```
curl https://public.dhe.ibm.com/software/ibmi/products/odbc/debs/dists/1.1.0/ibmi-acs-1.1.0.list | tee /etc/apt/sources.list.d/ibmi-acs-1.1.0.list
```

Redhat系ならこちら。こちらもsudoは環境次第です。
```
curl https://public.dhe.ibm.com/software/ibmi/products/odbc/rpms/ibmi-acs.repo | tee /etc/yum.repos.d/ibmi-acs.repo
```

#### ibm-iaccessをインストール

リポジトリを追加したら、updateしてからibm-iaccessをインストールします。これもsudoは環境次第。Redhat系はyumで読み替えてください。
```
apt-get update
apt-get install ibm-iaccess
```

#### odbcinst.iniの確認

odbcの設定ファイルとも言える`odbcinst.ini`を確認します。ubuntu22.04のdocker環境では、次の場所にありました。
```
/etc/odbcinst.ini
```

docker使うのであればvolumeをマウントしてしまって、ホストOSからすぐ確認できるようにしておくと便利です。

ibm-iaccessをインストールすると、次のような項目が追加されました。

```
[IBM i Access ODBC Driver]
Description=IBM i Access for Linux ODBC Driver
Driver=/opt/ibm/iaccess/lib/libcwbodbc.so
Setup=/opt/ibm/iaccess/lib/libcwbodbcs.so
Driver64=/opt/ibm/iaccess/lib64/libcwbodbc.so
Setup64=/opt/ibm/iaccess/lib64/libcwbodbcs.so
Threading=0
DontDLClose=1
UsageCount=1

[IBM i Access ODBC Driver 64-bit]
Description=IBM i Access for Linux 64-bit ODBC Driver
Driver=/opt/ibm/iaccess/lib64/libcwbodbc.so
Setup=/opt/ibm/iaccess/lib64/libcwbodbcs.so
Threading=0
DontDLClose=1
UsageCount=1
```

自動で設定されるって楽ですね...。
私は64bit環境ですので、64ibitの方を使用します。

`[]`で括られている文字列`IBM i Access ODBC Driver 64-bit`を、後で接続文字列として使用します。

ここまででubuntuの準備は完了です。

### phpを書く

接続用のphpを書きます。こんな感じです。

```
<?php

$db = odbc_pconnect(
    'Driver={IBM i Access ODBC Driver 64-bit};System=PUB400.COM;Port=50000;protocol=TCPIP',
    'ユーザ名',
    'パスワード'
);
```

接続文字列については、次のようになります（uidとpwdをここに含めることもできるのですが、ちょっとその辺り今改修中で色々どうなるかわからないので触れません）。

- Driver => 先ほど確認した`odbcinst.ini`の項目名
- System => 要はホスト名。PDO MySQLで言うところの`host`と同義
- Port => 50000
- protocol => TCPIP

ユーザー名とパスワードは、IBMiの認証情報と全く同じです。**ロック**。

### いざ実行

phpを実行してみましょう。
**認証に成功すると、何も出力されずに終了します**。

認証失敗時は次のようなエラーになります。

```
Warning: odbc_pconnect(): SQL error: [IBM][System i Access ODBC Driver]Communication link failure. comm rc=8002 - CWBSY0002 - Password for user {ユーザ名} on system PUB400.COM is not correct, Password length = 0, Prompt Mode = Never, System IP Address = {多分相手のIP}, SQL state 28000 in SQLConnect in {phpファイル}
```

また、レアケースですがシンタックスエラーを引いた場合は次のようになります（こうならないように今改修頑張ってます）。
```
Warning: odbc_pconnect(): SQL error: [IBM][System i Access ODBC Driver]Syntax error in connection string., SQL state S1000 in SQLConnect in {phpファイル}
```

### まとめ

IBMiを比較的手軽に利用できるサービスがあるのはとても助かります。

情報が少ないというか、あるにはあるのですがかなり断片化していて、最短かつ正解の手順の確立まで結構苦労しました。

phpでIBMi db2へODBCで接続しようとして困っているとても珍しい人のお役に立てたら幸いです。