Загадка транзакции MySQL - PullRequest
4 голосов
/ 28 декабря 2010

Мне нужно выполнить несколько вставок в одной атомарной транзакции. Например:

начать транзакцию;

вставить ...

вставить ...

фиксации;

Однако, когда MySQL обнаруживает ошибку, он прерывает только конкретный оператор, вызвавший ошибку. Например, если во втором операторе вставки есть ошибка, фиксация все равно будет иметь место, и первый оператор вставки будет записан. Таким образом, при возникновении ошибок транзакция MySQL на самом деле не является транзакцией. Чтобы преодолеть эту проблему, я использовал обработчик ошибок, когда я откатывал транзакцию. Сейчас транзакция молча прерывается, но я не знаю, в чем была проблема.

Так вот вам загадка:

Как я могу заставить MySQL прервать транзакцию при обнаружении ошибки и передать код ошибки вызывающей стороне?

Ответы [ 2 ]

3 голосов
/ 28 декабря 2010

Как можно заставить MySQL прервать транзакцию, когда она обнаружит ошибку, и передать код ошибки вызывающей стороне?

MySQL передает код ошибки ввызывающий и на основе этого кода ошибки вызывающий может решить, хочет ли он совершить работу, выполненную до настоящего момента (игнорируя ошибку с этим конкретным оператором INSERT), или откатить транзакцию.

Этоотличается от PostgreSQL, который всегда прерывает транзакцию при ошибке, и это поведение является источником многих проблем.

Обновление:

Использование безусловной практики - плохая практикаROLLBACK внутри хранимых процедур.

Хранимые процедуры являются наращиваемыми, а транзакции - нет, поэтому ROLLBACK в рамках вложенной хранимой процедуры будет возвращаться к самому началу транзакции, а не к состояниювыполнение хранимой процедуры.

Если вы хотите использовать транзакции для восстановления состояния базы данных при ошибках, используйте конструкции SAVEPOINT и DECLARE HANDLER для откатадо точек сохранения:

CREATE PROCEDURE prc_work()
BEGIN
        DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK TO sp_prc_work;
        SAVEPOINT sp_prc_work;
        INSERT  …;
        INSERT  …;
        …
END;

Сбой в любой из вставок откатит все изменения, сделанные процедурой, и завершит ее.

2 голосов
/ 30 декабря 2010

Используя пример г-на Кассного, вот мой лучший подход к обнаружению конкретных ошибок:

Идея состоит в том, чтобы использовать переменную tvariable для перехвата простого сообщения об ошибке, затем вы можете перехватить состояния sql, которые, по вашему мнению, могут произойти, сохранить пользовательские сообщения в вашей переменной:

DELIMITER $$

DROP PROCEDURE IF EXISTS prc_work $$
CREATE PROCEDURE prc_work ()
BEGIN
  DECLARE EXIT HANDLER FOR SQLSTATE '23000'
  BEGIN
    SET @prc_work_error = 'Repeated key';
    ROLLBACK TO sp_prc_work;
  END;
  DECLARE EXIT HANDLER FOR SQLEXCEPTION
  BEGIN
    SET @prc_work_error = 'Unknown error';
    ROLLBACK TO sp_prc_work;
  END;

  START TRANSACTION;
    SAVEPOINT sp_prc_work;
    INSERT into test (id, name) VALUES (1, 'SomeText');
  COMMIT;
END $$

DELIMITER ;

Затем вы просто делаете обычный вызов и делаете оператор выбора для переменной, например:

call prc_work(); select @prc_work_error;

Возвращается либо NULL, если ошибки нет, либо сообщение об ошибке в случае ошибки. Если вам нужно постоянное сообщение об ошибке, вы можете создать таблицу для его хранения.

Это утомительно и не очень гибко, поскольку требует сегмента DECLARE EXIT HANDLER для каждого кода состояния, который вы хотите перехватить, он также не отображает подробные сообщения об ошибках, но, эй, он работает.

...