Skip to content

Commit b65f780

Browse files
committed
Support decimal literal in span function
Signed-off-by: Yuanchun Shen <yuanchu@amazon.com>
1 parent fb7bc31 commit b65f780

3 files changed

Lines changed: 43 additions & 4 deletions

File tree

core/src/main/java/org/opensearch/sql/expression/function/udf/SpanFunction.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import org.apache.calcite.schema.impl.ScalarFunctionImpl;
2121
import org.apache.calcite.sql.type.CompositeOperandTypeChecker;
2222
import org.apache.calcite.sql.type.OperandTypes;
23-
import org.apache.calcite.sql.type.ReturnTypes;
2423
import org.apache.calcite.sql.type.SqlReturnTypeInference;
2524
import org.apache.calcite.sql.type.SqlTypeFamily;
2625
import org.apache.calcite.sql.type.SqlTypeUtil;
@@ -44,7 +43,17 @@ public SpanFunction() {
4443

4544
@Override
4645
public SqlReturnTypeInference getReturnTypeInference() {
47-
return ReturnTypes.ARG0;
46+
// Return arg0 type if it has a unit (i.e. time related span)
47+
return callBinding -> {
48+
if (SqlTypeUtil.isString(callBinding.getOperandType(2))) {
49+
return callBinding.getOperandType(0);
50+
}
51+
// Use the least restrictive type between the field type and the interval type if it's a
52+
// numeric span. E.g. span(int_field, double_literal) -> double
53+
return callBinding
54+
.getTypeFactory()
55+
.leastRestrictive(List.of(callBinding.getOperandType(0), callBinding.getOperandType(1)));
56+
};
4857
}
4958

5059
@Override
@@ -56,10 +65,9 @@ public UDFOperandMetadata getOperandMetadata() {
5665
.or(
5766
OperandTypes.family(
5867
SqlTypeFamily.DATETIME, SqlTypeFamily.NUMERIC, SqlTypeFamily.CHARACTER))
59-
// TODO: numeric span should support decimal as its interval
6068
.or(
6169
OperandTypes.family(
62-
SqlTypeFamily.NUMERIC, SqlTypeFamily.INTEGER, SqlTypeFamily.ANY)));
70+
SqlTypeFamily.NUMERIC, SqlTypeFamily.NUMERIC, SqlTypeFamily.ANY)));
6371
}
6472

6573
public static class SpanImplementor implements NotNullImplementor {

integ-test/src/test/java/org/opensearch/sql/ppl/StatsCommandIT.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -806,4 +806,21 @@ public void testStatsByDependentGroupFields() throws IOException {
806806
verifyDataRows(
807807
response, rows(61, 310, 41, 10, 31), rows(60, 390, 49, 10, 39), rows(59, 260, 36, 10, 26));
808808
}
809+
810+
@Test
811+
public void testStatsByFractionalSpan() throws IOException {
812+
JSONObject response1 =
813+
executeQuery(
814+
String.format(
815+
"source=%s | stats count by span(balance, 4170.5)",
816+
TEST_INDEX_BANK_WITH_NULL_VALUES));
817+
verifySchema(response1, schema("count", "bigint"), schema("span(balance,4170.5)", "double"));
818+
verifyDataRows(
819+
response1,
820+
rows(3, null),
821+
rows(1, 4170.5),
822+
rows(1, 29193.5),
823+
rows(1, 37534.5),
824+
rows(1, 45875.5));
825+
}
809826
}

ppl/src/test/java/org/opensearch/sql/ppl/parser/AstBuilderTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1488,4 +1488,18 @@ public void testReplaceCommandWithMultiplePairs() {
14881488
// Test multiple pattern/replacement pairs
14891489
plan("source=t | replace 'a' WITH 'A', 'b' WITH 'B' IN field");
14901490
}
1491+
1492+
@Test
1493+
public void testTimeSpanWithDecimalShouldThrow() {
1494+
Throwable t1 =
1495+
assertThrows(
1496+
SyntaxCheckException.class, () -> plan("source=t | timechart span=1.5d count"));
1497+
assertTrue(t1.getMessage().contains("[1.5d] is not a valid term at this part of the query"));
1498+
1499+
Throwable t2 =
1500+
assertThrows(
1501+
SyntaxCheckException.class,
1502+
() -> plan("source=t | stats count by span(@timestamp, 2.5y)"));
1503+
assertTrue(t2.getMessage().contains("[y] is not a valid term at this part of the query"));
1504+
}
14911505
}

0 commit comments

Comments
 (0)