When a Java-implemented upcall throws — scalar UDFs via JniBridge.invokeScalarUdf, table providers via JniBridge.invokeTableScan — the native side captures only class.getName() and getMessage() via jthrowable_to_string and surfaces it as DataFusionError::Execution(...). Since #81 routes that through to a typed ExecutionException, but the original Java stack trace is gone by then. A user whose UDF throws NullPointerException deep inside evaluate(...) sees:
with no way to find the failing line in their own code. The information exists; it's just discarded.
What changes are included in this PR?
This PR captures the Java-formatted trace (via Throwable.printStackTrace(PrintWriter) to a StringWriter) and appends it to the existing message. Verbosity is configurable per SessionContext so production paths that treat exception bodies as untrusted user input can opt out:
SessionContext.builder()
.exceptionVerbosity(ExceptionVerbosity.MESSAGE) // FULL (default), MESSAGE, NONE
.build();
The default is FULL -- the issue's stated default -- so callers who don't touch the setter immediately gain stack traces with no API change.
Yes, additive only -- default behaviour does change: the message string surfaced inside ExecutionException.getMessage() now includes the Java stack trace below the existing "Java UDF 'name' threw class: message" header. Callers that already only startsWith("Java UDF '...' threw")-style match keep working; anything stricter (an exact-equals on the message) would need the new MESSAGE verbosity to lock the pre-#55 format.
New public type ExceptionVerbosity.
New SessionContextBuilder.exceptionVerbosity(...) setter; default unchanged for callers that don't invoke it (their behaviour changes — they now get traces by default).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Which issue does this PR close?
Rationale for this change
When a Java-implemented upcall throws — scalar UDFs via JniBridge.invokeScalarUdf, table providers via JniBridge.invokeTableScan — the native side captures only class.getName() and getMessage() via jthrowable_to_string and surfaces it as DataFusionError::Execution(...). Since #81 routes that through to a typed ExecutionException, but the original Java stack trace is gone by then. A user whose UDF throws NullPointerException deep inside evaluate(...) sees:
with no way to find the failing line in their own code. The information exists; it's just discarded.
What changes are included in this PR?
This PR captures the Java-formatted trace (via Throwable.printStackTrace(PrintWriter) to a StringWriter) and appends it to the existing message. Verbosity is configurable per SessionContext so production paths that treat exception bodies as untrusted user input can opt out:
The default is FULL -- the issue's stated default -- so callers who don't touch the setter immediately gain stack traces with no API change.
Out of scope (called out in #55, deferred):
Are these changes tested?
Yes. 10 new tests in ExceptionVerbosityTest.
Are there any user-facing changes?
Yes, additive only -- default behaviour does change: the message string surfaced inside ExecutionException.getMessage() now includes the Java stack trace below the existing "Java UDF 'name' threw class: message" header. Callers that already only startsWith("Java UDF '...' threw")-style match keep working; anything stricter (an exact-equals on the message) would need the new MESSAGE verbosity to lock the pre-#55 format.