IfxPyでInformixに接続

PythonでInformixに繋ぐまともな方法はなかった。
あるとすれば以下の2つ。

- PyODBCでODBC経由
- JayDeBeApiでJDBC経由

遂にIfxPyがやってきた。

##IfxPy

- Python2/3 対応のネイティブドライバー
- pip install IfxPy インストール可能

[IfxPy - PyPi](https://pypi.org/project/IfxPy/)
[IfxPy - Github](https://github.com/OpenInformix/IfxPy)

素晴らしい！
今までJayDeBeApiでInformixに繋いでいた。
IfxPyだけで繋げられるようになると非常に楽になる。

早速試してみることにした。

## 環境

**Client**

- vagrant box [generic/debian10](https://app.vagrantup.com/generic/boxes/debian10)
- Informix Client SDK 4.50FC1
- CLIENT_LOCALE=ja_jp.utf8

```bash:Locale
$ localectl
System Locale: LANG=ja_JP.UTF-8
               LANGUAGE=ja_JP:ja
    VC Keymap: n/a
    X11 Layout: us
    X11 Model: pc105
```

**Server**

- CentOS7
- Informix Dynamic Server 12.10FCWE12
- DB_LOCALE=ja_jp.sjis-s


## 手順

```bash:
# make csdk folder
$ mkdir csdk
# extract
$ tar -xvf clientsdk.4.10.FC9DE.LINUX.tar -C csdk
$ cd csdk
# Install Library for installclientcsdk 
$ sudo apt -y install libaio1 bc libncurses5 ncurses-bin libpam0g
$ sudo apt -y install libncurses5-dev libelf1 

# Install
$ sudo ./installclientcsdk -i console

# SET Environment Variables
# export INFORMIXDIR=/opt/IBM/informix # CSDK 4.10 
export INFORMIXDIR=/opt/IBM/Informix_Client-SDK #CSDK 4.50
export LD_LIBRARY_PATH=${INFORMIXDIR}/lib:${INFORMIXDIR}/lib/esql:${INFORMIXDIR}/lib/cli

# Make sqlhosts
$ sudo cp /opt/IBM/Informix_Client-SDK/etc/sqlhosts.std /opt/IBM/Informix_Client-SDK/etc/sqlhosts
$ vi /opt/IBM/Informix_Client-SDK/etc/sqlhosts
# ...etc....
servername        onsoctcp        serverip      21435

# SET DB_LOCALE
$ export DB_LOCALE=ja_jp.sjis-s

# SET CLIENT_LOCALE
$ export DB_LOCALE=ja_jp.utf8

# pip install
$ sudo apt install python3-pip

# Install IfxPy
$ pip3 install IfxPy --user

# make python file
$ vi ifxcnnt.py

# Execute Python3
$ python3 ifxcnnt.py
  Traceback (most recent call last):
   File "ifxcnnt.py", line 6, in <module>
     conn=IfxPy.connect(ConStr,"","")
 Exception: [Informix][Informix ODBC Driver][Informix]Error opening required code-set conversion
```

あれ？
繋がらないぞ。

GitHubにIssue立てた。
[Don't deal Japanese](https://github.com/OpenInformix/IfxPy/issues/23)

jsagrera氏によるとConnectionStringにDB_LOCALEとCLIENT_LOCALEを書き足すと繋がるそうだ。

下記のように書く。

```python:code
#! /usr/bin/env python3
import IfxPyDbi as dbapi2

ConnectionString="SERVER=servername;DATABASE=database;HOST=IP;SERVICE=21435;UID=username;PWD=password;PROTOCOL=onsoctcp;CLIENT_LOCALE=ja_jp.utf8;DB_LOCALE=ja_jp.sjis-s"
  
 conn=dbapi2.connect(ConnectionString,"","")
 cur=conn.cursor()
 cur.execute("select 得意先名 from 得意先m where 得意先cd=1")
 rows=cur.fetchall()
 
 for i,row in enumerate(rows):
 	print("Row",i,"value=",row)

cur.close()
conn.close()
 
print("Done")
```

動くかな？

```bash:result
$ python3 ifxfetchall.py
Row 0 value= ('株式会社                            ',)
Done
```

よし！動いた。

## 日本語カラム対策

日本語のカラム名を指定すると動かない関数がある。
僕が探した中では下記の4つ。


- ifxPy.fetch_both
- ifxPy.fetch_assoc
- IfxPy.field_name
- IfxPy.result

SQLのASを使ってカラム名をASCIIな名前に変更する。
それで回避できる。

ifxPy.fetch_bothを例にしてみる。

```python:ifxPy.fetch_both
# Connection ConnectionStringなど省略

sql="SELECT 得意先cd as customer_cd, 得意先名 as customer_name from 得意先m where 得意先cd=1 or 得意先cd=2"
stmt=IfxPy.exec_immediate(conn,sql)
dic=IfxPy.fetch_both(stmt)


while dic != False:
   print(dic["customer_cd"])
   print(dic["customer_name"])
   dic=IfxPy.fetch_both(stmt)

IfxPy.free_result(stmt)
IfxPy.free_stmt(stmt)

IfxPy.close(conn)

```

さあ動くかな？

```bash:result
$ python3 ifxfetchboth.py 
2
株式会社　東京支店                  
1
株式会社               
```

よし動いた！

## 終わりに

僕が建てたGitHub Issueでは

- 4つの関数が日本語カラムを扱えない。
- それらのCのコードのPyUnicode_AsASCIIString()のような関数が悪さをしている


を伝えておいた。
次の次あたりに治っているといいな。