Как я могу сказать PostgreSQL не прерывать всю транзакцию, если одно ограничение не выполнено? - PullRequest
13 голосов
/ 24 февраля 2012

Postgres автоматически прерывает транзакции всякий раз, когда любой оператор SQL завершается с ошибкой, которая включает любое нарушение ограничения.Например:

glyph=# create table foo (bar integer, constraint blug check(bar > 5));
CREATE TABLE
glyph=# begin;
BEGIN
glyph=# insert into foo values (10);
INSERT 0 1
glyph=# insert into foo values (1);
ERROR:  new row for relation "foo" violates check constraint "blug"
STATEMENT:  insert into foo values (1);
ERROR:  new row for relation "foo" violates check constraint "blug"

Никаких сообщений на этот счет еще не было отправлено, но транзакция откатывается.Моя личная любимая строка этого сеанса следующая:

glyph=# commit;
ROLLBACK

... так как "ROLLBACK" выглядит как нечетное success сообщение для COMMIT.Но, действительно, он откатился, и в таблице нет строк:

glyph=# select * from foo;
 bar 
-----
(0 rows)

Я знаю, что могу создать тонну SAVEPOINT s и обработать ошибки в SQL таким образом, но этовключает в себя больший трафик в базу данных, большую задержку (в конце концов, мне, возможно, придется обработать ошибку из SAVEPOINT), для относительно небольшой выгоды.Я действительно просто хочу обработать ошибку в моем языке приложения (Python) с помощью try/except, поэтому единственное поведение, которое я хочу из SQL - это ошибки, которые не вызывают автоматический откат.Что я могу сделать?

Ответы [ 3 ]

1 голос
/ 28 октября 2012

Я чрезвычайно новичок в PostgreSQL, но один из примеров в документации PostgreSQL для триггеров / программирования на стороне сервера выглядит так, как будто он делает именно то, что вам нужно.

См .: http://www.postgresql.org/docs/9.2/static/trigger-example.html

Фрагмент страницы: " Таким образом, триггер действует как ненулевое ограничение, но не прерывает транзакцию. "

0 голосов
/ 15 сентября 2017

Я знаю, что это очень старый тикет, но (по состоянию на 2017 год) PostgreSQL все еще имеет такое же поведение автоматического отката, когда что-то идет не так в коммите.Я хотел бы поделиться некоторыми мыслями здесь.

Я не знаю, сможем ли мы изменить это поведение, и мне это не нужно, возможно, для лучшего делегирования PostgreSQL, чтобы справиться с откатом для нас (он знает, что делает, верно?).Откат означает, что данные возвращаются в исходное состояние до неудачной транзакции, это означает, что измененные или вставленные данные из триггеров также будут отброшены.В логике ACID это то, что мы хотим.Допустим, вы сами управляете откатом на бэкэнде, если во время вашего пользовательского отката что-то пойдет не так или если во время отката вы изменили базу данных одновременно с внешними транзакциями, данные станут несогласованными и ваша структура, скорее всего,collapse.

Итак, зная, что PostgreSQL будет управлять своей собственной стратегией отката, возникает вопрос: «как я могу расширить стратегию отката?» .Первое, о чем вы должны подумать: «что вызвало сбой транзакции?» .В вашей структуре try / catch попытайтесь обработать все возможные исключения и снова запустите транзакцию или отправьте отзыв внешнему приложению с некоторыми соответствующими сообщениями "не делать".Для меня это лучший способ справиться с ситуацией, это меньше кода, меньше накладных расходов, больше контроля, удобнее для пользователя, и ваша база данных будет вам благодарна.

Последний момент, на котором я хочу пролить свет,Стандарт SQL имеет код sqlstate , который можно использовать для связи с внутренними модулями.Неудачная операция во время транзакции вернет код sqlstate, затем вы можете использовать эти коды для создания соответствующих недостатков.Вы можете создавать свои собственные коды sqlstate, если они не связаны с зарезервированными (https://www.postgresql.org/message-id/20185.1359219138%40sss.pgh.pa.us). Например, в функции plpgsql

...
$$
begin
...do something...if it goes wrong
raise exception 'custom exception message' using errcode='12345';
end
$$
...

Это пример использования PDO в PHP(используя код ошибки выше):

...
$pdo->beginTransaction();
try {
  $s = $pdo->prepare('...');
  $s->execute([$value]);

  /**
   * Simulate a null violation exception
   * If it fails, PDO will not wait the commit
   * and will throw the exception, the code below
   * is not executed.
   */
  $s->execute([null]);

  /**
   * If nothing wrong happened, we commit to change
   * the database state.
   */
  $pdo->commit();
}
catch (PDOException $e) {
  /**
   * It is important to also have the commit here.
   * It will trigger PostgreSQL rollback.
   * And make the pdo Object back to auto-commit mode.
   */
  $pdo->commit();

  if ($e->getCode() === '12345') {
    send_feedback_to_client('please do not hack us.');
  }
}
...
0 голосов
/ 24 февраля 2012

Я бы настоятельно рекомендовал SqlAlchemy и использовать субтранзакции. Вы можете кодировать как:

#some code 
Session.begin(subtransactions=True)
#more stuff including sql activity then:
with Session.begin(nested=True):
    # get the security
    try:
       foo = MyCodeToMakeFOO(args)
       Session.add(foo)
       Session.flush()
    except:
       log.error("Database hated your foo(%s) but I'll do the rest"%foo)

Наиболее полезно, когда субтранзакция находится в цикле, где вы хотите обработать хорошие записи и зарегистрировать плохие.

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