Der CATCH-Block wird ausgelöst, wenn dies nicht der Fall sein sollte


7

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

GOTOAnweisungen können verwendet werden, um zu einer Beschriftung innerhalb desselben TRYoder CATCHBlocks zu springen oder einen TRYoder CATCHBlock 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 catchBlock 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 trans oder rollback transs 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 tryBlock "normal" verlässt . Interessanterweise machte das keinen Unterschied.

Was zum Teufel ist los und wie geht das richtig?


2
Ich bin nicht die „Outer - Handler ..“ Sehe Fehler auf SQL Server 2012, aber ich bin es auf SQL Server 2008 R2 Express zu sehen.
gbn


1
@gbn Wenn dies speziell auf Transaktionen angewendet wird, ist dies der Fall (die Vorlage wird jedoch etwas komplexer, da Fehler auftreten, die auftreten können 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 danach commit_and_exit:wird zweimal ausgeführt.
GSerg

@gbn Wollen Sie damit sagen, dass dies ein Fehler im Jahr 2008 ist, der im Jahr 2012 behoben wurde? Gibt es einen Eintrag bei connect?
GSerg

Antworten:


10

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 ...


Für mich ergibt das Sinn. Der Kontext eines tryBlocks ü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 überlebenden tryKontexts bringt .
GSerg
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.