JavaScript UDFs 简介¶
可以使用 JavaScript 编写用户定义函数 (UDF) 的处理程序。本部分中的主题描述如何设计和编写 JavaScript 处理程序。
有关 UDFs 的介绍,包括可用于编写 UDF 处理程序的语言列表,请参阅 用户定义的函数概述。
有了处理程序后,就可以用 SQL 创建 UDF。有关使用 SQL 创建或调用 UDF 的信息,请参阅 创建用户定义的函数 或 执行 UDF。
您可以在处理程序代码执行时获取日志和跟踪数据。有关更多信息,请参阅 日志记录、跟踪和指标。
备注
要了解与 JavaScript UDF 处理程序相关的限制,请参阅 JavaScript UDF 限制。
JavaScript 处理程序的工作原理¶
当用户调用 UDF 时,用户将 UDF 的名称和实参传递给 Snowflake。Snowflake 调用相关处理程序代码(如有实参则使用实参)来执行 UDF 的逻辑。然后,处理程序函数将输出返回给 Snowflake,后者将其传递回客户端。
对于传递给 UDF 的每行, UDF 返回标量(即单个)值,或者如果定义为表函数,则返回一组行。
示例¶
下面示例中的代码创建了一个名为 my_array_reverse 的 UDF,其处理程序代码接受输入 ARRAY,并按相反顺序返回包含元素的 ARRAY。根据 SQL-JavaScript 数据类型映射 中描述的映射,JavaScript 实参和返回类型通过 Snowflake 与 SQL 相互转换。
请注意,即使在 SQL 代码中输入的参数名称不是大写字母,JavaScript 代码也必须将这些名称全部大写。
JavaScript 数据类型¶
SQL 和 JavaScript UDFs 基于其原生数据类型支持,提供相似但不同的数据类型。Snowflake 和 JavaScript 中的对象使用以下映射进行传输。
整数和双精度¶
JavaScript 没有整型类型;所有数字都表示为双精度。除非通过类型转换,否则 JavaScript UDFs 不接受或返回整数值(即,您可以将向接受双精度的 JavaScript UDF 传递整数)。
Snowflake SQL 和 JavaScript 都支持双精度值。这些值按原样传输。
字符串¶
Snowflake SQL 和 JavaScript 都支持字符串值。这些值按原样传输。
二进制值¶
所有二进制值都转换为 JavaScript Uint8Array 对象。这些类型化数组的访问方式与常规 JavaScript 数组相同,但效率更高,并且支持更多方法。
如果 JavaScript UDF 返回 Uint8Array 对象,则将其转换为 Snowflake SQL 二进制值。
日期¶
所有时间戳和日期类型都将转换为 JavaScript Date() 对象。JavaScript 日期类型等效于 Snowflake SQL 中的 TIMESTAMP_LTZ(3)。
对于接受日期或时间的 JavaScript UDFs,请参考以下说明:
精度超过毫秒的所有数据都会丢失。
从 SQL TIMESTAMP_NTZ 生成的 JavaScript
Date不再作为“挂钟”时间;它会受到夏令时的影响。这与将 TIMESTAMP_NTZ 转换为 TIMESTAMP_LTZ 时的行为类似。从 SQL TIMESTAMP_TZ 生成的 JavaScript
Date会丢失时区信息,但表示的时间与输入时间相同(类似于将 TIMESTAMP_TZ 转换为 TIMESTAMP_LTZ)。SQL DATE 会转换为 JavaScript
Date,代表当地时区当前日期的午夜。
此外,对于返回 DATE 和 TIMESTAMP 类型的 JavaScript UDFs,请考虑以下注意事项:
JavaScript
Date对象会转换为 UDF 的结果数据类型,其转换语义与从 TIMESTAMP_LTZ(3) 到返回数据类型的转换语义相同。嵌套在 VARIANT 对象中的 JavaScript
Date对象始终是 TIMESTAMP_LTZ(3) 类型。
变体、对象和数组¶
JavaScript UDFs 允许轻松、直观地操作变体和 JSON 数据。传递给 UDF 的变体对象会转换为原生 JavaScript 类型和值。之前列出的任何值都会转换为相应的 JavaScript 类型。变体对象和数组会转换为 JavaScript 对象和数组。同样,UDF 返回的所有值都将转换为适当的变体值。请注意,UDF 返回的对象和数组受大小和深度的限制。
JavaScript 实参和返回值¶
在 JavaScript 中,实参可以通过名称直接引用。请注意,在引用未加引号的标识符时,必须使用大写的变量名。由于实参和 UDF 是在 JavaScript 中引用的,因此它们必须是合法的 JavaScript 标识符。具体来说,UDF 和实参名称必须以字母或 $ 开头,后面的字符可以是字母数字、$ 或 _。此外,名称不能是 JavaScript 保留字。
以下三个示例说明了使用按名称引用实参的 UDFs:
NULL 和未定义的值¶
使用 JavaScript UDFs 时,请密切注意可能包含 NULL 值的行和变量。具体而言,Snowflake 包含两个不同的 NULL 值(SQL NULL 和变体的 JSON null),而 JavaScript 除 null 外还包含 undefined 值。
JavaScript UDF 的 SQL NULL 实参将转换为 JavaScript undefined 值。同样,返回的 JavaScript undefined 值也会转换回 SQL NULL。这适用于所有数据类型,包括变体。对于非变体类型,返回的 JavaScript null 值也会转换为 SQL NULL 值。
变体类型的实参和返回值区分 JavaScript 的 undefined 和 null 值。SQL NULL 继续转换为 JavaScript undefined`(JavaScript :code:`undefined 返回 SQL NULL);变体 JSON null 转换为 JavaScript null`(JavaScript :code:`null 返回变体 JSON null)。嵌入到 JavaScript 对象(作为值)或数组中的 undefined 值将导致元素被省略。
创建一个包含一个字符串和一个
NULL值的表:创建一个函数,将字符串转换为
NULL,将NULL转换为字符串:调用函数:
创建一个函数,显示传递 SQL
NULL和传递变体 JSONnull之间的区别:创建一个函数,显示返回
undefined、null与包含undefined和null的变体之间的区别(注意,undefined值会从返回的变体中移除):
JavaScript 中的类型转换¶
JavaScript 会在许多不同类型之间隐式转换值。返回任何值时,该值首先转换为请求的返回类型,然后再转换为 SQL 值。例如,如果返回一个数字,但 UDF 声明返回的是一个字符串,则此数字将在 JavaScript 中转换为字符串。请注意,JavaScript 编程错误(如返回错误的类型)可能会被这种行为所掩盖。此外,如果在转换值的类型时出现错误,也会导致出错。
JavaScript 数字范围¶
精度完好无损的数字的范围是
-(2^53 -1)
到
(2^53 -1)
Snowflake NUMBER(p, s)和 DOUBLE 数据类型中的有效值范围更大。从 Snowflake 检索值并将其存储到 JavaScript 数值变量中可能会导致精度损失。例如:
前两列应匹配,第三列应包含 0.0。
该问题适用于 JavaScript 用户定义的函数 (UDFs) 和存储过程。
如果您在存储过程中使用 getColumnValue() 时遇到问题,则可以通过以字符串形式检索值来避免该问题,例如:
然后,您可以返回存储过程中的字符串,并在 SQL 中将该字符串转换为数字数据类型。
JavaScript 错误¶
用户在执行 JavaScript 时遇到的任何错误都会显示为 SQL 错误。这包括解析错误、运行时错误和 UDF 中抛出的未捕获错误。如果错误包含堆栈跟踪,则会与错误消息一起打印出来。为了结束查询并产生 SQL 错误,抛出错误而不捕获是可以接受的。
调试时,您可能会发现在打印错误消息的同时打印实参值非常有用,这样实参值就会显示在 SQL 错误消息文本中。对于确定性 UDFs,这为在本地 JavaScript 引擎中重现错误提供了必要的数据。一种常见的模式是将整个 JavaScript UDF 主体放在 try-catch 块中,将实参值追加到捕获的错误消息中,然后抛出带有扩展消息的错误。在将 UDFs 部署到生产环境之前,应考虑移除此类机制;在错误消息中记录值可能会无意中泄露敏感数据。
函数可以抛出并捕获预定义异常或自定义异常。点击 此处 可查看抛出自定义异常的简单示例。
另请参阅 JavaScript UDFs 故障排除。
JavaScript UDF 安全¶
JavaScript UDFs 通过提供多层查询和数据隔离,旨在确保安全可靠:
执行 JavaScript UDF 的虚拟仓库内的计算资源只能从您的账户内访问(即仓库不会与其他 Snowflake 账户共享资源)。
表数据在虚拟仓库中加密,以防止未经授权的访问。
JavaScript 代码是在受限的引擎内执行的,因此无法从 JavaScript 上下文的系统调用系统资源(例如,无法访问网络和磁盘),并限制了引擎可用的系统资源,特别是内存。
因此,JavaScript UDFs 只能访问执行定义功能所需的数据,除了消耗合理的内存和处理器时间外,不会影响底层系统的状态。