36.5. Funciones del lenguaje de consulta (SQL) #

36.5.1. Argumentos para funciones SQL
36.5.2. Funciones SQL en tipos base
36.5.3. Funciones SQL en tipos compuestos
36.5.4. Funciones SQL con parámetros de salida
36.5.5. Procedimientos SQL con parámetros de salida
36.5.6. Funciones SQL con un número variable de argumentos
36.5.7. Funciones SQL con valores por omisión para argumentos
36.5.8. Funciones SQL como fuentes de tabla
36.5.9. Funciones SQL que devuelven conjuntos
36.5.10. Funciones SQL que devuelven TABLE
36.5.11. Funciones SQL polimórficas
36.5.12. Funciones SQL con ordenamientos (collations)

Las funciones SQL ejecutan una lista arbitraria de sentencias SQL, devolviendo el resultado de la última consulta de la lista. En el caso simple (que no devuelve un conjunto), se devolverá la primera fila del resultado de la última consulta. (Ten en cuenta que la primera fila de un resultado de múltiples filas no está bien definida a menos que utilices ORDER BY). Si la última consulta resulta que no devuelve ninguna fila, se devolverá el valor nulo.

Alternativamente, se puede declarar que una función SQL devuelva un conjunto (es decir, múltiples filas) especificando el tipo de retorno de la función como SETOF sometype, o de manera equivalente declarándola como RETURNS TABLE(columns). En este caso se devuelven todas las filas del resultado de la última consulta. Más adelante se detallan otros aspectos.

El cuerpo de una función SQL debe ser una lista de sentencias SQL separadas por punto y coma. El punto y coma después de la última sentencia es opcional. A menos que la función se declare para devolver void, la última sentencia debe ser un SELECT, o un INSERT, UPDATE, DELETE o MERGE que tenga una cláusula RETURNING.

Cualquier colección de comandos en el lenguaje SQL puede empaquetarse y definirse como una función. Además de las consultas SELECT, los comandos pueden incluir consultas de modificación de datos (INSERT, UPDATE, DELETE y MERGE), así como otros comandos SQL. (No se pueden utilizar comandos de control de transacciones, por ejemplo, COMMIT, SAVEPOINT y algunos comandos de utilidad, por ejemplo, VACUUM, en las funciones SQL). Sin embargo, el último comando debe ser un SELECT o tener una cláusula RETURNING que devuelva lo que se haya especificado como el tipo de retorno de la función. Alternativamente, si deseas definir una función SQL que realice acciones pero no tenga un valor útil que devolver, puedes definirla como devuelta de tipo void. Por ejemplo, esta función elimina las filas con salarios negativos de la tabla emp:

CREATE FUNCTION clean_emp() RETURNS void AS '
    DELETE FROM emp
        WHERE salary < 0;
' LANGUAGE SQL;

SELECT clean_emp();

 clean_emp
-----------

(1 row)

También puedes escribir esto como un procedimiento, evitando así la cuestión del tipo de retorno. Por ejemplo:

CREATE PROCEDURE clean_emp() AS '
    DELETE FROM emp
        WHERE salary < 0;
' LANGUAGE SQL;

CALL clean_emp();

En casos sencillos como este, la diferencia entre una función que devuelve void y un procedimiento es principalmente estilística. Sin embargo, los procedimientos ofrecen funcionalidad adicional, como el control de transacciones, que no está disponible en las funciones. Además, los procedimientos son estándar SQL, mientras que devolver void es una extensión de PostgreSQL.

