Teradata Query Service ユーザガイド

[Teradata Vantage - ユーザガイド](https://docs.teradata.com/r/Teradata-Query-Service-Installation-Configuration-and-Usage-Guide-for-Customers/April-2022) [リリース番号：4.01.03.01　リリース日付：2022/6]の要約です。

> Vantageユーザガイド・シリーズのコンテンツです。

# 1. 概要
**Teradata Query Service (TQS)** は Teradata Vantage に接続するための REST API を提供するミドルウェアです。
クエリサービスを使用すると ワイヤプロトコルとしてHTTPを使用し データ交換形式として JSONを使用して Webページ、モバイルデバイス、またはスクリプト言語からTeradata がサポートするデータベースにクエリを実行できます。
アプリケーションは Query Service を使用してドライバーなしでTeradataがサポートするデータベースにアクセスできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778293/4d4d572b-7fa0-1f8c-e1fc-35f5ac1018f4.png)

Query Serviceは 様々な場面で利用できる可能性のあるサービスです。
　・プログラムからドライバー(JDBC/ODBCなど)なしでデータを取得する
　・WEBサービスのバックグラウンドでデータベースから情報を取得する
　・コールセンターシステムなどの社内アプリケーションからデータベースに問い合わせを行う
など 様々な場面が考えられます。

## どのような事ができますか?
- サポート対象のTeradataシステムを定義
- データベース セッションの作成
- データベースとオブジェクトのメタデータにアクセス
- SQLクエリを送信して応答にアクセスする

# 2. 具体的なサンプル

こちらは、Query Serviceに SQLをHTTPリクエストするサンプルです。
SQLの結果は JSON形式で要求しています。
**難しく感じるかもしれませんが 要求としては情報を4つの指定しているだけです**。
1. Query Service の URL
2. ログイン情報
3. SQL
4. 結果のフォーマット(JSON)

## JavaScriptの記述例
```html
<!DOCTYPE html>
<html>
<head>
<title>Query Service Example Using JQuery</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
$(function()
{
    var username = 'myuser';
    var password = 'mypassword';
    var sql = "SELECT TRIM(DatabaseName) AS \"Database\", PermSpace  AS \"Size\" FROM dbc.databases ORDER BY 2 DESC;"
    var url = "http://sdl57120.labs.teradata.com:1080/systems/mysystem/queries"

    $.ajax({
        type: "POST",
        url: url,
        contentType: "application/json",
        headers: {
            'Accept' : "application/vnd.com.teradata.rest-v1.0+json",
            'Authorization': 'Basic ' + btoa(username + ':' + password)
        },
        data: JSON.stringify({
            query: sql,
            format: 'object'
        }),
    }).done(function (data)
    {
        $("#message").text("RESULTS: SUCCESS");
        $("#result").append(JSON.stringify(data, null, 2));
    }).fail (function (error)
    {
        $("#message").text("RESULTS: ERROR, HTTP Code: " + error.status);
        $("#result").append(JSON.stringify(error.responseJSON, null, 2));
    })

    $("#query").text("Executing query: " + sql);
})
</script> 
</head>
<body>
<div>
  <span id="query"></span>
</div>
<div>
  <span id="message"></span>
  <pre id="result"></pre>
</div>
</body>
</html>
```
## Pythonの記述例
```python
#!/usr/bin/python
import json
import urllib2
import base64
import zlib

username = 'myusername'
password = 'mypassword'
teradataDatabaseAlias = 'mysystem'

# HTTP
url = 'http://sdl57120.labs.teradata.com:1080/systems/' + teradataDatabaseAlias + '/queries'

# HTTPS 
#url = 'https://sdl57120.labs.teradata.com:1443/systems/' + teradataDatabaseAlias + '/queries'

# Setup required HTTP headers
headers={}
headers['Content-Type'] = 'application/json'
headers['Accept'] = 'application/vnd.com.teradata.rest-v1.0+json'
headers['Authorization'] = "Basic %s" % base64.encodestring('%s:%s' % (username, password)).replace('\n', ''); 

# Uncomment to receive results gzip compressed.
#headers['Accept-Encoding'] = 'gzip'

# Set query bands
queryBands = {}
queryBands['applicationName'] = 'MyApp'
queryBands['version'] = '1.0'

# Set request fields, including SQL.
data = {}
data['query'] = 'SELECT * FROM DBC.DBCInfo'
data['queryBands'] = queryBands
data['format'] = 'array'

# Build request.
request = urllib2.Request(url, json.dumps(data), headers)

#Submit request
try:
  response = urllib2.urlopen(request);
  # Check if result have been compressed.
  if response.info().get('Content-Encoding') == 'gzip':  
    response = zlib.decompress(response.read(), 16+zlib.MAX_WBITS)    
  else:
    response = response.read();
except urllib2.HTTPError, e:
    print 'HTTPError = ' + str(e.code)
    response = e.read();
except urllib2.URLError, e:
    print 'URLError = ' + str(e.reason)
    response = e.read();

# Parse response to confirm value JSON.
results = json.loads(response);

# Print formatted results
print json.dumps(results, indent=4, sort_keys=True)

# Do something with the result.
data = results['results'][0]['data']
for d in data:
    if d[0] == 'VERSION':
        print '\nThe version of the Teradata Database is ' + d[1]
```

