3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

REST API による Db2アクセス

Last updated at Posted at 2022-09-30

はじめに

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サービス・コンテナを立てる構成としています。
image.png

  • 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
image.png

Step3. 認証情報の取得

Db2 RESTサービスの作成/呼び出しにあたり、認証情報トークンを付与する必要があります。
そこであらかじめ、トークンを取得します。
トークンは、DB接続情報をHTTPリクエストボディ(-dオプション)に記述して、URL「/v1/auth」にPOSTメソッドでアクセスすることで取得します。

REST docs 引用:
image.png

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
image.png

サンプルコマンドのうち、下記オプションについては環境に合わせて編集します。

  • 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
image.png

ここでは、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

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?