Trapping Error Messages Associated With STOP Condition

Posted by Rob Straight on 21-Sep-2016 13:31

Introduction

Progress has gotten several requests recently to have an ABL mechanism for trapping error messages associated with STOP conditions. After investigating this from several angles, we have come up with the following proposal and we'd like your feedback.

Please note:

  • This feature is currently not planned for any specific OpenEdge release or time frame.

  • Implementation of this feature as described below will introduce a change in behavior in your existing OpenEdge applications. Any CATCH block you have for either Progress.Lang.Error or Progress.Lang.SysError will now also catch most Stop conditions (the ones with error messages).

 

Proposal

We would introduce 2 new built-in objects:

               Progress.Lang.StopError (aka P.L.StopError)

This object would inherit from Progress.Lang.SysError and implement Progress.Lang.Error.

This would be associated with any STOP condition that has error messages.

               Progress.Lang.Stop (aka P.L.Stop)

This object would inherit from Progress.Lang.Object.

This would be generated by the STOP statement, CTRL-C, or a timeout from STOP-AFTER. When possible there will be a property in P.L.Stop that indicates what caused the STOP condition.

You would be able to code CATCH blocks using either of these new object types. Existing CATCH blocks will experience a change in behavior because P.L.StopError implements Progress.Lang.Error and inherits from Progress.Lang.SysError, so that any CATCH block you have for either of these will now also catch most STOP conditions as well (the ones with error messages). Note that you must recompile your code in order to experience this new/changed behavior.

STOP conditions still bubble up by default just as they do today. If there is a CATCH block that can catch P.L.StopError or P.L.Stop, a STOP condition that occurs many levels down the call stack will bubble up and be caught by that CATCH block (if there are no intervening ON STOP directives that trap it).

ON STOP block directives will work just as before. However, if there is a CATCH block on a block that also has an ON STOP directive, the CATCH block will take precedence (Just as CATCH blocks do today on blocks with ON ERROR directives).

There are many STOP conditions that today force execution to bubble up to the top of a transaction or even to a level that is above any database connections. With any such condition, ON STOP directives are ignored until that safe level is reached. This will still be true, and now any relevant CATCH blocks may also be ignored until the safe level is reached.

Questions

1. Is it a good thing that a CATCH for Progress.Lang.Error (etc.) can catch STOP conditions associated with error messages? Is this what you would expect, or would you rather have to code explicitly to catch any STOP condition?

2. Do you see an issue in that STOP can possibly get caught before it would have before, which is either never caught or at a DO ON STOP higher up the call stack?

3. Is there anything else that OpenEdge Engineering should consider?

All Replies

Posted by Mike Fechner on 21-Sep-2016 13:51

Hi Rob, I'll have to think more about the implications of your questions. But the most important thing is, that we can distinguish different STOP causes, especially STOP-AFTER and LOCK-WAIT-TIMEOUT. RUN a missing .p, CRC mismatch etc. are installation problems. And STOP-AFTER and LOCK-WAIT-TIMEOUT are caused by the application behavior, especially STOP-AFTER is more a feature than an issue.

So I'd like to see a StopAfterError inheriting from Progress.Lang.Stop or Progress.Lang.StopError to be able to handle that specifically (very close to an OPEN-QUERY statement causing the STOP-AFTER).

Posted by cwright on 21-Sep-2016 13:55

This would a great feature as currently CATCH does nothing with a STOP condition.

1. I think that CATCH should do just that; CATCH all error and stop conditions.

2. I think you can refactor to avoid the DO ON STOP higher in the call stack

3. Engineering needs to make sure that the finally block executes before the final STOP happens. This allows good clean-up.

My 2 Cents

Posted by dbeattie on 21-Sep-2016 14:30

I like it and have wanted this since CATCH was introduced, maybe longer. You might want something (client startup) that would revert to current behavior and not catch STOP conditions. I'm sure you can get this in 11.7.

Posted by marian.edu on 22-Sep-2016 00:20

