Опускання стовпців PostgreSQL 9.6 та побічні ефекти для функцій SQL з CTE


15

Якби у мене була таблиця з 3 стовпцями - скажімо, A, B і D - і мені довелося ввести нову - скажімо C, щоб замінити поточну позицію D. Я б застосував такий метод:

  1. Введіть 2 нові колонки як C і D2.
  2. Скопіюйте вміст від D до D2.
  3. Видалити D.
  4. Перейменуйте D2 на D.

Новий порядок був би A, B, C і D.

Я вважав, що це законна практика, оскільки вона (поки що) не викликала проблем.

Однак сьогодні я зіткнувся з проблемою, коли функція, що виконує операцію за тією ж таблицею, повернула таку помилку:

table row type and query-specified row type do not match

І наступна деталь:

Query provides a value for a dropped column at ordinal position 13

Я спробував перезапустити PostgreSQL, зробив VACUUM FULLі, нарешті, видалив та знову створив функцію, як пропонується тут і тут, але ці рішення не спрацювали (окрім того, що вони намагаються вирішити ситуацію, коли системну таблицю було змінено).

Маючи розкіш працювати з дуже малою базою даних, я експортував її, видалив, а потім повторно імпортував, і це вирішило проблему з моєю функцією.


Мені було відомо про те, що не слід возитися з природним порядком стовпців , змінюючи системні таблиці (забруднюючи руки pg_attributeтощо), як це видно тут:

Чи можливо змінити природний порядок стовпців у Postgres?

Судячи з помилки, виданої моєю функцією, я тепер усвідомлюю, що зміна порядку стовпців моїм методом також є ні-ні. Чи може хтось засвітити світло, чому те, що я роблю, теж не так?


Версія Postgres - 9.6.0.

Ось функція:

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

Я здійснив перейменування / перенастроювання обох стовпців facebook_idі stripe_id(перед цим був доданий новий стовпець, що є причиною перейменування, але цей запит не торкається).

Наявність стовпців у певному порядку є суто цікавим для порядку. Однак, причина задавати це питання викликає занепокоєння тим, що просте перейменування та видалення стовпця може викликати справжні проблеми для тих, хто використовує функції у виробничому режимі (як це трапилось у мене).


Погляньте на те, як я можу змінити положення стовпця в таблиці бази даних PostgreSQL? на Переповнення стека, що може бути корисним, хоча вони в основному пропонують НЕ переробити стовпці.
joanolo

Відповіді:


16

Можлива помилка 9.6 та 9.6.1

Це повністю схоже на помилку для мене ...

Я не знаю, чому це відбувається, але я можу підтвердити, що це відбувається. Це найпростіша налаштована установка, яка відтворює проблему (у версіях 9.6.0 та 9.6.1).

CREATE TABLE users
(
    id SERIAL PRIMARY KEY,
    email TEXT NOT NULL,
    column_that_we_will_drop TEXT
) ;

-- Function that uses the previous table, and that has a CTE
CREATE OR REPLACE FUNCTION __post_users
    (_useremail text) 
RETURNS integer AS
$$
-- Need a CTE to produce the error. A 'constant' one suffices.
WITH something_even_if_useless(a) AS
(
    VALUES (1)
)
UPDATE
    users
SET
    id = id
WHERE 
    -- The CTE needs to be referenced, if the next
    -- condition were not in place, the problem is not reproduced
    EXISTS (SELECT * FROM something_even_if_useless)
    AND email = _useremail
RETURNING
    id
$$
LANGUAGE "sql" ;

Після цього налаштування наступне твердження просто працює

SELECT * FROM __post_users('a@b.com');

На цьому етапі ми скидаємо один стовпець:

ALTER TABLE users 
    DROP COLUMN column_that_we_will_drop ;

Ця зміна робить наступне твердження для створення помилки

SELECT * FROM __post_users('a@b.com');

що таке саме, як згадував @Andy:

ERROR: table row type and query-specified row type do not match
SQL state: 42804
Detail: Query provides a value for a dropped column at ordinal position 3.
Context: SQL function "__post_users" statement 1
    SELECT * FROM __post_users('a@b.com');

Випадання та відтворення функції НЕ вирішує проблему.
ВАКУУМ ПОВНИЙ (таблиця або вся база даних) не вирішує проблему.


Звіт про помилку було передано у відповідний список розсилки PostgreSQL, і ми отримали дуже швидку відповідь :

Я не можу відтворити це в HEAD або 9.6 кінчику гілки. Я вважаю, що це вже було виправлено цим патчем, який увійшов трохи після 9.6.1:

https://git.postgresql.org/gitweb/?p=postgresql.git&a=commitdiff&h=f4d865f22

Але дякую за звіт!

З повагою, Том Лейн


Версія 9.6.2

2017-03-06 я можу підтвердити, що я не можу відтворити цю поведінку у версії 9.6.2. Тобто помилка, здається, була виправлена ​​в цьому випуску.

ОНОВЛЕННЯ

За коментарем @Jana: "Я можу підтвердити, що помилка присутня в 9.6.1 і була виправлена ​​в 9.6.2. Виправлення також вказане на веб-сайті випуску postgres : Виправити помилковий" запит надає значення для помилки, що випав "під час ВСТАВИТИ або ОНОВЛЕННЯ на таблиці зі скинутим стовпцем "



