Auf meiner unendlichen Suche, mich mit einer sicheren Transaktion in den Fuß zu schießen, habe ich anscheinend noch mehr Möglichkeiten gefunden, um den Questzweck zu erfüllen. Die Sicherungs-Transaktionsklausel selbst kommt diesmal nicht in Frage, aber deshalb habe ich den folgenden Code geschrieben.
Betrachten Sie das folgende vollständige Beispiel mit verschachtelten Fehlerbehandlungsroutinen:
begin try
begin try
select 'Step 1';
end try
begin catch
select 'Step 1 handler - handling ''' + error_message() + '''';
goto commit_and_exit;
end catch;
begin try
select 'Step 2';
raiserror('Step 2 error', 16, 1);
end try
begin catch
select 'Step 2 handler - handling ''' + error_message() + '''';
goto commit_and_exit;
end catch;
end try
begin catch
select 'Outer handler - handling ''' + error_message() + '''';
goto commit_and_exit;
end catch
commit_and_exit:
raiserror('Error raised for the caller to see', 16, 1);
Es ist dokumentiert, dass
GOTO
Anweisungen können verwendet werden, um zu einer Beschriftung innerhalb desselbenTRY
oderCATCH
Blocks zu springen oder einenTRY
oderCATCH
Block zu verlassen .
Oder kann es?
In Anbetracht des obigen Codes würde ein vernünftiger Programmierer die Ausgabe berechnen
Schritt 1
Schritt 2
Schritt 2 Handler - Behandlung von 'Schritt 2 Fehler'
<Fehler, den der Anrufer sehen muss>
In der Tat ist was passiert:
Schritt 1
Schritt 2
Schritt 2 Handler - Behandlung von 'Schritt 2 Fehler'
Äußerer Handler - Behandlung 'Fehler, den der Anrufer sehen kann'
<Fehler, den der Anrufer sehen muss>
Beim schrittweisen Debuggen kann ich sehen, dass das Steuerelement die try / catch-Blöcke vollständig verlässt, dann ein Fehler ausgelöst wird, das Steuerelement an den äußersten catch
Block zurückgegeben wird, dieser Block ausgeführt wird, das Steuerelement commit_and_exit:
erneut ausgeführt wird und dieser letzte Block ausgeführt wird wieder .
Wenn Sie einige commit tran
s oder rollback trans
s unter haben commit_and_exit:
, werden Sie versuchen, die Übertragung zweimal zu begehen. Mit vorstellbaren Ergebnissen, da der Anrufer äußere Transaktionen starten kann.
Ich habe auch versucht, ein anderes Etikett end_of_outer:
direkt vor dem äußeren zu erstellen end try
, damit das Steuerelement den äußeren try
Block "normal" verlässt . Interessanterweise machte das keinen Unterschied.
Was zum Teufel ist los und wie geht das richtig?
XACT_STATE() = 1
, sodass wir zu einem Speicherpunkt zurückkehren und weitermachen). Ich bin jedoch mehr besorgt über das Prinzip der Fehlerbehandlung, selbst wenn Transaktionen vorhanden sind. Jeder Code danachcommit_and_exit:
wird zweimal ausgeführt.Antworten:
Es sieht so aus, als wäre das Verhalten vor SQL Server 2012 ein Fehler.
Ich schließe dies aus diesem Connect-Artikel:
https://connect.microsoft.com/SQLServer/feedback/details/712003/sql-server-2012-error-in-exception-handling-mechanism
Ich könnte mich natürlich irren ...
quelle
try
Blocks überlebt fälschlicherweise, sodass die Fehlerfunktionen ihre Werte zurückgeben können, und eine weitere Konsequenz wäre, dass ein Fehler die Ausführung in den Handler des irrtümlich überlebendentry
Kontexts bringt .