远程服务输入和输出数据格式¶
当 Snowflake 将数据发送到远程服务或从远程服务接收数据时,必须正确格式化数据。本主题提供有关正确数据格式的信息。从 Snowflake 接收和返回的数据也必须具有 适当的数据类型。
例如,在执行外部函数时,Snowflake 发送和期望的数据符合此处描述的格式。它将数据发送到代理服务,而不是直接发送到远程服务(有关更多信息,请参阅 外部函数简介)。因此,代理服务必须接收(并返回)Snowflake 兼容格式的数据。尽管代理服务通常不变地传递数据,但代理可以重新格式化数据(发送和接收时),以满足远程服务和 Snowflake 的需求。
为简单起见,并帮助说明 Snowflake 期望发送和接收的格式,本部分中的大多数示例都假定远程服务读取和写入与 Snowflake 期望的格式相同的格式数据,且代理服务在两个方向上都不变地传递数据。
本主题内容:
Snowflake 发送的数据格式¶
来自 Snowflake 的每个 HTTP 请求都是一个 POST 或一个 GET。
正文格式¶
POST 请求的正文包含按 JSON 格式序列化的数据。
JSON 架构是:
- 顶层是一个 JSON 对象(一组名称对/值对,也称为“字典”)。 
- 目前,该对象只有一个项目;该项目的键名为“数据”。 
- 该“数据”项目的值是一个 JSON 数组,其中: - 每个元素都是一行数据。 
- 每行数据都是由一列或多列组成的 JSON 数组。 
- 第一列始终是行号(即批中的行从 0 开始的索引)。 
- 其余列包含函数的实参。 
 
- 数据类型按如下方式序列化: - 数字序列化为 JSON 数字。 
- 布尔值序列化为 JSON 布尔值。 
- 字符串序列化为 JSON 字符串。 
- 对象被序列化为 JSON 对象。 
- 所有其他受支持的数据类型都序列化为 JSON 字符串。 - 日期、时间和时间戳序列化为字符串。有关将这些数据类型格式化为字符串的详细信息,请参阅 日期和时间输入和输出格式 和 转换函数中的日期和时间格式。 
- 二进制列序列化为字符串。有关详细信息,请参阅 支持的二进制格式概述。 
 
- NULL 序列为 JSON NULL。 
 
有关在每个平台上的远程服务中提取数据的示例,请参阅:
- Azure:创建远程服务(Azure 函数)。 
或者,可以压缩 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"] ] }
标头格式¶
标头信息通常以一组键对/值对的形式提供给远程服务。标头信息包括:
- 以下 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(134217728) 
- 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"]
其他语言和其他云平台的详细信息会有所不同。
对于在 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 } ]
        ]
}
若要使用 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]
...
若要通过 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);
标头格式¶
响应还可以包含以下可选的 HTTP 标头:
- Content-MD5:Snowflake 使用可选的 Content-MD5 标头来检查响应的完整性。如果在响应中包含此标头,则 Snowflake 会计算响应正文上的 MD5 校验和,以确保它与返回标头中的相应校验和相匹配。如果值不匹配,则 SQL 查询将失败。校验和应先以 base64 表示形式编码,然后才能在标头中返回。请参阅下面的示例代码。 
或者,可以压缩 JSON 以通过网络传输。压缩记录于 CREATE EXTERNAL FUNCTION。
有关超时和重试的信息,请参阅 考虑超时错误 和 请勿假定远程服务只传递每行一次。
状态代码¶
响应还包含 HTTP 状态代码。Snowflake 可识别以下 HTTP 状态代码:
| 代码 | 描述 | 
|---|---|
| 200 | 批成功处理。 | 
| 202 | 批已收到,但仍在处理中。 | 
其他值将被视为错误。
响应创建示例¶
下面的示例 Python 代码返回正确的响应,包括 HTTP 响应代码、处理的数据和 MD5 标头(可选)。
此示例基于 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
    }