将应用程序逻辑添加到应用程序包¶
本主题介绍如何将应用程序逻辑添加到应用程序包的安装脚本中。本主题还介绍了如何在应用程序包中使用外部代码文件。
有关在应用程序包中包含 Streamlit in Snowflake 应用程序的信息,请参阅 使用 Streamlit 为应用程序添加前端体验。
使用存储过程和函数的注意事项¶
Snowflake Native App Framework 允许您在应用程序包中包含存储过程、用户的定义函数 (UDFs) 和外部函数。这些可以用 :ref:` Snowflake 支持的任何语言 <label-sp_udf_languages>` 编写。
如果您计划将 Snowflake Native App 以有限试用列表形式发布到 Snowflake Marketplace,并且希望限制这些试用使用者可使用的应用程序功能,请参阅 准备提供受限试用列表。
安全地添加应用程序代码¶
Snowflake Native App 中的所有存储过程和 UDFs 作为应用程序运行,并且可以访问已安装的 Snowflake Native App 中的所有对象。这可能会导致 SQL 注入攻击。
在开发用于 Snowflake Native App 的过程和函数时,Snowflake 建议使用绑定参数运行所有需要用户输入的 SQL 命令。这包括通过过程实参提供的输入。
有关更多信息,请参阅 创建存储过程。
关于调用方权限和所有者权限¶
由安装脚本创建的或在已安装的 Snowflake Native App 中运行的所有过程都必须以所有者的权限运行 (EXECUTE AS OWNER)。
之所以存在此限制,是因为如果 Snowflake Native App 在 Snowflake Native App 未拥有的过程中使用调用方的权限 (EXECUTE AS CALLER) 运行,则该过程将作为 Snowflake Native App 自身运行,并允许使用者创建代码来查看或修改 Snowflake Native App 的内容和共享数据内容。
有关更多信息,请参阅 了解调用方权限和所有者权限存储过程。
从安装脚本调用上下文函数时的限制¶
:doc:` 上下文函数 </sql-reference/functions-context>` 提供有关运行语句的上下文的信息。在 Snowflake Native App Framework 上下文中,某些上下文函数不可用。不可用的上下文函数要么被阻止并返回错误,要么始终返回空值。
通常,在应用于 Snowflake Native App 内的共享数据内容的策略中使用上下文函数时必须十分谨慎。某些函数(例如 CURRENT_IP_ADDRESS)在 Snowflake Native App 的上下文中表现不同。
请注意,在使用依赖于客户端组织内的命名空间的上下文函数时,可能会与其他命名空间中的函数发生冲突。例如,使用 CURRENT_USER 的行访问策略时,应注意同一用户名可以存在于多个账户中。
下表列出了 Snowflake Native App Framework 不支持的上下文函数:
上下文函数 |
在共享内容时被阻止(返回 null) |
在 Snowflake Native App 拥有的安装脚本和存储过程以及 UDFs 中被阻止(引发异常) |
---|---|---|
CURRENT_ROLE |
✔ |
|
CURRENT_ROLE_TYPE |
✔ |
|
CURRENT_USER |
✔ |
|
IS_ROLE_IN_SESSION |
✔ |
|
CURRENT_IP_ADDRESS |
✔ |
✔ |
CURRENT_AVAILABLE_ROLES |
✔ |
✔ |
CURRENT_SECONDARY_ROLES |
✔ |
✔ |
ALL_USER_NAMES |
✔ |
|
GET_USERS_FOR_COLLABORATION |
✔ |
|
CURRENT_WAREHOUSE |
✔ |
|
SYSTEM$ALLOWLIST |
✔ |
在应用程序包中使用 Snowpark 函数和过程¶
Snowflake Native App Framework 支持 Snowpark 库,用于使用 Java、Scala 和 Python 创建存储过程。
引用外部代码文件¶
应用程序包中可以包含两种类型的代码文件:
引用文件:包括二进制文件、库和其他代码文件。这些文件特定于应用程序包中定义的版本。在创建版本或向应用程序包添加版本时,这些文件必须位于暂存区的根目录中。
引用的文件不同于用户定义的函数和存储过程,因为它们未在应用程序包的安装脚本中定义。这些文件由安装脚本中定义的存储过程和 UDFs 中的导入语句所引用。
资源文件:包括半结构化数据、结构化数据和二进制文件,例如机器学习模型。 这些文件必须上传到应用程序包可访问的命名暂存区。
必须在安装脚本的版本化架构中创建引用这些类型的代码文件的存储过程、用户定义的函数或外部函数。在版本化架构中创建存储过程或函数时,必须引用相对于命名暂存区的根目录的代码文件。
例如,如果命名暂存区的根目录是 /app_files/dev
,则此目录将包含以下文件和目录:
manifest.yml
文件。包含安装脚本的目录,例如
scripts/setup_version.sql
。在安装脚本中创建存储过程、UDF 或外部函数时导入的引用文件,例如:
libraries/jars/lookup.jar
libraries/jars/log4j.jar
libraries/python/evaluate.py
在此场景中,目录结构如下所示:
@DEV_DB.DEV_SCHEMA.DEV_STAGE/V1:
└── app_files/
└── dev
├── manifest.yml
└── scripts/
├── setup_script.sql
└── libraries/
└── jars/
├── lookup.jar
└── log4j.jar
└── python
└── evaluation.py
若要访问此目录结构中的 JAR 文件,安装脚本中定义的存储过程将引用这些文件,如以下示例所示:
CREATE PROCEDURE PROGRAMS.LOOKUP(...)
RETURNS STRING
LANGUAGE JAVA
PACKAGES = ('com.snowflake:snowpark:latest')
IMPORTS = ('/scripts/libraries/jar/lookup.jar',
'/scripts/libraries/jar/log4j.jar')
HANDLER = 'com.acme.programs.Lookup';
在此示例中,IMPORTS 语句有相对于用于创建版本的根目录的路径,例如,manifest.yml
文件的位置。
在应用程序包中包含 Java 及 Scala 代码¶
Snowflake Native App Framework 支持在存储过程和外部代码文件中使用 Java 和 Scala。
以内联方式创建 Java 和 Scala UDFs¶
Snowflake Native App Framework 支持创建包含 Java 和 Scala 的存储过程。定义存储过程的代码必须添加到安装脚本中。
以下示例显示了包含 Java 函数的存储过程:
CREATE OR ALTER VERSIONED SCHEMA app_code;
CREATE STAGE app_code.app_jars;
CREATE FUNCTION app_code.add(x INT, y INT)
RETURNS INTEGER
LANGUAGE JAVA
HANDLER = 'TestAddFunc.add'
TARGET_PATH = '@app_code.app_jars/TestAddFunc.jar'
AS
$$
class TestAddFunc {
public static int add(int x, int y) {
Return x + y;
}
}
$$;
导入外部 Java 及 Scala UDFs¶
创建预编译 UDFs 的语法要求将导入的 JARs 作为一组版本化工件的一部分包含在内。要引用预编译 JARs,请使用相对路径,而不是在 IMPORT 子句中指定整个暂存区的位置。
路径必须相对于包含以单个正斜杠开头的版本的根目录,例如 IMPORTS = ('/path/to/JARs/from/version/root')
。有关相对路径的更多信息,请参阅 引用外部代码文件。
以下示例显示了代码文件的示例目录结构。
@DEV_DB.DEV_SCHEMA.DEV_STAGE/V1:
└── V1/
├── manifest.yml
├── setup_script.sql
└── JARs/
├── Java/
│ └── TestAddFunc.jar
└── Scala/
└── TestMulFunc.jar
以下示例演示如何使用 JAR 文件创建 Java 函数:
CREATE FUNCTION app_code.add(x INTEGER, y INTEGER)
RETURNS INTEGER
LANGUAGE JAVA
HANDLER = 'TestAddFunc.add'
IMPORTS = ('/JARs/Java/TestAddFunc.jar');
对 Java 和 Scala UDFs 的限制¶
Snowflake Native App Framework 在使用 Java 和 Scala 时施加以下限制:
只有在版本化架构中创建的 UDFs 才允许导入。
导入只能使用相对路径访问版本工件。
UDFs 在版本化架构之外创建的架构只能以内联方式创建。
TARGET_PATH 不支持相对路径。
将 Python 代码添加到应用程序包¶
Snowflake Native App Framework 支持在存储过程和外部代码文件中使用 Python。
在安装脚本中定义 Python 函数¶
Snowflake Native App Framework 支持使用 Python 创建存储过程。
以下示例显示了包含 Python 函数的存储过程:
CREATE FUNCTION app_code.py_echo_func(str STRING)
RETURNS STRING
LANGUAGE PYTHON
HANDLER = 'echo'
AS
$$
def echo(str):
return "ECHO: " + str
$$;
使用外部 Python 文件¶
以下示例演示如何在应用程序包中包含外部 Python 文件:
CREATE FUNCTION PY_PROCESS_DATA_FUNC()
RETURNS STRING
LANGUAGE PYTHON
HANDLER = 'TestPythonFunc.process'
IMPORTS = ('/python_modules/TestPythonFunc.py',
'/python_modules/data.csv')
有关相对路径的更多信息,请参阅 引用外部代码文件。
对 Python UDFs 的限制¶
Snowflake Native App Framework 对 Python UDFs 施加以下限制:
只有在版本化架构中创建的 UDFs 才允许导入。
导入只能使用相对路径访问版本工件。
UDFs 在版本化架构之外创建的架构只能以内联方式创建。
向应用程序包添加 JavaScript 函数和过程¶
Snowflake Native App Framework 支持在存储过程中使用 JavaScript,以及使用 JavaScript API 的用户定义的函数。
处理 JavaScript 错误¶
在应用程序包中使用 JavaScript 时,Snowflake 建议您捕获并处理错误。如果不这么做,则错误返回的错误消息和堆栈跟踪对使用者可见。若要确保数据内容和应用程序逻辑保持私密,请在访问敏感对象或数据的情况下使用 try/catch 块。
以下示例演示捕获错误并返回消息的 JavaScript 存储过程:
CREATE OR REPLACE PROCEDURE APP_SCHEMA.ERROR_CATCH()
RETURNS STRING
LANGUAGE JAVASCRIPT
EXECUTE AS OWNER
AS $$
try {
let x = y.length;
}
catch(err){
return "There is an error.";
}
return "Done";
$$;
此示例创建包含 try/catch 块的 JavaScript 存储过程。如果存储过程在运行 try
块中的语句时遇到错误,它会返回使用者可见的消息“There is an error”。
如果没有 try/catch 块,存储过程将返回原始错误消息和使用者可见的完整堆栈跟踪。
备注
Snowflake Native App Framework 支持的其他语言会返回 Snowflake Native App 中出现的编辑错误消息。
将外部函数添加到应用程序包¶
:doc:` 外部函数 </sql-reference/sql/create-external-function>` 允许 Snowflake Native App 调用托管在 Snowflake 外部的应用程序代码。外部函数要求您创建 API 集成对象。
由于 API 集成允许在使用者环境之外进行连接,因此使用者必须提供与 Snowflake Native App 集成的方法。
以下示例演示由安装脚本创建的存储过程,该脚本接受集成并创建外部函数。此示例演示如何在应用程序包的安装脚本中创建外部函数:
CREATE OR REPLACE PROCEDURE calculator.create_external_function(integration_name STRING)
RETURNS STRING
LANGUAGE SQL
EXECUTE AS OWNER
AS
DECLARE
CREATE_STATEMENT VARCHAR;
BEGIN
CREATE_STATEMENT := 'CREATE OR REPLACE EXTERNAL FUNCTION EXTERNAL_ADD(NUM1 FLOAT, NUM2 FLOAT)
RETURNS FLOAT API_INTEGRATION = ? AS ''https://xyz.execute-api.us-west-2.amazonaws.com/production/sum'';' ;
EXECUTE IMMEDIATE :CREATE_STATEMENT USING (INTEGRATION_NAME);
RETURN 'EXTERNAL FUNCTION CREATED';
END;
GRANT USAGE ON PROCEDURE calculator.create_external_function(string) TO APPLICATION ROLE app_public;
此示例定义用 SQL 编写的存储过程,并 创建一个外部函数,该函数引用 Snowflake 外部系统上托管的应用程序。外部函数返回 API 集成。
此示例还将存储过程的 USAGE 授予应用程序角色。使用者必须先向 Snowflake Native App 授予此权限,然后才能在安装脚本中调用此过程。