web-dev-qa-db-fra.com

Vérifiez si un type défini par l'utilisateur existe déjà dans PostgreSQL

Supposons que j'ai créé certains types définis par l'utilisateur dans la base de données,

c'est à dire. CREATE TYPE abc ...

Est-il alors possible de déterminer si le type défini par l'utilisateur existe ou non? Peut-être, en utilisant l'un des tableaux d'informations postgres?

La raison principale en est que PostgreSQL ne semble pas prendre en charge CREATE OR REPLACE TYPE ..., et si un certain type est créé plus d'une fois, je veux pouvoir supprimer celui existant d'abord, puis recharger le nouveau.

52
Larry

J'ajoute ici la solution complète pour créer des types dans un script simple, sans avoir besoin de créer une fonction juste à cet effet.

--create types
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'my_type') THEN
        CREATE TYPE my_type AS
        (
            --my fields here...
        );
    END IF;
    --more types here...
END$$;
83
bluish

La solution la plus simple que j'ai trouvée jusqu'à présent et qui résiste aux schémas, inspirée de la réponse de @ Cromax, est la suivante:

DO $$ BEGIN
    CREATE TYPE my_type AS (/* fields go here */);
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;

Exactement ce à quoi vous pouvez vous attendre - nous encapsulons simplement l'instruction CREATE TYPE dans un gestionnaire d'exceptions pour qu'elle n'interrompe pas la transaction en cours.

31
rog

Vous pouvez regarder dans le pg_type table:

select exists (select 1 from pg_type where typname = 'abc');

Si c'est vrai, alors abc existe.

18
mu is too short

En effet, Postgres n'a pas CREATE OR REPLACE fonctionnalité pour les types. La meilleure approche consiste donc à le supprimer:

DROP TYPE IF EXISTS YOUR_TYPE;
CREATE TYPE YOUR_TYPE AS (
    id      integer,
    field   varchar
);

Une solution simple est toujours la meilleure.

9
Nulik
-- All of this to create a type if it does not exist
CREATE OR REPLACE FUNCTION create_abc_type() RETURNS integer AS $$
DECLARE v_exists INTEGER;

BEGIN
    SELECT into v_exists (SELECT 1 FROM pg_type WHERE typname = 'abc');
    IF v_exists IS NULL THEN
        CREATE TYPE abc AS ENUM ('height', 'weight', 'distance');
    END IF;
    RETURN v_exists;
END;
$$ LANGUAGE plpgsql;

-- Call the function you just created
SELECT create_abc_type();

-- Remove the function you just created
DROP function create_abc_type();
-----------------------------------
4
Eloy Zuniga Jr.

Pour résoudre le dilemme de @ rog à la réponse de @ bluish, il pourrait être plus approprié d'utiliser le type de données regtype. Considère ceci:

DO $$ BEGIN
    PERFORM 'my_schema.my_type'::regtype;
EXCEPTION
    WHEN undefined_object THEN
        CREATE TYPE my_schema.my_type AS (/* fields go here */);
END $$;

PERFORM clause est comme SELECT, mais elle rejette les résultats, donc en gros nous vérifions s'il est possible de cast 'my_schema.my_type' (ou juste 'my_type' si vous ne vous souciez pas d'être spécifique au schéma) au type enregistré réel. Si le type existe, alors rien de "mauvais" ne se produira et à cause de RETURN tout le bloc se terminera — pas de changement, car le type my_type est déjà là. Mais si le cast n'est pas possible, alors il y aura un code d'erreur jeté 42704 qui porte le libellé undefined_object. Donc, dans les lignes suivantes, nous essayons de détecter cette erreur et si cela se produit, nous créons simplement notre nouveau type de données.

4
Cromax

J'essaie de faire la même chose, de m'assurer qu'un type existe.

J'ai commencé psql avec l'option --echo-hidden (-E) Et entré \dT:

$ psql -E
psql (9.1.9)
testdb=> \dT
********* QUERY **********
SELECT n.nspname as "Schema",
  pg_catalog.format_type(t.oid, NULL) AS "Name",
  pg_catalog.obj_description(t.oid, 'pg_type') as "Description"
FROM pg_catalog.pg_type t
     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
WHERE (t.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid))
  AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)
      AND n.nspname <> 'pg_catalog'
      AND n.nspname <> 'information_schema'
  AND pg_catalog.pg_type_is_visible(t.oid)
ORDER BY 1, 2;
**************************
 List of data types
 Schema |       Name       | Description 
--------+------------------+-------------
 public | errmsg_agg_state | 
(1 row)

Si vous utilisez des schémas et search_path (je le suis), vous devrez probablement conserver la vérification pg_catalog.pg_type_is_visible(t.oid). Je ne sais pas ce que font toutes les conditions du WHERE, mais elles ne semblaient pas pertinentes pour mon cas. Utilisant actuellement:

SELECT 1 FROM pg_catalog.pg_type as t
   WHERE typname = 'mytype' AND pg_catalog.pg_type_is_visible(t.oid);
1
bsb

Une solution plus générique

CREATE OR REPLACE FUNCTION create_type(name text, _type text) RETURNS 
integer AS $$
DECLARE v_exists INTEGER;

BEGIN
    SELECT into v_exists (SELECT 1 FROM pg_type WHERE typname = name);
    IF v_exists IS NULL THEN
            EXECUTE format('CREATE TYPE %I AS %s', name, _type);
    END IF;
    RETURN v_exists;
END;
$$ LANGUAGE plpgsql;

et vous pouvez l'appeler comme ceci:

select create_type('lwm2m_instancetype', 'enum (''single'',''multiple'')');

0
Feki Zied