Table of Contents
Go Beyond FileMaker Pro's Error Reporting Mechanism
By David Lalonde, 2011-04-07
This is a technical article that talks about error handling in FileMaker Pro. This article describes error handling in general, describes FileMaker Pro's error reporting capabilities and limitations then details a complimentary mechanism that overcomes FileMaker Pro's error reporting limitations. The intended audience for this article is the advanced FileMaker Pro developer, however the author has done his best to write this article for all audiences.
What is error handling? Error handling is a mechanism in software code that deals with errors.
The word "error" is a bit of a misnomer in software development. It can, of course, mean that something went wrong. It can also mean, however, that a noteworthy condition or result exists. Few would argue that an illegal parameter is not an error. Many, however, could argue that an empty find result is perfectly valid and expected. FileMaker Pro treats both as errors. The word "error", in software development, then means both error and special condition.
Errors in FileMaker Pro can come from many sources and can occur at different times. They can come from the electronic equipment, from the operating system, from FileMaker Pro itself, from FileMaker Pro plug-ins or from external applications. They can occur when no file is open, when a FileMaker Pro file is idle, when a user does an action that does not invoke a script or calculation or when a script, calculation or custom function is being executed. FileMaker Pro developers can only deal with errors through scripts, calculations and custom functions. For that reason, this article will only deal with errors which can be dealt with in scripts, calculations or custom functions.
Why add error handling to software code? There are many answers to this question.
Imagine this scenario. There is a script. A
Set Error Capture script step is set to
Off early in the script (or it is simply not
On). During execution, a perform find returns no record. This results in a FileMaker Pro error. The user sees a FileMaker Pro message saying "No records match this find criteria." and is given the option to modify the find criteria, continue the script execution or cancel the script execution.
Does the user know what a record is? Should the user be able to modify the find criteria? Should the script continue when nothing is found? Should the user have the option to cancel the script's execution? Should the user even see a message when nothing is found? A no to any of these questions is a reason to add error handling to the scenario's script so that something be done about a zero found count.
Programmers should all strive to produce software that deal gracefully with errors. Gracefully, in this case, can mean in such a way as to preserve data integrity, to maintain data security and to ensure a user does not feel lost. Error handling is an important way to deal gracefully with errors.
FileMaker Pro's Error Reporting Mechanism
FileMaker Pro programmers are expected to prevent errors where possible and to test for errors in scripts, calculations and custom functions when necessary. Programmers can only test for errors in FileMaker Pro by reading function return values. All functions in FileMaker Pro return values. FileMaker Pro's documentation indicates what constitutes error values in return values. Using return values to indicate errors, however, has its limits.
In some cases. an error result can be a valid non-error result. For example, what result would indicate an error for the
GetField function? Even an empty result could be a valid result. The function
EvaluationError is FileMaker Pro's answer to this problem.
EvaluationError returns an error code for a supplied calculation.
Then there is the matter of script steps. Only one script step returns a result:
Perform Script. Its result is never a FileMaker Pro error. Its result is either nothing or the result of the
Exit Script script step option, as set by the programmer. The function
Get (LastError) is FileMaker Pro's answer to this problem.
Get (LastError) returns a number representing the execution error of the most recently executed non-control script step. Control script steps never produce errors.
FileMaker Pro rounds off its error reporting mechanism with one additional function:
Get (LastODBCError) returns the ODBC error state string when the
Execute SQL or
Import Records (from an ODBC data source) script step is used.
FileMaker Pro's error reporting mechanism has important limitations: errors do not propagate between scripts and the mechanism is not extensible.
There is no built-in FileMaker Pro facility for a sub-script's error to be discovered by a calling script. If
Get (LastError) is called after a
Perform Script script step, it will indicate that the sub-script executed or that something prevented the sub-script from executing. There is no FileMaker Pro error value that indicates a sub-script only partially executed due to an error condition.
There is also no built-in FileMaker Pro facility for programmer-defined errors. Imagine a script that requires one script parameter for the script to execute without error. FileMaker Pro treats missing script parameters as if they were blank values. FileMaker Pro does not generate an error. The script needs to generate its own error because its parameter is defined as required. For the remainder of this article, errors defined by programmers in scripts, calculations and custom functions are called programmer-defined errors.
Because FileMaker Pro's error reporting mechanism has limitations, there is a need for a mechanism that can overcome these limitations. This mechanism needs to be able to test for programmer-defined errors, to be usable in scripts, calculations and custom functions and to allow errors to propagate between scripts.
Overcoming the Limitations
D-Cogit created a programmer-defined error reporting mechanism that overcomes all of FileMaker Pro's error reporting limitations. The remainder of this article details this mechanism and give instructions on its use.
As already stated, using script, calculation or function results to indicate error conditions can be problematic. Using global variables alone is also not the solution because all variables are file specific - scripts of file A calling scripts of file B could not check on file B's global variables. The programmer-defined error reporting mechanism uses a mix of global variables, custom functions and scripts.
A solution file containing the programmer-defined error reporting mechanism is available for download here. The names used for variables, custom functions and scripts in this article are identical to those in the solution file.
The Global Variables
The heart of the programmer-defined error reporting mechanism resides in two global variables: $$dcError__n and $$dcError__t. $$dcError__n contains a numerical error code and $$dcError__t contains an error text. Global variables are used because they are accessible in all modes, are independent of context, are not encumbered by privilege issues and are unique to each FileMaker Pro file and session.
Unlike FileMaker Pro's error reporting mechanism, the programmer-defined error reporting mechanism captures an optional error text as a convenient way of adding precise meaning for an error number. Let's say, for example, that a script receives an out-of-range parameter value. The error number may simply indicate an out-of-range value. The error text can indicate that the value was above OR below the acceptable range. The text can be used in error messages and can help during debugging.
The programmer-defined error reporting mechanism encourages programmers to avoid accessing the above global variables directly. Variables can change in name, number or purpose as features are added or modified in the future. FileMaker Pro does not update variable names already used in scripts and calculations when a variable name is changed. Scripts and calculations that make direct use of variables can break when changes are made to variable names.
The same is not true of custom function names. FileMaker Pro does change custom function names in scripts, calculations and custom functions when a custom function name changes. It is much easier to change a handful of custom functions than to revise an entire solution just because global variable names changed. For this to hold true, custom functions must maintain the number and order of their parameters and results throughout their lifetime. New features that require different parameters or produce different results must be implemented in new functions. There are other advantages to using custom functions to access global variables, however this article will not go any further on the topic.
The programmer-defined error reporting mechanism has three custom functions. The first,
dcError_set, acts as a setter. The other two,
dcError_getError__t, act as getters.
The Setter Custom Function
A setter is a function that changes the state of a thing, be it an object, a structure or simply a variable. The setter custom function,
dcError_set, validates and records the error condition. It validates the error code to ensure it is present and a number. Otherwise, it indicates there was an error setting the error (numerical value -1, no text value). In either cases,
dcError_set returns nothing in order to be transparent in calculations. This is an important part of its design because
dcError_set is used by concatenating it in calculations that makes use of it.
dcError_set (error__n; error__t)
IsEmpty (error__n) or Int (error__n) ≠ error__n;
Let ([$$dcError__n = -1; $$dcError__t = ""]; ""); // invalid error code
Let ([$$dcError__n = error__n; $$dcError__t = error__t]; "")
Here is a sample usage of
dcError_set in a custom function (explanation follows):
Filter (number__n; ",0123456789") ≠ number__n or PatternCount (number__n; ",") > 1;
dcError_set (1; "not a number") & ""; // not a number error
dcError_set (0; "no error") & If (
""; // nothing to calculate
Filter (GetAsText (number__n - Int (number__n)); "0123456789") // return fraction
The custom function returns the fraction part of a positive number. Here, the comma is the decimal separator. An error condition is set and nothing is returned if the supplied parameter is not a positive number. Otherwise, an error condition is still set, the no error error condition, and the fraction is returned.
dcError_set is used by any script step that evaluates a calculation. Many approaches are possible. Here is one (explanation follows):
Set Variable [$dcError_set;
dcError_set (0; "no error")]
This approach makes use of a
Set Variable script step. The variable name was chosen to make the intention of the script step easy to understand.
dcError_set is used in the variable's value to set the error condition. Note that no script variable will be created when the script executes because its value will be empty.
Note that both sample usages set an error code of 0 and an error text of "no error" when there is no error. It is important to set an error condition even when there is no error. You should expect every script step, calculation and custom function that generates errors to be checked for errors. A possible outcome of error-checking is that there is no error. If you do not set an error condition when there is no error, then the error condition of a previous script step, calculation or custom function will be returned during error-checking.
The Getter Custom Functions and Scripts
The getter custom functions,
dcError_getError__t, return the last recorded error condition.
dcError_getError__n returns the error code and
dcError_getError__t returns the error text.
Here is what their code looks like:
if ($$dcError__n; $$dcError__n; 0)
dcError_getError__n can return a default value. Remember that the error code must be a non-empty number. If
dcError_getError__n is used before
dcError_set is used, it will not be initialized. Therefore, in the absence of a value, the no error error code is returned.
All that is needed now is a way to propagate errors between files. Two very simple getter scripts round out the programmer-defined error reporting mechanism:
dcError_getError__t. The getter scripts simply re-implement the getter custom functions.
The programmer-defined error reporting mechanism allows the creation of programmer-defined errors, is usable in scripts, custom functions and calculations and allows the passing of programmer-defined errors between custom functions, calculations, scripts and files without affecting their results. The mechanism uses two global variables to hold an error condition and encapsulates these in custom functions. Programmers manipulate and access the error condition through the use of three custom functions, one that sets the error condition and two that get the last error condition's error code and text. Programmers can check on another file's error conditions by using two scripts, one returning its last error condition's error code and one returning its last error condition's error text.
Conclusion and Closing Remarks
The programmer-defined error reporting mechanism is a improved error capturing mechanism for single or multi-file solutions. It is a basic mechanism that complements FileMaker Pro's built-in error checking mechanism.
One important aspect of the programmer-defined error reporting mechanism was mostly omitted: the error code definitions. This article suggests only two error values, one to indicate a problematic error code (-1) and one for the no error error code (0). It is up to programmers to create their own table(s) of error codes and their meaning.
It was mentioned earlier that a result based user-error checking mechanisms can be problematic. This is not to say it can not be done. The C language has an issue similar to FileMaker Pro: procedures can only return one value. Returning both a return value and an error value can be achieved in C through the use of pointers and structures. Text structures in FileMaker Pro can serve the same purpose as structures in C. Challenging, but doable! However, this topic is best left to another article.
Comments, suggestions and questions are welcome. Please see the Company tab to send your feedback.
Many thanks go to Stéphane Tardif and László Vajda for their invaluable help during the creation of this article. The quality of this article has been greatly enhanced thanks to their wonderful, no-holds barred critiques.