Home > Enterprise >  Why does TRY CATCH successfully update rows on SSMS but not when run from Tomcat application?
Why does TRY CATCH successfully update rows on SSMS but not when run from Tomcat application?

Time:09-14

I have a TSQL procedure, on SQL for Azure. Here is a minimal reproducible example.

CREATE PROCEDURE MyPROC
AS
BEGIN
    BEGIN TRY
        SELECT 1/0
    END TRY
    BEGIN CATCH
        UPDATE error_log SET error_desc='test'
    END CATCH
END

When run directly from SSMS, then error_log is successfully updated.

When run from my Tomcat application (using the Tomcat user) then the procedure is executed but error_log is not updated.

Tomcat executes the procedure using the following Java code

    DBConnection dbConn  = DBConnection.getInstance();
    Connection conn = null;
    try {
        conn = dbConn.getConnection();
        conn.setAutoCommit(false);
        CallableStatement stmt = conn.prepareCall("{CALL MyPROC}");
        stmt.execute();
        result = stmt.getInt(fields.size());
        if (result == DBProcedures.RESULT_FAILED) { // error
            conn.rollback();
        } else { // ok
            conn.commit();
        }
    } catch (SQLException e) {
        result = DBProcedures.RESULT_FAILED;
        try {
            conn.rollback();
        } catch (SQLException e1) {
            System.out.println("Exception on "   this.toString()   " "   e.toString());
        }
        System.out.println("Exception on "   this.toString()   " "   e.toString());
    } finally {
        try {
            conn.setAutoCommit(true);
        } catch (SQLException e) {
            System.out.println("Exception on "   this.toString()   " "   e.toString());
        }
        dbConn.returnConnection(conn);
    }

I have checked that the Tomcat user has permissions on the error_log table.

What am I missing? I understand that not all errors are caught by TRY CATCH and I understand that sometimes the transaction cannot execute any Transact-SQL statements that would generate a write operation. However my example does not seem to fall under either of these two categories. Further it does work under SSMS.

CodePudding user response:

This is caused by the fact that auto_commit is set to off, which means that the driver executes the command SET IMPLICIT_TRANSACTIONS ON;

You can see this in action in this fiddle.

To get around it, you can explicitly rollback the original transaction first. You should also ideally use XACT_ABORT to prevent hanging transactions, and use THROW; to rethrow the original error.

CREATE PROCEDURE MyPROC
AS
BEGIN
    SET XACT_ABORT ON;

    BEGIN TRY
        SELECT 1/0;
    END TRY
    BEGIN CATCH
        IF @@TRANCOUNT > 0
            ROLLBACK;

        UPDATE error_log SET error_desc = 'test';

        THROW;  -- only if you want the error to propagate back to Tomcat
    END CATCH;
END;

To be brutally honest, I do not recommend using BEGIN CATCH to log errors, as not all errors are catchable. Instead, use the SQL Server error log, or an XEvent session, to log errors.

  • Related