Re: WHENEVER ANY ERROR
Posted in 1997
>From: Jim Young <jim_young@choicehotels.com> >Date: Fri, 08 Aug 1997 14:19:03 -0700 >X-Informix-List-Id: <news.41439> > >[...] > My approach to error handling is to use "whenever any error continue" > at the top of every 4gl module. I then use a library function that I > created to do the error handling. I write a lot of heavy duty data > manipulation apps that run against a 24x7 reservation system, so error > handling is very serious business over here. I default to WHENEVER ANY ERROR STOP, and around statements which I think might fail (and for which I'm prepared to write error handling code), I insert WHENEVER ANY ERROR CONTINUE and WHENEVER ANY ERROR STOP, inserting the status checking code at the appropriate points. That way, I get to find out about errors without having to test every statement, but I can still use fallible SQL statements, etc. Actually, I exaggerate slightly; I haven't done very much serious I4GL coding lately, and I used to use WHENEVER ERROR STOP explicitly, but I would now use WHENEVER ANY ERROR STOP unless I found that unbearable... I don't tend to test for things like failing to open a form; I assume that the environment is set up correctly, and that if it isn't, then I really don't have any alternative but to stop anyway. > In addition to the whenever statement, I also use "defer interrupt" and > "defer quit" in the beginning of the main function. Yes; me too. Though I tend to be less careful about checking QUIT_FLAG than INT_FLAG... >All sql statements follow this scenario: > if int_flag or quit_flag then > return false > end if > sql statement goes here > let g_error_code = status > call ck_error(g_error_code, sqlca.sqlerrd[2], NO_ROLL, ABORT, > "%M% %I% %C%) I'd make a couple of changes: IF INT_FLAG OR QUIT_FLAG THEN RETURN FALSE END IF WHENEVER ERROR CONTINUE # Change 1a ...SQL statement goes here WHENEVER ERROR STOP # Change 1b LET g_error_code = STATUS CALL ck_error(SQLCA.SQLCODE, SQLCA.SQLERRD[2], NO_ROLL, ABORT, "%M% %I% %C%") # Change 2 IF INT_FLAG OR QUIT_FLAG THEN # Change 3 RETURN FALSE END IF Change 1 reflects the difference between STOP and CONTINUE as the default error handling. Change 2 avoids problems with the volatility of STATUS (by using SQLCA.SQLCODE; your code is not significantly different, in practice). > If an interrupt/quit is trapped (kill -2 kill -3), the logic train > backs out of the calling functions back to the original calling > function which then calls an "orderly shutdown" function. Good. But change 3 above is there because you need to test after the SQL statement, because it is more likely to be a problem if the user spends a long time waiting for the SQL to complete. You'd also need to have consider whether to use OPTIONS SQL INTERRUPT ON or not. And checking INT_FLAG and QUIT_FLAG after an INPUT statement is even more important. > The error function returns without doing anything if the error > code is 0, or not found. If the error code is a real error, the > function displays a cleaned up error message (using err_get) and > module name, version #, and line #. Using SCCS -- neat; I've not used %C%, but that's a reasonable way to do so. Make sure you know whether the version of SCCS that you've got is year 2000 safe. Many are not. I discussed this earlier (May? June?) this year. For example, the Sun Solaris SCCS presently available is not Year 2000 safe; the Solaris 2.6 version is, and patches will be released later this year for older versions. I'm not willing to risk being stuck on a machine other than a Sun after the year 1999 with an old version of SCCS, so I've migrated almost all of my code to RCS (which uses 4-digits for the year already), despite my dislike of various other aspects of RCS. > Depending on the arguments passed, the transaction may be rolled > back and the application may be aborted. The messages displayed > all go to standard output which is redirected to append the log > file. Redirecting stdout is OK for batch mode programs but dubious for interactive programs. It will probably work because curses can make use of some dubious features of Unix i/o, such as the ability to write to stderr for the screen handling, or the ability to write to stdin (yes, stdin; try writing to file descriptor 0 -- it'll work on many (most? some?) systems). /* #include <unistd.h> -- for declaration of write() */ #define M "Hello World!\\n" #define F "** Failed**\\n" int main() { if (write(0, M, sizeof(M)-1) != sizeof(M) - 1) { write(2, F, sizeof(F)-1); return(1); } return(0); } > The drawbacks to this methodology are > 1. You're hosed if you forget to put ck_error() after every > sql statement - strange things happen when the program > continues after an error. That's why I use STOP rather than CONTINUE as the default! > 2. Using "any" in the statement means you better be careful > with assignments and any other statements that may > error. Yes; see also my comments above. > 3. Development of the application should proceed with the > "whenever" statement commented out until ready to test. With CONTINUE, probably; with STOP, no, it isn't desirable. You're saying that errors in the production system can be survived, even though the program does weird things when something goes wrong and the program continues. I take the view that I need to head off trouble before it happens. > In general, I like this method much better than any of the > previously mentioned methods I never use WHENEVER ERROR GOTO, if that's what you mean. I don't think continuing on an unexpected error is particularly good, which is why I recommend STOP rather than CONTINUE. > (and I hate the way errorlog formats the error messages and > doesn't tell you where the error occurred). When you use STOP, the error message often (but not always) contains both the file and the line number where the error occurred. Some errors are detected at points where it is difficult to identify the routine accurately. These tend to be errors that occur while evaluating expressions, for example. But an untrapped SQL error contains the source file name and line number... Calling ERRORLOG itself doesn't give you the line number and file name, which is a nuisance, but which is also rather difficult to overcome. Basically, I agree with a lot of what you said... Yours, Jonathan Leffler (johnl@informix.com) #include <witticism.h>