CREATE EXTERNAL FUNCTION - Amazon Redshift

CREATE EXTERNAL FUNCTION

Crea una función definida por el usuario (UDF) escalar basada en AWS Lambda para Amazon Redshift. Para obtener más información acerca de las funciones de Lambda definidas por el usuario, consulte UDF de Lambda escalares.

Privilegios necesarios

Los siguientes privilegios son necesarios para CREATE EXTERNAL FUNCTION:

  • Superusuario

  • Usuarios con el privilegio CREATE [ OR REPLACE ] EXTERNAL FUNCTION

Sintaxis

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::<Cuenta de AWS-id>:role/<role-name>’ RETRY_TIMEOUT milliseconds MAX_BATCH_ROWS count MAX_BATCH_SIZE size [ KB | MB ];

Parámetros

OR REPLACE

Se trata de una cláusula que especifica que, si ya existe una función con el mismo nombre y los mismos tipos de datos de argumento de entrada o firma que este, debe reemplazarse la función existente. Solo puede reemplazar una función con una nueva función que defina un conjunto idéntico de tipos de datos. Debe ser un superusuario para reemplazar una función.

Si define una función con el mismo nombre que una función existente, pero con una firma diferente, crea una nueva función. En otras palabras, se sobrecarga el nombre de la función. Para obtener más información, consulte Sobrecarga de los nombres de función.

external_fn_name

Se trata del nombre de la función externa. Si especifica un nombre de esquema (como myschema.myfunction), la función se crea con el esquema especificado. De lo contrario, la función se crea en el esquema actual. Para obtener más información acerca de los nombres válidos, consulte Nombres e identificadores.

Recomendamos que utilice el prefijo f_ en los nombres de todas las UDF. Amazon Redshift reserva el prefijo f_ para los nombres de las UDF. Con el uso del prefijo f_, se asegura de que los nombres de las UDF no entren en conflicto con ninguno de los nombres de las funciones SQL integradas para Amazon Redshift ahora o en el futuro. Para obtener más información, consulte Prevención de conflictos al dar nombre a las UDF.

data_type

Se trata del tipo de datos para los argumentos de entrada. Para obtener más información, consulte Tipos de datos.

RETURNS data_type

El tipo de datos del valor que la función devuelve. El tipo de datos RETURNS puede ser cualquier tipo de datos estándar de Amazon Redshift. Para obtener más información, consulte Tipos de datos de UDF de Python.

VOLATILE | STABLE

Informa al optimizador de consultas acerca de la volatilidad de la función.

Para obtener la mejor optimización, etiquete la función con la categoría de volatilidad válida más estricta. Las categorías de volatilidad, de la menos estricta a la más estricta, son las siguientes:

  • VOLATILE

  • STABLE

VOLATILE

Dados los mismos argumentos, la función puede devolver resultados diferentes en ejecuciones consecutivas, incluso para las filas de una única instrucción. El optimizador de consultas no puede hacer suposiciones sobre el comportamiento de una función volátil. Una consulta que utiliza una función volátil debe reevaluar la función para cada entrada.

STABLE

Dados los mismos argumentos, se garantiza que la función devuelve los mismos resultados en sucesivas llamadas procesadas en una misma instrucción. La función puede devolver resultados diferentes cuando se evoca en diferentes instrucciones. Con esta categoría, el optimizador puede reducir el número de veces que se llama a la función en una misma instrucción.

Tenga en cuenta que si el rigor elegido no es válido para la función, existe el riesgo de que el optimizador omita algunas llamadas basándose en este rigor. Esto puede provocar un conjunto de resultados incorrecto.

La cláusula IMMUTABLE no es compatible actualmente con las UDF de Lambda.

LAMBDA 'lambda_fn_name'

Se trata del nombre de la función a la que llama Amazon Redshift.

Si desea conocer los pasos necesarios para crear una función de AWS Lambda, consulte Creación de una función de Lambda con la consola en la Guía para desarrolladores de AWS Lambda.

