10.3. Funciones #

La función específica a la que hace referencia una llamada a función se determina mediante el siguiente procedimiento.

Resolución de tipos de funciones

  1. Selecciona las funciones a considerar del catálogo del sistema pg_proc. Si se utilizó un nombre de función no calificado por el esquema, las funciones consideradas son aquellas con el nombre y la cantidad de argumentos coincidentes que son visibles en la ruta de búsqueda actual (consulta Section 5.10.3). Si se proporcionó un nombre de función calificado, sólo se consideran las funciones en el esquema especificado.

    1. Si la ruta de búsqueda encuentra múltiples funciones con tipos de argumentos idénticos, sólo se considera la que aparece primero en la ruta. Las funciones de tipos de argumentos diferentes se consideran en pie de igualdad independientemente de su posición en la ruta de búsqueda.

    2. Si una función se declara con un parámetro de matriz VARIADIC, y la llamada no utiliza la palabra clave VARIADIC, entonces la función se trata como si el parámetro de matriz fuera reemplazado por una o más apariciones de su tipo de elemento, según sea necesario para coincidir con la llamada. Después de dicha expansión, la función podría tener tipos de argumentos efectivos idénticos a alguna función no variádica. En ese caso, se utiliza la función que aparece antes en la ruta de búsqueda, o si las dos funciones están en el mismo esquema, se prefiere la no variádica.

      Esto crea un riesgo de seguridad al llamar, a través de un nombre calificado [10], a una función variádica que se encuentre en un esquema que permita a usuarios no confiables crear objetos. Un usuario malicioso puede tomar el control y ejecutar funciones SQL arbitrarias como si las hubieras ejecutado tú. Sustituye por una llamada que contenga la palabra clave VARIADIC, lo que evita este riesgo. Las llamadas que completan parámetros VARIADIC "any" a menudo no tienen una formulación equivalente que contenga la palabra clave VARIADIC. Para realizar esas llamadas de forma segura, el esquema de la función debe permitir que sólo los usuarios de confianza creen objetos.

    3. Las funciones que tienen valores por defecto para los parámetros se consideran coincidentes con cualquier llamada que omita cero o más de las posiciones de parámetros con valor por defecto. Si más de una de estas funciones coincide con una llamada, se utiliza la que aparece antes en la ruta de búsqueda. Si hay dos o más de estas funciones en el mismo esquema con tipos de parámetros idénticos en las posiciones que no tienen valores por defecto (lo cual es posible si tienen diferentes conjuntos de parámetros con valor por defecto), el sistema no podrá determinar cuál preferir, por lo que se producirá un error de llamada a función ambigua si no se puede encontrar una mejor coincidencia para la llamada.

      Esto crea un riesgo de disponibilidad al llamar, a través de un nombre calificado [10], a cualquier función que se encuentre en un esquema que permita a usuarios no confiables crear objetos. Un usuario malicioso puede crear una función con el nombre de una función existente, replicando los parámetros de esa función y agregando nuevos parámetros con valores por defecto. Esto impide nuevas llamadas a la función original. Para prevenir este riesgo, coloca las funciones en esquemas que permitan únicamente a usuarios de confianza crear objetos.

  2. Comprueba si existe una función que acepte exactamente los tipos de argumentos de entrada. Si existe una (sólo puede haber una coincidencia exacta en el conjunto de funciones consideradas), utilízala. La falta de una coincidencia exacta crea un riesgo de seguridad al llamar, a través de un nombre calificado [10], a una función que se encuentre en un esquema que permita a usuarios no confiables crear objetos. En tales situaciones, realiza un cast sobre los argumentos para forzar una coincidencia exacta. (Los casos que involucran unknown nunca encontrarán una coincidencia en este paso).

  3. Si no se encuentra una coincidencia exacta, comprueba si la llamada a la función parece ser una solicitud especial de conversión de tipos. Esto sucede si la llamada a la función tiene un solo argumento y el nombre de la función es el mismo que el nombre (interno) de algún tipo de datos. Además, el argumento de la función debe ser un literal de tipo desconocido, o un tipo que sea coercible binariamente al tipo de datos nombrado, o un tipo que pueda convertirse al tipo de datos nombrado aplicando las funciones de E/S de ese tipo (es decir, la conversión es hacia o desde uno de los tipos de cadena estándar). Cuando estas condiciones se cumplen, la llamada a la función se trata como una forma de especificación CAST. [11]

  4. Busca la mejor coincidencia.

    1. Descarta las funciones candidatas para las cuales los tipos de entrada no coinciden y no se pueden convertir (utilizando una conversión implícita) para coincidir. Se asume que los literales unknown se pueden convertir a cualquier cosa para este propósito. Si sólo queda un candidato, utilízalo; de lo contrario, continúa con el siguiente paso.

    2. Si algún argumento de entrada es de un tipo de dominio, trátalo como si fuera del tipo base del dominio para todos los pasos siguientes. Esto asegura que los dominios actúen como sus tipos base a efectos de resolución de funciones ambiguas.

    3. Recorre todos los candidatos y conserva aquellos con la mayor cantidad de coincidencias exactas en los tipos de entrada. Conserva todos los candidatos si ninguno tiene coincidencias exactas. Si sólo queda un candidato, utilízalo; de lo contrario, continúa con el siguiente paso.

    4. Recorre todos los candidatos y conserva aquellos que acepten tipos preferidos (de la categoría de tipos del tipo de datos de entrada) en la mayor cantidad de posiciones donde se requiera conversión de tipos. Conserva todos los candidatos si ninguno acepta tipos preferidos. Si sólo queda un candidato, utilízalo; de lo contrario, continúa con el siguiente paso.

    5. Si algún argumento de entrada es unknown, comprueba las categorías de tipos aceptadas en esas posiciones de argumentos por los candidatos restantes. En cada posición, selecciona la categoría string si algún candidato acepta esa categoría. (Esta inclinación hacia string es apropiada ya que un literal de tipo desconocido se parece a una cadena). De lo contrario, si todos los candidatos restantes aceptan la misma categoría de tipo, selecciona esa categoría; de lo contrario, falla porque la elección correcta no se puede deducir sin más pistas. Ahora descarta los candidatos que no acepten la categoría de tipo seleccionada. Además, si algún candidato acepta un tipo preferido en esa categoría, descarta los candidatos que acepten tipos no preferidos para ese argumento. Conserva todos los candidatos si ninguno sobrevive a estas pruebas. Si sólo queda un candidato, utilízalo; de lo contrario, continúa con el siguiente paso.

    6. Si hay tanto argumentos unknown como de tipo conocido, y todos los argumentos de tipo conocido tienen el mismo tipo, asume que los argumentos unknown también son de ese tipo, y comprueba qué candidatos pueden aceptar ese tipo en las posiciones de argumentos unknown. Si exactamente un candidato supera esta prueba, utilízalo. De lo contrario, falla.

