はじめに
RDBであるIBM Db2 データベースのアクセス方法として、
- JavaやCなどで書かれたプログラムから接続して、SQLを実行する
- シェルスクリプトからコマンドを実行して、ExportやLoadなどを実行する
といった使われ方が最も多いと思いますが、Db2 11.5 からは REST API によるアクセスもできるようになっています。
任意のSQLを実行するRESTサービスを登録しておき、呼び出すことができます。
Db2 における REST API サポート
DBaaSである Db2 on Cloud, Db2 Warehouse on Cloud の場合には
テーブル作成/削除、Load、バックアップ・リストア、ユーザ管理、性能監視、CPUコア数やストレージ数の変更、災害発生時のサーバ切替、、など非常に数多くの REST API がIBMから提供/公開されています。
一方で、オンプレミス版やKubernetes版のDb2に関しては
そういった便利な既製APIの存在はマニュアルに言及されません。
オンプレミス版Db2における "REST APIサポート" とは、以下を指しているようです。
- RESTサービスの作成と実行
- ユーザーが事前に登録したSQL(SELECT/INSERT/UPDATE/DELETE) をRESTサービスとして実行
- SQLの結果セットはjson形式で受け取る
- サービスは同期実行、バッチジョブとしての非同期実行ともに可能
- 任意のSQLを実行可能
- /v1/services/execsql エンドポイントを使用して SQL を直接実行
Db2におけるRESTアクセスのおおまかな流れ
今回は、SELECT文を実行して1行の結果セットを受け取る簡単なサービスを作成/実行します。
インターフェースにはcurlコマンドを利用します。
Step1. 事前セットアップ
Step2. Db2 RESTサービス・コンテナ起動
Step3. 認証情報(token)取得
Step4. RESTサービス作成
Step5. RESTサービス呼び出し
環境
Db2 REST サービスは、RESTサービス・コンテナを構成することで利用可能になります。
今回は、Db2が稼働するサーバとは別のサーバにRESTサービス・コンテナを立てる構成としています。
-
Db2 データベース稼働サーバ
- RHEL 7.9
- Db2 11.5.6
-
Db2 RESTサービス・コンテナ稼働サーバ
- WSL2/Ubuntu 22.04
- podman version 3.4.4
- Db2 Rest Service Container
- curl 7.81.0
Step1. 事前セットアップ
RESTサービスを定義する前に、セットアップが必要となります。
これがかなり手数が多く、面倒です。長くなるので別記事に整理します。
別記事 → REST API による Db2アクセスのための事前準備
Step2. Db2 REST サービス・コンテナ起動
事前セットアップの中でRESTサービス・コンテナを作成/起動していますが、
時間が経過した場合などコンテナが停止状態の場合は、起動しておきます。
kanako@Ubuntu-Rest:~$ podman start db2rest
db2rest
補足:Db2 REST サービスドキュメント
Db2 RESTサービス・コンテナを立てると、RESTのドキュメントを参照できるようになり、
どんな操作が可能であるか、確認できます。
(webで見られる Db2製品マニュアル とはまた別にドキュメントが提供されています)
REST ドキュメントURL:
https://<db2restservice_container_host>:50050//docs
Step3. 認証情報の取得
Db2 RESTサービスの作成/呼び出しにあたり、認証情報トークンを付与する必要があります。
そこであらかじめ、トークンを取得します。
トークンは、DB接続情報をHTTPリクエストボディ(-dオプション)に記述して、URL「/v1/auth」にPOSTメソッドでアクセスすることで取得します。
DBサーバーへの接続情報を渡すため、dbParamsに下記の値を指定します。
- DBサーバーのホスト名
- DBサーバーのListenポート
- SSL(TLS)接続有無
- データベース名
- DB接続ユーザー名
- パスワード
また、今回はnon-SSL環境であるため、--insecure オプションを指定してSSL関連の警告を無視するモードでcurlコマンドを実行しています。
コマンド例:
curl -v --insecure -X POST \
https://localhost:50050/v1/auth \
-H "content-type: application/json" \
-d '{
"dbParms": {
"dbHost": "DBサーバホスト名",
"dbName": "データベース名",
"dbPort": DBサーバListenポート,
"isSSLConnection": false,
"username": "DB接続ユーザ",
"password": "DB接続ユーザパスワード"
},
"expiryTime": "24h" }'
実行例:
kanako@Ubuntu-Rest:~$ curl --insecure -X POST \
> https://localhost:50050/v1/auth \
> -H "content-type: application/json" \
> -d '{
> "dbParms": {
> "dbHost": "testserver",
> "dbName": "TESTDB2",
> "dbPort": 50000,
> "isSSLConnection": false,
> "username": "db2inst1",
> "password": "パスワード文字列"
> },
> "expiryTime": "24h" }'
{"token":"eyJh..(以下略)"}
"token"というキー名の右に、長い文字列が取得されているのがtoken文字列です。
実際のトークンは512文字程度の文字列です。
以降、トークン文字列は「トークン文字列」と表記します。
Step4. RESTサービス作成
今回作成するRESTサービスの概要は以下の通りです。
- サービス名は「sql_max_t2」 (→/v1/services/sql_max_t2/ というURLで呼び出す)
- サービス呼び出しを受けて「"SELECT max(C1) FROM T2"」というSQLを実行し、結果セットを戻す
前提(権限):
サービスの作成、更新、削除にあたっては、以下の権限が必要です。
- メタデータ表に対する SELECT、INSERT、UPDATE、および DELETE 特権。
- サービス作成呼び出しに関連するスキーマに対して CREATE SCHEMA または DROP SCHEMA ステートメントを実行する権限。
Step4-1. サービス作成
RESTサービス作成について、RESTサービス用ドキュメントに詳細なガイドがあります。
サービスを新規作成するには、「POST /v1/services」にサービス作成用の引数を与えて実行します。
各言語ごとのサンプルコマンドが、上記RESTサービス用ドキュメントの右の黒画面に提供されます。これをコピー&ペーストして、コード作成を開始することができます。
REST docs 引用:
https://<db2restservice_container_host>:50050/docs#operation/createService
サンプルコマンドのうち、下記オプションについては環境に合わせて編集します。
- authorization: 「Step3. 認証情報の取得」で取得したトークン文字列
- serviceName: サービス名
- 新規作成するサービスのURL文字列をここで命名します
- 大文字/小文字が区別されます
- serviceDescription: サービスについての説明
- version: バージョン
- sqlStatement: このサービスが呼び出されたときに実行するSQLステートメント
実行コマンド:
curl --insecure -X POST \
https://localhost:50050/v1/services \
-H "authorization: トークン文字列" \
-H "content-type: application/json" \
-d '{
"serviceName": "sql_max_t2",
"serviceDescription": "get the maximum value of c1 column",
"version": "1.0",
"sqlStatement": "SELECT max(C1) FROM T2" }'
実行結果:
kanako@Ubuntu-Rest:~$ curl --insecure -X POST \
> https://localhost:50050/v1/services \
> -H "authorization: eyJhb..(以下略)" \
> -H content-type: application/json" \
> -d '{
> "serviceName": "sql_max_t2",
> "serviceDescription": "get the maximum value of c1 column",
> "version": "1.0",
> "sqlStatement": "SELECT max(C1) FROM T2" }'
kanako@Ubuntu-Rest:~$
Step4-2. RESTサービスの存在確認
作成されたすべてのRESTサービスをリストし、Step4-1で作成した「sql_max_t2」が存在することを確認します。
kanako@IBM-PF2E8K5F:~$ curl --insecure -X GET \
> https://localhost:50050/v1/services \
> -H "authorization: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1ZSwiY2xpZW50X2lkIjoiZWJmZWI3NmItYzZmZC00N2MwLWI3YmYtN2Y0YjEzZTUwN2Y2IiwiZXhwIjoxNjY0NTg3ODgyLCJpc3MiOiJkYjJpbnN0MSJ9.rlea5MUbOLdAL4jf9EU0k5R1jTQV5Q42cKy-tlOsfyXUNrZYaNYPk3IyUeQ1_jEsZx8-P6GLjZd9rsaehpauABKhxo53FYka81daGcYjGR3-PA9K3PFOuasz5wyVDOpy6uPEeBVj03wxyDFSxgCU7gLiQ6N4ysHq1f_4rPc_967tkCVbW0Ht8DehUUCtAVDmVOT2yEDC4Pia8XHv_gDrVaIdU4fJORP24bmWsbdlgEkrapKyuWpufLqkASqkdRtG4kaGIlf06fa8WTIpg5lIOGNASXsjlrOwoaW5iBuOdtJ7PN_3Rr5QKayWgsyLj-1EleZP_ljcDwXHkUg2Rp6XfA" \
> -H "content-type: application/json"
{"Db2Services":[{"lastModified":"2022-09-07T03:29:20.157461Z","serviceCreator":"db2inst1","serviceDescription":"get the maximum value of c1 column","serviceName":"sql_max_t2","serviceUpdater":"db2inst1","version":"1.0"},{"lastModified":"2022-09-27T01:47:29.457954Z","serviceCreator":"db2inst1","serviceDescription":"Lists the t1 table","serviceName":"LISTT1","serviceUpdater":"db2inst1","version":"1.0"}]}
kanako@IBM-PF2E8K5F:~$
Step4-3. (オプション)RESTサービスの実体であるプロシージャの内容確認
サービス作成が完了すると、「Step1.事前セットアップ」の中で作成された
RESTSERVICE表に、作成したRESTサービスのレコードが1行挿入されます。
確認用SQLと実行結果:
[db2inst1@4c4fd18c3faf ~]$ db2 "select * from db2rest.restservice"
NAME VERSION DESCRIPTION PROCSCHEMA PROCNAME ISQUERY SERVICE_CREATOR SERVICE_UPDATER LAST_MODIFIED
---------------- -------- ---------------------------------------------------------------- ---------------- ---------------- ------- ---------------- ---------------- --------------------------
sql_max_t2 1.0 get the maximum value of c1 column DB2INST1 REST_SQL_MAX_T2_1_0 Y db2inst1 db2inst1 2022-09-07-03.29.20.157461
1 record(s) selected.
[db2inst1@4c4fd18c3faf ~]$
このRESTSERVICE表から、作成されたRESTサービス名、バージョン、説明、作成者、最終変更日時などの情報が確認できます。
しかし、実行されるSQLステートメントについてはこの表には格納されません。
RESTサービスの実体はプロシージャであり、このプロシージャの定義は、カタログビューSYSCAT.PROCEDURESにて確認することができます。
RESTサービス作成時に指定したSQLステートメントも、このカタログビューにて確認可能です。
確認用SQL:
RESTSERVICE表に格納されるプロシージャ名を条件指定し、1つのプロシージャに絞って検索します。
$ db2 "select substr(procschema, 1,16) as procschema, substr(procname,1,64) as procname, create_time, language, fenced, substr(class,1,16) as class, program_type, result_sets, substr(text,1,1000) as text from syscat.procedures where procname='REST_SQL_MAX_T2_1_0'"
確認結果:
カタログ列名 | 値 |
---|---|
PROCSCHEMA | DB2INST1 |
PROCNAME | REST_SQL_MAX_T2_1_0 |
CREATE_TIME | 2022-09-07-03.29.20.214406 |
LANGUAGE | SQL |
FENCED | (空白) |
CLASS | - |
PROGRAM_TYPE | M |
RESULT_SETS | 1 |
TEXT | CREATE PROCEDURE "DB2INST1"."REST_SQL_MAX_T2_1_0" () LANGUAGE SQL SPECIFIC "DB2INST1"."REST_SQL_MAX_T2_1_0" DYNAMIC RESULT SETS 1 BEGIN DECLARE C1 CURSOR WITH RETURN FOR -- BEGIN SQL STATEMENT SELECT max(C1) FROM T2; -- END SQL STATEMENT OPEN C1; END |
このように、「REST_SQL_MAX_T2_1_0」という名前のSQLプロシージャが、RESTサービス作成時刻近辺で作成されたことがわかります。
TEXT列にはプロシージャの定義文が格納され、RESTサービス作成時に指定したSQL文字列も確認することができます。
Db2にどんなRESTサービスが定義されているかわからないときは、
RESTSERVICE表を参照し、RESTサービスの実体であるSQLプロシージャの名前を特定し、
SYSCAT.PROCEDURESカタログビューでSQLステートメントを確認すると良さそうです。
Step5. RESTサービスの呼び出し
RESTサービス呼び出しについても、RESTサービス用マニュアルに詳細なガイドがあります。
REST docs 引用:
https://<db2restservice_container_host>:50050/docs#operation/invokeService
ここでは、Step4で作成したRESTサービス「sql_max_t2」を、curlで呼び出してみます。
「POST /v1/services」に続けて、「/サービス名/バージョン」を指定します。
(→ URLは「/v1/services/sql_max_t2/1.0」となります)
RESTサービス呼び出し(コマンド):
curl --insecure -X POST \
https://localhost:50050/v1/services/sql_max_t2/1.0 \
-H "authorization: トークン文字列" \
-H "content-type: application/json" \
-d '{
"sync": true }'
登録済のRESTサービスが呼び出され、ジョブステータスコードと結果セットがjson形式で返却されます。
RESTサービス呼び出し(実行結果):
kanako@Ubuntu-Rest:~$ curl --insecure -X POST \
> https://localhost:50050/v1/services/sql_max_t2/1.0 \
> -H "authorization: eyJh..(以下略)" \
> -H "content-type: application/json" \
> -d '{
> "sync": true }'
{"jobStatus":4,"jobStatusDescription":"Job is complete","resultSet":[{"1":15000}],"rowCount":1}
kanako@Ubuntu-Rest:~$
想定通り、結果が取得されました。jobStatus:4 は処理完了を表すコードです。
なお、ジョブステータスコードは下記の種類があります。
- 0 - ジョブの実行に失敗した。
- 1 - ジョブはキューに入れられている。
- 2 - ジョブは実行中。
- 3 - データが使用可能。
- 非同期要求時に戻されるコード(同期実行の場合は登場しない)
- 例えば、非同期要求が 2500 個のレコードを返すものの、要求が各応答のレコード数を最大 1000 個に制限している場合、最初の 1000 個のレコードを含む最初の応答が受信されると、要求の状況は 3 に変わります。 要求は、最後の 500 個のレコードを含む最終応答を受信するまで状況 3 のままとなり、最終応答を受信した時点で状況 4 に変わります。
- 4 - ジョブが完了した。
- 5 - ジョブはキャンセルされ、停止している。
参考:Db2マニュアル「REST サービスを使用するアプリケーションの作成」
補足:RESTサービス実行ユーザの権限
今回は簡易検証のため全手順をインスタンスオーナー(db2inst1)で行っていますが、
サービス作成者と利用者とで、使うユーザーを分けて、与える権限も分けることが望ましいです。
RESTサービスを作成しただけでは、作成者以外のユーザーから呼び出すことはできないため、サービスの実体となるプロシージャの実行権限を付与しておく必要があります。
参考:Db2マニュアル「Db2 で REST エンドポイントを操作するために必要な特権」
補足:RESTサービス実行時の詳細出力の出し方(curl)
Step4.で出力例を貼っている通り、curlコマンドでRESTサービスを作成すると、標準出力に何も表示されないため、RESTSERVICE表にエントリーが追加されたかどうかを見るまで実行結果がわかりません。
curlコマンドに -v オプションを付与して実行すると、以下のような情報が確認できて非常にわかりやすくなります。
- 実行結果(成否がHTTPステータスコードでわかります)
- 通信プロトコルのバージョン
- TLSハンドシェークの状況や、問題が起きている場合はエラーの発生状況
- クライアントから送信されたHTTPヘッダの内容
- 認証トークンなど
- リクエスト実行完了後の接続の状態(確立された接続がそのまま残されているか)
実行結果(RESTサービス作成):
kanako@Ubuntu-Rest:~$ curl -v --insecure -X POST \
> https://localhost:50050/v1/services \
> -H "authorization: eyJh..(以下略)" \
> -H "content-type: application/json" \
> -d '{
> "serviceName": "sql_max_t2",
> "serviceDescription": "get the maximum value of c1 column",
> "version": "1.0",
> "sqlStatement": "SELECT max(C1) FROM T2" }'
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 127.0.0.1:50050...
* Connected to localhost (127.0.0.1) port 50050 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS header, Finished (20):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS header, Finished (20):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: C=CA; ST=ON; L=TO; O=IBM=OU=Db2Rest; CN=ibm.com
* start date: Sep 6 09:11:59 2022 GMT
* expire date: Sep 6 09:11:59 2023 GMT
* issuer: C=CA; ST=ON; L=TO; O=IBM=OU=Db2Rest; CN=ibm.com
* SSL certificate verify result: self-signed certificate (18), continuing anyway.
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
> POST /v1/services HTTP/1.1
> Host: localhost:50050
> User-Agent: curl/7.81.0
> Accept: */*
> authorization: eyJh..(以下略)
> content-type: application/json
> Content-Length: 179
>
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* Mark bundle as not supporting multiuse
< HTTP/1.1 201 Created ←--- ステータスコード201(作成成功)
< Vary: Origin
< Date: Wed, 07 Sep 2022 03:29:20 GMT
< Content-Length: 0
<
* Connection #0 to host localhost left intact ←--- DBサーバへの接続が残されている
kanako@Ubuntu-Rest:~$
同様に、定義済RESTサービスを実行する際にも -v オプションを付与すると
詳細の情報が得られます。
実行結果(RESTサービス実行):
kanako@Ubuntu-Rest:~$ curl -v --insecure -X POST \
> https://localhost:50050/v1/services/sql_max_t2/1.0 \
> -H "authorization: eyJh..(以下略)" \
> -H "content-type: application/json" \
{
> -d '{
> "sync": true }'
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 127.0.0.1:50050...
* Connected to localhost (127.0.0.1) port 50050 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS header, Finished (20):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS header, Finished (20):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: C=CA; ST=ON; L=TO; O=IBM=OU=Db2Rest; CN=ibm.com
* start date: Sep 6 09:11:59 2022 GMT
* expire date: Sep 6 09:11:59 2023 GMT
* issuer: C=CA; ST=ON; L=TO; O=IBM=OU=Db2Rest; CN=ibm.com
* SSL certificate verify result: self-signed certificate (18), continuing anyway.
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
> POST /v1/services/sql_max_t2/1.0 HTTP/1.1
> Host: localhost:50050
> User-Agent: curl/7.81.0
> Accept: */*
> authorization: eyJh..(以下略)
> content-type: application/json
> Content-Length: 23
>
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: application/json
< Vary: Origin
< Date: Wed, 07 Sep 2022 03:34:12 GMT
< Content-Length: 96
<
{"jobStatus":4,"jobStatusDescription":"Job is complete","resultSet":[{"1":15000}],"rowCount":1}
* Connection #0 to host localhost left intact
kanako@Ubuntu-Rest:~$
参考情報
Db2マニュアル「Db2 REST サービス」
Db2 on Cloud REST API
Db2 Warehouse on CLoud REST API