Surface date-only and time-only fields as SQL DATE / TIME on the analytics-engine route#22062
Surface date-only and time-only fields as SQL DATE / TIME on the analytics-engine route#22062ahkcs wants to merge 3 commits into
Conversation
…-engine route OpenSearch stores every `date` field as an epoch-millisecond timestamp regardless of its mapping `format`, and OpenSearchSchemaBuilder typed every date/date_nanos column as TIMESTAMP — discarding the format. So a date-only field (e.g. `basic_date`, `year_month_day`, `yyyy-MM-dd`) surfaced on the analytics-engine route as TIMESTAMP (1984-04-12 00:00:00) instead of a SQL DATE (1984-04-12), diverging from the v2 / Calcite path. Introduce a DateType Calcite UDT (backed by TIMESTAMP, mirroring the IpType / BinaryType pattern) and a DateFormatClassifier that narrows a date/date_nanos field to DATE when its format is date-only. Because the UDT keeps a TIMESTAMP backing with the same precision a plain timestamp carries, storage, Substrait, and the DataFusion scan are unchanged — the column is still Timestamp(ms) on disk and binds exactly as before; only the planner-side logical type changes so the SQL plugin can render it as DATE. Time-only formats are classified (TIME) but still mapped to TIMESTAMP for now; a dedicated TIME type follows in a later phase. DateFormatClassifier mirrors OpenSearchDateType.getExprTypeFromFormatString in the SQL plugin; the named-format sets are kept in sync by hand until a shared classifier is extracted. Signed-off-by: Kai Huang <ahkcs@amazon.com>
PR Reviewer Guide 🔍(Review updated until commit 2a88264)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to 2a88264 Explore these optional code suggestions:
Previous suggestionsSuggestions up to commit 5a51c07
Suggestions up to commit 50722ca
|
|
❌ Gradle check result for 50722ca: FAILURE Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change? |
…-engine route Phase 2, extending the DateType work. Add a TimeType Calcite UDT (same TIMESTAMP-backed, precision-carrying pattern as DateType) and return it from OpenSearchSchemaBuilder.buildLeafType when DateFormatClassifier classifies a date/date_nanos field's format as time-only (e.g. hour, hour_minute_second, HH:mm:ss). Storage, Substrait, and DataFusion are unchanged — only the planner-side logical type differs so the SQL plugin can render it as TIME. The classifier already recognized time-only formats; this wires the result through to a UDT instead of falling back to TIMESTAMP. Signed-off-by: Kai Huang <ahkcs@amazon.com>
|
Persistent review updated to latest commit 5a51c07 |
Phase 2, extending the DateType consumer. Maps a TimeType column to ExprCoreType.TIME in OpenSearchTypeFactory.convertAnalyticsEngineRelDataTypeToExprType and materializes the cell as an ExprTimeValue (timestamp result truncated to a LocalTime) in AnalyticsExecutionEngine. Mirrors the DateType dispatch. Flips time-only fields on the analytics route from TIMESTAMP (1970-01-01 09:00:00) to TIME (09:00:00), matching the v2 / Calcite path. Updates CalciteAnalyticsDatetimeWireFormatIT.testTimeRootColumnHmsFormat, which previously asserted the widened-timestamp behavior, to expect the time type and value. Depends on the analytics-api TimeType class (opensearch-project/OpenSearch#22062). Signed-off-by: Kai Huang <ahkcs@amazon.com>
|
❌ Gradle check result for 5a51c07: FAILURE Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change? |
|
Persistent review updated to latest commit 2a88264 |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #22062 +/- ##
============================================
- Coverage 73.44% 73.36% -0.08%
+ Complexity 75617 75545 -72
============================================
Files 6038 6038
Lines 342958 342958
Branches 49340 49340
============================================
- Hits 251880 251623 -257
- Misses 70997 71283 +286
+ Partials 20081 20052 -29 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Closing as superseded. The date-only / time-only DATE/TIME surfacing this PR adds has already landed in Verified against current SQL-plugin consumer side is covered by the merged opensearch-project/sql#5521. |
Description
OpenSearch stores every
datefield as an epoch-millisecond timestamp regardless of its mappingformat. On the analytics-engine route,OpenSearchSchemaBuilder.mapFieldTypetyped everydate/date_nanoscolumn asTIMESTAMPand discarded theformat, so a date-only field (e.g.basic_date,yyyy-MM-dd) surfaced as1984-04-12 00:00:00and a time-only field (e.g.hour,HH:mm:ss) as1970-01-01 09:00:00, instead of the SQLDATE(1984-04-12) /TIME(09:00:00) the v2 / Calcite path produces.This closes both gaps (DATE + TIME).
Approach
DateTypeandTimeTypeCalcite UDTs, each backed bySqlTypeName.TIMESTAMPand carrying the same precision a plain timestamp does — mirroring the existingIpType/BinaryTypeUDTs. Because the backing type and precision are identical to a plain timestamp, storage, Substrait serialization, and the DataFusion scan are unchanged: the column is stillTimestamp(MILLISECOND)on disk and binds against the parquet column exactly as before. Only the planner-side logical type changes, so the SQL plugin can render it asDATE/TIME.DateFormatClassifier, which narrows adate/date_nanosfield toDATEorTIMEby itsformat(named formats, custom patterns likeyyyy-MM-dd/HH:mm:ss, and||-combined alternatives). It mirrorsOpenSearchDateType.getExprTypeFromFormatStringin the SQL plugin.OpenSearchSchemaBuilder.buildLeafTypebecomes format-aware and returnsDateType/TimeTypeaccordingly; datetime/epoch formats keepTIMESTAMP.Failure this fixes (cluster log, before)
A naive UDT without an explicit precision hit:
The fix carries the plain-timestamp precision so the type serializes to an identical Substrait
PrecisionTimestamp.Testing
DateFormatClassifierTests(named date/time/datetime formats, custom patterns,'T'-literal stripping, combined alternatives, null/blank) — all pass.-Dtests.analytics.parquet_indices=true:CalcitePPLAggregationIT.testCountByDateTypeSpanForDifferentFormatsCalcitePPLAggregationIT.testCountByDateTypeSpanWithDifferentUnitsCalcitePPLAggregationIT.testCountByTimeTypeSpanForDifferentFormatsCalcitePPLAggregationIT.testCountByTimeTypeSpanWithDifferentUnitsCalcitePPLAggregationIT.testCountByNullableTimeSpanCalcitePPLAggregationIT.testCountBySpanForCustomFormatsCalciteAnalyticsDatetimeWireFormatIT(date + time cols)No regressions: the change only alters columns whose mapping format is date-only or time-only; datetime/epoch columns are untouched. Other pre-existing analytics-route failures (percentile-approx, count-distinct-approx, nested fields,
date_add) are date/time-independent../gradlew :sandbox:libs:analytics-api:{spotlessJavaCheck,forbiddenApisMain,licenseHeaders}pass.Check List
--signoff.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.