リモートサービスの入力および出力データ形式

Snowflakeがリモートサービスにデータを送信する場合、またはリモートサービスからデータを受信する場合、データは正しくフォーマットされている必要があります。このトピックでは、適切なデータ形式に関する情報を提供します。Snowflakeとの間で送受信されるデータも、 適切なデータ型 である必要があります。

たとえば、外部関数を実行する場合、Snowflakeはここで説明する形式でデータを送信および期待します。データをリモートサービスに直接送信するのではなく、プロキシサービスに送信します(詳細については、 外部関数の紹介 をご参照ください)。したがって、プロキシサービスは、Snowflake互換の形式でデータを受信(および返す)する必要があります。通常、プロキシサービスはデータを変更せずに渡しますが、リモートサービスとSnowflakeの両方のニーズを満たすために、プロキシはデータを再フォーマット(送信と受信の両方)できます。

簡単にするためとSnowflakeが送受信する形式を説明するために、このセクションのほとんどの例では、リモートサービスがSnowflakeが期待する形式と同じ形式でデータを読み書きし、プロキシサービスが両方向でデータを変更せずに渡すことを想定しています。

このトピックの内容:

Snowflakeから送信されたデータ形式

Snowflakeからの各HTTPリクエストは、POSTまたはGETです。

  • POSTリクエストには、ヘッダーとリクエスト本文が含まれています。リクエストの本文には、行の バッチ が含まれています。

  • GET にはヘッダーのみが含まれ、リモートサービスが 非同期的 に結果を返す場合のポーリングのみに使用されます。

本文形式

POST リクエストの本文には、 JSON 形式でシリアル化されたデータが含まれます。

JSON のスキーマは次のとおりです。

  • トップレベルは JSON オブジェクト(名前と値のペアのセットで、「ディクショナリ」とも呼ばれる)です。

  • 現在、オブジェクトには1つのアイテムしかありません。そのアイテムのキーの名前は「データ」です。

  • その「データ」アイテムの値は JSON 配列であり、次のようになります。

    • 各要素は1行のデータです。

    • データの各行は、1つ以上の列の JSON 配列です。

    • 最初の列は常に行番号です(つまり、バッチ内における行の0ベースインデックス)。

    • 残りの列には、関数の引数が含まれています。

  • データ型は次のようにシリアル化されます。

    • 番号は JSON 番号としてシリアル化されます。

    • ブール値は JSON ブール値としてシリアル化されます。

    • 文字列は JSON 文字列としてシリアル化されます。

    • オブジェクトは JSON オブジェクトとしてシリアル化されます。

    • サポートされている他のすべてのデータ型は、 JSON 文字列としてシリアル化されます。

    • NULL は JSON nullとしてシリアル化されます。

各プラットフォームのリモートサービスでデータを抽出する例については、以下をご参照ください。

オプションで、ネットワークを介して送信するために JSON を圧縮できます。圧縮については、 CREATE EXTERNAL FUNCTION に記載されています。

本文の例

これは、署名 f(integer, varchar, timestamp) を持つ外部関数のシリアル化されたリクエストの例です。最初の列はバッチ内の行番号であり、次の3つの値は外部関数への引数であることに注意してください。

{
    "data": [
                [0, 10, "Alex", "2014-01-01 16:00:00"],
                [1, 20, "Steve", "2015-01-01 16:00:00"],
                [2, 30, "Alice", "2016-01-01 16:00:00"],
                [3, 40, "Adrian", "2017-01-01 16:00:00"]
            ]
}
Copy

ヘッダー形式

ヘッダー情報は通常、キーと値のペアのセットとしてリモートサービスで利用できます。ヘッダーの情報には次が含まれます。

  • 次の HTTP ヘッダー:

    • リクエストの本文でデータがシリアル化される方法を説明するヘッダー:

      • 「sf-external-function-format」:現在、常に「json」に設定されています。

      • 「sf-external-function-format-version」:現在、常に「1.0」に設定されています。

    • 「sf-external-function-current-query-id」:これには、この外部関数を呼び出したクエリのクエリ ID が含まれています。これを使用して、Snowflakeクエリをリモートサービスの呼び出しに関連付け、たとえば問題のデバッグに役立てることができます。

    • 「sf-external-function-query-batch-id」: バッチ ID は、このリクエストで処理された行の特定のバッチを一意に識別します。リモートサービスでは、この ID を使用して、処理中のバッチのステータスを追跡できます。エラーが原因でリクエストが再試行された場合、IDはべき等トークンとしても使用できます。IDは、リモートサービスによるリクエストのログ記録/トレースにも使用できます。

      GETのバッチIDは、対応するPOSTのバッチIDと同じです。

      バッチIDは、Snowflakeによって生成された不透明な値です。形式は将来のリリースで変更される可能性があるため、リモートサービスは特定の形式に依存したり、値を解釈しようとしたりしないでください。

    • SQL クエリで呼び出された外部関数の署名(名前と引数の型)と戻り値の型を説明するヘッダー。これらの値には、Snowflake 識別子 の標準文字ではない文字を含めることができるため、情報のBase64バージョンが含まれ、非標準文字は、非Base64バージョンの空白に置き換えられます。

      具体的なヘッダーは次のとおりです。

      • sf-external-function-name

      • sf-external-function-name-base64

      • sf-external-function-signature

      • sf-external-function-signature-base64

      • sf-external-function-return-type

      • sf-external-function-return-type-base64

      たとえば、関数 ext_func(n integer)  returns varchar に送信されるヘッダーは次のとおりです。

      • sf-external-function-name: ext_func

      • sf-external-function-name-base64: <base64 value>

      • sf-external-function-signature: (N NUMBER)

      • sf-external-function-signature-base64: <base64 value>

      • sf-external-function-return-type: VARCHAR(16777216)

      • sf-external-function-return-type-base64: <base64 value>

      SQL INTEGER 値は SQL NUMBER として扱われるため、型 INTEGER として宣言された SQL 引数は、型 NUMBER として記述されることに注意してください。

  • CREATE EXTERNAL FUNCTION の「ヘッダー」および「コンテキストヘッダー」プロパティで説明されている追加のオプションメタデータ。