4
Я використовую 9.6.0 і @joanolo правильно, я можу відтворити помилку за його методом. Якщо Том не може його відтворити, напевно, це була ізольована помилка з цією конкретною версією (і я вважаю, 9.6.1?). Як було сказано у відповіді, проблема виникає під час опускання стовпця в таблицю та використання функції з CTE, що впливає на цю таблицю. Таким чином, проблема полягає не лише в переупорядкуванні, а саме в цьому я намагався зіткнутися зі своїм питанням. Я відредагую заголовок, щоб це відобразити.
Енді

Я можу підтвердити, що помилка присутня в 9.6.1 і була виправлена ​​в 9.6.2. Виправлення вказано також на веб-сайті випуску postgres : Виправити помилкові "запити надають значення для випавшого стовпця" помилок під час INSERT або UPDATE на таблиці зі скинутим стовпцем
Jana

0

Я знаю, що ви, напевно, чули це раніше, але це жахлива ідея.

  • Логічне впорядкування впливає лише на такі речі SELECT *
  • Ефект логічного впорядкування обмежується зовнішнім виглядом.

Тож якщо жодне значення не перешкоджає вам, і ми визнаємо, що ми просто граємо у Photoshop із структурою рядків і одержимістю відображення, давайте пояснимо ще кілька речей.

  • Логічного впорядкування в PostgreSQL не існує
  • Фізичне впорядкування має реальні переваги (упаковка столів)
  • PostgreSQL не дає вам ніякого контролю над фізичним впорядкуванням після CREATE TABLE(хоча це було б набагато вищим пріоритетом)

Тож PostgreSQL - це поганий шар відображення. Незважаючи на це, хоч і ALTERпрацює добре, ви не повинні спокушати дракона. І це ALTER TABLE, і розділ CAVEAT згадують про це,

Деякі команди DDL, лише в даний час, TRUNCATEі форми перезапису таблиць ALTER TABLE, не є безпечними для MVCC. Це означає, що після обрізання або перезапису, таблиця виявиться порожньою для одночасних транзакцій, якщо вони використовують знімок, зроблений перед командою DDL. Це буде лише проблемою для транзакції, яка не отримала доступ до відповідної таблиці до початку команди DDL - будь-яка транзакція, яка зробила це, містила б щонайменше блокування таблиці ACCESS SHARE, яке б блокувало команду DDL, поки ця транзакція не завершиться. Таким чином, ці команди не спричинять явної невідповідності вмісту таблиці для послідовних запитів на цільовій таблиці, але вони можуть викликати видиму невідповідність між вмістом цільової таблиці та іншими таблицями бази даних.

І, якщо всього цього недостатньо, і ви все ще хочете зробити вигляд, що це гарна ідея, а не жахлива ідея. Потім спробуйте це,

  1. Звалити стіл pg_dump -t
  2. Впорядкуйте стовпчики вручну на звалищі.
  3. Завантажте матеріал у таблицю TEMP.
  4. BEGIN транзакція
  5. DROP старий стіл повністю,
  6. RENAME таблицю темп до таблиці prod.
  7. COMMIT

Якщо все це звучить надмірно, майте на увазі, що для оновлення рядків у базі даних потрібно перезаписати рядки (якщо припустити, що вони НЕ ТОСТИ . Вам потрібно буде проаналізувати дані та відновити схему таблиці, але в будь-якому випадку вам доведеться переписати рядок. Якби мені довелося виконати це завдання, то я б це зробив.

Але все це говорить загалом. Ніхто не відтворив ваші результати.

Ви дали тестовий випадок, який ми не можемо запустити

ERROR:  column users.authentication_code does not exist
LINE 24: users.authentication_code,

І, ви не сказали нам точну версію, на якій ви працюєте.


Дякую за ретельне пояснення, я відредагував питання, щоб включити конкретну версію.
Енді

4
Помилка відтворюється у версії 9.6.0
ypercubeᵀᴹ

0

Я усунув цю помилку, створивши резервну копію та відновивши свою базу даних.

Кроки до Хероку

  • Увімкніть режим технічного обслуговування: heroku maintenance:on
  • Резервне копіювання бази даних: heroku pg:backups:capture
  • Відновлення бази даних: heroku pg:backups:restore
  • Перезапустити додаток: heroku restart
  • Вимкніть режим технічного обслуговування: heroku maintenance:off

0

Я також наткнувся на цю помилку. Для тих, хто не хоче повністю створити резервну копію / відновити свою БД. Знайте, що просто копіювання таблиці працює. Однак немає "магічного" способу копіювання таблиці. Я зробив це за допомогою:

SELECT * INTO mytable_copy FROM mytable;
ALTER TABLE mytable RENAME TO mytable_backup; -- just in case. you never know
ALTER TABLE mytable_copy RENAME TO mytable;

Після цього вам потрібно буде вручну відтворити свої індекси, сторонні ключі та параметри за замовчуванням. Відтворення такої таблиці змусило помилку зникнути.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.