This is an automated email from the ASF dual-hosted git repository.

duanzhengqiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new 4515826080a Add check for select with union all routing to multi data 
sources (#35037)
4515826080a is described below

commit 4515826080a5f19b20b816d25b7d78025f7272f3
Author: ZhangCheng <chengzh...@apache.org>
AuthorDate: Wed Mar 19 16:33:54 2025 +0800

    Add check for select with union all routing to multi data sources (#35037)
    
    * Add check for select with union all routing to multi data sources
    
    * Add check for select with union all routing to multi data sources
---
 RELEASE-NOTES.md                                   |  1 +
 ...ectMultipleDataSourcesWithCombineException.java | 33 ++++++++
 .../ShardingRouteContextCheckerFactory.java        |  5 ++
 .../dml/ShardingSelectRouteContextChecker.java     | 57 +++++++++++++
 .../dml/ShardingSelectRouteContextCheckerTest.java | 97 ++++++++++++++++++++++
 5 files changed, 193 insertions(+)

diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index 6a1428145a5..beb32e75200 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -27,6 +27,7 @@
 1. SQL Parser: Support MySQL DELETE with statement parse - 
[#34817](https://github.com/apache/shardingsphere/pull/34817) 
 1. Encrypt: Use EncryptDerivedColumnSuffix to enhance encrypt table subquery 
rewrite logic - [#34829](https://github.com/apache/shardingsphere/pull/34829)
 1. Encrypt: Add quotes to encrypt rewrite derived columns - 
[#34950](https://github.com/apache/shardingsphere/pull/34950)
+1. SQL Router: Add check for select with union all routing to multi data 
sources - [#35037](https://github.com/apache/shardingsphere/pull/35037)
 
 ### Bug Fixes
 
diff --git 
a/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/exception/syntax/SelectMultipleDataSourcesWithCombineException.java
 
b/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/exception/syntax/SelectMultipleDataSourcesWithCombineException.java
new file mode 100644
index 00000000000..dcb43611ee6
--- /dev/null
+++ 
b/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/exception/syntax/SelectMultipleDataSourcesWithCombineException.java
@@ -0,0 +1,33 @@
+/*
+ * 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.shardingsphere.sharding.exception.syntax;
+
+import 
org.apache.shardingsphere.infra.exception.core.external.sql.sqlstate.XOpenSQLState;
+import org.apache.shardingsphere.sharding.exception.ShardingSQLException;
+
+/**
+ * Select multiple data sources with combine Exception.
+ */
+public final class SelectMultipleDataSourcesWithCombineException extends 
ShardingSQLException {
+    
+    private static final long serialVersionUID = 6817787033278995625L;
+    
+    public SelectMultipleDataSourcesWithCombineException(final String 
operation) {
+        super(XOpenSQLState.SYNTAX_ERROR, 36, "SELECT ... %s can not support 
route to multiple data sources.", operation);
+    }
+}
diff --git 
a/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/route/engine/checker/ShardingRouteContextCheckerFactory.java
 
b/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/route/engine/checker/ShardingRouteContextCheckerFactory.java
index 8b0aa3ff1da..6e40d8d150c 100644
--- 
a/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/route/engine/checker/ShardingRouteContextCheckerFactory.java
+++ 
b/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/route/engine/checker/ShardingRouteContextCheckerFactory.java
@@ -28,6 +28,7 @@ import 
org.apache.shardingsphere.sharding.route.engine.checker.ddl.ShardingPrepa
 import 
org.apache.shardingsphere.sharding.route.engine.checker.ddl.ShardingRenameTableRouteContextChecker;
 import 
org.apache.shardingsphere.sharding.route.engine.checker.dml.ShardingDeleteRouteContextChecker;
 import 
org.apache.shardingsphere.sharding.route.engine.checker.dml.ShardingInsertRouteContextChecker;
+import 
org.apache.shardingsphere.sharding.route.engine.checker.dml.ShardingSelectRouteContextChecker;
 import 
org.apache.shardingsphere.sharding.route.engine.checker.dml.ShardingUpdateRouteContextChecker;
 import 
org.apache.shardingsphere.sharding.route.engine.condition.ShardingConditions;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.SQLStatement;
@@ -42,6 +43,7 @@ import 
org.apache.shardingsphere.sql.parser.statement.core.statement.ddl.RenameT
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.dml.DMLStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.dml.DeleteStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.dml.InsertStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.dml.SelectStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.dml.UpdateStatement;
 
 import java.util.Optional;
@@ -104,6 +106,9 @@ public final class ShardingRouteContextCheckerFactory {
         if (sqlStatement instanceof DeleteStatement) {
             return Optional.of(new ShardingDeleteRouteContextChecker());
         }
+        if (sqlStatement instanceof SelectStatement) {
+            return Optional.of(new ShardingSelectRouteContextChecker());
+        }
         return Optional.empty();
     }
 }
diff --git 
a/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/route/engine/checker/dml/ShardingSelectRouteContextChecker.java
 
b/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/route/engine/checker/dml/ShardingSelectRouteContextChecker.java
new file mode 100644
index 00000000000..882e89f2259
--- /dev/null
+++ 
b/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/route/engine/checker/dml/ShardingSelectRouteContextChecker.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.shardingsphere.sharding.route.engine.checker.dml;
+
+import org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
+import 
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
+import org.apache.shardingsphere.infra.route.context.RouteContext;
+import org.apache.shardingsphere.infra.route.context.RouteUnit;
+import org.apache.shardingsphere.infra.session.query.QueryContext;
+import 
org.apache.shardingsphere.sharding.exception.syntax.SelectMultipleDataSourcesWithCombineException;
+import 
org.apache.shardingsphere.sharding.route.engine.checker.ShardingRouteContextChecker;
+import org.apache.shardingsphere.sharding.rule.ShardingRule;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.dml.SelectStatement;
+
+/**
+ * Sharding select route context checker.
+ */
+public final class ShardingSelectRouteContextChecker implements 
ShardingRouteContextChecker {
+    
+    @Override
+    public void check(final ShardingRule shardingRule, final QueryContext 
queryContext, final ShardingSphereDatabase database, final 
ConfigurationProperties props, final RouteContext routeContext) {
+        SelectStatement selectStatement = (SelectStatement) 
queryContext.getSqlStatementContext().getSqlStatement();
+        if (selectStatement.getCombine().isPresent() && 
!isRouteToSingleDataSource(routeContext)) {
+            throw new 
SelectMultipleDataSourcesWithCombineException(selectStatement.getCombine().get().getCombineType().name());
+        }
+    }
+    
+    private boolean isRouteToSingleDataSource(final RouteContext routeContext) 
{
+        if (routeContext.getRouteUnits().isEmpty()) {
+            return true;
+        }
+        boolean result = true;
+        String sampleDataSourceName = 
routeContext.getRouteUnits().iterator().next().getDataSourceMapper().getLogicName();
+        for (RouteUnit each : routeContext.getRouteUnits()) {
+            if 
(!each.getDataSourceMapper().getLogicName().equals(sampleDataSourceName)) {
+                result = false;
+                break;
+            }
+        }
+        return result;
+    }
+}
diff --git 
a/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/route/engine/checker/dml/ShardingSelectRouteContextCheckerTest.java
 
b/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/route/engine/checker/dml/ShardingSelectRouteContextCheckerTest.java
new file mode 100644
index 00000000000..fbedbe881ac
--- /dev/null
+++ 
b/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/route/engine/checker/dml/ShardingSelectRouteContextCheckerTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.shardingsphere.sharding.route.engine.checker.dml;
+
+import 
org.apache.shardingsphere.infra.binder.context.statement.dml.SelectStatementContext;
+import 
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
+import org.apache.shardingsphere.infra.route.context.RouteContext;
+import org.apache.shardingsphere.infra.route.context.RouteMapper;
+import org.apache.shardingsphere.infra.route.context.RouteUnit;
+import org.apache.shardingsphere.infra.session.query.QueryContext;
+import 
org.apache.shardingsphere.sharding.exception.syntax.SelectMultipleDataSourcesWithCombineException;
+import org.apache.shardingsphere.sharding.rule.ShardingRule;
+import org.apache.shardingsphere.sql.parser.statement.core.enums.CombineType;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.combine.CombineSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.dml.SelectStatement;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class ShardingSelectRouteContextCheckerTest {
+    
+    @Mock
+    private ShardingRule shardingRule;
+    
+    @Mock
+    private QueryContext queryContext;
+    
+    @Mock
+    private RouteContext routeContext;
+    
+    @Mock
+    private ShardingSphereDatabase database;
+    
+    @Test
+    void assertCombineExistsAndRoutesToMultipleDataSources() {
+        SelectStatement selectStatement = mock(SelectStatement.class);
+        CombineSegment combineSegment = mock(CombineSegment.class);
+        
when(combineSegment.getCombineType()).thenReturn(CombineType.UNION_ALL);
+        
when(selectStatement.getCombine()).thenReturn(Optional.of(combineSegment));
+        SelectStatementContext selectContext = 
mock(SelectStatementContext.class);
+        when(selectContext.getSqlStatement()).thenReturn(selectStatement);
+        when(queryContext.getSqlStatementContext()).thenReturn(selectContext);
+        
when(routeContext.getRouteUnits()).thenReturn(Arrays.asList(createMockRouteUnit("ds1"),
 createMockRouteUnit("ds2")));
+        assertThrows(SelectMultipleDataSourcesWithCombineException.class, () 
-> new ShardingSelectRouteContextChecker().check(shardingRule, queryContext, 
database, mock(), routeContext));
+    }
+    
+    @Test
+    void assertCombineExistsAndRoutesToSingleDataSource() {
+        SelectStatement selectStatement = mock(SelectStatement.class);
+        
when(selectStatement.getCombine()).thenReturn(Optional.of(mock(CombineSegment.class)));
+        SelectStatementContext selectContext = 
mock(SelectStatementContext.class);
+        when(selectContext.getSqlStatement()).thenReturn(selectStatement);
+        when(queryContext.getSqlStatementContext()).thenReturn(selectContext);
+        
when(routeContext.getRouteUnits()).thenReturn(Arrays.asList(createMockRouteUnit("ds1"),
 createMockRouteUnit("ds1")));
+        assertDoesNotThrow(() -> new 
ShardingSelectRouteContextChecker().check(shardingRule, queryContext, database, 
mock(), routeContext));
+    }
+    
+    @Test
+    void assertNoCombineExists() {
+        SelectStatement selectStatement = mock(SelectStatement.class);
+        when(selectStatement.getCombine()).thenReturn(Optional.empty());
+        SelectStatementContext selectContext = 
mock(SelectStatementContext.class);
+        when(selectContext.getSqlStatement()).thenReturn(selectStatement);
+        when(queryContext.getSqlStatementContext()).thenReturn(selectContext);
+        assertDoesNotThrow(() -> new 
ShardingSelectRouteContextChecker().check(shardingRule, queryContext, database, 
mock(), routeContext));
+    }
+    
+    private RouteUnit createMockRouteUnit(final String dataSourceName) {
+        return new RouteUnit(new RouteMapper(dataSourceName, dataSourceName), 
Collections.emptyList());
+    }
+}

Reply via email to