public final class ErrorHandler
extends java.lang.Object
Normally, exceptions thrown in Java code invoked by function and operator implementations are allowed to propagate normally. However, in certain cases, exceptions must be caught and handled silently. In these cases, the fact that an error was handled must affect the outcome of a subexpression within an SQL statement. Currently, the only case in which errors must be handled in this fashion is within a converted CAN-FIND statement, nested within a where clause (including within the where clause of an enclosing, converted, CAN-FIND statement). In this case, a nested CAN-FIND is converted into a subquery (a.k.a., subselect). If that subquery contains any built-in functions or operators which are implemented in Java, the functions implemented within this class are used to safely handle any potential exceptions.
Functions and operators implemented in Java, which are used in such a
circumstance, must be wrapped with a call to checkError(Boolean, Boolean)
, such that the nearest enclosing
subexpression which can evaluate to a boolean either evaluates normally
(if no error occurs), or evaluates to false
in the event an
error occurs. For instance, the subexpression
toInt('foo') = 5must evaluate to
false
if it occurs within a subquery, because
the left side of the equals operation will fail with an error. Such an
expression would be allowed to fail normally, however, if it occurred
outside a subquery. To accomplish the former, the above subexpression,
when occurring within a subquery, would actually be expanded to
checkError(initError(false), toInt('foo') = 5)
The call to initError(false)
initializes the current scope of
the handler to a known (error-free) state. In the event of an error within
the toInt()
function, handleError(RuntimeException)
is invoked, the current scope is marked as having an error, and
null
is returned by toInt()
. When the enclosing
checkError()
function is invoked, the current scope is checked
for errors. If an error occurred, checkError()
returns
false
. If no error occurred, the value of the second
parameter to checkError(Boolean, Boolean)
(i.e., the result of the
subexpression) is returned.
Implementation Notes
This implementation relies on several critical assumptions:
checkError()
will always be
initError(false)
(see next bullet as to why this is
important). This is the responsibility of the SQL author.
initError(Boolean)
method is invoked before the subexpression
whose boolean result is passed as the second parameter to the checkError(Boolean, Boolean)
method. The former initializes the
state of the ErrorHandler
for the current
subexpression's scope. If an error occurs within that subexpression,
the state is assumed to be initialized properly.
ThreadLocal
variable to share state across these
multiple calls. If different threads were to be used, this scheme
clearly would not work.
RuntimeException
, will call handleError(RuntimeException)
from within that catch block, and
will return null
after that call.
Modifier and Type | Field and Description |
---|---|
private static java.lang.ThreadLocal<java.util.Deque<java.lang.Boolean>> |
errors
Stack of error flags used to track errors by function scope
|
private static java.util.logging.Logger |
LOG
Logger
|
Constructor and Description |
---|
ErrorHandler() |
Modifier and Type | Method and Description |
---|---|
static java.lang.Boolean |
checkError(java.lang.Boolean init,
java.lang.Boolean result)
A special wrapper function for a function implemented in Java and called
within a SUBSELECT SQL phrase which represents a converted CAN-FIND
statement, where the wrapped function can potentially produce an error
condition.
|
(package private) static void |
handleError(java.lang.RuntimeException exc)
Handle a P2J runtime error encountered in the current function scope.
|
static java.lang.Boolean |
initError(java.lang.Boolean state)
Initialize the error flag stack for the current function scope by
pushing the given error state onto the stack.
|
private static final java.util.logging.Logger LOG
private static final java.lang.ThreadLocal<java.util.Deque<java.lang.Boolean>> errors
public static java.lang.Boolean initError(java.lang.Boolean state)
state
- Error state to push onto the stack.Boolean.TRUE
.public static java.lang.Boolean checkError(java.lang.Boolean init, java.lang.Boolean result)
...where (select id from some_table where checkError(initError(false), toInt_safe(...) = 5) or ... limit 1) is not null
The intended convention is to call initError(false)
as the
first parameter to the method, in order to initialize the error state
for the current scope, and to pass as the second parameter a boolean
sub-expression which contains a call to one of the *_safe() functions
implemented in Java.
This method checks the error flag for the current scope and does the following:
true
(indicating an error has occurred in a Java method invoked from
the enclosed boolean sub-expression), this method returns
false
;
false
(indicating no error has occurred in Java within the boolean
sub-expression), the result of executing the second parameter is
returned.
init
- The result of the initError(false)
function
called as the first parameter; not used.result
- The result of executing the boolean expression containing the
Java function or operator. This result indicates the
success/failure of the enclosed, boolean sub-expression.false
if an error occurred in this scope;
otherwise, the result of the boolean sub-expression.static void handleError(java.lang.RuntimeException exc)
initError(Boolean)
function, the error state for the
current function scope is set to true
. Otherwise, the
exception passed to this method is simply rethrown.exc
- The exception to be handled.initError(Boolean)
,
checkError(Boolean, Boolean)