2011-10-02 13 views
32

Say I stworzyli pewne typy zdefiniowane przez użytkownika w DB,Sprawdź, czy typ zdefiniowany przez użytkownika już istnieje w PostgreSQL

tj CREATE TYPE abc ...

Czy jest więc możliwe, aby ustalić, czy typ zdefiniowany przez użytkownika istnieje albo nie? Być może korzystając z którejkolwiek z tablic informacyjnych postgres?

Głównym powodem tego jest fakt, że PostgreSQL nie obsługuje CREATE OR REPLACE TYPE ..., a jeśli dany typ zostanie utworzony więcej niż raz, chcę najpierw usunąć istniejący, a następnie ponownie załadować nowy.

+0

Czy wiesz, że nie można upuścić lub zastąpić typu, jeśli jest on nadal używany przez tabelę? –

+3

Jeśli chcesz uniknąć BŁĘDU w transakcji dla typu, który już utworzyłeś w poprzedniej próbie nieudanej transakcji, zawsze możesz PRZESZUKAĆ TYP JEŚLI ZNAJDUJE się tuż przed instrukcją tworzenia. – Campa

Odpowiedz

12

Możesz zajrzeć w tabeli pg_type:

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

Jeśli to prawda następnie abc istnieje.

1

Próbuję zrobić to samo, upewnić się, że istnieje typ.

zacząłem psql z opcją (-E) --echo-hidden i wszedł \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) 

Jeśli używasz schematów i search_path (jestem), to prawdopodobnie będziesz musiał zachować kontrolę pg_catalog.pg_type_is_visible(t.oid). Nie wiem, jakie są wszystkie warunki w WHERE, ale nie wydawały mi się one odpowiednie w moim przypadku. Obecnie przy użyciu:

SELECT 1 FROM pg_catalog.pg_type as t 
    WHERE typname = 'mytype' AND pg_catalog.pg_type_is_visible(t.oid); 
3
-- 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(); 
----------------------------------- 
49

dodam tutaj kompletne rozwiązanie do tworzenia typów w prostego skryptu, bez konieczności tworzenia funkcję tylko do tego celu.

--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$$; 
+7

To powinna być zaakceptowana odpowiedź. –

+0

To nie jest całkiem poprawne, gdy używamy schematów, ponieważ fakt, że typ istnieje w tabeli pg_type, nie musi oznaczać, że istnieje w dowolnych schematach w bieżącej ścieżce wyszukiwania. ISTM, który musisz wybrać również w przestrzeni nazw, ale nie jestem pewien jak jeszcze. – rog

+0

@rog Zobacz mój [odpowiedź] (https://stackoverflow.com/a/48356064/2024692). – Cromax

1

Rzeczywiście, Postgres nie ma funkcji CREATE OR REPLACE dla typów. Najlepiej jest go usunąć:

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

Proste rozwiązanie jest zawsze najlepsze.

+2

Co się stanie, jeśli ten typ jest aktualnie używany przez tabelę? –

+0

@Shane Następnie 'DROP' wyświetli błąd. Możesz jednak użyć 'UPUŚĆ ... KASKADA 'do upuszczenia również obiektów zależnych - jeśli utrata danych w tym konkretnym przypadku jest akceptowalna. – Cromax

0

Aby rozwiązać dylemat @RRR do odpowiedzi @ blueish, lepiej byłoby użyć typu danych regtype. Rozważ to:

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 klauzula jest jak SELECT, ale odrzuca wyniki, więc w zasadzie jesteśmy sprawdzenie, czy możliwe jest, aby rzucić 'my_schema.my_type' (lub tylko 'my_type' jeśli nie obchodzi się specyficzny schemat) do rzeczywisty zarejestrowany typ. Jeśli typ istnieje, to nic złego się nie stanie i cały blok zostanie zakończony - bez zmian, ponieważ już istnieje typ my_type. Ale jeśli rzutowanie nie jest możliwe, zostanie zgłoszony kod błędu 42704 z etykietą undefined_object. W kolejnych linijkach próbujemy złapać ten błąd i jeśli tak się stanie, po prostu tworzymy nowy typ danych.

0

Najprostszym rozwiązaniem znalazłem tak daleko, że radzi sobie ze schematów, zainspirowany @ odpowiedź CROMAX, znajduje się poniżej:

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

Tylko to, co można się spodziewać naprawdę - po prostu owinąć CREATE TYPE oświadczenie w sposób obsługa wyjątków, aby nie przerwać bieżącej transakcji.

Powiązane problemy