La sintaxis del comando CREATE FUNCTION requiere que el cuerpo de la función se escriba como una constante de cadena. Por lo general, resulta más conveniente utilizar el entrecomillado de dólares (dollar quoting) (ver Section 4.1.2.4) para la constante de cadena. Si decides utilizar la sintaxis habitual de constantes de cadena entrecomilladas con comillas simples, debes duplicar las comillas simples (') y las barras diagonales inversas (\) (asumiendo la sintaxis de escape de cadenas) en el cuerpo de la función (ver Section 4.1.2.1).

36.5.1. Argumentos para funciones SQL #

Los argumentos de una función SQL se pueden referenciar en el cuerpo de la función utilizando nombres o números. A continuación se muestran ejemplos de ambos métodos.

Para usar un nombre, declara el argumento de la función con un nombre y luego simplemente escribe ese nombre en el cuerpo de la función. Si el nombre del argumento es el mismo que cualquier nombre de columna en el comando SQL actual dentro de la función, el nombre de la columna tendrá prioridad. Para anular esto, califica el nombre del argumento con el nombre de la función misma, es decir, function_name.argument_name. (Si esto entra en conflicto con un nombre de columna calificado, nuevamente gana el nombre de la columna. Puedes evitar la ambigüedad eligiendo un alias diferente para la tabla dentro del comando SQL).

En el enfoque numérico más antiguo, los argumentos se referencian utilizando la sintaxis $n: $1 se refiere al primer argumento de entrada, $2 al segundo, y así sucesivamente. Esto funcionará independientemente de si el argumento en particular se declaró con un nombre o no.

Si un argumento es de un tipo compuesto, se puede utilizar la notación de punto, por ejemplo, argname.fieldname o $1.fieldname, para acceder a los atributos del argumento. Nuevamente, es posible que debas calificar el nombre del argumento con el nombre de la función para que la forma con un nombre de argumento sea inequívoca.

Los argumentos de las funciones SQL solo se pueden utilizar como valores de datos, no como identificadores. Por lo tanto, por ejemplo, esto es razonable:

INSERT INTO mytable VALUES ($1);

pero esto no funcionará:

INSERT INTO $1 VALUES (42);

Note

La capacidad de usar nombres para referenciar argumentos de funciones SQL se añadió en PostgreSQL 9.2. Las funciones que se utilicen en servidores más antiguos deben usar la notación $n.

36.5.2. Funciones SQL en tipos base #

La función SQL más sencilla posible no tiene argumentos y simplemente devuelve un tipo base, como integer:

CREATE FUNCTION one() RETURNS integer AS $$
    SELECT 1 AS result;
$$ LANGUAGE SQL;

-- Sintaxis alternativa para constante de cadena:
CREATE FUNCTION one() RETURNS integer AS '
    SELECT 1 AS result;
' LANGUAGE SQL;

SELECT one();

 one
-----
   1

Observa que definimos un alias de columna dentro del cuerpo de la función para el resultado de la función (con el nombre result), pero este alias de columna no es visible fuera de la función. Por lo tanto, el resultado se etiqueta como one en lugar de result.

Es casi igual de fácil definir funciones SQL que tomen tipos base como argumentos:

CREATE FUNCTION add_em(x integer, y integer) RETURNS integer AS $$
    SELECT x + y;
$$ LANGUAGE SQL;

SELECT add_em(1, 2) AS answer;

 answer
--------
      3

Como alternativa, podríamos prescindir de los nombres de los argumentos y utilizar números:

CREATE FUNCTION add_em(integer, integer) RETURNS integer AS $$
    SELECT $1 + $2;
$$ LANGUAGE SQL;

SELECT add_em(1, 2) AS answer;

 answer
--------
      3

Aquí hay una función más útil, que podría utilizarse para debitar una cuenta bancaria:

CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS numeric AS $$
    UPDATE bank
        SET balance = balance - debit
        WHERE accountno = tf1.accountno;
    SELECT 1;
$$ LANGUAGE SQL;

Un usuario podría ejecutar esta función para debitar $100.00 de la cuenta 17 como sigue:

SELECT tf1(17, 100.0);

En este ejemplo, elegimos el nombre accountno para el primer argumento, pero este es el mismo que el nombre de una columna en la tabla bank. Dentro del comando UPDATE, accountno se refiere a la columna bank.accountno, por lo que se debe utilizar tf1.accountno para referirse al argumento. Por supuesto, podríamos evitar esto utilizando un nombre diferente para el argumento.

En la práctica, probablemente se querría obtener de la función un resultado más útil que una constante 1, por lo que una definición más probable es:

CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS numeric AS $$
    UPDATE bank
        SET balance = balance - debit
        WHERE accountno = tf1.accountno;
    SELECT balance FROM bank WHERE accountno = tf1.accountno;
$$ LANGUAGE SQL;

que ajusta el saldo y devuelve el nuevo saldo. Lo mismo se podría hacer en un solo comando utilizando RETURNING:

CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS numeric AS $$
    UPDATE bank
        SET balance = balance - debit
        WHERE accountno = tf1.accountno
    RETURNING balance;
$$ LANGUAGE SQL;

Si la cláusula final SELECT o RETURNING en una función SQL no devuelve exactamente el tipo de resultado declarado de la función, PostgreSQL convertirá automáticamente el valor al tipo requerido, si esto es posible con una conversión implícita o de asignación. De lo contrario, debes escribir una conversión explícita (cast). Por ejemplo, supongamos que queremos que la función add_em anterior devuelva en su lugar el tipo float8. Es suficiente escribir:

CREATE FUNCTION add_em(integer, integer) RETURNS float8 AS $$
    SELECT $1 + $2;
$$ LANGUAGE SQL;

ya que la suma de tipo integer se puede convertir implícitamente a float8. (Consulta la Chapter 10 o la CREATE CAST para obtener más información sobre las conversiones).

36.5.3. Funciones SQL en tipos compuestos #

Al escribir funciones con argumentos de tipos compuestos, no solo debemos especificar qué argumento queremos, sino también el atributo (campo) deseado de ese argumento. Por ejemplo, supongamos que emp es una tabla que contiene datos de empleados y, por lo tanto, también el nombre del tipo compuesto de cada fila de la tabla. Aquí hay una función double_salary que calcula cuál sería el salario de alguien si se duplicara:

CREATE TABLE emp (
    name        text,
    salary      numeric,
    age         integer,
    cubicle     point
);

INSERT INTO emp VALUES ('Bill', 4200, 45, '(2,1)');

CREATE FUNCTION double_salary(emp) RETURNS numeric AS $$
    SELECT $1.salary * 2 AS salary;
$$ LANGUAGE SQL;

SELECT name, double_salary(emp.*) AS dream
    FROM emp
    WHERE emp.cubicle ~= point '(2,1)';

 name | dream
------+-------
 Bill |  8400

Observa el uso de la sintaxis $1.salary para seleccionar un campo del valor de la fila del argumento. También observa cómo el comando SELECT invocador utiliza table_name.* para seleccionar toda la fila actual de una tabla como un valor compuesto. Alternativamente, se puede hacer referencia a la fila de la tabla utilizando simplemente el nombre de la tabla, así:

SELECT name, double_salary(emp) AS dream
    FROM emp
    WHERE emp.cubicle ~= point '(2,1)';

pero este uso está obsoleto ya que es fácil confundirse. (Consulta la Section 8.16.5 para obtener detalles sobre estas dos notaciones para el valor compuesto de una fila de tabla).

A veces resulta práctico construir un valor de argumento compuesto sobre la marcha. Esto se puede hacer con la estructura ROW. Por ejemplo, podríamos ajustar los datos que se pasan a la función:

SELECT name, double_salary(ROW(name, salary*1.1, age, cubicle)) AS dream
    FROM emp;

También es posible construir una función que devuelva un tipo compuesto. Este es un ejemplo de una función que devuelve una sola fila de tipo emp:

CREATE FUNCTION new_emp() RETURNS emp AS $$
    SELECT text 'None' AS name,
        1000.0 AS salary,
        25 AS age,
        point '(2,2)' AS cubicle;
$$ LANGUAGE SQL;

En este ejemplo hemos especificado cada uno de los atributos con un valor constante, pero se podría haber sustituido cualquier cálculo por estas constantes.

Ten en cuenta dos cosas importantes sobre la definición de la función:

  • El orden de la lista de selección en la consulta debe ser exactamente el mismo que aquel en el que aparecen las columnas en el tipo compuesto. (Dar nombre a las columnas, como hicimos anteriormente, es irrelevante para el sistema).

  • Debemos asegurarnos de que el tipo de cada expresión se pueda convertir al de la columna correspondiente del tipo compuesto. De lo contrario, obtendremos errores como este:

    
    ERROR:  return type mismatch in function declared to return emp
    DETAIL:  Final statement returns text instead of point at column 4.
    
    

    Al igual que en el caso del tipo base, el sistema no insertará conversiones explícitas automáticamente, solo conversiones implícitas o de asignación.

Una forma diferente de definir la misma función es:

CREATE FUNCTION new_emp() RETURNS emp AS $$
    SELECT ROW('None', 1000.0, 25, '(2,2)')::emp;
$$ LANGUAGE SQL;

Aquí escribimos un SELECT que devuelve solo una columna del tipo compuesto correcto. Esto no es realmente mejor en esta situación, pero es una alternativa práctica en algunos casos — por ejemplo, si necesitamos calcular el resultado invocando otra función que devuelve el valor compuesto deseado. Otro ejemplo es que si estamos tratando de escribir una función que devuelva un dominio sobre un tipo compuesto, en lugar de un tipo compuesto simple, siempre es necesario escribirla para que devuelva una sola columna, ya que no hay forma de forzar una coerción de todo el resultado de la fila.

Podríamos invocar esta función directamente ya sea utilizándola en una expresión de valor:

SELECT new_emp();

         new_emp
--------------------------
 (None,1000.0,25,"(2,2)")

o invocándola como una función de tabla:

SELECT * FROM new_emp();

 name | salary | age | cubicle
------+--------+-----+---------
 None | 1000.0 |  25 | (2,2)

La segunda forma se describe más detalladamente en la Section 36.5.8.

Cuando utilizas una función que devuelve un tipo compuesto, es posible que desees solo un campo (atributo) de su resultado. Puedes hacer eso con una sintaxis como esta:

SELECT (new_emp()).name;

 name
------
 None

Los paréntesis adicionales son necesarios para evitar que el analizador se confunda. Si intentas hacerlo sin ellos, obtendrás algo como esto:

SELECT new_emp().name;
ERROR:  syntax error at or near "."
LINE 1: SELECT new_emp().name;
                        ^

Como se explica en la Section 8.16.5, la notación de campo y la notación funcional son equivalentes.

Otra forma de utilizar una función que devuelve un tipo compuesto es pasar el resultado a otra función que acepte el tipo de fila correcto como entrada:

CREATE FUNCTION getname(emp) RETURNS text AS $$
    SELECT $1.name;
$$ LANGUAGE SQL;

SELECT getname(new_emp());
 getname
---------
 None
(1 row)

36.5.4. Funciones SQL con parámetros de salida #

Una forma alternativa de describir los resultados de una función es definirla con parámetros de salida, como en este ejemplo:

CREATE FUNCTION add_em (IN x int, IN y int, OUT sum int)
AS 'SELECT x + y'
LANGUAGE SQL;

SELECT add_em(3,7);
  add_em
--------
     10
(1 row)

Esto no es esencialmente diferente de la versión de add_em mostrada en la Section 36.5.2. El valor real de los parámetros de salida es que proporcionan una forma conveniente de definir funciones que devuelven varias columnas. Por ejemplo:

CREATE FUNCTION sum_n_product (x int, y int, OUT sum int, OUT product int)
AS 'SELECT x + y, x * y'
LANGUAGE SQL;

 SELECT * FROM sum_n_product(11,42);
 sum | product
-----+---------
  53 |     462
(1 row)

Lo que ha sucedido esencialmente aquí es que hemos creado un tipo compuesto anónimo para el resultado de la función. El ejemplo anterior tiene el mismo resultado final que:

CREATE TYPE sum_prod AS (sum int, product int);

CREATE FUNCTION sum_n_product (int, int) RETURNS sum_prod
AS 'SELECT $1 + $2, $1 * $2'
LANGUAGE SQL;

pero no tener que molestarse con la definición de tipo compuesto por separado suele ser muy práctico. Ten en cuenta que los nombres adjuntos a los parámetros de salida no son solo decorativos, sino que determinan los nombres de columna del tipo compuesto anónimo. (Si omites un nombre para un parámetro de salida, el sistema elegirá un nombre por su cuenta).

Ten en cuenta que los parámetros de salida no se incluyen en la lista de argumentos de llamada al invocar tal función desde SQL. Esto se debe a que PostgreSQL considera únicamente los parámetros de entrada para definir la firma de llamada de la función. Esto significa también que solo los parámetros de entrada importan al hacer referencia a la función para propósitos tales como eliminarla. Podríamos eliminar la función anterior con cualquiera de los siguientes comandos:

DROP FUNCTION sum_n_product (x int, y int, OUT sum int, OUT product int);
DROP FUNCTION sum_n_product (int, int);

Los parámetros se pueden marcar como IN (por defecto), OUT, INOUT o VARIADIC. Un parámetro INOUT sirve tanto como parámetro de entrada (parte de la lista de argumentos de llamada) como parámetro de salida (parte del tipo de registro de resultado). Los parámetros VARIADIC son parámetros de entrada, pero se tratan de manera especial como se describe a continuación.

36.5.5. Procedimientos SQL con parámetros de salida #

Los parámetros de salida también están admitidos en los procedimientos, pero funcionan de manera un poco diferente a las funciones. En los comandos CALL, los parámetros de salida deben incluirse en la lista de argumentos. Por ejemplo, la rutina de débito de cuenta bancaria anterior podría escribirse así:

CREATE PROCEDURE tp1 (accountno integer, debit numeric, OUT new_balance numeric) AS $$
    UPDATE bank
        SET balance = balance - debit
        WHERE accountno = tp1.accountno
    RETURNING balance;
$$ LANGUAGE SQL;

Para invocar este procedimiento, debe incluirse un argumento que coincida con el parámetro OUT. Es costumbre escribir NULL:

CALL tp1(17, 100.0, NULL);

Si escribes otra cosa, debe ser una expresión que se pueda convertir implícitamente al tipo declarado del parámetro, al igual que para los parámetros de entrada. Ten en cuenta, sin embargo, que dicha expresión no será evaluada.

Al invocar un procedimiento desde PL/pgSQL, en lugar de escribir NULL debes escribir una variable que recibirá la salida del procedimiento. Consulta la Section 41.6.3 para obtener detalles.

36.5.6. Funciones SQL con un número variable de argumentos #

Las funciones SQL pueden declararse para aceptar un número variable de argumentos, siempre que todos los argumentos opcionales sean del mismo tipo de datos. Los argumentos opcionales serán pasados a la función como un array. La función se declara marcando el último parámetro como VARIADIC; este parámetro debe declararse como un tipo de array. Por ejemplo:

CREATE FUNCTION mleast(VARIADIC arr numeric[]) RETURNS numeric AS $$
    SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i);
