tiny ‘n’ smart
database layer

Odkazy: dibi | API reference

Oznámení

Omlouváme se, provoz fóra byl ukončen

PostgreSQL: Transakce a „current transaction is aborted…“

před 10 lety

lucass
Člen | 87

Zdravím,

asi jsem objevil Ameriku, ale pro jistotu to sem píšu jako obecnou zkušenost s transakcemi v PostgreSQL, kdyby někdo řešil podobnou věc.

Nějaký čas jsem strávil nad hledáním této chyby:

current transaction is aborted, commands ignored until end of transaction block unique

Ve své metodě save() mám následující blok:

<?php

public function save($data, $id = false) {
    ...
    try {

        dibi::begin(get_class($this));

        // vlastni ulozeni pole $data

        dibi::commit(get_class($this));
    } catch (DibiException $e) {
        $this->result->error = $e->getMessage();
    }
    ...
}

?>

Přestože DibiException byla odchycena, tj. ocitl jsem se v catch bloku, aplikace celkově stejně padla na pozdějším DibiDriverException + výše zmíněná hláška. Chyba se stávala jen v případě, kdy jsem nad tabulkou, do níž ukládám, vytvořil UNIQUE CONSTRAINT či UNIQUE INDEX.

Po debugu a hledání po netu jsem nakonec zjistil, že chyba, protože je nalezena asi v hlubší vrstvě (kdyby to někdo dokázal vysvětlit přesněji, budu rád), je v rollbacku. V catch bloku, kdy je odchycena DibiException, je nutné doplnit následující:

<?php
    ...
    } catch (DibiException $e) {
        dibi::rollback(get_class($this)); // nutne

        $this->result->error = $e->getMessage();
    }
    ...
?>

Jak říkám, asi je to přístup samozřejmý, ale doposud mi dostačovalo jen odchytávat výjimky bez rollbacků a nikdy jsem se s tím nesetkal.

Editoval lucass (17. 11. 2009 22:15)

před 10 lety

Petr Motejlek
Člen | 293

Tohle téma už je trošku starší, ale když tak koukám na ten zápis, tak mi to nedá, a musím se zeptat ;). Co se bude dít, když ten dibi::rollback(…) vyhodí výjimku? Např. že hapala databáze?

Vím, že to je trošku podivný scénář, ale co kdyby dotaz v dibi::query(…) byl například syntakticky špatně (klidně to ale může být chyba vstupních dat) a dibi by vyhodila výjimku (databáze v klidu běží, otestovala dotaz a zjistila chybu, tak posílá do vyšší úrovně hlášku, že je něco špatně), já bych tu výjimku chytil, ale první, co bych dělal by byl dibi::rollback(…), v tu chvíli by ale databáze náhle spadla (takovej deus ex efekt, nebo tak něco ;)), nebo by někdo přerušil spojení mezi strojem, kde běží PHP a strojem, kde běží databáze. Teď by mi aplikace umřela s hláškou, že spadla databáze, což sice není špatně, ale to, že mám v aplikaci špatně dotaz, se dozvím možná až když za nějakou dobu někdo znovu použije můj kód… Nebylo by lepší to koncipovat spíš jako

<?php
$query = 'SELECT * FROM [myTable]';

try {
  dibi::begin();
} catch (Exception $e) {
  Debug::processException($e);
  throw $e;
};

try {
  dibi::query($query);
  dibi::commit();
} catch (Exception $e) {
  Debug::processException($e);

  try {
    dibi::rollback();
  } catch (Exception $f) {
    Debug::processException($f);
    throw $f;
  };

  throw $e;
};
?>

Možná to je trochu overkill, ale zase tomu už nic neuteče ;). Budu rád, když se tady ostatní vyjádří, jak je to podle nich nejlepší dělat. Díky ;)

Editoval Petr Motejlek (10. 12. 2009 15:39)