Überprüfen Sie, ob in PostgreSQL bereits ein benutzerdefinierter Typ vorhanden ist

75

Angenommen, ich habe einige benutzerdefinierte Typen in der Datenbank erstellt.

dh CREATE TYPE abc ...

Kann dann festgestellt werden, ob der benutzerdefinierte Typ vorhanden ist oder nicht? Vielleicht mit einer der Postgres-Informationstabellen?

Der Hauptgrund dafür ist, dass PostgreSQL nicht zu unterstützen scheint. CREATE OR REPLACE TYPE ...Wenn ein bestimmter Typ mehr als einmal erstellt wird, möchte ich in der Lage sein, den vorhandenen zuerst zu löschen und dann den neuen neu zu laden.

Larry
quelle
Sie wissen, dass Sie einen Typ nicht löschen oder ersetzen können, wenn er noch von einer Tabelle verwendet wird?
AH
4
Wenn Sie ERROR in einer Transaktion für einen Typ vermeiden möchten, den Sie bereits bei einem früheren Versuch einer fehlgeschlagenen Transaktion erstellt haben, können Sie den Typ immer fallen lassen, wenn er unmittelbar vor der Erstellungsanweisung vorhanden ist.
Campa

Antworten:

101

Ich füge hier die vollständige Lösung zum Erstellen von Typen in einem einfachen Skript hinzu, ohne dass eine Funktion nur für diesen Zweck erstellt werden muss.

--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$$;
bläulich
quelle
4
Dies ist bei der Verwendung von Schemas nicht ganz richtig, da die Tatsache, dass ein Typ in der Tabelle pg_type vorhanden ist, nicht unbedingt bedeutet, dass er in einem Schema im aktuellen Suchpfad vorhanden ist. ISTM, das Sie auch im Typnamespace auswählen müssen, aber ich bin mir noch nicht sicher, wie.
Rog
@Cromax Siehe meine etwas einfachere Version Ihrer Antwort.
Rog
@rog Scheint plausibel. 8) Und es ist nicht abhängig von den Funktionen von PSQL (wie regtype), also scheint es tatsächlich eine bessere Lösung zu sein. 8)
Cromax
Warum denken so viele, dass dies die einzige und beste Antwort ist? Wenn ich mit ihr antworte und einen Erstellungs-Typ in einer Datenbank mit einer Version des Typs ausführe, hat sich mit dieser SQL mein alter Typ nie geändert. Aus der Frage geht hervor, dass der Typ, den wir brauchen, in der Datenbank vorhanden sein kann oder nicht.
n3ko
Dieser hat bei mir nicht funktioniert, also habe ich mich für EXCEPTION entschieden.
iElectric
60

Die einfachste Lösung, die ich bisher gefunden habe, um mit Schemata fertig zu werden, die von der Antwort von @ Cromax inspiriert wurden, ist folgende:

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

Genau das, was Sie wirklich erwarten könnten - wir verpacken die Anweisung CREATE TYPE einfach in einen Ausnahmebehandler, damit die aktuelle Transaktion nicht abgebrochen wird.

Rog
quelle
Dies ist das einzige Beispiel, das für mich funktioniert hat. Vielen Dank!
iElectric
Ich bin völlig neu in dieser Sprache und möchte etwas Ähnliches tun, aber einen vorhandenen Typ nicht überladen. Ich möchte also überprüfen, ob ein Typ vorhanden ist, und wenn nicht, erstellen Sie ihn. Ich bin mir nicht sicher, was das EXCEPTIONhier bedeutet. Ist der Typ dann schon überladen oder kann ich ihn so verwenden wie er ist?
Kajsa
25

Sie können in der pg_typeTabelle schauen :

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

Wenn das stimmt, dann abcexistiert.

mu ist zu kurz
quelle
6
Für Leute, die von Google zu dieser Lösung kommen, gibt es eine kleine Einschränkung bei diesem Ansatz. Wenn Sie einen Typ in einem Schema mit dem Namen "my_schema" erstellt haben, gibt die Bedingung auch dann true zurück, wenn Sie ein anderes Schema einchecken. Die vollständige Abfrage wäre select exists (select 1 from pg_type where typname = 'abc' and typnamespace = (select oid from pg_namespace where nspname = 'my_schema')). Ersetzen Sie 'my_schema' durch 'public', wenn Sie eine Einzelschemakonfiguration verwenden.
Rohan Prabhu
18