$$ LANGUAGE SQL;

SELECT mleast(10, -1, 5, 4.4);
 mleast
--------
     -1
(1 row)

Efectivamente, todos los argumentos reales en la posición VARIADIC o más allá de ella se agrupan en un array unidimensional, como si hubieras escrito

SELECT mleast(ARRAY[10, -1, 5, 4.4]);    -- no funciona

En realidad no puedes escribir eso, o al menos no coincidirá con esta definición de función. Un parámetro marcado como VARIADIC coincide con una o más ocurrencias de su tipo de elemento, no de su propio tipo.

A veces es útil poder pasar un array ya construido a una función variádica; esto es particularmente útil cuando una función variádica quiere pasar su parámetro de array a otra. Además, esta es la única forma segura de llamar a una función variádica que se encuentre en un esquema que permita a usuarios no confiables crear objetos; consulta Section 10.3. Puedes hacerlo especificando VARIADIC en la llamada:

SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4]);

Esto evita la expansión del parámetro variádico de la función en su tipo de elemento, permitiendo así que el valor del argumento del array coincida normalmente. VARIADIC solo puede adjuntarse al último argumento real de una llamada a función.

Especificar VARIADIC en la llamada también es la única forma de pasar un array vacío a una función variádica, por ejemplo:

SELECT mleast(VARIADIC ARRAY[]::numeric[]);

