41.3. Declaraciones #

41.3.1. Declaración de parámetros de función
41.3.2. ALIAS
41.3.3. Copiado de tipos
41.3.4. Tipos de fila
41.3.5. Tipos record (registro)
41.3.6. Colación de variables de PL/pgSQL

Todas las variables utilizadas en un bloque deben declararse en la sección de declaraciones del bloque. (Las únicas excepciones son que la variable de bucle de un bucle FOR que itera sobre un rango de valores enteros se declara automáticamente como una variable entera, y de la misma manera la variable de bucle de un bucle FOR que itera sobre el resultado de un cursor se declara automáticamente como una variable de tipo record).

Las variables de PL/pgSQL pueden tener cualquier tipo de datos de SQL, como integer, varchar y char.

Aquí tienes algunos ejemplos de declaraciones de variables:

user_id integer;
quantity numeric(5);
url varchar;
myrow tablename%ROWTYPE;
myfield tablename.columnname%TYPE;
arow RECORD;

La sintaxis general de una declaración de variable es:

name [ CONSTANT ] type [ COLLATE collation_name ] [ NOT NULL ] [ { DEFAULT | := | = } expression ];

La cláusula DEFAULT, si se proporciona, especifica el valor inicial asignado a la variable al ingresar al bloque. Si no se proporciona la cláusula DEFAULT, la variable se inicializa con el valor nulo de SQL. La opción CONSTANT evita que la variable sea modificada después de la inicialización, por lo que su valor permanecerá constante durante la duración del bloque. La opción COLLATE especifica una colación a utilizar para la variable (consulta la Section 41.3.6). Si se especifica NOT NULL, la asignación de un valor nulo provoca un error en tiempo de ejecución. Todas las variables declaradas como NOT NULL deben tener especificado un valor predeterminado no nulo. Se puede usar el signo igual (=) en lugar de := compatible con PL/SQL.

El valor predeterminado de una variable se evalúa y se asigna a la variable cada vez que se ingresa al bloque (not solo una vez por llamada a la función). De este modo, por ejemplo, asignar now() a una variable de tipo timestamp hace que la variable tenga la hora de la llamada a la función actual, no la hora en que la función fue precompilada.

Ejemplos:

quantity integer DEFAULT 32;
url varchar := 'http://mysite.com';
transaction_time CONSTANT timestamp with time zone := now();

Una vez declarada, el valor de una variable se puede utilizar en expresiones de inicialización posteriores en el mismo bloque, por ejemplo:

DECLARE
  x integer := 1;
  y integer := x + 1;

41.3.1. Declaración de parámetros de función #

Los parámetros pasados a las funciones se nombran con los identificadores $1, $2, etc. Opcionalmente, se pueden declarar alias para los nombres de los parámetros $n para mejorar la legibilidad. Luego se puede usar el alias o el identificador numérico para hacer referencia al valor del parámetro.

Hay dos formas de crear un alias. La forma preferida es dar un nombre al parámetro en el comando CREATE FUNCTION, por ejemplo:

CREATE FUNCTION sales_tax(subtotal real) RETURNS real AS $$
BEGIN
    RETURN subtotal * 0.06;
END;
$$ LANGUAGE plpgsql;

La otra forma es declarar explícitamente un alias, utilizando la sintaxis de declaración:

nombre ALIAS FOR $n;

El mismo ejemplo con este estilo se vería así:

CREATE FUNCTION sales_tax(real) RETURNS real AS $$
DECLARE
    subtotal ALIAS FOR $1;
BEGIN
    RETURN subtotal * 0.06;
END;
$$ LANGUAGE plpgsql;

Note

Estos dos ejemplos no son perfectamente equivalentes. En el primer caso, se podría hacer referencia a subtotal como sales_tax.subtotal, pero en el segundo caso no. (Si hubiéramos adjuntado una etiqueta al bloque interno, subtotal podría calificarse con esa etiqueta en su lugar).

Algunos ejemplos más:

CREATE FUNCTION instr(varchar, integer) RETURNS integer AS $$
DECLARE
    v_string ALIAS FOR $1;
    index ALIAS FOR $2;
BEGIN
    -- algunos cálculos usando v_string e index aquí
END;
$$ LANGUAGE plpgsql;


