@Override public RouteResultset routeToMultiNode( SchemaConfig schema, boolean isSelect, boolean cache, QueryTreeNode ast, RouteResultset rrs, Collection<String> dataNodes, String stmt) throws SQLSyntaxErrorException { if (isSelect) { String sql = SelectSQLAnalyser.analyseMergeInf(rrs, ast, true, schema.getDefaultMaxLimit()); if (sql != null) { stmt = sql; } } RouteResultsetNode[] nodes = new RouteResultsetNode[dataNodes.size()]; int i = 0; for (String dataNode : dataNodes) { nodes[i++] = new RouteResultsetNode(dataNode, rrs.getSqlType(), stmt); } rrs.setCacheAble(cache); rrs.setNodes(nodes); return rrs; }
/** * 判断两个表所在节点集合是否相等 * * @param curRNodeSet 当前表所在的节点集合 * @param newNodeSet 新表所在节点集合 * @return 返回fase(不相等)或true(相等) * @author mycat */ private static boolean checkIfValidMultiTableRoute( Set<String> curRNodeSet, Collection<String> newNodeSet) { if (curRNodeSet.size() != newNodeSet.size()) { return false; } else { for (String dataNode : newNodeSet) { if (!curRNodeSet.contains(dataNode)) { return false; } } } return true; }
/** * 简单描述该方法的实现功能 * * @param ast QueryTreeNode * @param isSelect 是否是select语句 * @param rrs 数据路由集合 * @param schema 数据库名 the name of datebase * @param ctx ShardingParseInfo(分片) * @param sql 执行sql * @param cachePool * @return 一个数据路由集合 * @throws SQLNonTransientException * @author mycat */ private RouteResultset tryRouteForTables( QueryTreeNode ast, boolean isSelect, RouteResultset rrs, SchemaConfig schema, ShardingParseInfo ctx, String sql, LayerCachePool cachePool) throws SQLNonTransientException { Map<String, TableConfig> tables = schema.getTables(); Map<String, Map<String, Set<ColumnRoutePair>>> tbCondMap = ctx.tablesAndConditions; if (tbCondMap.size() == 1) { // only one table in this sql Map.Entry<String, Map<String, Set<ColumnRoutePair>>> entry = tbCondMap.entrySet().iterator().next(); TableConfig tc = getTableConfig(schema, entry.getKey()); if (tc.getRule() == null && tc.getDataNodes().size() == 1) { rrs.setCacheAble(isSelect); // 20140625 修复 配置为全局表单节点的语句不会自动加上limit sql = addSQLLmit(schema, rrs, ast, sql); return RouterUtil.routeToSingleNode(rrs, tc.getDataNodes().get(0), sql); } Map<String, Set<ColumnRoutePair>> colConds = entry.getValue(); return tryRouteForTable( ast, schema, rrs, isSelect, sql, tc, colConds.get(tc.getPartitionColumn()), colConds, cachePool); } else if (!ctx.joinList.isEmpty()) { for (JoinRel joinRel : ctx.joinList) { TableConfig rootc = schema.getJoinRel2TableMap().get(joinRel.joinSQLExp); if (rootc == null) { if (LOGGER.isDebugEnabled()) { LOGGER.debug( "can't find join relation in schema " + schema.getName() + " :" + joinRel.joinSQLExp + " maybe global table join"); } } else { if (rootc.getName().equals(joinRel.tableA)) { // table a is root table tbCondMap.remove(joinRel.tableB); } else if (rootc.getName().equals(joinRel.tableB)) { // table B is root table tbCondMap.remove(joinRel.tableA); } else if (tbCondMap.containsKey(rootc.getName())) { // contains root table in sql ,then remove all child tbCondMap.remove(joinRel.tableA); tbCondMap.remove(joinRel.tableB); } else { // both there A and B are not root table,remove any // one tbCondMap.remove(joinRel.tableA); } } } } if (tbCondMap.size() > 1) { Set<String> curRNodeSet = new LinkedHashSet<String>(); Set<String> routePairSet = new LinkedHashSet<String>(); // 拆分字段后路由节点 String curTableName = null; Map<String, List<String>> globalTableDataNodesMap = new LinkedHashMap<String, List<String>>(); for (Entry<String, Map<String, Set<ColumnRoutePair>>> e : tbCondMap.entrySet()) { String tableName = e.getKey(); Map<String, Set<ColumnRoutePair>> col2ValMap = e.getValue(); TableConfig tc = tables.get(tableName); if (tc == null) { String msg = "can't find table define in schema " + tableName + " schema:" + schema.getName(); LOGGER.warn(msg); throw new SQLNonTransientException(msg); } else if (tc.getTableType() == TableConfig.TYPE_GLOBAL_TABLE) { // add to globalTablelist globalTableDataNodesMap.put(tc.getName(), tc.getDataNodes()); continue; } Collection<String> newDataNodes = null; String partColmn = tc.getPartitionColumn(); Set<ColumnRoutePair> col2Val = partColmn == null ? null : col2ValMap.get(partColmn); if (col2Val == null || col2Val.isEmpty()) { if (tc.isRuleRequired()) { throw new IllegalArgumentException( "route rule for table " + tableName + " is required: " + sql); } newDataNodes = tc.getDataNodes(); } else { // match table with where condtion of partion colum values newDataNodes = RouterUtil.ruleCalculate(tc, col2Val); } if (curRNodeSet.isEmpty()) { curTableName = tc.getName(); curRNodeSet.addAll(newDataNodes); if (col2Val != null && !col2Val.isEmpty()) { routePairSet.addAll(newDataNodes); } } else { if (col2Val == null || col2Val.isEmpty()) { if (curRNodeSet.retainAll(newDataNodes) && routePairSet.isEmpty()) { String errMsg = "invalid route in sql, multi tables found but datanode has no intersection " + " sql:" + sql; LOGGER.warn(errMsg); throw new SQLNonTransientException(errMsg); } } else { if (routePairSet.isEmpty()) { routePairSet.addAll(newDataNodes); } else if (!checkIfValidMultiTableRoute(routePairSet, newDataNodes) || (curRNodeSet.retainAll(newDataNodes) && routePairSet.isEmpty())) { String errMsg = "invalid route in sql, " + routePairSet + " route to :" + Arrays.toString(routePairSet.toArray()) + " ,but " + tc.getName() + " to " + Arrays.toString(newDataNodes.toArray()) + " sql:" + sql; LOGGER.warn(errMsg); throw new SQLNonTransientException(errMsg); } } // if (!checkIfValidMultiTableRoute(curRNodeSet, // newDataNodes)) { // String errMsg = "invalid route in sql, " + curTableName // + " route to :" // + Arrays.toString(curRNodeSet.toArray()) // + " ,but " + tc.getName() + " to " // + Arrays.toString(newDataNodes.toArray()) // + " sql:" + sql; // LOGGER.warn(errMsg); // throw new SQLNonTransientException(errMsg); // } } } // only global table contains in sql if (!globalTableDataNodesMap.isEmpty() && curRNodeSet.isEmpty()) { List<String> resultList = Lists.newArrayList(); for (List<String> nodeList : globalTableDataNodesMap.values()) { if (resultList.isEmpty()) { resultList = nodeList; } else { if (resultList.retainAll(nodeList) && resultList.isEmpty()) { String errMsg = "invalid route in sql, multi global tables found but datanode has no intersection " + " sql:" + sql; LOGGER.warn(errMsg); throw new SQLNonTransientException(errMsg); } } } if (resultList.size() == 1) { rrs.setCacheAble(true); sql = addSQLLmit(schema, rrs, ast, sql); rrs = RouterUtil.routeToSingleNode(rrs, resultList.get(0), sql); } else { // mulit routes ,not cache route result rrs.setCacheAble(false); rrs = RouterUtil.routeToSingleNode(rrs, getRandomDataNode(resultList), sql); } return rrs; } else if (!globalTableDataNodesMap.isEmpty() && !curRNodeSet.isEmpty()) { // judge if global table contains all dataNodes of other tables for (Map.Entry<String, List<String>> entry : globalTableDataNodesMap.entrySet()) { if (!entry.getValue().containsAll(curRNodeSet)) { String errMsg = "invalid route in sql, " + curTableName + " route to :" + Arrays.toString(curRNodeSet.toArray()) + " ,but " + entry.getKey() + " to " + Arrays.toString(entry.getValue().toArray()) + " sql:" + sql; LOGGER.warn(errMsg); throw new SQLNonTransientException(errMsg); } } } if (curRNodeSet.size() > 1) { LOGGER.warn( "multi route tables found in this sql ,tables:" + Arrays.toString(tbCondMap.keySet().toArray()) + " sql:" + sql); return routeToMultiNode(schema, isSelect, isSelect, ast, rrs, curRNodeSet, sql); } else { return RouterUtil.routeToSingleNode(rrs, curRNodeSet.iterator().next(), sql); } } else { // only one table Map.Entry<String, Map<String, Set<ColumnRoutePair>>> entry = tbCondMap.entrySet().iterator().next(); Map<String, Set<ColumnRoutePair>> allColValues = entry.getValue(); TableConfig tc = getTableConfig(schema, entry.getKey()); return tryRouteForTable( ast, schema, rrs, isSelect, sql, tc, allColValues.get(tc.getPartitionColumn()), allColValues, cachePool); } }