Programing

PostgreSQL에 대해 존재하지 않는 경우 CREATE DATABASE를 시뮬레이션 하시겠습니까?

lottogame 2020. 9. 11. 19:26
반응형

PostgreSQL에 대해 존재하지 않는 경우 CREATE DATABASE를 시뮬레이션 하시겠습니까?


JDBC를 통해 존재하지 않는 데이터베이스를 만들고 싶습니다. MySQL과 달리 PostgreSQL은 create if not exists구문을 지원하지 않습니다 . 이를 수행하는 가장 좋은 방법은 무엇입니까?

응용 프로그램은 데이터베이스가 있는지 여부를 알지 못합니다. 데이터베이스가 존재하는지 확인하고 사용해야합니다. 따라서 원하는 데이터베이스에 연결하는 것이 합리적이며 데이터베이스가 존재하지 않아 연결이 실패하면 기본 postgres데이터베이스 에 연결하여 새 데이터베이스를 만들어야 합니다. Postgres에서 반환 한 오류 코드를 확인했지만 동일한 종류의 관련 코드를 찾을 수 없습니다.

이를 달성하는 또 다른 방법은 postgres데이터베이스 에 연결 하고 원하는 데이터베이스가 있는지 확인하고 그에 따라 조치를 취하는 것입니다. 두 번째는 운동하기가 조금 지루합니다.

Postgres에서이 기능을 달성하는 방법이 있습니까?


제한

pg_database동일한 데이터베이스 클러스터의 모든 데이터베이스에서 액세스 할 수 있는 시스템 카탈로그를 요청할 수 있습니다 . 까다로운 부분은 CREATE DATABASE단일 명령문으로 만 실행할 수 있다는 것 입니다. 매뉴얼 :

CREATE DATABASE 트랜잭션 블록 내에서 실행할 수 없습니다.

따라서 DO암시 적으로 트랜잭션 블록 내부에있는 함수 또는 내에서 직접 실행할 수 없습니다 .

(Postgres 11에 도입 된 SQL 프로시 저는 이것에도 도움이되지 않습니다 .)

psql 내에서 해결 방법

조건부로 DDL 문을 실행하여 psql 내에서이 문제를 해결할 수 있습니다.

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

매뉴얼 :

\gexec

현재 쿼리 버퍼를 서버로 보낸 다음 쿼리 출력 (있는 경우)의 각 행의 각 열을 실행할 SQL 문으로 처리합니다.

셸에서 해결 방법

으로 \gexec만 호출 psql의 필요 :

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

연결을 위해 더 많은 psql 옵션이 필요할 수 있습니다. 역할, 포트, 비밀번호, ... 참조 :

psql 메타 명령 psql -c "SELECT ...\gexec"이므로 옵션 은 수동으로 다음과 같은 단일 명령예상 하므로 호출 할 수 없습니다 .\gexec-c

command서버에서 완전히 구문 분석 할 수있는 명령 문자열이거나 (즉, psql 특정 기능이 포함되지 않음) 단일 백 슬래시 명령이어야합니다. 따라서 -c옵션 내에서 SQL 및 psql 메타 명령을 혼합 할 수 없습니다 .

Postgres 트랜잭션 내에서 해결 방법

dblink트랜잭션 블록 외부에서 실행되는 현재 데이터베이스에 대한 연결을 사용할 수 있습니다 . 따라서 효과도 롤백 할 수 없습니다.

이를 위해 추가 모듈 dblink를 설치합니다 (데이터베이스 당 한 번) :

그때:

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에 대한 자세한 설명 :

반복 사용을위한 기능으로 만들 수 있습니다.


another alternative, just in case you want to have a shell script which creates the database if it does not exist and otherwise just keeps it as it is:

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

I found this to be helpful in devops provisioning scripts, which you might want to run multiple times over the same instance.


I had to use a slightly extended version @Erwin Brandstetter used:

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$

I had to enable the dblink extension, plus i had to provide the credentials for dblink. Works with Postgres 9.4.


PostgreSQL does not support IF NOT EXISTS for CREATE DATABASE statement. It is supported only in CREATE SCHEMA. Moreover CREATE DATABASE cannot be issued in transaction therefore it cannot be in DO block with exception catching.

When CREATE SCHEMA IF NOT EXISTS is issued and schema already exists then notice (not error) with duplicate object information is raised.

To solve these problems you need to use dblink extension which opens a new connection to database server and execute query without entering into transaction. You can reuse connection parameters with supplying empty string.

Below is PL/pgSQL code which fully simulates CREATE DATABASE IF NOT EXISTS with same behavior like in CREATE SCHEMA IF NOT EXISTS. It calls CREATE DATABASE via dblink, catch duplicate_database exception (which is issued when database already exists) and converts it into notice with propagating errcode. String message has appended , skipping in the same way how it does 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
$$;

This solution is without any race condition like in other answers, where database can be created by external process (or other instance of same script) between checking if database exists and its own creation.

Moreover when CREATE DATABASE fails with other error than database already exists then this error is propagated as error and not silently discarded. There is only catch for duplicate_database error. So it really behaves as IF NOT EXISTS should.

You can put this code into own function, call it directly or from transaction. Just rollback (restore dropped database) would not work.

Testing output (called two times via DO and then directly):

$ 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

If you don't care about the data, you can drop database first and then recreate it:

DROP DATABASE IF EXISTS dbname;
CREATE DATABASE dbname;

Just create the database using createdb CLI tool:

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

If the database exists, it will return an error:

createdb: database creation failed: ERROR:  database "mydb" already exists

Upgrade to PostgreSQL 9.5 or greater. If (not) exists was introduced in version 9.5.

참고URL : https://stackoverflow.com/questions/18389124/simulate-create-database-if-not-exists-for-postgresql

반응형