Skip to content

Commit 97d59da

Browse files
committed
Support mapping _time field access to @timestamp
Signed-off-by: Yuanchun Shen <yuanchu@amazon.com>
1 parent c637bb2 commit 97d59da

4 files changed

Lines changed: 55 additions & 1 deletion

File tree

core/src/main/java/org/opensearch/sql/calcite/CalciteRexNodeVisitor.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,8 @@ public RexNode visitFunction(Function node, CalcitePlanContext context) {
502502
lambdaNode = analyze(arg, lambdaContext);
503503
}
504504
arguments.add(lambdaNode);
505+
} else if (arg instanceof Field) {
506+
arguments.add(resolveSpecialTimeFieldReference((Field) arg, context));
505507
} else {
506508
arguments.add(analyze(arg, context));
507509
}
@@ -516,6 +518,30 @@ public RexNode visitFunction(Function node, CalcitePlanContext context) {
516518
throw new IllegalArgumentException("Unsupported operator: " + node.getFuncName());
517519
}
518520

521+
/**
522+
* Replace {@code _time} field reference to {@code @timestamp} field if {@code _time} does not
523+
* exist in the input schema.
524+
*/
525+
private RexNode resolveSpecialTimeFieldReference(Field field, CalcitePlanContext context) {
526+
if (field.getField() instanceof QualifiedName && "_time".equals(field.getField().toString())) {
527+
try {
528+
// Use _time field if it exists
529+
return analyze(field, context);
530+
} catch (IllegalArgumentException ignored) {
531+
try {
532+
// Substitute _time field to @timestamp
533+
return referenceImplicitTimestampField(context);
534+
} catch (IllegalArgumentException e) {
535+
throw new SemanticCheckException(
536+
"Referring _time field requires an explicit '_time' field or an implicit"
537+
+ " '@timestamp' field, but none of them was found in the input schema.",
538+
e);
539+
}
540+
}
541+
}
542+
return analyze(field, context);
543+
}
544+
519545
@Override
520546
public RexNode visitWindowFunction(WindowFunction node, CalcitePlanContext context) {
521547
Function windowFunction = (Function) node.getFunction();

core/src/main/java/org/opensearch/sql/calcite/plan/OpenSearchConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public interface OpenSearchConstants {
2121
String METADATA_FIELD_ROUTING = "_routing";
2222

2323
String IMPLICIT_FIELD_TIMESTAMP = "@timestamp";
24+
String SPECIAL_FIELD_TIME = "_time";
2425

2526
java.util.Map<String, ExprType> METADATAFIELD_TYPE_MAP =
2627
Map.of(

core/src/main/java/org/opensearch/sql/executor/QueryService.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,12 @@ public RelNode optimize(RelNode plan, CalcitePlanContext context) {
262262

263263
private boolean isCalciteFallbackAllowed() {
264264
if (settings != null) {
265-
return settings.getSettingValue(Settings.Key.CALCITE_FALLBACK_ALLOWED);
265+
try {
266+
return settings.getSettingValue(Settings.Key.CALCITE_FALLBACK_ALLOWED);
267+
} catch (Exception e) {
268+
// If failed to retrieve the setting, default fallback to false
269+
return false;
270+
}
266271
} else {
267272
return true;
268273
}

integ-test/src/test/java/org/opensearch/sql/calcite/standalone/CalcitePPLDateTimeBuiltinFunctionIT.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@
3030
import org.json.JSONObject;
3131
import org.junit.jupiter.api.Test;
3232
import org.opensearch.client.Request;
33+
import org.opensearch.sql.common.utils.StringUtils;
3334
import org.opensearch.sql.data.model.ExprDateValue;
3435
import org.opensearch.sql.data.model.ExprIntegerValue;
36+
import org.opensearch.sql.exception.SemanticCheckException;
3537
import org.opensearch.sql.expression.function.FunctionProperties;
3638

3739
public class CalcitePPLDateTimeBuiltinFunctionIT extends CalcitePPLIntegTestCase {
@@ -46,6 +48,7 @@ public void init() throws IOException {
4648
loadIndex(Index.DATE);
4749
loadIndex(Index.PEOPLE2);
4850
loadIndex(Index.BANK);
51+
loadIndex(Index.BIG5);
4952
initRelativeDocs();
5053
}
5154

@@ -1476,6 +1479,25 @@ public void testMicrosecond() throws IOException {
14761479
verifyDataRows(actual, rows(0, 0, 0, 123456, 123456));
14771480
}
14781481

1482+
@Test
1483+
public void testAccessImplicitTimestampField() {
1484+
JSONObject actual = executeQuery("source=big5 | eval t = unix_timestamp(_time) | fields t");
1485+
verifySchema(actual, schema("t", "double"));
1486+
verifyDataRows(actual, rows(1672696954));
1487+
1488+
Throwable t =
1489+
assertThrowsWithReplace(
1490+
SemanticCheckException.class,
1491+
() ->
1492+
executeQuery(
1493+
StringUtils.format(
1494+
"source=%s | eval t = unix_timestamp(_time)", TEST_INDEX_DATE_FORMATS)));
1495+
verifyErrorMessageContains(
1496+
t,
1497+
"Referring _time field requires an explicit '_time' field or an implicit"
1498+
+ " '@timestamp' field, but none of them was found in the input schema.");
1499+
}
1500+
14791501
private static int getYearWeek(LocalDate date, int mode) {
14801502
return exprYearweek(new ExprDateValue(date), new ExprIntegerValue(mode)).integerValue();
14811503
}

0 commit comments

Comments
 (0)