PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

CREATE SCHEMA IF NOT EXISTS génère une erreur de clé en double

C'est un peu une verrue dans l'implémentation de IF NOT EXISTS pour les tables et les schémas. Fondamentalement, il s'agit d'une tentative d'upsert et PostgreSQL ne gère pas proprement les conditions de concurrence. C'est sûr, mais moche.

Si le schéma est créé simultanément dans une autre session mais n'est pas encore validé, il existe et n'existe pas, selon qui vous êtes et à quoi vous ressemblez. Il n'est pas possible pour d'autres transactions de "voir" le nouveau schéma dans les catalogues système car il n'est pas validé, il s'agit donc d'une entrée dans pg_namespace n'est pas visible pour les autres transactions. Alors CREATE SCHEMA / CREATE TABLE tente de le créer car, pour lui, l'objet n'existe pas.

Cependant, cela insère une ligne dans une table avec une contrainte unique. Les contraintes uniques doivent pouvoir voir les lignes non validées pour fonctionner. Ainsi, l'insertion bloque (s'arrête) jusqu'à la première transaction qui a fait le CREATE soit s'engage, soit annule. Si elle est validée, la deuxième transaction est abandonnée, car elle a tenté d'insérer une ligne qui viole une contrainte d'unicité. CREATE SCHEMA n'est pas assez intelligent pour détecter ce cas et réessayer.

Pour corriger correctement ce problème, PostgreSQL aurait probablement besoin d'un verrouillage de prédicat, où il pourrait verrouiller le potentiel d'une ligne . Cela pourrait être ajouté dans le cadre des travaux en cours pour la mise en œuvre de UPSERT .

Pour ces commandes particulières, PostgreSQL pourrait probablement faire une lecture sale des catalogues système, où il peut voir les modifications non validées. Ensuite, il pourrait attendre que la transaction non validée soit validée ou annulée, refaire la lecture sale pour voir si quelqu'un d'autre attend et réessayer. Mais cela aurait une condition de concurrence où quelqu'un d'autre pourrait créer le schéma entre le moment où vous effectuez la lecture pour le vérifier et le moment où vous essayez de le créer.

Donc le IF NOT EXISTS les variantes devraient :

  • Vérifiez si le schéma existe ; si c'est le cas, terminez sans rien faire.
  • Essayez de créer le tableau
  • Si la création échoue en raison d'une erreur de contrainte unique, réessayez au début
  • Si la création du tableau réussit, terminez

Autant que je sache, personne n'a mis en œuvre cela, ou ils ont essayé et cela n'a pas été accepté. Il y aurait des problèmes possibles avec le taux de gravure de l'ID de transaction, etc., avec cette approche.

Je pense que c'est une sorte de bogue, mais c'est un genre de bogue "ouais, nous savons", pas un genre de bogue "nous allons corriger ce problème". N'hésitez pas à poster sur pgsql-bugs à ce sujet; à tout le moins, la documentation devrait mentionner cette mise en garde à propos de IF NOT EXISTS .

Je ne recommande pas de faire DDL simultanément comme ça.