package io.mycat.route.impl; import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlReplaceStatement; import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; import com.alibaba.druid.sql.parser.SQLStatementParser; import com.google.common.base.Strings; import io.mycat.cache.LayerCachePool; import io.mycat.route.RouteResultset; import io.mycat.route.RouteResultsetNode; import io.mycat.route.parser.druid.*; import io.mycat.route.util.RouterUtil; import io.mycat.server.config.node.SchemaConfig; import io.mycat.server.parser.ServerParse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.SQLNonTransientException; import java.sql.SQLSyntaxErrorException; import java.util.Iterator; import java.util.SortedSet; import java.util.TreeSet; public class DruidMycatRouteStrategy extends AbstractRouteStrategy { private static final Logger LOGGER = LoggerFactory.getLogger(DruidMycatRouteStrategy.class); @Override public RouteResultset routeNormalSqlWithAST(SchemaConfig schema, String stmt, RouteResultset rrs, String charset, LayerCachePool cachePool) throws SQLNonTransientException { SQLStatementParser parser =null; if(schema.isNeedSupportMultiDBType()) { parser = new MycatStatementParser(stmt); } else { parser = new MySqlStatementParser(stmt); //只有mysql时只支持mysql语法 } 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); //DruidParser解析过程中已完成了路由的直接返回 if(rrs.isFinishedRoute()) { return rrs; } // rrs.setStatement(druidParser.getCtx().getSql()); //没有from的的select语句或其他 DruidShardingParseInfo ctx= druidParser.getCtx() ; 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>(); for(RouteCalculateUnit unit : druidParser.getCtx().getRouteCalculateUnits()) { RouteResultset rrsTmp = RouterUtil.tryRouteForTables(schema, druidParser.getCtx(), unit, rrs, isSelect(statement), cachePool); if(rrsTmp != null) { for(RouteResultsetNode node :rrsTmp.getNodes()) { nodeSet.add(node); } } } RouteResultsetNode[] nodes = new RouteResultsetNode[nodeSet.size()]; int i = 0; for (Iterator iterator = nodeSet.iterator(); iterator.hasNext();) { nodes[i] = (RouteResultsetNode) iterator.next(); i++; } rrs.setNodes(nodes); return rrs; } 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 "); } } /** * */ @Override public RouteResultset analyseShowSQL(SchemaConfig schema, RouteResultset rrs, String stmt) throws SQLSyntaxErrorException { String upStmt = stmt.toUpperCase(); int tabInd = upStmt.indexOf(" TABLES"); if (tabInd > 0) {// show tables int[] nextPost = RouterUtil.getSpecPos(upStmt, 0); if (nextPost[0] > 0) {// remove db info int end = RouterUtil.getSpecEndPos(upStmt, tabInd); if (upStmt.indexOf(" FULL") > 0) { stmt = "SHOW FULL TABLES" + stmt.substring(end); } else { stmt = "SHOW TABLES" + stmt.substring(end); } } String defaultNode= schema.getDataNode(); if(!Strings.isNullOrEmpty(defaultNode)) { return RouterUtil.routeToSingleNode(rrs, defaultNode, stmt); } return RouterUtil.routeToMultiNode(false, rrs, schema.getMetaDataNodes(), stmt); } // 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(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 @@ if(stmt.contains("@@")){ 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); } }