Allgemeine Fehlerbehandlung

Hallo Wissende,
ich will jetzt ganz groß :slight_smile: in die PL/pgSQL Programmierung einsteigen.
Dabei geht es zunächst um die Erstellung von Triggern. Da ich einsteige, habe ich wenig Ahnung. Da ich wenig Ahnung habe, wird mein Code wohl Fehler produzieren.
Um die Fehler zur erkennen, möchte ich diese in eine Tabelle loggen.
Ich möchte also eine allgemeine/universelle Fehlerbehandlung, bei der es zunächst nur darum geht, dass ich erkennen kann, dass und was für ein Fehler aufgetreten ist.

Ich habe mir das so vorgestellt, dass ich in jede Triggerfunktion einen EXCEPTION-Block einbaue und dort bei einem auftretenden Fehler eine Anfügeabfrage an eine Fehlertabelle rufe.

Deshalb meine Frage:
Welche allgemeinen Parameter (und wie) lassen sich bei einem Laufzeitfehler auslesen?

  • eine Fehlernummer? (in VB6 z.B. err.number)
  • eine Fehlerbeschreibung? (in VB6 z.B. Err.Description)
  • der Name der Prozedur? (geht in VB6 nicht)
  • Nummer der auslösenden Zeile? (in VB6 z.B. Erl)

Gruß Steffen

Ich glaube, Du kommst einfacher, die Fehler ganz normal in das Log von PostgreSQL wandern zu lassen. Ein TRIGGER läuft als eine Transaktion, wirft diese einen Fehler so wird die TX zurückgerollt. Wird dann schwer, danach was in einer Tabelle zu finden. Die Error-Codes und Fehlermeldungen sind in der Doku von PG beschrieben.

Btw.: falls Deine Sig stimmt, bist Du mit einer sehr alten Version unterwegs, und hast auch seit wohl Jahren keinerlei Updates gemacht.

Hallo,
ich habe deine Antwort leider nicht so ganz verstanden.
Die Logdatei von PG selbst gefällt mir nicht so recht, da es sehr viele für mich nicht relevante Meldungen enthält. Auch ist die Struktur nicht gerade übersichtlich.

Wird dann schwer, danach was in einer Tabelle zu finden.
Ich habe mir das ungefähr so gedacht:

CREATE OR REPLACE FUNCTION fnctrg()
  RETURNS trigger AS
$BODY$
DECLARE eAnz        Integer;

BEGIN
	eAnz = 4/0;
	-- eAnz = 100000000 * 100000000;

	EXCEPTION
	    WHEN division_by_zero 
		THEN  insert into errtab (fdatetime,ftext) values (now(),'division_by_zero');

    return new;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

Nun hätte ich das Ganze halt universell, dh. statt nur

division_by_zero

möchte ich alle Fehler abfangen und loggen.

Zur PG-Version:
Die DB wird von einem Fremdsystem (GIS) genutzt - Never change a running System. Der DB-Server ist vom Internet nicht erreichbar und intern ist die Sicherheit eher unrelevant.
Aber ich werde mich mal mit dem Admin zusammensetzen und es mit einem Update versuchen :-).

Nimm einfach “WHEN OTHERS” anstatt " WHEN division_by_zero"

Das Problem ist aber, dass das INSERT in die errtab Tabelle in der gleichen Transaktion läuft wie das Statement welches den Fehler verursacht hat. Und da das aber ein rollback erzwingt, wird auch das loggen der Fehlermeldung zurückgerollt.

In Oracle würde man das Problem mit einer “Logging Prozedur” und einer autonomen Transaktionen lösen. Leider unterstützt Postgres das (noch) nicht, deswegen musst Du eine autonome Transaktion simulieren indem Du den INSERT in die errlog Tabelle via DBLink machst: https://www.postgresql.org/docs/current/static/dblink.html

Ein Beispiel dafür findest Du z.b. hier: http://stackoverflow.com/questions/28190731/how-to-save-query-errors-in-plpgsql-to-a-table/28200306#28200306

In Deiner Funktion würdest Du dann die fn_log_error() im Exception Block aufrufen.

Hallo,

