package io.mycat.route.util;

import java.sql.SQLNonTransientException;
import java.sql.SQLSyntaxErrorException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.expr.SQLCharExpr;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.statement.SQLCharacterDataType;
import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;
import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement;
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;
import com.alibaba.druid.wall.spi.WallVisitorUtils;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;

import io.mycat.MycatServer;
import io.mycat.backend.datasource.PhysicalDBNode;
import io.mycat.backend.datasource.PhysicalDBPool;
import io.mycat.backend.datasource.PhysicalDatasource;
import io.mycat.backend.mysql.nio.handler.FetchStoreNodeOfChildTableHandler;
import io.mycat.backend.mysql.nio.handler.JDBCFetchStoreNodeOfChildTableHandler;
import io.mycat.cache.LayerCachePool;
import io.mycat.config.ErrorCode;
import io.mycat.config.MycatConfig;
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.SessionSQLPair;
import io.mycat.route.function.AbstractPartitionAlgorithm;
import io.mycat.route.function.SlotFunction;
import io.mycat.route.parser.druid.DruidShardingParseInfo;
import io.mycat.route.parser.druid.RouteCalculateUnit;
import io.mycat.server.ServerConnection;
import io.mycat.server.parser.ServerParse;
import io.mycat.sqlengine.mpp.ColumnRoutePair;
import io.mycat.sqlengine.mpp.LoadData;
import io.mycat.util.StringUtil;

/**
 * 从ServerRouterUtil中抽取的一些公用方法,路由解析工具类
 * @author wang.dw
 *
 */
public class RouterUtil {

	private static final Logger LOGGER = LoggerFactory.getLogger(RouterUtil.class);

	/**
	 * 移除执行语句中的数据库名
	 *
	 * @param stmt		 执行语句
	 * @param schema  	数据库名
	 * @return 			执行语句
	 * @author mycat
	 *
	 * @modification 修正移除schema的方法
	 * @date 2016/12/29
	 * @modifiedBy Hash Zhang
	 *
	 */
	public static String removeSchema(String stmt, String schema) {
		final String upStmt = stmt.toUpperCase();
		final String upSchema = schema.toUpperCase() + ".";
		final String upSchema2 = new StringBuilder("`").append(schema.toUpperCase()).append("`.").toString();
		int strtPos = 0;
		int indx = 0;

		int indx1 = upStmt.indexOf(upSchema, strtPos);
		int indx2 = upStmt.indexOf(upSchema2, strtPos);
		boolean flag = indx1 < indx2 ? indx1 == -1 : indx2 != -1;
		indx = !flag ? indx1 > 0 ? indx1 : indx2 : indx2 > 0 ? indx2 : indx1;
		if (indx < 0) {
			return stmt;
		}

		int firstE = upStmt.indexOf("'");
		int endE = upStmt.lastIndexOf("'");

		StringBuilder sb = new StringBuilder();
		while (indx > 0) {
			sb.append(stmt.substring(strtPos, indx));

			if (flag) {
				strtPos = indx + upSchema2.length();
			} else {
				strtPos = indx + upSchema.length();
			}
			if (indx > firstE && indx < endE && countChar(stmt, indx) % 2 == 1) {
				sb.append(stmt.substring(indx, indx + schema.length() + 1));
			}
			indx1 = upStmt.indexOf(upSchema, strtPos);
			indx2 = upStmt.indexOf(upSchema2, strtPos);
			flag = indx1 < indx2 ? indx1 == -1 : indx2 != -1;
			indx = !flag ? indx1 > 0 ? indx1 : indx2 : indx2 > 0 ? indx2 : indx1;
		}
		sb.append(stmt.substring(strtPos));
		return sb.toString();
	}

	private static int countChar(String sql,int end)
	{
		int count=0;
		boolean skipChar = false;
		for (int i = 0; i < end; i++) {
			if(sql.charAt(i)=='\'' && !skipChar) {
				count++;
				skipChar = false;
			}else if( sql.charAt(i)=='\\'){
				skipChar = true;
			}else{
				skipChar = false;
			}
		}
		return count;
	}

	/**
	 * 获取第一个节点作为路由
	 *
	 * @param rrs		          数据路由集合
	 * @param dataNode  	数据库所在节点
	 * @param stmt   		执行语句
	 * @return 				数据路由集合
	 *
	 * @author mycat
	 */
	public static RouteResultset routeToSingleNode(RouteResultset rrs,
			String dataNode, String stmt) {
		if (dataNode == null) {
			return rrs;
		}
		RouteResultsetNode[] nodes = new RouteResultsetNode[1];
		nodes[0] = new RouteResultsetNode(dataNode, rrs.getSqlType(), stmt);//rrs.getStatement()
		nodes[0].setSource(rrs);
		rrs.setNodes(nodes);
		rrs.setFinishedRoute(true);
		if(rrs.getDataNodeSlotMap().containsKey(dataNode)){
			nodes[0].setSlot(rrs.getDataNodeSlotMap().get(dataNode));
		}
		if (rrs.getCanRunInReadDB() != null) {
			nodes[0].setCanRunInReadDB(rrs.getCanRunInReadDB());
		}
		if(rrs.getRunOnSlave() != null){
			nodes[0].setRunOnSlave(rrs.getRunOnSlave());
		}

		return rrs;
	}



	/**
	 * 修复DDL路由
	 *
	 * @return RouteResultset
	 * @author aStoneGod
	 */
	public static RouteResultset routeToDDLNode(RouteResultset rrs, int sqlType, String stmt,SchemaConfig schema) throws SQLSyntaxErrorException {
		stmt = getFixedSql(stmt);
		String tablename = "";
		//去除sql前面的注册,如/* ApplicationName=DBeaver 6.0.1 - Main */,这个注册会导致create table出错
		stmt = stmt.replaceFirst("\\/\\*.*\\*\\/\\s*", "");
		final String upStmt = stmt.toUpperCase();
		String rrsStmt = new String(upStmt);
		if(upStmt.startsWith("CREATE")){
			if (upStmt.contains("CREATE INDEX ") || upStmt.contains("CREATE UNIQUE INDEX ")){
				tablename = RouterUtil.getTableName(stmt, RouterUtil.getCreateIndexPos(upStmt, 0));
				/**
				 * Date:2017年11月2日
				 * @author SvenAugustus
				修复oracle 语法不支持 drop index i_t_f on t_test,只有drop index i_t_f;
				 */
				if(!"oracle".equalsIgnoreCase(schema.getDefaultDataNodeDbType())){
					int onInd = upStmt.indexOf("ON", 0);
					rrsStmt= upStmt.substring(0, onInd);
				}
			}else {
				tablename = RouterUtil.getTableName(stmt, RouterUtil.getCreateTablePos(upStmt, 0));
			}
		}else if(upStmt.startsWith("DROP")){
			if (upStmt.contains("DROP INDEX ")){
				tablename = RouterUtil.getTableName(stmt, RouterUtil.getDropIndexPos(upStmt, 0));
			}else {
				tablename = RouterUtil.getTableName(stmt, RouterUtil.getDropTablePos(upStmt, 0));
			}
		}else if(upStmt.startsWith("ALTER")){
			tablename = RouterUtil.getTableName(stmt, RouterUtil.getAlterTablePos(upStmt, 0));
		}else if (upStmt.startsWith("TRUNCATE")){
			tablename = RouterUtil.getTableName(stmt, RouterUtil.getTruncateTablePos(upStmt, 0));
		}
		tablename = tablename.toUpperCase();

		if (schema.getTables().containsKey(tablename)){
			if(ServerParse.DDL==sqlType){
				List<String> dataNodes = new ArrayList<>();
				Map<String, TableConfig> tables = schema.getTables();
				TableConfig tc=tables.get(tablename);
				if (tables != null && (tc  != null)) {
					dataNodes = tc.getDataNodes();
				}
				boolean isSlotFunction= tc.getRule() != null && tc.getRule().getRuleAlgorithm() instanceof SlotFunction;
				Iterator<String> iterator1 = dataNodes.iterator();
				int nodeSize = dataNodes.size();
				RouteResultsetNode[] nodes = new RouteResultsetNode[nodeSize];
				if(isSlotFunction){
					stmt=changeCreateTable(schema,tablename,stmt);
				}
				for(int i=0;i<nodeSize;i++){
					String name = iterator1.next();
					nodes[i] = new RouteResultsetNode(name, sqlType, stmt);
					nodes[i].setSource(rrs);
					if(rrs.getDataNodeSlotMap().containsKey(name)){
						nodes[i].setSlot(rrs.getDataNodeSlotMap().get(name));
					}  else if(isSlotFunction){
						nodes[i].setSlot(-1);
					}
					nodes[i].setStatement(rrsStmt);
				}
				rrs.setNodes(nodes);
			}
			rrs.setStatement(rrsStmt);
			return rrs;
		}else if(schema.getDataNode()!=null){		//默认节点ddl
			RouteResultsetNode[] nodes = new RouteResultsetNode[1];
			nodes[0] = new RouteResultsetNode(schema.getDataNode(), sqlType, stmt);
			nodes[0].setSource(rrs);
			rrs.setNodes(nodes);
			return rrs;
		}
		//both tablename and defaultnode null
		LOGGER.error("table not in schema----"+tablename);
		throw new SQLSyntaxErrorException("op table not in schema----"+tablename);
	}

	private  static String changeCreateTable(SchemaConfig schema,String tableName,String sql) {
		if (schema.getTables().containsKey(tableName)) {
			MySqlStatementParser parser = new MySqlStatementParser(sql);
			SQLStatement insertStatement = parser.parseStatement();
			if (insertStatement instanceof MySqlCreateTableStatement) {
				TableConfig tableConfig = schema.getTables().get(tableName);
				AbstractPartitionAlgorithm algorithm = tableConfig.getRule().getRuleAlgorithm();
				if (algorithm instanceof SlotFunction) {
					SQLColumnDefinition column = new SQLColumnDefinition();
					column.setDataType(new SQLCharacterDataType("int"));
					column.setName(new SQLIdentifierExpr("_slot"));
					column.setComment(new SQLCharExpr("自动迁移算法slot,禁止修改"));
					((SQLCreateTableStatement) insertStatement).getTableElementList().add(column);
					return insertStatement.toString();

				}
			}

		}
		return sql;
	}

