diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/ast/expression/DataType.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/ast/expression/DataType.java index 6d1cf95b0c..13e6b422bd 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/ast/expression/DataType.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/ast/expression/DataType.java @@ -26,7 +26,7 @@ @RequiredArgsConstructor public enum DataType { TYPE_ERROR(ExprCoreType.UNKNOWN), - NULL(ExprCoreType.UNKNOWN), + NULL(ExprCoreType.UNDEFINED), INTEGER(ExprCoreType.INTEGER), LONG(ExprCoreType.LONG), diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprMissingValue.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprMissingValue.java index 635d8f2fd4..f2050561a7 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprMissingValue.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprMissingValue.java @@ -17,7 +17,6 @@ import com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType; import com.amazon.opendistroforelasticsearch.sql.data.type.ExprType; -import com.amazon.opendistroforelasticsearch.sql.exception.ExpressionEvaluationException; import java.util.Objects; /** @@ -40,7 +39,7 @@ public Object value() { @Override public ExprType type() { - return ExprCoreType.UNKNOWN; + return ExprCoreType.UNDEFINED; } @Override diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprNullValue.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprNullValue.java index 2638e62537..321587d1de 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprNullValue.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprNullValue.java @@ -49,7 +49,7 @@ public Object value() { @Override public ExprType type() { - return ExprCoreType.UNKNOWN; + return ExprCoreType.UNDEFINED; } @Override diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/type/ExprCoreType.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/type/ExprCoreType.java index 48202206a9..7928dbb85f 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/type/ExprCoreType.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/type/ExprCoreType.java @@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -29,14 +30,21 @@ */ public enum ExprCoreType implements ExprType { /** - * UNKNOWN. + * Unknown due to unsupported data type. */ UNKNOWN, + /** + * Undefined type for special literal such as NULL. + * As the root of data type tree, it is compatible with any other type. + * In other word, undefined type is the "narrowest" type. + */ + UNDEFINED, + /** * Numbers. */ - BYTE, + BYTE(UNDEFINED), SHORT(BYTE), INTEGER(SHORT), LONG(INTEGER), @@ -46,38 +54,38 @@ public enum ExprCoreType implements ExprType { /** * Boolean. */ - BOOLEAN, + BOOLEAN(UNDEFINED), /** * String. */ - STRING, + STRING(UNDEFINED), /** * Date. * Todo. compatible relationship. */ - TIMESTAMP, - DATE, - TIME, - DATETIME, - INTERVAL, + TIMESTAMP(UNDEFINED), + DATE(UNDEFINED), + TIME(UNDEFINED), + DATETIME(UNDEFINED), + INTERVAL(UNDEFINED), /** * Struct. */ - STRUCT, + STRUCT(UNDEFINED), /** * Array. */ - ARRAY; + ARRAY(UNDEFINED); /** - * Parent of current base type. + * Parents (wider/compatible types) of current base type. */ - private ExprCoreType parent; + private final List parents = new ArrayList<>(); /** * The mapping between Type and legacy JDBC type name. @@ -91,13 +99,13 @@ public enum ExprCoreType implements ExprType { ExprCoreType(ExprCoreType... compatibleTypes) { for (ExprCoreType subType : compatibleTypes) { - subType.parent = this; + subType.parents.add(this); } } @Override public List getParent() { - return Arrays.asList(parent == null ? UNKNOWN : parent); + return parents.isEmpty() ? ExprType.super.getParent() : parents; } @Override @@ -113,9 +121,11 @@ public String legacyTypeName() { /** * Return all the valid ExprCoreType. */ - public static List coreTypes() { - return Arrays.stream(ExprCoreType.values()).filter(type -> type != UNKNOWN) - .collect(Collectors.toList()); + public static List coreTypes() { + return Arrays.stream(ExprCoreType.values()) + .filter(type -> type != UNKNOWN) + .filter(type -> type != UNDEFINED) + .collect(Collectors.toList()); } public static List numberTypes() { diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/conditional/cases/CaseClause.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/conditional/cases/CaseClause.java index f00a6cfd12..2303bdae1d 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/conditional/cases/CaseClause.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/conditional/cases/CaseClause.java @@ -16,7 +16,7 @@ package com.amazon.opendistroforelasticsearch.sql.expression.conditional.cases; -import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.UNKNOWN; +import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.UNDEFINED; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprNullValue; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValue; @@ -75,8 +75,8 @@ public ExprValue valueOf(Environment valueEnv) { public ExprType type() { List types = allResultTypes(); - // Return unknown if all WHEN/ELSE return NULL - return types.isEmpty() ? UNKNOWN : types.get(0); + // Return undefined if all WHEN/ELSE return NULL + return types.isEmpty() ? UNDEFINED : types.get(0); } @Override @@ -98,7 +98,7 @@ public List allResultTypes() { types.add(defaultResult.type()); } - types.removeIf(type -> (type == UNKNOWN)); + types.removeIf(type -> (type == UNDEFINED)); return types; } diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/predicate/UnaryPredicateOperator.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/predicate/UnaryPredicateOperator.java index 2770bfec9f..8378bf1ad4 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/predicate/UnaryPredicateOperator.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/predicate/UnaryPredicateOperator.java @@ -102,7 +102,7 @@ private static FunctionResolver isNotNull() { private static FunctionResolver ifNull() { FunctionName functionName = BuiltinFunctionName.IFNULL.getName(); - List typeList = ExprCoreType.coreTypes(); + List typeList = ExprCoreType.coreTypes(); List>> functionsOne = typeList.stream().map(v -> @@ -121,7 +121,7 @@ private static FunctionResolver ifNull() { private static FunctionResolver nullIf() { FunctionName functionName = BuiltinFunctionName.NULLIF.getName(); - List typeList = ExprCoreType.coreTypes(); + List typeList = ExprCoreType.coreTypes(); FunctionResolver functionResolver = FunctionDSL.define(functionName, diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprMissingValueTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprMissingValueTest.java index 26f2ea3699..583e61b749 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprMissingValueTest.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprMissingValueTest.java @@ -43,7 +43,7 @@ public void getValue() { @Test public void getType() { - assertEquals(ExprCoreType.UNKNOWN, LITERAL_MISSING.type()); + assertEquals(ExprCoreType.UNDEFINED, LITERAL_MISSING.type()); } @Test diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprNullValueTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprNullValueTest.java index ca4c85cb6e..fd3103c37b 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprNullValueTest.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprNullValueTest.java @@ -42,7 +42,7 @@ public void getValue() { @Test public void getType() { - assertEquals(ExprCoreType.UNKNOWN, LITERAL_NULL.type()); + assertEquals(ExprCoreType.UNDEFINED, LITERAL_NULL.type()); } @Test diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/data/type/ExprTypeTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/data/type/ExprTypeTest.java index 0e1b4f859e..215dd370eb 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/data/type/ExprTypeTest.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/data/type/ExprTypeTest.java @@ -25,6 +25,7 @@ import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.SHORT; import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.STRING; import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.STRUCT; +import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.UNDEFINED; import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.UNKNOWN; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -51,6 +52,12 @@ public void isCompatible() { assertFalse(INTEGER.isCompatible(UNKNOWN)); } + @Test + public void isCompatibleWithUndefined() { + ExprCoreType.coreTypes().forEach(type -> assertTrue(type.isCompatible(UNDEFINED))); + ExprCoreType.coreTypes().forEach(type -> assertFalse(UNDEFINED.isCompatible(type))); + } + @Test public void getParent() { assertThat(((ExprType) () -> "test").getParent(), Matchers.contains(UNKNOWN)); diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/conditional/cases/CaseClauseTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/conditional/cases/CaseClauseTest.java index de91e9c32c..18ebf91719 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/conditional/cases/CaseClauseTest.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/conditional/cases/CaseClauseTest.java @@ -77,7 +77,7 @@ void should_use_type_of_when_clause() { @Test void should_use_type_of_nonnull_when_or_else_clause() { - when(whenClause.type()).thenReturn(ExprCoreType.UNKNOWN); + when(whenClause.type()).thenReturn(ExprCoreType.UNDEFINED); Expression defaultResult = mock(Expression.class); when(defaultResult.type()).thenReturn(ExprCoreType.STRING); @@ -87,12 +87,12 @@ void should_use_type_of_nonnull_when_or_else_clause() { @Test void should_use_unknown_type_of_if_all_when_and_else_return_null() { - when(whenClause.type()).thenReturn(ExprCoreType.UNKNOWN); + when(whenClause.type()).thenReturn(ExprCoreType.UNDEFINED); Expression defaultResult = mock(Expression.class); - when(defaultResult.type()).thenReturn(ExprCoreType.UNKNOWN); + when(defaultResult.type()).thenReturn(ExprCoreType.UNDEFINED); CaseClause caseClause = new CaseClause(ImmutableList.of(whenClause), defaultResult); - assertEquals(ExprCoreType.UNKNOWN, caseClause.type()); + assertEquals(ExprCoreType.UNDEFINED, caseClause.type()); } @Test @@ -109,9 +109,9 @@ void should_return_all_result_types_including_default() { @Test void should_return_all_result_types_excluding_null_result() { - when(whenClause.type()).thenReturn(ExprCoreType.UNKNOWN); + when(whenClause.type()).thenReturn(ExprCoreType.UNDEFINED); Expression defaultResult = mock(Expression.class); - when(defaultResult.type()).thenReturn(ExprCoreType.UNKNOWN); + when(defaultResult.type()).thenReturn(ExprCoreType.UNDEFINED); CaseClause caseClause = new CaseClause(ImmutableList.of(whenClause), defaultResult); assertEquals( diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/function/WideningTypeRuleTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/function/WideningTypeRuleTest.java index 0e614a4df5..76e07b8d36 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/function/WideningTypeRuleTest.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/function/WideningTypeRuleTest.java @@ -21,7 +21,7 @@ import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.INTEGER; import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.LONG; import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.SHORT; -import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.UNKNOWN; +import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.UNDEFINED; import static com.amazon.opendistroforelasticsearch.sql.data.type.WideningTypeRule.IMPOSSIBLE_WIDENING; import static com.amazon.opendistroforelasticsearch.sql.data.type.WideningTypeRule.TYPE_EQUAL; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -33,10 +33,9 @@ import com.google.common.collect.ImmutableTable; import com.google.common.collect.Lists; import com.google.common.collect.Table; -import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; import java.util.stream.Stream; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -60,12 +59,16 @@ class WideningTypeRuleTest { .put(LONG, FLOAT, 1) .put(LONG, DOUBLE, 2) .put(FLOAT, DOUBLE, 1) + .put(UNDEFINED, BYTE, 1) + .put(UNDEFINED, SHORT, 2) + .put(UNDEFINED, INTEGER, 3) + .put(UNDEFINED, LONG, 4) + .put(UNDEFINED, FLOAT, 5) + .put(UNDEFINED, DOUBLE, 6) .build(); private static Stream distanceArguments() { - List exprTypes = - Arrays.asList(ExprCoreType.values()).stream().filter(type -> type != UNKNOWN).collect( - Collectors.toList()); + List exprTypes = ExprCoreType.coreTypes(); return Lists.cartesianProduct(exprTypes, exprTypes).stream() .map(list -> { ExprCoreType type1 = list.get(0); @@ -81,9 +84,7 @@ private static Stream distanceArguments() { } private static Stream validMaxTypes() { - List exprTypes = - Arrays.asList(ExprCoreType.values()).stream().filter(type -> type != UNKNOWN).collect( - Collectors.toList()); + List exprTypes = ExprCoreType.coreTypes(); return Lists.cartesianProduct(exprTypes, exprTypes).stream() .map(list -> { ExprCoreType type1 = list.get(0); @@ -117,4 +118,13 @@ public void max(ExprCoreType v1, ExprCoreType v2, ExprCoreType expected) { assertEquals(expected, WideningTypeRule.max(v1, v2)); } } + + @Test + public void maxOfUndefinedAndOthersShouldBeTheOtherType() { + ExprCoreType.coreTypes().forEach(type -> + assertEquals(type, WideningTypeRule.max(type, UNDEFINED))); + ExprCoreType.coreTypes().forEach(type -> + assertEquals(type, WideningTypeRule.max(UNDEFINED, type))); + } + } \ No newline at end of file diff --git a/docs/user/general/datatypes.rst b/docs/user/general/datatypes.rst index 8ad7be0a96..768eb85064 100644 --- a/docs/user/general/datatypes.rst +++ b/docs/user/general/datatypes.rst @@ -103,9 +103,24 @@ The table below list the mapping between Elasticsearch Data Type, ODFE SQL Data | nested | array | STRUCT | +--------------------+---------------+-----------+ -Notes: Not all the ODFE SQL Type has correspond Elasticsearch Type. e.g. data and time. To use function which required such data type, user should explict convert the data type. +Notes: Not all the ODFE SQL Type has correspond Elasticsearch Type. e.g. data and time. To use function which required such data type, user should explicitly convert the data type. +Undefined Data Type +=================== + +The type of a null literal is special and different from any existing one. In this case, an ``UNDEFINED`` type is in use when the type cannot be inferred at "compile time" (during query parsing and analyzing). The corresponding SQL type is NULL according to JDBC specification. Because this undefined type is compatible with any other type by design, a null literal can be accepted as a valid operand or function argument. + +Here are examples for NULL literal and expressions with NULL literal involved:: + + od> SELECT NULL, NULL = NULL, 1 + NULL, LENGTH(NULL); + fetched rows / total rows = 1/1 + +--------+---------------+------------+----------------+ + | NULL | NULL = NULL | 1 + NULL | LENGTH(NULL) | + |--------+---------------+------------+----------------| + | null | null | null | null | + +--------+---------------+------------+----------------+ + Numeric Data Types ================== diff --git a/docs/user/index.rst b/docs/user/index.rst index d34326d949..e4a39b560b 100644 --- a/docs/user/index.rst +++ b/docs/user/index.rst @@ -23,6 +23,8 @@ Open Distro for Elasticsearch SQL enables you to extract insights out of Elastic - `Data Types `_ + - `Values `_ + * **Data Query Language** - `Expressions `_ diff --git a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/sql/NullLiteralIT.java b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/sql/NullLiteralIT.java new file mode 100644 index 0000000000..78881da080 --- /dev/null +++ b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/sql/NullLiteralIT.java @@ -0,0 +1,63 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + * + */ + +package com.amazon.opendistroforelasticsearch.sql.sql; + +import static com.amazon.opendistroforelasticsearch.sql.util.MatcherUtils.rows; +import static com.amazon.opendistroforelasticsearch.sql.util.MatcherUtils.schema; +import static com.amazon.opendistroforelasticsearch.sql.util.MatcherUtils.verifyDataRows; +import static com.amazon.opendistroforelasticsearch.sql.util.MatcherUtils.verifySchema; + +import com.amazon.opendistroforelasticsearch.sql.legacy.SQLIntegTestCase; +import org.json.JSONObject; +import org.junit.Test; + +/** + * This manual IT for NULL literal cannot be replaced with comparison test because other database + * has different type for expression with NULL involved, such as NULL rather than concrete type + * inferred like what we do in core engine. + */ +public class NullLiteralIT extends SQLIntegTestCase { + + @Test + public void testNullLiteralSchema() { + verifySchema( + query("SELECT NULL, ABS(NULL), 1 + NULL, NULL + 1.0"), + schema("NULL", "undefined"), + schema("ABS(NULL)", "byte"), + schema("1 + NULL", "integer"), + schema("NULL + 1.0", "double")); + } + + @Test + public void testNullLiteralInOperator() { + verifyDataRows( + query("SELECT NULL = NULL, NULL AND TRUE"), + rows(null, null)); + } + + @Test + public void testNullLiteralInFunction() { + verifyDataRows( + query("SELECT ABS(NULL), POW(2, FLOOR(NULL))"), + rows(null, null)); + } + + private JSONObject query(String sql) { + return new JSONObject(executeQuery(sql, "jdbc")); + } + +} diff --git a/integ-test/src/test/resources/correctness/expressions/literals.txt b/integ-test/src/test/resources/correctness/expressions/literals.txt index c56aa9ef05..b70fa41e83 100644 --- a/integ-test/src/test/resources/correctness/expressions/literals.txt +++ b/integ-test/src/test/resources/correctness/expressions/literals.txt @@ -1,3 +1,5 @@ +null +NULL 1 true -4.567 diff --git a/sql-jdbc/src/main/java/com/amazon/opendistroforelasticsearch/jdbc/types/ElasticsearchType.java b/sql-jdbc/src/main/java/com/amazon/opendistroforelasticsearch/jdbc/types/ElasticsearchType.java index 8c71bf8103..a4a49fd381 100644 --- a/sql-jdbc/src/main/java/com/amazon/opendistroforelasticsearch/jdbc/types/ElasticsearchType.java +++ b/sql-jdbc/src/main/java/com/amazon/opendistroforelasticsearch/jdbc/types/ElasticsearchType.java @@ -77,6 +77,7 @@ public enum ElasticsearchType { TIMESTAMP(JDBCType.TIMESTAMP, Timestamp.class, 24, 24, false), BINARY(JDBCType.VARBINARY, String.class, Integer.MAX_VALUE, 0, false), NULL(JDBCType.NULL, null, 0, 0, false), + UNDEFINED(JDBCType.NULL, null, 0, 0, false), UNSUPPORTED(JDBCType.OTHER, null, 0, 0, false); private static final Map jdbcTypeToESTypeMap; @@ -84,7 +85,7 @@ public enum ElasticsearchType { static { // Map JDBCType to corresponding ElasticsearchType jdbcTypeToESTypeMap = new HashMap<>(); - jdbcTypeToESTypeMap.put(JDBCType.NULL, NULL); + jdbcTypeToESTypeMap.put(JDBCType.NULL, UNDEFINED); jdbcTypeToESTypeMap.put(JDBCType.BOOLEAN, BOOLEAN); jdbcTypeToESTypeMap.put(JDBCType.TINYINT, BYTE); jdbcTypeToESTypeMap.put(JDBCType.SMALLINT, SHORT); diff --git a/sql-jdbc/src/main/java/com/amazon/opendistroforelasticsearch/jdbc/types/TypeConverters.java b/sql-jdbc/src/main/java/com/amazon/opendistroforelasticsearch/jdbc/types/TypeConverters.java index 1b11cdfb1e..6d15590378 100644 --- a/sql-jdbc/src/main/java/com/amazon/opendistroforelasticsearch/jdbc/types/TypeConverters.java +++ b/sql-jdbc/src/main/java/com/amazon/opendistroforelasticsearch/jdbc/types/TypeConverters.java @@ -18,6 +18,7 @@ import java.sql.Date; import java.sql.JDBCType; +import java.sql.SQLException; import java.sql.Time; import java.sql.Timestamp; import java.util.Arrays; @@ -63,6 +64,8 @@ public class TypeConverters { tcMap.put(JDBCType.BIGINT, new BigIntTypeConverter()); tcMap.put(JDBCType.BINARY, new BinaryTypeConverter()); + + tcMap.put(JDBCType.NULL, new NullTypeConverter()); } public static TypeConverter getInstance(JDBCType jdbcType) { @@ -302,4 +305,14 @@ public Class getDefaultJavaClass() { return Short.class; } } + + public static class NullTypeConverter implements TypeConverter { + + @Override + public T convert(Object value, Class clazz, Map conversionParams) { + // As Javadoc for ResultSet.getObject() API, a SQL NULL needs to be converted to + // a JAVA null here + return null; + } + } } diff --git a/sql-jdbc/src/test/java/com/amazon/opendistroforelasticsearch/jdbc/types/TypesTests.java b/sql-jdbc/src/test/java/com/amazon/opendistroforelasticsearch/jdbc/types/TypesTests.java index 055cf27f7f..c62186608a 100644 --- a/sql-jdbc/src/test/java/com/amazon/opendistroforelasticsearch/jdbc/types/TypesTests.java +++ b/sql-jdbc/src/test/java/com/amazon/opendistroforelasticsearch/jdbc/types/TypesTests.java @@ -16,9 +16,18 @@ package com.amazon.opendistroforelasticsearch.jdbc.types; +import static java.util.Collections.emptyMap; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.sql.JDBCType; +import java.sql.SQLException; +import org.junit.jupiter.api.Test; + public class TypesTests { - - public void testIntegerTypeConverter() { - TypeConverters.IntegerTypeConverter tc = new TypeConverters.IntegerTypeConverter(); + + @Test + public void testNullTypeConverter() throws SQLException { + TypeConverter tc = TypeConverters.getInstance(JDBCType.NULL); + assertNull(tc.convert(null, Object.class, emptyMap())); } }