Und da das aber ein rollback erzwingt, wird auch das loggen der Fehlermeldung zurückgerollt.
Bist du dir sicher !? Die Fehlerbehandlung sollte doch den Fehler abfangen und damit ein rollback verhindern.

Nimm einfach “WHEN OTHERS” anstatt " WHEN division_by_zero"
Ja das schon, aber wie kann ich dann die Fehlerart/Fehlermeldung ermitteln, um diese zu loggen?
[edit]gefunden SQLERRM[/edit]

… und gibt es für

  • der Name der Prozedur? (geht in VB6 nicht)
  • Nummer der auslösenden Zeile? (in VB6 z.B. Erl)

evtl. irgendwelche Variablen.

@castrop:

Und da das aber ein rollback erzwingt, wird auch das loggen der Fehlermeldung zurückgerollt.

Ich kann das in meinem Fall nicht bestätigen.
Innerhalb meiner Triggerfunktion arbeitet dieser Code so wie ich es wünsche:

CREATE OR REPLACE FUNCTION meintrigger()
  RETURNS trigger AS
$BODY$
BEGIN
--- ganz viel schlauer Code
	return new;

	EXCEPTION
		WHEN  OTHERS 
			THEN  insert into errtab (fdatetime,ftext) values (now(),SQLERRM);
		return new;

END;
$BODY$
  LANGUAGE plpgsql VOLATILE

Was mir jetzt halt noch fehlen würde wäre der variable Funktionsname und eine Fehlerzeilennummer.

@akretschmer:
Unabhängig von PG sagt mir meine Erfahrung, dass mit jedem beseitigten Fehler 2 neue auftreten und jedes Update das Programm langsamer macht :frowning: .
Aber im Ernst, ein System was 2 Jahr zuverlässig gelaufen ist, läuft aller Wahrscheinlichkeit nach auch weitere 2 Jahre sauber, solange sich die Umgebungsbedingungen nicht ändern.

Für mich stellt jedes update zunächst erst mal ein Risiko dar.

Hallo,
also das funktioniert so wie beschrieben alles recht zuverlässig.
Die Anfügeabfrage an die Fehlertabelle wird in jedem Fall ausgelöst, dh. sie ist von einem Rollback nicht betroffen. Ich gehe davon aus, dass zuerst ein Rollback durchgeführt und dann die Fehlerbehandlung (die Aktionen nach dem EXCEPTION) gestartet wird.
Das scheint mir eigentlich auch logisch wenn ich es z.B. mit VB vergleiche:
Wird ein Fehler nicht behandelt -> Programmabsturz. Wird ein Fehler behandelt wird nur die aktuelle Aktion abgebrochen und mit der Fehlerbehandlung fortgefahren

Was mir jetzt noch fehlt sind ein paar mehre Fehlerhinweise zum Mitloggen. So z.B. die auslösende Zeilennummer im Code. Notfalls müsste ich halt ein paar Marker setzen und loggen.

Gruß Steffen

Hallo,
ich habe jetzt doch noch eine etwas ausführlichere Fehlerausschrift inklusive Zeilennummer gefunden:

BEGIN
    insert into gibtsnich (fdatetime,fcontext,ftext) values (now(),err_desc,SQLERRM);

    --SELECT 1/0;

    return True ;

    EXCEPTION
        WHEN  OTHERS 
            THEN  
                GET STACKED DIAGNOSTICS err_desc = PG_EXCEPTION_CONTEXT;
                err_desc=replace(err_desc,E'\n',';');
                insert into zwi_errtab (fdatetime,fcontext,ftext) values (now(),err_desc,SQLERRM);
    return False;
end;



2017-02-27 15:16:06.273+01	Relation „gibtsnich“ existiert nicht	PL/pgSQL-Funktion mytest() Zeile 7 bei SQL-Anweisung
2017-02-27 15:16:13.855+01	Division durch Null	                SQL-Anweisung „SELECT 1/0“;PL/pgSQL-Funktion mytest() Zeile 9 bei SQL-Anweisung

Vielleicht kann es ja ein anderer auch mal gebrauchen :slight_smile:

Gruß Steffen