Ten en cuenta que las reglas de mejor coincidencia son idénticas para la resolución de tipos de operadores y funciones. A continuación se muestran algunos ejemplos.

Example 10.6. Resolución de tipos de argumentos de la función de redondeo

Sólo hay una función round que toma dos argumentos; toma un primer argumento de tipo numeric y un segundo argumento de tipo integer. De modo que la siguiente consulta convierte automáticamente el primer argumento de tipo integer a numeric:

SELECT round(4, 4);

 round
--------
 4.0000
(1 row)

Esa consulta es transformada realmente por el analizador a:

SELECT round(CAST (4 AS numeric), 4);

Dado que a las constantes numéricas con puntos decimales se les asigna inicialmente el tipo numeric, la siguiente consulta no requerirá conversión de tipos y, por lo tanto, podría ser un poco más eficiente:

SELECT round(4.0, 4);


Example 10.7. Resolución de funciones variádicas

CREATE FUNCTION public.variadic_example(VARIADIC numeric[]) RETURNS int
  LANGUAGE sql AS 'SELECT 1';
CREATE FUNCTION

Esta función acepta, pero no requiere, la palabra clave VARIADIC. Tolera argumentos tanto enteros como numéricos:

SELECT public.variadic_example(0),
       public.variadic_example(0.0),
       public.variadic_example(VARIADIC array[0.0]);
 variadic_example | variadic_example | variadic_example
------------------+------------------+------------------
                1 |                1 |                1
(1 row)

Sin embargo, la primera y la segunda llamada preferirán funciones más específicas, si están disponibles:

CREATE FUNCTION public.variadic_example(numeric) RETURNS int
  LANGUAGE sql AS 'SELECT 2';
CREATE FUNCTION

CREATE FUNCTION public.variadic_example(int) RETURNS int
  LANGUAGE sql AS 'SELECT 3';
CREATE FUNCTION

SELECT public.variadic_example(0),
       public.variadic_example(0.0),
       public.variadic_example(VARIADIC array[0.0]);
 variadic_example | variadic_example | variadic_example
------------------+------------------+------------------
                3 |                2 |                1
(1 row)

Dada la configuración por defecto y existiendo únicamente la primera función, la primera y la segunda llamada no son seguras. Cualquier usuario podría interceptarlas creando la segunda o la tercera función. Al hacer coincidir exactamente el tipo de argumento y usar la palabra clave VARIADIC, la tercera llamada es segura.


Example 10.8. Resolución de tipos de la función substring

Hay varias funciones substr, una de las cuales toma los tipos text e integer. Si se llama con una constante de cadena de tipo no especificado, el sistema elige la función candidata que acepta un argumento de la categoría preferida string (a saber, de tipo text).

SELECT substr('1234', 3);

 substr
--------
     34
(1 row)

Si la cadena se declara de tipo varchar, como podría ser el caso si proviene de una tabla, el analizador intentará convertirla para que sea text:

SELECT substr(varchar '1234', 3);

 substr
--------
     34
(1 row)

Esto es transformado por el analizador para convertirse efectivamente en:

SELECT substr(CAST (varchar '1234' AS text), 3);

Note

El analizador aprende del catálogo pg_cast que text y varchar son compatibles binariamente, lo que significa que uno se puede pasar a una función que acepte el otro sin realizar ninguna conversión física. Por lo tanto, en este caso realmente no se inserta ninguna llamada de conversión de tipos.

Y si la función se llama con un argumento de tipo integer, el analizador intentará convertirlo a text:

SELECT substr(1234, 3);
ERROR:  function substr(integer, integer) does not exist
HINT:  No function matches the given name and argument types. You might need
to add explicit type casts.

Esto no funciona porque integer no tiene un cast implícito a text. Sin embargo, un cast explícito sí funcionará:

SELECT substr(CAST (1234 AS text), 3);

 substr
--------
     34
(1 row)




[10] El riesgo no surge con un nombre no calificado por el esquema, porque una ruta de búsqueda que contiene esquemas que permiten a usuarios no confiables crear objetos no es un patrón de uso seguro de esquemas.

[11] La razón de este paso es admitir especificaciones de cast de estilo función en los casos en que no existe una función de cast real. Si existe una función de cast, convencionalmente se nombra según su tipo de salida, por lo que no es necesario tener un caso especial. Consulta CREATE CAST para comentarios adicionales.