Escribir simplemente SELECT mleast() no funciona porque un parámetro variádico debe coincidir al menos con un argumento real. (Podrías definir una segunda función también llamada mleast, sin parámetros, si quisieras permitir tales llamadas).

Los parámetros de elementos de array generados a partir de un parámetro variádico se tratan como si no tuvieran nombres propios. Esto significa que no es posible llamar a una función variádica usando argumentos con nombre (Section 4.3), excepto cuando especificas VARIADIC. Por ejemplo, esto funcionará:

SELECT mleast(VARIADIC arr => ARRAY[10, -1, 5, 4.4]);

but not these:

SELECT mleast(arr => 10);
SELECT mleast(arr => ARRAY[10, -1, 5, 4.4]);

36.5.7. Funciones SQL con valores por omisión para argumentos #

Las funciones pueden declararse con valores por omisión para algunos o todos los argumentos de entrada. Los valores por omisión se insertan cuando la función se llama con argumentos reales insuficientes. Dado que los argumentos solo se pueden omitir del final de la lista de argumentos reales, todos los parámetros después de un parámetro con un valor por omisión también deben tener valores por omisión. (Aunque el uso de la notación de argumentos con nombre podría permitir relajar esta restricción, todavía se impone para que la notación de argumentos posicionales funcione de manera lógica). Ya sea que lo uses o no, esta capacidad genera la necesidad de precauciones al llamar funciones en bases de datos donde algunos usuarios desconfían de otros; consulta Section 10.3.

