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 bd231af807f IGNITE-26261 SQL: Fix failure with different key classes
for one entity - Fixes #12359.
bd231af807f is described below
commit bd231af807fc879c6b8aa1ea7b97942f8f4dd444
Author: Aleksey Plekhanov <[email protected]>
AuthorDate: Wed Sep 24 15:18:35 2025 +0300
IGNITE-26261 SQL: Fix failure with different key classes for one entity -
Fixes #12359.
Signed-off-by: Aleksey Plekhanov <[email protected]>
---
.../ignite/internal/binary/BinaryFieldEx.java | 28 ++
.../ignite/internal/binary/BinaryFieldImpl.java | 7 +-
.../AbstractMultiEngineIntegrationTest.java | 66 +++++
.../integration/KeyClassChangeIntegrationTest.java | 287 +++++++++++++++++++++
.../calcite/integration/ViewsIntegrationTest.java | 46 +---
.../ignite/testsuites/IntegrationTestSuite.java | 2 +
.../query/property/QueryBinaryProperty.java | 9 +-
7 files changed, 405 insertions(+), 40 deletions(-)
diff --git
a/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryFieldEx.java
b/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryFieldEx.java
new file mode 100644
index 00000000000..704c973c432
--- /dev/null
+++
b/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryFieldEx.java
@@ -0,0 +1,28 @@
+/*
+ * 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.binary;
+
+import org.apache.ignite.binary.BinaryField;
+
+/**
+ * Extended binary object field.
+ */
+public interface BinaryFieldEx extends BinaryField {
+ /** Get field's enclosing type ID. */
+ public int enclosingTypeId();
+}
diff --git
a/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryFieldImpl.java
b/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryFieldImpl.java
index 321d05742eb..d50c41ac67b 100644
---
a/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryFieldImpl.java
+++
b/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryFieldImpl.java
@@ -29,7 +29,7 @@ import static java.util.Objects.nonNull;
/**
* Implementation of binary field descriptor.
*/
-class BinaryFieldImpl implements BinaryField {
+class BinaryFieldImpl implements BinaryFieldEx {
/** Binary context that created this field. */
private final BinaryContext ctx;
@@ -148,6 +148,11 @@ class BinaryFieldImpl implements BinaryField {
return schema.order(fieldId);
}
+ /** {@inheritDoc} */
+ @Override public int enclosingTypeId() {
+ return typeId;
+ }
+
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(BinaryFieldImpl.class, this);
diff --git
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractMultiEngineIntegrationTest.java
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractMultiEngineIntegrationTest.java
new file mode 100644
index 00000000000..e2dd32874fe
--- /dev/null
+++
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractMultiEngineIntegrationTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.calcite.integration;
+
+import java.util.Arrays;
+import java.util.List;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.calcite.CalciteQueryEngineConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.SqlConfiguration;
+import org.apache.ignite.indexing.IndexingQueryEngineConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/** */
+@RunWith(Parameterized.class)
+public class AbstractMultiEngineIntegrationTest extends
AbstractBasicIntegrationTest {
+ /** */
+ @Parameterized.Parameter
+ public String engine;
+
+ /** */
+ @Parameterized.Parameters(name = "Query engine={0}")
+ public static Iterable<Object> params() {
+ return Arrays.asList(CalciteQueryEngineConfiguration.ENGINE_NAME,
IndexingQueryEngineConfiguration.ENGINE_NAME);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration getConfiguration(String
igniteInstanceName) throws Exception {
+ return super.getConfiguration(igniteInstanceName)
+ .setSqlConfiguration(new SqlConfiguration()
+
.setQueryEnginesConfiguration(engine.equals(CalciteQueryEngineConfiguration.ENGINE_NAME)
?
+ new CalciteQueryEngineConfiguration() : new
IndexingQueryEngineConfiguration()));
+ }
+
+ /** */
+ @Override protected List<List<?>> sql(IgniteEx ignite, String sql,
Object... params) {
+ return ignite.context().query().querySqlFields(new
SqlFieldsQuery(sql).setArgs(params), true)
+ .getAll();
+ }
+}
diff --git
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/KeyClassChangeIntegrationTest.java
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/KeyClassChangeIntegrationTest.java
new file mode 100644
index 00000000000..45078d52a44
--- /dev/null
+++
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/KeyClassChangeIntegrationTest.java
@@ -0,0 +1,287 @@
+/*
+ * 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.calcite.integration;
+
+import java.util.UUID;
+import java.util.function.IntFunction;
+import javax.cache.CacheException;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheWriteSynchronizationMode;
+import org.apache.ignite.cache.affinity.AffinityKeyMapped;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.cluster.ClusterState;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.failure.TestFailureHandler;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/** */
+public class KeyClassChangeIntegrationTest extends
AbstractMultiEngineIntegrationTest {
+ /** */
+ public static final int SRV_CNT = 3;
+
+ /** */
+ private TestFailureHandler failureHnd;
+
+ /** */
+ private boolean validateTypes;
+
+ /** */
+ private Class<?> indexedKeyCls;
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ failureHnd = new TestFailureHandler(true);
+ validateTypes = false;
+ indexedKeyCls = BaseKey.class;
+
+ cleanPersistenceDir();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ super.afterTest();
+
+ stopAllGrids();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration getConfiguration(String
igniteInstanceName) throws Exception {
+ IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+ cfg.setDataStorageConfiguration(new DataStorageConfiguration()
+ .setDefaultDataRegionConfiguration(new DataRegionConfiguration()
+ .setPersistenceEnabled(true)));
+
+ cfg.setFailureHandler(failureHnd);
+
+ cfg.setCacheConfiguration(new CacheConfiguration<>(DEFAULT_CACHE_NAME)
+ .setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL)
+ .setBackups(2)
+
.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC)
+ .setIndexedTypes(indexedKeyCls, TestValue.class));
+
+ cfg.getSqlConfiguration().setValidationEnabled(validateTypes);
+
+ return cfg;
+ }
+
+ /** */
+ @Test
+ public void testCompatibleKeys() throws Exception {
+ testDifferentKeyTypes(false, CompatibleKey::new, false, false);
+ }
+
+ /** */
+ @Test
+ public void testCompatibleKeysWithRestart() throws Exception {
+ testDifferentKeyTypes(true, CompatibleKey::new, false, false);
+ }
+
+ /** */
+ @Test
+ public void testIncompatibleFieldWithValidation() throws Exception {
+ validateTypes = true;
+
+ testDifferentKeyTypes(false, IncompatibleFieldKey::new, true, false);
+ }
+
+ /** */
+ @Test
+ public void testIncompatibleFieldWithoutValidation() throws Exception {
+ testDifferentKeyTypes(false, IncompatibleFieldKey::new, false, true);
+ }
+
+ /** */
+ @Test
+ public void testIncompatibleIndexedField() throws Exception {
+ indexedKeyCls = IndexedFieldKey.class;
+
+ testDifferentKeyTypes(false, IncompatibleFieldKey::new, true, false);
+ }
+
+ /** */
+ @Ignore("https://issues.apache.org/jira/browse/IGNITE-26467")
+ @Test
+ public void testIncompatibleAffinityField() throws Exception {
+ testDifferentKeyTypes(false, IncompatibleAffinityFieldKey::new, true,
false);
+ }
+
+ /** */
+ private void testDifferentKeyTypes(
+ boolean restart,
+ IntFunction<Object> keyFactory,
+ boolean expFailureOnPut,
+ boolean expFailureOnSelect
+ ) throws Exception {
+ startGrids(SRV_CNT).cluster().state(ClusterState.ACTIVE);
+
+ IgniteCache<Object, Object> cache0 = grid(0).cache(DEFAULT_CACHE_NAME);
+
+ int keyCnt = 10;
+
+ for (int i = 0; i < keyCnt; i++)
+ cache0.put(new BaseKey(i), new TestValue(UUID.randomUUID(), "0"));
+
+ checkSize(keyCnt);
+
+ if (restart) {
+ grid(0).cluster().state(ClusterState.INACTIVE);
+
+ stopAllGrids();
+
+ startGrids(SRV_CNT);
+ }
+
+ IgniteCache<Object, Object> cache1 = grid(0).cache(DEFAULT_CACHE_NAME);
+
+ for (int i = 0; i < keyCnt; i++) {
+ Object key = keyFactory.apply(i);
+ Object val = new TestValue(UUID.randomUUID(), "1");
+
+ if (expFailureOnPut)
+ GridTestUtils.assertThrowsWithCause(() -> cache1.put(key,
val), CacheException.class);
+ else
+ cache1.put(key, val);
+ }
+
+ int expSize = keyCnt * (expFailureOnPut ? 1 : 2);
+
+ if (expFailureOnSelect)
+ GridTestUtils.assertThrowsWithCause(() -> checkSize(expSize),
Exception.class);
+ else
+ checkSize(expSize);
+
+ assertNull("Nodes failed", failureHnd.awaitFailure(1_000L));
+ }
+
+ /** */
+ private void checkSize(int exp) {
+ assertEquals("Unexpected cache size", exp,
grid(0).cache(DEFAULT_CACHE_NAME).size());
+ assertEquals("Cache size and SQL size differs", exp,
+ sql(grid(0), "select kuid, name from \"" + DEFAULT_CACHE_NAME +
"\".TESTVALUE order by key").size());
+ }
+
+ /** */
+ private static class TestValue {
+ /** */
+ @QuerySqlField
+ private final UUID uid;
+
+ /** */
+ @QuerySqlField
+ private final String name;
+
+ /** */
+ private TestValue(UUID uid, String name) {
+ this.uid = uid;
+ this.name = name;
+ }
+ }
+
+ /** */
+ private static class BaseKey {
+ /** */
+ @AffinityKeyMapped
+ @QuerySqlField
+ private final int key;
+
+ /** */
+ @QuerySqlField
+ private final UUID kuid = UUID.randomUUID();
+
+ /** */
+ private BaseKey(int key) {
+ this.key = key;
+ }
+ }
+
+ /** */
+ private static class CompatibleKey {
+ /** */
+ @AffinityKeyMapped
+ @QuerySqlField
+ private final int key;
+
+ /** */
+ @QuerySqlField
+ private final UUID kuid = UUID.randomUUID();
+
+ /** */
+ private CompatibleKey(int key) {
+ this.key = key;
+ }
+ }
+
+ /** */
+ private static class IndexedFieldKey {
+ /** */
+ @AffinityKeyMapped
+ @QuerySqlField
+ private final int key;
+
+ /** */
+ @QuerySqlField(index = true)
+ private final UUID kuid = UUID.randomUUID();
+
+ /** */
+ private IndexedFieldKey(int key) {
+ this.key = key;
+ }
+ }
+
+ /** */
+ private static class IncompatibleFieldKey {
+ /** */
+ @AffinityKeyMapped
+ @QuerySqlField
+ private final int key;
+
+ /** */
+ @QuerySqlField
+ private final String kuid = UUID.randomUUID().toString();
+
+ /** */
+ private IncompatibleFieldKey(int key) {
+ this.key = key;
+ }
+ }
+
+ /** */
+ private static class IncompatibleAffinityFieldKey {
+ /** */
+ @AffinityKeyMapped
+ @QuerySqlField
+ private final long key;
+
+ /** */
+ @QuerySqlField
+ private final UUID kuid = UUID.randomUUID();
+
+ /** */
+ private IncompatibleAffinityFieldKey(int key) {
+ this.key = key;
+ }
+ }
+}
diff --git
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/ViewsIntegrationTest.java
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/ViewsIntegrationTest.java
index 87f66394e8e..f6a284baf9b 100644
---
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/ViewsIntegrationTest.java
+++
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/ViewsIntegrationTest.java
@@ -16,60 +16,38 @@
*/
package org.apache.ignite.internal.processors.query.calcite.integration;
-import java.util.Arrays;
-import java.util.List;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.cache.query.SqlFieldsQuery;
-import org.apache.ignite.calcite.CalciteQueryEngineConfiguration;
import org.apache.ignite.cluster.ClusterState;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.DataRegionConfiguration;
import org.apache.ignite.configuration.DataStorageConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
-import org.apache.ignite.configuration.SqlConfiguration;
-import org.apache.ignite.indexing.IndexingQueryEngineConfiguration;
import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.apache.ignite.internal.util.typedef.G;
import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
/** */
-@RunWith(Parameterized.class)
-public class ViewsIntegrationTest extends AbstractBasicIntegrationTest {
+public class ViewsIntegrationTest extends AbstractMultiEngineIntegrationTest {
/** */
private boolean persistenceEnabled;
/** */
private String[] predefinedSchemas;
- /** */
- @Parameterized.Parameter
- public String engine;
-
- /** */
- @Parameterized.Parameters(name = "Query engine={0}")
- public static Iterable<Object> params() {
- return Arrays.asList(CalciteQueryEngineConfiguration.ENGINE_NAME,
IndexingQueryEngineConfiguration.ENGINE_NAME);
- }
-
/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String
igniteInstanceName) throws Exception {
- return super.getConfiguration(igniteInstanceName)
- .setSqlConfiguration(new SqlConfiguration()
- .setSqlSchemas(predefinedSchemas)
-
.setQueryEnginesConfiguration(engine.equals(CalciteQueryEngineConfiguration.ENGINE_NAME)
?
- new CalciteQueryEngineConfiguration() : new
IndexingQueryEngineConfiguration()))
- .setDataStorageConfiguration(new DataStorageConfiguration()
- .setDefaultDataRegionConfiguration(new
DataRegionConfiguration()
- .setPersistenceEnabled(persistenceEnabled)));
- }
+ IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
- /** {@inheritDoc} */
- @Override protected void beforeTestsStarted() throws Exception {
- // No-op.
+ cfg.setDataStorageConfiguration(new DataStorageConfiguration()
+ .setDefaultDataRegionConfiguration(new DataRegionConfiguration()
+ .setPersistenceEnabled(persistenceEnabled)));
+
+ cfg.getSqlConfiguration().setSqlSchemas(predefinedSchemas);
+
+ return cfg;
}
/** {@inheritDoc} */
@@ -406,12 +384,6 @@ public class ViewsIntegrationTest extends
AbstractBasicIntegrationTest {
sql("insert into my_table values (?, ?, ?)", i, i,
Integer.toString(i));
}
- /** */
- @Override protected List<List<?>> sql(IgniteEx ignite, String sql,
Object... params) {
- return ignite.context().query().querySqlFields(new
SqlFieldsQuery(sql).setArgs(params), true)
- .getAll();
- }
-
/** */
private void assertRowsCount(int cnt, String sql, Object... params) {
assertEquals(cnt, sql(sql, params).size());
diff --git
a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
index e5264bc0b02..cba9a7d3f4a 100644
---
a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
+++
b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
@@ -45,6 +45,7 @@ import
org.apache.ignite.internal.processors.query.calcite.integration.IndexSpoo
import
org.apache.ignite.internal.processors.query.calcite.integration.IntervalTest;
import
org.apache.ignite.internal.processors.query.calcite.integration.JoinIntegrationTest;
import
org.apache.ignite.internal.processors.query.calcite.integration.KeepBinaryIntegrationTest;
+import
org.apache.ignite.internal.processors.query.calcite.integration.KeyClassChangeIntegrationTest;
import
org.apache.ignite.internal.processors.query.calcite.integration.KillCommandDdlIntegrationTest;
import
org.apache.ignite.internal.processors.query.calcite.integration.KillQueryCommandDdlIntegrationTest;
import
org.apache.ignite.internal.processors.query.calcite.integration.LimitOffsetIntegrationTest;
@@ -167,6 +168,7 @@ import org.junit.runners.Suite;
TpchScale100Test.class,
UnnestIntegrationTest.class,
CalcitePlanningDumpTest.class,
+ KeyClassChangeIntegrationTest.class,
})
public class IntegrationTestSuite {
}
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/property/QueryBinaryProperty.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/property/QueryBinaryProperty.java
index 49bcf3b7a4a..cf1a182cd7d 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/property/QueryBinaryProperty.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/property/QueryBinaryProperty.java
@@ -23,6 +23,7 @@ import org.apache.ignite.binary.BinaryObject;
import org.apache.ignite.binary.BinaryObjectBuilder;
import org.apache.ignite.binary.BinaryType;
import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.binary.BinaryFieldEx;
import org.apache.ignite.internal.binary.BinaryObjectEx;
import org.apache.ignite.internal.binary.BinaryUtils;
import org.apache.ignite.internal.processors.cache.KeyCacheObjectImpl;
@@ -198,8 +199,12 @@ public class QueryBinaryProperty implements
GridQueryProperty {
BinaryField field0 = field;
- if (field0 == null && !fieldTaken) {
- BinaryType type = obj instanceof BinaryObjectEx ?
((BinaryObjectEx)obj).rawType() : obj.type();
+ assert obj instanceof BinaryObjectEx : "Unexpected object class " +
obj.getClass().getName();
+ assert field0 == null || field0 instanceof BinaryFieldEx : "Unexpected
field class " + field0.getClass().getName();
+
+ if ((field0 == null && !fieldTaken) ||
+ (field0 != null && ((BinaryObjectEx)obj).typeId() !=
((BinaryFieldEx)field0).enclosingTypeId())) {
+ BinaryType type = ((BinaryObjectEx)obj).rawType();
if (type != null) {
field0 = type.field(propName);