A cím egy kicsit megtévesztő lehet. Ugye PostgreSQL-ben nincs tárolt eljárás, hanem olyan funkciók vannak, amelyek nem térnek vissza értékkel (pontosabban: returns void AS $$).
PostgreSQL kapcsolatban gyakran elhangzik, hogy fejlett tranzakció kezeléssel rendelkezik, ami tulajdonképpen igaz is. De akkor mi is a probléma?
Próbáljuk ki a következőt:
Hozzunk létre egy táblát.
CREATE TABLE Test
(
Data VARCHAR(15)
,UNIQUE (Data)
)
Illetve egy tárolt eljárást:
CREATE OR REPLACE FUNCTION AddTestItem
(
p_item VARCHAR(15)
)
RETURNS void AS $$
BEGIN
INSERT INTO test VALUES (p_item);
INSERT INTO test VALUES (p_item);
END
$$ LANGUAGE plpgsql;
SELECT addtestitem( 'adat')
Futtassuk le! Egy hiba üzenetet fog fogadni, hogy nem lehet hozzáadni a második elemet az egyedi kulcs miatt. Ha megnézzük a tábla tartalmát, láthatjuk, hogy nem is adott hozzá semmit, az első sikeres insert is visszagörgetődött automatikusan. Ez eddig jól hangzik! Akkor mi is a bajom?
Próbáljuk ki a következőt:
CREATE OR REPLACE FUNCTION AddTestItem
(
p_item VARCHAR(15)
)
RETURNS void AS $$
BEGIN
INSERT INTO test VALUES (p_item);
ROLLBACK;
END
$$ LANGUAGE plpgsql;
SELECT addtestitem( 'adat')
A következő történik:
ERROR: SPI_execute_plan failed executing query "rollback": SPI_ERROR_TRANSACTION
SQL state: XX000
Context: PL/pgSQL function "addtestitem" line 3 at SQL statement
Hasonló az eredmény ha commit-ot használnánk, vagy esetleg savepoint-ot építünk be és addig görgetnénk vissza.
Ennek az az oka, függvény már így is tartalmaz egy tranzakciót, a PostgreSQL nem támogatja a beágyazott tranzakciókat. Legalábbis ilyesmit olvastam a neten. A dokumentációban ugyanis nincs erre vonatkozó megjegyzés. Mivel viszont tárolt eljárást, csak függvényben lehet deklarálni, ezért a kör bezárult! Nincs lehetőség tárolt eljárás keretében hibaüzenet nélküli tranzakciós műveletekre! (rollback, commit)
De ha nekem mégis kell akkor mit kell csinálni?
Vagy a program felületről indított tranzakcióval kezeljük le az ilyen eseteket vagy...
Ha mondjuk olyan rollback-et szeretnénk végerhajtani, amely nem jár hibaüzenettel, akkor valami ilyesmit lehet végrehajtani:
CREATE OR REPLACE FUNCTION AddTestItem
(
p_item VARCHAR(15)
)
RETURNS void AS $$
BEGIN
INSERT INTO test VALUES (p_item);
RAISE EXCEPTION 'Elszállás';
EXCEPTION
WHEN OTHERS THEN
NULL;
END
$$ LANGUAGE plpgsql;
És ha commit-ot vagy savepoint-ot szeretnénk? Azon még nekem is gondolkodnom kell egy kicsit :D