2013-06-18 25 views
5

Używam małej metody w CodeIgniter do wstawienia niektórych linii w bazie danych (ta sama tabela). Chciałbym zobaczyć, które wstawienie nie powiodło się w transakcji (zwracając tablicę tytułów). Mój kod to:Codeigniter, śledzenie błędów w transakcji

$failure = array(); //the array where we store what failed 
$this->db->trans_start(); 
foreach ($data as $ressourceCsv){ //data is an array of arrays to feed the database 
    $this->ajout_ressource($ressourceCsv); //method to insert (basically, just an insert with active record) 
    if (($this->db->_error_message())!=null) { 
      $failure[] = $ressourceCsv['title']; 
    } 
} 
$this->db->trans_complete(); 
return $failure; 

Faktem jest, że jeśli nie uda transakcji (bez $ this-> db-> trans _...), to działa doskonale i mam tablicę zawierającą kilka tytuły. Ale w przypadku transakcji tablica zawiera wszystkie tytuły od pierwszego błędu. Czy istnieje sposób na uzyskanie tytułu z insercji, która spowodowała wycofanie transakcji?

Próbowałem również z:

$failure = array(); //the array where we store what failed 
$this->db->trans_start(); 
foreach ($data as $ressourceCsv){ //data is an array of arrays to feed the database 

    if (!$this->ajout_ressource($ressourceCsv)) { //active record insertion return true 
      $failure[] = $ressourceCsv['title']; // if successful 
    } 
} 
$this->db->trans_complete(); 
return $failure; 

Odpowiedz

1

Wierzę, że po wystąpieniu błędu wewnątrz transakcji, ty musi wycofywania przed wszelkimi więcej modów DB mogą być wykonane. To by wyjaśniało zachowanie, które widzisz. Po pierwszym błędzie transakcja jest "przerywana" i kontynuowana jest twoja pętla, co powoduje, że każde kolejne polecenie SQL również się nie powiedzie. Można to zilustrować następująco:

db=# select * from test1; 
id | foo | bar 
----+-----+----- 
(0 rows) 

db=# begin; 
BEGIN 
db=# insert into test1 (foo, bar) values (1, 'One'); 
INSERT 0 1 
db=# insert into test1 (foo, bar) values (Oops); 
ERROR: column "oops" does not exist 
LINE 1: insert into test1 (foo, bar) values (Oops); 
              ^
db=# insert into test1 (foo, bar) values (2, 'Two'); 
ERROR: current transaction is aborted, commands ignored until end of transaction block 
db=# select * from test1; 
ERROR: current transaction is aborted, commands ignored until end of transaction block 
db=# commit; 
ROLLBACK 
ace_db=# select * from test1; 
id | foo | bar 
----+-----+----- 
(0 rows) 

db=# 

uwaga wydaje się, że „popełnić” robi „wycofywania”, jeśli wystąpił błąd (to nie literówka).

także BTW: korzystania $this->db->trans_status() === FALSE sprawdzić za błąd podczas transakcji.

Aktualizacja: Oto niektóre z nich (niesprawdzone) kod to zrobić w transakcji tak, że wkładki nie są postrzegane przez innych, dopóki nie są gotowe:

$failure = array(); //the array where we store what failed 
$done = false; 
do { 
    $this->db->trans_begin(); 
    foreach ($data as $key => $ressourceCsv){ //data is an array of arrays to feed the database 
     $this->ajout_ressource($ressourceCsv); //method to insert (basically, just an insert with active record) 
     if ($this->db->trans_status() === false) { // an insert failed 
      $failure[] = $ressourceCsv['title']; // save the failed title 
      unset($data[$key]);     // remove failed insert from data set 
      $this->db->trans_rollback();   // rollback the transaction 
      break;         // retry the insertion 
     } 
    } 
    $done = true;         // completed without failure 
} while (count($data) and ! $done);    // keep going until no data or success 

/* 
* Two options (uncomment one): 
* 1. Commit the successful inserts even if there were failures. 

$this->db->trans_commit(); 

* 2. Commit the successful inserts only if no failures. 

if (count($failure)) { 
    $this->db->trans_rollback(); 
} else { 
    $this->db->trans_commit(); 
} 
*/ 

return $failure; 
+0

Ok, więc jeśli mogę go rację, bardzo struktura transakcji nie pozwala mi kontynuować, tak jak chciałem. Również nie mogę użyć $ this-> db-> trans_status() === FALSE, ponieważ faktycznie zawijam linie transakcji w instrukcji if (nie zawsze używam transakcji). Aby rozwiązać mój problem, w końcu zapisałem identyfikator pomyślnego wstawienia, aby je usunąć w końcu, jeśli chcę wycofać, a tablica $ failure nie jest pusta – Dargor

+0

Przypuszczam, że jeśli ** musisz ** wykonać wszystkie wstawki na raz, niezależnie od tego, wszelkich niepowodzeń, możesz to zrobić, ale jeśli twoim intencją jest, aby nikt nie zobaczył żadnej z tych wstawek, jeśli niektóre zawiodą, wtedy nie możesz tego dokonać w ten sposób. Możesz mieć kolumnę boolowską o nazwie "accepted" i ustawić ją na TRUE tylko po zakończeniu wszystkich wstawień i nieudanym - i dodaj klauzulę "WHERE accepted = TRUE" do innych zapytań. – user9645

+0

Dodałem aktualizację, aby pokazać, jak wykonać pętlę wewnątrz transakcji, jeśli chcesz. – user9645