远程服务输入和输出数据格式¶
当 Snowflake 将数据发送到远程服务或从远程服务接收数据时,必须正确格式化数据。本主题提供有关正确数据格式的信息。从 Snowflake 接收和返回的数据也必须具有 适当的数据类型。
例如,在执行外部函数时,Snowflake 发送和期望的数据符合此处描述的格式。它将数据发送到代理服务,而不是直接发送到远程服务(有关更多信息,请参阅 外部函数简介)。因此,代理服务必须接收(并返回)Snowflake 兼容格式的数据。尽管代理服务通常不变地传递数据,但代理可以重新格式化数据(发送和接收时),以满足远程服务和 Snowflake 的需求。
为简单起见,并帮助说明 Snowflake 期望发送和接收的格式,本部分中的大多数示例都假定远程服务读取和写入与 Snowflake 期望的格式相同的格式数据,且代理服务在两个方向上都不变地传递数据。
本主题内容:
Snowflake 发送的数据格式¶
来自 Snowflake 的每个 HTTP 请求都是一个 POST 或一个 GET。
正文格式¶
POST 请求的正文包含按 JSON 格式序列化的数据。
JSON 架构是:
顶层是一个 JSON 对象(一组名称对/值对,也称为“字典”)。
目前,该对象只有一个项目;该项目的键名为“数据”。
该“数据”项目的值是一个 JSON 数组,其中:
每个元素都是一行数据。
每行数据都是由一列或多列组成的 JSON 数组。
第一列始终是行号(即批中的行从 0 开始的索引)。
其余列包含函数的实参。
数据类型按如下方式序列化:
数字序列化为 JSON 数字。
布尔值序列化为 JSON 布尔值。
字符串序列化为 JSON 字符串。
对象被序列化为 JSON 对象。
所有其他受支持的数据类型都序列化为 JSON 字符串。
日期、时间和时间戳序列化为字符串。有关将这些数据类型格式化为字符串的详细信息,请参阅 日期和时间输入和输出格式 和 转换函数中的日期和时间格式。
二进制列序列化为字符串。有关详细信息,请参阅 Overview of Supported Binary Formats。
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 (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"]
其他语言和其他云平台的详细信息会有所不同。
对于在 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 标头(可选)。(这是使用 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
}