Skip to content

Pre-Reqs in SQL JSON Functions before improving NULL Support#204

Merged
siddharthteotia merged 4 commits into
masterfrom
siddharthteotia/json-extract-prereqs
Jun 4, 2026
Merged

Pre-Reqs in SQL JSON Functions before improving NULL Support#204
siddharthteotia merged 4 commits into
masterfrom
siddharthteotia/json-extract-prereqs

Conversation

@siddharthteotia

@siddharthteotia siddharthteotia commented May 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

I am working on improving NULL Support in JSON. I started that work directly upstream and it was based on top off some orthogonal refactoring in the JSON functions which is not available in the fork. The refactoring is important and I am porting it first to fork before creating the actual NULL feature work in JSON.

Capabilities added in this PR

  • Users can supply default values in jsonExtractScalar calls
  • More result types are supported: BOOLEAN, TIMESTAMP, JSON, BYTES
  • BigDecimal precision is preserved through the JSON parser instead of being silently downcast
  • Error messages on unsupported types are clearer

Commits (in order)

Upstream SHA Upstream PR What
f2153a1382 apache/pinot#16683 Handle null values in JsonExtractScalarTransformFunction with default value support
c297fbec3b apache/pinot#17867 Enhance JSON extraction functions to support null handling in queries (introduces 3-arg init(..., boolean nullHandlingEnabled))
5cb954d578 apache/pinot#18429 Improve JsonExtractScalarTransformFunction: type coercion, BIG_DECIMAL_ARRAY, guards (introduces _dataType / _storedType fields + per-type literal extraction)

@siddharthteotia siddharthteotia changed the title Port upstream JSON extract refactors (prereqs for null handling) Pre-Req Refactoring in SQL JSON Functions before improving NULL Support May 28, 2026
gortiz and others added 3 commits May 29, 2026 10:23
@siddharthteotia siddharthteotia force-pushed the siddharthteotia/json-extract-prereqs branch from 59d55a6 to 6c2dc35 Compare May 29, 2026 18:35
…IG_DECIMAL

li-pinot's BaseTransformFunction.init 3-arg form takes QueryContext, not
the boolean nullHandlingEnabled used by upstream. Apply the corresponding
signature change in JsonExtractScalarTransformFunction. Field access via
the inherited _nullHandlingEnabled is unchanged (set by super.init from
queryContext.isNullHandlingEnabled()).

li-pinot does not yet have MV BIG_DECIMAL support
(transformToBigDecimalValuesMV / initBigDecimalValuesMV /
_bigDecimalValuesMV — added upstream by apache/pinot#18326 which is not
in this prereq chain). Drop the BIG_DECIMAL_ARRAY case from this
transform's MV path and from the supported-types error message and
Javadoc. SV BIG_DECIMAL handling is unaffected. Update tests accordingly.
@siddharthteotia siddharthteotia changed the title Pre-Req Refactoring in SQL JSON Functions before improving NULL Support Pre-Reqs in SQL JSON Functions before improving NULL Support May 31, 2026
@siddharthteotia

Copy link
Copy Markdown
Collaborator Author

This is now ready for review. Once merged, #205 should be rebased on this and can be reviewed.

@UOETianleZhang UOETianleZhang left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems we are mostly cherry-picking from OSS. The only extra change 4ad3802 is minior and it LGTM.

There is an issue in OSS commits and I have left one comment on it. It is non-blocking, we can address it in a follow up PR.

/// a resolved array still throw.
///
/// **Supported `resultsType`:** `INT`, `LONG`, `FLOAT`, `DOUBLE`, `BIG_DECIMAL`, `BOOLEAN`, `TIMESTAMP`,
/// `STRING`, `JSON`, `BYTES`, plus `_ARRAY` variants of `INT` / `LONG` / `FLOAT` / `DOUBLE` /

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are mentioning BYTES result type is supported but it will throw exception at runtime. This class never overrides transformToBytesValuesSV so it will call parent class's method BaseTransformFunction.transformToBytesValuesSV. In which an exception will be raised at runtime (BYTES has stored type BYTES, so it falls through to the base class):

  switch (resultDataType.getStoredType()) {                                                                                                                                                                                              
    case BIG_DECIMAL: ...                                                                                                                                                                                                                
    case STRING:      ...                                                                                                                                                                                                                
    case UNKNOWN:     ...                                                                                                                                                                                                                
    default:                                                                                                                                                                                                                             
      throw new IllegalStateException("Cannot read SV " + resultDataType + " as BYTES");                                                                                                                                                 
  }     

To fix it, we cab either implement transformToBytesValuesSV, or drop BYTES from the supported-types.

I am aware this issue is inherited from OSS, so we can also resolve it in a follow up PR.

@siddharthteotia siddharthteotia Jun 4, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, 80% code is a cherry-pick. It's needed for a capability I am building in PR #205

Good catch. I will remove BYTES call out from supported types. Thanks for the review.

@siddharthteotia siddharthteotia merged commit 6f6cdca into master Jun 4, 2026
17 of 18 checks passed
@siddharthteotia siddharthteotia deleted the siddharthteotia/json-extract-prereqs branch June 4, 2026 08:24
siddharthteotia added a commit that referenced this pull request Jun 12, 2026
The prior WIP commit narrowed the per-row catch to PathNotFoundException +
InvalidJsonException to surface non-JSON input as a hard error. But jayway
throws a third, distinct exception for a null/empty JSON cell —
IllegalArgumentException("json can not be null") from ParseContextImpl.parse —
which neither arm caught. That exception escaped and broke the 14
testDefaultValue cases from #204 (null JSON cell + a configured default must
return the default, not throw).

Restore the broad catch for everything that represents a missing value, and
peel off only genuinely malformed input:

  - InvalidJsonException        -> throw IllegalArgumentException("...not valid
                                   JSON..."), regardless of null handling
                                   (non-JSON column is a schema modeling error).
  - everything else (null/empty input, missing path) -> swallowed; result stays
                                   null and the existing null-handling / default
                                   cascade decides (preserves #204 and #205).

Add a boundary test (testNonJsonInputThrowsEvenWithDefaultValue) proving a
configured default does not mask non-JSON input — the parse failure is thrown
before the default cascade is reached.

Verified locally: JsonExtractScalarTransformFunctionTest 145/145,
QueryRunnerTest 131/131 (case #12 throws the expected message), spotless +
checkstyle clean on pinot-core and pinot-query-runtime.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants