In modernen Datenbanksystemen wird die Business-Logik oft serverseitig umgesetzt – mit Prozeduren und Funktionen. Wenn du mit PostgreSQL arbeitest, ist es wichtig, den Unterschied zwischen Funktionen und Prozeduren zu verstehen (vor allem seit es Prozeduren ab Version 11+ gibt) und wie sie mit Transaktionen zusammenspielen.
Hier erzähle ich dir die wichtigsten Fakten zur Transaktionsmechanik, verschachtelten Aufrufen und teilweisem Rollback von Änderungen in Prozeduren/Funktionen von PostgreSQL 17 – laut offizieller Doku und aktuellen Einschränkungen.
Wichtige Begriffe: Funktionen vs Prozeduren
Funktion (CREATE FUNCTION) – läuft immer im Rahmen einer äußeren Transaktion; innerhalb von Funktionen darfst du keine expliziten Transaktionsbefehle (BEGIN, COMMIT, ROLLBACK, SAVEPOINT) verwenden.
- Alle Änderungen werden nur auf Ebene der äußeren Transaktion festgeschrieben oder zurückgesetzt.
- Für einen „teilweisen Rollback“ innerhalb von Funktionen nutzt man
BEGIN ... EXCEPTION ... END, aber damit kann man keine Commits innerhalb der Funktion machen.
Prozedur (CREATE PROCEDURE) – wurde eingeführt, um Transaktionen direkt auf dem Server zu steuern (z.B. Teil-Commits, Rollbacks von Schritten usw.).
- In Prozeduren (PL/pgSQL) kannst du
COMMIT,ROLLBACK,SAVEPOINT,RELEASE SAVEPOINTverwenden. - WICHTIG:
ROLLBACK TO SAVEPOINTist in einer PL/pgSQL-Prozedur nicht erlaubt (gibt einen Syntaxfehler). - Prozeduren kann man nur mit dem separaten SQL-Befehl
CALL ...aufrufen, nicht perSELECToder innerhalb anderer Funktionen.
Wie ruft man eine Prozedur/Funktion aus einer anderen auf?
Funktionen rufen andere Funktionen „transparent“ über den Funktionsnamen auf:
-- Beispiel: Funktion zur Berechnung des Rabatts
CREATE OR REPLACE FUNCTION berechne_rabatt(bestellwert NUMERIC)
RETURNS NUMERIC AS $$
BEGIN
IF bestellwert >= 100 THEN
RETURN bestellwert * 0.1;
ELSE
RETURN 0;
END IF;
END;
$$ LANGUAGE plpgsql;
-- Bestellverarbeitungsfunktion ruft eine andere Funktion auf
CREATE OR REPLACE FUNCTION bestellung_verarbeiten(bestell_id INT, bestellwert NUMERIC)
RETURNS VOID AS $$
DECLARE
rabatt NUMERIC;
BEGIN
rabatt := berechne_rabatt(bestellwert);
RAISE NOTICE 'Rabatt: %', rabatt;
INSERT INTO bestellungen_log (bestell_id, bestellwert, rabatt)
VALUES (bestell_id, bestellwert, rabatt);
END;
$$ LANGUAGE plpgsql;
Alles läuft in einer äußeren Transaktion! Ein Fehler in einer Funktion führt zum Rollback aller Änderungen.
Aufruf von Prozeduren und verschachtelte Transaktionen
Prozeduren kann man innerhalb anderer Prozeduren mit CALL ... aufrufen (in PostgreSQL 17 ist ein Aufruf-Stack wie CALL proc1() -> CALL proc2() erlaubt), aber die Transaktionsregeln bleiben:
- Transaktionsbefehle (
COMMIT,ROLLBACK,SAVEPOINT,RELEASE SAVEPOINT) sind nur auf der obersten Ebene von Prozeduren erlaubt. - Wenn eine Prozedur mit Transaktionssteuerung innerhalb einer bereits aktiven expliziten Transaktion aufgerufen wird (z.B. über einen Client ohne Autocommit), führt ein Versuch,
COMMIT/SAVEPOINTauszuführen, zu einem Fehler.
Prozeduren dürfen nicht innerhalb von Funktionen oder anonymen Blöcken (DO ...) gestartet werden. Nur mit dem separaten CALL-Befehl.
Beispiel für eine Prozedur mit Transaktionssteuerung
-- Prozedur mit schrittweisem Commit (funktioniert nur im Autocommit-Modus der Verbindung)
CREATE PROCEDURE bestellungen_im_batch_verarbeiten()
LANGUAGE plpgsql
AS $$
DECLARE
rec RECORD;
BEGIN
FOR rec IN SELECT bestell_id, bestellwert FROM eingehende_bestellungen LOOP
BEGIN
-- Jede Datencharge einzeln speichern
INSERT INTO bestellungen (bestell_id, gesamt) VALUES (rec.bestell_id, rec.bestellwert);
EXCEPTION WHEN OTHERS THEN
INSERT INTO bestellungsfehler(bestell_id, fehler_text) VALUES (rec.bestell_id, SQLERRM);
END;
COMMIT;
END LOOP;
END;
$$;
-- Prozedur aufrufen
CALL bestellungen_im_batch_verarbeiten();
Nach jedem COMMIT startet automatisch eine neue Transaktion.
Teilweiser Rollback (savepoint-ähnliches Verhalten) in PL/pgSQL
PL/pgSQL (sowohl in Funktionen als auch in Prozeduren) unterstützt nicht den Befehl ROLLBACK TO SAVEPOINT.
Um Änderungen in einem Teil des Codes zurückzusetzen, nutzt man nur den Block BEGIN ... EXCEPTION ... END:
BEGIN
-- irgendwelche Aktionen
BEGIN
-- potenziell fehlerhafte Operation
EXCEPTION WHEN OTHERS THEN
-- alle Änderungen in diesem Block werden zurückgesetzt
RAISE NOTICE 'Rollback im Block!';
END;
END;
In Prozeduren kann man auch SAVEPOINT und RELEASE SAVEPOINT verwenden, aber nicht ROLLBACK TO SAVEPOINT. Der Sinn ist, Schritte zu trennen, aber steuern kann man sie nur über Exception-Handling.
Einschränkungen und Best Practices
- Funktionen – nur atomare Operationen: alles oder nichts. Wenn etwas schiefgeht, werden alle Änderungen zurückgesetzt.
- Prozeduren – nur per CALL: und nur als separater SQL-Befehl, nicht aus SELECT/Funktionen. Verschachtelte Transaktionssteuerung ist möglich, aber nur unter Einhaltung der PL/pgSQL-Einschränkungen.
- Teilweiser Rollback – nur über EXCEPTION: offiziell empfohlener und unterstützter Weg für teilweisen Rollback (wie SAVEPOINT).
- Verschachtelte Prozeduren können Transaktionen nur beim Aufruf per CALL steuern: sonst gibt es einen Fehler.
Fragen zum Zusammenspiel von Logik und Transaktionen
Kann ich eine „verschachtelte“ Transaktion innerhalb einer Funktion machen?
Nein. Alles läuft in einer Transaktion. Für teilweisen Rollback – nur EXCEPTION-Blöcke.
Kann ich COMMIT/ROLLBACK in einer Funktion oder einem anonymen Block machen?
Nein, das ist ein Syntaxfehler. Nutze Prozeduren dafür.
Kann man eine Prozedur aus einer Funktion aufrufen?
Nein, nur mit dem CALL-Befehl. Aus Funktion/SELECT – geht nicht.
Kann man in einer Prozedur ROLLBACK TO SAVEPOINT machen?
Nein! In PL/pgSQL ist das verboten. Nutze EXCEPTION-Blöcke.
GO TO FULL VERSION