Structured Error Handling

Posted by Cherian George on 05-Dec-2019 19:41

Following code does not compile because there is a second for each after the catch/end catch block.  I think that is a pretty big weakness in structured error handling and what I have shown below is a valid use case. 

Is any body working on fixing this issue ?? 

DEFINE TEMP-TABLE ttdata NO-UNDo
      FIELD F1 AS CHAR.
     

DEFINE TEMP-TABLE ttdata2 NO-UNDo
      FIELD F2 AS CHAR.   
     
DEFINE TEMP-TABLE ttdata3 NO-UNDo
      FIELD F2 AS CHAR.        
B1:     
for each ttdata ON ERROR UNDO, THROW :
    for each ttdata2 on error undo, throw :
    end.
    catch e AS Progress.Lang.Error    :           
       next b1.
    end catch
    /*This will not compile because nothing is allowed after catch statement */
    for each ttdata3 no-lock :    
    end.    
END.
CATCH e AS Progress.Lang.Error    :           

END CATCH. 

All Replies

Posted by onnodehaan on 05-Dec-2019 19:44

Have you looked into

Finally:

End finally.

Posted by Laura Stern on 05-Dec-2019 19:59

Sorry, I don't understand what the use case is.  Hard to comment on this without knowing what you're trying to do.

Do you want a catch block for the first for each, but not the second?  If so, then put the catch in the for each block that you want it to pertain to. i.e.,

B1:

for each ttdata ON ERROR UNDO, THROW :

   for each ttdata2 on error undo, throw :    

       catch e AS Progress.Lang.Error    :          

          next b1.

       end catch.

   end.

   /*This will not compile because nothing is allowed after catch statement */

   for each ttdata3 no-lock :    

   end.  

END.

You can get it to do whatever you want.  If you want both for each statements to be governed by the catch, then move the catch to the outer for each:

B1:

for each ttdata ON ERROR UNDO, THROW :

   for each ttdata2 on error undo, throw :    

      ...

   end.

   /*This will not compile because nothing is allowed after catch statement */

   for each ttdata3 no-lock :    

   end.  

   catch e AS Progress.Lang.Error    :          

       next b1.

   end catch.

END.

If not one of those, then what are you trying to accomplish?

Posted by ssouthwe on 05-Dec-2019 20:09

If you want the first catch to apply only to the first for-each block, you can wrap the block in a DO: block and put the catch right before the END. statement.

Posted by Shelley Chase on 05-Dec-2019 21:58

It is intentional that once an error is thrown, the block execution is done. The compile throws and error because that code would never be reached. You have two choices as already mentioned:

- Put you code after the catch into a finally block

- Put your first for each in a try..catch block

Thanks

-Shelley

Posted by Cherian George on 06-Dec-2019 06:22

Let me try again
 
As you can see I have two For Each Blocks (call it Child1 & Child 2)  that is nested inside a parent For Each Block (Call it Parent1). So if an error is raised inside Child 1 then I want to handle it and then still proceed to Child 2.     – I hope I explained it this time 😊.
 
Using a Finally Block is an option but it is not something that I would have needed to do in other languages like C# and I am wondering why in ABL we cannot do the same as in C#.
 
Many thanks for your valuable knowledge and insights .
 
Cherian
 

Posted by Simon L. Prinsloo on 06-Dec-2019 09:18

To achieve that, you should put the catch block inside the child1 for each and simply leave the child1 block in oder to proceed to child to.

When structured error handling was introduced, Progress had a dilemma. In a procedure file, the file itself is an implicit procedure block, so to put the catch after the block, as is traditional with C#, Java, Delphi etc. was not possible, because you cannot put it after the end-of-file, which marks the end of the implicit procedure block.

The only possibility was to put the catch at the end of the file. This had a ripple effect for all other blocks. If you would put the catch after an explicit block, like a for each, and the for each is the outer most block in the procedure file, how would the compiler know if the catch applies to the procedure block of the file or to the for each?

So they had to put the catch block INSIDE the block where the error needed to be trapped in all cases. The same goes for a finally.

Technically, any block ends right BEFORE the first catch (or finally) in the block, so inside these blocks transactions have been rolled back (or committed), (some) locally scoped stuff are out of scope etc. You can think of it as the compiler moving the catch/finally blocks down to after the (implicit or explicit) end of the block where you coded it. But for that to work, you cannot have any code between the catch/finally blocks and the end of the block.

Posted by frank.meulblok on 06-Dec-2019 09:21

- Using a FINALLY block should not be considered here. Those are meant for cleanup, not for functional logic.

- As [mention:c53dde151a18454c953485683d3b91d4:e9ed411860ed4f2ba0265705b8793d05] pointed out, you can always use another block to handle the scope of your error handling.opt to

- If you want your code to proceed with the next child, your catch should not contain statements that tell it to proceed with the next parent.

This should be close to what you're after:

DEFINE TEMP-TABLE ttdata NO-UNDo
      FIELD F1 AS CHAR.
      
 
DEFINE TEMP-TABLE ttdata2 NO-UNDo
      FIELD F2 AS CHAR.   
      
DEFINE TEMP-TABLE ttdata3 NO-UNDo
      FIELD F2 AS CHAR.        

CREATE ttdata.
CREATE ttdata2.
CREATE ttdata3.

B1:     
for each ttdata ON ERROR UNDO, THROW :
    MESSAGE "found parent".
    
    DO ON ERROR UNDO, THROW:
    
      for each ttdata2 on error undo, throw :
        MESSAGE "found ttdata2".
        MESSAGE DATE("20/20/2020"). /* make an error happen */
      end.
      CATCH e AS Progress.Lang.Error    :           
         /*next b1.*/
      END catch.
    END.
    
    for each ttdata3 no-lock :    
    MESSAGE "found ttdata3".
    end.  
    
END.
CATCH e AS Progress.Lang.Error    :           
 
END CATCH. 

Posted by Laura Stern on 09-Dec-2019 14:06

Simon described it correctly.  But the other thing is that a CATCH block handles the error for one iteration of an iterating block (e.g., a FOR EACH).  So it will not cause the loop to end.  It will undo that iteration and move on to the next iteration. Here's what you need if you want to get out of the first inner "for each" when an error happens. :

for each ttdata ON ERROR UNDO, THROW :

  Loop1:

  for each ttdata2 on error undo, throw :    

      catch e AS Progress.Lang.Error    :          

          leave Loop1.

      end catch.

  end.

   for each ttdata3 no-lock :    

  end.  

end.

This thread is closed