web-dev-qa-db-fra.com

Concat utilisé dans l'index Causes Erreur: fonctions dans l'expression d'index doit être marquée immuable

Je suis confronté à l'erreur suivante:

ERROR:  functions in index expression must be marked IMMUTABLE

Lorsque vous essayez de créer un index comme celui-ci:

CREATE INDEX full_phone_number ON orders_clientphone (concat(area_code, phone));

D'autre part, lors de l'utilisation de la syntaxe alternative pour la concaténation:

CREATE INDEX full_phone_number ON orders_clientphone ((area_code || phone));

Postgres est presque ok avec ça.
[.____] Les deux colonnes sont définies comme character varying(256).

6
andilabs

Le facteur décisif pour postgrees est que la fonction concat() est définie Stable et non immuable Dans le catalogue système pg_proc:

SELECT proname, provolatile, proargtypes, proargtypes[0]::regtype AS argtype, prosrc
FROM pg_proc
WHERE proname = 'concat';

proname | provolatile | proargtypes| argtype | prosrc
--------+-------------+------------+---------+-----------
concat  | s           | 2276       | "any"   | text_concat

le manuel sur pg_proc.provolatile :

provolatile indique si le résultat de la fonction dépend uniquement de ses arguments d'entrée ou est affecté par des facteurs extérieurs. C'est i pour les fonctions "immuables", qui fournissent toujours le même résultat pour les mêmes entrées. C'est s pour les fonctions "stables", dont les résultats (pour les intrants fixes) ne changent pas dans une analyse.

J'ai également ajouté les types d'arguments de la fonction ("any") pour vous connecter aux réponses de @Dezso et @jjanes, qui fournissent la justification de la décision de rendre cette fonction uniquement stable. Et le nom de la fonction interne (text_concat).

Voici une question connexe pour illustrer pourquoi l'immuabilité des expressions d'index est une condition sinon non condition:

En ce qui concerne l'utilisation de l'opérateur ||:

    SELECT o.oprname, o.oprleft::regtype, o.oprright::regtype, o.oprcode, p.provolatile
    FROM   pg_operator o
    JOIN   pg_proc     p ON p.oid = o.oprcode
    WHERE  oprname = '||';


 oprname |   oprleft   |  oprright   |     oprcode     | provolatile
---------+-------------+-------------+-----------------+-------------
 ||      | anyarray    | anyelement  | array_append    | i
 ||      | anyelement  | anyarray    | array_prepend   | i
 ||      | anyarray    | anyarray    | array_cat       | i
 ||      | text        | text        | textcat         | i
 ||      | bit varying | bit varying | bitcat          | i
 ||      | bytea       | bytea       | byteacat        | i
 ||      | text        | anynonarray | textanycat      | s
 ||      | anynonarray | text        | anytextcat      | s
 ||      | tsvector    | tsvector    | tsvector_concat | i
 ||      | tsquery     | tsquery     | tsquery_or      | i
 ||      | jsonb       | jsonb       | jsonb_concat    | i

La fonction à utiliser en interne dépend de la fonction de données réelle des opérandes. La définition d'un opérateur inclut les types de données des opérandes dans Postgres. Toutes les fonctions sont différentes et différentes de text_concat ci-dessus. || est également stable, lorsque l'un des opérateurs est anynonarray. Les choses ne sont pas si triviales derrière les rideaux.

character varying(256) (comme n'importe quel varchar variante) est Binary-coercable à text SO résolution de type fonction par défaut à text.

6
Erwin Brandstetter

Je crois que Concat, défini dans la documentation comme suit:

FUNCTION                                  RETURN TYPE  DESCRIPTION
-----------------------------------------------------------------------------------
concat(str "any" [, str "any" [, ...] ])  text         Concatenate the text repre-
                                                       sentations of all the arguments. 
                                                       NULL arguments are ignored.

n'est pas immuable car la représentation de texte peut dépendre des paramètres de la base de données, comme celle d'une date ou d'un horodatage .

3
dezso

Pour remplir la réponse de DEZSO, voici un exemple de la même entrée produisant différentes sorties en fonction de l'état de la base de données:

select concat(1.0000000000003::float8,56);

1.000000000000356

set extra_float_digits TO 3;

select concat(1.0000000000003::float8,56);

1.0000000000002999856
3
jjanes