web-dev-qa-db-ja.com

PostgreSQLカスタムオペレーターUUIDからvarcharへ

多くのUUIDフィールドがVARCHARとして正しく格納されていない、かなり複雑なPostgresデータベースがあります。それらを少しずつ移行したいのですが、Postgresにはvarchar = uuidの組み込み演算子がないため、残念ながらすべてのビューが壊れてしまいます。すべてのビューを書き直すか、単一の大規模な移行を試みるのではなく、移行が完了するまで一時的にuuid = varchar演算子を作成したかったのです。

私はこれまでにカスタムオペレーターを作成したことがなく、以下への私の試みはうまくいきません:

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 = =
);

ただし、この演算子はすべてを壊します。単純なvarchar = varchar比較を含めます(以下を参照):

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

誰かが私が間違っていることを私に説明できますか?不可能なことをしようとしていますか?

6
keithhackbarth

あなたがすべきことは CREATE CAST 演算子ではありません。これが問題です:

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.

したがって、CASTを作成する必要があります。これにより、必要に応じてvarcharがuuidに昇格します。 本当にが欲しかったなら、あなたは他の方法で行くことができますが。これを行う場合は、キャスト(uuid ASテキスト)を作成する必要があります。型システムはvarcharを認識していません。PostgreSQLでは使用しません。これは本質的にtextであり、型に関係のない長さの制約があるため、遅くなります。

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

そして今、あなたはもう一度試すことができます。

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

参考のために、

  • IMPLICIT

    キャストは任意のコンテキストで暗黙的に呼び出すことができることを示します。

  • INOUT

    キャストがI/O変換キャストであることを示します。ソースデータ型の出力関数を呼び出し、結果の文字列をターゲットデータ型の入力関数に渡します。

とはいえ、基礎となるタイプが変更された場合、すべてのビューを再作成する必要があります。

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

CREATE VIEW bar AS TABLE foo;

次に、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"

失敗したので、barをドロップしてタイプを変更し、再作成します。

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

そして私たちは喜びを持っています。

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