Gdybym miał tabelę z 3 kolumnami - powiedzmy A, B i D - i musiałbym wprowadzić nową - powiedzmy C, aby zastąpić obecną pozycję D. Użyłbym następującej metody:
- Wprowadź 2 nowe kolumny jako C i D2.
- Skopiuj zawartość D do D2.
- Usuń D.
- Zmień nazwę D2 na D.
Nowe zamówienie będzie A, B, C i D.
Myślałem, że jest to uzasadniona praktyka, ponieważ (jak dotąd) nie powodowała żadnych problemów.
Jednak dzisiaj natknąłem się na problem, gdy funkcja wykonująca instrukcję w tej samej tabeli zwróciła następujący błąd:
table row type and query-specified row type do not match
I następujący szczegół:
Query provides a value for a dropped column at ordinal position 13
Próbowałem zrestartować PostgreSQL, VACUUM FULL
a na końcu usunąć i ponownie utworzyć funkcję, jak to sugerowano tutaj i tutaj, ale te rozwiązania nie działały (poza tym, że próbują rozwiązać sytuację, w której zmieniono tabelę systemową).
Mając luksus pracy z bardzo małą bazą danych, wyeksportowałem ją, usunąłem, a następnie ponownie zaimportowałem, co rozwiązało problem z moją funkcją.
Wiedziałem, że nie należy mieszać się z naturalną kolejnością kolumn , modyfikując tabele systemowe (brudząc sobie ręce pg_attribute
itp.), Jak pokazano tutaj:
Czy można zmienić naturalną kolejność kolumn w Postgres?
Sądząc po błędzie rzuconym przez moją funkcję, teraz zdaję sobie sprawę, że zmiana kolejności kolumn za pomocą mojej metody jest również nie-nie. Czy ktoś może świecić światłem, dlaczego to, co robię, jest również złe?
Wersja Postgres to 9.6.0.
Oto funkcja:
CREATE OR REPLACE FUNCTION "public"."__post_users" ("facebookid" text, "useremail" text, "username" text) RETURNS TABLE (authentication_code text, id integer, key text, stripe_id text) AS '
-- First, select the user:
WITH select_user AS
(SELECT
users.id
FROM
users
WHERE
useremail = users.email),
-- Second, update the user (if user exists):
update_user AS
(UPDATE
users
SET
authentication_code = GEN_RANDOM_UUID(),
authentication_date = current_timestamp,
facebook_id = facebookid
WHERE EXISTS (SELECT * FROM select_user)
AND
useremail = users.email
RETURNING
users.authentication_code,
users.id,
users.key,
users.stripe_id),
-- Third, insert the user (if user does not exist):
insert_user AS
(INSERT INTO
users (authentication_code, authentication_date, email, key, name, facebook_id)
SELECT
GEN_RANDOM_UUID(),
current_timestamp,
useremail,
GEN_RANDOM_UUID(),
COALESCE(username, SUBSTRING(useremail FROM ''([^@]+)'')),
facebookid
WHERE NOT EXISTS (SELECT * FROM select_user)
RETURNING
users.authentication_code,
users.id,
users.key,
users.stripe_id)
-- Finally, select the authentication code, ID, key and Stripe ID:
SELECT
*
FROM
update_user
UNION ALL
SELECT
*
FROM
insert_user' LANGUAGE "sql" COST 100 ROWS 1
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
Przeprowadziłem zmianę nazwy / kolejność na obu kolumnach facebook_id
i stripe_id
(przed nimi została dodana nowa kolumna, co jest przyczyną zmiany nazwy, ale zapytanie to nie dotyczy).
Posiadanie kolumn w określonej kolejności nie leży w interesie porządku. Jednak powodem zadawania tego pytania nie jest obawa, że prosta zmiana nazwy i usunięcie kolumny może wywołać prawdziwe problemy dla kogoś, kto używa funkcji w trybie produkcyjnym (jak to się stało).
Odpowiedzi:
Prawdopodobny błąd w 9.6 i 9.6.1
Dla mnie to wygląda jak błąd ...
Nie wiem, dlaczego tak się dzieje, ale mogę potwierdzić, że tak się dzieje. Jest to najprostsza znaleziona konfiguracja, która odtwarza problem (w wersji 9.6.0 i 9.6.1).
Po tej konfiguracji następna instrukcja po prostu działa
W tym momencie KROKUJEMY jedną kolumnę:
Ta zmiana powoduje, że następna instrukcja generuje błąd
który jest taki sam jak wspomniany przez @Andy:
Upuszczenie i ponowne odtworzenie funkcji NIE rozwiązuje problemu.
VACUUM FULL (tabela lub cała baza danych) nie rozwiązuje problemu.
Raport o błędzie został przekazany na odpowiednią listę mailingową PostgreSQL i otrzymaliśmy bardzo szybką odpowiedź :
Wersja 9.6.2
W dniu 2017-03-06 mogę potwierdzić, że nie mogę odtworzyć tego zachowania w wersji 9.6.2. Oznacza to, że błąd został poprawiony w tym wydaniu.
AKTUALIZACJA
Na komentarz @Jana: „Mogę potwierdzić, że błąd jest obecny w wersji 9.6.1 i został naprawiony w wersji 9.6.2. Poprawka jest również wymieniona na stronie internetowej wydania postgres : Poprawka fałszywa„ zapytanie zawiera wartość błędów w usuniętej kolumnie ”podczas INSERT lub UPDATE w tabeli z upuszczoną kolumną „
źródło
Wiem, że pewnie już to słyszałeś, ale to okropny pomysł.
SELECT *
Jeśli więc nie ma znaczenia, że cię to nie zniechęca i przyznamy, że gramy w Photoshopie ze strukturą wierszy i obsesją na punkcie wyświetlania, wyjaśnijmy jeszcze kilka rzeczy.
CREATE TABLE
żadnym z nich (choć byłby to o wiele wyższy priorytet)PostgreSQL jest więc złą warstwą wyświetlania. Poza tym, chociaż
ALTER
działa dobrze, nie powinieneś temperować smoka. ZarównoALTER TABLE
, jak i sekcja CAVEAT wspominają o tym,A jeśli to wszystko nie wystarczy, a ty nadal chcesz udawać, że to dobry pomysł, a raczej okropny pomysł. Następnie spróbuj tego
pg_dump -t
BEGIN
transakcjaDROP
stary stół całkowicie,RENAME
tabela temp do tabeli prod.COMMIT
Jeśli wszystko to wydaje się nadmierne, pamiętaj, że aktualizacja wierszy w bazie danych wymaga przepisania wierszy (zakładając, że nie są one TOAST . Musisz przeanalizować dane i odbudować schemat tabeli, ale tak czy inaczej musisz przepisać wiersz. Gdybym musiał wykonać to zadanie, tak bym to zrobił.
Ale to wszystko mówi ogólnie. Nikt nie odtworzył twoich wyników.
Dałeś przypadek testowy, którego nie możemy uruchomić
I nie podałeś nam dokładnie tej wersji.
źródło
Rozwiązałem ten problem, tworząc kopię zapasową i przywracając bazę danych.
Kroki dla Heroku
heroku maintenance:on
heroku pg:backups:capture
heroku pg:backups:restore
heroku restart
heroku maintenance:off
źródło
Na ten błąd też wpadłem. Dla tych, którzy nie chcą w pełni tworzyć kopii zapasowych / przywracać swoją bazę danych. Wiedz, że po prostu kopiowanie tabeli działa. Nie ma jednak „magicznego” sposobu na skopiowanie tabeli. Zrobiłem to za pomocą:
Po tym nadal będziesz musiał ręcznie ponownie utworzyć indeksy, klucze obce i wartości domyślne. Ponowne odtworzenie tabeli sprawiło, że błąd zniknął.
źródło