Documentation PostgreSQL 14.22 » Langage SQL » Conversion de types » Fonctions

Précédent | Niveau supérieur | Suivant
10.2. Opérateurs | Sommaire | 10.4. Stockage de valeurs

## 10.3. Fonctions

La fonction spécifique référencée par un appel de fonction est déterminée selon les étapes suivantes.

Résolution de types pour les fonctions

1. Sélectionner les fonctions à examiner depuis le catalogue système `pg_proc`. Si un nom non qualifié de fonction est utilisé, les fonctions examinées sont celles avec un nom et un nombre d'arguments corrects et qui sont visibles dans le chemin de recherche courant (voir la Section 5.9.3). Si un nom qualifié de fonction a été donné, seules les fonctions dans le schéma spécifique sont examinées.

   1. Si un chemin de recherche trouve de nombreuses fonctions avec des types d'arguments identiques, seule celle apparaissant le plus tôt dans le chemin sera examinée. Mais les fonctions avec des types d'arguments différents sont examinées sur une base d'égalité indépendamment de leur position dans le chemin de recherche.

   2. Si une fonction est déclarée avec un paramètre `VARIADIC` et que l'appel n'utilise pas le mot-clé `VARIADIC`, alors la fonction est traitée comme si le paramètre tableau était remplacé par une ou plusieurs occurrences de son type élémentaire, autant que nécessaire pour correspondre à l'appel. Après cette expansion, la fonction pourrait avoir des types d'arguments identiques à certaines fonctions non variadic. Dans ce cas, la fonction apparaissant plus tôt dans le chemin de recherche est utilisée ou, si les deux fonctions sont dans le même schéma, celle qui n'est pas VARIADIC est préférée.

      Le manque de correspondance exacte crée un risque de sécurité lors de l'appel, via un nom qualifié [10], d'une fonction avec un nombre variable d'arguments trouvée dans un schéma qui permet à des utilisateurs sans confiance de créer des objets. Un utilisateur mal intentionné peut prendre contrôle et exécuter des fonctions SQL arbitraires comme si vous les aviez exécutées. Remplacez un appel utilisant le mot-clé `VARIADIC` qui contourne ce risque. Les appels utilisant des paramètres `VARIADIC "any"` n'ont généralement pas de formulation équivalente contenant le mot-clé `VARIADIC`. Pour réaliser ces appels en toute sécurité, le schéma de la fonction doit permettre de créer des objets uniquement à des utilisateurs de confiance.

   3. Les fonctions qui ont des valeurs par défaut pour les paramètres sont considérées comme correspondant à un appel qui omet zéro ou plusieurs des paramètres ayant des valeurs par défaut. Si plus d'une fonction de ce type correspondent à un appel, celui apparaissant en premier dans le chemin des schémas est utilisé. S'il existe deux ou plus de ces fonctions dans le même schéma avec les mêmes types de paramètres pour les paramètres sans valeur par défaut (ce qui est possible s'ils ont des ensembles différents de paramètres par défaut), le système ne sera pas capable de déterminer laquelle sélectionner, ce dont il résultera une erreur « ambiguous function call ».

      Ceci crée un risque de disponibilité lors de l'appel, via un nom qualifié[10], de toute fonction trouvée dans un schéma permettant à des utilisateurs sans confiance de créer des objets. Un utilisateur mal intentionné peut créer une fonction avec le nom d'une fonction existante, répliquant les paramètres de la fonction et ajouter des nouveaux paramètres avec des valeurs par défaut. Ceci empêche les nouveaux appels à la fonction originale. Pour supprimer ce risque, placez les fonctions dans des schémas permettant uniquement aux utilisateurs de confiance de créer des objets.

2. Vérifier que la fonction accepte le type exact des arguments en entrée. Si une fonction existe (il peut y en avoir uniquement une qui corresponde exactement dans tout l'ensemble des fonctions considérées), utiliser cette fonction. Le manque d'une correspondance exacte crée un risque de sécurité lors de l'appel, via un nom qualifié[10], d'une fonction trouvée dans un schéma permettant à des utilisateurs sans confiance de créer des objets. Dans de telles situations, convertir les arguments pour forcer une correspondance exacte. (Les cas impliquant le type `unknown` ne trouveront jamais de correspondance à cette étape).

3. Si aucune correspondance n'est trouvée, vérifier si l'appel à la fonction apparaît être une requête spéciale de conversion de types. Cela arrive si l'appel à la fonction a juste un argument et si le nom de la fonction est le même que le nom (interne) de certains types de données. De plus, l'argument de la fonction doit être soit un type inconnu soit, un type qui a une compatibilité binaire avec le type de données nommé, soit un type qui peut être converti dans le type de données indiqué en appliquant les fonctions d'entrées/sorties du type (c'est-à-dire que la conversion est vers ou à partir d'un type standard de chaîne). Quand ces conditions sont rencontrées, l'appel de la fonction est traité sous la forme d'une spécification `CAST`. [11]

4. Regarder pour la meilleure correspondance.

   1. Se débarrasser des fonctions candidates pour lesquelles les types en entrée ne correspondent pas et qui ne peuvent pas être convertis (en utilisant une conversion implicite) pour correspondre. Le type `unknown` est supposé être convertible vers n'importe quoi. Si un seul candidat reste, l'utiliser ; sinon, aller à la prochaine étape.

   2. Si tout argument en entrée est un type domaine, le traiter comme son type de base pour toutes les étapes suivantes. Ceci nous assure que les domaines agissent comme leurs types de base pour la résolution des fonctions ambiguës.

   3. Parcourir tous les candidats et garder ceux avec la correspondance la plus exacte par rapport aux types en entrée. Garder tous les candidats si aucun n'a de correspondance exacte. Si un seul candidat reste, l'utiliser ; sinon, aller à la prochaine étape.

   4. Parcourir tous les candidats et garder ceux qui acceptent les types préférés (de la catégorie des types de données en entrée) aux positions où la conversion de types aurait été requise. Garder tous les candidats si aucun n'accepte les types préférés. Si un seul candidat reste, l'utiliser ; sinon, aller à la prochaine étape.

   5. Si des arguments en entrée sont `unknown`, vérifier les catégories de types acceptées à la position de ces arguments par les candidats restants. À chaque position, sélectionner la catégorie `chaîne de caractères` si un des candidats accepte cette catégorie (cette préférence envers les chaînes de caractères est appropriée depuis que le terme type-inconnu ressemble à une chaîne de caractères). Dans le cas contraire, si tous les candidats restants acceptent la même catégorie de types, sélectionner cette catégorie. Dans le cas contraire, échouer, car le choix correct ne peut pas être déduit sans plus d'indices. Se débarrasser maintenant des candidats qui n'acceptent pas la catégorie sélectionnée. De plus, si des candidats acceptent un type préféré dans cette catégorie, se débarrasser des candidats qui acceptent, pour cet argument, les types qui ne sont pas préférés. Garder tous les candidats si aucun ne survit à ces tests. Si un seul candidat reste, l'utiliser. Sinon, continuer avec l'étape suivante.

   6. S'il y a des arguments à la fois `unknown` et connus, et que tous les arguments de type connu ont le même type, supposer que les arguments `unknown` sont de ce même type, et vérifier les candidats qui acceptent ce type aux positions des arguments de type `unknown`. Si un seul candidat réussit ce test, l'utiliser. Sinon, échec.

Notez que les règles de « correspondance optimale » sont identiques pour la résolution de types concernant les opérateurs et les fonctions. Quelques exemples suivent.

Exemple 10.6. Résolution de types pour les arguments de la fonction arrondi

Il n'existe qu'une seule fonction `round` avec deux arguments (le premier est de type `numeric`, le second est de type `integer`). Ainsi, la requête suivante convertit automatiquement le type du premier argument de `integer` vers `numeric`.

    SELECT round(4, 4);

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

La requête est en fait transformée par l'analyseur en

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

Puisque le type `numeric` est initialement assigné aux constantes numériques avec un point décimal, la requête suivante ne requièrera pas une conversion de types et pourra par conséquent être un peu plus efficace :

    SELECT round(4.0, 4);

Exemple 10.7. Résolution de fonction à arguments variables

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

Cette fonction accepte, mais ne requiert pas, le mot-clé VARIADIC. Elle tolère des arguments à la fois entier et numérique :

    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)

Néanmoins, le premier et le deuxième appel préféreront des fonctions plus spécifiques si elles sont 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)