	/**
	 * 处理SQL
	 *
	 * @param stmt   执行语句
	 * @return 		 处理后SQL
	 * @author AStoneGod
	 */
	public static String getFixedSql(String stmt){
		stmt = stmt.replaceAll("\r\n", " "); //对于\r\n的字符 用 空格处理 rainbow
		return stmt = stmt.trim(); //.toUpperCase();
	}

	/**
	 * 获取table名字
	 *
	 * @param stmt  	执行语句
	 * @param repPos	开始位置和位数
	 * @return 表名
	 * @author AStoneGod
	 */
	public static String getTableName(String stmt, int[] repPos) {
		int startPos = repPos[0];
		int secInd = stmt.indexOf(' ', startPos + 1);
		if (secInd < 0) {
			secInd = stmt.length();
		}
		int thiInd = stmt.indexOf('(',secInd+1);
		if (thiInd < 0) {
			thiInd = stmt.length();
		}
		repPos[1] = secInd;
		String tableName = "";
		if (stmt.toUpperCase().startsWith("DESC")||stmt.toUpperCase().startsWith("DESCRIBE")){
			tableName = stmt.substring(startPos, thiInd).trim();
		}else {
			tableName = stmt.substring(secInd, thiInd).trim();
		}

		//ALTER TABLE
		if (tableName.contains(" ")){
			tableName = tableName.substring(0,tableName.indexOf(" "));
		}
		int ind2 = tableName.indexOf('.');
		if (ind2 > 0) {
			tableName = tableName.substring(ind2 + 1);
		}
		return tableName;
	}


	/**
	 * 获取show语句table名字
	 *
	 * @param stmt	        执行语句
	 * @param repPos   开始位置和位数
	 * @return 表名
	 * @author AStoneGod
	 */
	public static String getShowTableName(SchemaConfig schema, String stmt, int[] repPos) {
		int startPos = repPos[0];
        startPos = getStartPos(stmt, startPos);
		int secInd = stmt.indexOf(' ', startPos + 1);
		if (secInd < 0) {
			secInd = stmt.length();
		}

		repPos[1] = secInd;
		String tableName = stmt.substring(startPos, secInd).trim();
        if (!schema.isCheckSQLSchema()) {
            return tableName;
        }

        int ind2 = tableName.indexOf('.');
		if (ind2 > 0) {
			tableName = tableName.substring(ind2 + 1);
		}
		return tableName;
	}

	/**
	 * 获取语句中前关键字位置和占位个数表名位置
	 *
	 * @param upStmt     执行语句
	 * @param start      开始位置
	 * @return int[]	  关键字位置和占位个数
	 *
	 * @author mycat
	 *
	 * @modification 修改支持语句中包含“IF NOT EXISTS”的情况
	 * @date 2016/12/8
	 * @modifiedBy Hash Zhang
	 */
	public static int[] getCreateTablePos(String upStmt, int start) {
		String token1 = "CREATE ";
		String token2 = " TABLE ";
		String token3 = " EXISTS ";
		int createInd = upStmt.indexOf(token1, start);
		int tabInd1 = upStmt.indexOf(token2, start);
		int tabInd2 = upStmt.indexOf(token3, tabInd1);
		// 既包含CREATE又包含TABLE,且CREATE关键字在TABLE关键字之前
		if (createInd >= 0 && tabInd2 > 0 && tabInd2 > createInd) {
			return new int[] { tabInd2, token3.length() };
		} else if(createInd >= 0 && tabInd1 > 0 && tabInd1 > createInd) {
			return new int[] { tabInd1, token2.length() };
		} else {
			return new int[] { -1, token2.length() };// 不满足条件时,只关注第一个返回值为-1,第二个任意
		}
	}

	/**
	 * 获取语句中前关键字位置和占位个数表名位置
	 *
	 * @param upStmt
	 *            执行语句
	 * @param start
	 *            开始位置
	 * @return int[]关键字位置和占位个数
	 * @author aStoneGod
	 */
	public static int[] getCreateIndexPos(String upStmt, int start) {
		String token1 = "CREATE ";
		String token2 = " INDEX ";
		String token3 = " ON ";
		int createInd = upStmt.indexOf(token1, start);
		int idxInd = upStmt.indexOf(token2, start);
		int onInd = upStmt.indexOf(token3, start);
		// 既包含CREATE又包含INDEX,且CREATE关键字在INDEX关键字之前, 且包含ON...
		if (createInd >= 0 && idxInd > 0 && idxInd > createInd && onInd > 0 && onInd > idxInd) {
			return new int[] {onInd , token3.length() };
		} else {
			return new int[] { -1, token2.length() };// 不满足条件时,只关注第一个返回值为-1,第二个任意
		}
	}

	/**
	 * 获取ALTER语句中前关键字位置和占位个数表名位置
	 *
	 * @param upStmt   执行语句
	 * @param start    开始位置
	 * @return int[]   关键字位置和占位个数
	 * @author aStoneGod
	 */
	public static int[] getAlterTablePos(String upStmt, int start) {
		String token1 = "ALTER ";
		String token2 = " TABLE ";
		int createInd = upStmt.indexOf(token1, start);
		int tabInd = upStmt.indexOf(token2, start);
		// 既包含CREATE又包含TABLE,且CREATE关键字在TABLE关键字之前
		if (createInd >= 0 && tabInd > 0 && tabInd > createInd) {
			return new int[] { tabInd, token2.length() };
		} else {
			return new int[] { -1, token2.length() };// 不满足条件时,只关注第一个返回值为-1,第二个任意
		}
	}

	/**
	 * 获取DROP语句中前关键字位置和占位个数表名位置
	 *
	 * @param upStmt 	执行语句
	 * @param start  	开始位置
	 * @return int[]	关键字位置和占位个数
	 * @author aStoneGod
	 */
	public static int[] getDropTablePos(String upStmt, int start) {
		//增加 if exists判断
		if(upStmt.contains("EXISTS")){
			String token1 = "IF ";
			String token2 = " EXISTS ";
			int ifInd = upStmt.indexOf(token1, start);
			int tabInd = upStmt.indexOf(token2, start);
			if (ifInd >= 0 && tabInd > 0 && tabInd > ifInd) {
				return new int[] { tabInd, token2.length() };
			} else {
				return new int[] { -1, token2.length() };// 不满足条件时,只关注第一个返回值为-1,第二个任意
			}
		}else {
			String token1 = "DROP ";
			String token2 = " TABLE ";
			int createInd = upStmt.indexOf(token1, start);
			int tabInd = upStmt.indexOf(token2, start);

			if (createInd >= 0 && tabInd > 0 && tabInd > createInd) {
				return new int[] { tabInd, token2.length() };
			} else {
				return new int[] { -1, token2.length() };// 不满足条件时,只关注第一个返回值为-1,第二个任意
			}
		}
	}


	/**
	 * 获取DROP语句中前关键字位置和占位个数表名位置
	 *
	 * @param upStmt
	 *            执行语句
	 * @param start
	 *            开始位置
	 * @return int[]关键字位置和占位个数
	 * @author aStoneGod
	 */

	public static int[] getDropIndexPos(String upStmt, int start) {
		String token1 = "DROP ";
		String token2 = " INDEX ";
		String token3 = " ON ";
		int createInd = upStmt.indexOf(token1, start);
		int idxInd = upStmt.indexOf(token2, start);
		int onInd = upStmt.indexOf(token3, start);
		// 既包含CREATE又包含INDEX,且CREATE关键字在INDEX关键字之前, 且包含ON...
		if (createInd >= 0 && idxInd > 0 && idxInd > createInd && onInd > 0 && onInd > idxInd) {
			return new int[] {onInd , token3.length() };
		} else {
			return new int[] { -1, token2.length() };// 不满足条件时,只关注第一个返回值为-1,第二个任意
		}
	}

	/**
	 * 获取TRUNCATE语句中前关键字位置和占位个数表名位置
	 *
	 * @param upStmt    执行语句
	 * @param start     开始位置
	 * @return int[]	关键字位置和占位个数
	 * @author aStoneGod
	 */
	public static int[] getTruncateTablePos(String upStmt, int start) {
		String token1 = "TRUNCATE ";
		String token2 = " TABLE ";
		int createInd = upStmt.indexOf(token1, start);
		int tabInd = upStmt.indexOf(token2, start);
		// 既包含CREATE又包含TABLE,且CREATE关键字在TABLE关键字之前
		if (createInd >= 0 && tabInd > 0 && tabInd > createInd) {
			return new int[] { tabInd, token2.length() };
		} else {
			return new int[] { -1, token2.length() };// 不满足条件时,只关注第一个返回值为-1,第二个任意
		}
	}

	/**
	 * 获取语句中前关键字位置和占位个数表名位置
	 *
	 * @param upStmt   执行语句
	 * @param start    开始位置
	 * @return int[]   关键字位置和占位个数
	 * @author mycat
	 */
	public static int[] getSpecPos(String upStmt, int start) {
		String token1 = " FROM ";
		String token2 = " IN ";
		int tabInd1 = upStmt.indexOf(token1, start);
		int tabInd2 = upStmt.indexOf(token2, start);
		if (tabInd1 > 0) {
			if (tabInd2 < 0) {
				return new int[] { tabInd1, token1.length() };
			}
			return (tabInd1 < tabInd2) ? new int[] { tabInd1, token1.length() }
					: new int[] { tabInd2, token2.length() };
		} else {
			return new int[] { tabInd2, token2.length() };
		}
	}

	/**
	 * 获取开始位置后的 LIKE、WHERE 位置 如果不含 LIKE、WHERE 则返回执行语句的长度
	 *
	 * @param upStmt   执行sql
	 * @param start    开始位置
	 * @return int
	 * @author mycat
	 */
	public static int getSpecEndPos(String upStmt, int start) {
		int tabInd = upStmt.toUpperCase().indexOf(" LIKE ", start);
		if (tabInd < 0) {
			tabInd = upStmt.toUpperCase().indexOf(" WHERE ", start);
		}
		if (tabInd < 0) {
			return upStmt.length();
		}
		return tabInd;
	}

