Funkcja ROLLBACK nie działa po INSERT INTO nowo utworzonej tabeli docelowej

11

Pracuję nad skryptem PHP, który importuje plik CSV ( customers.csv) do tabeli MySQL ( customers).

Przed wstawieniem zawartości pliku CSV do tabeli mysql najpierw tworzę kopię zapasową oryginalnej customerstabeli.

Zawijam cały proces importu (w tym tworzenie kopii zapasowej) w transakcji mysql (aby uwzględnić przypadki, w których CSV jest uszkodzony gdzieś pośrodku i aby upewnić się, że import jest atomowy).

Problem polega na tym, że funkcja ROLLBACK nie działa, gdy wywołuję ją zaraz po INSERT INTOinstrukcji: podczas sprawdzania bazy danych za pomocą phpMyAdmin widzę nowo utworzoną tabelę ORAZ WIERSZE WEWNĄTRZ po jej wywołaniu .

Oto dziennik operacji:

[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" [] []

Zastanawiam się więc, dlaczego nazywa się depsite ROLLBACK, transakcja nie jest anulowana. Rozumiem, że CREATE TABLEnie ma charakteru transakcyjnego i nie można go wycofać. Ale zakładałem, że INSERT INTOponieważ zajmuje się wstawianiem wierszy (nie definiowaniem schematu), BĘDZIE faktycznie transakcyjne, a po ROLLBACK pozostanie pusta tabela docelowa. Dlaczego tak nie jest?

A oto wynik SHOW CREATE TABLE customers(więc moja tabela jest 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

a oto dane wyjściowe dla tabeli 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
źródło
Czy zachowanie takie samo, jeśli kolejność w pierwszej kolejności create table, a potem start transaction, insert, rollback?
ypercubeᵀᴹ
Właśnie miałem to powiedzieć !!!
RolandoMySQLDBA
Czy wyłączasz automatyczne zatwierdzanie połączenia w swoim programie?
mustaccio,

Odpowiedzi:

13

Powodem jest to, że niektóre instrukcje, na przykład, CREATE TABLEpowodują niejawne zatwierdzenie. Możesz o nich przeczytać w dokumentacji: Oświadczenia, które powodują niejawne zatwierdzenie .

Oryginalna sekwencja instrukcji:

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

rozwinie się w:

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

Rozwiązaniem byłoby rozpocząć transakcję (lub nową) po CREATE TABLEwyciągu lub użyć tabeli tymczasowej.

ypercubeᵀᴹ
źródło
@Dimitry, dzięki za edycję.
ypercubeᵀᴹ
1
I @RolandoMySQLDBA za miłe słowa. Jestem dzisiaj FGITW (i tylko 15 sekund szybciej od ciebie;)
ypercubeᵀᴹ
@ypercube welcome! Zajęło mi trochę czasu, aby dowiedzieć się, gdzie dokładnie ten CREAT TABLE cause an implicit commit... Więc musiałem zrobić ten zarys na papierze :) @RolandoMySQLDBA również dziękuję za szybkie wejście. W ubiegłym roku przeczytałem kilkadziesiąt waszych odpowiedzi i bardzo mi pomogły !!
Dimitry K
Więc mówisz, że niejawna popełnić przedINSERT , spowodowane rachunku telefonicznego, również w jakiś sposób powoduje popełnić po wkładki?
mustaccio,
1
Tak, rozumowanie składa się z dwóch części, ale moim zdaniem główną częścią, której PO nie mógł zrozumieć, było ukryte zatwierdzenie przez tabelę tworzenia.
ypercubeᵀᴹ
3

Wygląda na to, że problem powoduje kolejność instrukcji.

W moim starym wierszu blokowania wiersza w ramach transakcji ACID innodb nazwałem 12 instrukcji przerywających transakcję sporadycznie. W twoim konkretnym przypadku było to CREATE TABLEstwierdzenie.

Po uruchomieniu CREATE TABLEw START TRANSACTION... COMMIT/ROLLBACKbloku nie było żadnych ram do wycofania.

Po prostu uruchom CREATE TABLEwcześniej START TRANSACTIONi wszystko powinno być w porządku.

Spróbuj !!!

RolandoMySQLDBA
źródło