Skip to content

[WIP][SPARK-57811][SQL] Support string to nanosecond-precision timestamp coercion in comparisons and predicates#57223

Open
stevomitric wants to merge 4 commits into
apache:masterfrom
stevomitric:stevomitric/spark-57811-nanos
Open

[WIP][SPARK-57811][SQL] Support string to nanosecond-precision timestamp coercion in comparisons and predicates#57223
stevomitric wants to merge 4 commits into
apache:masterfrom
stevomitric:stevomitric/spark-57811-nanos

Conversation

@stevomitric

@stevomitric stevomitric commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

Add non-ANSI type-coercion arms so that a string compared against a nanosecond-precision timestamp column (TIMESTAMP_NTZ(p)/TIMESTAMP_LTZ(p), p in [7, 9]) is cast to that nanos type, mirroring the existing microsecond TimestampType handling (StringPromotionTypeCoercion equality arms + TypeCoercion.findCommonTypeForBinaryComparison).

Why are the changes needed?

Micros TimestampType has string-coercion arms that honor ...datetimeToString.enabled (legacy → promote to string; equality → cast to timestamp). Nanos had none, so it fell through to config-blind AtomicType promotion. This adds the arms so nanos matches TimestampType's legacy behavior.

Does this PR introduce any user-facing change?

Only under legacy datetimeToString=true + ANSI off: range comparisons (<, BETWEEN, …) now promote to string (matching micros); equality unchanged. All other configs identical.

How was this patch tested?

Extended existing suites and added golden file tests.

Was this patch authored or co-authored using generative AI tooling?

Generated-by: Claude Code

…oercion in comparisons and predicates

Type coercion previously handled only microsecond TimestampType when one
operand of a comparison or predicate is a string: the string was cast to
TimestampType so subsecond digits compared exactly. Nanosecond timestamp
types (Timestamp{LTZ,NTZ}NanosType) had no such arm, so a string compared
against a nanos column fell through to generic handling and lost the 7th-9th
fractional digits.

This adds the missing non-ANSI arms, mirroring the existing microsecond
handling: two Equality arms in StringPromotionTypeCoercion cast the string to
the concrete nanos operand type, and two findCommonTypeForBinaryComparison
cases in TypeCoercion return that nanos type for the range path. The concrete
family and precision of the timestamp operand are preserved, the arms are
ordered so equality fires before the range path, and legacy
castDatetimeToString is honored (range promotes both sides to string, equality
still casts the string to nanos). ANSI type coercion is intentionally left
unchanged.

Tests: TypeCoercionSuite "binary comparison with string promotion" gains
nanos assertions (all precisions, both families, plus a legacy-mode block that
exercises the new arms); TimestampNanosWideningSuiteBase adds an end-to-end
string-operand test; and the timestamp-{ltz,ntz}-nanos golden files add
equality/range/BETWEEN cases.

Co-authored-by: Isaac
…ion divergence

Add a comment at the new findCommonTypeForBinaryComparison nanos arms noting
that AnyTimestampNanoType covers both LTZ and NTZ, so the nanos types honor
castDatetimeToString like micros TimestampType (LTZ) -- deliberately more
precise than micros TimestampNTZType, which has no arm and falls through to
the config-blind canPromoteAsInBinaryComparison case.

Co-authored-by: Isaac
… string-nanos coercion arm

The existing golden cases in timestamp-{ntz,ltz}-nanos.sql run only in the
default config (ANSI on), where a string compared to a nanos timestamp
already coerces to the nanos type via the pre-existing string-promotion
fall-through -- so those cases are byte-identical on the parent commit and
do not lock in the arm this PR adds.

Add a case under `spark.sql.ansi.enabled=false` +
`spark.sql.legacy.typeCoercion.datetimeToString.enabled=true` -- the only
config where the new findCommonTypeForBinaryComparison arm changes the plan.
The regenerated analyzer-results show the split the arm produces: the range
comparison promotes the column to string (`cast(c as string) < '...'`,
matching micro TIMESTAMP), while equality still casts the string to the
nanos type (the Equality arm fires first). Reverting the production arm
makes these analyzer goldens fail, confirming they are non-vacuous.

Co-authored-by: Isaac
…s coercion tests

Address review feedback: the `Equality` extractor used by the new
StringPromotionTypeCoercion arms matches both `EqualTo` and `EqualNullSafe`,
so the `<=>` operator is coerced identically -- but only `EqualTo` was
asserted. Add `EqualNullSafe` ruleTest cases in both the default-config
parameterized block and the legacy `castDatetimeToString` block, mirroring
the existing `EqualTo` assertions.

Co-authored-by: Isaac
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.

1 participant