Étant donné la configuration par défaut et si seule la première fonction existe, le premier et le deuxième appel ne sont pas sécurisés. Tout utilisateur peut les intercepter en créant la deuxième et la troisième fonction. En utilisant une correspondance exacte du type d'argument et en utilisant le mot-clé `VARIADIC`, le troisième appel est sécurisé.

Exemple 10.8. Résolution de types pour les fonctions retournant un segment de chaîne

Il existe plusieurs fonctions `substr`, une d'entre elles prend les types `text` et `integer`. Si cette fonction est appelée avec une constante de chaînes d'un type inconnu, le système choisit la fonction candidate qui accepte un argument issu de la catégorie préférée `string` (c'est-à-dire de type `text`).

    SELECT substr('1234', 3);

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

Si la chaîne de caractères est déclarée comme étant du type `varchar` (chaîne de caractères de longueur variable), ce qui peut être le cas si elle vient d'une table, alors l'analyseur essaiera de la convertir en `text` :

    SELECT substr(varchar '1234', 3);

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

Ceci est transformé par l'analyseur en

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

### Note

L'analyseur apprend depuis le catalogue `pg_cast` que les types `text` et `varchar` ont une compatibilité binaire, ce qui veut dire que l'un peut être passé à une fonction qui accepte l'autre sans avoir à faire de conversion physique. Par conséquent, aucun appel de conversion de types n'est réellement inséré dans ce cas.

Et si la fonction est appelée avec un argument de type `integer`, l'analyseur essaie de le convertir en `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.

Ceci ne fonctionne pas, car `integer` n'a pas de conversion implicite vers `text`. Néanmoins, une conversion explicite fonctionnera :

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

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

[10] Le risque ne vient pas d'un nom sans qualification de schéma parce qu'un chemin de recherche contenant des schémas permettant à des utilisateurs sans confiance de créer des objets n'est pas un modèle d'utilisation sécurisée des schémas.

[11] La raison de cette étape est le support des spécifications de conversion au format fonction pour les cas où la vraie fonction de conversion n'existe pas. S'il existe une fonction de conversion, elle est habituellement nommée suivant le nom du type en sortie et donc il n'est pas nécessaire d'avoir un cas spécial. Pour plus d'informations, voir CREATE CAST.