This is an automated email from the ASF dual-hosted git repository. alexpl pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push: new 8998f078f55 IGNITE-16806 Fix SQL query property type validation failure in case of implicit convertible types - Fixes #9988. 8998f078f55 is described below commit 8998f078f55fede320d62a5cd6df38f3f6285d1b Author: Mikhail Petrov <pmgheap....@gmail.com> AuthorDate: Thu May 12 15:21:01 2022 +0300 IGNITE-16806 Fix SQL query property type validation failure in case of implicit convertible types - Fixes #9988. Signed-off-by: Aleksey Plekhanov <plehanov.a...@gmail.com> --- .../apache/ignite/internal/binary/BinaryUtils.java | 7 + .../processors/query/GridQueryIndexing.java | 13 ++ .../processors/query/QueryTypeDescriptorImpl.java | 86 ++++----- .../processors/query/DummyQueryIndexing.java | 5 + .../internal/processors/query/h2/H2Utils.java | 41 ++++- .../processors/query/h2/IgniteH2Indexing.java | 17 ++ .../IgniteQueryConvertibleTypesValidationTest.java | 205 +++++++++++++++++++++ .../h2/H2ColumnTypeConversionCheckSelfTest.java | 57 ++++++ .../IgniteBinaryCacheQueryTestSuite3.java | 5 + 9 files changed, 389 insertions(+), 47 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java index f3203a627e1..60a00441737 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java @@ -2726,6 +2726,13 @@ public class BinaryUtils { return Object[].class == cls || BinaryArray.class == cls || BinaryEnumArray.class == cls; } + /** @return Type name of the specified object. If {@link BinaryObject} was specified its type will be returned. */ + public static String typeName(Object obj) { + return obj instanceof BinaryObject + ? ((BinaryObject)obj).type().typeName() + : obj == null ? null : obj.getClass().getSimpleName(); + } + /** * Enum type. */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java index 3629f254eb4..64f09934139 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java @@ -462,4 +462,17 @@ public interface GridQueryIndexing { default Map<String, Integer> secondaryIndexesInlineSize() { return Collections.emptyMap(); } + + /** + * Checks if object of the specified class can be stored in the specified table column by the query engine. + * + * @param schemaName Schema name. + * @param tblName Table name. + * @param colName Name of the column. + * @param cls Class to perform check on. + * @return Whether object of the specified class can be successfully stored and accessed from the SQL column by the + * query engine. + * @throws IgniteSQLException if table or column with specified name was not found. + */ + boolean isConvertibleToColumnType(String schemaName, String tblName, String colName, Class<?> cls); } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java index abe0fa475e2..bca1593bac2 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java @@ -44,6 +44,7 @@ import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; import org.jetbrains.annotations.Nullable; +import static org.apache.ignite.internal.binary.BinaryUtils.typeName; import static org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode.KEY_SCALE_OUT_OF_RANGE; import static org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode.NULL_KEY; import static org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode.NULL_VALUE; @@ -636,27 +637,9 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor { isKey ? NULL_KEY : NULL_VALUE); } - if (validateTypes && propVal != null) { - if (!(propVal instanceof BinaryObject) || propVal instanceof BinaryArray) { - if (!U.box(prop.type()).isAssignableFrom(U.box(propVal.getClass()))) { - // Some reference type arrays end up being converted to Object[] - if (!(prop.type().isArray() && BinaryUtils.isObjectArray(propVal.getClass()) && - Arrays.stream(BinaryUtils.rawArrayFromBinary(propVal)). - noneMatch(x -> x != null && !U.box(prop.type().getComponentType()).isAssignableFrom(U.box(x.getClass()))))) { - throw new IgniteSQLException("Type for a column '" + prop.name() + - "' is not compatible with table definition. Expected '" + - prop.type().getSimpleName() + "', actual type '" + - propVal.getClass().getSimpleName() + "'"); - } - } - } - else if (coCtx.kernalContext().cacheObjects().typeId(prop.type().getName()) != - ((BinaryObject)propVal).type().typeId()) { - throw new IgniteSQLException("Type for a column '" + prop.name() + - "' is not compatible with table definition. Expected '" + - prop.type().getSimpleName() + "', actual type '" + - ((BinaryObject)propVal).type().typeName() + "'"); - } + if (validateTypes && propVal != null && !isCompatibleWithPropertyType(propVal, prop.name(), prop.type())) { + throw new IgniteSQLException("Type for a column '" + prop.name() + "' is not compatible with table definition." + + " Expected '" + prop.type().getSimpleName() + "', actual type '" + typeName(propVal) + "'"); } if (propVal == null || prop.precision() == -1) @@ -721,36 +704,47 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor { if (propVal == null) continue; - if (!(propVal instanceof BinaryObject) || propVal instanceof BinaryArray) { - if (!U.box(propType).isAssignableFrom(U.box(propVal.getClass()))) { - // Some reference type arrays end up being converted to Object[] - if (!(propType.isArray() && BinaryUtils.isObjectArray(propVal.getClass()) && - Arrays.stream(BinaryUtils.rawArrayFromBinary(propVal)). - noneMatch(x -> x != null && !U.box(propType.getComponentType()).isAssignableFrom(U.box(x.getClass()))))) { - throw new IgniteSQLException("Type for a column '" + idxField + - "' is not compatible with index definition. Expected '" + - propType.getSimpleName() + "', actual type '" + - propVal.getClass().getSimpleName() + "'"); - } - } - } - else if (coCtx.kernalContext().cacheObjects().typeId(propType.getName()) != - ((BinaryObject)propVal).type().typeId()) { - // Check for classes/enums implementing indexed interfaces. - final Class<?> cls = U.classForName(((BinaryObject)propVal).type().typeName(), null, true); - - if ((cls == null && propType == Object.class) || (cls != null && propType.isAssignableFrom(cls))) - continue; - - throw new IgniteSQLException("Type for a column '" + idxField + - "' is not compatible with index definition. Expected '" + - propType.getSimpleName() + "', actual type '" + - ((BinaryObject)propVal).type().typeName() + "'"); + if (!isCompatibleWithPropertyType(propVal, idxField, propType)) { + throw new IgniteSQLException("Type for a column '" + idxField + "' is not compatible with index definition." + + " Expected '" + prop.type().getSimpleName() + "', actual type '" + typeName(propVal) + "'"); } } } } + /** + * Checks if the specified object is compatible with the type of the column through which this object will be accessed. + * + * @param val Object to check. + * @param colName Name of the column to which current property corresponds. + * @param expColType Type of the column based on Query Property info. + */ + private boolean isCompatibleWithPropertyType(Object val, String colName, Class<?> expColType) { + if (!(val instanceof BinaryObject) || val instanceof BinaryArray) { + if (U.box(expColType).isAssignableFrom(U.box(val.getClass()))) + return true; + + GridQueryIndexing indexing = coCtx.kernalContext().query().getIndexing(); + + assert indexing != null; + + if (indexing.isConvertibleToColumnType(schemaName, tableName(), colName, val.getClass())) + return true; + + return expColType.isArray() + && BinaryUtils.isObjectArray(val.getClass()) + && Arrays.stream(BinaryUtils.rawArrayFromBinary(val)) + .allMatch(x -> x == null || U.box(expColType.getComponentType()).isAssignableFrom(U.box(x.getClass()))); + } + else if (coCtx.kernalContext().cacheObjects().typeId(expColType.getName()) != ((BinaryObject)val).type().typeId()) { + final Class<?> cls = U.classForName(((BinaryObject)val).type().typeName(), null, true); + + return (cls == null && expColType == Object.class) || (cls != null && expColType.isAssignableFrom(cls)); + } + + return true; + } + /** {@inheritDoc} */ @SuppressWarnings("ForLoopReplaceableByForEach") @Override public void setDefaults(Object key, Object val) throws IgniteCheckedException { diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/query/DummyQueryIndexing.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/query/DummyQueryIndexing.java index ec746ba3f15..08e7ff25111 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/query/DummyQueryIndexing.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/query/DummyQueryIndexing.java @@ -318,4 +318,9 @@ public class DummyQueryIndexing implements GridQueryIndexing { String colNamePtrn) { return null; } + + /** {@inheritDoc} */ + @Override public boolean isConvertibleToColumnType(String schemaName, String tblName, String colName, Class<?> cls) { + return false; + } } diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Utils.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Utils.java index 52b0d1f5575..b55077bc854 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Utils.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Utils.java @@ -31,17 +31,21 @@ import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; import java.text.MessageFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.UUID; - import javax.cache.CacheException; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteException; @@ -153,6 +157,21 @@ public class H2Utils { /** Quotation character. */ private static final char ESC_CH = '\"'; + /** Types that can be implicitly converted to H2 column type. */ + private static final Map<Integer, Set<Class<?>>> CONVERTABLE_TYPES = new HashMap<Integer, Set<Class<?>>>() { + { + put(Value.TIMESTAMP, new HashSet<Class<?>>() { + { + add(LocalDateTime.class); + add(java.util.Date.class); + add(java.sql.Date.class); + } + }); + put(Value.TIME, Collections.singleton(LocalTime.class)); + put(Value.DATE, Collections.singleton(LocalDate.class)); + } + }; + /** * @param c1 First column. * @param c2 Second column. @@ -569,13 +588,33 @@ public class H2Utils { return false; } + /** + * @param cls The class whose convertibility is to be tested. + * @param colType Column target type. + * @return Whether specified class can be implicitly converted to the specified type. + * @see #wrap(CacheObjectValueContext, Object, int) + */ + public static boolean isConvertableToColumnType(Class<?> cls, int colType) { + assert cls != null; + + if (DataType.getTypeClassName(colType).equals(cls.getName())) + return true; + + Set<Class<?>> types = CONVERTABLE_TYPES.get(colType); + + return types != null && types.contains(cls); + } + /** * Wraps object to respective {@link Value}. * + * Note, implicit type conversions must be also included into {@link #CONVERTABLE_TYPES}. + * * @param obj Object. * @param type Value type. * @return Value. * @throws IgniteCheckedException If failed. + * @see #isConvertableToColumnType(Class, int) */ @SuppressWarnings("ConstantConditions") public static Value wrap(CacheObjectValueContext coCtx, Object obj, int type) throws IgniteCheckedException { diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java index 868895055f4..f3679f277d5 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java @@ -185,6 +185,7 @@ import org.h2.engine.Session; import org.h2.engine.SysProperties; import org.h2.index.Index; import org.h2.index.IndexType; +import org.h2.message.DbException; import org.h2.table.Column; import org.h2.table.IndexColumn; import org.h2.table.TableType; @@ -2068,6 +2069,22 @@ public class IgniteH2Indexing implements GridQueryIndexing { return infos; } + /** {@inheritDoc} */ + @Override public boolean isConvertibleToColumnType(String schemaName, String tblName, String colName, Class<?> cls) { + GridH2Table table = schemaMgr.dataTable(schemaName, tblName); + + if (table == null) + throw new IgniteSQLException("Table was not found [schemaName=" + schemaName + ", tableName=" + tblName + ']'); + + try { + return H2Utils.isConvertableToColumnType(cls, table.getColumn(colName).getType()); + } + catch (DbException e) { + throw new IgniteSQLException("Colum with specified name was not found for the table [schemaName=" + schemaName + + ", tableName=" + tblName + ", colName=" + colName + ']', e); + } + } + /** {@inheritDoc} */ @Override public boolean isStreamableInsertStatement(String schemaName, SqlFieldsQuery qry) throws SQLException { QueryParserResult parsed = parser.parse(schemaName, qry, true); diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteQueryConvertibleTypesValidationTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteQueryConvertibleTypesValidationTest.java new file mode 100644 index 00000000000..dc045273406 --- /dev/null +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteQueryConvertibleTypesValidationTest.java @@ -0,0 +1,205 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.apache.ignite.internal.processors.query; + +import java.io.Serializable; +import java.sql.Timestamp; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Objects; +import java.util.function.Function; +import org.apache.ignite.cache.QueryEntity; +import org.apache.ignite.cache.query.SqlFieldsQuery; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import static java.util.Collections.singletonList; +import static org.h2.util.LocalDateTimeUtils.localDateToDateValue; +import static org.h2.util.LocalDateTimeUtils.localTimeToTimeValue; + +/** */ +@RunWith(Parameterized.class) +public class IgniteQueryConvertibleTypesValidationTest extends GridCommonAbstractTest { + /** */ + @Parameterized.Parameter() + public boolean isValidationEnabled; + + /** */ + @Parameterized.Parameter(1) + public String sqlType; + + /** */ + @Parameterized.Parameter(2) + public Class<?> objType; + + /** */ + @Parameterized.Parameter(3) + public Function<Object, Object> sqlTypeConverter; + + /** */ + @Parameterized.Parameter(4) + public boolean isDdl; + + /** */ + @Parameterized.Parameters(name = "isValidationEnabled={0}, sqlType={1}, testObjCls={2}, isDdl={4}") + public static Collection<Object[]> parameters() { + Collection<Object[]> params = new ArrayList<>(); + + for (boolean isV : Arrays.asList(true, false)) { + for (boolean isDdl : Arrays.asList(true, false)) { + params.add(new Object[] {isV, "TIMESTAMP", LocalDateTime.class, f(d -> Timestamp.valueOf((LocalDateTime)d)), isDdl}); + params.add(new Object[] {isV, "TIMESTAMP", Date.class, f(d -> new Timestamp(((Date)d).getTime())), isDdl}); + params.add(new Object[] {isV, "TIMESTAMP", java.sql.Date.class, f(d -> new Timestamp(((Date)d).getTime())), isDdl}); + params.add(new Object[] {isV, "DATE", LocalDate.class, f(d -> localDateToDateValue(d).getDate()), isDdl}); + params.add(new Object[] {isV, "TIME", LocalTime.class, f(d -> localTimeToTimeValue(d).getTime()), isDdl}); + } + } + + return params; + } + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); + + cfg.getSqlConfiguration().setValidationEnabled(isValidationEnabled); + + return cfg; + } + + /** {@inheritDoc} */ + @Override protected void beforeTest() throws Exception { + super.beforeTest(); + + stopAllGrids(); + } + + /** */ + @Test + public void testIgniteQueryConvertibleTypesValidation() throws Exception { + startGrid(0); + + createTable(); + + execute("CREATE INDEX DATA_IDX ON DATA(data DESC);"); + + Object testObj = generateTestObject(objType); + + execute("INSERT INTO DATA(_key, id, data) values(?, ?, ?)", 0, 0, testObj); + + grid(0).cache(DEFAULT_CACHE_NAME).put(1, new Data(1, testObj)); + + grid(0).cache(DEFAULT_CACHE_NAME).put(2, grid(0).binary().toBinary(new Data(2, testObj))); + + List<List<?>> selectData = execute("SELECT data FROM DATA"); + + Object sqlObj = sqlTypeConverter.apply(testObj); + + assertTrue(selectData.get(0).stream().allMatch(d -> Objects.equals(sqlObj, d))); + } + + /** */ + private void createTable() { + if (isDdl) { + execute("CREATE TABLE DATA (id INT PRIMARY KEY, data " + sqlType + ") WITH" + + " \"KEY_TYPE=java.lang.Integer" + + ", VALUE_TYPE=org.apache.ignite.internal.processors.query.IgniteQueryConvertibleTypesValidationTest$Data" + + ", CACHE_NAME=default\""); + } + else { + QueryEntity projEntity = new QueryEntity(); + projEntity.setKeyType(Integer.class.getName()); + projEntity.setValueType(Data.class.getName()); + projEntity.addQueryField("id", Integer.class.getName(), null); + projEntity.addQueryField("data", toClassName(sqlType), null); + + projEntity.setTableName("DATA"); + + grid(0).createCache(new CacheConfiguration<>(DEFAULT_CACHE_NAME) + .setQueryEntities(singletonList(projEntity)) + .setSqlSchema("PUBLIC")); + } + } + + /** */ + private List<List<?>> execute(String qry, Object... args) { + return grid(0).context().query().querySqlFields(new SqlFieldsQuery(qry).setArgs(args), false).getAll(); + } + + /** */ + private static Object generateTestObject(Class<?> cls) { + if (cls == LocalDateTime.class) + return LocalDateTime.now(); + else if (cls == LocalTime.class) + return LocalTime.now(); + else if (cls == LocalDate.class) + return LocalDate.now(); + else if (cls == Date.class) + return Date.from(Instant.now()); + else if (cls == java.sql.Date.class) + return java.sql.Date.valueOf(LocalDate.now()); + else if (cls == java.sql.Time.class) + return java.sql.Time.valueOf(LocalTime.now()); + else + throw new IllegalStateException(); + } + + /** */ + private static String toClassName(String sqlType) { + switch (sqlType) { + case "TIMESTAMP" : return java.sql.Timestamp.class.getName(); + case "TIME" : return java.sql.Time.class.getName(); + case "DATE" : return java.sql.Date.class.getName(); + default: throw new IllegalStateException(); + } + } + + /** */ + private static <T, R> Function<Object, Object> f(Function<T, R> f) { + return (Function<Object, Object>)f; + } + + /** */ + public static class Data implements Serializable { + /** Serial version UID. */ + private static final long serialVersionUID = 1L; + + /** */ + public int id; + + /** */ + public Object data; + + /** */ + public Data(int id, Object data) { + this.id = id; + this.data = data; + } + } +} diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/H2ColumnTypeConversionCheckSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/H2ColumnTypeConversionCheckSelfTest.java new file mode 100644 index 00000000000..ed73d1edc7b --- /dev/null +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/H2ColumnTypeConversionCheckSelfTest.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.apache.ignite.internal.processors.query.h2; + +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import org.apache.ignite.cache.query.SqlFieldsQuery; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import org.junit.Test; + +/** */ +public class H2ColumnTypeConversionCheckSelfTest extends GridCommonAbstractTest { + /** */ + @Test + public void testConversions() throws Exception { + startGrid(0); + + grid(0).context().query().querySqlFields( + new SqlFieldsQuery("CREATE TABLE TBL (id INT PRIMARY KEY, timestamp TIMESTAMP, date DATE, time TIME)"), false); + + assertTrue(isConvertible("TIMESTAMP", Timestamp.class)); + assertTrue(isConvertible("TIMESTAMP", java.sql.Date.class)); + assertTrue(isConvertible("TIMESTAMP", java.util.Date.class)); + assertTrue(isConvertible("TIMESTAMP", LocalDateTime.class)); + assertFalse(isConvertible("TIMESTAMP", Integer.class)); + + assertTrue(isConvertible("DATE", java.sql.Date.class)); + assertTrue(isConvertible("DATE", LocalDate.class)); + assertFalse(isConvertible("DATE", Integer.class)); + + assertTrue(isConvertible("TIME", java.sql.Time.class)); + assertTrue(isConvertible("TIME", LocalTime.class)); + assertFalse(isConvertible("TIME", Integer.class)); + } + + /** */ + private boolean isConvertible(String colName, Class<?> cls) { + return grid(0).context().query().getIndexing().isConvertibleToColumnType("PUBLIC", "TBL", colName, cls); + } +} diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite3.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite3.java index 225a9a54521..8d53763a233 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite3.java +++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite3.java @@ -100,6 +100,7 @@ import org.apache.ignite.internal.processors.cache.metric.SqlViewExporterSpiTest import org.apache.ignite.internal.processors.cache.query.IgniteCacheQueryCacheDestroySelfTest; import org.apache.ignite.internal.processors.cache.query.ScanQueryConcurrentSqlUpdatesTest; import org.apache.ignite.internal.processors.cache.query.ScanQueryConcurrentUpdatesTest; +import org.apache.ignite.internal.processors.query.IgniteQueryConvertibleTypesValidationTest; import org.apache.ignite.internal.processors.query.IgniteQueryDedicatedPoolTest; import org.apache.ignite.internal.processors.query.IgniteSqlCustomSchemaTest; import org.apache.ignite.internal.processors.query.IgniteSqlCustomSchemaWithPdsEnabled; @@ -135,6 +136,7 @@ import org.apache.ignite.internal.processors.query.SqlQueryHistorySelfTest; import org.apache.ignite.internal.processors.query.SqlSystemViewsSelfTest; import org.apache.ignite.internal.processors.query.h2.GridIndexRebuildSelfTest; import org.apache.ignite.internal.processors.query.h2.GridIndexRebuildTest; +import org.apache.ignite.internal.processors.query.h2.H2ColumnTypeConversionCheckSelfTest; import org.apache.ignite.internal.processors.query.h2.QueryParserMetricsHolderSelfTest; import org.apache.ignite.internal.processors.query.h2.RowCountTableStatisticsSurvivesNodeRestartTest; import org.apache.ignite.internal.processors.query.h2.RowCountTableStatisticsUsageTest; @@ -370,6 +372,9 @@ import org.junit.runners.Suite; InlineIndexKeyTypeRegistryTest.class, + IgniteQueryConvertibleTypesValidationTest.class, + H2ColumnTypeConversionCheckSelfTest.class, + IgniteStatisticsTestSuite.class, // CDC tests.