CREATE FUNCTION concat_selected_fields(in_t sometablename) RETURNS text AS $$
BEGIN
    RETURN in_t.f1 || in_t.f3 || in_t.f5 || in_t.f7;
END;
$$ LANGUAGE plpgsql;

Cuando se declara una función de PL/pgSQL con parámetros de salida, a los parámetros de salida se les asignan nombres $n y alias opcionales exactamente de la misma manera que a los parámetros de entrada normales. Un parámetro de salida es efectivamente una variable que comienza como NULL; se le debe asignar un valor durante la ejecución de la función. El valor final del parámetro es lo que se devuelve. Por ejemplo, el ejemplo del impuesto sobre las ventas (sales-tax) también podría hacerse de esta manera:

CREATE FUNCTION sales_tax(subtotal real, OUT tax real) AS $$
BEGIN
    tax := subtotal * 0.06;
END;
$$ LANGUAGE plpgsql;

Ten en cuenta que omitimos RETURNS real — podríamos haberlo incluido, pero sería redundante.

Para llamar a una función con parámetros OUT, omite el o los parámetros de salida en la llamada a la función:

SELECT sales_tax(100.00);

Los parámetros de salida son más útiles cuando se devuelven múltiples valores. Un ejemplo trivial es:

CREATE FUNCTION sum_n_product(x int, y int, OUT sum int, OUT prod int) AS $$
BEGIN
    sum := x + y;
{{ ... }}
 sum | prod
-----+------
   6 |    8

Como se analiza en la Section 36.5.4, esto crea efectivamente un tipo record anónimo para los resultados de la función. Si se proporciona una cláusula RETURNS, esta debe decir RETURNS record.

Esto también funciona con procedimientos, por ejemplo:

CREATE PROCEDURE sum_n_product(x int, y int, OUT sum int, OUT prod int) AS $$
BEGIN
    sum := x + y;
    prod := x * y;
END;
$$ LANGUAGE plpgsql;

En una llamada a un procedimiento, se deben especificar todos los parámetros. Para los parámetros de salida, se puede especificar NULL al llamar al procedimiento desde SQL plano:

CALL sum_n_product(2, 4, NULL, NULL);
 sum | prod
-----+------
   6 |    8

Sin embargo, al llamar a un procedimiento desde PL/pgSQL, deberías en su lugar escribir una variable para cualquier parámetro de salida; la variable recibirá el resultado de la llamada. Consulta la Section 41.6.3 para obtener más detalles.

Otra forma de declarar una función de PL/pgSQL es con RETURNS TABLE, por ejemplo:

CREATE FUNCTION extended_sales(p_itemno int)
RETURNS TABLE(quantity int, total numeric) AS $$
BEGIN
    RETURN QUERY SELECT s.quantity, s.quantity * s.price FROM sales AS s
                 WHERE s.itemno = p_itemno;
END;
$$ LANGUAGE plpgsql;

Esto es exactamente equivalente a declarar uno o más parámetros OUT y especificar RETURNS SETOF sometype.

Cuando el tipo de retorno de una función de PL/pgSQL se declara como un tipo polimórfico (consulta la Section 36.2.5), se crea un parámetro especial $0. Su tipo de datos es el tipo de retorno real de la función, tal como se deduce de los tipos de entrada reales. Esto permite que la función acceda a su tipo de retorno real como se muestra en la Section 41.3.3. $0 se inicializa en nulo y puede ser modificado por la función, por lo que se puede usar para contener el valor de retorno si se desea, aunque no es obligatorio. A $0 también se le puede asignar un alias. Por ejemplo, esta función funciona en cualquier tipo de datos que tenga un operador +:

CREATE FUNCTION add_three_values(v1 anyelement, v2 anyelement, v3 anyelement)
RETURNS anyelement AS $$
DECLARE
    result ALIAS FOR $0;
BEGIN
    result := v1 + v2 + v3;
    RETURN result;
END;
$$ LANGUAGE plpgsql;

El mismo efecto se puede obtener declarando uno o más parámetros de salida como tipos polimórficos. En este caso no se utiliza el parámetro especial $0; los propios parámetros de salida sirven para el mismo propósito. Por ejemplo:

