This is an automated email from the ASF dual-hosted git repository.
zstan 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 5c3cf86f3c4 IGNITE-22789 Throw an exception when join is used for
partitioned cache with affinity key and replicated cache with different
partitions count (#11449)
5c3cf86f3c4 is described below
commit 5c3cf86f3c4125f1f8d7c7023f5227d8659ebebc
Author: Evgeniy Stanilovskiy <[email protected]>
AuthorDate: Wed Jul 24 08:20:48 2024 +0300
IGNITE-22789 Throw an exception when join is used for partitioned cache
with affinity key and replicated cache with different partitions count (#11449)
---
.../query/h2/sql/GridSqlQuerySplitter.java | 7 +++
.../processors/query/h2/sql/SqlAstTraverser.java | 64 ++++++++++++++++++++--
.../org/apache/ignite/sqltests/BaseSqlTest.java | 2 -
.../ReplicatedSqlCustomPartitionsTest.java | 61 ++++++++++++++++++++-
4 files changed, 127 insertions(+), 7 deletions(-)
diff --git
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
index 818d5e009a1..0bc44b5935b 100644
---
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
+++
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
@@ -48,6 +48,7 @@ import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.h2.command.Prepared;
import org.h2.command.dml.Query;
+import org.jetbrains.annotations.Nullable;
import static
org.apache.ignite.internal.processors.query.h2.opt.join.CollocationModel.isCollocated;
import static
org.apache.ignite.internal.processors.query.h2.sql.GridSqlConst.TRUE;
@@ -1271,6 +1272,12 @@ public class GridSqlQuerySplitter {
SqlAstTraverser traverser = new SqlAstTraverser(mapQry,
distributedJoins, log);
traverser.traverse();
+ @Nullable SqlAstTraverser.MixedModeCachesJoinIssue mixedJoinIssue =
traverser.hasOuterJoinMixedCacheModeIssue();
+
+ if (mixedJoinIssue != null && mixedJoinIssue.error()) {
+ throw new CacheException(mixedJoinIssue.errorMessage());
+ }
+
map.columns(collectColumns(mapExps));
map.sortColumns(mapQry.sort());
map.partitioned(traverser.hasPartitionedTables());
diff --git
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/SqlAstTraverser.java
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/SqlAstTraverser.java
index f854c3f3565..764069b6ffc 100644
---
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/SqlAstTraverser.java
+++
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/SqlAstTraverser.java
@@ -18,9 +18,11 @@
package org.apache.ignite.internal.processors.query.h2.sql;
import java.util.HashSet;
+import java.util.Objects;
import java.util.Set;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
+import org.jetbrains.annotations.Nullable;
/**
* Traverse over query AST to find info about partitioned table usage.
@@ -44,6 +46,9 @@ class SqlAstTraverser {
/** Whether query has joins between replicated and partitioned tables. */
private boolean hasOuterJoinReplicatedPartitioned;
+ /** */
+ private @Nullable MixedModeCachesJoinIssue hasOuterJoinMixedCacheModeIssue;
+
/** Whether top-level table is replicated. */
private boolean isRootTableReplicated;
@@ -86,6 +91,11 @@ class SqlAstTraverser {
return (isRootTableReplicated && hasSubQueries &&
hasPartitionedTables);
}
+ /** */
+ public @Nullable MixedModeCachesJoinIssue
hasOuterJoinMixedCacheModeIssue() {
+ return hasOuterJoinMixedCacheModeIssue;
+ }
+
/**
* Traverse AST while join operation isn't found. Check it if found.
*
@@ -168,8 +178,24 @@ class SqlAstTraverser {
if (left == null || right == null)
return;
- if (join.isLeftOuter() && !left.isPartitioned() &&
right.isPartitioned())
- hasOuterJoinReplicatedPartitioned = true;
+ if (join.isLeftOuter() && !left.isPartitioned() &&
right.isPartitioned()) {
+ if (left.cacheContext().affinity().partitions() !=
right.cacheContext().affinity().partitions()) {
+ hasOuterJoinMixedCacheModeIssue = new
MixedModeCachesJoinIssue("Cache [cacheName=" + left.cacheName() +
+ ", partitionsCount=" +
left.cacheContext().affinity().partitions() +
+ "] can`t be joined with [cacheName=" +
right.cacheName() +
+ ", partitionsCount=" +
right.cacheContext().affinity().partitions() +
+ "] due to different affinity configuration. Join
between PARTITIONED and REPLICATED caches is possible "
+ + "only with the same partitions number
configuration.");
+ }
+ // the only way to compare predicate classes, not work for
different class loaders.
+ else if
(!Objects.equals(className(left.cacheInfo().config().getNodeFilter()),
className(right.cacheInfo().config()
+ .getNodeFilter()))) {
+ hasOuterJoinMixedCacheModeIssue = new
MixedModeCachesJoinIssue("Cache [cacheName=" + left.cacheName() + "] "
+ + "can`t be joined with [cacheName=" +
right.cacheName() + "] due to different node filters configuration.");
+ }
+ else
+ hasOuterJoinReplicatedPartitioned = true;
+ }
// Skip check if at least one of tables isn't partitioned.
if (!(left.isPartitioned() && right.isPartitioned()))
@@ -179,6 +205,11 @@ class SqlAstTraverser {
checkPartitionedJoin(join, where, left, right, log);
}
+ /** Object class name. */
+ @Nullable private static String className(@Nullable Object obj) {
+ return obj != null ? obj.getClass().getName() : null;
+ }
+
/**
* Checks whether an AST contains valid join operation between partitioned
tables.
* Join condition should be an equality operation of affinity keys of
tables. Conditions can be splitted between
@@ -242,7 +273,7 @@ class SqlAstTraverser {
private Set<String> affKeys(boolean pk, GridH2Table tbl) {
Set<String> affKeys = new HashSet<>();
- // User explicitly specify an affinity key. Otherwise use primary key.
+ // User explicitly specify an affinity key. Otherwise, use primary key.
if (!pk)
affKeys.add(tbl.getAffinityKeyColumn().columnName);
else {
@@ -279,7 +310,7 @@ class SqlAstTraverser {
if (GridSqlOperationType.EQUAL == op.operationType())
checkEqualityOperation(op, leftTbl, leftAffKeys, pkLeft, rightTbl,
rightAffKeys, pkRight);
- // Check affinity condition is covered fully. If true then return.
Otherwise go deeper.
+ // Check affinity condition is covered fully. If true then return.
Otherwise, go deeper.
if (affinityCondIsCovered(leftAffKeys, rightAffKeys))
return true;
@@ -342,4 +373,29 @@ class SqlAstTraverser {
private boolean affinityCondIsCovered(Set<String> leftAffKeys, Set<String>
rightAffKeys) {
return leftAffKeys.isEmpty() && rightAffKeys.isEmpty();
}
+
+ /** Mixed cache mode join issues. */
+ static class MixedModeCachesJoinIssue {
+ /** */
+ private final boolean err;
+
+ /** */
+ private final String msg;
+
+ /** Constructor. */
+ MixedModeCachesJoinIssue(String errMsg) {
+ err = true;
+ msg = errMsg;
+ }
+
+ /** Return {@code true} if error present. */
+ boolean error() {
+ return err;
+ }
+
+ /** Return appropriate error message. */
+ String errorMessage() {
+ return msg;
+ }
+ }
}
diff --git
a/modules/indexing/src/test/java/org/apache/ignite/sqltests/BaseSqlTest.java
b/modules/indexing/src/test/java/org/apache/ignite/sqltests/BaseSqlTest.java
index 61e48c6c64e..51a5efafdcb 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/sqltests/BaseSqlTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/sqltests/BaseSqlTest.java
@@ -1224,7 +1224,6 @@ public class BaseSqlTest extends
AbstractIndexingCommonTest {
/**
* Check that FULL OUTER JOIN (which is currently unsupported) causes
valid error message.
*/
- @SuppressWarnings("ThrowableNotThrown")
@Test
public void testFullOuterJoinIsNotSupported() {
testAllNodes(node -> {
@@ -1245,7 +1244,6 @@ public class BaseSqlTest extends
AbstractIndexingCommonTest {
/**
* Check that distributed FULL OUTER JOIN (which is currently unsupported)
causes valid error message.
*/
- @SuppressWarnings("ThrowableNotThrown")
@Test
public void testFullOuterDistributedJoinIsNotSupported() {
testAllNodes(node -> {
diff --git
a/modules/indexing/src/test/java/org/apache/ignite/sqltests/ReplicatedSqlCustomPartitionsTest.java
b/modules/indexing/src/test/java/org/apache/ignite/sqltests/ReplicatedSqlCustomPartitionsTest.java
index 0c8ab2baf0c..2c6bf601313 100644
---
a/modules/indexing/src/test/java/org/apache/ignite/sqltests/ReplicatedSqlCustomPartitionsTest.java
+++
b/modules/indexing/src/test/java/org/apache/ignite/sqltests/ReplicatedSqlCustomPartitionsTest.java
@@ -17,10 +17,12 @@
package org.apache.ignite.sqltests;
+import org.apache.ignite.IgniteException;
import org.apache.ignite.cache.CacheMode;
import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.testframework.GridTestUtils;
import org.junit.Test;
/**
@@ -28,7 +30,13 @@ import org.junit.Test;
*/
public class ReplicatedSqlCustomPartitionsTest extends ReplicatedSqlTest {
/** Test partitions count. */
- private static final int NUM_OF_PARTITIONS = 509;
+ static final int NUM_OF_PARTITIONS = 509;
+
+ /** */
+ static final String DEP_PART_TAB_DIFF = "DepartmentPartDiff";
+
+ /** */
+ static final String DEP_PART_TAB_DIFF_NF = "DepartmentPartDiffNf";
/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String
igniteInstanceName) throws Exception {
@@ -36,6 +44,11 @@ public class ReplicatedSqlCustomPartitionsTest extends
ReplicatedSqlTest {
.setCacheConfiguration(
new CacheConfiguration("partitioned" + NUM_OF_PARTITIONS + "*")
.setAffinity(new RendezvousAffinityFunction(false,
NUM_OF_PARTITIONS)),
+ new CacheConfiguration("partitioned" + NUM_OF_PARTITIONS +
"_DIFF*")
+ .setAffinity(new RendezvousAffinityFunction(false,
NUM_OF_PARTITIONS + 1)),
+ new CacheConfiguration("partitioned" + NUM_OF_PARTITIONS +
"_DIFF_NF*")
+ .setAffinity(new RendezvousAffinityFunction(false,
NUM_OF_PARTITIONS))
+ .setNodeFilter(clusterNode -> true),
new CacheConfiguration("replicated" + NUM_OF_PARTITIONS + "*")
.setCacheMode(CacheMode.REPLICATED)
.setAffinity(new RendezvousAffinityFunction(false,
NUM_OF_PARTITIONS))
@@ -55,6 +68,14 @@ public class ReplicatedSqlCustomPartitionsTest extends
ReplicatedSqlTest {
createDepartmentTable(DEP_PART_TAB, "template=partitioned" +
NUM_OF_PARTITIONS);
fillDepartmentTable(DEP_PART_TAB);
+
+ createDepartmentTable(DEP_PART_TAB_DIFF, "template=partitioned" +
NUM_OF_PARTITIONS + "_DIFF");
+
+ fillDepartmentTable(DEP_PART_TAB_DIFF);
+
+ createDepartmentTable(DEP_PART_TAB_DIFF_NF, "template=partitioned" +
NUM_OF_PARTITIONS + "_DIFF_NF");
+
+ fillDepartmentTable(DEP_PART_TAB_DIFF_NF);
}
/**
@@ -73,4 +94,42 @@ public class ReplicatedSqlCustomPartitionsTest extends
ReplicatedSqlTest {
public void testRightJoinPartitionedReplicated() {
checkRightJoinDepartmentEmployee(DEP_PART_TAB);
}
+
+ /**
+ * Check LEFT JOIN with collocated data of replicated and partitioned
tables with different affinity.
+ * This test relies on having the same number of partitions in replicated
and partitioned caches
+ */
+ @Test
+ public void testLeftJoinReplicatedPartitionedDiffPartitionsErr() {
+ GridTestUtils.assertThrows(log, () ->
checkLeftJoinEmployeeDepartment(DEP_PART_TAB_DIFF), IgniteException.class,
+ "only with the same partitions number configuration");
+ }
+
+ /**
+ * Check RIGHT JOIN with collocated data of partitioned and replicated
tables with different affinity.
+ */
+ @Test
+ public void testRightJoinPartitionedReplicatedDiffPartitionsErr() {
+ GridTestUtils.assertThrows(log, () ->
checkRightJoinDepartmentEmployee(DEP_PART_TAB_DIFF), IgniteException.class,
+ "only with the same partitions number configuration");
+ }
+
+ /**
+ * Check LEFT JOIN with collocated data of replicated and partitioned
tables with different node filter.
+ * This test relies on having the same number of partitions in replicated
and partitioned caches
+ */
+ @Test
+ public void testLeftJoinReplicatedPartitionedDiffNodeFilterErr() {
+ GridTestUtils.assertThrows(log, () ->
checkLeftJoinEmployeeDepartment(DEP_PART_TAB_DIFF_NF), IgniteException.class,
+ "due to different node filters configuration");
+ }
+
+ /**
+ * Check RIGHT JOIN with collocated data of partitioned and replicated
tables with different node filter.
+ */
+ @Test
+ public void testRightJoinPartitionedReplicatedDiffNodeFilterErr() {
+ GridTestUtils.assertThrows(log, () ->
checkRightJoinDepartmentEmployee(DEP_PART_TAB_DIFF_NF), IgniteException.class,
+ "due to different node filters configuration");
+ }
}