ヘッダーアクセスの例

ヘッダーをPythonディクショナリとして受け取るPythonで記述された AWS Lambda関数内から、「sf-external-function-signature」ヘッダーを抽出するには、次を実行します。

def handler(event, context):

    request_headers = event["headers"]
    signature = request_headers["sf-external-function-signature"]
Copy

詳細は、他の言語や他のクラウドプラットフォームにより異なります。

(AWS で開発されたリモートサービスの場合の、ヘッダーとラムダプロキシ統合の詳細については、 AWS API Gatewayのドキュメント をご参照ください。)

Snowflakeが受信するデータ形式

本文形式

リモートサービスがバッチの処理を終了すると、リモートサービスはSnowflakeによって送信されたデータの形式と同様の JSON 形式でSnowflakeにデータを返信する必要があります。

Snowflakeに返される JSON 応答には、Snowflakeによって送信された各行に1行が含まれている必要があります。返された各行には2つの値が含まれます。

  • 行番号(つまり、バッチ内における行の0ベースインデックス)。

  • その行の関数から返された値。値は複合値(たとえば、 OBJECT)にすることができますが、Snowflakeのスカラー関数すべて(外部またはその他)が単一の値を返すため、正確に単一の値でなければなりません。

それにより、Snowflakeはリクエストと応答を関連付けることができ、返されるデータの行番号は、Snowflakeが送信したデータの行番号に対応している必要があり、受信した順序と同じ順序で返される必要があります。

本文アクセスの例

次の JSON の例は、 OBJECT 値を含む2つの行を示しており、それぞれの前に行番号が付いています。

{
    "data":
        [
            [ 0, { "City" : "Warsaw",  "latitude" : 52.23, "longitude" :  21.01 } ],
            [ 1, { "City" : "Toronto", "latitude" : 43.65, "longitude" : -79.38 } ]
        ]
}
Copy

これらの返された行の1つをPythonで作成するには、次のコードを使用できます。

...
row_number = 0
output_value = {}

output_value["city"] = "Warsaw"
output_value["latitude"] = 21.01
output_value["longitude"] = 52.23
row_to_return = [row_number, output_value]
...
Copy

SQL で返された行の OBJECT 値にアクセスするには、 半構造化データの走査 で説明されている表記を使用します。例:

select val:city, val:latitude, val:longitude
    from (select ext_func_city_lat_long(city_name) as val from table_of_city_names);
Copy

ヘッダー形式

応答には、次のオプションの HTTP ヘッダーを含めることもできます。

  • Content-MD5: Snowflakeは、オプションのContent-MD5 ヘッダーを使用して、応答の整合性をチェックします。このヘッダーが応答に含まれている場合、Snowflakeは、応答本文で MD5 チェックサムを計算して、返されたヘッダーの対応するチェックサムと一致することを確認します。値が一致しない場合、 SQL クエリは失敗します。チェックサムは、ヘッダーで返される前に、Base64表現でエンコードする必要があります。以下のサンプルコードをご参照ください。

オプションで、ネットワークを介して送信するために JSON を圧縮できます。圧縮については、 CREATE EXTERNAL FUNCTION に記載されています。

タイムアウトと再試行の詳細については、 タイムアウトエラーの説明 および リモートサービスが各行に1回だけ渡されると思い込まない をご参照ください。

ステータスコード

応答には、 HTTP ステータスコードも含まれています。Snowflakeは、次の HTTP ステータスコードを認識します。

コード

説明

200

バッチが正常に処理されました。

202

バッチを受信し、まだ処理中です。

その他の値はエラーとして扱われます。

応答作成例

以下のPythonコードの例は、 HTTP 応答コード、処理されたデータ、および MD5 ヘッダー(オプション)を含む適切な応答を返します。(これは、Python 3.8を使用して記述。)

この例は、 AWS Lambda関数に基づいています。一部のコードには、プラットフォームごとにカスタマイズが必要な場合があります。

import json
import hashlib
import base64

def handler(event, context):

    # The return value should contain an array of arrays (one inner array
    # per input row for a scalar function).
    array_of_rows_to_return = [ ]

    ...

    json_compatible_string_to_return = json.dumps({"data" : array_of_rows_to_return})

    # Calculate MD5 checksum for the response
    md5digest = hashlib.md5(json_compatible_string_to_return.encode('utf-8')).digest()
    response_headers = {
        'Content-MD5' : base64.b64encode(md5digest)
    }

    # Return the HTTP status code, the processed data, and the headers
    # (including the Content-MD5 header).
    return {
        'statusCode': 200,
        'body': json_compatible_string_to_return,
        'headers': response_headers
    }
Copy