Isso é um pouco problemático na implementação de
IF NOT EXISTS
para tabelas e esquemas. Basicamente, eles são uma tentativa de upsert, e o PostgreSQL não lida com as condições de corrida de forma limpa. É seguro, mas feio. Se o esquema está sendo criado simultaneamente em outra sessão, mas ainda não foi confirmado, ele existe e não existe, dependendo de quem você é e de sua aparência. Não é possível que outras transações "vejam" o novo esquema nos catálogos do sistema porque ele não está confirmado, então é entrada em
pg_namespace
não é visível para outras transações. Então CREATE SCHEMA
/ CREATE TABLE
tenta criá-lo porque, no que lhe diz respeito, o objeto não existe. No entanto, isso insere uma linha em uma tabela com uma restrição exclusiva. As restrições exclusivas devem poder ver linhas não confirmadas para funcionar. Então o insert bloqueia (para) até a primeira transação que fez o
CREATE
ou confirma ou reverte. Se confirmar, a segunda transação será abortada, porque tentou inserir uma linha que viola uma restrição exclusiva. CREATE SCHEMA
não é inteligente o suficiente para pegar este caso e tentar novamente. Para corrigir isso corretamente, o PostgreSQL provavelmente precisaria de bloqueio de predicado, onde poderia bloquear o potencial para uma linha . Isso pode ser adicionado como parte do trabalho atual em andamento para implementar o
UPSERT
. Para esses comandos em particular, o PostgreSQL provavelmente poderia fazer uma leitura suja dos catálogos do sistema, onde pode ver as alterações não confirmadas. Em seguida, ele pode esperar que a transação não confirmada seja confirmada ou revertida, refaça a leitura suja para ver se alguém está esperando e tente novamente. Mas isso teria uma condição de corrida em que outra pessoa poderia criar o esquema entre quando você faz a leitura para verificar e quando você tenta criá-lo.
Portanto, o
IF NOT EXISTS
variantes teriam que:- Verifique se o esquema existe; se isso acontecer, termine sem fazer nada.
- Tente criar a tabela
- Se a criação falhar devido a um erro de restrição exclusivo, tente novamente no início
- Se a criação da tabela for bem-sucedida, termine
Até onde eu sei, ninguém implementou isso, ou eles tentaram e não foi aceito. Haveria possíveis problemas com a taxa de queima de ID de transação, etc., com essa abordagem.
Eu acho que isso é um tipo de bug, mas é um tipo de bug do tipo "sim, nós sabemos", não um tipo de bug "vamos corrigir isso". Sinta-se à vontade para postar no pgsql-bugs sobre isso; no mínimo a documentação deve mencionar esta advertência sobre
IF NOT EXISTS
. Eu não recomendo fazer DDL simultaneamente assim.