C言語+ODBC で Db2 に接続してみた 

## C言語からDb2へアクセスするには

C言語で作成するプログラムからDb2データベースにアクセスするにあたり、以下のようなプログラミング・インターフェースを利用できるようです。

 1. [ODBC](https://learn.microsoft.com/ja-jp/sql/odbc/reference/what-is-odbc?view=sql-server-ver16) (このメモでこれから試そうとしている方法)
 2. [Db2 CLI](https://www.ibm.com/docs/ja/db2/11.5?topic=odbc-processing-transactions)
 2. [組み込みSQL](https://www.ibm.com/docs/ja/db2/11.5?topic=da-embedded-sql) ( ---> [別記事「Db2で組み込みSQLのC言語アプリケーションを実行してみた」](https://qiita.com/mi-kana/items/2e716e5e54e1d4f795e5)で試行)

## C言語＋ODBCでデータベース接続する方法が知りたい

古くからある手法ですし、きっとしかるべき書物を紐解けば精緻な情報にたどり着けそうな基礎レベルの話のはずですが、案外Google検索しても情報がなかなか見つからないので、

 * ソースコード
 * コンパイル方法(必要なオプション)
 * 実行方法

など、整理してみようと思いました。

といってもC言語スキルはありませんので、こちらのサイトのサンプルコードを拝借して動かしてみます。
[プログラミングの勉強をしよう@Wiki     Windows-C > サンプル > DB2](https://w.atwiki.jp/m_shige1979/pages/601.html)

上のサイトではWindows環境で実行されていますが、Windows10/Db2 11.5環境ではコンパイルがうまくいかなかったため、Linux環境でコンパイル/実行しました。


## 前提：

 * Cのコンパイラ(gcc)が導入されていること
 * Db2サーバ、Db2 ODBC Driverが導入されていること
 * Linux環境におけるDb2-ODBCアプリ接続のための事前構成が済んでいること<br>  ---> [HOWTO: Setup ODBC application connectivity on Linux](https://www.ibm.com/support/pages/node/521323)
	 * この「HOWTO..」のTehnote、製品マニュアルに情報が整理されてない中で大変有難い存在ながら、見づらいし db2dsdriver.cfg ファイルのパスが間違ってるとか言いたいことは色々あります..
	 * db2dsdriver.cfgファイルは「\<odbcdriver_instroot\>/cfg/db2dsdriver.cfg」に置いてください



## 環境：

 * Red Hat Enterprise Linux Server release 7.9 (Maipo)
 * gcc version 4.8.5 20150623 (Red Hat 4.8.5-44)
 * unixODBC-2.3.1-14.el7.x86_64
 * IBM Data Server Driver for ODBC and CLI (Linux/x86-64 64 bit) V11.5.5 Fix Pack 1
 * Db2 11.5.5.1


## サンプルコード：

引用元：[プログラミングの勉強をしよう@Wiki     Windows-C > サンプル > DB2](https://w.atwiki.jp/m_shige1979/pages/601.html)

```db2_sample01.c
/* Standard C/C++ Headers */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
/* DB2 ODBC Header */
#include <sqlcli1.h>
 
int main(int argc, char** argv)
{
    /* Handles */   
    SQLHENV  henv = SQL_NULL_HENV;
    SQLHDBC  hdbc = SQL_NULL_HDBC;
    SQLHSTMT hstmt= SQL_NULL_HSTMT;
 
    /* Local variables */   
    SQLRETURN     rc;   
    SQLCHAR       statement[32761];
    SQLCHAR       mycolout[12];
    SQLINTEGER    mycoloutlen = 0;
    char          dbname[8 + 1];
    char          user[18 + 1];
    char          pswd[30 + 1];
 
    if (argc < 4) {       
        printf ("Usage: dbconn_ODBC_CLI <database name> <userid> <password>\n");
        exit (0);   
    }   
    strcpy (dbname, argv[1]);
    strcpy (user,   argv[2]);
    strcpy (pswd,   argv[3]);
 
    rc = SQLAllocHandle(SQL_HANDLE_ENV,
                        SQL_NULL_HANDLE,
                        &henv);   
 
    rc = SQLSetEnvAttr(henv
                     , SQL_ATTR_ODBC_VERSION
                     , (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);
 
    rc = SQLAllocHandle(SQL_HANDLE_DBC,
                        henv,
                        &hdbc);
 
    /* connect to the database */
    rc = SQLConnect(hdbc
                  , (SQLCHAR *)dbname
                  , SQL_NTS
                  , (SQLCHAR *)user
                  , SQL_NTS
                  , (SQLCHAR *)pswd
                  , SQL_NTS);
    printf ("\n  Successful connection to '%s' db using ODBC/CLI \n",dbname);
 
    rc = SQLAllocHandle(SQL_HANDLE_STMT,
                        hdbc,
                        &hstmt);
 
    strcpy(statement,"SELECT empno");
    strcat(statement,"  FROM DB2INST1.EMPLOYEE;");
 
    rc = SQLExecDirect(hstmt
                     , statement
                     , SQL_NTS);
 
    rc = SQLBindCol(hstmt
                  , 1
                  , SQL_C_CHAR
                  , mycolout
                  , sizeof(mycolout)
                  , &mycoloutlen);
 
    while((rc = SQLFetch(hstmt)) == SQL_SUCCESS
            || rc == SQL_SUCCESS_WITH_INFO){
        /* Print data */     
        printf("  Successful retrieval of record.  Column 'empno' has a value of '%s'\n",mycolout);
    }
 
    rc = SQLCloseCursor(hstmt);   
    rc = SQLFreeStmt(hstmt, SQL_RESET_PARAMS);
    rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
    rc = SQLDisconnect(hdbc);
    printf ("  Successful Disconnection from database '%s'\n",dbname);
 
    rc = SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
    rc = SQLFreeHandle(SQL_HANDLE_ENV, henv);
    printf ("  End of Program\n");
    return 0;
 
}
```



## コンパイル：

```
# su - db2inst1
$ gcc -I"/database/config/db2inst1/sqllib/include" db2_sample01.c -o db2_sample01 -Wl,-rpath,/database/config/db2inst1/sqllib/lib64 -L/database/config/db2inst1/sqllib/lib64 -ldb2
[db2inst1@2a4bb38ac023 c]$ ls -ltr
total 106556
-rw-rw-r--. 1 db2inst1 db2iadm1 108615680 Apr 13 06:43 v11.5.5fp1_linuxx64_odbc_cli.tar
-rw-rw-r--. 1     1003     1004    471511 Apr 13 10:21 unixODBC-2.3.9-1.fc33.x86_64.rpm
-rw-r--r--. 1 db2inst1 db2iadm1      2567 Apr 13 11:14 db2_sample01.c
-rwxr-xr-x. 1 db2inst1 db2iadm1     13064 Apr 13 11:25 db2_sample01 　　← 生成された

```

## 実行 ---> 成功！

コンパイルの結果生成された実行ファイル(db2_sample01) に引数を与えて実行すると、指定したDB/ユーザID/パスワードにてDb2に接続出来ることが確認できます。

```
$ ./db2_sample01 testdb db2inst1 cicdtest

  Successful connection to 'testdb' db using ODBC/CLI
  Successful Disconnection from database 'testdb'
  End of Program
$

```

Windows用に開発されたコードなのでLinuxでは動かない可能性もありましたが、運よく無事動きました。


## 備考：コンパイルエラー(オプション不足)

Db2 11.5 (64bit) にアクセスしたいCプログラムをコンパイルするには、このオプションが必須：
「-L/database/config/db2inst1/sqllib/lib64 -ldb2」

オプションを入れ忘れてコンパイルしようとすると、以下のようなエラーが出ます。

```
$ gcc -I"/database/config/db2inst1/sqllib/include" db2_sample01.c -o db2_sample01.exe
/tmp/cc6tx5sJ.o: In function `main':
db2_sample01.c:(.text+0xc7): undefined reference to `SQLAllocHandle'
db2_sample01.c:(.text+0xe4): undefined reference to `SQLSetEnvAttr'
db2_sample01.c:(.text+0xfb): undefined reference to `SQLAllocHandle'
db2_sample01.c:(.text+0x136): undefined reference to `SQLConnect'
db2_sample01.c:(.text+0x166): undefined reference to `SQLAllocHandle'
db2_sample01.c:(.text+0x200): undefined reference to `SQLExecDirect'
db2_sample01.c:(.text+0x232): undefined reference to `SQLBindCol'
db2_sample01.c:(.text+0x25b): undefined reference to `SQLFetch'
db2_sample01.c:(.text+0x277): undefined reference to `SQLCloseCursor'
db2_sample01.c:(.text+0x28a): undefined reference to `SQLFreeStmt'
db2_sample01.c:(.text+0x29d): undefined reference to `SQLFreeHandle'
db2_sample01.c:(.text+0x2ab): undefined reference to `SQLDisconnect'
db2_sample01.c:(.text+0x2d7): undefined reference to `SQLFreeHandle'
db2_sample01.c:(.text+0x2ea): undefined reference to `SQLFreeHandle'
collect2: error: ld returned 1 exit status
```



(以上です)