Beberapa jawaban menyarankan untuk menggunakan pola: periksa apakah peran tidak ada dan jika tidak maka keluarkan CREATE ROLE
perintah. Ini memiliki satu kelemahan: kondisi balapan. Jika orang lain membuat peran baru antara check dan mengeluarkan CREATE ROLE
perintah, makaCREATE ROLE
jelas gagal dengan kesalahan fatal.
Untuk mengatasi masalah di atas, lebih banyak jawaban lain yang telah disebutkan penggunaan PL/pgSQL
, mengeluarkan CREATE ROLE
tanpa syarat dan kemudian menangkap pengecualian dari panggilan itu. Hanya ada satu masalah dengan solusi ini. Mereka diam-diam menjatuhkan semua kesalahan, termasuk yang tidak dihasilkan oleh fakta bahwa peran sudah ada. CREATE ROLE
dapat membuang juga kesalahan lain dan simulasi IF NOT EXISTS
harus membungkam kesalahan hanya jika peran sudah ada.
CREATE ROLE
melempar duplicate_object
kesalahan saat peran sudah ada. Dan penangan pengecualian harus menangkap hanya satu kesalahan ini. Seperti jawaban lain yang disebutkan, sebaiknya ubah kesalahan fatal menjadi pemberitahuan sederhana. IF NOT EXISTS
Perintah PostgreSQL lainnya menambahkan, skipping
ke dalam pesan mereka, jadi untuk konsistensi saya juga menambahkannya di sini.
Berikut adalah kode SQL lengkap untuk simulasi CREATE ROLE IF NOT EXISTS
dengan pengecualian yang benar dan propagasi sqlstate:
DO $$
BEGIN
CREATE ROLE test;
EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
END
$$;
Output tes (dipanggil dua kali melalui DO dan kemudian secara langsung):
$ sudo -u postgres psql
psql (9.6.12)
Type "help" for help.
postgres=# \set ON_ERROR_STOP on
postgres=# \set VERBOSITY verbose
postgres=#
postgres=# DO $$
postgres$# BEGIN
postgres$# CREATE ROLE test;
postgres$# EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
postgres$# END
postgres$# $$;
DO
postgres=#
postgres=# DO $$
postgres$# BEGIN
postgres$# CREATE ROLE test;
postgres$# EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
postgres$# END
postgres$# $$;
NOTICE: 42710: role "test" already exists, skipping
LOCATION: exec_stmt_raise, pl_exec.c:3165
DO
postgres=#
postgres=# CREATE ROLE test;
ERROR: 42710: role "test" already exists
LOCATION: CreateRole, user.c:337