Como adicionar lógica de aplicativo a um pacote de aplicativo

Este tópico descreve como adicionar lógica de aplicativo ao script de configuração de um pacote de aplicativo. Ele também descreve como usar arquivos de código externos em um pacote de aplicativo.

Consulte Adição de um aplicativo Streamlit a um pacote de aplicativo para obter mais informações sobre como incluir um aplicativo Streamlit em um pacote de aplicativo.

Considerações sobre o uso de procedimentos armazenados e funções

O Snowflake Native App Framework permite incluir procedimentos armazenados, funções definidas pelo usuário (UDFs) e funções externas em um pacote de aplicativo. Estes podem ser escritos em qualquer uma das linguagens com suporte Snowflake.

Como adicionar o código do aplicativo com segurança

Todos os procedimentos armazenados e UDFs dentro de um Snowflake Native App são executados como o aplicativo e têm acesso a todos os objetos dentro do Snowflake Native App instalado. Isso pode levar a ataques de injeção SQL.

Ao desenvolver procedimentos e funções para uso em um Snowflake Native App, a Snowflake recomenda que todos os comandos SQL que exigem entrada de usuários sejam executados usando parâmetros vinculados. Isso inclui entrada fornecida por meio de argumentos de procedimento.

Consulte Criação de um procedimento armazenado para obter mais informações.

Sobre os direitos do chamador e os direitos do proprietário

Todos os procedimentos criados pelo script de configuração ou executados no Snowflake Native App instalado devem ser executados com os direitos do proprietário (EXECUTE AS OWNER).

Essa restrição existe porque se o Snowflake Native App fosse executado com os direitos do chamador (EXECUTE AS CALLER) em um procedimento que o Snowflake Native App não possui, o procedimento seria executado como o próprio Snowflake Native App e permitiria que um consumidor criasse código para visualizar ou modificar o conteúdo de Snowflake Native App e conteúdo de dados compartilhados.

Consulte Understanding Caller’s Rights and Owner’s Rights Stored Procedures para obter mais informações.

Limitações ao chamar funções de contexto do script de configuração

As funções de contexto fornecem informações sobre o contexto no qual uma instrução é executada. Dentro do contexto de Snowflake Native App Framework, algumas funções de contexto não estão disponíveis. As funções de contexto que não estão disponíveis são bloqueadas e retornam um erro ou sempre retornam um valor nulo.

Em geral, você deve ter cuidado ao usar funções de contexto em políticas aplicadas ao conteúdo de dados compartilhados em um Snowflake Native App. Algumas funções, por exemplo CURRENT_IP_ADDRESS, se comportam de maneira diferente no contexto de um Snowflake Native App.

Observe que ao usar funções de contexto que dependem do namespace na organização do cliente, pode haver conflitos com funções em outros namespaces. Por exemplo, uma política de acesso a linhas usando CURRENT_USER deve estar ciente de que o mesmo nome de usuário pode existir em várias contas.

A tabela a seguir lista as funções de contexto que não são compatíveis com o Snowflake Native App Framework:

Função de contexto

Bloqueado em conteúdo compartilhado (retorna nulo)

Bloqueado em scripts de configuração e procedimento armazenado e UDFs de propriedade do Snowflake Native App (lança uma exceção).

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

Como usar funções e procedimentos do Snowpark em um aplicativo

O Snowflake Native App Framework oferece suporte às bibliotecas Snowpark para criação de procedimentos armazenados em Java, Scala e Python.

Referência a arquivos de código externos

