ROLLBACK não funciona após INSERT INTO na tabela de destino recém-criada

11

Estou trabalhando no script PHP, que importa o arquivo CSV ( customers.csv) na tabela MySQL ( customers).

Antes de inserir o conteúdo do arquivo CSV na tabela mysql, primeiro faço o backup da customerstabela original .

Estou agrupando todo o processo de importação (incluindo o backup) em uma transação mysql (para considerar casos em que o CSV está corrompido em algum lugar no meio e para garantir que a importação seja atômica).

O problema é que o ROLLBACK parece não funcionar quando eu o chamo logo após a INSERT INTOinstrução: ao verificar o banco de dados via phpMyAdmin, posso ver a tabela recém-criada E AS LINHAS DENTRO ainda presentes após o roollback .

Aqui está o log das operações:

[2015-01-19 14:08:11] DEBUG: "START TRANSACTION" [] []
[2015-01-19 14:08:11] DEBUG: SHOW TABLES LIKE :table_name; [] []
[2015-01-19 14:08:28] DEBUG: CREATE TABLE `customers__20150119_14_08_20` LIKE `customers` [] []
[2015-01-19 14:08:37] DEBUG: INSERT INTO `customers__20150119_14_08_20` SELECT * FROM `customers` [] []
[2015-01-19 14:08:50] DEBUG: "ROLLBACK" [] []

Então, eu me pergunto por que o depsite ROLLBACKé chamado, a transação não é cancelada. Entendo que CREATE TABLEnão é de natureza transacional e não pode ser revertida. Mas eu estava assumindo que, INSERT INTOpor lidar com a inserção de linhas (sem definir o esquema), SERÁ realmente transacional e, após ROLLBACK, ficarei com a tabela de destino vazia. Por que não é esse o caso?

E aqui está a saída SHOW CREATE TABLE customers(então minha tabela é InnoDb):

CREATE TABLE `customers` (
 `Code` varchar(32) NOT NULL,
 `Name` varchar(128) DEFAULT NULL,
 `Price` varchar(128) DEFAULT NULL,
 PRIMARY KEY (`Code`),
 KEY `Price` (`Price`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

e aqui está a saída para a tabela de desination:

CREATE TABLE `customers__20150119_14_08_20` (
 `Code` varchar(32) NOT NULL,
 `Name` varchar(128) DEFAULT NULL,
 `Price` varchar(128) DEFAULT NULL,
 PRIMARY KEY (`Code`),
 KEY `Price` (`Price`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
Dimitry K
fonte
O comportamento é o mesmo se você reordenar primeiro create table, então start transaction, insert, rollback?
precisa saber é o seguinte
Eu estava prestes a dizer isso para !!!
RolandoMySQLDBA 19/01
Você desabilita a confirmação automática na conexão do seu programa?
mustaccio

Respostas:

13

O motivo é que algumas declarações, como CREATE TABLEcausam um commit implícito. Você pode ler sobre eles na documentação: Declarações que causam um comprometimento implícito .

Portanto, a sequência original de instruções:

START TRANSACTION
SHOW TABLES LIKE customers
CREATE TABLE `customers__20150119_14_08_20` LIKE `customers`
INSERT INTO `customers__20150119_14_08_20` SELECT * FROM `customers`
ROLLBACK

expandirá para:

START TRANSACTION ;   -- transaction context created
SHOW TABLES LIKE customers ;

COMMIT ;              -- CREATE TABLE forces commit before itself
                      --     (at this point the previous transaction is done.)
START TRANSACTION ;   -- and a new transaction  
CREATE TABLE `customers__20150119_14_08_20` 
    LIKE `customers` ;
COMMIT ;              -- CREATE TABLE forces commit after itself. 
                      -- At this point there's no transaction context

START TRANSACTION ;   --  starts a new transaction
INSERT INTO `customers__20150119_14_08_20` 
    SELECT * FROM `customers` ;
COMMIT ;              -- caused by "autocommit on" setting (guess). 

ROLLBACK ;            -- this rollback HAS NOTHING to undo

A solução seria iniciar a transação (ou uma nova) após a CREATE TABLEinstrução ou usar uma tabela temporária.

ypercubeᵀᴹ
fonte
@ Dimitry, thnx para a edição.
precisa saber é o seguinte
1
E @RolandoMySQLDBA por suas amáveis ​​palavras. Eu sou o FGITW hoje (e apenas 15 segundos mais rápido do que você;)
ypercubeᵀᴹ
@ypercube welcome! Demorei um pouco para descobrir onde exatamente essa CREAT TABLE irá cause an implicit commit... Então, tive que fazer esse esboço no papel de qualquer maneira :) @RolandoMySQLDBA, agradeço também pela contribuição rápida. Eu li algumas dezenas de respostas no ano passado e elas me ajudaram bastante !!
precisa saber é o seguinte
Então você está dizendo que o commit implícito antes do INSERT, causado pela instrução DDL, também de alguma forma causa um commit após a inserção?
mustaccio
1
Sim, existem duas partes no raciocínio, mas a parte principal, na minha opinião, é que o OP não conseguiu descobrir o comprometimento implícito da tabela de criação.
precisa saber é o seguinte
3

Parece que a ordem das instruções está causando o problema.

No meu antigo bloqueio de linha de postagem na transação ACID innodb , nomeei 12 instruções que interrompem uma transação de forma intermitente. No seu caso particular, foi a CREATE TABLEafirmação.

Depois que você rodava CREATE TABLEdentro de um bloco START TRANSACTION... COMMIT/ROLLBACK, não havia estrutura para reverter.

Basta executar o CREATE TABLEantes START TRANSACTIONe você deve ficar bem.

De uma chance !!!

RolandoMySQLDBA
fonte