That’s a very good idea Rob, everyone with some OO background gets bite by this one when dealing with OO ABL :(


I’m with Mike on having distinct errors for different causes: stop after, class(procedure) not found, I/O, lock timeout… but that will probably require more changes and there might not be many using that fine granularity level when catching errors.

What intrigues me is you mention (several time) that this will work only for 'stop conditions that have error messages', what else is left out of this, something that we can still catch with on-stop or things that should never happen like GPF?


Marian Edu

Acorn IT 
+40 740 036 212

Posted by David Abdala on 22-Sep-2016 05:36

Hi all,

I must disagree with the implementation proposal, due to the fact that it changes CATCH semantics.

What I see is two different things when it comes to the STOP:

- The STOP condition

- The ERROR that raised the STOP condition

The STOP condition, for me, *is not an ERROR*, it may be the result of an ERROR. I think you already realized it by proposing two different objects to handle STOP conditions.

Making actual CATCH blocks catch STOP changes CATCH semantics, and I don't like it.

I think you should handle the ERROR that raises the STOP condition (if exists) with current ERROR semantics, that is, with an CATCHable object, that will get caught by existing CATCH blocks, catching base error objects.

BUT the STOP condition should behave as it does, bubbling up, until finding a specific catch block for the STOP, once in a 'secure level'.

The general behavior I want is:

- An STOP condition is raised

- If there is an error associated to the STOP condition, then it will be caught by the corresponding CATCH block, honoring every FINALLY along the way

- The STOP will keep bubbling up until a CATCH STOP is found, honoring every finally block along the way.

Alternative paths:

- If no CATCH STOP is found, current STOP behavior happens, honoring FINALLY blocks

- If no CATCH for the STOP ERROR is found, current *uncatch* behavior happens, honoring FINALLY blocks

So, in the end, you will have to explicitly code for STOP conditions, avoiding getting bitten by the semantic change in CATCH.

Take into account I'm not really versed in STOP condition handling, and that my main goal is to avoid semantic changes.

Posted by marian.edu on 22-Sep-2016 06:26

David,


the catch block did changed the on-error semantics already, it’s hard sometimes to move forward if we keep on looking behind :(

For me stop was always some sort of error, just found out there is actually a ‘stop’ statement so one can raise the stop condition from 4gl… and yes, no message is being given so that might count as one of those cases when stop occurs without an error message. Wonder if anyone actually uses that and if yes what the use case was, why not simply returning error for instance? It’s like a ‘fatal error’ that should bubble out till catch with on-stop else will just restart from the start-up program.

No matter how you look at it a stop condition is a signal of an error and the fact that you need special handling out of catch block for that is confusing for anyone that has any prior experience with try/catch out of the 4gl world. 

Given Rob’s proposed solution if you really want to let  the stop bubble out of the catch block just add a stop statement at the end of that block, chances are those that started to use try/catch already work around stop conditions so the semantic change will most probably be in-line with what they wanted in the first place but had to add unnecessarily on-stop undo, retry and check for retry in the main block.


Marian Edu

Acorn IT 
+40 740 036 212

Posted by Simon L. Prinsloo on 22-Sep-2016 08:55

I distinguish three types of STOP conditions, each with a unique need::

1. STOP-AFTER - an expected condition that is foreseen by the developer and, by design, is not nearly as severe as any other STOP condition..

2. STOP raised by conditions that can potentially be recovered from - e.g. a premature menu update give the user an option for which the r-code has not been deployed - no need to kick the user, just tell him "sorry" and life continues.

3. Rather severe STOP conditions, e.g. stack overflows, nesting to deep or loosing the db connection and thereby invalidating some programs in memory.

The first type is, for me an "END-ERROR" type of scenario, in that it is expected to happen and must detectable and handled as part of normal program flow. The described behaviour in the proposal will be usable, but the proposal is a bit thin on the ability to distinguish this condition from other STOP conditions.

The second type is simply a mislabeled ERROR and the described proposal would finally fix that.

The third type is more appropriately handled by the current behaviour of the STOP condition. Note that the "classification" of such a condition may change as the stack unwinds - i.e. when a DB is disconnected, the stack needs to behave as it currently does as long as any program references that database. But once the stack unwound to a level where there is no more references to that database, it should be re-classified to the second type, since we could potentially reconnect the database (a batch job can typically keep trying every x minutes until the db is back and continue its work) or terminate gracefully.

As I understand the proposal, it will basically work in the way described for the third type as well.

What I am missing and am unsure of, is the following two cases:

1. User hit CTRL-C / CTRL-BREAK - This should probably be handled like STOP-AFTER, in that a human decided that a code block running for some arbitrary time period took too long. The same probably goes for a lock wait timeout raised by the database. 

2. The STOP statement. My only use case for the STOP statement is that I sometimes use it during testing or debugging as an quick and easy way to force a "rollback, unwind the stack and terminate" in order to preserve the state of the system from one run to the next, so that I can re-run without the need to change data back.  I cannot think of a single case in a production setting in the more than 20 years I am using the ABL where STOP would be an appropriate instruction. Thus, for this STOP statement, I would typically prefer that its behaviour remains unchanged. (Or classify it to behave more like the third type than the second.)

Posted by Thomas Mercer-Hursh on 22-Sep-2016 09:37

I think David is on the mark here in separating the cause from the stop condition itself.  Some of the things which produce the stop condition now are really things that should be trappable.  Give us the ability to trap them and we don't need for it to proceed to stop.  Genuine stop should be reserved for Simon's type three conditions, i.e., something has happened that cannot be sensibly be recovered from.  Then, one wants to capture as much information about what happened in a log and then give up.  Even a missing .p or .cls should be trappable.  Yes, there are some places where if one is missing one doesn't have a very graceful way to recover, but at the least one can give a useful message, log some things, and then fall back to a higher level.  Other places, the missing .p or .cls is just a missing option.

Posted by Laura Stern on 26-Sep-2016 12:30

Re the post by cwright on 9/21 @ 14:55:  Yes, if there is a FINALLY block associated with the block that has a CATCH block for STOP, it will execute. This is similar to what happens today with this code - the finally will run:

/* You shouldn't need the ON ERROR part, but currently you do to even compile the FINALLY block */

DO ON ERROR UNDO, LEAVE ON STOP UNDO, LEAVE:

   STOP.

   FINALLY:

       MESSAGE "Finally"

           VIEW-AS ALERT-BOX INFORMATION BUTTONS OK.

   END.

END.

Posted by Laura Stern on 26-Sep-2016 12:39

Re Marian Edu's post on 9/22 at 1:20: Re your last paragraph - we did not say this only works for 'stop conditions that have error messages'.  We said "You would be able to code CATCH blocks using either of these new object types." (i.e., P.L.Stop or P.L.StopError.  But only the latter implements Progress.Lang.Error - thus only stop conditions with error messages are the ones trappable by a CATCH block for Progress.Lang.Error or Progress.Lang.SysError.  

You would be able to use a CATCH block explicitly for P.L.Stop to trap STOP raised by the STOP statement, STOP-AFTER or CTRL-C and also for lock wait time-out (which is basically CTRL-C).  

There are other things like GPFs or Out of memory that are not trappable today with DO ON STOP and that will continue to be the case with this new feature.  They will not be trappable.

Posted by Laura Stern on 26-Sep-2016 13:02

Re David Abdala's post on 9/22 @ 6:36: Marian already responded to this.  And I agree with her assessment that stop is really some kind of error.  It has always been a bit murky as to why one error condition raises ERROR and another raises STOP.  Sometimes the rationale made sense 30 years ago, but no longer does (e.g., RUN lskdjlfj.p).  

Also, the basic semantics of a CATCH block is that once the CATCH block runs the condition is cleared.  To not do that with STOP conditions seems very confusing.  It is also problematic in that there can be code in the CATCH block that directs the AVM to take some further action, like the NEXT statement.  What would we do if you coded NEXT?  Do we do honor the NEXT or do we bubble up the STOP condition?  It really doesn't make much sense.

I do understand you're wanting to run FINALLY blocks.  We certainly could not do that for cases where we  need to get out of a transaction or DB connection.  But for other blocks -  should we be honoring FINALLY blocks?  I'm not sure that is doable now, but it's an interesting idea.

Posted by jquerijero on 26-Sep-2016 13:26

This seems reasonable. Just add the class name of the error that was thrown to the error messagebox if it wasn't handled. :)

Now let's move on making THROW also consistent, and do away with the inconsistency producing statement BLOCK-LEVEL ON ERROR UNDO, THROW.

Posted by marian.edu on 26-Sep-2016 13:26

Laura, this is from Rob’s original post…


"Any CATCH block you have for either Progress.Lang.Error or Progress.Lang.SysError will now also catch most Stop conditions (the ones with error messages).

So I was just confused, beside stop statement does not have any option to set a message… if we take out the “comments” between parentheses the proposed solution sounds good to me.


Marian Edu

Acorn IT 
+40 740 036 212

Posted by David Abdala on 26-Sep-2016 13:53

I'm certain I wasn't clear enough.

I think (now) the problem resides in STOP having multiple meanings. I think this is the right opportunity to fix this, and make STOP a unified concept of: execution can't continue due to a general failure condition with AVM, stack is poped up to a safe state, where execution can continue.

So, there are several ERRORs that raised the STOP condition, that now should not do that, they should be just ERRORs following the same rules as any other ERROR. In the other hand you have the STOP condition, that can be set up by a STOP statement, Ctrl+C, a Memory Access Violation, etc. that behaves exactly as STOP does today, ignoring CATCH, and ignoring FINALLY, up to a safe level, this requires explicit coding to be handled, as shouldn't be trapped by "normal CATCH".

There is a "middle ground" approach, where a STOP condition stops execution up to a safe stack level, and at that level it becomes an STOP-ERROR that can be CATCH by normal error handling.

I believe the latter is the way to go, as almost "honors" current behavior, and also "honors" error handling behavior.

Posted by GSchug on 27-Sep-2016 07:06

1. Is it a good thing that a CATCH for Progress.Lang.Error (etc.) can catch STOP conditions associated with error messages? Is this what you would expect, or would you rather have to code explicitly to catch any STOP condition?

Don't want to code STOP explicitly. Don't care for difference between old ERROR and STOP. Both are the same to me (at a first glance). Of course in special cases, I'd like to differentiate, but in 99% of the cases, there's no difference for me.

2. Do you see an issue in that STOP can possibly get caught before it would have before, which is either never caught or at a DO ON STOP higher up the call stack?

no

3. Is there anything else that OpenEdge Engineering should consider?

no. I'd need that feature very soon. We've tried to introduce structured error handling in the application and still have this ugly "do on stop" statements. Buuuh.

Posted by gus bjorklund on 27-Sep-2016 14:32

Some of the causes of fatal errors do not have any safe way to continue or to give control back to the 4GL.

And there must be at least one sure-fire way to kill a process when the application will not or actively refuses to stop.

What should be done for those situations?

Posted by Laura Stern on 27-Sep-2016 15:27

We're only talking about STOP conditions that have been trappable in the past.  i.e., You can already trap (and therefore clear) the STOP condition with a DO ON STOP statement.  We are not proposing any changes to the kind of fatal errors you are talking about.

Posted by Laura Stern on 27-Sep-2016 15:45

Re Marian's post on 9/26 at 14:26: Sorry Marian, but I'm now more confused than before!  i.e., I'm not sure if we are in sync or not.  I don't understand why you said that if we take out the comment about "the ones with error messages" that you like the solution.  

At the risk of repeating myself: Certain STOP conditions intrinsically have error messages associated with them.  The STOP statement is not one of those.  And I agree (with someone above; can't find who) I don't know if anyone actually uses this except for testing or experimenting.  Nevertheless, it exists.  The STOP conditions from STOP-AFTER or CTRL-C also have no error message.  So only STOP conditions with errors can be associated with a P.L.StopError object, which implements P.L.Error.  If there is no error message, it doesn't make sense to map the condition to an interface designed for giving you access to messages and message numbers.  

But we know people would still like to trap the STOP from a STOP-AFTER or a lock wait time-out, etc.  So you can instead catch those by using a CATCH for P.L.Stop. (or some sub-class as a couple of people proposed).  In these cases, there is no message, but you should be able to find out what caused the stop condition via a property of the P.L.Stop object (or by the class type if we use sub-classes).

Posted by jquerijero on 27-Sep-2016 15:54

1. Is it a good thing that a CATCH for Progress.Lang.Error (etc.) can catch STOP conditions associated with error messages? Is this what you would expect, or would you rather have to code explicitly to catch any STOP condition?

>>> A Progress.Lang.Error derived class for STOP with error should be fine. If Progress.Lang.Error is intended to be the base class of all ABL related errors, I would expect Progress.Lang.Error to catch all trappable errors. Having a separate base class for STOP with no message is OK, although I think it is still superfluous (see next answer).

2. Do you see an issue in that STOP can possibly get caught before it would have before, which is either never caught or at a DO ON STOP higher up the call stack?

>>> It might be a good information to know if the error is just a STOP condition, but I really don't see a difference in handling a standard error (or even STOP with message) and STOP condition. Also, the object type is more important than the message itself, so an instance of P.L.Error with blank message is acceptable.

As for the STOP behavior, if I really need to bubble up an error that is already caught, I think implementing a re-THROW (THROW without parameter) that can be included inside the CATCH is fine.

3. Is there anything else that OpenEdge Engineering should consider?

>>> A more consistent THROW.

Posted by Thomas Mercer-Hursh on 27-Sep-2016 16:21

It might be worth tabulating all of the possible sources of a STOP condition and clearly identifying 2 properties - whether they are reasonably trapable and whether or not there is an associated message.  Then, review the ones without messages and ask yourself if it is appropriate that it have no message.   One of the issues here is the extent to which the condition is likely to occur in normal processing.  STOP-AFTER, for example, is not really a condition where one would expect to bail out, but rather one in which one might like to give information to the user and ask for a course of action.  It seems like it would be easy enough to create a useful message to convey what happened.

Posted by jquerijero on 27-Sep-2016 16:47

Almost always you don't display the message associated with an error verbatim in an application. So to me, knowing the object type is more important than having a message associated with an error. Basically, a P.L.Error derived class for STOP with no message will still work even if the message property is blank.

Posted by Thomas Mercer-Hursh on 27-Sep-2016 16:52

To be sure, it is my style to program in such a way that the source of the error should be obvious whether there is a nice message or not, but it seems to me that "Query stopped after N seconds", for example, is a trivial addition and adds clarity.

Posted by jquerijero on 27-Sep-2016 17:08

That's true.

If it's my decision, I will go as far as assigning each error (or at least, each family of error) its own super-class to make the structured error handling more robust. But that might be more of a wishful thinking.

Posted by gus bjorklund on 27-Sep-2016 17:34

wouldn’t that make exception handling a lot more complex?

>

> If it's my decision, I will go as far as assigning each error (or at least, each family of error) its own super-class to make the structured error handling more robust. But that might be more of a wishful thinking.

Posted by marian.edu on 28-Sep-2016 00:04

Not a bit, if one doesn’t care about different types of error the code block could throw away catch-all Progress.Lang.Error is all that is needed.


I’m definitively for identifying errors by type whenever possible but introducing that in AVM generated errors would probably too much of a stretch so we can live with SysError when it comes of errors generated by AVM.

What I would love to see implemented is an option to add ’throws’ information of method/function/procedure definition so one knows what kind of errors to expect (extend the IDE to support that with some code completion will be nice as well).

 
Marian Edu

Acorn IT 
+40 740 036 212

Posted by marian.edu on 28-Sep-2016 00:10

Laura, if you look at the first post Rob uses exactly that phrase in there a few times… so for me, that sounded like the proposed solution only applies to stop conditions that does not have a message, from what you say this is not the case and everything that we can now trap with `do on error` will be catchable and I’m perfectly fine with that.


As Thomas said there are probably only a hand full of ’stop conditions’ that can occur - and we can catch - so I would say different subtypes of P.L.S errors would be nice and don’t bother with the extra property.

  
Marian Edu

Acorn IT 
+40 740 036 212

Posted by marian.edu on 28-Sep-2016 01:38

On a second thought… how about this slightly changed version?


Progress.Lang.Exception
New root interface, Progress.Lang.Error will inherit from it.

Progress.Lang.StopError (aka P.L.StopError)

This object would inherit from Progress.Lang.Exception.

This would be associated with any STOP condition (anything really that doesn’t need failsafe approach), it might or not have any error messages.

Progress.Lang.TimeOutException

This object would inherit from P.L.StopError, fired when a timeout occurs on STOP-AFTER.

Progress.Lang.ClassNotFoundException (couldn’t find a more appropriate name, sic)

This object would inherit from P.L.StopError, fired when procedure not found on RUN or DYNAMIC-NEW.


No idea how easy this could be implemented, if possible, but it won’t change anything on existing code… aka, no stop conditions will be unintentionally catch by P.L.SysError or P.L.Error blocks. If we need to catch everything it’s just a matter of changing P.L.Error to P.L.Exception.


Marian Edu

Acorn IT 
+40 740 036 212

Posted by Thomas Mercer-Hursh on 28-Sep-2016 09:33

It is exactly the kind of categorization Marian suggests that I was hoping would fall out of tablulating all the possibilities.  In some cases adding a message to something not now having a message might allow more unitform handling, but one also might identify categories of response which deserve to either be a unique message type within a particular class or which even deserve being a separate class.  E.g., the P.L.TimeOutException could have a property for the number of seconds in the time out.

Posted by Simon L. Prinsloo on 28-Sep-2016 10:58

Will it also be possible to change the ON STOP phrase to allow intercepting the STOP and then throw an error instead, as described in this OpenEdge 11.6 Community Input ?

Posted by Laura Stern on 28-Sep-2016 13:21

There's nothing to change other than what has already been said.  If you CATCH the stop condition, the stop condition is cleared.  Then you can do anything you want from there.  If you want to throw an error, be my guest :-)

Posted by Rob Straight on 28-Sep-2016 16:23

Thank you for the excellent responses.

One additional question/clarification: this would likely be introduced as an "opt in" feature- the original behavior will stay in place unless you elect to go with the modified behavior. If you opt in, any code that does structured error handling needs to be recompiled (so to be safe, all of your source needs to be recompiled). Is there any issue/concern with a mandatory recompile of your application in something other than a major (OE10/11/12) release? We normally do not require a recompile in "point" releases (11.5, 11.6, 11.7, etc.).

Posted by jquerijero on 28-Sep-2016 16:31

Will you be able to re-throw the current stop condition (or any trappable error) inside the CATCH block just in case you need to bubble the stop condition?

Posted by Laura Stern on 29-Sep-2016 08:30

Yes, you will be able to re-throw the P.L.Stop or P.L.StopError object that you caught, which will again raise the STOP condition and will retain all the information in the existing object.

Posted by Fernando Souza on 29-Sep-2016 08:36

[mention:eec949bfa7364ddbb70465982d500e0f:e9ed411860ed4f2ba0265705b8793d05] , Could you elaborate a little bit more on what you mean by "more consistent THROW" ? What is the inconsistency you are referring to ?

Posted by jquerijero on 29-Sep-2016 09:36

This statement BLOCK-LEVEL ON ERROR UNDO, THROW. There is probably some functional requirements why it is there. However, it makes program behave differently. It's hard to tell sometimes what is going on when the program calls involve mixed programs with the statement specified and others not, why the throw is not being raised or caught. Can it be a standalone statement that can be called anywhere in the code to transfer control over to the structured error handling?

I'm not totally familiar with the BLOCK-LEVEL statement, but is this something that can be parameterized to apply to all programs. 

Posted by Fernando Souza on 29-Sep-2016 10:15

That was introduced as a convenience so that you would not have to change every block in your code to throw the error to the caller. By default, that doesn't happen so if you want to get an error to be thrown to the caller, you would add that to the top of your procedure and get that behavior.

Now there is a startup parameter named -undothrow that you can specify to change the default error-handling for blocks in ABL code. If you specify -undothrow 2, every single procedure/class that is compiled in that session will be compiled as if you had included the BLOCK-LEVEL statement in them.

Posted by gus bjorklund on 29-Sep-2016 15:17

Yaaaay.

Posted by jquerijero on 11-Oct-2016 10:21

And if ON STOP is not specified, how will it behave when a stop condition is reached?

Posted by Laura Stern on 11-Oct-2016 10:26

Not sure what you're asking.  If you don't use DO ON STOP, and don't have a relevant CATCH block, you will get the old behavior, i.e., STOP is raised and any message is displayed.  

Posted by jquerijero on 22-Dec-2016 12:33

I'm more thinking about the difference between two blocks with CATCH block but one with ON STOP and other has none.

Posted by Laura Stern on 22-Dec-2016 13:07

Not sure what you're thinking about, but with the new functionality, if the CATCH block is compatible with the stop condition, there is no difference.  i.e., The catch block takes precedence over any ON STOP phrase, so it doesn't matter if you have it or not.

Posted by jquerijero on 22-Dec-2016 13:16

That is what I'm looking for just trying to get a clarification about the role of ON STOP when a CATCH block is present. I was thinking ON STOP is an override to the CATCH block, and CATCH block will only catch STOP condition when there is no ON STOP.

Posted by Laura Stern on 22-Dec-2016 13:20

No.  It will behave the same as for errors.  CATCH blocks override DO ON ERROR phrases.

This thread is closed