Existem dois tipos de arquivos de código que você pode incluir em um pacote de aplicativo:

  • Arquivos referenciados: incluem binários, bibliotecas e outros arquivos de código. Esses arquivos são específicos de uma versão definida em um pacote de aplicativo. Esses arquivos devem estar localizados no diretório raiz do estágio ao criar ou adicionar uma versão a um pacote de aplicativo.

    Os arquivos referenciados são diferentes das funções definidas pelo usuário e dos procedimentos armazenados porque não são definidos no script de configuração de um pacote de aplicativo. Esses arquivos são referenciados por instruções de importação dentro dos procedimentos armazenados e UDFs definidos no script de configuração.

  • Arquivos de recursos: incluem dados semiestruturados, dados estruturados e binários, por exemplo, um modelo de aprendizado de máquina. Esses arquivos devem ser carregados em um estágio nomeado acessível ao pacote de aplicativo.

Um procedimento armazenado, função definida pelo usuário ou função externa que faz referência a esses tipos de arquivos de código deve ser criado em um esquema com versão no script de configuração. Ao criar procedimentos armazenados ou funções em um esquema com versão, você deve fazer referência a um arquivo de código relativo ao diretório raiz do estágio nomeado.

Por exemplo, se o diretório raiz do estágio nomeado for /app_files/dev, esse diretório conterá os seguintes arquivos e diretórios:

  • Um arquivo manifest.yml.

  • Um diretório que contém o script de configuração, por exemplo scripts/setup_version.sql.

  • Arquivos referenciados que são importados ao criar um procedimento armazenado, UDF ou função externa dentro do script de configuração, por exemplo:

    • libraries/jars/lookup.jar

    • libraries/jars/log4j.jar

    • libraries/python/evaluate.py

Nesse cenário, a estrutura de diretórios seria a seguinte:

@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
Copy

Para acessar os arquivos JAR nessa estrutura de diretório, um procedimento armazenado definido no script de configuração faria referência a esses arquivos conforme mostrado no exemplo a seguir:

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';
Copy

Neste exemplo, a instrução IMPORTS tem um caminho relativo ao diretório raiz usado para criar a versão, por exemplo, o local do arquivo manifest.yml.

Como adicionar código Java e Scala a um pacote de aplicativo

O Snowflake Native App Framework oferece suporte ao uso de Java e Scala em procedimentos armazenados e em arquivos de código externos.

Como criar UDFs de Java e Scala em linha

O Snowflake Native App Framework oferece suporte à criação de procedimentos armazenados contendo Java e Scala. O código que define o procedimento armazenado deve ser adicionado ao script de configuração.

O exemplo a seguir mostra um procedimento armazenado contendo uma função 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;
       }
   }
 $$;
Copy

Como importar UDFs de Java e Scala externas

A sintaxe para criar UDFs pré-compiladas exige que os JARs importados sejam incluídos como parte de um conjunto de artefatos com versão. Para se referir aos JARs pré-compilados, use o caminho relativo em vez de especificar o local completo do estágio na cláusula IMPORT.

O caminho deve ser relativo ao diretório raiz que contém a versão que começa com uma única barra, por exemplo IMPORTS = ('/path/to/JARs/from/version/root'). Consulte Referência a arquivos de código externos para obter mais informações sobre caminhos relativos.

A seguir é mostrado um exemplo de estrutura de diretório para os arquivos de código.

@DEV_DB.DEV_SCHEMA.DEV_STAGE/V1:
└── V1/
 ├── manifest.yml
 ├── setup_script.sql
 └── JARs/
     ├── Java/
     │   └── TestAddFunc.jar
     └── Scala/
         └── TestMulFunc.jar
Copy

O exemplo a seguir mostra como criar uma função Java usando um arquivo JAR:

CREATE FUNCTION app_code.add(x INTEGER, y INTEGER)
  RETURNS INTEGER
  LANGUAGE JAVA
  HANDLER = 'TestAddFunc.add'
  IMPORTS = ('/JARs/Java/TestAddFunc.jar');
Copy

Restrições em UDFs de Java e Scala

O Snowflake Native App Framework impõe as seguintes restrições ao usar Java e Scala:

  • As importações são permitidas apenas para UDFs criadas em um esquema com versão.

  • As importações só podem acessar os artefatos de versão usando um caminho relativo.

  • UDFs criadas fora dos esquemas com versão só podem ser criados em linha.

  • Caminhos relativos não são suportados para TARGET_PATH.

