Выберите для обновления запроса возвращает нарушение кардинальности - PullRequest
1 голос
/ 04 июня 2019

Мы выполняем этот запрос в Postgres 9.6.10 в управляемой облачной БД Google:

WITH update AS
  (UPDATE cart SET loyalty = loyalty || jsonb_insert('{}', '{coupon}', loyalty#>'{scan_coupon}' || $1) WHERE id = 
  (SELECT id FROM cart WHERE id = $2 AND status = $3 and item_version = $4 FOR UPDATE) returning *)
SELECT * FROM updated

cart - это таблица, в которой id является первичным ключом. loyalty - это столбец jsonb, а item_version - функция, которая увеличивает некоторые операции, но ожидается, что до обновления item_version произойдет несколько обновлений. status является перечисляемым типом.

При очень параллельных обновлениях мы редко получаем следующую ошибку:

Cardinality_violation, file: "nodeSubplan.c", line: "1127", message: "more than one row returned by a subquery used as an expression", pg_code: "21000", routine: "ExecSetParamPlan", severity: "ERROR", unknown: "ERROR"

Я подтвердил, что $2 на самом деле является целым числом и указывает на существующую строку, а поскольку id является первичным ключом, я не вижу, как это могло бы когда-либо возвращать более одной строки.

Является ли SELECT FOR UPDATE ошибочным запросом? Как этот запрос может вернуть более одной строки, если id является первичным ключом.

1 Ответ

2 голосов
/ 04 июня 2019

Похоже, вы можете упростить до:

UPDATE cart
SET    loyalty = loyalty || jsonb_build_object('coupon', loyalty->'scan_coupon') || $1
WHERE  id  =  $2
AND    status = $3
AND    item_version = $4
RETURNING *;

UPDATE блокирует строку точно так же, как ваша вложенная SELECT ... FOR UPDATE.

И jsonb_build_object() проще, делая то же самое, что и ваш jsonb_insert(). Или, может быть, проще, но:

SET    loyalty = jsonb_insert(loyalty, '{coupon}', loyalty->'scan_coupon') || $1

Я так же удивлен, как и вы, что подзапрос (вам не нужен) каким-то образом вернул бы более одной строки. Кажется невозможным Вы уверены, что это источник сообщения об ошибке?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...