远程服务输入和输出数据格式

当 Snowflake 将数据发送到远程服务或从远程服务接收数据时,必须正确格式化数据。本主题提供有关正确数据格式的信息。从 Snowflake 接收和返回的数据也必须具有 适当的数据类型

例如,在执行外部函数时,Snowflake 发送和期望的数据符合此处描述的格式。它将数据发送到代理服务,而不是直接发送到远程服务(有关更多信息,请参阅 外部函数简介)。因此,代理服务必须接收(并返回)Snowflake 兼容格式的数据。尽管代理服务通常不变地传递数据,但代理可以重新格式化数据(发送和接收时),以满足远程服务和 Snowflake 的需求。

为简单起见,并帮助说明 Snowflake 期望发送和接收的格式,本部分中的大多数示例都假定远程服务读取和写入与 Snowflake 期望的格式相同的格式数据,且代理服务在两个方向上都不变地传递数据。

本主题内容:

Snowflake 发送的数据格式

来自 Snowflake 的每个 HTTP 请求都是一个 POST 或一个 GET。

  • 一个 POST 请求包含标头和请求正文。请求正文包含一 行。

  • 一个 GET 仅包含标头,并且仅在远程服务 异步 返回结果时用于轮询。

正文格式

POST 请求的正文包含按 JSON 格式序列化的数据。

JSON 架构是:

  • 顶层是一个 JSON 对象(一组名称对/值对,也称为“字典”)。

  • 目前,该对象只有一个项目;该项目的键名为“数据”。

  • 该“数据”项目的值是一个 JSON 数组,其中:

    • 每个元素都是一行数据。

    • 每行数据都是由一列或多列组成的 JSON 数组。

    • 第一列始终是行号(即批中的行从 0 开始的索引)。

    • 其余列包含函数的实参。

  • 数据类型按如下方式序列化:

    • 数字序列化为 JSON 数字。

    • 布尔值序列化为 JSON 布尔值。

    • 字符串序列化为 JSON 字符串。

    • 对象被序列化为 JSON 对象。

    • 所有其他受支持的数据类型都序列化为 JSON 字符串。

    • NULL 序列为 JSON NULL。

有关在每个平台上的远程服务中提取数据的示例,请参阅:

或者,可以压缩 JSON 以通过网络传输。压缩记录于 CREATE EXTERNAL FUNCTION

正文示例

下面是带有签名 f(integer, varchar, timestamp) 的外部函数的序列化请求示例。请注意,第一列是批中的行号,接下来的三个值是外部函数的实参。

{
    "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 生成的 Opaque 值。格式在将来的版本中可能会更改,因此远程服务不应依赖特定格式或尝试解释该值。

    • 标头描述了 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 的“headers”和“context_headers”属性中描述的其他可选元数据。

标头访问示例

要从用 Python 编写的 AWS Lambda 函数(该函数将标头接收为 Python 字典)中提取“sf-external-function-signature”标头,请执行以下操作:

def handler(event, context):

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

其他语言和其他云平台的详细信息会有所不同。

对于在 AWS 上开发的远程服务,有关标头和 Lambda 代理集成的更多信息,请参阅 AWS API Gateway 文档 (https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format)。

Snowflake 接收的数据格式

正文格式

当远程服务完成处理批时,远程服务应以类似于 Snowflake 所发送数据的 JSON 格式将数据发送回 Snowflake。

返回到 Snowflake 的 JSON 响应应包含 Snowflake 发送的每一行的一行。每个返回的行包含两个值:

  • 行号(即批中的行从 0 开始的索引)。

  • 从该行的函数返回的值。该值可以是复合值(例如,一个 OBJECT),但它必须正好是一个值,因为所有标量 Snowflake 函数(外部函数或其他函数)都返回一个值。

为了使 Snowflake 能够将响应与请求相关联,返回数据中的行号必须与 Snowflake 发送的数据中的行号相对应,并且必须按照接收的相同顺序返回。

正文访问示例

下面的 JSON 示例显示了包含 OBJECT 值的两行,每行前面都有一个行号:

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

若要使用 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

有关超时和重试的信息,请参阅 考虑超时错误请勿假定远程服务只传递每行一次

状态代码

响应还包含 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
语言: 中文