CREATE FUNCTION add_three_values(v1 anyelement, v2 anyelement, v3 anyelement,
                                 OUT sum anyelement)
AS $$
BEGIN
    sum := v1 + v2 + v3;
END;
$$ LANGUAGE plpgsql;

En la práctica, podría ser más útil declarar una función polimórfica utilizando la familia de tipos anycompatible, de modo que ocurra la promoción automática de los argumentos de entrada a un tipo común. Por ejemplo:

CREATE FUNCTION add_three_values(v1 anycompatible, v2 anycompatible, v3 anycompatible)
RETURNS anycompatible AS $$
BEGIN
    RETURN v1 + v2 + v3;
END;
$$ LANGUAGE plpgsql;

Con este ejemplo, una llamada como:

SELECT add_three_values(1, 2, 4.7);

funcionará, promoviendo automáticamente las entradas enteras a numéricas. La función que utiliza anyelement requeriría que conviertas manualmente las tres entradas al mismo tipo.

41.3.2. ALIAS #

nuevonombre ALIAS FOR antiguonombre;

La sintaxis de ALIAS es más general de lo que se sugiere en la sección anterior: puedes declarar un alias para cualquier variable, no solo para los parámetros de la función. El principal uso práctico de esto es asignar un nombre diferente a las variables con nombres predeterminados, como NEW o OLD dentro de una función disparadora (trigger).

Ejemplos:

DECLARE
  prior ALIAS FOR old;
  updated ALIAS FOR new;

Dado que ALIAS crea dos formas diferentes de nombrar el mismo objeto, su uso sin restricciones puede ser confuso. Lo mejor es usarlo solo para el propósito de sobrescribir nombres predeterminados.

41.3.3. Copiado de tipos #

nombre tabla.columna%TYPE
nombre variable%TYPE

%TYPE proporciona el tipo de datos de una columna de tabla o de una variable de PL/pgSQL declarada previamente. Puedes usar esto para declarar variables que contendrán valores de la base de datos. Por ejemplo, supongamos que tienes una columna llamada user_id en tu tabla users. Para declarar una variable con el mismo tipo de datos que users.user_id escribes:

user_id users.user_id%TYPE;

También es posible escribir la decoración de array después de %TYPE, creando así una variable que contiene un array del tipo referenciado:

user_ids users.user_id%TYPE[];
user_ids users.user_id%TYPE ARRAY[4];  -- equivalente a lo anterior

Al igual que al declarar columnas de tabla que son arrays, no importa si escribes múltiples pares de corchetes o dimensiones de array específicas: PostgreSQL trata a todos los arrays de un tipo de elemento dado como el mismo tipo, independientemente de la dimensionalidad. (Consulta la Section 8.15.1).

Al utilizar %TYPE no necesitas conocer el tipo de datos de la estructura que estás referenciando y, lo más importante, si el tipo de datos del elemento referenciado cambia en el futuro (por ejemplo, si cambias el tipo de user_id de integer a real), es posible que no necesites cambiar la definición de tu función.

%TYPE es particularmente valioso en funciones polimórficas, ya que los tipos de datos necesarios para las variables internas pueden cambiar de una llamada a la siguiente. Se pueden crear variables adecuadas aplicando %TYPE a los argumentos o marcadores de posición de resultado de la función.

41.3.4. Tipos de fila #

nombre nombre_tabla%ROWTYPE;
nombre nombre_tipo_compuesto;

Una variable de un tipo compuesto se denomina variable de fila (o variable de tipo de fila). Dicha variable puede contener una fila completa del resultado de una consulta SELECT o FOR, siempre que el conjunto de columnas de esa consulta coincida con el tipo declarado de la variable. Se accede a los campos individuales del valor de la fila utilizando la notación de punto habitual, por ejemplo rowvar.field.

Se puede declarar que una variable de fila tiene el mismo tipo que las filas de una tabla o vista existente, utilizando la notación nombre_tabla%ROWTYPE; o se puede declarar proporcionando el nombre de un tipo compuesto. (Dado que cada tabla tiene un tipo compuesto asociado con el mismo nombre, en realidad no importa en PostgreSQL si escribes %ROWTYPE o no. Pero la forma con %ROWTYPE es más portable).

