You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ni...@apache.org on 2020/10/11 09:59:40 UTC
[ignite] branch master updated: IGNITE-13553 Check type of the
indexed fields added (#8330)
This is an automated email from the ASF dual-hosted git repository.
nizhikov 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 2987996 IGNITE-13553 Check type of the indexed fields added (#8330)
2987996 is described below
commit 29879964cd74ce022bad5c59b5c6db170f19e822
Author: Nikolay <ni...@apache.org>
AuthorDate: Sun Oct 11 12:59:12 2020 +0300
IGNITE-13553 Check type of the indexed fields added (#8330)
---
.../processors/query/QueryTypeDescriptorImpl.java | 66 +++++
.../apache/ignite/tests/p2p/cache/Container.java | 29 +++
.../processors/cache/index/BasicIndexTest.java | 4 +-
.../query/CreateIndexOnInvalidDataTypeTest.java | 18 +-
.../query/WrongQueryEntityFieldTypeTest.java | 289 +++++++++++++++++++++
.../IgniteBinaryCacheQueryTestSuite2.java | 2 +
6 files changed, 395 insertions(+), 13 deletions(-)
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 f53b38c..4fe0fcc 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
@@ -581,6 +581,16 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor {
/** {@inheritDoc} */
@SuppressWarnings("ForLoopReplaceableByForEach")
@Override public void validateKeyAndValue(Object key, Object val) throws IgniteCheckedException {
+ if (F.isEmpty(validateProps) && F.isEmpty(idxs))
+ return;
+
+ validateProps(key, val);
+
+ validateIndexes(key, val);
+ }
+
+ /** Validate properties. */
+ private void validateProps(Object key, Object val) throws IgniteCheckedException {
if (F.isEmpty(validateProps))
return;
@@ -660,6 +670,62 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor {
}
}
+ /** Validate indexed values. */
+ private void validateIndexes(Object key, Object val) throws IgniteCheckedException {
+ if (F.isEmpty(idxs))
+ return;
+
+ for (QueryIndexDescriptorImpl idx : idxs.values()) {
+ for (String idxField : idx.fields()) {
+ GridQueryProperty prop = props.get(idxField);
+
+ Object propVal;
+ Class<?> propType;
+
+ if (F.eq(idxField, keyFieldAlias()) || F.eq(idxField, KEY_FIELD_NAME)) {
+ propVal = key instanceof KeyCacheObject ? ((CacheObject) key).value(coCtx, true) : key;
+
+ propType = propVal == null ? null : propVal.getClass();
+ }
+ else if (F.eq(idxField, valueFieldAlias()) || F.eq(idxField, VAL_FIELD_NAME)) {
+ propVal = val instanceof CacheObject ? ((CacheObject)val).value(coCtx, true) : val;
+
+ propType = propVal == null ? null : propVal.getClass();
+ }
+ else {
+ propVal = prop.value(key, val);
+
+ propType = prop.type();
+ }
+
+ if (propVal == null)
+ continue;
+
+ if (!(propVal instanceof BinaryObject)) {
+ if (!U.box(propType).isAssignableFrom(U.box(propVal.getClass()))) {
+ // Some reference type arrays end up being converted to Object[]
+ if (!(propType.isArray() && Object[].class == propVal.getClass() &&
+ Arrays.stream((Object[]) 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()) {
+ throw new IgniteSQLException("Type for a column '" + idxField +
+ "' is not compatible with index definition. Expected '" +
+ propType.getSimpleName() + "', actual type '" +
+ ((BinaryObject)propVal).type().typeName() + "'");
+ }
+ }
+ }
+ }
+
/** {@inheritDoc} */
@SuppressWarnings("ForLoopReplaceableByForEach")
@Override public void setDefaults(Object key, Object val) throws IgniteCheckedException {
diff --git a/modules/extdata/p2p/src/main/java/org/apache/ignite/tests/p2p/cache/Container.java b/modules/extdata/p2p/src/main/java/org/apache/ignite/tests/p2p/cache/Container.java
new file mode 100644
index 0000000..fff3612
--- /dev/null
+++ b/modules/extdata/p2p/src/main/java/org/apache/ignite/tests/p2p/cache/Container.java
@@ -0,0 +1,29 @@
+/*
+ * 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.tests.p2p.cache;
+
+/** */
+public class Container {
+ /** */
+ private Object field;
+
+ /** */
+ public Container(Object field) {
+ this.field = field;
+ }
+}
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/BasicIndexTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/BasicIndexTest.java
index 8725e3a..24a3759 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/BasicIndexTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/BasicIndexTest.java
@@ -1351,11 +1351,11 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
startGrid();
- sql("CREATE TABLE TEST (ID INT PRIMARY KEY, val_int INT, VAL_OBJ OTHER)");
+ sql("CREATE TABLE TEST (ID INT PRIMARY KEY, val_int INT, VAL_OBJ LONG)");
sql("CREATE INDEX TEST_VAL_INT ON TEST(VAL_INT)");
sql("CREATE INDEX TEST_VAL_OBJ ON TEST(VAL_OBJ)");
- sql("INSERT INTO TEST VALUES (0, 0, ?)", new Pojo(0));
+ sql("INSERT INTO TEST VALUES (0, 0, ?)", 0L);
GridTestUtils.assertThrows(log, () -> {
sql("SELECT * FROM TEST WHERE VAL_OBJ < CURRENT_TIMESTAMP()").getAll();
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/CreateIndexOnInvalidDataTypeTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/CreateIndexOnInvalidDataTypeTest.java
index bead5e4..269693e 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/CreateIndexOnInvalidDataTypeTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/CreateIndexOnInvalidDataTypeTest.java
@@ -33,12 +33,13 @@ import org.apache.ignite.configuration.DataRegionConfiguration;
import org.apache.ignite.configuration.DataStorageConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.failure.StopNodeFailureHandler;
-import org.apache.ignite.internal.processors.cache.CachePartialUpdateCheckedException;
import org.apache.ignite.internal.processors.cache.index.AbstractIndexingCommonTest;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.testframework.GridTestUtils;
import org.junit.Test;
+import static org.apache.ignite.testframework.GridTestUtils.assertThrowsWithCause;
+
/**
* Checks add field with invalid data type to index.
*/
@@ -157,12 +158,9 @@ public class CreateIndexOnInvalidDataTypeTest extends AbstractIndexingCommonTest
bob.setField("VAL_INT", 10);
bob.setField("VAL_DATE", new java.util.Date());
- GridTestUtils.assertThrowsAnyCause(log, () -> {
- grid().cache("test").put(0, bob.build());
+ assertThrowsWithCause(() -> grid().cache("test").put(0, bob.build()), IgniteSQLException.class);
- return null;
- },
- CachePartialUpdateCheckedException.class, "Failed to update keys");
+ assertNull(grid().cache("test").get(0));
sql("DROP INDEX TEST_0_VAL_DATE_IDX");
@@ -171,7 +169,7 @@ public class CreateIndexOnInvalidDataTypeTest extends AbstractIndexingCommonTest
List<List<?>> res = sql("SELECT VAL_INT FROM TEST WHERE VAL_INT > 0").getAll();
- assertEquals(2, res.size());
+ assertEquals(1, res.size());
}
/**
@@ -185,16 +183,14 @@ public class CreateIndexOnInvalidDataTypeTest extends AbstractIndexingCommonTest
.setArgs(args), false);
}
- /**
- *
- */
+ /** */
private static class Value {
/** */
@QuerySqlField
int val_int;
/** */
- java.util.Date val_date;
+ Date val_date;
/**
* @param val Test value.
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/WrongQueryEntityFieldTypeTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/WrongQueryEntityFieldTypeTest.java
new file mode 100644
index 0000000..0f17b11
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/WrongQueryEntityFieldTypeTest.java
@@ -0,0 +1,289 @@
+/*
+ * 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.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.function.BiConsumer;
+import java.util.function.Supplier;
+import javax.cache.CacheException;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.client.ClientCache;
+import org.apache.ignite.client.ClientCacheConfiguration;
+import org.apache.ignite.client.ClientException;
+import org.apache.ignite.client.ClientTransaction;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.failure.FailureContext;
+import org.apache.ignite.failure.FailureHandler;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.apache.ignite.transactions.Transaction;
+import org.apache.ignite.transactions.TransactionConcurrency;
+import org.apache.ignite.transactions.TransactionIsolation;
+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_SNAPSHOT;
+import static org.apache.ignite.testframework.GridTestUtils.assertThrowsWithCause;
+import static org.apache.ignite.transactions.TransactionConcurrency.OPTIMISTIC;
+
+/** */
+@RunWith(Parameterized.class)
+public class WrongQueryEntityFieldTypeTest extends GridCommonAbstractTest {
+ /** */
+ @Parameterized.Parameter()
+ public CacheAtomicityMode mode;
+
+ /** */
+ @Parameterized.Parameter(1)
+ public int backups;
+
+ /** */
+ @Parameterized.Parameter(2)
+ public Supplier<?> val;
+
+ /** */
+ @Parameterized.Parameter(3)
+ public String idxFld;
+
+ /** */
+ @Parameterized.Parameter(4)
+ public Class<?> idxFldType;
+
+ /** */
+ @Parameterized.Parameter(5)
+ public int gridCnt;
+
+ /** */
+ @Parameterized.Parameters(name = "cacheMode={0},backups={1},idxFld={3},idxFldType={4},gridCnt={5}")
+ public static Collection<Object[]> parameters() {
+ Supplier<?> person = WrongQueryEntityFieldTypeTest::personInside;
+ Supplier<?> floatInsteadLong = WrongQueryEntityFieldTypeTest::floatInside;
+ Supplier<?> organization = WrongQueryEntityFieldTypeTest::personInsideOrganization;
+
+ Collection<Object[]> params = new ArrayList<>();
+
+ for (CacheAtomicityMode cacheMode : CacheAtomicityMode.values()) {
+ for (int backups = 0; backups < 4; backups++) {
+ for (int gridCnt = 1; gridCnt < 4; gridCnt++) {
+ params.add(new Object[] {cacheMode, backups, person, "field", String.class, gridCnt});
+ params.add(new Object[] {cacheMode, backups, floatInsteadLong, "field", Long.class, gridCnt});
+ params.add(new Object[] {cacheMode, backups, organization, "head", String.class, gridCnt});
+ }
+ }
+ }
+
+ return params;
+ }
+
+ /** */
+ private volatile boolean sysThreadFail;
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ stopAllGrids();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected FailureHandler getFailureHandler(String igniteInstanceName) {
+ return new FailureHandler() {
+ @Override public boolean onFailure(Ignite ignite, FailureContext failureCtx) {
+ sysThreadFail = true;
+
+ return false;
+ }
+ };
+ }
+
+ /** */
+ @Test
+ public void testPutFromThinClient() throws Exception {
+ withThinClient((cli, cache) -> {
+ assertThrowsWithCause(() -> cache.put(1, val.get()), ClientException.class);
+
+ assertNull(cache.withKeepBinary().get(1));
+ });
+ }
+
+ /** */
+ @Test
+ public void testPut() throws Exception {
+ withNode((ign, cache) -> {
+ assertThrowsWithCause(() -> cache.put(1, val.get()), CacheException.class);
+
+ assertNull(cache.withKeepBinary().get(1));
+ });
+ }
+
+ /** */
+ @Test
+ public void testPutFromThinClientTx() throws Exception {
+ if (mode == ATOMIC)
+ return;
+
+ withThinClient((cli, cache) -> {
+ for (TransactionConcurrency conc : TransactionConcurrency.values()) {
+ for (TransactionIsolation iso: TransactionIsolation.values()) {
+ if (conc == OPTIMISTIC && mode == TRANSACTIONAL_SNAPSHOT)
+ continue;
+
+ assertThrowsWithCause(() -> {
+ try (ClientTransaction tx = cli.transactions().txStart(conc, iso)) {
+ cache.put(1, val.get());
+
+ tx.commit();
+ }
+ }, ClientException.class);
+
+ assertNull(cache.withKeepBinary().get(1));
+ }
+ }
+ });
+ }
+
+ /** */
+ @Test
+ public void testPutTx() throws Exception {
+ if (mode == ATOMIC)
+ return;
+
+ withNode((ign, cache) -> {
+ for (TransactionConcurrency conc : TransactionConcurrency.values()) {
+ for (TransactionIsolation iso : TransactionIsolation.values()) {
+ if (conc == OPTIMISTIC && mode == TRANSACTIONAL_SNAPSHOT)
+ continue;
+
+ assertThrowsWithCause(() -> {
+ try (Transaction tx = ign.transactions().txStart(conc, iso)) {
+ cache.put(1, val.get());
+
+ tx.commit();
+ }
+ }, mode == TRANSACTIONAL_SNAPSHOT ? CacheException.class : IgniteSQLException.class);
+
+ assertNull(cache.withKeepBinary().get(1));
+ }
+ }
+ });
+ }
+
+ /** Perform action with Thin client. */
+ private void withThinClient(BiConsumer<IgniteClient, ClientCache<Integer, Object>> consumer) throws Exception {
+ startGrids(gridCnt);
+
+ try (IgniteClient cli = Ignition.startClient(new ClientConfiguration().setAddresses("127.0.0.1:10800"))) {
+ ClientCache<Integer, Object> cache = cli.createCache(new ClientCacheConfiguration()
+ .setName("TEST")
+ .setAtomicityMode(mode)
+ .setBackups(backups)
+ .setQueryEntities(queryEntity()));
+
+ consumer.accept(cli, cache);
+
+ assertFalse(sysThreadFail);
+ }
+ }
+
+ /** Perform action with Ignite node. */
+ private void withNode(BiConsumer<Ignite, IgniteCache<Integer, Object>> consumer) throws Exception {
+ IgniteEx ign = startGrids(gridCnt);
+
+ IgniteCache<Integer, Object> cache = ign.createCache(new CacheConfiguration<Integer, Object>()
+ .setName("TEST")
+ .setAtomicityMode(mode)
+ .setBackups(backups)
+ .setQueryEntities(Collections.singleton(queryEntity())));
+
+ consumer.accept(ign, cache);
+
+ assertFalse(sysThreadFail);
+ }
+
+ /** @return Organization with the Person inside. */
+ public static Object personInsideOrganization() {
+ try {
+ ClassLoader ldr = getExternalClassLoader();
+
+ Class<?> organization = ldr.loadClass("org.apache.ignite.tests.p2p.cache.Organization");
+ Class<?> person = ldr.loadClass("org.apache.ignite.tests.p2p.cache.Person");
+ Class<?> address = ldr.loadClass("org.apache.ignite.tests.p2p.cache.Address");
+
+ Object p = person.getConstructor(String.class).newInstance("test");
+
+ return organization.getConstructor(String.class, person, address).newInstance("org", p, null);
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** @return Container with the Person inside. */
+ public static Object personInside() {
+ try {
+ ClassLoader ldr = getExternalClassLoader();
+
+ Class<?> container = ldr.loadClass("org.apache.ignite.tests.p2p.cache.Container");
+ Class<?> personCls = ldr.loadClass("org.apache.ignite.tests.p2p.cache.Person");
+
+ Object person = personCls.getConstructor().newInstance();
+
+ return container.getConstructor(Object.class).newInstance(person);
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** @return Container with the float inside. */
+ public static Object floatInside() {
+ try {
+ ClassLoader ldr = getExternalClassLoader();
+
+ Class<?> container = ldr.loadClass("org.apache.ignite.tests.p2p.cache.Container");
+
+ return container.getConstructor(Object.class).newInstance(1f);
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** @return Query entity. */
+ private QueryEntity queryEntity() {
+ LinkedHashMap<String, String> fields = new LinkedHashMap<>();
+
+ fields.put(idxFld, idxFldType.getName()); //Actual type of the field is different.
+
+ return new QueryEntity()
+ .setKeyType(Integer.class.getName())
+ .setValueType(val.get().getClass().getName())
+ .setFields(fields)
+ .setIndexes(Collections.singleton(new QueryIndex(idxFld)));
+ }
+}
diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite2.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite2.java
index 5f96c9f..2022fce 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite2.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite2.java
@@ -71,6 +71,7 @@ import org.apache.ignite.internal.processors.query.SqlLocalQueryConnectionAndSta
import org.apache.ignite.internal.processors.query.SqlPartOfComplexPkLookupTest;
import org.apache.ignite.internal.processors.query.SqlQueriesTopologyMappingTest;
import org.apache.ignite.internal.processors.query.SqlTwoCachesInGroupWithSameEntryTest;
+import org.apache.ignite.internal.processors.query.WrongQueryEntityFieldTypeTest;
import org.apache.ignite.internal.processors.query.h2.CacheQueryEntityWithDateTimeApiFieldsTest;
import org.apache.ignite.internal.processors.query.h2.DmlStatementsProcessorTest;
import org.apache.ignite.internal.processors.query.h2.twostep.CacheQueryMemoryLeakTest;
@@ -94,6 +95,7 @@ import org.junit.runners.Suite;
DefaultQueryTimeoutTestSuite.class,
CreateIndexOnInvalidDataTypeTest.class,
+ WrongQueryEntityFieldTypeTest.class,
DisabledSqlFunctionsTest.class,