Para obtener información acerca de los permisos necesarios para la función de Lambda, consulte Permisos de AWS Lambda en la Guía para desarrolladores de AWS Lambda.

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

Utilice la palabra clave predeterminada para que Amazon Redshift utilice el rol de IAM configurado como predeterminado y asociado al clúster cuando se ejecuta el comando CREATE EXTERNAL FUNCTION.

Utilice el nombre de recurso de Amazon (ARN), de un rol de IAM que el clúster utiliza para la autenticación y la autorización. El comando CREATE EXTERNAL FUNCTION tiene autorización para invocar funciones de Lambda a través de este rol de IAM. Si su clúster tiene un rol de IAM existente con permisos para invocar funciones de Lambda adjuntas, puede sustituir el ARN de su rol. Para obtener más información, consulte Configuración del parámetro de autorización para las UDF de Lambda.

A continuación, se muestra la sintaxis del parámetro IAM_ROLE.

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

Se trata de la cantidad de tiempo total en milisegundos que Amazon Redshift utiliza para los retrasos en el reintento de retroceso.

En lugar de volver a intentar ejecutar inmediatamente cualquier consulta que presente error, Amazon Redshift realiza retrocesos y espera cierta cantidad de tiempo entre reintentos. Luego, Amazon Redshift vuelve a intentar realizar la solicitud para ejecutar la consulta que presenta error de nuevo hasta que la suma de todos los retrasos sea igual o superior al valor RETRY_TIMEOUT que especificó. El valor predeterminado es 20 000 milisegundos.

Cuando se invoca una función de Lambda, Amazon Redshift vuelve a intentar ejecutar las consultas que presentan errores, como TooManyRequestsException, EC2ThrottledException y ServiceException.

Puede configurar el parámetro RETRY_TIMEOUT en 0 milisegundos para evitar cualquier reintento de ejecución de una UDF de Lambda.

MAX_BATCH_ROWS recuento

El número máximo de filas que Amazon Redshift envía en una sola solicitud por lotes para una única invocación de lambda.

El valor mínimo de este parámetro es 1. El valor máximo es INT_MAX o 2 147 483 647.

Este parámetro es opcional. El valor predeterminado es INT_MAX o 2 147 483 647.

MAX_BATCH_SIZE tamaño [ KB | MB ]

El tamaño máximo de la carga útil de datos que Amazon Redshift envía en una sola solicitud por lotes para una única invocación de lambda.

El valor mínimo de este parámetro es 1 KB. El valor máximo es 5 MB.

El valor predeterminado de este parámetro es 5 MB.

KB y MB son opcionales. Si no establece la unidad de medida, Amazon Redshift utilizará KB de forma predeterminada.

Notas de uso

Tenga en cuenta lo siguiente al crear UDF de Lambda:

  • El orden de las llamadas a la función de Lambda en los argumentos de entrada no es fijo ni está garantizado. Puede variar de una instancia a otra de las consultas en ejecución, en función de la configuración del clúster.

  • No se garantiza que las funciones se apliquen a cada argumento de entrada una vez y solo una vez. La interacción entre Amazon Redshift y AWS Lambda podría dar lugar a llamadas repetitivas con las mismas entradas.

Ejemplos

A continuación, se proporcionan ejemplos de uso de funciones escalares de Lambda definidas por el usuario (UDF).

Ejemplo de UDF escalar de Lambda con una función Node.js de Lambda

En el siguiente ejemplo, se crea una función externa denominada exfunc_sum que toma dos valores enteros como argumentos de entrada. Esta función devuelve la suma como una salida expresada con un número entero. El nombre de la función de Lambda a la que se llamará es lambda_sum. El lenguaje utilizado para esta función de Lambda es Node.js 12.x. Asegúrese de especificar el rol de IAM. El ejemplo utiliza 'arn:aws:iam::123456789012:user/johndoe' como rol de IAM.

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