# 3. 結果の取得
結果セットの形式は 要求された形式によって異なります 。次の形式がサポートされています。
- JSON
- JSON Array
- Comma Separated Value (CSV)

## JSONで取得した場合
SELECT * FROM DBC.DBCInfo :
```json
{
    "queryDuration": 45, 
    "queueDuration": 3, 
    "results": [
        {
            "data": [
                {
                    "InfoData": "15.00.00.01", 
                    "InfoKey": "VERSION"
                }, 
                {
                    "InfoData": "Japanese", 
                    "InfoKey": "LANGUAGE SUPPORT MODE"
                }, 
                {
                    "InfoData": "15.00.00.01", 
                    "InfoKey": "RELEASE"
                }
            ], 
            "resultSet": true, 
            "rowCount": 3, 
            "rowLimitExceeded": false
        }
    ]
}
```

## JSON Array で取得した場合
SELECT * FROM DBC.DBCInfo :
```json
{
"queryDuration": 11,
"queueDuration": 3, "results": [
{
"columns": [
{
"name": "InfoKey",
"type": "VARCHAR"
},
{
"name": "InfoData",
"type": "VARCHAR"
}
],
"data": [ [
"VERSION", "15.00.00.01"
], [
"LANGUAGE SUPPORT MODE",
"Japanese"
], [
"RELEASE", "15.00.00.01"
]
],
"resultSet": true, "rowCount": 3, "rowLimitExceeded": false
}
]
}
```

## Comma Separated Value (CSV)で取得した場合
SELECT * FROM DBC.DBCInfo :
```
csvsInfoKey,InfoData 
VERSION,15.00.00.01
LANGUAGE SUPPORT MODE,Japanese 
RELEASE,15.00.00.01
```

# 4. Query Serviceの利用方法
ここからは APIの種類について説明します。
APIの種類は大きく分けて2種類です。
システムの構成に関するAPIとデータベースリクエストに関するAPIです。

## Query Service 構成 API
|URL|Verb|説明|
|:-|:-|:-|
|/adminusers|GET|Administrator ユーザーの一覧を取得する|
|/adminusers/[userId]|PUT|Administrator ユーザーのパスワードを更新する|
|/certificates|GET|証明書の一覧を取得する|
|/certificates|POST|HTTPS証明書をインストールする|
|/certificates|DELETE|証明書を削除する|
|/certificates/authorities|GET|認証局の一覧を取得する|
|/certificates/authorities|POST|信頼できる署名付き証明書をインストールする|
|/certificates/config|GET|証明書の構成を取得する|
|/certificates/config|PUT|証明書構成を作成または更新する|
|/certificates/selfsigned|POST|自己署名入り証明書を作成または更新する|
|/certificates/signingrequest|POST|証明書署名要求の作成|
|/certificates/pkcs|POST|PKS 証明書を作成または更新する|
|/general|GET|一般的なサービス構成を取得する|
|/general|PUT|一般的なサービス構成の更新|
|/general/export|POST|Teradata Database 構成のエクスポート|

## Database API
|URL|Verb|説明|
|:-|:-|:-|
|/systems|GET|ターゲットシステムの一覧を取得します|
|/systems/[systemName]|GET|指定ターゲット・システム情報を取得|
|/systems/[systemName]|PUT|ターゲット・システムを作成または更新(管理者の認証が必要)|
|/systems/[systemName]|DELETE|指定ターゲットシステムを削除(管理者の資格情報が必要)|
|/testsystem|POST|Teradataシステムへの接続をテスト|
|/systems/[systemName]/queries|POST|SQLリクエストを送信します|
|/systems/[systemName]/queries|GET|アクティブまたはキューに入れられたクエリのリストを取得|
|/systems/[systemName]/queries/[queryId]|GET|クエリーIDを取得|
|/systems/[systemName]/queries/[queryId]|DELETE|クエリをIDで削除|
|/systems/[systemName]/queries/[queryId]/results|GET|クエリーの結果をIDで取得|
|/systems/[systemName]/sessions|POST|明示的にセッションを作成|
|/systems/[systemName]/sessions|GET|開かれているセッションのリストを取得|
|/systems/[systemName]/sessions/[sessionId]|GET|IDによるセッション情報の取得|
|/systems/[systemName]/sessions/[sessionId]|DELETE|IDによるセッションの破棄|
|/systems/[systemName]/databases|GET|データベースのリストを取得|
|/systems/[systemName]/databases/[databaseName]|GET|データベース情報を取得|
|/systems/[systemName]/databases/[databaseName]/functions|GET|定義済み関数の一覧を取得|
|/systems/[systemName]/databases/[databaseName]/macros|GET|マクロの一覧を取得|
|/systems/[systemName]/databases/[databaseName]/procedures|GET|プロシージャの一覧を取得|
|/systems/[systemName]/databases/[databaseName]/tables|GET|テーブルの一覧を取得|
|/systems/[systemName]/databases/[databaseName]/tables/[tableName]|GET|テーブル情報を取得|
|/systems/[systemName]/databases/[databaseName]/views|GET|ビューの一覧を取得|
|/systems/[systemName]/databases/[databaseName]/views/[viewName]|GET|ビュー情報を取得|

