CREATE EXTERNAL FUNCTION - Amazon Redshift

CREATE EXTERNAL FUNCTION

Cria uma função definida pelo usuário (UDF) escalar baseada em AWS Lambda para o Amazon Redshift. Para obter mais informações sobre as funções definidas pelo usuário do Lambda, consulte Criar uma UDF do Lambda escalar.

Privilégios obrigatórios

A seguir estão os privilégios necessários para CREATE EXTERNAL FUNCTION:

  • Superusuário

  • Usuários com o privilégio CREATE [ OR REPLACE ] EXTERNAL FUNCTION

Sintaxe

CREATE [ OR REPLACE ] EXTERNAL FUNCTION external_fn_name ( [data_type] [, ...] ) RETURNS data_type { VOLATILE | STABLE } LAMBDA 'lambda_fn_name' IAM_ROLE { default | ‘arn:aws:iam::<Conta da AWS-id>:role/<role-name>’ RETRY_TIMEOUT milliseconds MAX_BATCH_ROWS count MAX_BATCH_SIZE size [ KB | MB ];

A seguir está a sintaxe para Machine Learning no Amazon Redshift. Para obter informações sobre parâmetros específicos do modelo, consulte Parâmetros.

CREATE [ OR REPLACE ] EXTERNAL FUNCTION external_fn_name ( [data_type] [, ...] ) RETURNS data_type { VOLATILE | STABLE } SAGEMAKER'endpoint_name' IAM_ROLE { default | ‘arn:aws:iam::<Conta da AWS-id>:role/<role-name>’ };

Parâmetros

OR REPLACE

Uma cláusula que especifica que se uma função com o mesmo nome e tipo de dados do argumento de entrada ou assinatura já existir, a função existente será substituída. Você só pode substituir uma função por uma nova função que defina um conjunto idêntico de tipos de dados. É preciso ser um superusuário para substituir uma função.

Se uma função for definida com o mesmo nome que uma função existente mas com uma assinatura diferente, uma nova função será criada. Em outras palavras, o nome da função fica sobrecarregado. Para obter mais informações, consulte Sobrecarga de nomes de função.

external_fn_name

O nome da função externa. Se você especificar um nome de esquema (como myschema.myfunction), a função será criada usando o esquema especificado. Caso contrário, a função será criada no esquema atual. Para obter mais informações sobre nomes válidos, consulte Nomes e identificadores.

Recomendamos que você prefixe os nomes de todos os UDF com f_. O Amazon Redshift reserva o prefixo f_ para nomes UDF. Usando o prefixo f_, você ajuda a garantir que o nome de seu UDF não entrará em conflito com qualquer nome de função SQL integrada do Amazon Redshift agora ou no futuro. Para obter mais informações, consulte Nomeação de UDFs.

data_type

O tipo de dados dos argumentos de entrada. Para obter mais informações, consulte Tipos de dados.

RETURNS tipo_dados

Tipo de dados do valor retornado pela função. O tipo de dados RETURNS pode ser qualquer tipo de dados padrão do Amazon Redshift. Para ter mais informações, consulte Tipos de dados da UDF Python.

VOLATILE | STABLE

Informa o otimizador de consulta sobre a volatilidade da função.

Você terá a melhor otimização se rotular sua função com a categoria mais restrita de volatilidade válida para ela. Por ordem de nível de restrição, começando a partir da menos restrita, as categorias são:

  • VOLATILE

  • STABLE

VOLATILE

Dados os mesmos argumentos, a função pode retornar resultados diferentes em chamadas sucessivas, mesmo para as linhas em uma única instrução. O otimizador de consultas não pode fazer suposições sobre o comportamento de uma função volátil. Uma consulta que usa uma função volátil deve reavaliar a função para cada entrada.

STABLE

Em vista dos mesmos argumentos, a função com certeza retorna os mesmos resultados em chamadas sucessivas processadas em uma única instrução. A função pode retornar resultados diferentes quando chamada em instruções diferentes. Essa categoria possibilita que o otimizador reduza o número de vezes que a função é chamada em uma única instrução.

Se a rigidez escolhida não for válida para a função, existe o risco de o otimizador ignorar  algumas chamadas com base nessa rigidez. Isso pode resultar em um conjunto de resultados incorreto.

No momento, a cláusula IMMUTABLE não é compatível com UDFs do Lambda.

LAMBDA 'lambda_fn_name'

O nome da função chamada pelo Amazon Redshift.

Para obter etapas para criar uma função AWS Lambda, consulte Criar uma função do Lambda com o console no AWS LambdaGuia do desenvolvedor.

Para obter informações sobre permissões necessárias para a função do Lambda, consulte Permissões do AWS Lambda no AWS LambdaGuia do desenvolvedor.

IAM_ROLE { default | ‘arn:aws:iam::<Conta da AWS-id>:role/<role-name>

Use a palavra-chave padrão para que o Amazon Redshift use a função do IAM definida como padrão e associada ao cluster quando o comando CREATE EXTERNAL FUNCTION for executado.

Use o nome do recurso da Amazon (ARN) de uma função do IAM que seu cluster usa para autenticação e autorização. O comando CREATE EXTERNAL FUNCTION está autorizado a invocar funções do Lambda por meio desta função do IAM. Se o cluster tiver uma função do IAM existente com permissões para invocar funções do Lambda anexadas, você poderá substituir o ARN da função. Para obter mais informações, consulte Configurar o parâmetro de autorização para UDFs do Lambda.

A seguir, a sintaxe do parâmetro IAM_ROLE.

IAM_ROLE 'arn:aws:iam::aws-account-id:role/role-name'
RETRY_TIMEOUT milliseconds

A quantidade de tempo total em milissegundos que o Amazon Redshift usa para os atrasos em recuos de novas tentativas.

Em vez de tentar novamente imediatamente para quaisquer consultas com falha, o Amazon Redshift realiza backoffs e espera por um certo período de tempo entre novas tentativas. Em seguida, o Amazon Redshift tenta novamente a solicitação para executar novamente a consulta com falha até que a soma de todos os atrasos seja igual ou exceda o valor RETRY_TIMEOUT especificado. O valor padrão é 20.000 milissegundos.

Quando uma função do Lambda é chamada, o Amazon Redshift tenta novamente consultas que recebem erros como TooManyRequestsException, EC2ThrottledException, eServiceException.

Você pode definir o parâmetro RETRY_TIMEOUT como 0 milissegundos para evitar quaisquer tentativas para um Lambda UDF.

Contagem de MAX_BATCH_ROWS

O número máximo de linhas que o Amazon Redshift envia em uma única solicitação em lote para uma invocação do Lambda.

O valor mínimo desse parâmetro é 1. O valor máximo é INT_MAX, ou 2.147.483.647.

Esse parâmetro é opcional. O valor padrão é INT_MAX, ou 2.147.483.647.

Tamanho de MAX_BATCH_SIZE [ KB | MB ]

O tamanho máximo da carga de dados que o Amazon Redshift envia em uma única solicitação em lote para uma invocação do Lambda.

O valor mínimo desse parâmetro é 1 KB. O valor máximo é 5 MB.

O valor padrão desse parâmetro é 5 MB.

KB e MB são opcionais. Se você não definir a unidade de medida, o Amazon Redshift usará KB como padrão.

Observações de uso

Considere o seguinte ao criar UDFs do Lambda:

  • A ordem das chamadas da função do Lambda nos argumentos de entrada não é fixa nem garantida. Isso pode variar entre as instâncias de execução de consultas, dependendo da configuração do cluster.

  • Não é garantido que as funções sejam aplicadas a cada argumento de entrada somente uma vez. A interação entre o Amazon Redshift e o AWS Lambda pode levar a chamadas repetitivas com as mesmas entradas.

Exemplos

A seguir estão exemplos de uso de funções definidas pelo usuário (UDFs) do Lambda escalar.

Exemplo de UDF Lambda escalar usando uma função Node.js Lambda

O exemplo a seguir cria uma função externa chamada exfunc_sum que leva dois inteiros como argumentos de entrada. Esta função retorna a soma como uma saída inteira. O nome da função do Lambda a ser chamada é lambda_sum. A linguagem usada para essa função do Lambda é Node.js 12.x. Especifique a função do IAM. O exemplo usa 'arn:aws:iam::123456789012:user/johndoe' como a função do IAM.

CREATE EXTERNAL FUNCTION exfunc_sum(INT,INT) RETURNS INT VOLATILE LAMBDA 'lambda_sum' IAM_ROLE 'arn:aws:iam::123456789012:role/Redshift-Exfunc-Test';

A função do Lambda recebe a carga útil de solicitação e itera sobre cada linha. Todos os valores em uma única linha são adicionados para calcular a soma dessa linha, que é salva na matriz de resposta. O número de linhas na matriz de resultados é semelhante ao número de linhas recebidas na carga útil da solicitação.

A carga útil da resposta JSON deve ter os dados do resultado no campo “resultados” para que ele seja reconhecido pela função externa. O campo argumentos na solicitação enviada para a função Lambda contém a carga útil de dados. Pode haver várias linhas na carga útil de dados no caso de uma solicitação em lote. A seguinte função do Lambda itera sobre todas as linhas na carga útil de dados de solicitação. Ele também itera individualmente sobre todos os valores dentro de uma única linha.

exports.handler = async (event) => { // The 'arguments' field in the request sent to the Lambda function contains the data payload. var t1 = event['arguments']; // 'len(t1)' represents the number of rows in the request payload. // The number of results in the response payload should be the same as the number of rows received. const resp = new Array(t1.length); // Iterating over all the rows in the request payload. for (const [i, x] of t1.entries()) { var sum = 0; // Iterating over all the values in a single row. for (const y of x) { sum = sum + y; } resp[i] = sum; } // The 'results' field should contain the results of the lambda call. const response = { results: resp }; return JSON.stringify(response); };

O exemplo a seguir chama a função externa com valores literais.

select exfunc_sum(1,2); exfunc_sum ------------ 3 (1 row)

O exemplo a seguir cria uma tabela chamada t_sum com duas colunas, c1 e c2, do tipo de dados inteiro e insere duas linhas de dados. Em seguida, a função externa é chamada passando os nomes de coluna desta tabela. As duas linhas da tabela são enviadas em uma solicitação de lote na carga útil de solicitação como uma única invocação do Lambda.

CREATE TABLE t_sum(c1 int, c2 int); INSERT INTO t_sum VALUES (4,5), (6,7); SELECT exfunc_sum(c1,c2) FROM t_sum; exfunc_sum --------------- 9 13 (2 rows)

Exemplo de UDF Lambda escalar usando o atributo RETRY_TIMEOUT

Na seção a seguir, você pode encontrar um exemplo de como usar o atributo RETRY_TIMEOUT em UDFs do Lambda.

Funções AWS Lambda têm limites de simultaneidade que você pode definir para cada função. Para obter mais informações sobre limites de simultaneidade, consulte Gerenciamento de simultaneidade para uma função do Lambda no Guia do desenvolvedor do AWS Lambda e a publicação Gerenciamento da simultaneidade de funções do AWS Lambda no Blog de computação da AWS.

Quando o número de solicitações que estão sendo atendidas por um UDF Lambda excede os limites de simultaneidade, as novas solicitações recebem o erro TooManyRequestsException. O UDF Lambda tenta novamente neste erro até que a soma de todos os atrasos entre as solicitações enviadas para a função do Lambda seja igual ou exceda o valor RETRY_TIMEOUT definido. O valor padrão de RETRY_TIMEOUT é 20.000 milissegundos.

O exemplo a seguir cria uma função do Lambda chamada exfunc_sleep_3. Esta função recebe a carga útil de solicitação, itera sobre cada linha e converte a entrada para maiúsculas. Em seguida, dorme por 3 segundos e retorna o resultado. A linguagem usada para esta função do Lambda é Python 3.8.

O número de linhas na matriz de resultados é semelhante ao número de linhas recebidas na carga útil da solicitação. A carga útil da resposta JSON deve ter os dados do resultado no campo results para que ele seja reconhecido pela função externa. O campo arguments na solicitação enviada para a função do Lambda contém a carga útil de dados. Pode haver várias linhas na carga útil de dados no caso de uma solicitação em lote.

O limite de simultaneidade para essa função é definido especificamente como 1 em simultaneidade reservada para demonstrar o uso do atributo RETRY_TIMEOUT. Quando o atributo é definido como 1, a função do Lambda só pode servir uma solicitação por vez.

import json import time def lambda_handler(event, context): t1 = event['arguments'] # 'len(t1)' represents the number of rows in the request payload. # The number of results in the response payload should be the same as the number of rows received. resp = [None]*len(t1) # Iterating over all rows in the request payload. for i, x in enumerate(t1): # Iterating over all the values in a single row. for j, y in enumerate(x): resp[i] = y.upper() time.sleep(3) ret = dict() ret['results'] = resp ret_json = json.dumps(ret) return ret_json

A seguir, dois exemplos adicionais ilustram o atributo RETRY_TIMEOUT. Cada um deles invoca um único UDF Lambda. Ao invocar o UDF Lambda, cada exemplo executa a mesma consulta SQL para invocar o UDF Lambda de duas sessões simultâneas de banco de dados ao mesmo tempo. Quando a primeira consulta que invoca o UDF Lambda está sendo atendida pelo UDF, a segunda consulta recebe o erro TooManyRequestsException. Esse resultado ocorre porque você define especificamente a simultaneidade reservada no UDF como 1. Para obter informações sobre como definir a simultaneidade reservada para funções do Lambda, consulte Configurar a simultaneidade reservada.

O primeiro exemplo a seguir define o atributo RETRY_TIMEOUT para o UDF Lambda como 0 milissegundos. Se a solicitação do Lambda receber exceções da função do Lambda, o Amazon Redshift não fará novas tentativas. Esse resultado ocorre porque o atributo RETRY_TIMEOUT está definido como 0.

CREATE OR REPLACE EXTERNAL FUNCTION exfunc_upper(varchar) RETURNS varchar VOLATILE LAMBDA 'exfunc_sleep_3' IAM_ROLE 'arn:aws:iam::123456789012:role/Redshift-Exfunc-Test' RETRY_TIMEOUT 0;

Com o RETRY_TIMEOUT definido como 0, você pode executar as duas consultas a seguir de sessões de banco de dados separadas para ver resultados diferentes.

A primeira consulta SQL que usa o UDF Lambda é executada com êxito.

select exfunc_upper('Varchar'); exfunc_upper -------------- VARCHAR (1 row)

A segunda consulta, que é executada a partir de uma sessão de banco de dados separada ao mesmo tempo, recebe o erro TooManyRequestsException.

select exfunc_upper('Varchar'); ERROR: Rate Exceeded.; Exception: TooManyRequestsException; ShouldRetry: 1 DETAIL: ----------------------------------------------- error: Rate Exceeded.; Exception: TooManyRequestsException; ShouldRetry: 1 code: 32103 context:query: 0 location: exfunc_client.cpp:102 process: padbmaster [pid=26384] -----------------------------------------------

O segundo exemplo a seguir define o atributo RETRY_TIMEOUT para o UDF Lambda como 3.000 milissegundos. Mesmo se a segunda consulta for executada simultaneamente, o UDF Lambda tenta novamente até que o total de atrasos seja de 3.000 milissegundos. Assim, ambas as consultas são executadas com êxito.

CREATE OR REPLACE EXTERNAL FUNCTION exfunc_upper(varchar) RETURNS varchar VOLATILE LAMBDA 'exfunc_sleep_3' IAM_ROLE 'arn:aws:iam::123456789012:role/Redshift-Exfunc-Test' RETRY_TIMEOUT 3000;

Com o RETRY_TIMEOUT definido como 3.000 milissegundos, você pode executar as duas consultas a seguir de sessões de banco de dados separadas para ver os mesmos resultados.

A primeira consulta SQL que usa o UDF Lambda é executada com êxito.

select exfunc_upper('Varchar'); exfunc_upper -------------- VARCHAR (1 row)

A segunda consulta é executada simultaneamente e o UDF Lambda tenta novamente até que o atraso total seja de 3.000 milissegundos.

select exfunc_upper('Varchar'); exfunc_upper -------------- VARCHAR (1 row)

Exemplo de UDF Lambda escalar usando uma função Python Lambda

O exemplo a seguir cria uma função externa chamada exfunc_multiplication que multiplica números e retorna um inteiro. Este exemplo incorpora o campo success e error_msg na resposta do Lambda. O campo success é definido como false quando há um estouro de inteiro no resultado da multiplicação, e a propriedade error_msg está definida como Integer multiplication overflow. A função exfunc_multiplication leva três inteiros como argumentos de entrada e retorna a soma como uma saída inteira.

O nome da função do Lambda chamada é lambda_multiplication. A linguagem usada para esta função do Lambda é Python 3.8. Especifique a função do IAM.

CREATE EXTERNAL FUNCTION exfunc_multiplication(int, int, int) RETURNS INT VOLATILE LAMBDA 'lambda_multiplication' IAM_ROLE 'arn:aws:iam::123456789012:role/Redshift-Exfunc-Test';

A função do Lambda recebe a carga útil de solicitação e itera sobre cada linha. Todos os valores em uma única linha são multiplicados para calcular o resultado dessa linha, que é salvo na lista de respostas. Este exemplo usa um valor de sucesso booleano definido como true por padrão. Se o resultado da multiplicação de uma linha tiver um estouro de inteiro, o valor de sucesso será definido como false. Em seguida, o loop de iteração quebra.

Ao criar a carga útil de resposta, se o valor de sucesso for false, a seguinte função do Lambda adiciona o campo error_msg na carga útil. Também define a mensagem de erro como Integer multiplication overflow. Se o valor de sucesso for true, os dados do resultado serão adicionados no campo de resultados. O número de linhas na matriz de resultados, se houver, é semelhante ao número de linhas recebidas na carga útil da solicitação.

O campo argumentos na solicitação enviada para a função Lambda contém a carga útil de dados. Pode haver várias linhas na carga útil de dados no caso de uma solicitação em lote. A seguinte função do Lambda itera sobre todas as linhas na carga útil de dados de solicitação e itera individualmente sobre todos os valores dentro de uma única linha.

import json def lambda_handler(event, context): t1 = event['arguments'] # 'len(t1)' represents the number of rows in the request payload. # The number of results in the response payload should be the same as the number of rows received. resp = [None]*len(t1) # By default success is set to 'True'. success = True # Iterating over all rows in the request payload. for i, x in enumerate(t1): mul = 1 # Iterating over all the values in a single row. for j, y in enumerate(x): mul = mul*y # Check integer overflow. if (mul >= 9223372036854775807 or mul <= -9223372036854775808): success = False break else: resp[i] = mul ret = dict() ret['success'] = success if not success: ret['error_msg'] = "Integer multiplication overflow" else: ret['results'] = resp ret_json = json.dumps(ret) return ret_json

O exemplo a seguir chama a função externa com valores literais.

SELECT exfunc_multiplication(8, 9, 2); exfunc_multiplication --------------------------- 144 (1 row)

O exemplo a seguir cria uma tabela chamada t_multi com três colunas, c1, c2 e c3, do tipo de dados inteiro. A função externa é chamada passando os nomes das colunas desta tabela. Os dados são inseridos de forma a causar estouro de inteiro para mostrar como o erro é propagado.

CREATE TABLE t_multi (c1 int, c2 int, c3 int); INSERT INTO t_multi VALUES (2147483647, 2147483647, 4); SELECT exfunc_multiplication(c1, c2, c3) FROM t_multi; DETAIL: ----------------------------------------------- error: Integer multiplication overflow code: 32004context: context: query: 38 location: exfunc_data.cpp:276 process: query2_16_38 [pid=30494] -----------------------------------------------