Al igual que con %TYPE, %ROWTYPE puede ir seguido de la decoración de array para declarar una variable que contiene un array del tipo compuesto referenciado.

Los parámetros de una función pueden ser tipos compuestos (filas completas de tabla). En ese caso, el identificador correspondiente $n será una variable de fila, y se pueden seleccionar campos de ella, por ejemplo $1.user_id.

Aquí tienes un ejemplo del uso de tipos compuestos. table1 y table2 son tablas existentes que tienen al menos los campos mencionados:

CREATE FUNCTION merge_fields(t_row table1) RETURNS text AS $$
DECLARE
    t2_row table2%ROWTYPE;
{{ ... }}

41.3.5. Tipos record (registro) #

nombre RECORD;

Las variables de tipo record son similares a las variables de tipo de fila, pero no tienen una estructura predefinida. Toman la estructura de fila real de la fila que se les asigna durante un comando SELECT o FOR. La subestructura de una variable de tipo record puede cambiar cada vez que se le asigna un valor. Una consecuencia de esto es que hasta que se le asigne un valor por primera vez, no tiene subestructura, y cualquier intento de acceder a un campo en ella generará un error en tiempo de ejecución.

Ten en cuenta que RECORD no es un tipo de datos real, sino solo un marcador de posición. También se debe tener en cuenta que cuando una función de PL/pgSQL se declara para devolver el tipo record, este no es exactamente el mismo concepto que una variable de tipo record, aunque dicha función podría usar una variable de tipo record para contener su resultado. En ambos casos, la estructura de fila real es desconocida cuando se escribe la función, pero para una función que devuelve record, la estructura real se determina cuando se analiza la consulta que la llama, mientras que una variable de tipo record puede cambiar su estructura de fila sobre la marcha.

41.3.6. Colación de variables de PL/pgSQL #

Cuando una función de PL/pgSQL tiene uno o más parámetros de tipos de datos colacionables, se identifica una colación para cada llamada de función según las colaciones asignadas a los argumentos reales, como se describe en la Section 23.2. Si se identifica correctamente una colación (es decir, no hay conflictos de colaciones implícitas entre los argumentos), entonces todos los parámetros colacionables se tratan como si tuvieran esa colación implícitamente. Esto afectará al comportamiento de las operaciones sensibles a la colación dentro de la función. Por ejemplo, considera:

CREATE FUNCTION less_than(a text, b text) RETURNS boolean AS $$
BEGIN
    RETURN a < b;
END;
$$ LANGUAGE plpgsql;

SELECT less_than(text_field_1, text_field_2) FROM table1;
SELECT less_than(text_field_1, text_field_2 COLLATE "C") FROM table1;

El primer uso de less_than utilizará la colación común de text_field_1 y text_field_2 para la comparación, mientras que el segundo utilizará la colación C.

Además, la colación identificada también se asume como la colación de cualquier variable local que sea de tipos colacionables. Por lo tanto, esta función no funcionaría de manera diferente si se escribiera como:

CREATE FUNCTION less_than(a text, b text) RETURNS boolean AS $$
DECLARE
    local_a text := a;
    local_b text := b;
BEGIN
    RETURN local_a < local_b;
END;
$$ LANGUAGE plpgsql;

Si no hay parámetros de tipos de datos colacionables, o no se puede identificar una colación común para ellos, los parámetros y variables locales utilizan la colación predeterminada de su tipo de datos (que suele ser la colación predeterminada de la base de datos, pero podría ser diferente para variables de tipos de dominio).

Una variable local de un tipo de datos colacionable puede tener una colación diferente asociada al incluir la opción COLLATE en su declaración, por ejemplo:

DECLARE
    local_a text COLLATE "en_US";

Esta opción anula la colación que de otro modo se le daría a la variable según las reglas anteriores.

Además, por supuesto, se pueden escribir cláusulas COLLATE explícitas dentro de una función si se desea forzar el uso de una colación particular en una operación específica. Por ejemplo:

CREATE FUNCTION less_than_c(a text, b text) RETURNS boolean AS $$
BEGIN
    RETURN a < b COLLATE "C";
END;
$$ LANGUAGE plpgsql;

Esto anula las colaciones asociadas con las columnas de tabla, parámetros o variables locales utilizadas en la expresión, just as would happen in a plain SQL command.