In der Tat hat Postgres keine CREATE OR REPLACEFunktionalität für Typen. Der beste Ansatz ist es also, es fallen zu lassen:

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

Einfache Lösung ist immer die beste.

Nulik
quelle
14
Was ist, wenn dieser Typ derzeit von einer Tabelle verwendet wird?
Shane Hughes
2
@Shane Dann DROPwird ein Fehler ausgegeben . Sie können jedoch DROP ... CASCADEauch abhängige Objekte löschen, wenn der Datenverlust in diesem speziellen Fall akzeptabel ist.
Cromax
8

Um das Dilemma von @ rog auf die Antwort von @ bluish zu lösen, könnte es angemessener sein, den regtypeDatentyp zu verwenden. Bedenken Sie:

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

PERFORMDie Klausel ist wie SELECT, aber sie verwirft die Ergebnisse. Im Grunde prüfen wir also, ob es möglich ist , den tatsächlich registrierten Typ umzuwandeln 'my_schema.my_type'(oder nur, 'my_type'wenn Sie nicht schemaspezifisch sein möchten). Wenn der Typ vorhanden ist, passiert nichts "Falsches" und RETURNendet aufgrund des gesamten Blocks - keine Änderungen, da der Typ my_typebereits vorhanden ist. Wenn die Besetzung jedoch nicht möglich ist, wird ein Fehlercode mit 42704der Bezeichnung " ausgelöst" ausgegeben undefined_object. In den nächsten Zeilen versuchen wir, diesen Fehler abzufangen. In diesem Fall erstellen wir einfach unseren neuen Datentyp.

Cromax
quelle
Hervorragende Erklärung, danke! Dies scheint hier bei weitem die beste Antwort zu sein; Schade, dass es nicht mehr Aufmerksamkeit bekommen hat.
Wildcard
1
Danke, gern geschehen. Siehe auch die überarbeitete Antwort von @ rog, sie ist kürzer. Ein Nachteil (wie bei meinem POV) ist die langsamere Ausführung, wenn die Überprüfungen mehrmals durchgeführt werden, da die Ausnahme jedes Mal ausgelöst und abgefangen wird, wenn der Typ vorhanden ist (und nach der ersten Ausführung vorhanden ist). Aber es ist vielleicht doch nicht so wichtig. 8)
Cromax
4
-- 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();
-----------------------------------
Eloy Zuniga Jr.
quelle
1

Ich versuche das Gleiche zu tun und sicherzustellen, dass ein Typ existiert.

Ich habe psql mit der Option --echo-hidden( -E) gestartet und Folgendes eingegeben \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)

Wenn Sie Schemas und search_path verwenden (ich bin es), müssen Sie wahrscheinlich die pg_catalog.pg_type_is_visible(t.oid)Prüfung behalten . Ich weiß nicht, was alle Bedingungen im WO tun, aber sie schienen für meinen Fall nicht relevant zu sein. Derzeit verwendet:

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

Eine allgemeinere Lösung

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;

und dann können Sie es so nennen:

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

Feki Zied
quelle
0

Eine andere Alternative

WITH namespace AS(
    SELECT oid 
        FROM pg_namespace 
        WHERE nspname = 'my_schema'
),
type_name AS (
    SELECT 1 type_exist  
        FROM pg_type 
        WHERE typname = 'my_type' AND typnamespace = (SELECT * FROM namespace)
)
SELECT EXISTS (SELECT * FROM type_name);
Panchove
quelle
0

Dies funktioniert gut mit Schemas und vermeidet die Ausnahmebehandlung:

DO $$
BEGIN
    IF NOT EXISTS (
      SELECT 1 FROM pg_type t
      LEFT JOIN pg_namespace p ON t.typnamespace=p.oid
      WHERE t.typname='my_type' AND p.nspname='my_schema'
    ) THEN
        CREATE TYPE my_schema.my_type AS (/* fields go here */);
    END IF;
END
$$;
Julien
quelle