/* * Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software;Designed and Developed mainly by many Chinese * opensource volunteers. you can redistribute it and/or modify it under the * terms of the GNU General Public License version 2 only, as published by the * Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Any questions about this component can be directed to it's project Web address * https://code.google.com/p/opencloudb/. * */ package io.mycat.route; import io.mycat.cache.CachePool; import io.mycat.cache.CacheService; import io.mycat.cache.LayerCachePool; import io.mycat.config.Versions; import io.mycat.config.model.SchemaConfig; import io.mycat.config.model.SystemConfig; import io.mycat.route.factory.RouteStrategyFactory; import io.mycat.route.handler.HintHandler; import io.mycat.route.handler.HintHandlerFactory; import io.mycat.route.handler.HintSQLHandler; import io.mycat.server.ServerConnection; import io.mycat.server.parser.ServerParse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.SQLException; import java.sql.SQLSyntaxErrorException; import java.util.HashMap; import java.util.Map; public class RouteService { private static final Logger LOGGER = LoggerFactory .getLogger(RouteService.class); public static final String HINT_TYPE = "_serverHintType"; private final CachePool sqlRouteCache; private final LayerCachePool tableId2DataNodeCache; private final String HINT_SPLIT = "="; public RouteService(CacheService cachService) { sqlRouteCache = cachService.getCachePool("SQLRouteCache"); tableId2DataNodeCache = (LayerCachePool) cachService .getCachePool("TableID2DataNodeCache"); } public LayerCachePool getTableId2DataNodeCache() { return tableId2DataNodeCache; } public RouteResultset route(SystemConfig sysconf, SchemaConfig schema, int sqlType, String stmt, String charset, ServerConnection sc) throws SQLException { RouteResultset rrs = null; String cacheKey = null; /** * SELECT 类型的SQL, 检测,debug 模式下不缓存 */ if (sqlType == ServerParse.SELECT && !LOGGER.isDebugEnabled()) { cacheKey = (schema == null ? "NULL" : schema.getName())+"_"+sc.getUser()+"_" + stmt; rrs = (RouteResultset) sqlRouteCache.get(cacheKey); if (rrs != null) { return rrs; } } /*!mycat: sql = select name from aa */ /*!mycat: schema = test */ // boolean isMatchOldHint = stmt.startsWith(OLD_MYCAT_HINT); // boolean isMatchNewHint = stmt.startsWith(NEW_MYCAT_HINT); // if (isMatchOldHint || isMatchNewHint ) { int hintLength = RouteService.isHintSql(stmt); if(hintLength != -1){ int endPos = stmt.indexOf("*/"); if (endPos > 0) { // 用!mycat:内部的语句来做路由分析 // int hintLength = isMatchOldHint ? OLD_MYCAT_HINT.length() : NEW_MYCAT_HINT.length(); String hint = stmt.substring(hintLength, endPos).trim(); int firstSplitPos = hint.indexOf(HINT_SPLIT); if(firstSplitPos > 0 ){ Map hintMap= parseHint(hint); String hintType = (String) hintMap.get(HINT_TYPE); String hintSql = (String) hintMap.get(hintType); if( hintSql.length() == 0 ) { String msg = "comment in sql must meet :/*!"+ Versions.ANNOTATION_NAME+"type=value*/ or /*#"+ Versions.ANNOTATION_NAME+"type=value*/ or /*"+ Versions.ANNOTATION_NAME+"type=value*/: "+stmt; LOGGER.warn(msg); throw new SQLSyntaxErrorException(msg); } String realSQL = stmt.substring(endPos + "*/".length()).trim(); HintHandler hintHandler = HintHandlerFactory.getHintHandler(hintType); if( hintHandler != null ) { if ( hintHandler instanceof HintSQLHandler) { /** * 修复 注解SQL的 sqlType 与 实际SQL的 sqlType 不一致问题, 如: hint=SELECT,real=INSERT * fixed by zhuam */ int hintSqlType = ServerParse.parse( hintSql ) & 0xff; rrs = hintHandler.route(schema, sqlType, realSQL, charset, sc, tableId2DataNodeCache, hintSql,hintSqlType,hintMap); } else { rrs = hintHandler.route( schema, sqlType, realSQL, charset, sc, tableId2DataNodeCache, hintSql,sqlType,hintMap); } }else{ LOGGER.warn("TODO , support hint sql type : " + hintType); } }else{//fixed by runfriends@126.com String msg = "comment in sql must meet :/*!"+ Versions.ANNOTATION_NAME+"type=value*/ or /*#"+ Versions.ANNOTATION_NAME+"type=value*/ or /*"+ Versions.ANNOTATION_NAME+"type=value*/: "+stmt; LOGGER.warn(msg); throw new SQLSyntaxErrorException(msg); } } } else { stmt = stmt.trim(); rrs = RouteStrategyFactory.getRouteStrategy().route(schema, sqlType, stmt, charset, sc, tableId2DataNodeCache); } if (rrs != null && sqlType == ServerParse.SELECT && rrs.isCacheAble()&&!LOGGER.isDebugEnabled()) { sqlRouteCache.putIfAbsent(cacheKey, rrs); } return rrs; } public static int isHintSql(String sql){ char[] annotation = Versions.ANNOTATION_NAME.toCharArray(); int j = 0; int len = sql.length(); if(sql.charAt(j++) == '/' && sql.charAt(j++) == '*'){ char c = sql.charAt(j); // 过滤掉 空格 和 * 两种字符, 支持: "/** !mycat: */" 和 "/** #mycat: */" 形式的注解 while(j < len && c != '!' && c != '#' && (c == ' ' || c == '*')){ c = sql.charAt(++j); } //注解支持的'!'不被mysql单库兼容, //注解支持的'#'不被mybatis兼容 //考虑用mycat字符前缀标志Hintsql:"/** mycat: */" if(sql.charAt(j)==annotation[0]){ j--; } if(j + 6 >= len) {// prevent the following sql.charAt overflow return -1; // false } for(int i = 0;i < annotation.length;i++){ if(sql.charAt(++j) != annotation[i]){ break; } if(i == annotation.length-1){ return j + 1; } } } return -1; // false } private Map parseHint( String sql) { Map map=new HashMap(); int y=0; int begin=0; for(int i=0;i