PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

CREATE SCHEMA IF NOT EXISTS genera un errore di chiave duplicata

Questa è un po' una verruca nell'implementazione di IF NOT EXISTS per tabelle e schemi. Fondamentalmente, sono un tentativo di upsert e PostgreSQL non gestisce le condizioni di gara in modo pulito. È sicuro, ma brutto.

Se lo schema viene creato contemporaneamente in un'altra sessione ma non è ancora stato eseguito il commit, allora esiste e non esiste, a seconda di chi sei e del tuo aspetto. Non è possibile che altre transazioni "vedano" il nuovo schema nei cataloghi di sistema perché non è vincolato, quindi è inserito in pg_namespace non è visibile ad altre transazioni. Quindi CREATE SCHEMA / CREATE TABLE cerca di crearlo perché, per quanto lo riguarda, l'oggetto non esiste.

Tuttavia, ciò inserisce una riga in una tabella con un vincolo univoco. I vincoli univoci devono essere in grado di visualizzare le righe non vincolate per funzionare. Quindi l'inserimento si blocca (si interrompe) fino alla prima transazione che ha eseguito il CREATE o esegue il commit o esegue il rollback. Se esegue il commit, la seconda transazione viene interrotta, poiché ha tentato di inserire una riga che viola un vincolo univoco. CREATE SCHEMA non è abbastanza intelligente da prendere questo caso e riprovare.

Per correggere correttamente questo PostgreSQL sarebbe probabilmente necessario il blocco del predicato, in cui potrebbe bloccare il potenziale per una riga . Questo potrebbe essere aggiunto come parte del lavoro in corso per l'implementazione di UPSERT .

Per questi comandi particolari, PostgreSQL potrebbe probabilmente eseguire una lettura sporca dei cataloghi di sistema, dove può vedere le modifiche non vincolate. Quindi potrebbe attendere il commit o il rollback della transazione non vincolata, ripetere la lettura sporca per vedere se qualcun altro è in attesa e riprovare. Ma questo avrebbe una condizione di competizione in cui qualcun altro potrebbe creare lo schema tra quando esegui la lettura per verificarlo e quando provi a crearlo.

Quindi IF NOT EXISTS le varianti dovrebbero:

  • Verifica se lo schema esiste; se lo fa, finisci senza fare nulla.
  • Tentativo di creare la tabella
  • Se la creazione non riesce a causa di un errore di vincolo univoco, riprova dall'inizio
  • Se la creazione della tabella riesce, termina

Per quanto ne so nessuno l'ha implementato, oppure ci hanno provato e non è stato accettato. Ci sarebbero possibili problemi con la velocità di masterizzazione dell'ID transazione e così via con questo approccio.

Penso che questo sia una specie di bug, ma è un tipo di bug "sì, lo sappiamo", non un tipo di bug "faremo in modo di risolverlo". Sentiti libero di postare su pgsql-bugs a riguardo; perlomeno la documentazione dovrebbe menzionare questo avvertimento su IF NOT EXISTS .

Non consiglio di eseguire DDL contemporaneamente in questo modo.