web-dev-qa-db-fra.com

UUID de l'opérateur personnalisé PostgreSQL vers varchar

J'ai une base de données Postgres assez compliquée dans laquelle de nombreux champs UUID étaient incorrectement stockés en tant que VARCHAR. Je voudrais les migrer au coup par coup, mais malheureusement, cela brise toutes mes vues car Postgres n'a pas d'opérateur intégré pour varchar = uuid. Plutôt réécrire toutes mes vues ou tenter une seule migration massive, je voulais créer temporairement un opérateur uuid = varchar jusqu'à ce que la migration soit terminée.

Je n'ai jamais créé d'opérateur personnalisé auparavant et ma tentative ci-dessous ne fonctionne pas:

CREATE OR REPLACE FUNCTION uuid_equal_varchar (varchar, uuid)
RETURNS boolean AS 'SELECT $1::text = $2::text;' LANGUAGE sql IMMUTABLE;

CREATE OPERATOR = (
    leftarg = character varying,
    rightarg = uuid,
    procedure = uuid_equal_varchar,
    commutator = =
);

Cependant, cet opérateur casse tout. Incluant une simple comparaison varchar = varchar (voir ci-dessous):

SELECT * FROM test WHERE pk_test = '123';
ERROR:  invalid input syntax for uuid: "123"

Quelqu'un peut-il m'expliquer ce que je fais mal? Suis-je en train d'essayer quelque chose qui n'est pas possible?

6
keithhackbarth

Ce que vous devez faire est CREATE CAST pas un opérateur. C'est le problème:

SELECT pg_typeof(uuid), uuid = uuid::varchar AS eq
FROM gen_random_uuid() AS t(uuid);
ERROR:  operator does not exist: uuid = character varying
LINE 1: SELECT pg_typeof(uuid), uuid = uuid::varchar FROM gen_random...
                                     ^
HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.

Nous devons donc créer un CAST. Cela favorise le varchar à uuid en cas de besoin. Bien que vous puissiez aller dans l'autre sens si vous vraiment vouliez. Si vous faites cela, vous devez créer la distribution (texte AS uuid). Le système de type ne connaît pas varchar: nous ne l'utilisons pas dans PostgreSQL; c'est essentiellement text avec une contrainte de longueur sans conséquence et donc plus lent.

CREATE CAST (varchar AS uuid)
  WITH INOUT
  AS IMPLICIT;

Et maintenant, vous pouvez réessayer.

 pg_typeof | eq
-----------+----------
 uuid      | t
(1 row)

Pour référence,

  • IMPLICIT

    Indique que le transtypage peut être invoqué implicitement dans n'importe quel contexte.

  • INOUT

    Indique que la conversion est une conversion de conversion d'E/S, effectuée en appelant la fonction de sortie du type de données source et en passant la chaîne résultante à la fonction d'entrée du type de données cible.

Cela dit, toutes les vues doivent être recréées lorsqu'un type sous-jacent change,

CREATE TABLE foo(uuid)
AS
  VALUES (gen_random_uuid()::varchar);

CREATE VIEW bar AS TABLE foo;

Maintenant, nous essayons de changer le type dans foo

ALTER TABLE foo
  ALTER uuid
  SET DATA TYPE uuid;
ERROR:  cannot alter type of a column used by a view or rule
DETAIL:  rule _RETURN on view bar depends on column "uuid"

Cela échoue, donc nous laissons bar changer le type et le recréer,

BEGIN;
  DROP VIEW bar;
  ALTER TABLE foo ALTER uuid SET DATA TYPE uuid;
  CREATE VIEW bar AS TABLE foo;
COMMIT;

Et nous avons de la joie.

 \d bar;
     View "public.bar"
 Column | Type | Modifiers 
--------+------+-----------
 uuid   | uuid | 
8
Evan Carroll