private String[] getDataNodes() { String[] dataNodes = new String[rrs.getNodes().length]; for (int i = 0; i < rrs.getNodes().length; i++) { dataNodes[i] = rrs.getNodes()[i].getName(); } return dataNodes; }
/** * 获取第一个节点作为路由 * * @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() rrs.setNodes(nodes); rrs.setFinishedRoute(true); if (rrs.getCanRunInReadDB() != null) { nodes[0].setCanRunInReadDB(rrs.getCanRunInReadDB()); } 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); if (rrs.getCanRunInReadDB() != null) { nodes[0].setCanRunInReadDB(rrs.getCanRunInReadDB()); } rrs.setNodes(nodes); }
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); if (rrs.getCanRunInReadDB() != null) { node.setCanRunInReadDB(rrs.getCanRunInReadDB()); } nodes[i++] = node; } rrs.setCacheAble(cache); rrs.setNodes(nodes); return rrs; }
public void processSQL(String sql, EngineCtx ctx) { String ssql = joinParser.getSql(); getRoute(ssql); RouteResultsetNode[] nodes = rrs.getNodes(); if (nodes == null || nodes.length == 0 || nodes[0].getName() == null || nodes[0].getName().equals("")) { ctx.getSession() .getSource() .writeErrMessage( ErrorCode.ER_NO_DB_ERROR, "No dataNode found ,please check tables defined in schema:" + ctx.getSession().getSource().getSchema()); return; } this.ctx = ctx; String[] dataNodes = getDataNodes(); maxjob = dataNodes.length; ShareDBJoinHandler joinHandler = new ShareDBJoinHandler(this, joinParser.getJoinLkey()); ctx.executeNativeSQLSequnceJob(dataNodes, ssql, joinHandler); EngineCtx.LOGGER.info("Catlet exec:" + getDataNode(getDataNodes()) + " sql:" + ssql); ctx.setAllJobFinishedListener( new AllJobFinishedListener() { @Override public void onAllJobFinished(EngineCtx ctx) { if (!jointTableIsData) { ctx.writeHeader(fields); } ctx.writeEof(); EngineCtx.LOGGER.info("发送数据OK"); } }); }
/** * 修复DDL路由 * * @return RouteResultset * @author aStoneGod */ public static RouteResultset routeToDDLNode( RouteResultset rrs, int sqlType, String stmt, SchemaConfig schema) throws SQLSyntaxErrorException { // 检查表是否在配置文件中 stmt = getFixedSql(stmt); String tablename = ""; final String upStmt = stmt.toUpperCase(); if (upStmt.startsWith("CREATE")) { tablename = RouterUtil.getTableName(stmt, RouterUtil.getCreateTablePos(upStmt, 0)); } else if (upStmt.startsWith("DROP")) { 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; if (tables != null && (tc = tables.get(tablename)) != null) { dataNodes = tc.getDataNodes(); } Iterator<String> iterator1 = dataNodes.iterator(); int nodeSize = dataNodes.size(); RouteResultsetNode[] nodes = new RouteResultsetNode[nodeSize]; for (int i = 0; i < nodeSize; i++) { String name = iterator1.next(); nodes[i] = new RouteResultsetNode(name, sqlType, stmt); } rrs.setNodes(nodes); } return rrs; } else if (schema.getDataNode() != null) { // 默认节点ddl RouteResultsetNode[] nodes = new RouteResultsetNode[1]; nodes[0] = new RouteResultsetNode(schema.getDataNode(), sqlType, stmt); rrs.setNodes(nodes); return rrs; } // 不在,返回null LOGGER.error("table not in schema----" + tablename); throw new SQLSyntaxErrorException("op table not in schema----" + tablename); }
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 boolean processERChildTable( final SchemaConfig schema, final String origSQL, final ServerConnection sc) throws SQLNonTransientException { String tableName = StringUtil.getTableName(origSQL).toUpperCase(); final TableConfig tc = schema.getTables().get(tableName); if (null != tc && tc.isChildTable()) { final RouteResultset rrs = new RouteResultset(origSQL, ServerParse.INSERT); String joinKey = tc.getJoinKey(); MySqlInsertStatement insertStmt = (MySqlInsertStatement) (new MySqlStatementParser(origSQL)).parseInsert(); 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); } String joinKeyVal = insertStmt.getValues().getValues().get(joinKeyIndex).toString(); String sql = insertStmt.toString(); // try to route by ER parent partion key RouteResultset theRrs = RouterUtil.routeByERParentKey(sql, rrs, tc, joinKeyVal); if (theRrs != null) { rrs.setFinishedRoute(true); sc.getSession2().execute(rrs, ServerParse.INSERT); return true; } // route by sql query root parent's datanode 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 { FetchStoreNodeOfChildTableHandler fetchHandler = new FetchStoreNodeOfChildTableHandler(); return fetchHandler.execute( schema.getName(), findRootTBSql, tc.getRootParent().getDataNodes()); } }); 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); 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); } 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()); return true; } return false; }
/** * 处理分库表路由 * * @param schema * @param tablesAndConditions * @param tablesRouteMap * @throws SQLNonTransientException */ 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.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()) { if (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; 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) { // 分库表 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) { if (pair.colValue != null) { Integer nodeIndex = tableConfig.getRule().getRuleAlgorithm().calculate(pair.colValue); if (nodeIndex == null) { String msg = "can't find any valid datanode :" + tableConfig.getName() + " -> " + tableConfig.getPartitionColumn() + " -> " + pair.colValue; LOGGER.warn(msg); throw new SQLNonTransientException(msg); } String node = tableConfig.getDataNodes().get(nodeIndex); if (node != null) { if (tablesRouteMap.get(tableName) == null) { tablesRouteMap.put(tableName, new HashSet<String>()); } tablesRouteMap.get(tableName).add(node); } } if (pair.rangeValue != null) { Integer[] nodeIndexs = tableConfig .getRule() .getRuleAlgorithm() .calculateRange( pair.rangeValue.beginValue.toString(), pair.rangeValue.endValue.toString()); for (Integer idx : nodeIndexs) { String node = tableConfig.getDataNodes().get(idx); if (node != null) { if (tablesRouteMap.get(tableName) == null) { tablesRouteMap.put(tableName, new HashSet<String>()); } 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); ColumnRoutePair joinCol = null; 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>()); } tablesRouteMap.get(tableName).addAll(tableConfig.getDataNodes()); } } } }
/** * 单表路由 * * @param schema * @param ctx * @param tableName * @param rrs * @param isSelect * @return * @throws SQLNonTransientException */ 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); } if (tc.isGlobalTable()) { // 全局表 if (isSelect) { // global select ,not cache route result rrs.setCacheAble(false); return routeToSingleNode(rrs, tc.getRandomDataNode(), 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 { // boolean isCache = rrs.isCacheAble(); // if(tablesRouteMap.get(tableName).size() > 1) { // // } return routeToMultiNode( rrs.isCacheAble(), rrs, tablesRouteMap.get(tableName), ctx.getSql()); } } } }
/** * 多表路由 * * @param schema * @param ctx * @param tables * @param rrs * @param isSelect * @return * @throws SQLNonTransientException */ 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()); } // 只有一个表的 if (tables.size() == 1) { return RouterUtil.tryRouteForOneTable( schema, ctx, routeUnit, tables.get(0), rrs, isSelect, cachePool); } Set<String> retNodesSet = new HashSet<String>(); // 每个表对应的路由映射 Map<String, Set<String>> tablesRouteMap = new HashMap<String, Set<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; } } // 为全局表和单库表找路由 for (String tableName : tables) { TableConfig tableConfig = schema.getTables().get(tableName.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()); } } 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) { if (retNodesSet.size() > 1 && isAllGlobalTable(ctx, schema)) { // mulit routes ,not cache route result if (isSelect) { rrs.setCacheAble(false); routeToSingleNode(rrs, retNodesSet.iterator().next(), ctx.getSql()); } else { // delete 删除全局表的记录 routeToMultiNode(isSelect, rrs, retNodesSet, ctx.getSql(), true); } } else { routeToMultiNode(isSelect, rrs, retNodesSet, ctx.getSql()); } } return rrs; }
/** @return dataNodeIndex -> [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 it = colRoutePairSet.iterator(); ColumnRoutePair joinCol = (ColumnRoutePair) it.next(); joinValue = joinCol.colValue; } Set<String> retNodeSet = new LinkedHashSet<String>(); Set<String> nodeSet = new LinkedHashSet<String>(); if (tc.isSecondLevel() && tc.getParentTC().getPartitionColumn().equals(tc.getParentKey())) { // using // parent // rule to // find // datanode nodeSet = ruleCalculate(tc.getParentTC(), colRoutePairSet); 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; }