La función de Lambda toma la carga de solicitudes e itera sobre cada fila. Se suman todos los valores de una sola fila para calcular la suma de esa fila, que se guarda en la matriz de respuesta. La cantidad de filas en la matriz de resultados es similar a la cantidad de filas recibidas en la carga de la solicitud.

La carga de respuesta JSON debe tener los datos de resultado en el campo “results” para que la función externa la reconozca. El campo de argumentos de la solicitud enviada a la función de Lambda contiene la carga de datos. En el caso de una solicitud por lote, es posible que existan varias filas en la carga de datos. La siguiente función de Lambda itera sobre todas las filas de la carga de datos de la solicitud. También itera de forma individual sobre todos los valores dentro de una sola fila.

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); };

En el siguiente ejemplo, se llama a la función externa con valores literales.

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

En el siguiente ejemplo, se crea una tabla denominada t_sum con dos columnas, c1 y c2, del tipo de datos entero y se insertan dos filas de datos. Luego, se llama a la función externa transmitiendo los nombres de las columnas de esta tabla. Las dos filas de la tabla se envían en una solicitud por lote en la carga de la solicitud como una sola invocación de 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)

Ejemplo de UDF escalar de Lambda con el atributo RETRY_TIMEOUT

En la siguiente sección, encontrará un ejemplo de cómo utilizar el atributo RETRY_TIMEOUT en las UDF de Lambda.

Las funciones de AWS Lambda tienen límites de simultaneidad que se pueden establecer para cada función. Para obtener más información acerca de los límites de simultaneidad, consulte Administración de la simultaneidad para una función de Lambda en la Guía para desarrolladores de AWS Lambda y la publicación Administración de la simultaneidad de la función AWS Lambda en el blog de informática de AWS.

Cuando la cantidad de solicitudes que atiende una UDF de Lambda supera los límites de simultaneidad, las solicitudes nuevas reciben el error TooManyRequestsException. La UDF de Lambda intenta solucionar este error de nuevo hasta que la suma de todos los retrasos entre las solicitudes enviadas a la función de Lambda sea igual o superior al valor RETRY_TIMEOUT que estableció. El valor predeterminado de RETRY_TIMEOUT es 20 000 milisegundos.

En el siguiente ejemplo, se utiliza una función de Lambda denominada exfunc_sleep_3. Esta función toma la carga de la solicitud, itera sobre cada fila y convierte la entrada a mayúsculas. Luego, está inactiva durante 3 segundos y devuelve el resultado. El lenguaje utilizado para esta función de Lambda es Python 3.8.

La cantidad de filas en la matriz de resultados es similar a la cantidad de filas recibidas en la carga de la solicitud. La carga de respuesta JSON debe tener los datos de resultado en el campo results para que la función externa la reconozca. El campo de arguments de la solicitud enviada a la función de Lambda contiene la carga de datos. En el caso de una solicitud por lote, es posible que aparezcan varias filas en la carga de datos.

El límite de simultaneidad para esta función se configura específicamente en 1 en simultaneidad reservada para demostrar el uso del atributo RETRY_TIMEOUT. Cuando el atributo se configura en 1, la función de Lambda solo puede atender una solicitud a la 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 continuación, se incluyen dos ejemplos adicionales que ilustran el atributo RETRY_TIMEOUT. Cada ejemplo invoca una sola UDF de Lambda. Cuando invocan la UDF de Lambda, cada ejemplo ejecuta la misma consulta SQL para invocar la UDF de Lambda desde dos sesiones de base de datos simultáneas. Cuando la primera consulta que invoca la UDF de Lambda es atendida por la UDF, la segunda consulta recibe el error TooManyRequestsException. Este resultado se produce porque se configura de forma específica la simultaneidad reservada en 1 en la UDF. Para obtener información sobre cómo establecer la simultaneidad reservada para las funciones de Lambda, consulte Configuración de la simultaneidad reservada.

