2011-10-02 16 views
32

dire di aver creato alcuni tipi definiti dall'utente nel DB,Controllare se un tipo definito dall'utente esiste già in PostgreSQL

cioè CREATE TYPE abc ...

E 'quindi possibile determinare se il tipo definito dall'utente esiste o no? Forse, usando una qualsiasi delle tabelle di informazioni postgres?

Il motivo principale è che PostgreSQL non sembra supportare CREATE OR REPLACE TYPE ... e se un determinato tipo viene creato più di una volta, voglio essere in grado di eliminare prima quello esistente, quindi ricaricare quello nuovo.

+0

Sapete che non è possibile eliminare o sostituire un tipo se è ancora utilizzato da un tavolo? –

+3

Nel caso in cui si desideri evitare ERRORE in una transazione per un tipo già creato in un precedente tentativo di transazione non riuscita, è sempre possibile DROP TYPE IF EXISTS appena prima dell'istruzione di creazione. – Campa

risposta

12

Potete guardare nella tabella pg_type:

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

Se questo è vero allora abc esiste.

1

Sto provando a fare la stessa cosa, assicurarsi che esista un tipo.

ho iniziato psql con l'opzione --echo-hidden (-E) ed è entrato \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) 

Se si sta utilizzando schemi e search_path (Io sono) allora avrete probabilmente bisogno di mantenere il controllo pg_catalog.pg_type_is_visible(t.oid). Non so quali siano le condizioni del WHERE, ma non sembrano rilevanti per il mio caso. Attualmente utilizzando:

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

aggiungo qui la soluzione completa per la creazione di tipi in un semplice script, senza la necessità di creare una funzione solo per questo scopo.

--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

Questa dovrebbe essere la risposta accettata. –

+0

Questo non è del tutto corretto quando si usano gli schemi, poiché il fatto che un tipo esiste nella tabella pg_type non significa necessariamente che esista in qualsiasi schema nel percorso di ricerca corrente. ISTM che devi selezionare anche su typnamespace, ma non so ancora come. – rog

+0

@rog Vedi la mia [risposta] (https://stackoverflow.com/a/48356064/2024692). – Cromax

1

Infatti, Postgres non ha CREATE OR REPLACE funzionalità per i tipi. Quindi l'approccio migliore è di lasciarlo:

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

La soluzione semplice è sempre la migliore.

+2

Cosa succede se quel tipo è attualmente utilizzato da una tabella? –

+0

@Shane Quindi 'DROP' genererà un errore. Puoi comunque usare 'DROP ... CASCADE' per eliminare anche gli oggetti dipendenti-se la perdita di dati in quel caso particolare è accettabile. – Cromax

0

Per risolvere il dilemma di @ rog alla risposta di @ bluish, potrebbe essere più appropriato utilizzare il tipo di dati regtype. Considerate questo:

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 clausola è come SELECT, ma scarta i risultati, in modo sostanzialmente stiamo controllando se è possibile lanciare 'my_schema.my_type' (o solo 'my_type' se non si cura di essere schema specifico) per effettivo tipo registrato. Se il tipo esiste, allora non accade nulla di "sbagliato" e a causa di RETURN l'intero blocco non termina, poiché il tipo my_type è già presente. Ma se il cast non è possibile, allora verrà generato il codice di errore 42704 che ha l'etichetta di undefined_object. Quindi nelle righe successive cerchiamo di rilevare l'errore e se ciò accade, semplicemente creiamo il nostro nuovo tipo di dati.

0

La soluzione più semplice che ho trovato finora che affronta con schemi, ispirate @ risposta di Cromax, è questa:

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

Proprio quello che ci si potrebbe aspettare in realtà - abbiamo appena avvolgere l'istruzione CREATE TYPE in un gestore di eccezioni in modo che non interrompa la transazione corrente.

Problemi correlati