Como adicionar código Python a um pacote de aplicativo

O Snowflake Native App Framework oferece suporte ao uso do Python em procedimentos armazenados e em arquivos de código externos.

Como definir uma função Python no script de configuração

O Snowflake Native App Framework oferece suporte à criação de procedimentos armazenados em Python.

O exemplo a seguir mostra um procedimento armazenado contendo uma função Python:

CREATE FUNCTION app_code.py_echo_func(str STRING)
RETURNS STRING
LANGUAGE PYTHON
HANDLER = 'echo'
AS
$$
  def echo(str):
    return "ECHO: " + str
$$;
Copy

Como usar arquivos Python externos

O exemplo a seguir mostra como incluir um arquivo Python externo em um pacote de aplicativo:

CREATE FUNCTION PY_PROCESS_DATA_FUNC()
  RETURNS STRING
  LANGUAGE PYTHON
  HANDLER = 'TestPythonFunc.process'
  IMPORTS = ('/python_modules/TestPythonFunc.py',
    '/python_modules/data.csv')
Copy

Consulte Referência a arquivos de código externos para obter mais informações sobre caminhos relativos.

Restrições de UDFs de Python

Snowflake Native App Framework impõe as seguintes restrições às UDFs de Python:

  • As importações são permitidas apenas para UDFs criadas em um esquema com versão.

  • As importações só podem acessar os artefatos de versão usando um caminho relativo.

  • UDFs criadas fora dos esquemas com versão só podem ser criados em linha.

Como adicionar funções e procedimentos JavaScript a um pacote de aplicativo

O Snowflake Native App Framework oferece suporte ao uso de JavaScript em procedimentos armazenados e funções definidas pelo usuário usando a API JavaScript.

Tratamento de erros de JavaScript

Ao usar JavaScript em um pacote de aplicativo, Snowflake recomenda que você detecte e lide com os erros. Caso contrário, a mensagem de erro e o rastreamento de pilha que o erro retorna ficam visíveis para o consumidor. Para garantir que o conteúdo dos dados e a lógica do aplicativo sejam mantidos privados, use blocos try/catch em situações em que objetos ou dados confidenciais estão sendo acessados.

O exemplo a seguir mostra um procedimento armazenado JavaScript que detecta um erro e retorna uma mensagem:

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";
  $$;
Copy

Este exemplo cria um procedimento armazenado JavaScript que contém um bloco try/catch. Se o procedimento armazenado encontrar um erro ao executar a instrução no bloco try, ele retornará a mensagem «There is an error”, que fica visível para o consumidor.

Sem o bloco try/catch, o procedimento armazenado retornaria a mensagem de erro original e o rastreamento de pilha completo que ficaria visível para o consumidor.

Nota

Outras linguagens suportadas pelo Snowflake Native App Framework retornam mensagens de erro redigidas que ocorrem em um Snowflake Native App.

Como adicionar funções externas a um pacote de aplicativo

As funções externas permitem que um Snowflake Native App faça chamadas para o código do aplicativo hospedado fora do Snowflake. As funções externas exigem que você crie um objeto de integração de API.

Como as integrações de API permitem conectividade fora do ambiente do consumidor, o consumidor deve fornecer o método de integração ao Snowflake Native App.

O exemplo a seguir mostra um procedimento armazenado criado pelo script de configuração que aceita a integração e cria uma função externa. Este exemplo mostra como criar uma função externa no script de configuração do pacote de aplicativo:

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;
Copy

Este exemplo define um procedimento armazenado, escrito em SQL, e cria uma função externa que faz referência a um aplicativo hospedado em um sistema fora do Snowflake. A função externa retorna uma integração de API.

Este exemplo também concede USAGE no procedimento armazenado para uma função de aplicativo. O consumidor deve conceder esse privilégio ao Snowflake Native App antes de chamar esse procedimento no script de configuração.