it-swarm.com.de

CREATE DATABASE simulieren, falls es PostgreSQL nicht gibt?

Ich möchte eine Datenbank erstellen, die nicht über JDBC existiert. Im Gegensatz zu MySQL unterstützt PostgreSQL die Syntax create if not exists Nicht. Was ist der beste Weg, um dies zu erreichen?

Die Anwendung weiß nicht, ob die Datenbank vorhanden ist oder nicht. Es sollte prüfen, ob die Datenbank vorhanden ist, und sie sollte verwendet werden. Daher ist es sinnvoll, eine Verbindung mit der gewünschten Datenbank herzustellen. Wenn die Verbindung fehlschlägt, weil keine Datenbank vorhanden ist, sollte eine neue Datenbank erstellt werden (durch Herstellen einer Verbindung mit der Standarddatenbank postgres). Ich habe den von Postgres zurückgegebenen Fehlercode überprüft, aber keinen relevanten Code gefunden, der die gleiche Art hat.

Eine andere Methode, um dies zu erreichen, besteht darin, eine Verbindung zur Datenbank postgres herzustellen und zu prüfen, ob die gewünschte Datenbank vorhanden ist, und entsprechende Maßnahmen zu ergreifen. Der zweite ist ein bisschen mühsam zu trainieren.

Gibt es eine Möglichkeit, diese Funktionalität in Postgres zu erreichen?

87

Beschränkungen

Sie können den Systemkatalog pg_database - anfordern, auf den von jeder Datenbank im selben Datenbankcluster zugegriffen werden kann. Der schwierige Teil ist, dass CREATE DATABASE nur als einzelne Anweisung ausgeführt werden kann. Das Handbuch:

CREATE DATABASE Kann nicht innerhalb eines Transaktionsblocks ausgeführt werden.

Daher kann es nicht direkt in einer Funktion oder DO - Anweisung ausgeführt werden, wenn es implizit in einem Transaktionsblock enthalten wäre.

(SQL-Prozeduren, eingeführt mit Postgres 11, kann auch hier nicht helfen .)

Problemumgehung aus psql heraus

Sie können das Problem in psql umgehen, indem Sie die DDL-Anweisung unter folgenden Bedingungen ausführen:

SELECT 'CREATE DATABASE mydb'
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'mydb')\gexec

Das Handbuch:

\gexec

Sendet den aktuellen Abfragepuffer an den Server und behandelt dann jede Spalte jeder Zeile der Ausgabe der Abfrage (falls vorhanden) als auszuführende SQL-Anweisung.

Problemumgehung von der Shell

Mit \gexec Müssen Sie nur psql einmal aufrufen:

echo "SELECT 'CREATE DATABASE mydb' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'mydb')\gexec" | psql

Möglicherweise benötigen Sie weitere psql-Optionen für Ihre Verbindung. Rolle, Port, Passwort, ... Siehe:

Dasselbe kann nicht mit psql -c "SELECT ...\gexec" Aufgerufen werden, da \gexec Ein psql-Metabefehl ist und die Option -c Einen einzelnen Befehl erwartet, für den - im Handbuch heißt es:

command muss entweder eine Befehlszeichenfolge sein, die vom Server vollständig syntaktisch analysiert werden kann (dh es ist enthält keine psql-spezifischen Funktionen) oder einen einzelnen Backslash-Befehl. Daher können Sie in einer -c - Option keine SQL- und psql-Metabefehle mischen.

Problemumgehung innerhalb der Postgres-Transaktion

Sie können eine dblink -Verbindung zur aktuellen Datenbank verwenden, die außerhalb des Transaktionsblocks ausgeführt wird. Effekte können daher auch nicht zurückgesetzt werden.

Installieren Sie dazu das Zusatzmodul dblink (einmal pro Datenbank):

Dann:

DO
$do$
BEGIN
   IF EXISTS (SELECT FROM pg_database WHERE datname = 'mydb') THEN
      RAISE NOTICE 'Database already exists';  -- optional
   ELSE
      PERFORM dblink_exec('dbname=' || current_database()  -- current db
                        , 'CREATE DATABASE mydb');
   END IF;
END
$do$;

Auch hier benötigen Sie möglicherweise mehr psql-Optionen für die Verbindung. Siehe Ortwins zusätzliche Antwort:

Detaillierte Erklärung für dblink:

Sie können dies zu einer Funktion für den wiederholten Gebrauch machen.

70

eine andere Alternative, nur für den Fall, dass Sie ein Shell-Skript haben möchten, das die Datenbank erstellt, wenn sie nicht existiert, und sie ansonsten einfach so beibehält, wie sie ist:

psql -U postgres -tc "SELECT 1 FROM pg_database WHERE datname = 'my_db'" | grep -q 1 || psql -U postgres -c "CREATE DATABASE my_db"

Ich fand dies hilfreich bei der Bereitstellung von Skripten für Entwickler, die möglicherweise mehrmals auf derselben Instanz ausgeführt werden sollen.

107
andreasl

Ich musste eine etwas erweiterte Version von @Erwin Brandstetter verwenden:

DO
$do$
DECLARE
  _db TEXT := 'some_db';
  _user TEXT := 'postgres_user';
  _password TEXT := 'password';