	public static boolean processWithMycatSeq(SchemaConfig schema, int sqlType,
			String origSQL, ServerConnection sc) {
		// check if origSQL is with global sequence
		// @micmiu it is just a simple judgement
		//对应本地文件配置方式:insert into table1(id,name) values(next value for MYCATSEQ_GLOBAL,‘test’);
		// edit by dingw,增加mycatseq_ 兼容,因为ServerConnection的373行,进行路由计算时,将原始语句全部转换为小写
		if (origSQL.indexOf(" MYCATSEQ_") != -1 || origSQL.indexOf("mycatseq_") != -1) {
			processSQL(sc,schema,origSQL,sqlType);
			return true;
		}
		return false;
	}

	public static void processSQL(ServerConnection sc,SchemaConfig schema,String sql,int sqlType){
//		int sequenceHandlerType = MycatServer.getInstance().getConfig().getSystem().getSequenceHandlerType();
		final SessionSQLPair sessionSQLPair = new SessionSQLPair(sc.getSession2(), schema, sql, sqlType);
//      modify by yanjunli  序列获取修改为多线程方式。使用分段锁方式,一个序列一把锁。  begin
//		MycatServer.getInstance().getSequnceProcessor().addNewSql(sessionSQLPair);
		MycatServer.getInstance().getSequenceExecutor().execute(new Runnable() {
			@Override
			public void run() {
				MycatServer.getInstance().getSequnceProcessor().executeSeq(sessionSQLPair);
			}
		});
//      modify   序列获取修改为多线程方式。使用分段锁方式,一个序列一把锁。  end
//		}
	}

	public static boolean processInsert(SchemaConfig schema, int sqlType,
			String origSQL, ServerConnection sc) throws SQLNonTransientException {
		String tableName = StringUtil.getTableName(origSQL).toUpperCase();
		TableConfig tableConfig = schema.getTables().get(tableName);
		boolean processedInsert=false;
		//判断是有自增字段
		if (null != tableConfig && tableConfig.isAutoIncrement()) {
			String primaryKey = tableConfig.getPrimaryKey();
			processedInsert=processInsert(sc,schema,sqlType,origSQL,tableName,primaryKey);
		}
		return processedInsert;
	}
	/*
	 *  找到返回主键的的位置
	 *  找不到返回 -1
	 * */
	private static int isPKInFields(String origSQL,String primaryKey,int firstLeftBracketIndex,int firstRightBracketIndex){

		if (primaryKey == null) {
			throw new RuntimeException("please make sure the primaryKey's config is not null in schemal.xml");
		}

		boolean isPrimaryKeyInFields = false;
		int  pkStart = 0;
		String upperSQL = origSQL.substring(firstLeftBracketIndex, firstRightBracketIndex + 1).toUpperCase();
		for (int pkOffset = 0, primaryKeyLength = primaryKey.length();;) {
			pkStart = upperSQL.indexOf(primaryKey, pkOffset);
			if (pkStart >= 0 && pkStart < firstRightBracketIndex) {
				char pkSide = upperSQL.charAt(pkStart - 1);
				if (pkSide <= ' ' || pkSide == '`' || pkSide == ',' || pkSide == '(') {
					pkSide = upperSQL.charAt(pkStart + primaryKey.length());
					isPrimaryKeyInFields = pkSide <= ' ' || pkSide == '`' || pkSide == ',' || pkSide == ')';
				}
				if (isPrimaryKeyInFields) {
					break;
				}
				pkOffset = pkStart + primaryKeyLength;
			} else {
				break;
			}
		}
		if (isPrimaryKeyInFields) {
			return firstLeftBracketIndex + pkStart;
		} else {
			return  -1;
		}

	}

	public static boolean processInsert(ServerConnection sc,SchemaConfig schema,
			int sqlType,String origSQL,String tableName,String primaryKey) throws SQLNonTransientException {

		int firstLeftBracketIndex = origSQL.indexOf("(");
		int firstRightBracketIndex = origSQL.indexOf(")");
		String upperSql = origSQL.toUpperCase();
		int valuesIndex = upperSql.indexOf("VALUES");
		int selectIndex = upperSql.indexOf("SELECT");
		int fromIndex = upperSql.indexOf("FROM");
		//屏蔽insert into table1 select * from table2语句
		if(firstLeftBracketIndex < 0) {
			String msg = "invalid sql:" + origSQL;
			LOGGER.warn(msg);
			throw new SQLNonTransientException(msg);
		}
		//屏蔽批量插入
		if(selectIndex > 0 &&fromIndex>0&&selectIndex>firstRightBracketIndex&&valuesIndex<0) {
			String msg = "multi insert not provided" ;
			LOGGER.warn(msg);
			throw new SQLNonTransientException(msg);
		}
		//插入语句必须提供列结构,因为MyCat默认对于表结构无感知
		if(valuesIndex + "VALUES".length() <= firstLeftBracketIndex) {
			throw new SQLSyntaxErrorException("insert must provide ColumnList");
		}
		Object[] vauleArrayAndSuffixStr = parseSqlValueArrayAndSuffixStr(origSQL , valuesIndex);
		List<List<String>> vauleArray = (List<List<String>>) vauleArrayAndSuffixStr[0];
		String suffixStr = null;
		if (vauleArrayAndSuffixStr.length > 1) {
			suffixStr = (String) vauleArrayAndSuffixStr[1];
		}
		//两种情况处理 1 有主键的 id ,但是值为null 进行改下
		//            2 没有主键的 需要插入 进行改写

		//如果主键不在插入语句的fields中,则需要进一步处理
		boolean processedInsert= false;
		int pkStart = isPKInFields(origSQL,primaryKey,firstLeftBracketIndex,firstRightBracketIndex);


		if(pkStart == -1){
			processedInsert = true;
			handleBatchInsert(sc, schema, sqlType,origSQL, valuesIndex, tableName, primaryKey, vauleArray, suffixStr);
		} else {
			//判断 主键id的值是否为null
			if(pkStart != -1) {
				String subPrefix = origSQL.substring(0, pkStart);
				char c;
				int pkIndex = 0;
				for(int index = 0, len = subPrefix.length(); index < len; index++) {
					c = subPrefix.charAt(index);
					if(c == ',') {
						pkIndex ++;
					}
				}
				processedInsert  = handleBatchInsertWithPK(sc, schema, sqlType,origSQL, valuesIndex, tableName, primaryKey, vauleArray, suffixStr, pkIndex);
			}
		}
		return processedInsert;
	}

	private static boolean handleBatchInsertWithPK(ServerConnection sc, SchemaConfig schema, int sqlType,
			String origSQL, int valuesIndex, String tableName, String primaryKey, List<List<String>> vauleList,
			String suffixStr, int pkIndex) {
		boolean processedInsert = false;
//	  	final String pk = "\\("+primaryKey+",";
		final String mycatSeqPrefix = "next value for MYCATSEQ_"+tableName.toUpperCase() ;

		/*"VALUES".length() ==6 */
		String prefix = origSQL.substring(0, valuesIndex + 6);
//

		StringBuilder sb = new StringBuilder("");
		for(List<String> list : vauleList) {
			sb.append("(");
			String pkValue = list.get(pkIndex).trim().toLowerCase();
			//null值替换为 next value for MYCATSEQ_tableName
			if("null".equals(pkValue.trim())) {
				list.set(pkIndex, mycatSeqPrefix);
				processedInsert = true;
			}
			for(String val : list) {
				sb.append(val).append(",");
			}
			sb.setCharAt(sb.length() - 1, ')');
			sb.append(",");
		}
		sb.setCharAt(sb.length() - 1, ' ');
		if (suffixStr != null) {
			sb.append(suffixStr);
		}
		if(processedInsert) {
			processSQL(sc, schema,prefix+sb.toString(), sqlType);
		}
		return processedInsert;
	}

