Jest to trochę kłopotliwe w implementacji IF NOT EXISTS
dla tabel i schematów. Zasadniczo są one próbą upsert, a PostgreSQL nie radzi sobie w czysty sposób z warunkami wyścigu. Jest bezpieczny, ale brzydki.
Jeśli schemat jest współbieżnie tworzony w innej sesji, ale nie został jeszcze zatwierdzony, to zarówno istnieje, jak i nie istnieje, w zależności od tego, kim jesteś i jak wyglądasz. Inne transakcje nie mogą „zobaczyć” nowego schematu w katalogach systemowych, ponieważ jest on niezatwierdzony, więc jest to wpis w pg_namespace
nie jest widoczny dla innych transakcji. Więc CREATE SCHEMA
/ CREATE TABLE
próbuje go stworzyć, ponieważ obiekt nie istnieje.
Jednak wstawia to wiersz do tabeli z ograniczeniem unikatowym. Unikalne ograniczenia muszą być w stanie zobaczyć niezatwierdzone wiersze, aby mogły działać. Tak więc insert blokuje się (zatrzymuje) aż do pierwszej transakcji, która wykonała CREATE
zatwierdza lub wycofuje. Jeśli się zatwierdzi, druga transakcja zostaje przerwana, ponieważ próbowała wstawić wiersz, który narusza ograniczenie unikalności. CREATE SCHEMA
nie jest na tyle sprytny, aby złapać tę sprawę i spróbować ponownie.
Prawidłowe naprawienie tego PostgreSQL prawdopodobnie wymagałoby blokowania predykatów, gdzie może zablokować potencjał wiersza . Może to zostać dodane w ramach bieżących prac nad implementacją UPSERT
.
W przypadku tych konkretnych poleceń PostgreSQL prawdopodobnie wykona brudny odczyt katalogów systemowych, w których może zobaczyć niezatwierdzone zmiany. Następnie może poczekać, aż niezatwierdzona transakcja zostanie zatwierdzona lub wycofana, ponownie wykonać brudny odczyt, aby sprawdzić, czy ktoś inny czeka, i ponowić próbę. Ale to spowodowałoby wyścig, w którym ktoś inny mógłby stworzyć schemat między momentem, w którym wykonujesz odczyt, aby go sprawdzić, a próbą jego utworzenia.
Więc IF NOT EXISTS
warianty musiałyby:
- Sprawdź, czy schemat istnieje; jeśli tak, skończ bez robienia czegokolwiek.
- Spróbuj utworzyć tabelę
- Jeśli tworzenie nie powiedzie się z powodu unikalnego błędu ograniczenia, spróbuj ponownie na początku
- Jeśli tworzenie tabeli się powiedzie, zakończ
O ile wiem, nikt tego nie wdrożył, albo próbowali i nie zostało to zaakceptowane. Przy takim podejściu mogą wystąpić problemy z szybkością nagrywania identyfikatorów transakcji itp.
Myślę, że jest to pewien rodzaj błędu, ale jest to rodzaj błędu typu „tak, wiemy”, a nie typu „będziemy dobrze go naprawić”. Nie krępuj się pisać o tym na pgsql-bugs; przynajmniej dokumentacja powinna wspomnieć o tym zastrzeżeniu dotyczącym IF NOT EXISTS
.
Nie polecam jednoczesnego wykonywania DDL w ten sposób.