A continuación, el primer ejemplo configura el atributo RETRY_TIMEOUT para la UDF de Lambda en 0 milisegundos. Si la solicitud de Lambda recibe alguna excepción de la función de Lambda, Amazon Redshift no realiza ningún reintento. Este resultado se produce porque el atributo RETRY_TIMEOUT se configura en 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;

Con RETRY_TIMEOUT configurado en 0, puede ejecutar las siguientes dos consultas desde sesiones de base de datos independientes para ver resultados diferentes.

La primera consulta SQL que utiliza la UDF de Lambda se ejecuta correctamente.

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

La segunda consulta, que se ejecuta desde una sesión de base de datos independiente al mismo tiempo, recibe el error 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] -----------------------------------------------

A continuación, el segundo ejemplo configura el atributo RETRY_TIMEOUT para la UDF de Lambda en 3000 milisegundos. Incluso si la segunda consulta se ejecuta de forma simultánea, la UDF de Lambda reintenta la ejecución hasta que el retraso total sea de 3000 milisegundos. Por lo tanto, ambas consultas se ejecutan correctamente.

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;

Con RETRY_TIMEOUT configurado en 3000, puede ejecutar las siguientes dos consultas desde sesiones de base de datos independientes para ver los mismos resultados.

La primera consulta SQL que ejecuta la UDF de Lambda se ejecuta correctamente.

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

La segunda consulta se ejecuta de forma simultánea, y la UDF de Lambda reintenta la ejecución hasta que el retraso total sea de 3000 milisegundos.

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

Ejemplo de UDF escalar de Lambda con una función Python de Lambda

En el siguiente ejemplo, se crea una función externa denominada exfunc_multiplication que multiplica números y devuelve un número entero. Este ejemplo incorpora los campos de éxito y error_msg en la respuesta de Lambda. El campo de éxito se establece como false cuando hay un desbordamiento de valores enteros en el resultado de la multiplicación y el mensaje error_msg se establece como Integer multiplication overflow. La función exfunc_multiplication toma tres valores enteros como argumentos de entrada y devuelve la suma como un valor de salida entero.

El nombre de la función de Lambda que se llama es lambda_multiplication. El lenguaje utilizado para esta función de Lambda es Python 3.8. Asegúrese de especificar el rol de 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';

La función de Lambda toma la carga de solicitudes e itera sobre cada fila. Todos los valores de una sola fila se multiplican para calcular el resultado de esa fila, que se guarda en la lista de respuestas. En este ejemplo, se utiliza un valor booleano de éxito establecido como true de manera predeterminada. Si el resultado de la multiplicación de una fila tiene un desbordamiento de valores enteros, el valor de éxito se establece como false. Por lo tanto, se interrumpe el ciclo de iteraciones.

Durante la creación de la carga de respuesta, si el valor de éxito es false, la siguiente función de Lambda agrega el campo error_msg a la carga. También establece el mensaje de error en Integer multiplication overflow. Si el valor de éxito es true, los datos de resultado se agregan en el campo de resultados. La cantidad de filas en la matriz de resultados, si hay, es similar a la cantidad de filas recibidas en la carga de la solicitud.

El campo de argumentos de la solicitud enviada a la función de Lambda contiene la carga de datos. En el caso de una solicitud por lote, es posible que existan varias filas en la carga de datos. La siguiente función de Lambda itera sobre todas las filas de la carga de datos de la solicitud e itera de manera individual sobre todos los valores dentro de una sola fila.

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

En el siguiente ejemplo, se llama a la función externa con valores literales.

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

En el siguiente ejemplo, se crea una tabla denominada t_multi con tres columnas, c1, c2 y c3, del tipo de datos entero. Se llama a la función externa transmitiendo los nombres de las columnas de esta tabla. Los datos se insertan de tal manera que provocan un desbordamiento de valores enteros para mostrar cómo se propaga el error.

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] -----------------------------------------------