# 5. HTTPリクエストの要件
ここからは 一般的なHTTPリクエストの要件の説明になります。
HTTPリクエストには ヘッダ情報とボディ情報が必要です。
リクエストを実行すると 結果とステータスが帰ってきます。
- **ヘッダ情報とは** このHTTPリクエストはどのような要求なのかを記載します。
- **ボディ情報とは** 実際のリクエスト内容です。
- **ステータスとは** 成功は200番でそれ以外は失敗の理由コードです。

## HTTPヘッダ
|ヘッダ|値|説明|必須|
|:-|:-|:-|:-|
|Authorization|Basic [Base64 encoded "username:password"]|Teradata Databaseへのアクセスに使用される認証情報が含まれる。Authorizationヘッダは以下のように構成される。<br>1. ユーザー名とパスワードを1つの文字列（"username:password"）に結合する。<br>2. 結果の文字列は、Base64のRFC2045-MIMEバリアントを使用してエンコードされる（76文字/行に限定されない）。<br>3. 符号化された文字列に認証方法とスペース("Basic")が追加される。|Yes|
|Accept|application/vnd.com.teradata.rest- v1.0+json|クライアントがTeradata DatabaseのREST APIの1.0バージョンを使用したいことをWebサービスに指示する。このヘッダーは、REST API が変更された場合の後方互換性をサポートします。|Yes|
|Accept- Encoding|gzip|GZIP 形式でレスポンスを圧縮するよう、Web サービスに指示します。省略した場合、レスポンスはプレーンテキストで返されます。|No|
|Content- Type|application/json|リクエストに JSON データが含まれていることをウェブサービスに指示します。|Yes|

## リクエストボディ
|名前|タイプ|説明|初期値|必須|
|:-|:-|:-|:-|:-|
|query|String|セミコロンで区切って実行する1つまたは複数のSQL文。SQL文はSQLリテラルの代わりにクエスチョンマークを含めることでパラメータ化することができます。例えば、INSERT INTO mytable VALUES (?, ?, ?) とします。|-|Yes|
|params|Array|SQL文のパラメータを含む配列の配列。複数の配列が存在する場合は、ステートメントが複数回実行され そのたびに配列の次のパラメータの配列が使用されます。|-|No|
|outParams|Array|ストアドプロシージャの出力パラメータ名の配列。|-|No|
|batch|Boolean|ステートメントがJDBCバッチ処理を使用して実行される場合、trueに設定、それ以外の場合はfalseに設定します。|FALSE|No|
|session|Long|サービスが生成するセッションID。そのセッションがすでに使用されている場合は、リクエストはキューに入れられる。|-|No<br>(指定がない場合は、暗黙のセッションを使用します)|
|queryBands|Object|SESSION クエリバンドは、クエリ実行前に追加します。例えば、{ "queryBand1": "value1", "queryBand2": "value2" }のような感じです。|-|No|
|queueTimeout|Integer|リクエストがタイムアウトするまでにキューで待機する秒数。ゼロの値は、リクエストがキューに入れられないことを示し、エラーを返します。|no timeout|No|
|queryTimeout|Integer|クライアントがクエリの完了を待機してタイムアウトするまでの秒数。例えば、JDBC Statement.setQueryTimeout() を参照してください。|no timeout|No|
|clientId|String|クエリの状態を後で検索するためにクライアントが指定する一意の ID。|-|No|
|rowOffset|Integer|結果セットの先頭で破棄する行の数。ページングを実装する際に使用します。|0|No|
|rowLimit|Integer|応答を制限する行数。値0は制限を無効にします。|1000|No|
|format|String|データを返すためのフォーマット。フォーマットは以下のいずれかとする。<br>"object" : データはJSONオブジェクトの配列として返される。<br>"array" : JSON配列の配列としてデータを返す。<br>"csv" : データはカンマ区切りの値として返されます。このフォーマットはJSONレスポンスの結果セクションに影響します|object|No|
|includeColumns|Boolean|カラム名や型などのカラムのメタデータをレスポンスに含めるかどうかを指定します．リクエスト形式が "csv" の場合、カラムは CSV 出力に含まれます。|FALSE|No|