	public static List<String> handleBatchInsert(String origSQL, int valuesIndex) {
		List<String> handledSQLs = new LinkedList<>();
		String prefix = origSQL.substring(0, valuesIndex + "VALUES".length());
		String values = origSQL.substring(valuesIndex + "VALUES".length());
		int flag = 0;
		StringBuilder currentValue = new StringBuilder();
		currentValue.append(prefix);
		for (int i = 0; i < values.length(); i++) {
			char j = values.charAt(i);
			if (j == '(' && flag == 0) {
				flag = 1;
				currentValue.append(j);
			} else if (j == '\"' && flag == 1) {
				flag = 2;
				currentValue.append(j);
			} else if (j == '\'' && flag == 1) {
				flag = 3;
				currentValue.append(j);
			} else if (j == '\\' && flag == 2) {
				flag = 4;
				currentValue.append(j);
			} else if (j == '\\' && flag == 3) {
				flag = 5;
				currentValue.append(j);
			} else if (flag == 4) {
				flag = 2;
				currentValue.append(j);
			} else if (flag == 5) {
				flag = 3;
				currentValue.append(j);
			} else if (j == '\"' && flag == 2) {
				flag = 1;
				currentValue.append(j);
			} else if (j == '\'' && flag == 3) {
				flag = 1;
				currentValue.append(j);
			} else if (j == ')' && flag == 1) {
				flag = 0;
				currentValue.append(j);
				handledSQLs.add(currentValue.toString());
				currentValue = new StringBuilder();
				currentValue.append(prefix);
			} else if (j == ',' && flag == 0) {
				continue;
			} else {
				currentValue.append(j);
			}
		}
		return handledSQLs;
	}
	/**
	 * 对于插入的sql : "insert into hotnews(title,name) values('test1',\"name\"),('(test)',\"(test)\"),('\\\"',\"\\'\"),(\")\",\"\\\"\\')\")":
	 *  需要返回结果:
	 *[[ 'test1', "name"],
	 *	['(test)', "(test)"],
	 *	['\"', "\'"],
	 *	[")", "\"\')"],
	 *	[ 1,  null]
	 * 值结果的解析
	 */
	public static Object[] parseSqlValueArrayAndSuffixStr(String origSQL, int valuesIndex) {
		List<List<String>> valueArray = new ArrayList<>();
		String valuesAndSuffixStr = origSQL.substring(valuesIndex + 6);// 6 values 长度为6
		int pos = 0 ;
		int flag  = 4;
		int len = valuesAndSuffixStr.length();
		StringBuilder currentValue = new StringBuilder();
//        int colNum = 2; //
		char c ;
		List<String> curList = new ArrayList<>();
		int parenCount = 0;
		for( ;pos < len; pos ++) {
			c = valuesAndSuffixStr.charAt(pos);
			if (flag == 1  || flag == 2) {
				currentValue.append(c);
				if (c == '\\') {
					char nextCode = valuesAndSuffixStr.charAt(pos + 1);
					if (nextCode == '\'' || nextCode == '\"') {
						currentValue.append(nextCode);
						pos++;
						continue;
					}
				}
				if (c == '\"' && flag == 1) {
					flag = 0;
					continue;
				}
				if (c == '\'' && flag == 2) {
					flag = 0;
					continue;
				}
			} else if (flag == 5) {
				currentValue.append(c);
				if (c == '(') {
					parenCount++;
				} else if (c == ')') {
					parenCount--;
				}
				if (parenCount == 0) {
					flag = 0;
				}
			} else if (c == '\"'){
				currentValue.append(c);
				flag = 1;
			} else if (c == '\'') {
				currentValue.append(c);
				flag = 2;
			} else if (c == '(') {
				if (flag == 4) {
					curList = new ArrayList<>();
					flag = 0;
				} else {
					currentValue.append(c);
					flag = 5;
					parenCount++;
				}
			} else if (flag == 4) {
				if (c == 'o' || c == 'O') {
					String suffixStr = valuesAndSuffixStr.substring(pos);
					return new Object[]{valueArray, suffixStr};
				}
				continue;
			} else if (c == ',') {
//                System.out.println(currentValue);
				curList.add(currentValue.toString());
				currentValue.delete(0, currentValue.length());
			} else if (c == ')'){
				flag = 4;
//                System.out.println(currentValue);
				curList.add(currentValue.toString());
				currentValue.delete(0, currentValue.length());
				valueArray.add(curList);
			}  else {
				currentValue.append(c);
			}
		}
		return new Object[]{valueArray};
	}
	/**
	 * 对于主键不在插入语句的fields中的SQL,需要改写。比如hotnews主键为id,插入语句为:
	 * insert into hotnews(title) values('aaa');
	 * 需要改写成:
	 * insert into hotnews(id, title) values(next value for MYCATSEQ_hotnews,'aaa');
	 */
	public static void handleBatchInsert(ServerConnection sc, SchemaConfig schema,
			int sqlType,String origSQL, int valuesIndex,String tableName, String primaryKey , List<List<String>> vauleList, String suffixStr) {

		final String pk = "\\("+primaryKey+",";
		final String mycatSeqPrefix = "(next value for MYCATSEQ_"+tableName.toUpperCase()+"";

		/*"VALUES".length() ==6 */
		String prefix = origSQL.substring(0, valuesIndex + 6);
//
		prefix = prefix.replaceFirst("\\(", pk);

		StringBuilder sb = new StringBuilder("");
		for(List<String> list : vauleList) {
			sb.append(mycatSeqPrefix);
			for(String val : list) {
				sb.append(",").append(val);
			}
			sb.append("),");
		}
		sb.setCharAt(sb.length() - 1, ' ');
		if (suffixStr != null) {
			sb.append(suffixStr);
		}
		processSQL(sc, schema,prefix+sb.toString(), sqlType);
	}
//	  /**
//	  * 对于主键不在插入语句的fields中的SQL,需要改写。比如hotnews主键为id,插入语句为:
//	  * insert into hotnews(title) values('aaa');
//	  * 需要改写成:
//	  * insert into hotnews(id, title) values(next value for MYCATSEQ_hotnews,'aaa');
//	  */
//    public static void handleBatchInsert(ServerConnection sc, SchemaConfig schema,
//            int sqlType,String origSQL, int valuesIndex,String tableName, String primaryKey) {
//
//    	final String pk = "\\("+primaryKey+",";
//        final String mycatSeqPrefix = "(next value for MYCATSEQ_"+tableName.toUpperCase()+",";
//
//    	/*"VALUES".length() ==6 */
//        String prefix = origSQL.substring(0, valuesIndex + 6);
//        String values = origSQL.substring(valuesIndex + 6);
//
//        prefix = prefix.replaceFirst("\\(", pk);
//        values = values.replaceFirst("\\(", mycatSeqPrefix);
//        values =Pattern.compile(",\\s*\\(").matcher(values).replaceAll(","+mycatSeqPrefix);
//        processSQL(sc, schema,prefix+values, sqlType);
//    }


	public static RouteResultset routeToMultiNode(boolean cache,RouteResultset rrs, Collection<String> dataNodes, String stmt) {
		RouteResultsetNode[] nodes = new RouteResultsetNode[dataNodes.size()];
		int i = 0;
		RouteResultsetNode node;
		for (String dataNode : dataNodes) {
			node = new RouteResultsetNode(dataNode, rrs.getSqlType(), stmt);
			node.setSource(rrs);
			if(rrs.getDataNodeSlotMap().containsKey(dataNode)){
				node.setSlot(rrs.getDataNodeSlotMap().get(dataNode));
			}
			if (rrs.getCanRunInReadDB() != null) {
				node.setCanRunInReadDB(rrs.getCanRunInReadDB());
			}
			if(rrs.getRunOnSlave() != null){
				nodes[0].setRunOnSlave(rrs.getRunOnSlave());
			}
			nodes[i++] = node;
		}
		rrs.setCacheAble(cache);
		rrs.setNodes(nodes);
		return rrs;
	}

	public static RouteResultset routeToMultiNode(boolean cache, RouteResultset rrs, Collection<String> dataNodes,
			String stmt, boolean isGlobalTable) {

		rrs = routeToMultiNode(cache, rrs, dataNodes, stmt);
		rrs.setGlobalTable(isGlobalTable);
		return rrs;
	}

	public static void routeForTableMeta(RouteResultset rrs,
			SchemaConfig schema, String tableName, String sql) {
		String dataNode = null;
		if (isNoSharding(schema,tableName)) {//不分库的直接从schema中获取dataNode
			dataNode = schema.getDataNode();
		} else {
			dataNode = getMetaReadDataNode(schema, tableName);
		}

		RouteResultsetNode[] nodes = new RouteResultsetNode[1];
		nodes[0] = new RouteResultsetNode(dataNode, rrs.getSqlType(), sql);
		nodes[0].setSource(rrs);
		if(rrs.getDataNodeSlotMap().containsKey(dataNode)){
			nodes[0].setSlot(rrs.getDataNodeSlotMap().get(dataNode));
		}
		if (rrs.getCanRunInReadDB() != null) {
			nodes[0].setCanRunInReadDB(rrs.getCanRunInReadDB());
		}
		if(rrs.getRunOnSlave() != null){
			nodes[0].setRunOnSlave(rrs.getRunOnSlave());
		}
		rrs.setNodes(nodes);
	}

	/**
	 * 根据表名随机获取一个节点
	 *
	 * @param schema     数据库名
	 * @param table      表名
	 * @return 			  数据节点
	 * @author mycat
	 */
	private static String getMetaReadDataNode(SchemaConfig schema,
			String table) {
		// Table名字被转化为大写的,存储在schema
		table = table.toUpperCase();
		String dataNode = null;
		Map<String, TableConfig> tables = schema.getTables();
		TableConfig tc;
		if (tables != null && (tc = tables.get(table)) != null) {
			dataNode = getAliveRandomDataNode(tc);
		}
		return dataNode;
	}

	/**
	 * 解决getRandomDataNode方法获取错误节点的问题.
	 * @param tc
	 * @return
	 */
	public static String getAliveRandomDataNode(TableConfig tc) {
		List<String> randomDns = (List<String>)tc.getDataNodes().clone();

		MycatConfig mycatConfig = MycatServer.getInstance().getConfig();
		if (mycatConfig != null) {
			Collections.shuffle(randomDns);
			for (String randomDn : randomDns) {
				PhysicalDBNode physicalDBNode = mycatConfig.getDataNodes().get(randomDn);
				if (physicalDBNode != null) {
					if (physicalDBNode.getDbPool().getSource().isAlive()) {
						for (PhysicalDBPool pool : MycatServer.getInstance().getConfig().getDataHosts().values()) {
							PhysicalDatasource source = pool.getSource();
							if (source.getHostConfig().containDataNode(randomDn) && pool.getSource().isAlive()) {
								return randomDn;
							}
						}
					}
				}
			}
		}

		// all fail return default
		return tc.getRandomDataNode();
	}

	@Deprecated
	private static String getRandomDataNode(TableConfig tc) {
		//写节点不可用,意味着读节点也不可用。
		//直接使用下一个 dataHost
		String randomDn = tc.getRandomDataNode();
		MycatConfig mycatConfig = MycatServer.getInstance().getConfig();
		if (mycatConfig != null) {
			PhysicalDBNode physicalDBNode = mycatConfig.getDataNodes().get(randomDn);
			if (physicalDBNode != null) {
				if (physicalDBNode.getDbPool().getSource().isAlive()) {
					for (PhysicalDBPool pool : MycatServer.getInstance()
							.getConfig()
							.getDataHosts()
							.values()) {
						if (pool.getSource().getHostConfig().containDataNode(randomDn)) {
							continue;
						}

						if (pool.getSource().isAlive()) {
							return pool.getSource().getHostConfig().getRandomDataNode();
						}
					}
				}
			}
		}

		//all fail return default
		return randomDn;
	}

	/**
	 * 根据 ER分片规则获取路由集合
	 *
	 * @param stmt            执行的语句
	 * @param rrs      		     数据路由集合
	 * @param tc	      	     表实体
	 * @param joinKeyVal      连接属性
	 * @return RouteResultset(数据路由集合)	 *
	 * @throws SQLNonTransientException,IllegalShardingColumnValueException
	 * @author mycat
	 */

