JDBCを介して存在しないデータベースを作成したい。 MySQLとは異なり、PostgreSQLはcreate if not exists
構文をサポートしていません。これを達成する最良の方法は何ですか?
アプリケーションは、データベースが存在するかどうかを知りません。これを確認し、データベースが存在する場合は使用する必要があります。したがって、目的のデータベースに接続することは理にかなっており、データベースが存在しないために接続が失敗した場合は、新しいデータベースを作成する必要があります(デフォルトのpostgres
データベースに接続することにより)。 Postgresから返されたエラーコードを確認しましたが、同じ種類の関連コードは見つかりませんでした。
これを実現する別の方法は、postgres
データベースに接続し、目的のデータベースが存在するかどうかを確認し、それに応じてアクションを実行することです。 2つ目は、作業するのが少し面倒です。
Postgresでこの機能を実現する方法はありますか?
システムカタログを要求できます pg_database
-同じデータベースクラスター内の任意のデータベースからアクセスできます。トリッキーな部分は、 CREATE DATABASE
が単一のステートメントとしてのみ実行できることです。 マニュアル:
CREATE DATABASE
はトランザクションブロック内で実行できません。
したがって、関数または DO
ステートメント内で直接実行することはできません。この場合、暗黙的にトランザクションブロック内になります。
(Postgres 11で導入されたSQLプロシージャ これも助けにはなりません 。)
DDLステートメントを条件付きで実行することにより、psql内から回避できます。
SELECT 'CREATE DATABASE mydb'
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'mydb')\gexec
\gexec
現在のクエリバッファーをサーバーに送信し、クエリの出力の各行の各列(存在する場合)を実行するSQLステートメントとして扱います。
\gexec
を使用すると、psql onceを呼び出すだけで済みます。
echo "SELECT 'CREATE DATABASE mydb' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'mydb')\gexec" | psql
接続にはさらに多くのpsqlオプションが必要になる場合があります。役割、ポート、パスワード、...
psql -c "SELECT ...\gexec"
はpsqlメタコマンドであり、\gexec
オプションは単一のcommandを必要とするため、-c
で同じものを呼び出すことはできません マニュアルの状態:
command
は、サーバーによって完全に解析可能なコマンド文字列である必要があります(つまり、psql固有の機能は含まれていません) 、または単一のバックスラッシュコマンド。したがって、-c
オプション内でSQLとpsqlのメタコマンドを混在させることはできません。
現在のデータベースへのdblink
接続を使用して、トランザクションブロックの外部で実行できます。したがって、エフェクトをロールバックすることもできません。
このために追加のモジュールdblinkをインストールします(データベースごとに1回):
次に:
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$;
繰り返しますが、接続にはより多くのpsqlオプションが必要になる場合があります。 Ortwinの追加の回答を参照してください。
Dblinkの詳細な説明:
これを繰り返し使用するための関数にすることができます。
別の選択肢として、データベースが存在しない場合はデータベースを作成し、それ以外の場合はそのまま保持するシェルスクリプトが必要な場合に備えて、次のようにします。
psql -U postgres -tc "SELECT 1 FROM pg_database WHERE datname = 'my_db'" | grep -q 1 || psql -U postgres -c "CREATE DATABASE my_db"
これは、devopsプロビジョニングスクリプトで役立つことがわかりました。同じインスタンスで複数回実行することをお勧めします。
@Erwin Brandstetterが使用したわずかに拡張されたバージョンを使用する必要がありました。
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$
dblink
拡張機能を有効にする必要があり、さらにdblinkの資格情報を提供する必要がありました。 Postgres 9.4で動作します。
PostgreSQLは、IF NOT EXISTS
ステートメントに対してCREATE DATABASE
をサポートしていません。 CREATE SCHEMA
でのみサポートされています。さらに、CREATE DATABASE
はトランザクションで発行できないため、例外をキャッチしてDO
ブロックに入れることはできません。
CREATE SCHEMA IF NOT EXISTS
が発行され、スキーマが既に存在する場合、オブジェクト情報が重複していることに注意してください(エラーではありません)。
これらの問題を解決するには、データベースサーバーへの新しい接続を開き、トランザクションを開始せずにクエリを実行するdblink
拡張機能を使用する必要があります。空の文字列を指定して接続パラメーターを再利用できます。
以下は、PL/pgSQL
と同じ動作でCREATE DATABASE IF NOT EXISTS
を完全にシミュレートするCREATE SCHEMA IF NOT EXISTS
コードです。 dblink
を介してCREATE DATABASE
を呼び出し、duplicate_database
例外(データベースが既に存在するときに発行される)をキャッチし、errcode
を伝播して通知に変換します。文字列メッセージには、, skipping
と同じ方法で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
$$;
このソリューションには、データベースが存在するかどうかを確認してから独自に作成するまでの外部プロセス(または同じスクリプトの他のインスタンス)でデータベースを作成できる他の回答のような競合状態はありません。
さらに、CREATE DATABASE
がデータベースが既に存在する以外のエラーで失敗した場合、このエラーはエラーとして伝播され、静かに破棄されません。 duplicate_database
エラーのキャッチのみがあります。したがって、IF NOT EXISTS
のように動作します。
このコードを独自の関数に入れて、直接またはトランザクションから呼び出すことができます。単にロールバック(削除されたデータベースを復元)は機能しません。
出力のテスト(DOを介して2回呼び出され、その後直接呼び出されます):
$ 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
データを気にしない場合は、最初にデータベースを削除してから再作成できます。
DROP DATABASE IF EXISTS dbname;
CREATE DATABASE dbname;
createdb
CLIツールを使用してデータベースを作成するだけです。
PGHOST="my.database.domain.com"
PGUSER="postgres"
PGDB="mydb"
createdb -h $PGHOST -p $PGPORT -U $PGUSER $PGDB
データベースが存在する場合、エラーを返します。
createdb: database creation failed: ERROR: database "mydb" already exists