Por ejemplo:

CREATE FUNCTION foo(a int, b int DEFAULT 2, c int DEFAULT 3)
RETURNS int
LANGUAGE SQL
AS $$
    SELECT $1 + $2 + $3;
$$;

SELECT foo(10, 20, 30);
 foo
-----
  60
(1 row)

SELECT foo(10, 20);
 foo
-----
  33
(1 row)

SELECT foo(10);
 foo
-----
  15
(1 row)

SELECT foo();  -- falla porque no hay valor por omisión para el primer argumento
ERROR:  function foo() does not exist

El signo = también se puede usar en lugar de la palabra clave DEFAULT.

36.5.8. Funciones SQL como fuentes de tabla #

Todas las funciones SQL se pueden usar en la cláusula FROM de una consulta, pero esto es particularmente útil para funciones que devuelven tipos compuestos. Si la función está definida para devolver un tipo base, la función de tabla produce una tabla de una sola columna. Si la función está definida para devolver un tipo compuesto, la función de tabla produce una columna para cada atributo del tipo compuesto.

Aquí hay un ejemplo:

CREATE TABLE foo (fooid int, foosubid int, fooname text);
INSERT INTO foo VALUES (1, 1, 'Joe');
INSERT INTO foo VALUES (1, 2, 'Ed');
INSERT INTO foo VALUES (2, 1, 'Mary');

CREATE FUNCTION getfoo(int) RETURNS foo AS $$
    SELECT * FROM foo WHERE fooid = $1;
$$ LANGUAGE SQL;

SELECT *, upper(fooname) FROM getfoo(1) AS t1;

 fooid | foosubid | fooname | upper
-------+----------+---------+-------
     1 |        1 | Joe     | JOE
(1 row)

Como muestra el ejemplo, podemos trabajar con las columnas del resultado de la función de la misma manera que si fueran columnas de una tabla común y corriente.

Ten en cuenta que solo obtuvimos una fila de la función. Esto se debe a que no usamos SETOF. Eso se describe en la siguiente sección.

36.5.9. Funciones SQL que devuelven conjuntos #

Cuando una función SQL se declara como que devuelve SETOF sometype, la última consulta de la función se ejecuta hasta completarse, y cada fila que produce se devuelve como un elemento del conjunto de resultados.

Esta característica se utiliza normalmente cuando se llama a la función en la cláusula FROM. En este caso, cada fila devuelta por la función se convierte en una fila de la tabla vista por la consulta. Por ejemplo, supón que la tabla foo tiene el mismo contenido que arriba, y decimos:

CREATE FUNCTION getfoo(int) RETURNS SETOF foo AS $$
    SELECT * FROM foo WHERE fooid = $1;
$$ LANGUAGE SQL;

SELECT * FROM getfoo(1) AS t1;

Entonces obtendríamos:

 fooid | foosubid | fooname
-------+----------+---------
     1 |        1 | Joe
     1 |        2 | Ed
(2 rows)

También es posible devolver múltiples filas con las columnas definidas por parámetros de salida, como esto:

CREATE TABLE tab (y int, z int);
INSERT INTO tab VALUES (1, 2), (3, 4), (5, 6), (7, 8);

