Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
import org.opensearch.sql.calcite.type.AbstractExprRelDataType;
import org.opensearch.sql.calcite.utils.OpenSearchTypeFactory.ExprUDT;

/** Datetime language extension that normalizes UDT types and casts output for wire-format. */
/** Datetime language extension that normalizes datetime UDT types to standard Calcite types. */
public class DatetimeExtension implements LanguageExtension {

@Override
public List<RelShuttle> postAnalysisRules() {
// Fresh instances per plan() because RelHomogeneousShuttle inherits a stateful stack.
return List.of(new DatetimeUdtNormalizeRule(), new DatetimeOutputCastRule());
// Fresh instance per plan() because RelHomogeneousShuttle inherits a stateful stack.
return List.of(new DatetimeUdtNormalizeRule());
}

/** Maps datetime UDT types to their standard Calcite equivalents. */
Expand All @@ -45,10 +45,5 @@ static Optional<UdtMapping> fromUdtType(RelDataType type) {
ExprUDT udt = e.getUdt();
return Arrays.stream(values()).filter(u -> u.udtType == udt).findFirst();
}

/** Returns true if the given SqlTypeName is a standard datetime type. */
static boolean isDatetimeType(SqlTypeName typeName) {
return Arrays.stream(values()).anyMatch(u -> u.stdType == typeName);
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
import org.opensearch.sql.executor.QueryType;

/**
* Tests that DatetimeExtension post-analysis rules (UDT normalization and output VARCHAR cast)
* apply correctly to the SQL V2 parser path through CalciteRelNodeVisitor.
* Tests that the DatetimeExtension UDT-normalization post-analysis rule applies correctly to the
* SQL V2 parser path through CalciteRelNodeVisitor.
*/
public class DatetimeExtensionSqlTest extends UnifiedQueryTestBase {

Expand All @@ -47,7 +47,7 @@ protected Map<String, Table> getTableMap() {
private Table createEventsTable() {
return SimpleTable.builder()
.col("id", INTEGER)
.col("name", VARCHAR)
.col("event_str", VARCHAR)
.col("hire_date", DATE)
.col("start_time", TIME)
.col("created_at", TIMESTAMP)
Expand All @@ -57,12 +57,11 @@ private Table createEventsTable() {
}

@Test
public void testAllStandardDatetimeTypesCastToVarchar() {
public void testStandardDatetimeTypesNotWrapped() {
givenQuery("SELECT * FROM catalog.events")
.assertPlan(
"""
LogicalProject(id=[$0], name=[$1], hire_date=[CAST($2):VARCHAR NOT NULL], start_time=[CAST($3):VARCHAR NOT NULL], created_at=[CAST($4):VARCHAR NOT NULL])
LogicalTableScan(table=[[catalog, events]])
LogicalTableScan(table=[[catalog, events]])
""");
}

Expand All @@ -71,21 +70,19 @@ public void testFilterWithTimestampLiteral() {
givenQuery("SELECT * FROM catalog.events WHERE created_at > '2024-01-01T00:00:00Z'")
.assertPlan(
"""
LogicalProject(id=[$0], name=[$1], hire_date=[CAST($2):VARCHAR NOT NULL], start_time=[CAST($3):VARCHAR NOT NULL], created_at=[CAST($4):VARCHAR NOT NULL])
LogicalFilter(condition=[>($4, TIMESTAMP('2024-01-01T00:00:00Z':VARCHAR))])
LogicalTableScan(table=[[catalog, events]])
LogicalFilter(condition=[>($4, TIMESTAMP('2024-01-01T00:00:00Z':VARCHAR))])
LogicalTableScan(table=[[catalog, events]])
""")
.assertReturnType("TIMESTAMP", TIMESTAMP, 9);
}

@Test
public void testComparisonWithDatetimeUdf() {
givenQuery("SELECT * FROM catalog.events WHERE created_at < DATE(name)")
givenQuery("SELECT * FROM catalog.events WHERE created_at < DATE(event_str)")
.assertPlan(
"""
LogicalProject(id=[$0], name=[$1], hire_date=[CAST($2):VARCHAR NOT NULL], start_time=[CAST($3):VARCHAR NOT NULL], created_at=[CAST($4):VARCHAR NOT NULL])
LogicalFilter(condition=[<($4, TIMESTAMP(DATE($1)))])
LogicalTableScan(table=[[catalog, events]])
LogicalFilter(condition=[<($4, TIMESTAMP(DATE($1)))])
LogicalTableScan(table=[[catalog, events]])
""")
.assertReturnType("DATE", DATE)
.assertReturnType("TIMESTAMP", TIMESTAMP, 9);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public void setUp() {
private Table createEventsTable() {
return SimpleTable.builder()
.col("id", INTEGER)
.col("name", VARCHAR)
.col("event_str", VARCHAR)
.col("hire_date", DATE)
.col("start_time", TIME)
.col("created_at", TIMESTAMP)
Expand All @@ -63,18 +63,17 @@ private Table createEventsTable() {
}

@Test
public void testUdfResultNormalizedAndCastToVarchar() {
public void testUdfResultNormalized() {
givenQuery(
"""
source = catalog.events \
| eval d = DATE(name), t = TIME(name), ts = TIMESTAMP(name) \
| eval d = DATE(event_str), t = TIME(event_str), ts = TIMESTAMP(event_str) \
| fields d, t, ts\
""")
.assertPlan(
"""
LogicalProject(d=[CAST($0):VARCHAR], t=[CAST($1):VARCHAR], ts=[CAST($2):VARCHAR])
LogicalProject(d=[DATE($1)], t=[TIME($1)], ts=[TIMESTAMP($1)])
LogicalTableScan(table=[[catalog, events]])
LogicalProject(d=[DATE($1)], t=[TIME($1)], ts=[TIMESTAMP($1)])
LogicalTableScan(table=[[catalog, events]])
""")
.assertReturnType("DATE", DATE)
.assertReturnType("TIME", TIME, 9)
Expand All @@ -83,7 +82,9 @@ public void testUdfResultNormalizedAndCastToVarchar() {

@Test
public void testNestedUdfCallsNormalized() {
givenQuery("source = catalog.events | eval d = DATEDIFF(DATE(name), DATE(name)) | fields d")
givenQuery(
"source = catalog.events | eval d = DATEDIFF(DATE(event_str), DATE(event_str)) | fields"
+ " d")
.assertPlan(
"""
LogicalProject(d=[DATEDIFF(DATE($1), DATE($1))])
Expand All @@ -94,13 +95,12 @@ public void testNestedUdfCallsNormalized() {
}

@Test
public void testDateLiteralCastToVarchar() {
public void testDateLiteralNormalized() {
givenQuery("source = catalog.events | eval d = DATE('2024-01-01') | fields d")
.assertPlan(
"""
LogicalProject(d=[CAST($0):VARCHAR])
LogicalProject(d=[DATE('2024-01-01':VARCHAR)])
LogicalTableScan(table=[[catalog, events]])
LogicalProject(d=[DATE('2024-01-01':VARCHAR)])
LogicalTableScan(table=[[catalog, events]])
""")
.assertReturnType("DATE", DATE);
}
Expand All @@ -122,7 +122,7 @@ public void testFilterWithTimestampLiteral() {

@Test
public void testComparisonWithDatetimeUdf() {
givenQuery("source = catalog.events | where created_at < DATE(name) | fields id")
givenQuery("source = catalog.events | where created_at < DATE(event_str) | fields id")
.assertPlan(
"""
LogicalProject(id=[$0])
Expand All @@ -134,22 +134,21 @@ public void testComparisonWithDatetimeUdf() {
}

@Test
public void testAllStandardDatetimeTypesCastToVarchar() {
public void testStandardDatetimeFieldsNotWrapped() {
givenQuery("source = catalog.events | fields hire_date, start_time, created_at")
.assertPlan(
"""
LogicalProject(hire_date=[CAST($0):VARCHAR NOT NULL], start_time=[CAST($1):VARCHAR NOT NULL], created_at=[CAST($2):VARCHAR NOT NULL])
LogicalProject(hire_date=[$2], start_time=[$3], created_at=[$4])
LogicalTableScan(table=[[catalog, events]])
LogicalProject(hire_date=[$2], start_time=[$3], created_at=[$4])
LogicalTableScan(table=[[catalog, events]])
""");
}

@Test
public void testNonDatetimeFieldsNotWrapped() {
givenQuery("source = catalog.events | fields id, name")
givenQuery("source = catalog.events | fields id, event_str")
.assertPlan(
"""
LogicalProject(id=[$0], name=[$1])
LogicalProject(id=[$0], event_str=[$1])
LogicalTableScan(table=[[catalog, events]])
""");
}
Expand All @@ -168,27 +167,24 @@ public void testSequentialPlanCallsDoNotCorruptShuttleStack() {
+ " | stats count() as field_count, distinct_count(created_at) as distinct_count");
planner.plan(
"source = catalog.events"
+ " | eval ts = TIMESTAMP(name)"
+ " | eval ts = TIMESTAMP(event_str)"
+ " | stats count() as field_count, distinct_count(ts) as distinct_count");
planner.plan(
"source = catalog.events | where created_at > \"2024-01-01\" | fields hire_date");
}
}

@Test
public void testOutputCastCanCompileAndExecute() throws Exception {
public void testDatetimeFieldsPreserveStandardTypes() throws Exception {
RelNode plan =
planner.plan("source = catalog.events | fields hire_date, start_time, created_at");
try (PreparedStatement statement = compiler.compile(plan)) {
ResultSet resultSet = statement.executeQuery();
verify(resultSet)
.expectSchema(
col("hire_date", java.sql.Types.VARCHAR),
col("start_time", java.sql.Types.VARCHAR),
col("created_at", java.sql.Types.VARCHAR))
.expectData(
row("2024-01-16", "12:00:00", "2024-01-15 08:00:00"),
row("2024-06-20", "14:00:00", "2024-06-20 00:00:00"));
col("hire_date", java.sql.Types.DATE),
col("start_time", java.sql.Types.TIME),
col("created_at", java.sql.Types.TIMESTAMP));
}
}
}
Loading
Loading