## ステータスコード
|コード|定義|説明|
|:-|:-|:-|
|200|OK|リクエストは成功した。|
|201|Created|リクエストは成功し、レスポンスには作成されたオブジェクトの情報が含まれています。|
|400|Bad Request|構文が不正なため、サービスがリクエストを理解できませんでした。クライアントはこの要求を修正せずに繰り返さないようにしてください。|
|401|Unauthorized|リクエストにはユーザー認証が必要です。|
|404|Not Found|指定された URL で参照されるリソースが見つかりませんでした。|
|412|Precondition Failed|指定されたセッションが現在使用中であるか、リクエストを実行できるスレッドがなく、キューのタイムアウトを超えました。|
|420|Database Error|対象のTeradata Databaseがエラーを返しました。|
|429|Too Many Sessions|ユーザーはセッションの上限に達しています。|
|500|Internal Server Error|サービスが予期しない条件に遭遇し、要求を満たすことができませんでした。|

# 6. LOG
Query Serviceのサーバログについて説明します。
Teradata Query Serviceのログファイルは **/var/opt/teradata/rest/daemon/logs**にあります。ログファイルは100MBごとにローテートされます。ログファイルは、\*.log1～\*.log10という名前で、最大10個まで過去のログファイルが保存されます。
|名前|説明|
|:-|:-|
|tdrestd.log|サービスの状態を反映したサービスログメッセージが含まれる|
|tdrest.audit.log|サービスの利用者が作成したクエリやセッションの監査ログが含まれる|
|init.out|サービス開始スクリプトのstdoutとstderrが含まれています。|

# 7. ソフトウェアの入手とインストール
ここからは Query Serviceのインストール方法について説明します。

## ソフトウェア要件
- Java JDK 8
- SuSE Linux or Red Hat Linu

## 使用するポート
|port|プロトコル|
|:-|:-|
|1080|HTTP|
|1443|HTTPS|

## ソフトウェアの入手
カスタマサポートサービスの[Teradata Software Server](https://tssprod.teradata.com:8443/TSFS/)から入手可能です。
**Client > REST APIs. > query-service-04.01.03.01.**

## インストール
/opt/teradata/restにパッケージをインストールします。
```shell
rpm -ivh query-service-*.rpm
```

## 必要に応じてセキュリティ機能を変更します
/opt/teradata/rest/tdrest/application.properties

|カテゴリ|変数名|説明|初期値|影響|
|:-|:-|:-|:-|:-|
|資格証明ストアのプロパティ  |credential.key.store.random |ランダム キーまたはハッシュ キー|true|単体サーバは true に設定し、高可用性の構成では false に設定します|
|                           |token.duration.minutes      |認証トークンの有効期間|120 (minutes)||
|管理者パスワードのプロパティ|user.password.min.length    |管理者パスワードの最小文字数|6||
|                           |user.password.min.lower.case|管理者パスワードの小文字の最小数|1||
|                           |user.password.min.numeric   |管理者パスワードの数字の最小数|1||
|                           |user.password.min.upper.case|管理者パスワードの大文字の最小数|1||
|                           |user.password.min.special   |管理者パスワードに含まれる特殊文字の最小数|1||
|サーバーのプロパティ        |server.http.enabled         |HTTP 接続を有効にします|false||
|                           |server.http.redirect.to.https|HTTP 接続を HTTPS に自動的にリダイレクトします|true||
|                           |server.session.timeout       |セッションの有効期間|3600 (seconds)||
|API制限のプロパティ         |import.export.api.disabled   |インポート API とエクスポート API を無効にします|false||
|                           |metadata.api.disabled         |メタデータ API を無効にします|false||
|                           |users.api.disabled           |ユーザー API を無効にします|false||

## サービスの開始
サーバー上の /etc/init.d ディレクトリに初期化スクリプトがあります。
```shell
# 開始
/etc/init.d/query-service start
# 終了
/etc/init.d/query-service stop
```

# おわりに
:::note warn
警告
この本書は[Teradata Vantageドキュメンテーション](https://docs.teradata.com/)よりトピックに必要な情報を抜粋したものです。掲載内容の正確性・完全性・信頼性・最新性を保証するものではございません。正確な内容については、原本をご参照下さい。
また、修正が必要な箇所や、ご要望についてはコメントをよろしくお願いします。
:::

## Teradata Vantageへのお問合せ
[Teradata Vantage へのお問合せ](https://www.teradata.jp/About-Us/Contact)