CREATE FUNCTION sum_n_product_with_tab (x int, OUT sum int, OUT product int)
RETURNS SETOF record
AS $$
    SELECT $1 + tab.y, $1 * tab.y FROM tab;
$$ LANGUAGE SQL;

SELECT * FROM sum_n_product_with_tab(10);
 sum | product
-----+---------
  11 |      10
  13 |      30
  15 |      50
  17 |      70
(4 rows)

Con frecuencia es útil construir el resultado de una consulta invocando una función que devuelve un conjunto múltiples veces, con los parámetros para cada invocación provenientes de filas sucesivas de una tabla o subconsulta. La forma preferida de hacer esto es usar la palabra clave LATERAL, que se describe en la Section 7.2.1.5. Aquí hay un ejemplo que usa una función que devuelve un conjunto para enumerar elementos de una estructura de árbol:

SELECT * FROM nodes;
   name    | parent
-----------+--------
 Top       |
 Child1    | Top
 Child2    | Top
 Child3    | Top
 SubChild1 | Child1
 SubChild2 | Child1
(6 rows)

CREATE FUNCTION listchildren(text) RETURNS SETOF text AS $$
    SELECT name FROM nodes WHERE parent = $1
$$ LANGUAGE SQL STABLE;

SELECT * FROM listchildren('Top');
 listchildren
--------------
 Child1
 Child2
 Child3
(3 rows)

SELECT name, child FROM nodes, LATERAL listchildren(name) AS child;
  name  |   child
--------+-----------
 Top    | Child1
 Top    | Child2
 Top    | Child3
 Child1 | SubChild1
 Child1 | SubChild2
(5 rows)

Este ejemplo no hace nada que no pudiéramos haber hecho con un join simple, pero en cálculos más complejos la opción de poner parte del trabajo en una función puede ser bastante conveniente.

Las funciones que devuelven conjuntos también se pueden llamar en la lista de selección de una consulta. Para cada fila que la consulta genera por sí misma, se invoca la función que devuelve un conjunto, y se genera una fila de salida para cada elemento del conjunto de resultados de la función. El ejemplo anterior también se podría realizar con consultas como estas:

SELECT listchildren('Top');
 listchildren
--------------
 Child1
 Child2
 Child3
(3 rows)

SELECT name, listchildren(name) FROM nodes;
  name  | listchildren
--------+--------------
 Top    | Child1
 Top    | Child2
 Top    | Child3
 Child1 | SubChild1
 Child1 | SubChild2
(5 rows)

En el último SELECT, observa que no aparece ninguna fila de salida para Child2, Child3, etc. Esto sucede porque listchildren devuelve un conjunto vacío para esos argumentos, por lo que no se generan filas de resultado. Este es el mismo comportamiento que obtuvimos de un inner join con el resultado de la función cuando usamos la sintaxis LATERAL.

El comportamiento de PostgreSQL para una función que devuelve un conjunto en la lista de selección de una consulta es casi exactamente el mismo que si la función que devuelve un conjunto se hubiera escrito en un elemento de la cláusula LATERAL FROM en su lugar. Por ejemplo,

SELECT x, generate_series(1,5) AS g FROM tab;

es casi equivalente a

SELECT x, g FROM tab, LATERAL generate_series(1,5) AS g;

Sería exactamente igual, excepto que en este ejemplo específico, el planificador podría optar por colocar g en el lado exterior del join de bucle anidado (nested-loop join), ya que g no tiene una dependencia lateral real de tab. Eso daría como resultado un orden de filas de salida diferente. Las funciones que devuelven conjuntos en la lista de selección siempre se evalúan como si estuvieran en el lado interior de un join de bucle anidado con el resto de la cláusula FROM, de modo que la(s) función(es) se ejecutan hasta completarse antes de que se considere la siguiente fila de la cláusula FROM.

Si hay más de una función que devuelve un conjunto en la lista de selección de la consulta, el comportamiento es similar a lo que se obtiene al colocar las funciones en un único elemento LATERAL ROWS FROM( ... ) de la cláusula FROM. Para cada fila de la consulta subyacente, hay una fila de salida que utiliza el primer resultado de cada función, luego una fila de salida que utiliza el segundo resultado, y así sucesivamente. Si algunas de las funciones que devuelven conjuntos producen menos resultados que otras, se sustituyen con valores nulos los datos faltantes, de modo que el número total de filas emitidas para una fila subyacente es el mismo que para la función que devuelve un conjunto que produjo la mayor cantidad de resultados. Por lo tanto, las funciones que devuelven conjuntos se ejecutan al unísono hasta que se agoten todas, y luego la ejecución continúa con la siguiente fila subyacente.

Las funciones que devuelven conjuntos se pueden anidar en una lista de selección, aunque eso no está permitido en los elementos de la cláusula FROM. En tales casos, cada nivel de anidamiento se trata por separado, como si fuera un elemento LATERAL ROWS FROM( ... ) independiente. Por ejemplo, en

SELECT srf1(srf2(x), srf3(y)), srf4(srf5(z)) FROM tab;

