You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by al...@apache.org on 2020/07/20 13:05:48 UTC

[ignite] branch master updated: IGNITE-13110 Add validation of SQL field types on cache put - Fixes #8055.

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 2e8d5a7  IGNITE-13110 Add validation of SQL field types on cache put - Fixes #8055.
2e8d5a7 is described below

commit 2e8d5a7c32b04e43ae5411d9c39dcd5f7585c343
Author: Sergey Kalashnikov <zk...@gmail.com>
AuthorDate: Mon Jul 20 17:45:18 2020 +0500

    IGNITE-13110 Add validation of SQL field types on cache put - Fixes #8055.
    
    Signed-off-by: Aleksey Plekhanov <pl...@gmail.com>
---
 .../ignite/configuration/SqlConfiguration.java     |  25 ++
 .../processors/query/QueryTypeDescriptorImpl.java  |  37 ++-
 ...SqlFieldTypeValidationOnKeyValueInsertTest.java | 325 +++++++++++++++++++++
 .../query/SqlFieldTypeValidationTypesTest.java     | 202 +++++++++++++
 .../IgniteBinaryCacheQueryTestSuite.java           |   6 +-
 5 files changed, 587 insertions(+), 8 deletions(-)

diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/SqlConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/SqlConfiguration.java
index e3dd475..6b297cd 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/SqlConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/SqlConfiguration.java
@@ -45,6 +45,9 @@ public class SqlConfiguration {
     /** SQL query history size. */
     private int sqlQryHistSize = DFLT_SQL_QUERY_HISTORY_SIZE;
 
+    /** Enable validation of key & values against sql schema. */
+    private boolean validationEnabled;
+
     /**
      * Defines the default query timeout.
      *
@@ -147,6 +150,28 @@ public class SqlConfiguration {
         return this;
     }
 
+    /**
+     * Is key & value validation enabled.
+     *
+     * @return {@code true} When key & value shall be validated against SQL schema.
+     */
+    public boolean isValidationEnabled() {
+        return validationEnabled;
+    }
+
+    /**
+     * Enable/disable key & value validation.
+     *
+     * @param validationEnabled {@code true} When key & value shall be validated against SQL schema.
+     * Default value is {@code false}.
+     * @return {@code this} for chaining.
+     */
+    public SqlConfiguration setValidationEnabled(boolean validationEnabled) {
+        this.validationEnabled = validationEnabled;
+
+        return this;
+    }
+
     /** {@inheritDoc} */
     @Override public String toString() {
         return S.toString(SqlConfiguration.class, this);
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 fb0b664..21e0154 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
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.processors.query;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -36,6 +37,7 @@ import org.apache.ignite.internal.util.tostring.GridToStringInclude;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.A;
 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.processors.cache.query.IgniteQueryErrorCode.KEY_SCALE_OUT_OF_RANGE;
@@ -420,13 +422,8 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor {
         if (uppercaseProps.put(name.toUpperCase(), prop) != null && failOnDuplicate)
             throw new IgniteCheckedException("Property with upper cased name '" + name + "' already exists.");
 
-        if (prop.notNull()) {
-            if (validateProps == null)
-                validateProps = new ArrayList<>();
-
-            validateProps.add(prop);
-        }
-        else if (prop.precision() != -1) {
+        if (prop.notNull() || prop.precision() != -1 ||
+            coCtx.kernalContext().config().getSqlConfiguration().isValidationEnabled()) {
             if (validateProps == null)
                 validateProps = new ArrayList<>();
 
@@ -583,6 +580,8 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor {
         if (F.isEmpty(validateProps))
             return;
 
+        final boolean validateTypes = coCtx.kernalContext().config().getSqlConfiguration().isValidationEnabled();
+
         for (int i = 0; i < validateProps.size(); ++i) {
             GridQueryProperty prop = validateProps.get(i);
 
@@ -606,6 +605,30 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor {
                     isKey ? NULL_KEY : NULL_VALUE);
             }
 
+            if (validateTypes && propVal != null) {
+                if (!(propVal instanceof BinaryObject)) {
+                    if (!U.box(prop.type()).isAssignableFrom(U.box(propVal.getClass()))) {
+                        // Some reference type arrays end up being converted to Object[]
+                        if (!(prop.type().isArray() && Object[].class == propVal.getClass() &&
+                            Arrays.stream((Object[]) 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 (propVal == null || prop.precision() == -1)
                 continue;
 
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlFieldTypeValidationOnKeyValueInsertTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlFieldTypeValidationOnKeyValueInsertTest.java
new file mode 100644
index 0000000..b215e18
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlFieldTypeValidationOnKeyValueInsertTest.java
@@ -0,0 +1,325 @@
+/*
+ * 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.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import javax.cache.processor.EntryProcessor;
+import javax.cache.processor.EntryProcessorResult;
+import javax.cache.processor.MutableEntry;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.binary.BinaryBasicNameMapper;
+import org.apache.ignite.binary.BinaryObject;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.configuration.BinaryConfiguration;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.NearCacheConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.cache.index.AbstractIndexingCommonTest;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.CacheMode.REPLICATED;
+
+/**
+ *
+ */
+@RunWith(Parameterized.class)
+public class SqlFieldTypeValidationOnKeyValueInsertTest extends AbstractIndexingCommonTest {
+    /** */
+    private static final String SQL_TEXT = "select id, name from Person where id=1";
+
+    /** */
+    private static final String ERROR = "Type for a column 'NAME' is not compatible with table definition.";
+
+    /** */
+    private static boolean validate;
+
+    /** */
+    @Parameterized.Parameter(0)
+    public CacheMode cacheMode;
+
+    /** */
+    @Parameterized.Parameter(1)
+    public CacheAtomicityMode atomicityMode;
+
+    /** */
+    @Parameterized.Parameter(2)
+    public boolean near;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        cfg.getSqlConfiguration().setValidationEnabled(validate);
+
+        cfg.setCacheConfiguration(new CacheConfiguration<>(DEFAULT_CACHE_NAME)
+                .setCacheMode(cacheMode)
+                .setAtomicityMode(atomicityMode)
+                .setNearConfiguration(near ? new NearCacheConfiguration<>().setNearStartSize(1) : null)
+                .setQueryEntities(Collections.singletonList(
+                        new QueryEntity()
+                                .setKeyFieldName("id")
+                                .setValueType("Person")
+                                .setFields(new LinkedHashMap<>(
+                                        F.asMap("id", "java.lang.Integer",
+                                                "name", "java.util.UUID"))))));
+
+        cfg.setBinaryConfiguration(new BinaryConfiguration().setNameMapper(new BinaryBasicNameMapper(true)));
+
+        return cfg;
+    }
+
+    /** */
+    @Parameterized.Parameters(name = "{index} {0} {1} {2}")
+    public static List<Object[]> data() {
+        List<Object[]> res = new ArrayList<>();
+
+        res.add(new Object[] {REPLICATED, TRANSACTIONAL, true});
+        res.add(new Object[] {REPLICATED, TRANSACTIONAL, false});
+        res.add(new Object[] {REPLICATED, ATOMIC, true});
+        res.add(new Object[] {REPLICATED, ATOMIC, false});
+        res.add(new Object[] {PARTITIONED, TRANSACTIONAL, true});
+        res.add(new Object[] {PARTITIONED, TRANSACTIONAL, false});
+        res.add(new Object[] {PARTITIONED, ATOMIC, true});
+        res.add(new Object[] {PARTITIONED, ATOMIC, false});
+
+        return res;
+    }
+
+    /** */
+    @After
+    public void tearDown() {
+        stopAllGrids();
+    }
+
+    /** */
+    @Test
+    public void testError() throws Exception {
+        validate = true;
+
+        startGrids(2);
+
+        IgniteEx clnt = startClientGrid(2);
+
+        IgniteCache<Object, Object> cache = clnt.cache(DEFAULT_CACHE_NAME);
+
+        BinaryObject obj = clnt.binary().builder("Person")
+                .setField("id", 1)
+                .setField("name", UUID.randomUUID().toString())
+                .build();
+
+        doInsertsError(cache, obj);
+    }
+
+    /** */
+    @Test
+    public void testSuccess() throws Exception {
+        validate = true;
+
+        startGrids(2);
+
+        IgniteEx clnt = startClientGrid(2);
+
+        IgniteCache<Object, Object> cache = clnt.cache(DEFAULT_CACHE_NAME).withKeepBinary();
+
+        BinaryObject obj = clnt.binary().builder("Person")
+                .setField("id", 1)
+                .setField("name", UUID.randomUUID())
+                .build();
+
+        doInsertSuccess(cache, obj);
+
+        assertEquals(obj, cache.withKeepBinary().get(1));
+
+        grid(0).context().query()
+                .querySqlFields(
+                new SqlFieldsQuery(SQL_TEXT).setSchema(DEFAULT_CACHE_NAME), true)
+                .getAll();
+    }
+
+    /** */
+    @Test
+    public void testSkipValidation() throws Exception {
+        validate = false;
+
+        startGrids(2);
+
+        IgniteEx clnt = startClientGrid(2);
+
+        IgniteCache<Object, Object> cache = clnt.cache(DEFAULT_CACHE_NAME).withKeepBinary();
+
+        BinaryObject obj = clnt.binary().builder("Person")
+                .setField("id", 1)
+                .setField("name", UUID.randomUUID().toString())
+                .build();
+
+        doInsertSuccess(cache, obj);
+
+        assertEquals(obj, cache.withKeepBinary().get(1));
+    }
+
+    /** */
+    @Test
+    public void testClassInstanceError() throws Exception {
+        validate = true;
+
+        startGrids(2);
+
+        IgniteEx clnt = startClientGrid(2);
+
+        IgniteCache<Object, Object> cache = clnt.cache(DEFAULT_CACHE_NAME);
+
+        doInsertsError(cache, new Person(UUID.randomUUID().toString()));
+    }
+
+    /** */
+    @Test
+    public void testClassInstanceSkipValidation() throws Exception {
+        validate = false;
+
+        startGrids(2);
+
+        IgniteEx clnt = startClientGrid(2);
+
+        IgniteCache<Object, Object> cache = clnt.cache(DEFAULT_CACHE_NAME);
+
+        Person obj = new Person(UUID.randomUUID().toString());
+
+        doInsertSuccess(cache, obj);
+
+        assertEquals(obj, cache.get(1));
+    }
+
+    /** */
+    private void doInsertsError(IgniteCache<Object, Object> cache, Object obj) {
+        assertThrows(() -> cache.put(1, obj), ERROR);
+
+        assertThrows(() -> cache.putAll(F.asMap(1, obj, 2, obj)), ERROR);
+
+        assertThrows(() -> cache.putIfAbsent(1, obj), ERROR);
+
+        assertThrows(() -> cache.invoke(1, new TestEntryProcessor(obj)), ERROR);
+
+        assertThrows(() -> cache.invokeAll(
+                F.asMap(1, new TestEntryProcessor(obj), 2, new TestEntryProcessor(obj))).get(1).get(),
+                ERROR);
+
+        assertThrows(() -> cache.replace(1, obj), ERROR);
+
+        assertThrows(() -> cache.getAndPut(1, obj), ERROR);
+
+        assertThrows(() -> cache.getAndPutIfAbsent(1, obj), ERROR);
+
+        assertThrows(() -> cache.getAndReplace(1, obj), ERROR);
+    }
+
+    /** */
+    private void assertThrows(GridTestUtils.RunnableX runx, String msg) {
+        try {
+            runx.runx();
+        }
+        catch (Exception e) {
+            if (X.hasCause(e, msg, Throwable.class))
+                return;
+
+            throw new AssertionError("Unexpected exception ", e);
+        }
+
+        throw new AssertionError("Exception has not been thrown.");
+    }
+
+    /** */
+    private void doInsertSuccess(IgniteCache<Object, Object> cache, Object obj) {
+        cache.put(1, obj);
+
+        cache.putAll(F.asMap(1, obj, 2, obj));
+
+        cache.putIfAbsent(1, obj);
+
+        cache.invoke(1, new TestEntryProcessor(obj));
+
+        Map<Object, EntryProcessorResult<Void>> m =
+            cache.invokeAll(F.asMap(1, new TestEntryProcessor(obj), 2, new TestEntryProcessor(obj)));
+
+        assertTrue(m == null || m.isEmpty());
+
+        cache.replace(1, obj);
+
+        cache.getAndPut(1, obj);
+
+        cache.getAndPutIfAbsent(1, obj);
+
+        cache.getAndReplace(1, obj);
+    }
+
+    /** */
+    public static class Person {
+        /** */
+        private final Object name;
+
+        /** */
+        public Person(Object name) {
+            this.name = name;
+        }
+
+        /** */
+        public Object name() {
+            return name;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object obj) {
+            return obj instanceof Person && F.eq(((Person) obj).name, name);
+        }
+    }
+
+    /** */
+    public static class TestEntryProcessor implements EntryProcessor<Object, Object, Void> {
+        /** */
+        private final Object val;
+
+        /** */
+        public TestEntryProcessor(Object val) {
+            this.val = val;
+        }
+
+        /** */
+        @Override public Void process(MutableEntry<Object, Object> entry, Object... args) {
+            entry.setValue(val);
+
+            return null;
+        }
+    }
+}
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlFieldTypeValidationTypesTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlFieldTypeValidationTypesTest.java
new file mode 100644
index 0000000..800ac21
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlFieldTypeValidationTypesTest.java
@@ -0,0 +1,202 @@
+/*
+ * 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.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.UUID;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.binary.BinaryBasicNameMapper;
+import org.apache.ignite.binary.BinaryObject;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.configuration.BinaryConfiguration;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.processors.cache.index.AbstractIndexingCommonTest;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ *
+ */
+@RunWith(Parameterized.class)
+public class SqlFieldTypeValidationTypesTest extends AbstractIndexingCommonTest {
+    /** */
+    private static final String ERROR = "Type for a column 'NAME' is not compatible with table definition.";
+
+    /** */
+    @Parameterized.Parameter(0)
+    public Class<?> fieldType;
+
+    /** */
+    @Parameterized.Parameter(1)
+    public Object okVal;
+
+    /** */
+    @Parameterized.Parameter(2)
+    public Object errVal;
+
+    /** */
+    @Parameterized.Parameter(3)
+    public boolean indexed;
+
+    /** */
+    private static boolean validate;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        cfg.getSqlConfiguration().setValidationEnabled(validate);
+
+        cfg.setCacheConfiguration(new CacheConfiguration<>(DEFAULT_CACHE_NAME).setQueryEntities(
+                Collections.singletonList(
+                        new QueryEntity()
+                                .setKeyFieldName("id")
+                                .setValueType("Person")
+                                .setFields(new LinkedHashMap<>(
+                                        F.asMap("id", "java.lang.Integer",
+                                                "name", fieldType.getName())))
+                                .setIndexes(indexed ? Collections.singletonList(new QueryIndex("name")) : null)
+        )));
+
+        cfg.setBinaryConfiguration(new BinaryConfiguration().setNameMapper(new BinaryBasicNameMapper(true)));
+
+        return cfg;
+    }
+
+    /** */
+    @Parameterized.Parameters(name = "{index} type={0} idx={3}")
+    public static List<Object[]> data() {
+        List<Object[]> res = new ArrayList<>();
+
+        // Type, ok value, error value, indexed flag.
+        res.add(new Object[] {Integer.class, 1, "1", false});
+        res.add(new Object[] {Integer.class, 1, "1", true});
+
+        res.add(new Object[] {String.class, "1", 1, false});
+        res.add(new Object[] {String.class, "1", 1, true});
+
+        res.add(new Object[] {UUID.class, UUID.randomUUID(), UUID.randomUUID().toString(), false});
+        res.add(new Object[] {UUID.class, UUID.randomUUID(), UUID.randomUUID().toString(), true});
+
+        res.add(new Object[] {TestEnum.class, TestEnum.A, "C", false});
+        res.add(new Object[] {TestEnum.class, TestEnum.A, "C", true});
+
+        res.add(new Object[] {Integer[].class, new Integer[] {0,1}, new String[] {"0", "1"}, false});
+        res.add(new Object[] {Integer[].class, new Integer[] {0,1}, new String[] {"0", "1"}, true});
+
+        res.add(new Object[] {int[].class, new Integer[] {0,1}, new String[] {"0", "1"}, false});
+        res.add(new Object[] {int[].class, new Integer[] {0,1}, new String[] {"0", "1"}, true});
+
+        res.add(new Object[] {int[].class, new Object[] {0,1}, new Object[] {"0", "1"}, false});
+        res.add(new Object[] {int[].class, new Object[] {0,1}, new Object[] {"0", "1"}, true});
+
+        return res;
+    }
+
+    /** */
+    @After
+    public void tearDown() {
+        stopAllGrids();
+    }
+
+    /** */
+    @Test
+    public void testError() throws Exception {
+        validate = true;
+
+        startGrid(0);
+
+        IgniteCache<Object, Object> cache = grid(0).cache(DEFAULT_CACHE_NAME);
+
+        BinaryObject obj = grid(0).binary().builder("Person")
+                .setField("id", 1)
+                .setField("name", errVal)
+                .build();
+
+        GridTestUtils.assertThrows(log, () -> cache.put(1, obj), IgniteException.class, ERROR);
+
+        assertNull(cache.withKeepBinary().get(1));
+    }
+
+    /** */
+    @Test
+    public void testClassInstanceError() throws Exception {
+        validate = true;
+
+        startGrid(0);
+
+        IgniteCache<Object, Object> cache = grid(0).cache(DEFAULT_CACHE_NAME);
+
+        GridTestUtils.assertThrows(log, () -> cache.put(1, new Person(errVal)), IgniteException.class, ERROR);
+
+        assertNull(cache.withKeepBinary().get(1));
+    }
+
+    /** */
+    private boolean expectIndexFail() {
+        return indexed && (UUID.class == fieldType || Integer.class == fieldType);
+    }
+
+    /** */
+    public static class Person {
+        /** */
+        private final Object name;
+
+        /** */
+        public Person(Object name) {
+            this.name = name;
+        }
+
+        /** */
+        public Object name() {
+            return name;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object obj) {
+            if (!(obj instanceof Person))
+                return false;
+
+            if (name instanceof Object[])
+                return Arrays.equals((Object[])name, (Object[])((Person) obj).name);
+
+            return F.eq(name, ((Person) obj).name);
+        }
+    }
+
+    /**
+     *
+     */
+    public enum TestEnum {
+        /** */
+        A,
+
+        /** */
+        B
+    }
+}
diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java
index 992eb03..a846f24 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java
@@ -219,6 +219,8 @@ import org.apache.ignite.internal.processors.query.KillQueryOnClientDisconnectTe
 import org.apache.ignite.internal.processors.query.KillQueryTest;
 import org.apache.ignite.internal.processors.query.MultipleStatementsSqlQuerySelfTest;
 import org.apache.ignite.internal.processors.query.RunningQueriesTest;
+import org.apache.ignite.internal.processors.query.SqlFieldTypeValidationOnKeyValueInsertTest;
+import org.apache.ignite.internal.processors.query.SqlFieldTypeValidationTypesTest;
 import org.apache.ignite.internal.processors.query.SqlIllegalSchemaSelfTest;
 import org.apache.ignite.internal.processors.query.SqlIncompatibleDataTypeExceptionTest;
 import org.apache.ignite.internal.processors.query.SqlMergeOnClientNodeTest;
@@ -623,8 +625,10 @@ import org.junit.runners.Suite;
 
     IgniteCacheMergeSqlQuerySelfTest.class,
     SqlMergeTest.class,
-    SqlMergeOnClientNodeTest.class
+    SqlMergeOnClientNodeTest.class,
 
+    SqlFieldTypeValidationTypesTest.class,
+    SqlFieldTypeValidationOnKeyValueInsertTest.class
 })
 public class IgniteBinaryCacheQueryTestSuite {
 }