CATCH Brain-freeze - Forum - OpenEdge Development - Progress Community
 Forum

CATCH Brain-freeze

  • Hi.

    OE 11.3.2.

    Why is the following code displaying both my caught error AND then the standard error output?  Shouldn't the CATCH cause the other to be supressed?

    DEFINE TEMP-TABLE ttRow
      FIELD RowIdent AS CHARACTER
      INDEX ttRow1 IS UNIQUE RowIdent.
    
    DEFINE VARIABLE i AS INTEGER NO-UNDO.
    
    FOR EACH sports2000.customer NO-LOCK ON ERROR UNDO, THROW:
      CREATE ttRow.
      ASSIGN ttRow.Rowident = "1".
    END.
    
    CATCH e AS PROGRESS.Lang.SysError:
      MESSAGE "I got it! " SKIP(1) e:GetMessage(1) VIEW-AS ALERT-BOX.
    END CATCH.


    Jeff Ledbetter

    Roundtable Product Architect

    www.roundtable-software.com

  • The second error is thrown because the bad ttRow record still exists when the procedure (and its temp-table) goes out of scope. You must delete any bad records before they go out of scope to avoid this.  

  • Why is the UNDO not UNDO'ing the temp-table create? The temp-table is /not/ defined with NO-UNDO so I would have expected it to be UNDOne?

  • Because you don't have a transaction.

    Change your NO-LOCK to EXCLUSIVE-LOCK and you have a transaction and temp-table is being UN-DONE

    /Torben

  • hmm

    it does have a 'create' there, sure it must be a transaction block only not sure what Stefan expected to be rolled-back... running the code I only end-up with one record in temp-table and that is for me the expected result (for each transaction in on every block iteration, only second record created gets roll-back)

    why the error seems to still propagate even if was catch already escapes me, maybe a nasty bug? :)

  • Create need to be on a 'real' database object not a temp-table to initiate a transaction!

  • Interestingly, if I change it as below, I just get the catch error.

    DEFINE TEMP-TABLE ttRow 
      FIELD RowIdent AS CHARACTER
      INDEX ttRow1 IS UNIQUE RowIdent.
     
    DEFINE VARIABLE i AS INTEGER NO-UNDO.
     
    do transaction ON ERROR UNDO, THROW:
      FOR EACH customer NO-LOCK ON ERROR UNDO, THROW:
        CREATE ttRow.
        ASSIGN ttRow.Rowident = "1".
      END.
    end. 
     
    CATCH e AS PROGRESS.Lang.SysError:
      MESSAGE "I got it! " SKIP(1) e:GetMessage(1) VIEW-AS ALERT-BOX.
    END CATCH.
    

    Edit: I should add I'm on 11.2.1

  • Which was explained by Torben - I did not have a transaction to be undone. If you put the do transaction around the create and assign of the temp-table you also have enough transaction.

    The amount of additional undoing and throwing always throws (pun intended) me off structured error handling.

  • DEFINE TEMP-TABLE ttRow
      FIELD RowIdent AS CHARACTER
      INDEX ttRow1 IS UNIQUE RowIdent.
     
    DEFINE VARIABLE i AS INTEGER NO-UNDO.
     
    FOR EACH sports2000.customer NO-LOCK ON ERROR UNDO, THROW:
      DO transaction ON ERROR UNDO, THROW:
        CREATE ttRow.
        ASSIGN ttRow.Rowident = "1".
      END.
    END.
     
    CATCH e AS PROGRESS.Lang.SysError:
      MESSAGE "I got it! " SKIP(1) e:GetMessage(1) VIEW-AS ALERT-BOX.
    END CATCH.
    

  • Note that one can use the TRANSACTION keyword directly on the FOR EACH block.  

    Also note that this issue is independent of the structured error handling. If you remove the catch block you will get more instances of the message. If you remove the ON ERROR , THROW entirely the infinite loop protection seems to get confused (you can get out with a couple of breaks).  

  • Thanks everyone. Adding the TRANSACTION to the FOR..EACH scope does indeed resolve. I won't pretend to understand exactly why, but it does. :)

    Jeff Ledbetter

    Roundtable Product Architect

    www.roundtable-software.com

  • The behavior of this code would be the same whether you were using structured error handling or not.  The multiple errors come from the fact that the record create and update were not undone - as you all have said.  It is not related to the fact that the error is being thrown or re-thrown.  For example, if you changed ON ERROR UNDO, THROW to ON ERROR UNDO, LEAVE and you removed the CATCH block, you will see the error 3 times - once on the ASSIGN line where the error is first raised, once when you leave the FOR EACH block and once when you leave the PROCEDURE block (which is the transaction block unless you explicitly code otherwise).  

    So I'd be interested to hear more details on your statement that the additional undoing and throwing has discouraged you from using structured error handling.

  • I agree that there is just as much undoing and leaving as there is undoing and throwing. In the first posts there were multiple (unnecessary) undo, throws which tripped me a bit (along with a missing transaction block)

    When CATCH was initially introduced I had hoped that the CATCH would be a more ultimate CATCH (for example at the entry point of an AppServer call), but each block needs to be catching and throwing. Maybe this is unrealistic, but it could have made CATCH a very powerful thing.

    Currently I'm only CATCHing OUTPUT TO statements which CATCH can deal with whereas NO-ERROR cannot.

  • Hi.

    "When CATCH was initially introduced I had hoped that the CATCH would be a more ultimate CATCH (for example at the entry point of an AppServer call), but each block needs to be catching and throwing."

    That has not been my experience when using the ROUTINE-LEVEL ON ERROR UNDO, THROW. For one of our products, we have a single appserver entry point that catches all App and System errors.

    There are some exceptions where we want to catch a specific error to handle it right away before it is thrown back up the stack but the ROUTINE-LEVEL definition seems to help with all the rest. I'm not sure if that is a best-practice, but it has worked for us.

    Jeff Ledbetter

    Roundtable Product Architect

    www.roundtable-software.com

  • > There are some exceptions where we want to catch a specific error to handle it right away before it is thrown back up the stack but the ROUTINE-LEVEL definition seems to help with all the rest. I'm not sure if that is a best-practice, but it has worked for us.

    +1 . FWIW I follow the same approach that Jeff does, with the minor exception that in 11.3 I'll start using the new BLOCK-LEVEL statement which is a superset of ROUTINE-LEVEL.
     
    There are a couple of places you should have catches:
     
    -  Currently you need to add a CATCH on the 'service interface' program on the AppServer (ie the program being called on the AppServer) to catch any errors thrown by the logic called there (since those errors cannot yet be returned/thrown across the AppServer boundary).
     
    - If you're using GUI for .NET , you need catch blocks in your ABL event handlers since it's not currently possible to have a global catch during a WAIT-FOR.
     
     
    *
    Instead of adding the statement to source-code files, you can use the -undothrow 1 startup parameter to change the default error-handling on routine-level blocks to UNDO, THROW during compilation. See the OpenEdge Deployment: Startup Command and Parameter Reference for more information.
    *
    The BLOCK-LEVEL ON ERROR UNDO, THROW statement can be used if you want to change the default error-handling on REPEAT, FOR, and DO TRANSACTION blocks in addition to routine-level blocks. (You can use the -undothrow 2 startup parameter to change the default error-handling to UNDO, THROW on every block affected by the BLOCK-LEVEL statement during compilation.)
     
    -- peter
     
     
    From: Jeff Ledbetter [mailto:bounce-jeffledbetter@community.progress.com]
    Sent: Wednesday, 05 March, 2014 12:02
    To: TU.OE.Development@community.progress.com
    Subject: RE: CATCH Brain-freeze
     
    Reply by Jeff Ledbetter

    Hi.

    "When CATCH was initially introduced I had hoped that the CATCH would be a more ultimate CATCH (for example at the entry point of an AppServer call), but each block needs to be catching and throwing."

    That has not been my experience when using the ROUTINE-LEVEL ON ERROR UNDO, THROW. For one of our products, we have a single appserver entry point that catches all App and System errors.

    There are some exceptions where we want to catch a specific error to handle it right away before it is thrown back up the stack but the ROUTINE-LEVEL definition seems to help with all the rest. I'm not sure if that is a best-practice, but it has worked for us.

    Stop receiving emails on this subject.

    Flag this post as spam/abuse.