Ini sedikit kutil dalam implementasi IF NOT EXISTS
untuk tabel dan skema. Pada dasarnya, ini adalah upaya upsert, dan PostgreSQL tidak menangani kondisi balapan dengan bersih. Aman, tapi jelek.
Jika skema sedang dibuat secara bersamaan di sesi lain tetapi belum dilakukan, maka skema itu ada dan tidak ada, tergantung pada siapa Anda dan bagaimana penampilan Anda. Tidak mungkin bagi transaksi lain untuk "melihat" skema baru di katalog sistem karena tidak dikomit, jadi ini masuk di pg_namespace
tidak terlihat oleh transaksi lain. Jadi CREATE SCHEMA
/ CREATE TABLE
mencoba membuatnya karena, sejauh yang bersangkutan, objeknya tidak ada.
Namun, itu menyisipkan baris ke dalam tabel dengan batasan unik. Batasan unik harus dapat melihat baris yang tidak dikomit agar berfungsi. Jadi insert block (berhenti) sampai transaksi pertama yang melakukan CREATE
baik melakukan atau memutar kembali. Jika komit, transaksi kedua dibatalkan, karena mencoba menyisipkan baris yang melanggar batasan unik. CREATE SCHEMA
tidak cukup pintar untuk menangkap kasus ini dan mencoba lagi.
Untuk memperbaiki PostgreSQL ini dengan benar mungkin perlu penguncian predikat, di mana ia dapat mengunci potensi baris . Ini mungkin ditambahkan sebagai bagian dari pekerjaan saat ini yang sedang berlangsung untuk mengimplementasikan UPSERT
.
Untuk perintah khusus ini, PostgreSQL mungkin dapat melakukan bacaan kotor dari katalog sistem, di mana ia dapat melihat perubahan yang tidak dikomit. Kemudian bisa menunggu transaksi yang tidak dikomit untuk melakukan atau memutar kembali, melakukan kembali pembacaan kotor untuk melihat apakah orang lain sedang menunggu, dan coba lagi. Tapi ini akan memiliki kondisi balapan di mana orang lain mungkin membuat skema antara saat Anda membaca untuk memeriksanya dan saat Anda mencoba membuatnya.
Jadi IF NOT EXISTS
varian harus:
- Periksa untuk melihat apakah skema itu ada; jika ya, selesaikan tanpa melakukan apa pun.
- Berusaha membuat tabel
- Jika pembuatan gagal karena kesalahan batasan unik, coba lagi di awal
- Jika pembuatan tabel berhasil, selesaikan
Sejauh yang saya tahu tidak ada yang menerapkan itu, atau mereka mencoba dan itu tidak diterima. Akan ada kemungkinan masalah dengan tingkat pembakaran ID transaksi, dll, dengan pendekatan ini.
Saya pikir ini semacam bug, tetapi ini adalah jenis bug "ya, kami tahu", bukan jenis bug "kami akan memperbaikinya". Jangan ragu untuk memposting ke pgsql-bug tentang hal itu; setidaknya dokumentasi harus menyebutkan peringatan ini tentang IF NOT EXISTS
.
Saya tidak menyarankan melakukan DDL secara bersamaan seperti itu.