package io.mycat.route.impl; import java.sql.SQLNonTransientException; import java.sql.SQLSyntaxErrorException; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.regex.Matcher; import com.alibaba.druid.sql.SQLUtils; import com.alibaba.druid.sql.ast.SQLObject; import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.expr.SQLAllExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; import com.alibaba.druid.sql.ast.expr.SQLExistsExpr; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.expr.SQLInSubQueryExpr; import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlReplaceStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; import com.alibaba.druid.sql.parser.SQLStatementParser; import com.alibaba.druid.stat.TableStat.Relationship; import com.google.common.base.Strings; import io.mycat.backend.datasource.PhysicalDBNode; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.mycat.MycatServer; import io.mycat.backend.mysql.nio.handler.MiddlerQueryResultHandler; import io.mycat.backend.mysql.nio.handler.MiddlerResultHandler; import io.mycat.backend.mysql.nio.handler.SecondHandler; import io.mycat.cache.LayerCachePool; import io.mycat.config.ErrorCode; import io.mycat.config.model.SchemaConfig; import io.mycat.config.model.TableConfig; import io.mycat.config.model.rule.RuleConfig; import io.mycat.route.RouteResultset; import io.mycat.route.RouteResultsetNode; import io.mycat.route.function.SlotFunction; import io.mycat.route.impl.middlerResultStrategy.BinaryOpResultHandler; import io.mycat.route.impl.middlerResultStrategy.InSubQueryResultHandler; import io.mycat.route.impl.middlerResultStrategy.RouteMiddlerReaultHandler; import io.mycat.route.impl.middlerResultStrategy.SQLAllResultHandler; import io.mycat.route.impl.middlerResultStrategy.SQLExistsResultHandler; import io.mycat.route.impl.middlerResultStrategy.SQLQueryResultHandler; import io.mycat.route.parser.druid.DruidParser; import io.mycat.route.parser.druid.DruidParserFactory; import io.mycat.route.parser.druid.DruidShardingParseInfo; import io.mycat.route.parser.druid.MycatSchemaStatVisitor; import io.mycat.route.parser.druid.MycatStatementParser; import io.mycat.route.parser.druid.RouteCalculateUnit; import io.mycat.route.parser.util.ParseUtil; import io.mycat.route.util.RouterUtil; import io.mycat.server.NonBlockingSession; import io.mycat.server.ServerConnection; import io.mycat.server.parser.ServerParse; import io.mycat.server.util.SchemaUtil; public class DruidMycatRouteStrategy extends AbstractRouteStrategy { public static final Logger LOGGER = LoggerFactory.getLogger(DruidMycatRouteStrategy.class); private static Map<Class<?>,RouteMiddlerReaultHandler> middlerResultHandler = new HashMap<>(); static{ middlerResultHandler.put(SQLQueryExpr.class, new SQLQueryResultHandler()); middlerResultHandler.put(SQLBinaryOpExpr.class, new BinaryOpResultHandler()); middlerResultHandler.put(SQLInSubQueryExpr.class, new InSubQueryResultHandler()); middlerResultHandler.put(SQLExistsExpr.class, new SQLExistsResultHandler()); middlerResultHandler.put(SQLAllExpr.class, new SQLAllResultHandler()); } @Override public RouteResultset routeNormalSqlWithAST(SchemaConfig schema, String stmt, RouteResultset rrs,String charset, LayerCachePool cachePool,int sqlType,ServerConnection sc) throws SQLNonTransientException { /** * 只有mysql时只支持mysql语法 */ SQLStatementParser parser = null; if (schema.isNeedSupportMultiDBType()) { parser = new MycatStatementParser(stmt); } else { parser = new MySqlStatementParser(stmt); } MycatSchemaStatVisitor visitor = null; SQLStatement statement; /** * 解析出现问题统一抛SQL语法错误 */ try { statement = parser.parseStatement(); visitor = new MycatSchemaStatVisitor(); } catch (Exception t) { LOGGER.error("DruidMycatRouteStrategyError", t); throw new SQLSyntaxErrorException(t); } /** * 检验unsupported statement */ checkUnSupportedStatement(statement); DruidParser druidParser = DruidParserFactory.create(schema, statement, visitor); druidParser.parser(schema, rrs, statement, stmt,cachePool,visitor); DruidShardingParseInfo ctx= druidParser.getCtx() ; rrs.setTables(ctx.getTables()); if(visitor.isSubqueryRelationOr()){ String err = "In subQuery,the or condition is not supported."; LOGGER.error(err); throw new SQLSyntaxErrorException(err); } /* 按照以下情况路由 1.2.1 可以直接路由. 1.2.2 两个表夸库join的sql.调用calat 1.2.3 需要先执行subquery 的sql.把subquery拆分出来.获取结果后,与outerquery */ //add huangyiming 分片规则不一样的且表中带查询条件的则走Catlet List<String> tables = ctx.getTables(); SchemaConfig schemaConf = MycatServer.getInstance().getConfig().getSchemas().get(schema.getName()); int index = 0; RuleConfig firstRule = null; boolean directRoute = true; Set<String> firstDataNodes = new HashSet<String>(); Map<String, TableConfig> tconfigs = schemaConf==null?null:schemaConf.getTables(); Map<String,RuleConfig> rulemap = new HashMap<>(); if(tconfigs!=null){ for(String tableName : tables){ TableConfig tc = tconfigs.get(tableName); if(tc == null){ //add 别名中取 Map<String, String> tableAliasMap = ctx.getTableAliasMap(); if(tableAliasMap !=null && tableAliasMap.get(tableName) !=null){ tc = schemaConf.getTables().get(tableAliasMap.get(tableName)); } } if(index == 0){ if(tc !=null){ firstRule= tc.getRule(); //没有指定分片规则时,不做处理 if(firstRule==null){ continue; } firstDataNodes.addAll(tc.getDataNodes()); rulemap.put(tc.getName(), firstRule); } }else{ if(tc !=null){ //ER关系表的时候是可能存在字表中没有tablerule的情况,所以加上判断 RuleConfig ruleCfg = tc.getRule(); if(ruleCfg==null){ //没有指定分片规则时,不做处理 continue; } Set<String> dataNodes = new HashSet<>(tc.getDataNodes()); rulemap.put(tc.getName(), ruleCfg); //如果匹配规则不相同或者分片的datanode不相同则需要走子查询处理 if(firstRule!=null&&((ruleCfg !=null && !ruleCfg.getRuleAlgorithm().equals(firstRule.getRuleAlgorithm()) )||( !dataNodes.equals(firstDataNodes)))){ directRoute = false; break; } } } index++; } } RouteResultset rrsResult = rrs; if(directRoute){ //直接路由 if(!RouterUtil.isAllGlobalTable(ctx, schemaConf)){ if(rulemap.size()>1&&!checkRuleField(rulemap,visitor)){ String err = "In case of slice table,there is no rule field in the relationship condition!"; LOGGER.error(err); throw new SQLSyntaxErrorException(err); } } rrsResult = directRoute(rrs,ctx,schema,druidParser,statement,cachePool); }else{ int subQuerySize = visitor.getSubQuerys().size(); if(subQuerySize==0&&ctx.getTables().size()==2){ //两表关联,考虑使用catlet if(!visitor.getRelationships().isEmpty()){ rrs.setCacheAble(false); rrs.setFinishedRoute(true); rrsResult = catletRoute(schema,ctx.getSql(),charset,sc); }else{ rrsResult = directRoute(rrs,ctx,schema,druidParser,statement,cachePool); } }else if(subQuerySize==1){ //只涉及一张表的子查询,使用 MiddlerResultHandler 获取中间结果后,改写原有 sql 继续执行 TODO 后期可能会考虑多个子查询的情况. SQLSelect sqlselect = visitor.getSubQuerys().iterator().next(); if(!visitor.getRelationships().isEmpty()){ // 当 inner query 和 outer query 有关联条件时,暂不支持 String err = "In case of slice table,sql have different rules,the relationship condition is not supported."; LOGGER.error(err); throw new SQLSyntaxErrorException(err); }else{ SQLSelectQuery sqlSelectQuery = sqlselect.getQuery(); if(((MySqlSelectQueryBlock)sqlSelectQuery).getFrom() instanceof SQLExprTableSource) { rrs.setCacheAble(false); rrs.setFinishedRoute(true); rrsResult = middlerResultRoute(schema,charset,sqlselect,sqlType,statement,sc); } } }else if(subQuerySize >=2){ String err = "In case of slice table,sql has different rules,currently only one subQuery is supported."; LOGGER.error(err); throw new SQLSyntaxErrorException(err); } } return rrsResult; } /** * 子查询中存在关联查询的情况下,检查关联字段是否是分片字段 * @param rulemap * @param * @return */ private boolean checkRuleField(Map<String,RuleConfig> rulemap,MycatSchemaStatVisitor visitor){ if(!MycatServer.getInstance().getConfig().getSystem().isSubqueryRelationshipCheck()){ return true; } Set<Relationship> ships = visitor.getRelationships(); Iterator<Relationship> iter = ships.iterator(); while(iter.hasNext()){ Relationship ship = iter.next(); String lefttable = ship.getLeft().getTable().toUpperCase(); String righttable = ship.getRight().getTable().toUpperCase(); // 如果是同一个表中的关联条件,不做处理 if(lefttable.equals(righttable)){ return true; } RuleConfig leftconfig = rulemap.get(lefttable); RuleConfig rightconfig = rulemap.get(righttable); if(null!=leftconfig&&null!=rightconfig &&leftconfig.equals(rightconfig) &&leftconfig.getColumn().equals(ship.getLeft().getName().toUpperCase()) &&rightconfig.getColumn().equals(ship.getRight().getName().toUpperCase())){ return true; } } return false; } private RouteResultset middlerResultRoute(final SchemaConfig schema,final String charset,final SQLSelect sqlselect, final int sqlType,final SQLStatement statement,final ServerConnection sc){ final String middlesql = SQLUtils.toMySqlString(sqlselect); MiddlerResultHandler<String> middlerResultHandler = new MiddlerQueryResultHandler<>(new SecondHandler() { @Override public void doExecute(List param) { sc.getSession2().setMiddlerResultHandler(null); String sqls = null; // 路由计算 RouteResultset rrs = null; try { sqls = buildSql(statement,sqlselect,param); rrs = MycatServer .getInstance() .getRouterservice() .route(MycatServer.getInstance().getConfig().getSystem(), schema, sqlType,sqls.toLowerCase(), charset,sc ); } catch (Exception e) { StringBuilder s = new StringBuilder(); LOGGER.warn(s.append(this).append(sqls).toString() + " err:" + e.toString(),e); String msg = e.getMessage(); sc.writeErrMessage(ErrorCode.ER_PARSE_ERROR, msg == null ? e.getClass().getSimpleName() : msg); return; } NonBlockingSession noBlockSession = new NonBlockingSession(sc.getSession2().getSource()); noBlockSession.setMiddlerResultHandler(null); //session的预编译标示传递 noBlockSession.setPrepared(sc.getSession2().isPrepared()); if (rrs != null) { noBlockSession.setCanClose(false); noBlockSession.execute(rrs, ServerParse.SELECT); } } } ); sc.getSession2().setMiddlerResultHandler(middlerResultHandler); sc.getSession2().setCanClose(false); // 路由计算 RouteResultset rrs = null; try { rrs = MycatServer .getInstance() .getRouterservice() .route(MycatServer.getInstance().getConfig().getSystem(), schema, ServerParse.SELECT, middlesql, charset, sc); } catch (Exception e) { StringBuilder s = new StringBuilder(); LOGGER.warn(s.append(this).append(middlesql).toString() + " err:" + e.toString(),e); String msg = e.getMessage(); sc.writeErrMessage(ErrorCode.ER_PARSE_ERROR, msg == null ? e.getClass().getSimpleName() : msg); return null; } if(rrs!=null){ rrs.setCacheAble(false); } return rrs; } /** * 获取子查询执行结果后,改写原始sql 继续执行. * @param statement * @param sqlselect * @param param * @return */ private String buildSql(SQLStatement statement,SQLSelect sqlselect,List param){ SQLObject parent = sqlselect.getParent(); RouteMiddlerReaultHandler handler = middlerResultHandler.get(parent.getClass()); if(handler==null){ throw new UnsupportedOperationException(parent.getClass()+" current is not supported "); } return handler.dohandler(statement, sqlselect, parent, param); } /** * 两个表的情况,catlet * @param schema * @param stmt * @param charset * @param sc * @return */ private RouteResultset catletRoute(SchemaConfig schema,String stmt,String charset,ServerConnection sc){ RouteResultset rrs = null; try { rrs = MycatServer .getInstance() .getRouterservice() .route(MycatServer.getInstance().getConfig().getSystem(), schema, ServerParse.SELECT, "/*!mycat:catlet=io.mycat.catlets.ShareJoin */ "+stmt, charset, sc); }catch(Exception e){ } return rrs; } /** * 直接结果路由 * @param rrs * @param ctx * @param schema * @param druidParser * @param statement * @param cachePool * @return * @throws SQLNonTransientException */ private RouteResultset directRoute(RouteResultset rrs,DruidShardingParseInfo ctx,SchemaConfig schema, DruidParser druidParser,SQLStatement statement,LayerCachePool cachePool) throws SQLNonTransientException{ //改写sql:如insert语句主键自增长, 在直接结果路由的情况下,进行sql 改写处理 druidParser.changeSql(schema, rrs, statement,cachePool); /** * DruidParser 解析过程中已完成了路由的直接返回 */ if ( rrs.isFinishedRoute() ) { return rrs; } /** * 没有from的select语句或其他 */ if((ctx.getTables() == null || ctx.getTables().size() == 0)&&(ctx.getTableAliasMap()==null||ctx.getTableAliasMap().isEmpty())) { return RouterUtil.routeToSingleNode(rrs, schema.getRandomDataNode(), druidParser.getCtx().getSql()); } if(druidParser.getCtx().getRouteCalculateUnits().size() == 0) { RouteCalculateUnit routeCalculateUnit = new RouteCalculateUnit(); druidParser.getCtx().addRouteCalculateUnit(routeCalculateUnit); } SortedSet<RouteResultsetNode> nodeSet = new TreeSet<RouteResultsetNode>(); boolean isAllGlobalTable = RouterUtil.isAllGlobalTable(ctx, schema); for(RouteCalculateUnit unit: druidParser.getCtx().getRouteCalculateUnits()) { RouteResultset rrsTmp = RouterUtil.tryRouteForTables(schema, druidParser.getCtx(), unit, rrs, isSelect(statement), cachePool); if(rrsTmp != null&&rrsTmp.getNodes()!=null) { for(RouteResultsetNode node :rrsTmp.getNodes()) { nodeSet.add(node); } } if(isAllGlobalTable) {//都是全局表时只计算一遍路由 break; } } RouteResultsetNode[] nodes = new RouteResultsetNode[nodeSet.size()]; int i = 0; for (RouteResultsetNode aNodeSet : nodeSet) { nodes[i] = aNodeSet; if(statement instanceof MySqlInsertStatement &&ctx.getTables().size()==1&&schema.getTables().containsKey(ctx.getTables().get(0))) { RuleConfig rule = schema.getTables().get(ctx.getTables().get(0)).getRule(); if(rule!=null&& rule.getRuleAlgorithm() instanceof SlotFunction){ aNodeSet.setStatement(ParseUtil.changeInsertAddSlot(aNodeSet.getStatement(),aNodeSet.getSlot())); } } i++; } rrs.setNodes(nodes); //分表 /** * subTables="t_order$1-2,t_order3" *目前分表 1.6 开始支持 幵丏 dataNode 在分表条件下只能配置一个,分表条件下不支持join。 */ if(rrs.isDistTable()){ return this.routeDisTable(statement,rrs); } return rrs; } private SQLExprTableSource getDisTable(SQLTableSource tableSource,RouteResultsetNode node) throws SQLSyntaxErrorException{ if(node.getSubTableName()==null){ String msg = " sub table not exists for " + node.getName() + " on " + tableSource; LOGGER.error("DruidMycatRouteStrategyError " + msg); throw new SQLSyntaxErrorException(msg); } SQLIdentifierExpr sqlIdentifierExpr = new SQLIdentifierExpr(); sqlIdentifierExpr.setParent(tableSource.getParent()); sqlIdentifierExpr.setName(node.getSubTableName()); SQLExprTableSource from2 = new SQLExprTableSource(sqlIdentifierExpr); return from2; } private RouteResultset routeDisTable(SQLStatement statement, RouteResultset rrs) throws SQLSyntaxErrorException{ SQLTableSource tableSource = null; if(statement instanceof SQLInsertStatement) { SQLInsertStatement insertStatement = (SQLInsertStatement) statement; tableSource = insertStatement.getTableSource(); for (RouteResultsetNode node : rrs.getNodes()) { SQLExprTableSource from2 = getDisTable(tableSource, node); insertStatement.setTableSource(from2); node.setStatement(insertStatement.toString()); } } if(statement instanceof SQLDeleteStatement) { SQLDeleteStatement deleteStatement = (SQLDeleteStatement) statement; tableSource = deleteStatement.getTableSource(); SQLTableSource from = deleteStatement.getFrom(); for (RouteResultsetNode node : rrs.getNodes()) { SQLExprTableSource from2 = getDisTable(tableSource, node); if (from == null) { from2.setAlias(tableSource.toString()); deleteStatement.setFrom(from2); } else { String alias = from.getAlias(); from2.setAlias(alias); deleteStatement.setFrom(from2); } node.setStatement(deleteStatement.toString()); } } if(statement instanceof SQLUpdateStatement) { SQLUpdateStatement updateStatement = (SQLUpdateStatement) statement; tableSource = updateStatement.getTableSource(); String alias = tableSource.getAlias(); SQLExprTableSource exprSource = (SQLExprTableSource) tableSource; SQLIdentifierExpr expr = (SQLIdentifierExpr) exprSource.getExpr(); alias = alias == null ? expr.getName() : alias; for (RouteResultsetNode node : rrs.getNodes()) { SQLExprTableSource from2 = getDisTable(tableSource, node); //修复 // EXPLAIN UPDATE travelrecord SET user_id = 'Fred' WHERE id IN 1; => UPDATE travelrecord travelrecord SET user_id = 'Fred' WHERE id IN (1) if (!from2.toString().equals(alias)){ from2.setAlias(alias); } updateStatement.setTableSource(from2); node.setStatement(updateStatement.toString()); } } return rrs; } /** * SELECT 语句 */ private boolean isSelect(SQLStatement statement) { if(statement instanceof SQLSelectStatement) { return true; } return false; } /** * 检验不支持的SQLStatement类型 :不支持的类型直接抛SQLSyntaxErrorException异常 * @param statement * @throws SQLSyntaxErrorException */ private void checkUnSupportedStatement(SQLStatement statement) throws SQLSyntaxErrorException { //不支持replace语句 if(statement instanceof MySqlReplaceStatement) { throw new SQLSyntaxErrorException(" ReplaceStatement can't be supported,use insert into ...on duplicate key update... instead "); } } /** * 分析 SHOW SQL */ @Override public RouteResultset analyseShowSQL(SchemaConfig schema, RouteResultset rrs, String stmt) throws SQLSyntaxErrorException { String[] fields = SchemaUtil.parseShowTable(stmt); if ("1".equals(fields[0])) {// show tables String relSchema = fields[3]; String tableName = fields[8]; // if (relSchema != null && !relSchema.equalsIgnoreCase(schema.getName())) { schema = MycatServer.getInstance().getConfig().getSchemas().get(relSchema); if (schema == null) { throw new SQLSyntaxErrorException("not found schema : " + relSchema); } } // 2020/03/19 Ken.Li // if (StringUtils.isNotBlank(tableName) && tableName.indexOf("%") < 0) { if (StringUtils.isNotBlank(tableName) && tableName.indexOf("%") < 0 && !schema.getTables().isEmpty()) { TableConfig tableConfig = schema.getTables().get(tableName.toUpperCase()); if (tableConfig == null) { throw new SQLSyntaxErrorException( "not found table : " + tableName + " in schema " + schema.getName()); } String relSchemaTmp = RouterUtil.getAliveRandomDataNode(tableConfig); if (StringUtils.isNotBlank(relSchema)) { stmt = stmt.replaceAll(relSchema, relSchemaTmp); } return RouterUtil.routeToSingleNode(rrs, relSchemaTmp, stmt); } // remove db if (StringUtils.isNotBlank(relSchema)) { if(schema.getDataNode() !=null) { // 2020/03/19 Ken.Li // stmt = stmt.replaceAll(relSchema, schema.getDataNode()); PhysicalDBNode dataNode = MycatServer.getInstance().getConfig().getDataNodes().get(schema.getDataNode()); stmt = stmt.replaceAll(relSchema, dataNode.getDatabase()); } else { if(fields[2] !=null ) { stmt = stmt.replaceAll( fields[2] + "\\s*" + relSchema, ""); } } } // String defaultNode = schema.getDataNode(); if (!Strings.isNullOrEmpty(defaultNode)) { return RouterUtil.routeToSingleNode(rrs, defaultNode, stmt); } return RouterUtil.routeToMultiNode(false, rrs, schema.getMetaDataNodes(), stmt); } String upStmt = stmt.toUpperCase(); /** * show index or column */ int[] indx = RouterUtil.getSpecPos(upStmt, 0); if (indx[0] > 0) { /** * has table */ int[] repPos = { indx[0] + indx[1], 0 }; String tableName = RouterUtil.getShowTableName(schema,stmt, repPos); /** * IN DB pattern */ int[] indx2 = RouterUtil.getSpecPos(upStmt, indx[0] + indx[1] + 1); if (indx2[0] > 0) {// find LIKE OR WHERE repPos[1] = RouterUtil.getSpecEndPos(upStmt, indx2[0] + indx2[1]); } stmt = stmt.substring(0, indx[0]) + " FROM " + tableName + stmt.substring(repPos[1]); RouterUtil.routeForTableMeta(rrs, schema, tableName, stmt); return rrs; } /** * show create table tableName */ int[] createTabInd = RouterUtil.getCreateTablePos(upStmt, 0); if (createTabInd[0] > 0) { int tableNameIndex = createTabInd[0] + createTabInd[1]; if (upStmt.length() > tableNameIndex) { String tableName = stmt.substring(tableNameIndex).trim(); int ind2 = tableName.indexOf('.'); if (ind2 > 0) { tableName = tableName.substring(ind2 + 1); } RouterUtil.routeForTableMeta(rrs, schema, tableName, stmt); return rrs; } } return RouterUtil.routeToSingleNode(rrs, schema.getRandomDataNode(), stmt); } // /** // * 为一个表进行条件路由 // * @param schema // * @param tablesAndConditions // * @param tablesRouteMap // * @throws SQLNonTransientException // */ // private static RouteResultset findRouteWithcConditionsForOneTable(SchemaConfig schema, RouteResultset rrs, // Map<String, Set<ColumnRoutePair>> conditions, String tableName, String sql) throws SQLNonTransientException { // boolean cache = rrs.isCacheAble(); // //为分库表找路由 // tableName = tableName.toUpperCase(); // TableConfig tableConfig = schema.getTables().get(tableName); // //全局表或者不分库的表略过(全局表后面再计算) // if(tableConfig.isGlobalTable()) { // return null; // } else {//非全局表 // Set<String> routeSet = new HashSet<String>(); // String joinKey = tableConfig.getJoinKey(); // for(Map.Entry<String, Set<ColumnRoutePair>> condition : conditions.entrySet()) { // String colName = condition.getKey(); // //条件字段是拆分字段 // if(colName.equals(tableConfig.getPartitionColumn())) { // Set<ColumnRoutePair> columnPairs = condition.getValue(); // // for(ColumnRoutePair pair : columnPairs) { // if(pair.colValue != null) { // Integer nodeIndex = tableConfig.getRule().getRuleAlgorithm().calculate(pair.colValue); // if(nodeIndex == null) { // String msg = "can't find any valid datanode :" + tableConfig.getName() // + " -> " + tableConfig.getPartitionColumn() + " -> " + pair.colValue; // LOGGER.warn(msg); // throw new SQLNonTransientException(msg); // } // String node = tableConfig.getDataNodes().get(nodeIndex); // if(node != null) {//找到一个路由节点 // routeSet.add(node); // } // } // if(pair.rangeValue != null) { // Integer[] nodeIndexs = tableConfig.getRule().getRuleAlgorithm() // .calculateRange(pair.rangeValue.beginValue.toString(), pair.rangeValue.endValue.toString()); // for(Integer idx : nodeIndexs) { // String node = tableConfig.getDataNodes().get(idx); // if(node != null) {//找到一个路由节点 // routeSet.add(node); // } // } // } // } // } else if(joinKey != null && joinKey.equals(colName)) { // Set<String> dataNodeSet = RouterUtil.ruleCalculate( // tableConfig.getParentTC(), condition.getValue()); // if (dataNodeSet.isEmpty()) { // throw new SQLNonTransientException( // "parent key can't find any valid datanode "); // } // if (LOGGER.isDebugEnabled()) { // LOGGER.debug("found partion nodes (using parent partion rule directly) for child table to update " // + Arrays.toString(dataNodeSet.toArray()) + " sql :" + sql); // } // if (dataNodeSet.size() > 1) { // return RouterUtil.routeToMultiNode(rrs.isCacheAble(), rrs, schema.getAllDataNodes(), sql); // } else { // rrs.setCacheAble(true); // return RouterUtil.routeToSingleNode(rrs, dataNodeSet.iterator().next(), sql); // } // } else {//条件字段不是拆分字段也不是join字段,略过 // continue; // // } // } // return RouterUtil.routeToMultiNode(cache, rrs, routeSet, sql); // // } // // } public RouteResultset routeSystemInfo(SchemaConfig schema, int sqlType, String stmt, RouteResultset rrs) throws SQLSyntaxErrorException { switch(sqlType){ case ServerParse.SHOW:// if origSQL is like show tables return analyseShowSQL(schema, rrs, stmt); case ServerParse.SELECT://if origSQL is like select @@ int index = stmt.indexOf("@@"); if(index > 0 && "SELECT".equals(stmt.substring(0, index).trim().toUpperCase())){ return analyseDoubleAtSgin(schema, rrs, stmt); } break; case ServerParse.DESCRIBE:// if origSQL is meta SQL, such as describe table int ind = stmt.indexOf(' '); stmt = stmt.trim(); return analyseDescrSQL(schema, rrs, stmt, ind + 1); } return null; } /** * 对Desc语句进行分析 返回数据路由集合 * * * @param schema 数据库名 * @param rrs 数据路由集合 * @param stmt 执行语句 * @param ind 第一个' '的位置 * @return RouteResultset (数据路由集合) * @author mycat */ private static RouteResultset analyseDescrSQL(SchemaConfig schema, RouteResultset rrs, String stmt, int ind) { final String MATCHED_FEATURE = "DESCRIBE "; final String MATCHED2_FEATURE = "DESC "; int pos = 0; while (pos < stmt.length()) { char ch = stmt.charAt(pos); // 忽略处理注释 /* */ BEN if(ch == '/' && pos+4 < stmt.length() && stmt.charAt(pos+1) == '*') { if(stmt.substring(pos+2).indexOf("*/") != -1) { pos += stmt.substring(pos+2).indexOf("*/")+4; continue; } else { // 不应该发生这类情况。 throw new IllegalArgumentException("sql 注释 语法错误"); } } else if(ch == 'D'||ch == 'd') { // 匹配 [describe ] if(pos+MATCHED_FEATURE.length() < stmt.length() && (stmt.substring(pos).toUpperCase().indexOf(MATCHED_FEATURE) != -1)) { pos = pos + MATCHED_FEATURE.length(); break; } else if(pos+MATCHED2_FEATURE.length() < stmt.length() && (stmt.substring(pos).toUpperCase().indexOf(MATCHED2_FEATURE) != -1)) { pos = pos + MATCHED2_FEATURE.length(); break; } else { pos++; } } } // 重置ind坐标。BEN GONG ind = pos; int[] repPos = { ind, 0 }; String tableName = RouterUtil.getTableName(stmt, repPos); stmt = stmt.substring(0, ind) + tableName + stmt.substring(repPos[1]); RouterUtil.routeForTableMeta(rrs, schema, tableName, stmt); return rrs; } /** * 根据执行语句判断数据路由 * * @param schema 数据库名 * @param rrs 数据路由集合 * @param stmt 执行sql * @return RouteResultset 数据路由集合 * @throws SQLSyntaxErrorException * @author mycat */ private RouteResultset analyseDoubleAtSgin(SchemaConfig schema, RouteResultset rrs, String stmt) throws SQLSyntaxErrorException { String upStmt = stmt.toUpperCase(); int atSginInd = upStmt.indexOf(" @@"); if (atSginInd > 0) { return RouterUtil.routeToMultiNode(false, rrs, schema.getMetaDataNodes(), stmt); } return RouterUtil.routeToSingleNode(rrs, schema.getRandomDataNode(), stmt); } }