Skip to content

Commit

Permalink
Support implicit type conversion from string to temporal (#171)
Browse files Browse the repository at this point in the history
* Support implicit cast from string to temporal types

Signed-off-by: Chen Dai <daichen@amazon.com>

* Add comparison test

Signed-off-by: Chen Dai <daichen@amazon.com>

* Add doctest

Signed-off-by: Chen Dai <daichen@amazon.com>

* Fix doctest

Signed-off-by: Chen Dai <daichen@amazon.com>

* Add more user manual

Signed-off-by: Chen Dai <daichen@amazon.com>

* Add more user manual

Signed-off-by: Chen Dai <daichen@amazon.com>

* Fix doctest

Signed-off-by: Chen Dai <daichen@amazon.com>

* Update user manual

Signed-off-by: Chen Dai <daichen@amazon.com>
  • Loading branch information
dai-chen authored Aug 4, 2021
1 parent b3dfc49 commit 21cad56
Show file tree
Hide file tree
Showing 11 changed files with 87 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import static org.opensearch.sql.expression.function.BuiltinFunctionName.CAST_TO_BOOLEAN;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.CAST_TO_BYTE;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.CAST_TO_DATE;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.CAST_TO_DATETIME;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.CAST_TO_DOUBLE;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.CAST_TO_FLOAT;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.CAST_TO_INT;
Expand Down Expand Up @@ -77,6 +78,7 @@ public class Cast extends UnresolvedExpression {
.put("date", CAST_TO_DATE.getName())
.put("time", CAST_TO_TIME.getName())
.put("timestamp", CAST_TO_TIMESTAMP.getName())
.put("datetime", CAST_TO_DATETIME.getName())
.build();

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ public enum ExprCoreType implements ExprType {
* Date.
* Todo. compatible relationship.
*/
TIMESTAMP(UNDEFINED),
DATE(UNDEFINED),
TIME(UNDEFINED),
DATETIME(UNDEFINED),
TIMESTAMP(STRING),
DATE(STRING),
TIME(STRING),
DATETIME(STRING),
INTERVAL(UNDEFINED),

/**
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/java/org/opensearch/sql/expression/DSL.java
Original file line number Diff line number Diff line change
Expand Up @@ -645,4 +645,9 @@ public FunctionExpression castTimestamp(Expression value) {
return (FunctionExpression) repository
.compile(BuiltinFunctionName.CAST_TO_TIMESTAMP.getName(), Arrays.asList(value));
}

public FunctionExpression castDatetime(Expression value) {
return (FunctionExpression) repository
.compile(BuiltinFunctionName.CAST_TO_DATETIME.getName(), Arrays.asList(value));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@ public enum BuiltinFunctionName {
CAST_TO_BOOLEAN(FunctionName.of("cast_to_boolean")),
CAST_TO_DATE(FunctionName.of("cast_to_date")),
CAST_TO_TIME(FunctionName.of("cast_to_time")),
CAST_TO_TIMESTAMP(FunctionName.of("cast_to_timestamp"));
CAST_TO_TIMESTAMP(FunctionName.of("cast_to_timestamp")),
CAST_TO_DATETIME(FunctionName.of("cast_to_datetime"));

private final FunctionName name;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import org.opensearch.sql.data.model.ExprBooleanValue;
import org.opensearch.sql.data.model.ExprByteValue;
import org.opensearch.sql.data.model.ExprDateValue;
import org.opensearch.sql.data.model.ExprDatetimeValue;
import org.opensearch.sql.data.model.ExprDoubleValue;
import org.opensearch.sql.data.model.ExprFloatValue;
import org.opensearch.sql.data.model.ExprIntegerValue;
Expand Down Expand Up @@ -80,6 +81,7 @@ public static void register(BuiltinFunctionRepository repository) {
repository.register(castToDate());
repository.register(castToTime());
repository.register(castToTimestamp());
repository.register(castToDatetime());
}


Expand Down Expand Up @@ -205,4 +207,15 @@ private static FunctionResolver castToTimestamp() {
impl(nullMissingHandling((v) -> v), TIMESTAMP, TIMESTAMP)
);
}

private static FunctionResolver castToDatetime() {
return FunctionDSL.define(BuiltinFunctionName.CAST_TO_DATETIME.getName(),
impl(nullMissingHandling(
(v) -> new ExprDatetimeValue(v.stringValue())), DATETIME, STRING),
impl(nullMissingHandling(
(v) -> new ExprDatetimeValue(v.datetimeValue())), DATETIME, TIMESTAMP),
impl(nullMissingHandling(
(v) -> new ExprDatetimeValue(v.datetimeValue())), DATETIME, DATE)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ public void castAnalyzer() {
);

assertThrows(IllegalStateException.class, () -> analyze(AstDSL.cast(AstDSL.unresolvedAttr(
"boolean_value"), AstDSL.stringLiteral("DATETIME"))));
"boolean_value"), AstDSL.stringLiteral("INTERVAL"))));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,17 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.opensearch.sql.data.type.ExprCoreType.ARRAY;
import static org.opensearch.sql.data.type.ExprCoreType.BOOLEAN;
import static org.opensearch.sql.data.type.ExprCoreType.DATE;
import static org.opensearch.sql.data.type.ExprCoreType.DATETIME;
import static org.opensearch.sql.data.type.ExprCoreType.DOUBLE;
import static org.opensearch.sql.data.type.ExprCoreType.FLOAT;
import static org.opensearch.sql.data.type.ExprCoreType.INTEGER;
import static org.opensearch.sql.data.type.ExprCoreType.LONG;
import static org.opensearch.sql.data.type.ExprCoreType.SHORT;
import static org.opensearch.sql.data.type.ExprCoreType.STRING;
import static org.opensearch.sql.data.type.ExprCoreType.STRUCT;
import static org.opensearch.sql.data.type.ExprCoreType.TIME;
import static org.opensearch.sql.data.type.ExprCoreType.TIMESTAMP;
import static org.opensearch.sql.data.type.ExprCoreType.UNDEFINED;
import static org.opensearch.sql.data.type.ExprCoreType.UNKNOWN;

Expand All @@ -59,7 +63,12 @@ public void isCompatible() {
assertTrue(FLOAT.isCompatible(LONG));
assertTrue(FLOAT.isCompatible(INTEGER));
assertTrue(FLOAT.isCompatible(SHORT));

assertTrue(BOOLEAN.isCompatible(STRING));
assertTrue(TIMESTAMP.isCompatible(STRING));
assertTrue(DATE.isCompatible(STRING));
assertTrue(TIME.isCompatible(STRING));
assertTrue(DATETIME.isCompatible(STRING));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,16 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.opensearch.sql.data.type.ExprCoreType.BOOLEAN;
import static org.opensearch.sql.data.type.ExprCoreType.BYTE;
import static org.opensearch.sql.data.type.ExprCoreType.DATE;
import static org.opensearch.sql.data.type.ExprCoreType.DATETIME;
import static org.opensearch.sql.data.type.ExprCoreType.DOUBLE;
import static org.opensearch.sql.data.type.ExprCoreType.FLOAT;
import static org.opensearch.sql.data.type.ExprCoreType.INTEGER;
import static org.opensearch.sql.data.type.ExprCoreType.LONG;
import static org.opensearch.sql.data.type.ExprCoreType.SHORT;
import static org.opensearch.sql.data.type.ExprCoreType.STRING;
import static org.opensearch.sql.data.type.ExprCoreType.TIME;
import static org.opensearch.sql.data.type.ExprCoreType.TIMESTAMP;
import static org.opensearch.sql.data.type.ExprCoreType.UNDEFINED;
import static org.opensearch.sql.data.type.WideningTypeRule.IMPOSSIBLE_WIDENING;
import static org.opensearch.sql.data.type.WideningTypeRule.TYPE_EQUAL;
Expand Down Expand Up @@ -73,6 +77,10 @@ class WideningTypeRuleTest {
.put(LONG, DOUBLE, 2)
.put(FLOAT, DOUBLE, 1)
.put(STRING, BOOLEAN, 1)
.put(STRING, TIMESTAMP, 1)
.put(STRING, DATE, 1)
.put(STRING, TIME, 1)
.put(STRING, DATETIME, 1)
.put(UNDEFINED, BYTE, 1)
.put(UNDEFINED, SHORT, 2)
.put(UNDEFINED, INTEGER, 3)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import static org.opensearch.sql.data.type.ExprCoreType.BOOLEAN;
import static org.opensearch.sql.data.type.ExprCoreType.BYTE;
import static org.opensearch.sql.data.type.ExprCoreType.DATE;
import static org.opensearch.sql.data.type.ExprCoreType.DATETIME;
import static org.opensearch.sql.data.type.ExprCoreType.DOUBLE;
import static org.opensearch.sql.data.type.ExprCoreType.FLOAT;
import static org.opensearch.sql.data.type.ExprCoreType.INTEGER;
Expand Down Expand Up @@ -366,4 +367,20 @@ void castToTimestamp() {
assertEquals(TIMESTAMP, expression.type());
assertEquals(new ExprTimestampValue("2012-08-07 01:01:01"), expression.valueOf(null));
}

@Test
void castToDatetime() {
FunctionExpression expression = dsl.castDatetime(DSL.literal("2012-08-07 01:01:01"));
assertEquals(DATETIME, expression.type());
assertEquals(new ExprDatetimeValue("2012-08-07 01:01:01"), expression.valueOf(null));

expression = dsl.castDatetime(DSL.literal(new ExprTimestampValue("2012-08-07 01:01:01")));
assertEquals(DATETIME, expression.type());
assertEquals(new ExprDatetimeValue("2012-08-07 01:01:01"), expression.valueOf(null));

expression = dsl.castDatetime(DSL.literal(new ExprDateValue("2012-08-07")));
assertEquals(DATETIME, expression.type());
assertEquals(new ExprDatetimeValue("2012-08-07 00:00:00"), expression.valueOf(null));
}

}
30 changes: 23 additions & 7 deletions docs/user/general/datatypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ The following matrix illustrates the conversions allowed by our query engine for
+--------------+------+-------+---------+------+-------+--------+---------+--------------+------+--------+-----------+------+------+----------+----------+-----------+-----+--------+-----------+---------+
| TEXT | | | | | | | | | N/A | IE | | | | X | X | X | X | X | X | X |
+--------------+------+-------+---------+------+-------+--------+---------+--------------+------+--------+-----------+------+------+----------+----------+-----------+-----+--------+-----------+---------+
| STRING | E | E | E | E | E | E | IE | X | X | N/A | E | E | E | X | X | X | X | X | X | X |
| STRING | E | E | E | E | E | E | IE | X | X | N/A | IE | IE | IE | IE | X | X | X | X | X | X |
+--------------+------+-------+---------+------+-------+--------+---------+--------------+------+--------+-----------+------+------+----------+----------+-----------+-----+--------+-----------+---------+
| TIMESTAMP | X | X | X | X | X | X | X | X | X | E | N/A | | | X | X | X | X | X | X | X |
+--------------+------+-------+---------+------+-------+--------+---------+--------------+------+--------+-----------+------+------+----------+----------+-----------+-----+--------+-----------+---------+
Expand Down Expand Up @@ -183,13 +183,14 @@ Here are a few examples for implicit type conversion::

os> SELECT
... 1 = 1.0,
... 'True' = true;
... 'True' = true,
... DATE('2021-06-10') < '2021-06-11';
fetched rows / total rows = 1/1
+-----------+-----------------+
| 1 = 1.0 | 'True' = true |
|-----------+-----------------|
| True | True |
+-----------+-----------------+
+-----------+-----------------+-------------------------------------+
| 1 = 1.0 | 'True' = true | DATE('2021-06-10') < '2021-06-11' |
|-----------+-----------------+-------------------------------------|
| True | True | True |
+-----------+-----------------+-------------------------------------+

Here are a few examples for explicit type conversion::

Expand Down Expand Up @@ -331,6 +332,21 @@ Conversion from TIMESTAMP

- Conversion from timestamp is much more straightforward. To convert it to date is to extract the date value, and conversion to time is to extract the time value. Conversion to datetime, it will extracts the datetime value and leave the timezone information over. For example, the result to convert datetime '2020-08-17 14:09:00' UTC to date is date '2020-08-17', to time is '14:09:00' and to datetime is datetime '2020-08-17 14:09:00'.

Conversion from string to date and time types
---------------------------------------------

A string can also represent and be converted to date and time types (except to interval type). As long as the string value is of valid format required by the target date and time types, the conversion can happen implicitly or explicitly as follows::

os> SELECT
... TIMESTAMP('2021-06-17 00:00:00') = '2021-06-17 00:00:00',
... '2021-06-18' < DATE('2021-06-17'),
... '10:20:00' <= TIME('11:00:00');
fetched rows / total rows = 1/1
+------------------------------------------------------------+-------------------------------------+----------------------------------+
| TIMESTAMP('2021-06-17 00:00:00') = '2021-06-17 00:00:00' | '2021-06-18' < DATE('2021-06-17') | '10:20:00' <= TIME('11:00:00') |
|------------------------------------------------------------+-------------------------------------+----------------------------------|
| True | False | True |
+------------------------------------------------------------+-------------------------------------+----------------------------------+

String Data Types
=================
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ false = 'False' as implicitCast
false = 'true' as implicitCast
'TRUE' = true as implicitCast
'false' = true as implicitCast
CAST('2021-06-17 00:00:00' AS TIMESTAMP) = '2021-06-17 00:00:00' as implicitCast
'2021-06-18' < CAST('2021-06-17' AS DATE) as implicitCast
'10:20:00' <= CAST('11:00:00' AS TIME) as implicitCast

0 comments on commit 21cad56

Please sign in to comment.