これは、IF NOT EXISTS
の実装におけるちょっとした問題です。 テーブルとスキーマの場合。基本的に、これらはアップサートの試みであり、PostgreSQLは競合状態を適切に処理しません。安全ですが、醜いです。
スキーマが別のセッションで同時に作成されているが、まだコミットされていない場合は、自分が誰であるか、どのように見えるかに応じて、スキーマは存在する場合と存在しない場合の両方があります。コミットされていないため、他のトランザクションがシステムカタログ内の新しいスキーマを「見る」ことはできません。そのため、pg_namespace
のエントリになります。 他のトランザクションには表示されません。したがって、CREATE SCHEMA
/ CREATE TABLE
オブジェクトが存在しないため、作成しようとします。
ただし、これにより、一意の制約を使用して行がテーブルに挿入されます。一意の制約が機能するには、コミットされていない行を表示できる必要があります。したがって、挿入は、CREATE
を実行した最初のトランザクションまでブロック(停止)します コミットまたはロールバックします。コミットすると、2番目のトランザクションは、一意の制約に違反する行を挿入しようとしたため、中止されます。 CREATE SCHEMA
このケースを見つけて再試行するほど賢くはありません。
このPostgreSQLを適切に修正するには、述語ロックが必要になる可能性があります。述語ロックでは、行の可能性をロックできます。 。これは、UPSERT
を実装するために進行中の現在の作業の一部として追加される可能性があります 。
これらの特定のコマンドの場合、PostgreSQLはおそらくダーティリードを実行できます。 システムカタログの、コミットされていない変更を確認できます。次に、コミットされていないトランザクションがコミットまたはロールバックするのを待ち、ダーティリードを再実行して、他の誰かが待機しているかどうかを確認し、再試行します。ただし、これには、読み取りを行ってスキーマをチェックしてから作成しようとするまでの間に、他の誰かがスキーマを作成する可能性があるという競合状態が発生します。
したがって、IF NOT EXISTS
バリアントは次のことを行う必要があります:
- スキーマが存在するかどうかを確認します。もしそうなら、何もせずに終了します。
- テーブルの作成を試みます
- 一意の制約エラーが原因で作成が失敗した場合は、最初から再試行してください
- テーブルの作成が成功したら、終了します
私の知る限り、誰もそれを実装していないか、彼らが試みて受け入れられませんでした。このアプローチでは、トランザクションIDの書き込み速度などに問題が発生する可能性があります。
これはある種のバグだと思いますが、これは「ええ、私たちは知っている」種類のバグであり、「その修正を正しく行う」種類のバグではありません。 pgsqlに気軽に投稿してください-それについてのバグ。少なくとも、ドキュメントにはIF NOT EXISTS
に関するこの警告を記載する必要があります 。
そのように同時にDDLを実行することはお勧めしません。