BEGIN
  CREATE EXTENSION IF NOT EXISTS dblink; -- enable extension 
  IF EXISTS (SELECT 1 FROM pg_database WHERE datname = _db) THEN
    RAISE NOTICE 'Database already exists';
  ELSE
    PERFORM dblink_connect('Host=localhost user=' || _user || ' password=' || _password || ' dbname=' || current_database());
    PERFORM dblink_exec('CREATE DATABASE ' || _db);
  END IF;
END
$do$

Ich musste die dblink -Erweiterung aktivieren und die Anmeldeinformationen für dblink bereitstellen. Funktioniert mit Postgres 9.4.

6

PostgreSQL unterstützt IF NOT EXISTS Für die Anweisung CREATE DATABASE Nicht. Es wird nur in CREATE SCHEMA Unterstützt. Außerdem kann CREATE DATABASE Nicht in einer Transaktion ausgegeben werden, daher kann es sich nicht in einem DO -Block mit Ausnahmefang befinden.

Wenn CREATE SCHEMA IF NOT EXISTS Ausgegeben wird und das Schema bereits vorhanden ist, wird ein Hinweis (kein Fehler) mit doppelten Objektinformationen ausgegeben.

Um diese Probleme zu lösen, müssen Sie die Erweiterung dblink verwenden, die eine neue Verbindung zum Datenbankserver herstellt und die Abfrage ausführt, ohne eine Transaktion einzugehen. Sie können Verbindungsparameter wiederverwenden, indem Sie eine leere Zeichenfolge angeben.

Unten sehen Sie den Code PL/pgSQL, Der CREATE DATABASE IF NOT EXISTS Mit demselben Verhalten wie in CREATE SCHEMA IF NOT EXISTS Vollständig simuliert. Es ruft CREATE DATABASE Über dblink auf, fängt die duplicate_database - Ausnahme ab (die ausgegeben wird, wenn die Datenbank bereits existiert) und konvertiert sie in eine Benachrichtigung mit der Weitergabe von errcode. String message hat , skipping Auf die gleiche Weise angehängt wie CREATE SCHEMA IF NOT EXISTS.

CREATE EXTENSION IF NOT EXISTS dblink;

DO $$
BEGIN
PERFORM dblink_exec('', 'CREATE DATABASE testdb');
EXCEPTION WHEN duplicate_database THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
END
$$;

Diese Lösung enthält keine Race-Bedingung wie in anderen Antworten, bei denen die Datenbank durch einen externen Prozess (oder eine andere Instanz desselben Skripts) zwischen der Überprüfung, ob die Datenbank vorhanden ist, und ihrer eigenen Erstellung erstellt werden kann.

Wenn außerdem CREATE DATABASE Mit einem anderen Fehler als der bereits vorhandenen Datenbank fehlschlägt, wird dieser Fehler als Fehler weitergegeben und nicht stillschweigend verworfen. Es gibt nur einen Fang für den Fehler duplicate_database. Es verhält sich also wirklich so, wie es IF NOT EXISTS Sollte.

Sie können diesen Code in eine eigene Funktion setzen, ihn direkt oder aus einer Transaktion aufrufen. Nur ein Rollback (Wiederherstellung der gelöschten Datenbank) würde nicht funktionieren.

Ausgabe testen (zweimal über DO und dann direkt aufgerufen):

$ Sudo -u postgres psql
psql (9.6.12)
Type "help" for help.

postgres=# \set ON_ERROR_STOP on
postgres=# \set VERBOSITY verbose
postgres=# 
postgres=# CREATE EXTENSION IF NOT EXISTS dblink;
CREATE EXTENSION
postgres=# DO $$
postgres$# BEGIN
postgres$# PERFORM dblink_exec('', 'CREATE DATABASE testdb');
postgres$# EXCEPTION WHEN duplicate_database THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
postgres$# END
postgres$# $$;
DO
postgres=# 
postgres=# CREATE EXTENSION IF NOT EXISTS dblink;
NOTICE:  42710: extension "dblink" already exists, skipping
LOCATION:  CreateExtension, extension.c:1539
CREATE EXTENSION
postgres=# DO $$
postgres$# BEGIN
postgres$# PERFORM dblink_exec('', 'CREATE DATABASE testdb');
postgres$# EXCEPTION WHEN duplicate_database THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
postgres$# END
postgres$# $$;
NOTICE:  42P04: database "testdb" already exists, skipping
LOCATION:  exec_stmt_raise, pl_exec.c:3165
DO
postgres=# 
postgres=# CREATE DATABASE testdb;
ERROR:  42P04: database "testdb" already exists
LOCATION:  createdb, dbcommands.c:467
2
Pali

Wenn Sie sich nicht für die Daten interessieren, können Sie die Datenbank zuerst löschen und dann neu erstellen:

DROP DATABASE IF EXISTS dbname;
CREATE DATABASE dbname;
1
Andrey Semakin

Erstellen Sie einfach die Datenbank mit dem CLI-Tool createdb:

PGHOST="my.database.domain.com"
PGUSER="postgres"
PGDB="mydb"
createdb -h $PGHOST -p $PGPORT -U $PGUSER $PGDB

Wenn die Datenbank vorhanden ist, wird ein Fehler zurückgegeben:

createdb: database creation failed: ERROR:  database "mydb" already exists
0
James Wierzba