/* * 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.route.factory.RouteStrategyFactory; import io.mycat.route.handler.HintHandler; import io.mycat.route.handler.HintHandlerFactory; import io.mycat.route.handler.HintMasterDBHandler; import io.mycat.server.MySQLFrontConnection; import io.mycat.server.config.node.SchemaConfig; import io.mycat.server.config.node.SystemConfig; 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.Locale; public class RouteService { private static final Logger LOGGER = LoggerFactory.getLogger(RouteService.class); private final CachePool sqlRouteCache; private final LayerCachePool tableId2DataNodeCache; private final String OLD_MYCAT_HINT = "/*!mycat:"; // 处理自定义分片注解, 注解格式:/*!mycat: type = value */ sql private final String NEW_MYCAT_HINT = "/*#mycat:"; // 新的注解格式:/* !mycat: type = value */ sql,oldMycatHint的格式不兼容直连mysql 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, MySQLFrontConnection sc) throws SQLNonTransientException { RouteResultset rrs = null; String cacheKey = null; if (sqlType == ServerParse.SELECT) { cacheKey = schema.getName() + 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 ){ String hintType = hint.substring(0,firstSplitPos).trim().toLowerCase(Locale.US); String hintValue = hint.substring(firstSplitPos + HINT_SPLIT.length()).trim(); if(hintValue.length()==0){ LOGGER.warn("comment int sql must meet :/*!mycat:type=value*/ or /*#mycat:type=value*/: "+stmt); throw new SQLSyntaxErrorException("comment int sql must meet :/*!mycat:type=value*/ or /*#mycat:type=value*/: "+stmt); } String realSQL = stmt.substring(endPos + "*/".length()).trim(); HintHandler hintHandler = HintHandlerFactory.getHintHandler(hintType); if(hintType != null && hintType.equalsIgnoreCase("db_type")) hintHandler = new HintMasterDBHandler(); if(hintHandler != null){ rrs = hintHandler.route(sysconf,schema,sqlType,realSQL,charset,sc,tableId2DataNodeCache,hintValue); }else{ LOGGER.warn("TODO , support hint sql type : " + hintType); } }else{//fixed by runfriends@126.com LOGGER.warn("comment in sql must meet :/*!mycat:type=value*/ or /*#mycat:type=value*/: "+stmt); throw new SQLSyntaxErrorException("comment in sql must meet :/*!mcat:type=value*/ or /*#mycat:type=value*/: "+stmt); } } } else { stmt = stmt.trim(); rrs = RouteStrategyFactory.getRouteStrategy().route(sysconf, schema, sqlType, stmt, charset, sc, tableId2DataNodeCache); } if (rrs!=null && sqlType == ServerParse.SELECT && rrs.isCacheAble()) { sqlRouteCache.putIfAbsent(cacheKey, rrs); } return rrs; } public static int isHintSql(String sql){ int j = 0; int len = sql.length(); if(sql.charAt(j++) == '/' && sql.charAt(j++) == '*'){ char c = sql.charAt(j); // 过滤掉 空格 和 * 两种字符。 // 支持: "/** !mycat: */" 和 "/** #mycat: */" 形式的注解. by: digdeep@126.com while(j < len && c != '!' && c != '#' && (c == ' ' || c == '*')){ c = sql.charAt(++j); } if(j + 6 >= len) // prevent the following sql.charAt overflow return -1; // false if(sql.charAt(++j) == 'm' && sql.charAt(++j) == 'y' && sql.charAt(++j) == 'c' && sql.charAt(++j) == 'a' && sql.charAt(++j) == 't' && sql.charAt(++j) == ':') return j+1; // true,同时返回注解部分的长度 } return -1; // false } }