/** * 对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) { 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; }
/** * 根据show语句获取数据路由集合 * * @param schema 数据库名 * @param rrs 数据路由集合 * @param stmt 执行的语句 * @return RouteResultset数据路由集合 * @throws SQLSyntaxErrorException * @author mycat */ 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 = getSpecPos(upStmt, 0); if (nextPost[0] > 0) { // remove db info int end = getSpecEndPos(upStmt, tabInd); if (upStmt.indexOf(" FULL") > 0) { stmt = "SHOW FULL TABLES" + stmt.substring(end); } else { stmt = "SHOW TABLES" + stmt.substring(end); } } return routeToMultiNode(schema, false, false, null, rrs, schema.getMetaDataNodes(), stmt); } // show index or column int[] indx = getSpecPos(upStmt, 0); if (indx[0] > 0) { // has table int[] repPos = {indx[0] + indx[1], 0}; String tableName = RouterUtil.getTableName(stmt, repPos); // IN DB pattern int[] indx2 = getSpecPos(upStmt, indx[0] + indx[1] + 1); if (indx2[0] > 0) { // find LIKE OR WHERE repPos[1] = 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 = 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); }
private static RouteResultset routeChildTableInsert( SchemaConfig schema, TableConfig tc, QueryTreeNode ast, InsertParseInf parsInf, RouteResultset rrs, String stmt, LayerCachePool cachePool) throws SQLNonTransientException { if (tc.isChildTable()) { String joinKeyVal = parsInf.columnPairMap.get(tc.getJoinKey()); if (joinKeyVal == null) { String inf = "joinKey not provided :" + tc.getJoinKey() + "," + stmt; LOGGER.warn(inf); throw new SQLNonTransientException(inf); } // try to route by ER parent partion key RouteResultset theRrs = RouterUtil.routeByERParentKey(stmt, rrs, tc, joinKeyVal); if (theRrs != null) { return theRrs; } // route by sql query root parent's datanode String findRootTBSql = RouterUtil.buildFindRootTbSql( tc.getLocateRTableKeySql(), joinKeyVal); // tc.getLocateRTableKeySql().toLowerCase() + " = " + joinKeyVal; if (LOGGER.isDebugEnabled()) { LOGGER.debug("find root parent's node sql " + findRootTBSql); } FetchStoreNodeOfChildTableHandler fetchHandler = new FetchStoreNodeOfChildTableHandler(); String dn = fetchHandler.execute(schema.getName(), findRootTBSql, tc.getRootParent().getDataNodes()); if (dn == null) { throw new SQLNonTransientException( "can't find (root) parent sharding node for sql:" + stmt); } if (LOGGER.isDebugEnabled()) { LOGGER.debug("found partion node for child table to insert " + dn + " sql :" + stmt); } return RouterUtil.routeToSingleNode(rrs, dn, stmt); } return null; }
/** * 根据执行语句判断数据路由 * * @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 routeToMultiNode(schema, false, false, null, rrs, schema.getMetaDataNodes(), stmt); } return RouterUtil.routeToSingleNode(rrs, schema.getRandomDataNode(), stmt); }
private RouteResultset routeSelect( SchemaConfig schema, QueryTreeNode ast, RouteResultset rrs, String stmt, LayerCachePool cachePool) throws SQLNonTransientException { ResultSetNode rsNode = ((CursorNode) ast).getResultSetNode(); if (rsNode instanceof SelectNode) { if (((SelectNode) rsNode).getFromList().isEmpty()) { // if it is a sql about system info, such as select charset etc. return RouterUtil.routeToSingleNode(rrs, schema.getRandomDataNode(), stmt); } } // standard SELECT operation SelectParseInf parsInf = new SelectParseInf(); parsInf.ctx = new ShardingParseInfo(); SelectSQLAnalyser.analyse(parsInf, ast); return tryRouteForTables(ast, true, rrs, schema, parsInf.ctx, stmt, cachePool); }
/** * 简单描述该方法的实现功能 * * @param ast QueryTreeNode * @param isSelect 是否是select语句 * @param rrs 数据路由集合 * @param schema 数据库名 the name of datebase * @param ctx ShardingParseInfo(分片) * @param sql 执行sql * @param cachePool * @return 一个数据路由集合 * @throws SQLNonTransientException * @author mycat */ private RouteResultset tryRouteForTables( QueryTreeNode ast, boolean isSelect, RouteResultset rrs, SchemaConfig schema, ShardingParseInfo ctx, String sql, LayerCachePool cachePool) throws SQLNonTransientException { Map<String, TableConfig> tables = schema.getTables(); Map<String, Map<String, Set<ColumnRoutePair>>> tbCondMap = ctx.tablesAndConditions; if (tbCondMap.size() == 1) { // only one table in this sql Map.Entry<String, Map<String, Set<ColumnRoutePair>>> entry = tbCondMap.entrySet().iterator().next(); TableConfig tc = getTableConfig(schema, entry.getKey()); if (tc.getRule() == null && tc.getDataNodes().size() == 1) { rrs.setCacheAble(isSelect); // 20140625 修复 配置为全局表单节点的语句不会自动加上limit sql = addSQLLmit(schema, rrs, ast, sql); return RouterUtil.routeToSingleNode(rrs, tc.getDataNodes().get(0), sql); } Map<String, Set<ColumnRoutePair>> colConds = entry.getValue(); return tryRouteForTable( ast, schema, rrs, isSelect, sql, tc, colConds.get(tc.getPartitionColumn()), colConds, cachePool); } else if (!ctx.joinList.isEmpty()) { for (JoinRel joinRel : ctx.joinList) { TableConfig rootc = schema.getJoinRel2TableMap().get(joinRel.joinSQLExp); if (rootc == null) { if (LOGGER.isDebugEnabled()) { LOGGER.debug( "can't find join relation in schema " + schema.getName() + " :" + joinRel.joinSQLExp + " maybe global table join"); } } else { if (rootc.getName().equals(joinRel.tableA)) { // table a is root table tbCondMap.remove(joinRel.tableB); } else if (rootc.getName().equals(joinRel.tableB)) { // table B is root table tbCondMap.remove(joinRel.tableA); } else if (tbCondMap.containsKey(rootc.getName())) { // contains root table in sql ,then remove all child tbCondMap.remove(joinRel.tableA); tbCondMap.remove(joinRel.tableB); } else { // both there A and B are not root table,remove any // one tbCondMap.remove(joinRel.tableA); } } } } if (tbCondMap.size() > 1) { Set<String> curRNodeSet = new LinkedHashSet<String>(); Set<String> routePairSet = new LinkedHashSet<String>(); // 拆分字段后路由节点 String curTableName = null; Map<String, List<String>> globalTableDataNodesMap = new LinkedHashMap<String, List<String>>(); for (Entry<String, Map<String, Set<ColumnRoutePair>>> e : tbCondMap.entrySet()) { String tableName = e.getKey(); Map<String, Set<ColumnRoutePair>> col2ValMap = e.getValue(); TableConfig tc = tables.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); } else if (tc.getTableType() == TableConfig.TYPE_GLOBAL_TABLE) { // add to globalTablelist globalTableDataNodesMap.put(tc.getName(), tc.getDataNodes()); continue; } Collection<String> newDataNodes = null; String partColmn = tc.getPartitionColumn(); Set<ColumnRoutePair> col2Val = partColmn == null ? null : col2ValMap.get(partColmn); if (col2Val == null || col2Val.isEmpty()) { if (tc.isRuleRequired()) { throw new IllegalArgumentException( "route rule for table " + tableName + " is required: " + sql); } newDataNodes = tc.getDataNodes(); } else { // match table with where condtion of partion colum values newDataNodes = RouterUtil.ruleCalculate(tc, col2Val); } if (curRNodeSet.isEmpty()) { curTableName = tc.getName(); curRNodeSet.addAll(newDataNodes); if (col2Val != null && !col2Val.isEmpty()) { routePairSet.addAll(newDataNodes); } } else { if (col2Val == null || col2Val.isEmpty()) { if (curRNodeSet.retainAll(newDataNodes) && routePairSet.isEmpty()) { String errMsg = "invalid route in sql, multi tables found but datanode has no intersection " + " sql:" + sql; LOGGER.warn(errMsg); throw new SQLNonTransientException(errMsg); } } else { if (routePairSet.isEmpty()) { routePairSet.addAll(newDataNodes); } else if (!checkIfValidMultiTableRoute(routePairSet, newDataNodes) || (curRNodeSet.retainAll(newDataNodes) && routePairSet.isEmpty())) { String errMsg = "invalid route in sql, " + routePairSet + " route to :" + Arrays.toString(routePairSet.toArray()) + " ,but " + tc.getName() + " to " + Arrays.toString(newDataNodes.toArray()) + " sql:" + sql; LOGGER.warn(errMsg); throw new SQLNonTransientException(errMsg); } } // if (!checkIfValidMultiTableRoute(curRNodeSet, // newDataNodes)) { // String errMsg = "invalid route in sql, " + curTableName // + " route to :" // + Arrays.toString(curRNodeSet.toArray()) // + " ,but " + tc.getName() + " to " // + Arrays.toString(newDataNodes.toArray()) // + " sql:" + sql; // LOGGER.warn(errMsg); // throw new SQLNonTransientException(errMsg); // } } } // only global table contains in sql if (!globalTableDataNodesMap.isEmpty() && curRNodeSet.isEmpty()) { List<String> resultList = Lists.newArrayList(); for (List<String> nodeList : globalTableDataNodesMap.values()) { if (resultList.isEmpty()) { resultList = nodeList; } else { if (resultList.retainAll(nodeList) && resultList.isEmpty()) { String errMsg = "invalid route in sql, multi global tables found but datanode has no intersection " + " sql:" + sql; LOGGER.warn(errMsg); throw new SQLNonTransientException(errMsg); } } } if (resultList.size() == 1) { rrs.setCacheAble(true); sql = addSQLLmit(schema, rrs, ast, sql); rrs = RouterUtil.routeToSingleNode(rrs, resultList.get(0), sql); } else { // mulit routes ,not cache route result rrs.setCacheAble(false); rrs = RouterUtil.routeToSingleNode(rrs, getRandomDataNode(resultList), sql); } return rrs; } else if (!globalTableDataNodesMap.isEmpty() && !curRNodeSet.isEmpty()) { // judge if global table contains all dataNodes of other tables for (Map.Entry<String, List<String>> entry : globalTableDataNodesMap.entrySet()) { if (!entry.getValue().containsAll(curRNodeSet)) { String errMsg = "invalid route in sql, " + curTableName + " route to :" + Arrays.toString(curRNodeSet.toArray()) + " ,but " + entry.getKey() + " to " + Arrays.toString(entry.getValue().toArray()) + " sql:" + sql; LOGGER.warn(errMsg); throw new SQLNonTransientException(errMsg); } } } if (curRNodeSet.size() > 1) { LOGGER.warn( "multi route tables found in this sql ,tables:" + Arrays.toString(tbCondMap.keySet().toArray()) + " sql:" + sql); return routeToMultiNode(schema, isSelect, isSelect, ast, rrs, curRNodeSet, sql); } else { return RouterUtil.routeToSingleNode(rrs, curRNodeSet.iterator().next(), sql); } } else { // only one table Map.Entry<String, Map<String, Set<ColumnRoutePair>>> entry = tbCondMap.entrySet().iterator().next(); Map<String, Set<ColumnRoutePair>> allColValues = entry.getValue(); TableConfig tc = getTableConfig(schema, entry.getKey()); return tryRouteForTable( ast, schema, rrs, isSelect, sql, tc, allColValues.get(tc.getPartitionColumn()), allColValues, cachePool); } }
/** * 简单描述该方法的实现功能 * * @param ast QueryTreeNode * @param schema 数据库名 * @param rrs 数据路由集合 * @param isSelect 是否是select语句标志 * @param sql 执行语句 * @param tc 表实体 * @param ruleCol2Val 一个ColumnRoutePair集合 * @param allColConds 一个ColumnRoutePair集合 * @param cachePool * @return 一个数据路由集合 * @throws SQLNonTransientException * @author mycat */ private RouteResultset tryRouteForTable( QueryTreeNode ast, SchemaConfig schema, RouteResultset rrs, boolean isSelect, String sql, TableConfig tc, Set<ColumnRoutePair> ruleCol2Val, Map<String, Set<ColumnRoutePair>> allColConds, LayerCachePool cachePool) throws SQLNonTransientException { if (tc.getTableType() == TableConfig.TYPE_GLOBAL_TABLE && isSelect) { sql = addSQLLmit(schema, rrs, ast, sql); return RouterUtil.routeToSingleNode(rrs, tc.getRandomDataNode(), sql); } // no partion define or no where condtion for this table or no // partion column condtions boolean cache = isSelect; if (ruleCol2Val == null || ruleCol2Val.isEmpty()) { if (tc.isRuleRequired()) { throw new IllegalArgumentException( "route rule for table " + tc.getName() + " is required: " + sql); } else if (allColConds != null && allColConds.size() == 1) { // try if can route by ER relation if (tc.isSecondLevel() && tc.getParentTC().getPartitionColumn().equals(tc.getParentKey())) { Set<ColumnRoutePair> joinKeyPairs = allColConds.get(tc.getJoinKey()); if (joinKeyPairs != null) { Set<String> dataNodeSet = RouterUtil.ruleCalculate(tc.getParentTC(), joinKeyPairs); 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 routeToMultiNode(schema, isSelect, isSelect, ast, rrs, dataNodeSet, sql); } else { rrs.setCacheAble(true); return RouterUtil.routeToSingleNode(rrs, dataNodeSet.iterator().next(), sql); } } } // try by primary key if found in cache Set<ColumnRoutePair> primaryKeyPairs = allColConds.get(tc.getPrimaryKey()); if (primaryKeyPairs != null) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("try to find cache by primary key "); } cache = false; Set<String> dataNodes = new HashSet<String>(primaryKeyPairs.size()); boolean allFound = true; String tableKey = schema.getName() + '_' + tc.getName(); for (ColumnRoutePair pair : primaryKeyPairs) { String cacheKey = pair.colValue; String dataNode = (String) cachePool.get(tableKey, cacheKey); if (dataNode == null) { allFound = false; break; } else { dataNodes.add(dataNode); } } if (allFound) { return routeToMultiNode(schema, isSelect, isSelect, ast, rrs, dataNodes, sql); } // need cache primary key ->datanode relation if (isSelect && tc.getPrimaryKey() != null) { rrs.setPrimaryKey(tableKey + '.' + tc.getPrimaryKey()); } } } return routeToMultiNode(schema, isSelect, cache, ast, rrs, tc.getDataNodes(), sql); } // match table with where condtion of partion colum values Set<String> dataNodeSet = RouterUtil.ruleCalculate(tc, ruleCol2Val); if (dataNodeSet.size() == 1) { rrs.setCacheAble(isSelect); return RouterUtil.routeToSingleNode(rrs, dataNodeSet.iterator().next(), sql); } else { return routeToMultiNode(schema, isSelect, isSelect, ast, rrs, dataNodeSet, sql); } }