las funciones que devuelven conjuntos srf2, srf3, y srf5 se ejecutarían al unísono para cada fila de tab, y luego srf1 y srf4 se aplicarían al unísono a cada fila producida por las funciones inferiores.

Las funciones que devuelven conjuntos no se pueden utilizar dentro de construcciones de evaluación condicional, como CASE o COALESCE. Por ejemplo, considera

SELECT x, CASE WHEN x > 0 THEN generate_series(1, 5) ELSE 0 END FROM tab;

Podría parecer que esto debería producir cinco repeticiones de las filas de entrada que tienen x > 0, y una sola repetición de las que no; pero en realidad, debido a que generate_series(1, 5) se ejecutaría en un elemento implícito de la cláusula LATERAL FROM antes de que se evalúe la expresión CASE, produciría cinco repeticiones de cada fila de entrada. Para reducir la confusión, estos casos producen un error en tiempo de análisis sintáctico.

Note

Si el último comando de una función es INSERT, UPDATE, DELETE o MERGE con RETURNING, ese comando siempre se ejecutará hasta completarse, incluso si la función no está declarada con SETOF o la consulta que la llama no recupera todas las filas del resultado. Cualquier fila adicional producida por la cláusula RETURNING se descarta silenciosamente, pero las modificaciones de la tabla ordenadas siguen ocurriendo (y se completan todas antes de retornar de la función).

Note

Antes de PostgreSQL 10, poner más de una función que devuelva un conjunto en la misma lista de selección no se comportaba de manera muy sensata a menos que siempre produjeran el mismo número de filas. De lo contrario, lo que se obtenía era un número de filas de salida igual al mínimo común múltiplo del número de filas producidas por las funciones que devuelven conjuntos. Además, las funciones anidadas que devuelven conjuntos no funcionaban como se describió anteriormente; en su lugar, una función que devuelve un conjunto podía tener como máximo un argumento que devolviera un conjunto, y cada anidamiento de funciones que devuelven conjuntos se ejecutaba de forma independiente. Asimismo, la ejecución condicional (funciones que devuelven conjuntos dentro de CASE, etc.) estaba permitida anteriormente, lo que complicaba aún más las cosas. Se recomienda el uso de la sintaxis LATERAL al escribir consultas que necesiten funcionar en versiones anteriores de PostgreSQL, porque eso dará resultados consistentes entre diferentes versiones. Si tienes una consulta que depende de la ejecución condicional de una función que devuelve un conjunto, puedes solucionarlo moviendo la prueba condicional a una función personalizada que devuelva un conjunto. Por ejemplo,

SELECT x, CASE WHEN y > 0 THEN generate_series(1, z) ELSE 5 END FROM tab;

podría convertirse en

CREATE FUNCTION case_generate_series(cond bool, start int, fin int, els int)
  RETURNS SETOF int AS $$
BEGIN
  IF cond THEN
    RETURN QUERY SELECT generate_series(start, fin);
  ELSE
    RETURN QUERY SELECT els;
  END IF;
END$$ LANGUAGE plpgsql;

SELECT x, case_generate_series(y > 0, 1, z, 5) FROM tab;

Esta formulación funcionará igual en todas las versiones de PostgreSQL.

36.5.10. Funciones SQL que devuelven TABLE #

Existe otra forma de declarar que una función devuelve un conjunto, que es utilizar la sintaxis RETURNS TABLE(columns). Esto es equivalente a utilizar uno o más parámetros OUT más marcar la función como que devuelve SETOF record (o SETOF del tipo de un único parámetro de salida, según corresponda). Esta notación se especifica en versiones recientes del estándar SQL, y por lo tanto puede ser más portable que utilizar SETOF.

Por ejemplo, el ejemplo anterior de suma y producto también podría hacerse de esta manera:

CREATE FUNCTION sum_n_product_with_tab (x int)
RETURNS TABLE(sum int, product int) AS $$
    SELECT $1 + tab.y, $1 * tab.y FROM tab;
$$ LANGUAGE SQL;

No está permitido utilizar parámetros explícitos OUT o INOUT con la notación RETURNS TABLE; debes colocar todas las columnas de salida en la lista TABLE.

36.5.11. Funciones SQL polimórficas #

Puedes declarar funciones SQL para que acepten y devuelvan los tipos polimórficos descritos en la Section 36.2.5. Aquí tienes una función polimórfica make_array que construye un array a partir de dos elementos de tipos de datos arbitrarios:

CREATE FUNCTION make_array(anyelement, anyelement) RETURNS anyarray AS $$
    SELECT ARRAY[$1, $2];
$$ LANGUAGE SQL;

SELECT make_array(1, 2) AS intarray, make_array('a'::text, 'b') AS textarray;
 intarray | textarray
----------+-----------
 {1,2}    | {a,b}
(1 row)

Observa el uso de la conversión de tipo 'a'::text para especificar que el argumento es de tipo text. Esto es necesario si el argumento es solo una cadena literal, ya que de lo contrario se trataría como de tipo unknown, y un array de unknown no es un tipo válido. Sin la conversión de tipo, obtendrás errores como este:

