Перейти к содержанию

Транзакции

В Picodata поддерживаются транзакции — блоки команд. В рамках транзакции пользователь может выполнить несколько SQL-запросов атомарно — так, как если бы это был единый запрос. Для транзакционных запросов действуют ограничения.

Структура

Транзакционный блок представляет собой запрос вида DO $$ BEGIN ... END $$;, внутри которого можно поместить набор вложенных запросов. Такой блок функционально является неименованной процедурой.

Поддерживаемые команды исполнения

На данный момент для транзакционных блоков поддерживаются следующие команды исполнения:

  • <DML> — выполнить вложенный DML-запрос (см. ограничения ниже).
  • RETURN QUERY <DQL> — выполнить вложенный запрос и отобразить результат выполнения (используется для DQL-запросов).
  • LET <name> = (<DQL>) — выполнить вложенный запрос, возвращающий одну колонку, и сохранить результат в переменной <name>, доступной в последующих командах блока (см. ниже).
  • IF <expr> THEN ... END IF; — условно выполнить вложенные DML-команды, если выражение <expr> истинно (см. ниже).

Пример использования

DO $$ BEGIN
  LET cur = (SELECT money FROM bank WHERE id = 1);
  RETURN QUERY SELECT cur;
  IF cur >= 100 THEN
    UPDATE bank SET money = money - 100 WHERE id = 1;
    UPDATE bank SET money = money + 100 WHERE id = 1;
  END IF;
END $$;

LET-переменные

Команда LET связывает результат однострочного запроса с одной колонкой с именованной переменной, которую можно использовать в последующих командах блока (в том числе в выражениях WHERE, SET, в условии IF и в RHS другого LET):

DO $$ BEGIN
  LET price = (SELECT price FROM store WHERE id = 2);
  UPDATE warehouse SET price = price * 2 WHERE id = 2;
END $$;

Ограничения для LET:

  • RHS должен возвращать ровно одну колонку. Если выборка возвращает более одной строки, то будет возвращенна последняя строка из выборки. Если выборка пуста, переменная получает значение NULL.
  • Переменная не может быть объявлена повторно с другим типом. Повторное объявление с тем же типом разрешено и переиспользует существующий слот.
  • Объявленная переменная обязана быть использована хотя бы в одной последующей команде блока. Неиспользованная переменная приводит к ошибке планирования.
  • Если имя LET-переменной совпадает с именем колонки таблицы, доступной в запросе, ссылка считается неоднозначной и отклоняется.

Условные блоки IF

Команда IF <expr> THEN ... END IF; выполняет вложенные DML-команды, если выражение <expr> истинно.

DO $$ BEGIN
  LET cur = (SELECT money FROM bank WHERE id = 1);
  IF cur >= 100 THEN
    UPDATE bank SET money = money - 100 WHERE id = 1;
    UPDATE bank SET money = money + 100 WHERE id = 1;
  END IF;
END $$;

Ограничения для IF:

  • Условие должно быть скалярным булевым выражением; ссылки на колонки таблиц в условии не допускаются — используйте LET для подготовки значений.
  • В теле допускаются только DML-команды (UPDATE / DELETE / INSERT). Команды LET, RETURN QUERY и вложенный IF внутри тела не разрешены.

Ограничения

Поддержка транзакционного выполнения команд в Picodata имеет ограничения:

  • В рамках одной транзакции нельзя обращаться к более чем одному движку хранения
  • Все запросы к шардированным таблицам внутри блока должны затрагивать только один бакет. При нарушении этого условия Picodata вернет пользователю ошибку. В будущем появится поддержка команды EXPLAIN для транзакционных блоков, с помощью которой можно будет наглядно увидеть затронутые бакеты.
  • Поддерживаются только запросы, не требующие перемещения данных. Иными словами, в текущей реализации транзакции должны быть выполнимы в рамках одного узла Picodata. Глобальные транзакции, затрагивающие данные на нескольких узлах, не поддерживаются.
  • Для глобальных таблиц поддерживаются только читающие запросы (DQL), но не модифицирующие (DML).
  • Читающие команды в блоке должны располагаться строго до модифицирующих: то есть, все RETURN QUERY SELECT ... должны идти до DML и дальше уже появиться не могут.