	public static RouteResultset routeByERParentKey(ServerConnection sc,SchemaConfig schema,
			int sqlType,String stmt,
			RouteResultset rrs, TableConfig tc, String joinKeyVal)
			throws SQLNonTransientException {

		// only has one parent level and ER parent key is parent
		// table's partition key
		if (tc.isSecondLevel()
				//判断是否为二级子表(父表不再有父表)
				&& tc.getParentTC().getPartitionColumn()
				.equals(tc.getParentKey())) { // using
			// parent
			// rule to
			// find
			// datanode
			Set<ColumnRoutePair> parentColVal = new HashSet<ColumnRoutePair>(1);
			ColumnRoutePair pair = new ColumnRoutePair(joinKeyVal);
			parentColVal.add(pair);
			Set<String> dataNodeSet = ruleCalculate(tc.getParentTC(), parentColVal,rrs.getDataNodeSlotMap());
			if (dataNodeSet.isEmpty() || dataNodeSet.size() > 1) {
				throw new SQLNonTransientException(
						"parent key can't find  valid datanode ,expect 1 but found: "
								+ dataNodeSet.size());
			}
			String dn = dataNodeSet.iterator().next();
			if (LOGGER.isDebugEnabled()) {
				LOGGER.debug("found partion node (using parent partion rule directly) for child table to insert  "
						+ dn + " sql :" + stmt);
			}
			return RouterUtil.routeToSingleNode(rrs, dn, stmt);
		}
		return null;
	}

	/**
	 * @return dataNodeIndex -&gt; [partitionKeysValueTuple+]
	 */
	public static Set<String> ruleByJoinValueCalculate(RouteResultset rrs, TableConfig tc,
			Set<ColumnRoutePair> colRoutePairSet) throws SQLNonTransientException {

		String joinValue = "";

		if(colRoutePairSet.size() > 1) {
			LOGGER.warn("joinKey can't have multi Value");
		} else {
			Iterator<ColumnRoutePair> it = colRoutePairSet.iterator();
			ColumnRoutePair joinCol = it.next();
			joinValue = joinCol.colValue;
		}

		Set<String> retNodeSet = new LinkedHashSet<String>();

		Set<String> nodeSet;
		if (tc.isSecondLevel()
				&& tc.getParentTC().getPartitionColumn()
				.equals(tc.getParentKey())) { // using
			// parent
			// rule to
			// find
			// datanode

			nodeSet = ruleCalculate(tc.getParentTC(),colRoutePairSet,rrs.getDataNodeSlotMap());
			if (nodeSet.isEmpty()) {
				throw new SQLNonTransientException(
						"parent key can't find  valid datanode ,expect 1 but found: "
								+ nodeSet.size());
			}
			if (LOGGER.isDebugEnabled()) {
				LOGGER.debug("found partion node (using parent partion rule directly) for child table to insert  "
						+ nodeSet + " sql :" + rrs.getStatement());
			}
			retNodeSet.addAll(nodeSet);

//			for(ColumnRoutePair pair : colRoutePairSet) {
//				nodeSet = ruleCalculate(tc.getParentTC(),colRoutePairSet);
//				if (nodeSet.isEmpty() || nodeSet.size() > 1) {//an exception would be thrown, if sql was executed on more than on sharding
//					throw new SQLNonTransientException(
//							"parent key can't find  valid datanode ,expect 1 but found: "
//									+ nodeSet.size());
//				}
//				String dn = nodeSet.iterator().next();
//				if (LOGGER.isDebugEnabled()) {
//					LOGGER.debug("found partion node (using parent partion rule directly) for child table to insert  "
//							+ dn + " sql :" + rrs.getStatement());
//				}
//				retNodeSet.addAll(nodeSet);
//			}
			return retNodeSet;
		} else {
			retNodeSet.addAll(tc.getParentTC().getDataNodes());
		}

		return retNodeSet;
	}


	/**
	 * @return dataNodeIndex -&gt; [partitionKeysValueTuple+]
	 */
	public static Set<String> ruleCalculate(TableConfig tc,
			Set<ColumnRoutePair> colRoutePairSet,Map<String,Integer>   dataNodeSlotMap)  {
		Set<String> routeNodeSet = new LinkedHashSet<String>();
		String col = tc.getRule().getColumn();
		RuleConfig rule = tc.getRule();
		AbstractPartitionAlgorithm algorithm = rule.getRuleAlgorithm();
		for (ColumnRoutePair colPair : colRoutePairSet) {
			if (colPair.colValue != null) {
				Integer nodeIndx = algorithm.calculate(StringUtil.removeBackquote(colPair.colValue));
				if (nodeIndx == null) {
					throw new IllegalArgumentException(
							"can't find datanode for sharding column:" + col
									+ " val:" + colPair.colValue);
				} else {
					String dataNode = tc.getDataNodes().get(nodeIndx);
					routeNodeSet.add(dataNode);
					if(algorithm instanceof SlotFunction) {
						dataNodeSlotMap.put(dataNode,((SlotFunction) algorithm).slotValue());
					}
					colPair.setNodeId(nodeIndx);
				}
			} else if (colPair.rangeValue != null) {
				Integer[] nodeRange = algorithm.calculateRange(
						String.valueOf(colPair.rangeValue.beginValue),
						String.valueOf(colPair.rangeValue.endValue));
				if (nodeRange != null) {
					/**
					 * 不能确认 colPair的 nodeid是否会有其它影响
					 */
					if (nodeRange.length == 0) {
						routeNodeSet.addAll(tc.getDataNodes());
					} else {
						ArrayList<String> dataNodes = tc.getDataNodes();
						String dataNode = null;
						for (Integer nodeId : nodeRange) {
							dataNode = dataNodes.get(nodeId);
							if(algorithm instanceof SlotFunction) {
								dataNodeSlotMap.put(dataNode,((SlotFunction) algorithm).slotValue());
							}
							routeNodeSet.add(dataNode);
						}
					}
				}
			}

		}
		return routeNodeSet;
	}

	/**
	 * 多表路由
	 */
	public static RouteResultset tryRouteForTables(SchemaConfig schema, DruidShardingParseInfo ctx,
			RouteCalculateUnit routeUnit, RouteResultset rrs, boolean isSelect, LayerCachePool cachePool)
			throws SQLNonTransientException {

		List<String> tables = ctx.getTables();


		if(schema.isNoSharding()||(tables.size() >= 1&&isNoSharding(schema,tables.get(0)))) {
			return routeToSingleNode(rrs, schema.getDataNode(), ctx.getSql());
		}

		//每个表对应的路由映射
		Map<String,Set<String>> tablesRouteMap = new HashMap<String,Set<String>>();

		//为全局表和单库表找路由
		for(String tableName : tables) {

			TableConfig tableConfig = schema.getTables().get(tableName.toUpperCase());

			if(tableConfig == null) {
				//add 如果表读取不到则先将表名从别名中读取转化后再读取
				String alias = ctx.getTableAliasMap().get(tableName);
				if(!StringUtil.isEmpty(alias)){
					tableConfig = schema.getTables().get(alias.toUpperCase());
				}

				if(tableConfig == null){
					String msg = "can't find table define in schema "+ tableName + " schema:" + schema.getName();
					LOGGER.warn(msg);
					throw new SQLNonTransientException(msg);
				}

			}
			if(tableConfig.isGlobalTable()) {//全局表
				if(tablesRouteMap.get(tableName) == null) {
					tablesRouteMap.put(tableName, new HashSet<String>());
				}
				tablesRouteMap.get(tableName).addAll(tableConfig.getDataNodes());
			} else if(tablesRouteMap.get(tableName) == null) { //余下的表都是单库表
				tablesRouteMap.put(tableName, new HashSet<String>());
				tablesRouteMap.get(tableName).addAll(tableConfig.getDataNodes());
			}

			if(tableConfig.getDistTables().size() > 0) {
				Map<String, List<String>> subTablesmap = rrs.getSubTableMaps();
				if (subTablesmap == null) {
					subTablesmap = Maps.newHashMap();
					rrs.setSubTableMaps(subTablesmap);
				}

				subTablesmap.put(tableName.toUpperCase(), tableConfig.getDistTables());
			}
		}


		//只有一个表的
		if(tables.size() == 1) {
			return RouterUtil.tryRouteForOneTable(schema, ctx, routeUnit, tables.get(0), rrs, isSelect, cachePool);
		}

		Set<String> retNodesSet = new HashSet<String>();

		//分库解析信息不为空
		Map<String, Map<String, Set<ColumnRoutePair>>> tablesAndConditions = routeUnit.getTablesAndConditions();
		if(tablesAndConditions != null && tablesAndConditions.size() > 0) {
			//为分库表找路由
			RouterUtil.findRouteWithcConditionsForTables(schema, rrs, tablesAndConditions, tablesRouteMap, ctx.getSql(), cachePool, isSelect);
			if(rrs.isFinishedRoute()) {
				return rrs;
			}
		}


		boolean isFirstAdd = true;
		for(Map.Entry<String, Set<String>> entry : tablesRouteMap.entrySet()) {
			if(entry.getValue() == null || entry.getValue().size() == 0) {
				throw new SQLNonTransientException("parent key can't find any valid datanode ");
			} else {
				if(isFirstAdd) {
					retNodesSet.addAll(entry.getValue());
					isFirstAdd = false;
				} else {
					retNodesSet.retainAll(entry.getValue());
					if(retNodesSet.size() == 0) {//两个表的路由无交集
						String errMsg = "invalid route in sql, multi tables found but datanode has no intersection "
								+ " sql:" + ctx.getSql();
						LOGGER.warn(errMsg);
						throw new SQLNonTransientException(errMsg);
					}
				}
			}
		}

		if(retNodesSet != null && retNodesSet.size() > 0) {
			String tableName = tables.get(0);
			TableConfig tableConfig = schema.getTables().get(tableName.toUpperCase());
			if(tableConfig.isDistTable()){
			    routeToDistTableNode(schema, rrs, ctx.getSql(), tablesAndConditions, cachePool, isSelect, null);
				return rrs;
			}

			if(retNodesSet.size() > 1 && isAllGlobalTable(ctx, schema)) {
				// mulit routes ,not cache route result
				if (isSelect) {
					rrs.setCacheAble(false);
					ArrayList<String> retNodeList = new ArrayList<String>(retNodesSet);
					Collections.shuffle(retNodeList);//by kaiz : add shuffle
					routeToSingleNode(rrs, retNodeList.get(0), ctx.getSql());
				}
				else {//delete 删除全局表的记录
					routeToMultiNode(isSelect, rrs, retNodesSet, ctx.getSql(),true);
				}

			} else {
				routeToMultiNode(isSelect, rrs, retNodesSet, ctx.getSql());
			}

		}
		return rrs;

	}