ERROR:  could not determine polymorphic type because input has type unknown

Con make_array declarada como arriba, debes proporcionar dos argumentos que sean exactamente del mismo tipo de datos; el sistema no intentará resolver ninguna diferencia de tipo. Así, por ejemplo, esto no funciona:

SELECT make_array(1, 2.5) AS numericarray;
ERROR:  function make_array(integer, numeric) does not exist

Un enfoque alternativo es utilizar la familia common de tipos polimórficos, lo que permite al sistema intentar identificar un tipo común adecuado:

CREATE FUNCTION make_array2(anycompatible, anycompatible)
RETURNS anycompatiblearray AS $$
    SELECT ARRAY[$1, $2];
$$ LANGUAGE SQL;

SELECT make_array2(1, 2.5) AS numericarray;
 numericarray
--------------
 {1,2.5}
(1 row)

Debido a que las reglas para la resolución de tipos comunes eligen por defecto el tipo text cuando todas las entradas son de tipos desconocidos, esto también funciona:

SELECT make_array2('a', 'b') AS textarray;
 textarray
-----------
 {a,b}
(1 row)

Está permitido tener argumentos polimórficos con un tipo de retorno fijo, pero no al revés. Por ejemplo:

CREATE FUNCTION is_greater(anyelement, anyelement) RETURNS boolean AS $$
    SELECT $1 > $2;
$$ LANGUAGE SQL;

SELECT is_greater(1, 2);
 is_greater
------------
 f
(1 row)

CREATE FUNCTION invalid_func() RETURNS anyelement AS $$
    SELECT 1;
$$ LANGUAGE SQL;
ERROR:  cannot determine result data type
DETAIL:  A result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.

Puedes usar el polimorfismo con funciones que tienen argumentos de salida. Por ejemplo:

CREATE FUNCTION dup (f1 anyelement, OUT f2 anyelement, OUT f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE SQL;

SELECT * FROM dup(22);
 f2 |   f3
----+---------
 22 | {22,22}
(1 row)

También puedes usar el polimorfismo con funciones variádicas. Por ejemplo:

CREATE FUNCTION anyleast (VARIADIC anyarray) RETURNS anyelement AS $$
    SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i);
$$ LANGUAGE SQL;

SELECT anyleast(10, -1, 5, 4);
 anyleast
----------
       -1
(1 row)

SELECT anyleast('abc'::text, 'def');
 anyleast
----------
 abc
(1 row)

CREATE FUNCTION concat_values(text, VARIADIC anyarray) RETURNS text AS $$
    SELECT array_to_string($2, $1);
$$ LANGUAGE SQL;

SELECT concat_values('|', 1, 4, 2);
 concat_values
---------------
 1|4|2
(1 row)

36.5.12. Funciones SQL con ordenamientos (collations) #

Cuando una función SQL tiene uno o más parámetros de tipos de datos ordenables (collatable), se identifica un ordenamiento para cada llamada a la función dependiendo de los ordenamientos asignados a los argumentos reales, como se describe en la Section 23.2. Si se identifica con éxito un ordenamiento (es decir, no hay conflictos de ordenamientos implícitos entre los argumentos), entonces todos los parámetros ordenables se tratan como si tuvieran ese ordenamiento implícitamente. Esto afectará el comportamiento de las operaciones sensibles al ordenamiento dentro de la función. Por ejemplo, utilizando la función anyleast descrita anteriormente, el resultado de:

SELECT anyleast('abc'::text, 'ABC');

dependerá del ordenamiento por defecto de la base de datos. En la configuración regional C el resultado será ABC, pero en muchas otras configuraciones regionales será abc. El ordenamiento a utilizar se puede forzar añadiendo una cláusula COLLATE a cualquiera de los argumentos, por ejemplo:

SELECT anyleast('abc'::text, 'ABC' COLLATE "C");

Alternativamente, si deseas que una función funcione con un ordenamiento particular independientemente de con qué se llame, inserta cláusulas COLLATE según sea necesario en la definición de la función. Esta versión de anyleast siempre utilizaría la configuración regional en_US para comparar cadenas:

CREATE FUNCTION anyleast (VARIADIC anyarray) RETURNS anyelement AS $$
    SELECT min($1[i] COLLATE "en_US") FROM generate_subscripts($1, 1) g(i);
$$ LANGUAGE SQL;

Pero ten en cuenta que esto arrojará un error si se aplica a un tipo de datos no ordenable.

Si no se puede identificar un ordenamiento común entre los argumentos reales, entonces una función SQL trata sus parámetros como si tuvieran el ordenamiento por defecto de sus tipos de datos (que suele ser el ordenamiento por defecto de la base de datos, pero podría ser diferente para parámetros de tipos de dominio).

El comportamiento de los parámetros ordenables se puede considerar como una forma limitada de polimorfismo, aplicable únicamente a tipos de datos de texto.