{"record":{"id":"03c075d8ffe379ed","repo":"apache/shardingsphere","slug":"36-03c075","errorCode":"36","errorMessage":"SELECT ... %s can not support route to multiple data sources.","messagePattern":"SELECT \\.\\.\\. (.+?) can not support route to multiple data sources\\.","errorType":"exception","errorClass":"SelectMultipleDataSourcesWithCombineException","httpStatus":null,"severity":"error","filePath":"features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/route/engine/checker/dml/ShardingSelectRouteContextChecker.java","lineNumber":39,"sourceCode":"import org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;\nimport org.apache.shardingsphere.infra.route.context.RouteContext;\nimport org.apache.shardingsphere.infra.route.context.RouteUnit;\nimport org.apache.shardingsphere.infra.session.query.QueryContext;\nimport org.apache.shardingsphere.sharding.exception.syntax.SelectMultipleDataSourcesWithCombineException;\nimport org.apache.shardingsphere.sharding.route.engine.checker.ShardingRouteContextChecker;\nimport org.apache.shardingsphere.sharding.rule.ShardingRule;\nimport org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.SelectStatement;\n\n/**\n * Sharding select route context checker.\n */\npublic final class ShardingSelectRouteContextChecker implements ShardingRouteContextChecker {\n    \n    @Override\n    public void check(final ShardingRule shardingRule, final QueryContext queryContext, final ShardingSphereDatabase database, final ConfigurationProperties props, final RouteContext routeContext) {\n        SelectStatement selectStatement = (SelectStatement) queryContext.getSqlStatementContext().getSqlStatement();\n        if (selectStatement.getCombine().isPresent() && !isRouteToSingleDataSource(routeContext)) {\n            throw new SelectMultipleDataSourcesWithCombineException(selectStatement.getCombine().get().getCombineType().name());\n        }\n    }\n    \n    private boolean isRouteToSingleDataSource(final RouteContext routeContext) {\n        if (routeContext.getRouteUnits().isEmpty()) {\n            return true;\n        }\n        boolean result = true;\n        String sampleDataSourceName = routeContext.getRouteUnits().iterator().next().getDataSourceMapper().getLogicName();\n        for (RouteUnit each : routeContext.getRouteUnits()) {\n            if (!each.getDataSourceMapper().getLogicName().equals(sampleDataSourceName)) {\n                result = false;\n                break;\n            }\n        }\n        return result;\n    }\n}","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/apache/shardingsphere/blob/e952770a215630a3659c75d64369168cd3e26b82/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/route/engine/checker/dml/ShardingSelectRouteContextChecker.java#L21-L57","documentation":"SelectMultipleDataSourcesWithCombineException is thrown by ShardingSelectRouteContextChecker when a SELECT containing a combine operator (UNION / UNION ALL / INTERSECT / EXCEPT / MINUS) routes to more than one data source. Combining result sets requires a single merge point; ShardingSphere can merge combine results only when every branch is served by the same data source, checked here by comparing each route unit's data source logic name against the first unit's.","triggerScenarios":"SELECT ... UNION/INTERSECT/EXCEPT ... where the combined branches' routing touches two or more distinct data source logic names (isRouteToSingleDataSource returns false because some unit's DataSourceMapper logic name differs from the sample).","commonSituations":"UNION ALL over two sharded tables sharded by different keys landing on different data sources; a UNION of a sharded table with a single (broadcast/default) table stored in another data source; UNION queries that worked on one data source breaking after adding shards.","solutions":["Add predicates so every branch routes to the same data source (e.g. filter each branch on the same sharding-key range).","Move the involved tables into the same data source, or replicate the small/static table into every data source so all branches resolve locally.","Execute each branch separately and combine in the application instead of in SQL."],"exampleFix":"-- before: branches may land on different data sources\nSELECT id FROM t_order WHERE cust_id=?)\nUNION ALL\nSELECT id FROM t_refund WHERE cust_id=?;\n\n-- after: guarantee single data source per execution, or merge client-side\n-- (run two statements and union in application code)","handlingStrategy":"validation","validationCode":"// Before executing a combined SELECT, verify all branches target one data source\nString sql = \"SELECT id FROM t_order WHERE cust_id=? UNION ALL SELECT id FROM t_refund WHERE cust_id=?\";\nboolean hasCombine = sql.toUpperCase(Locale.ROOT).matches(\".*\\\\bUNION(\\\\s+ALL)?\\\\b.*|.*\\\\bINTERSECT\\\\b.*|.*\\\\bEXCEPT\\\\b.*\");\nif (hasCombine) {\n    // execute each branch separately with EXPLAIN and confirm identical data source in plans, else merge in app\n}","typeGuard":null,"tryCatchPattern":"try {\n    rs = stmt.executeQuery(sql);\n} catch (final SelectMultipleDataSourcesWithCombineException ex) {\n    // split into per-branch queries and merge results client-side\n}","preventionTips":["Prefer merging UNION branches in application code for sharded schemas.","Ensure tables combined in one query share data sources (or are broadcast tables)."],"tags":["sharding","select","union","combine","federation"],"backgroundTag":null,"analyzedSha":"e952770a215630a3659c75d64369168cd3e26b82","analyzedAt":"2026-08-14T13:54:53.392Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}