	/**
	 *
	 * 单表路由
	 */
	public static RouteResultset tryRouteForOneTable(SchemaConfig schema, DruidShardingParseInfo ctx,
			RouteCalculateUnit routeUnit, String tableName, RouteResultset rrs, boolean isSelect,
			LayerCachePool cachePool) throws SQLNonTransientException {

		if (isNoSharding(schema, tableName)) {
			return routeToSingleNode(rrs, schema.getDataNode(), ctx.getSql());
		}

		TableConfig tc = schema.getTables().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);
		}

		Map<String, Map<String, Set<ColumnRoutePair>>> tablesAndConditions = routeUnit.getTablesAndConditions();
        if(tc.isDistTable()){
            Set<String> keySet = tablesAndConditions.keySet();
            //Map.Entry<String, Map<String, Set<ColumnRoutePair>>> entry = (Entry<String, Map<String, Set<ColumnRoutePair>>>) tablesAndConditions.get(keySet.toArray()[0]);
            return routeToDistTableNode(schema,rrs,ctx.getSql(), tablesAndConditions, cachePool,isSelect, null);
        }

		if(tc.isGlobalTable()) {//全局表
			if(isSelect) {
				// global select ,not cache route result
				rrs.setCacheAble(false);
				return routeToSingleNode(rrs, getAliveRandomDataNode(tc)/*getRandomDataNode(tc)*/, ctx.getSql());
			} else {//insert into 全局表的记录
				return routeToMultiNode(false, rrs, tc.getDataNodes(), ctx.getSql(),true);
			}
		} else {//单表或者分库表
			if (!checkRuleRequired(schema, ctx, routeUnit, tc)) {
				throw new IllegalArgumentException("route rule for table "
						+ tc.getName() + " is required: " + ctx.getSql());

			}
			if(tc.getPartitionColumn() == null && !tc.isSecondLevel()) {//单表且不是childTable
//				return RouterUtil.routeToSingleNode(rrs, tc.getDataNodes().get(0),ctx.getSql());
				return routeToMultiNode(rrs.isCacheAble(), rrs, tc.getDataNodes(), ctx.getSql());
			} else {
				//每个表对应的路由映射
				Map<String,Set<String>> tablesRouteMap = new HashMap<String,Set<String>>();
				if(routeUnit.getTablesAndConditions() != null && routeUnit.getTablesAndConditions().size() > 0) {
					RouterUtil.findRouteWithcConditionsForTables(schema, rrs, routeUnit.getTablesAndConditions(), tablesRouteMap, ctx.getSql(), cachePool, isSelect);
					if(rrs.isFinishedRoute()) {
						return rrs;
					}
				}

				if(tablesRouteMap.get(tableName) == null) {
					return routeToMultiNode(rrs.isCacheAble(), rrs, tc.getDataNodes(), ctx.getSql());
				} else {
					return routeToMultiNode(rrs.isCacheAble(), rrs, tablesRouteMap.get(tableName), ctx.getSql());
				}
			}
		}
	}

	private static RouteResultset routeToDistTableNode(SchemaConfig schema, RouteResultset rrs,
        String orgSql, Map<String, Map<String, Set<ColumnRoutePair>>> tablesAndConditions,
        LayerCachePool cachePool, boolean isSelect, Map.Entry<String, Map<String, Set<ColumnRoutePair>>> entry) throws SQLNonTransientException {


        String tableName = null;
        if (entry != null) {
            tableName = entry.getKey().toUpperCase();
        } else {
            List<String> tables = rrs.getTables();
            tableName = tables.get(0);
        }
        TableConfig tableConfig = schema.getTables().get(tableName);
        if(tableConfig == null) {
            String msg = "can't find table define in schema " + tableName + " schema:" + schema.getName();
            LOGGER.warn(msg);
            throw new SQLNonTransientException(msg);
        }
        if(tableConfig.isGlobalTable()){
            String msg = "can't suport district table  " + tableName + " schema:" + schema.getName() + " for global table ";
            LOGGER.warn(msg);
            throw new SQLNonTransientException(msg);
        }
        String partionCol = tableConfig.getPartitionColumn();
    //  String primaryKey = tableConfig.getPrimaryKey();
        //boolean isLoadData=false;
    
        Set<String> tablesRouteSet = new HashSet<String>();
    
        List<String> dataNodes = tableConfig.getDataNodes();
        if(dataNodes.size()>1){
            String msg = "can't suport district table  " + tableName + " schema:" + schema.getName() + " for mutiple dataNode " + dataNodes;
            LOGGER.warn(msg);
            throw new SQLNonTransientException(msg);
        }
        String dataNode = dataNodes.get(0);
    
        RouteResultsetNode[] nodes = null;
        //主键查找缓存暂时不实现
        if(tablesAndConditions.isEmpty()){
            List<String> subTables = tableConfig.getDistTables();
            tablesRouteSet.addAll(subTables);
    
            nodes = getNode(rrs, orgSql, tablesRouteSet, dataNode, false, tableName);
        } else {
            if (entry == null) {
                for(Map.Entry<String, Map<String, Set<ColumnRoutePair>>> entry1 : tablesAndConditions.entrySet()) {
                    //boolean isFoundPartitionValue = partionCol != null && entry.getValue().get(partionCol) != null;
                    setNodes(rrs, tableConfig, partionCol, tablesRouteSet, entry1);
                }
            } else {
                setNodes(rrs, tableConfig, partionCol, tablesRouteSet, entry);
            }
    
    
            nodes = getNode(rrs, orgSql, tablesRouteSet, dataNode, true, tableName);
        }
        rrs.setNodes(nodes);
        rrs.setSubTables(tablesRouteSet);
        rrs.setFinishedRoute(true);
    
        return rrs;
    }
	
	private static void setNodes(RouteResultset rrs, TableConfig tableConfig, String partionCol,
        Set<String> tablesRouteSet, Map.Entry<String, Map<String, Set<ColumnRoutePair>>> entry1)
        throws SQLNonTransientException {
        Map<String, Set<ColumnRoutePair>> columnsMap = entry1.getValue();
    
        Set<ColumnRoutePair> partitionValue = columnsMap.get(partionCol);
        if(partitionValue == null || partitionValue.size() == 0) {
            tablesRouteSet.addAll(tableConfig.getDistTables());
        } else {
            for(ColumnRoutePair pair : partitionValue) {
                AbstractPartitionAlgorithm algorithm = tableConfig.getRule().getRuleAlgorithm();
                if(pair.colValue != null) {
                    Integer tableIndex = algorithm.calculate(pair.colValue);
                    if(tableIndex == null) {
                        String msg = "can't find any valid datanode :" + tableConfig.getName()
                                + " -> " + tableConfig.getPartitionColumn() + " -> " + pair.colValue;
                        LOGGER.warn(msg);
                        throw new SQLNonTransientException(msg);
                    }
                    String subTable = tableConfig.getDistTables().get(tableIndex);
                    if(subTable != null) {
                        tablesRouteSet.add(subTable);
                        if(algorithm instanceof SlotFunction){
                            rrs.getDataNodeSlotMap().put(subTable,((SlotFunction) algorithm).slotValue());
                        }
                    }
                }
                if(pair.rangeValue != null) {
                    Integer[] tableIndexs = algorithm
                            .calculateRange(pair.rangeValue.beginValue.toString(), pair.rangeValue.endValue.toString());
                    for(Integer idx : tableIndexs) {
                        String subTable = tableConfig.getDistTables().get(idx);
                        if(subTable != null) {
                            tablesRouteSet.add(subTable);
                            if(algorithm instanceof SlotFunction){
                                rrs.getDataNodeSlotMap().put(subTable,((SlotFunction) algorithm).slotValue());
                            }
                        }
                    }
                }
            }
        }
    }

	private static RouteResultsetNode[] getNode(RouteResultset rrs, String orgSql, Set<String> tablesRouteSet,
			String dataNode, boolean is, String tableName) {
		Object[] subTables =  tablesRouteSet.toArray();
		RouteResultsetNode[] nodes = new RouteResultsetNode[subTables.length];
		Map<String,Integer> dataNodeSlotMap= rrs.getDataNodeSlotMap();
		for(int i=0;i<nodes.length;i++){
			String table = String.valueOf(subTables[i]);
			String changeSql = orgSql;
			nodes[i] = new RouteResultsetNode(dataNode, rrs.getSqlType(), changeSql);//rrs.getStatement()
			nodes[i].setSubTableName(table);

			if (is) {
				Map<String, List<String>> subTableMaps = rrs.getSubTableMaps();
				if(subTableMaps != null) {
					List<String> list = subTableMaps.get(tableName);
					int index = 0;
					for (String subTable : list) {
						if (table.equals(subTable)) {
							break;
						}
						index++;
					}
					for (String tableSource : subTableMaps.keySet()) {
						Map<String, String> subTableNames = nodes[i].getSubTableNames();
						if (subTableNames == null) {
							subTableNames = Maps.newHashMap();
							nodes[i].setSubTableNames(subTableNames);
						}
						if (tableSource.equals(tableName)) {
							subTableNames.put(tableSource, table);
						} else {
							subTableNames.put(tableSource, subTableMaps.get(tableSource).get(index));
						}

					}
				}
			} else {
				Map<String, List<String>> subTableMaps = rrs.getSubTableMaps();
				if(subTableMaps != null) {
					for (String tableSource : subTableMaps.keySet()) {
						Map<String, String> subTableNames = nodes[i].getSubTableNames();
						if (subTableNames == null) {
							subTableNames = Maps.newHashMap();
							nodes[i].setSubTableNames(subTableNames);
						}
						subTableNames.put(tableSource, subTableMaps.get(tableSource).get(i));
					}
				}
			}

			nodes[i].setSource(rrs);
			if(rrs.getDataNodeSlotMap().containsKey(dataNode)){
				nodes[i].setSlot(rrs.getDataNodeSlotMap().get(dataNode));
			}
			if (rrs.getCanRunInReadDB() != null) {
				nodes[i].setCanRunInReadDB(rrs.getCanRunInReadDB());
			}
			if(dataNodeSlotMap.containsKey(table))  {
				nodes[i].setSlot(dataNodeSlotMap.get(table));
			}
			if(rrs.getRunOnSlave() != null){
				nodes[0].setRunOnSlave(rrs.getRunOnSlave());
			}
		}
		return nodes;
	}

	/**
	 * 处理分库表路由
	 */
	public static void findRouteWithcConditionsForTables(SchemaConfig schema, RouteResultset rrs,
			Map<String, Map<String, Set<ColumnRoutePair>>> tablesAndConditions,
			Map<String, Set<String>> tablesRouteMap, String sql, LayerCachePool cachePool, boolean isSelect)
			throws SQLNonTransientException {

		//为分库表找路由
		for(Map.Entry<String, Map<String, Set<ColumnRoutePair>>> entry : tablesAndConditions.entrySet()) {
			String tableName = entry.getKey().toUpperCase();
			TableConfig tableConfig = schema.getTables().get(tableName);
			if(tableConfig == null) {
				String msg = "can't find table define in schema "
						+ tableName + " schema:" + schema.getName();
				LOGGER.warn(msg);
				throw new SQLNonTransientException(msg);
			}
			if(tableConfig.getDistTables()!=null && tableConfig.getDistTables().size()>0){
			    routeToDistTableNode(schema,rrs,sql, tablesAndConditions, cachePool,isSelect, entry);
			}
			//全局表或者不分库的表略过(全局表后面再计算)
			if(tableConfig.isGlobalTable() || schema.getTables().get(tableName).getDataNodes().size() == 1) {
				continue;
			} else {//非全局表:分库表、childTable、其他
				Map<String, Set<ColumnRoutePair>> columnsMap = entry.getValue();
				String joinKey = tableConfig.getJoinKey();
				String partionCol = tableConfig.getPartitionColumn();
				String primaryKey = tableConfig.getPrimaryKey();
				boolean isFoundPartitionValue = partionCol != null && entry.getValue().get(partionCol) != null;
				boolean isLoadData=false;
				if (LOGGER.isDebugEnabled()
						&& sql.startsWith(LoadData.loadDataHint)||rrs.isLoadData()) {
					//由于load data一次会计算很多路由数据,如果输出此日志会极大降低load data的性能
					isLoadData=true;
				}
				if(entry.getValue().get(primaryKey) != null && entry.getValue().size() == 1&&!isLoadData)
				{//主键查找
					// try by primary key if found in cache
					Set<ColumnRoutePair> primaryKeyPairs = entry.getValue().get(primaryKey);
					if (primaryKeyPairs != null) {
						if (LOGGER.isDebugEnabled()) {
							LOGGER.debug("try to find cache by primary key ");
						}
						String tableKey = schema.getName() + '_' + tableName;
						tableKey = tableKey.toUpperCase();
						boolean allFound = true;
						for (ColumnRoutePair pair : primaryKeyPairs) {//可能id in(1,2,3)多主键
							String cacheKey = pair.colValue;
							String dataNode = (String) cachePool.get(tableKey, cacheKey);
							if (dataNode == null) {
								allFound = false;
								continue;
							} else {
								if(tablesRouteMap.get(tableName) == null) {
									tablesRouteMap.put(tableName, new HashSet<String>());
								}
								tablesRouteMap.get(tableName).add(dataNode);
								continue;
							}
						}
						if (!allFound) {
							// need cache primary key ->datanode relation
							if (isSelect && tableConfig.getPrimaryKey() != null) {
								rrs.setPrimaryKey(tableKey + '.' + tableConfig.getPrimaryKey());
							}
						} else {//主键缓存中找到了就执行循环的下一轮
							continue;
						}
					}
				}
				if (isFoundPartitionValue) {//分库表
                    tablesRouteMap.clear();
					Set<ColumnRoutePair> partitionValue = columnsMap.get(partionCol);
					if(partitionValue == null || partitionValue.size() == 0) {
						if(tablesRouteMap.get(tableName) == null) {
							tablesRouteMap.put(tableName, new HashSet<String>());
						}
						tablesRouteMap.get(tableName).addAll(tableConfig.getDataNodes());
					} else {
						for(ColumnRoutePair pair : partitionValue) {
							AbstractPartitionAlgorithm algorithm = tableConfig.getRule().getRuleAlgorithm();
							if(pair.colValue != null) {
								Integer nodeIndex = algorithm.calculate(StringUtil.removeBackquote(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);
								}

								ArrayList<String> dataNodes = tableConfig.getDataNodes();
								String node;
								if (nodeIndex >=0 && nodeIndex < dataNodes.size()) {
									node = dataNodes.get(nodeIndex);

								} else {
									node = null;
									String msg = "Can't find a valid data node for specified node index :"
											+ tableConfig.getName() + " -> " + tableConfig.getPartitionColumn()
											+ " -> " + pair.colValue + " -> " + "Index : " + nodeIndex;
									LOGGER.warn(msg);
									throw new SQLNonTransientException(msg);
								}
								if(node != null) {
									if(tablesRouteMap.get(tableName) == null) {
										tablesRouteMap.put(tableName, new HashSet<String>());
									}
									if(algorithm instanceof SlotFunction){
										rrs.getDataNodeSlotMap().put(node,((SlotFunction) algorithm).slotValue());
									}
									tablesRouteMap.get(tableName).add(node);
								}
							}
							if(pair.rangeValue != null) {
								Integer[] nodeIndexs = algorithm
										.calculateRange(pair.rangeValue.beginValue.toString(), pair.rangeValue.endValue.toString());
								ArrayList<String> dataNodes = tableConfig.getDataNodes();
								String node;
								for(Integer idx : nodeIndexs) {
									if (idx >= 0 && idx < dataNodes.size()) {
										node = dataNodes.get(idx);
									} else {
										String msg = "Can't find valid data node(s) for some of specified node indexes :"
												+ tableConfig.getName() + " -> " + tableConfig.getPartitionColumn();
										LOGGER.warn(msg);
										throw new SQLNonTransientException(msg);
									}
									if(node != null) {
										if(tablesRouteMap.get(tableName) == null) {
											tablesRouteMap.put(tableName, new HashSet<String>());
										}
										if(algorithm instanceof SlotFunction){
											rrs.getDataNodeSlotMap().put(node,((SlotFunction) algorithm).slotValue());
										}
										tablesRouteMap.get(tableName).add(node);

									}
								}
							}
						}
					}
				} else if(joinKey != null && columnsMap.get(joinKey) != null && columnsMap.get(joinKey).size() != 0) {//childTable  (如果是select 语句的父子表join)之前要找到root table,将childTable移除,只留下root table
					Set<ColumnRoutePair> joinKeyValue = columnsMap.get(joinKey);

					Set<String> dataNodeSet = ruleByJoinValueCalculate(rrs, tableConfig, joinKeyValue);

					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) {
						routeToMultiNode(rrs.isCacheAble(), rrs, dataNodeSet, sql);
						rrs.setFinishedRoute(true);
						return;
					} else {
						rrs.setCacheAble(true);
						routeToSingleNode(rrs, dataNodeSet.iterator().next(), sql);
						return;
					}

				} else {
					//没找到拆分字段,该表的所有节点都路由
					if(tablesRouteMap.get(tableName) == null) {
						tablesRouteMap.put(tableName, new HashSet<String>());
					}
					boolean isSlotFunction= tableConfig.getRule() != null && tableConfig.getRule().getRuleAlgorithm() instanceof SlotFunction;
					if(isSlotFunction){
						for (String dn : tableConfig.getDataNodes()) {
							rrs.getDataNodeSlotMap().put(dn,-1);
						}
					}
					tablesRouteMap.get(tableName).addAll(tableConfig.getDataNodes());
				}
			}
		}
	}

	public static boolean isAllGlobalTable(DruidShardingParseInfo ctx, SchemaConfig schema) {
		boolean isAllGlobal = false;
		for(String table : ctx.getTables()) {
			TableConfig tableConfig = schema.getTables().get(table);
			if(tableConfig!=null && tableConfig.isGlobalTable()) {
				isAllGlobal = true;
			} else {
				return false;
			}
		}
		return isAllGlobal;
	}

	/**
	 *
	 * @param schema
	 * @param ctx
	 * @param tc
	 * @return true表示校验通过,false表示检验不通过
	 */
	public static boolean checkRuleRequired(SchemaConfig schema, DruidShardingParseInfo ctx, RouteCalculateUnit routeUnit, TableConfig tc) {
		if(!tc.isRuleRequired()) {
			return true;
		}
		boolean hasRequiredValue = false;
		String tableName = tc.getName();
		if(routeUnit.getTablesAndConditions().get(tableName) == null || routeUnit.getTablesAndConditions().get(tableName).size() == 0) {
			hasRequiredValue = false;
		} else {
			for(Map.Entry<String, Set<ColumnRoutePair>> condition : routeUnit.getTablesAndConditions().get(tableName).entrySet()) {

				String colName = condition.getKey();
				//条件字段是拆分字段
				if(colName.equals(tc.getPartitionColumn())) {
					hasRequiredValue = true;
					break;
				}
			}
		}
		return hasRequiredValue;
	}


	/**
	 * 增加判断支持未配置分片的表走默认的dataNode
	 * @param schemaConfig
	 * @param tableName
	 * @return
	 */
	public static boolean isNoSharding(SchemaConfig schemaConfig, String tableName) {
		// Table名字被转化为大写的,存储在schema
		tableName = tableName.toUpperCase();
		if (schemaConfig.isNoSharding()) {
			return true;
		}

		if (schemaConfig.getDataNode() != null && !schemaConfig.getTables().containsKey(tableName)) {
			return true;
		}

		return false;
	}

	/**
	 * 系统表判断,某些sql语句会查询系统表或者跟系统表关联
	 * @author lian
	 * @date 2016年12月2日
	 * @param tableName
	 * @return
	 */
	public static boolean isSystemSchema(String tableName) {
		// 以information_schema, mysql开头的是系统表
		if (tableName.startsWith("INFORMATION_SCHEMA.")
				|| tableName.startsWith("MYSQL.")
				|| tableName.startsWith("PERFORMANCE_SCHEMA.")) {
			return true;
		}

		return false;
	}

	/**
	 * 判断条件是否永真
	 * @param expr
	 * @return
	 */
	public static boolean isConditionAlwaysTrue(SQLExpr expr) {
		Object o = WallVisitorUtils.getValue(expr);
		if(Boolean.TRUE.equals(o)) {
			return true;
		}
		return false;
	}

	/**
	 * 判断条件是否永假的
	 * @param expr
	 * @return
	 */
	public static boolean isConditionAlwaysFalse(SQLExpr expr) {
		Object o = WallVisitorUtils.getValue(expr);
		if(Boolean.FALSE.equals(o)) {
			return true;
		}
		return false;
	}


	/**
	 * 该方法,返回是否是ER子表
	 * @param schema
	 * @param origSQL
	 * @param sc
	 * @return
	 * @throws SQLNonTransientException
	 *
	 * 备注说明:
	 *     edit by ding.w at 2017.4.28, 主要处理 CLIENT_MULTI_STATEMENTS(insert into ; insert into)的情况
	 *     目前仅支持mysql,并COM_QUERY请求包中的所有insert语句要么全部是er表,要么全部不是
	 *
	 *
	 */
	public static boolean processERChildTable(final SchemaConfig schema, final String origSQL,
			final ServerConnection sc) throws SQLNonTransientException {

		MySqlStatementParser parser = new MySqlStatementParser(origSQL);
		List<SQLStatement> statements = parser.parseStatementList();

		if(statements == null || statements.isEmpty() ) {
			throw new SQLNonTransientException(String.format("无效的SQL语句:%s", origSQL));
		}


		boolean erFlag = false; //是否是er表
		for(SQLStatement stmt : statements ) {
			MySqlInsertStatement insertStmt = (MySqlInsertStatement) stmt;
			String tableName = insertStmt.getTableName().getSimpleName().toUpperCase();
			final TableConfig tc = schema.getTables().get(tableName);

			if (null != tc && tc.isChildTable()) {
				erFlag = true;

				String sql = insertStmt.toString();

				final RouteResultset rrs = new RouteResultset(sql, ServerParse.INSERT);
				String joinKey = tc.getJoinKey();
				//因为是Insert语句,用MySqlInsertStatement进行parse
//				MySqlInsertStatement insertStmt = (MySqlInsertStatement) (new MySqlStatementParser(origSQL)).parseInsert();
				//判断条件完整性,取得解析后语句列中的joinkey列的index
				int joinKeyIndex = getJoinKeyIndex(insertStmt.getColumns(), joinKey);
				if (joinKeyIndex == -1) {
					String inf = "joinKey not provided :" + tc.getJoinKey() + "," + insertStmt;
					LOGGER.warn(inf);
					throw new SQLNonTransientException(inf);
				}
				//子表不支持批量插入
				if (isMultiInsert(insertStmt)) {
					String msg = "ChildTable multi insert not provided";
					LOGGER.warn(msg);
					throw new SQLNonTransientException(msg);
				}
				//取得joinkey的值
				String joinKeyVal = insertStmt.getValues().getValues().get(joinKeyIndex).toString();
				//解决bug #938,当关联字段的值为char类型时,去掉前后"'"
				String realVal = joinKeyVal;
				if (joinKeyVal.startsWith("'") && joinKeyVal.endsWith("'") && joinKeyVal.length() > 2) {
					realVal = joinKeyVal.substring(1, joinKeyVal.length() - 1);
				}

				// try to route by ER parent partion key
				//如果是二级子表(父表不再有父表),并且分片字段正好是joinkey字段,调用routeByERParentKey
				RouteResultset theRrs = RouterUtil.routeByERParentKey(sc, schema, ServerParse.INSERT, sql, rrs, tc, realVal);
				if (theRrs != null) {
					boolean processedInsert=false;
					//判断是否需要全局序列号
					if ( sc!=null && tc.isAutoIncrement()) {
						String primaryKey = tc.getPrimaryKey();
						processedInsert=processInsert(sc,schema,ServerParse.INSERT,sql,tc.getName(),primaryKey);
					}
					if(processedInsert==false){
						rrs.setFinishedRoute(true);
						sc.getSession2().execute(rrs, ServerParse.INSERT);
					}
					// return true;
					//继续处理下一条
					continue;
				}

				// route by sql query root parent's datanode
				//如果不是二级子表或者分片字段不是joinKey字段结果为空,则启动异步线程去后台分片查询出datanode
				//只要查询出上一级表的parentkey字段的对应值在哪个分片即可
				final String findRootTBSql = tc.getLocateRTableKeySql().toLowerCase() + joinKeyVal;
				if (LOGGER.isDebugEnabled()) {
					LOGGER.debug("find root parent's node sql " + findRootTBSql);
				}

				ListenableFuture<String> listenableFuture = MycatServer.getInstance().
						getListeningExecutorService().submit(new Callable<String>() {
					@Override
					public String call() throws Exception {
						if (tc.getRootParent().getFetchStoreNodeByJdbc()) {
							JDBCFetchStoreNodeOfChildTableHandler jdbcFetchStoreNodeOfChildTableHandler =
									new JDBCFetchStoreNodeOfChildTableHandler();
							return jdbcFetchStoreNodeOfChildTableHandler
									.execute(schema.getName(), findRootTBSql, tc.getRootParent().getDataNodes());
						} else {
							FetchStoreNodeOfChildTableHandler fetchHandler = new FetchStoreNodeOfChildTableHandler();
							return fetchHandler.execute(schema.getName(), findRootTBSql, tc.getRootParent().getDataNodes(), sc);
						}

					}
				});


				Futures.addCallback(listenableFuture, new FutureCallback<String>() {
					@Override
					public void onSuccess(String result) {
						//结果为空,证明上一级表中不存在那条记录,失败
						if (Strings.isNullOrEmpty(result)) {
							StringBuilder s = new StringBuilder();
							LOGGER.warn(s.append(sc.getSession2()).append(origSQL).toString() +
									" err:" + "can't find (root) parent sharding node for sql:" + origSQL);
							if(!sc.isAutocommit()) { // 处于事务下失败, 必须回滚
								sc.setTxInterrupt("can't find (root) parent sharding node for sql:" + origSQL);
							}
							sc.writeErrMessage(ErrorCode.ER_PARSE_ERROR, "can't find (root) parent sharding node for sql:" + origSQL);
							return;
						}

						if (LOGGER.isDebugEnabled()) {
							LOGGER.debug("found partion node for child table to insert " + result + " sql :" + origSQL);
						}
						//找到分片,进行插入(和其他的一样,需要判断是否需要全局自增ID)
						boolean processedInsert=false;
						if ( sc!=null && tc.isAutoIncrement()) {
							try {
								String primaryKey = tc.getPrimaryKey();
								processedInsert=processInsert(sc,schema,ServerParse.INSERT,origSQL,tc.getName(),primaryKey);
							} catch (SQLNonTransientException e) {
								LOGGER.warn("sequence processInsert error,",e);
								sc.writeErrMessage(ErrorCode.ER_PARSE_ERROR , "sequence processInsert error," + e.getMessage());
							}
						}
						if(processedInsert==false){
							RouteResultset executeRrs = RouterUtil.routeToSingleNode(rrs, result, origSQL);
							sc.getSession2().execute(executeRrs, ServerParse.INSERT);
						}

					}

					@Override
					public void onFailure(Throwable t) {
						StringBuilder s = new StringBuilder();
						LOGGER.warn(s.append(sc.getSession2()).append(origSQL).toString() +
								" err:" + t.getMessage());
						sc.writeErrMessage(ErrorCode.ER_PARSE_ERROR, t.getMessage() + " " + s.toString());
					}
				}, MycatServer.getInstance().
						getListeningExecutorService());

			} else if(erFlag) {
				throw new SQLNonTransientException(String.format("%s包含不是ER分片的表", origSQL));
			}
		}


		return erFlag;
	}

	/**
	 * 寻找joinKey的索引
	 *
	 * @param columns
	 * @param joinKey
	 * @return -1表示没找到,>=0表示找到了
	 */
	private static int getJoinKeyIndex(List<SQLExpr> columns, String joinKey) {
		for (int i = 0; i < columns.size(); i++) {
			String col = StringUtil.removeBackquote(columns.get(i).toString()).toUpperCase();
			if (col.equals(joinKey)) {
				return i;
			}
		}
		return -1;
	}

	/**
	 * 是否为批量插入:insert into ...values (),()...或 insert into ...select.....
	 *
	 * @param insertStmt
	 * @return
	 */
	private static boolean isMultiInsert(MySqlInsertStatement insertStmt) {
		return (insertStmt.getValuesList() != null && insertStmt.getValuesList().size() > 1)
				|| insertStmt.getQuery() != null;
	}
    /**
     * escape white spaces and get the real start position.
     * @author kevin
     * @param stmt  The sql statement.
     * @param startPos  The initial start position.
     * @return  int  The real start position.
     */
    private static int getStartPos(String stmt, int startPos) {
        while (startPos < stmt.length()) {
            if (!Character.isWhitespace(stmt.charAt(startPos))) {
                break;
            }
            ++startPos;
        }
        return startPos;
    }
}