Search in sources :

Example 1 with MySqlInsertStatement

use of com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement in project Mycat-Server by MyCATApache.

the class DruidMycatRouteStrategy method routeNormalSqlWithAST.

@Override
public RouteResultset routeNormalSqlWithAST(SchemaConfig schema, String stmt, RouteResultset rrs, String charset, LayerCachePool cachePool) throws SQLNonTransientException {
    /**
		 *  只有mysql时只支持mysql语法
		 */
    SQLStatementParser parser = null;
    if (schema.isNeedSupportMultiDBType()) {
        parser = new MycatStatementParser(stmt);
    } else {
        parser = new MySqlStatementParser(stmt);
    }
    MycatSchemaStatVisitor visitor = null;
    SQLStatement statement;
    /**
		 * 解析出现问题统一抛SQL语法错误
		 */
    try {
        statement = parser.parseStatement();
        visitor = new MycatSchemaStatVisitor();
    } catch (Exception t) {
        LOGGER.error("DruidMycatRouteStrategyError", t);
        throw new SQLSyntaxErrorException(t);
    }
    /**
		 * 检验unsupported statement
		 */
    checkUnSupportedStatement(statement);
    DruidParser druidParser = DruidParserFactory.create(schema, statement, visitor);
    druidParser.parser(schema, rrs, statement, stmt, cachePool, visitor);
    DruidShardingParseInfo ctx = druidParser.getCtx();
    rrs.setTables(ctx.getTables());
    /**
		 * DruidParser 解析过程中已完成了路由的直接返回
		 */
    if (rrs.isFinishedRoute()) {
        return rrs;
    }
    /**
		 * 没有from的select语句或其他
		 */
    if ((ctx.getTables() == null || ctx.getTables().size() == 0) && (ctx.getTableAliasMap() == null || ctx.getTableAliasMap().isEmpty())) {
        return RouterUtil.routeToSingleNode(rrs, schema.getRandomDataNode(), druidParser.getCtx().getSql());
    }
    if (druidParser.getCtx().getRouteCalculateUnits().size() == 0) {
        RouteCalculateUnit routeCalculateUnit = new RouteCalculateUnit();
        druidParser.getCtx().addRouteCalculateUnit(routeCalculateUnit);
    }
    SortedSet<RouteResultsetNode> nodeSet = new TreeSet<RouteResultsetNode>();
    for (RouteCalculateUnit unit : druidParser.getCtx().getRouteCalculateUnits()) {
        RouteResultset rrsTmp = RouterUtil.tryRouteForTables(schema, druidParser.getCtx(), unit, rrs, isSelect(statement), cachePool);
        if (rrsTmp != null) {
            for (RouteResultsetNode node : rrsTmp.getNodes()) {
                nodeSet.add(node);
            }
        }
    }
    RouteResultsetNode[] nodes = new RouteResultsetNode[nodeSet.size()];
    int i = 0;
    for (RouteResultsetNode aNodeSet : nodeSet) {
        nodes[i] = aNodeSet;
        if (statement instanceof MySqlInsertStatement && ctx.getTables().size() == 1 && schema.getTables().containsKey(ctx.getTables().get(0))) {
            RuleConfig rule = schema.getTables().get(ctx.getTables().get(0)).getRule();
            if (rule != null && rule.getRuleAlgorithm() instanceof SlotFunction) {
                aNodeSet.setStatement(ParseUtil.changeInsertAddSlot(aNodeSet.getStatement(), aNodeSet.getSlot()));
            }
        }
        i++;
    }
    rrs.setNodes(nodes);
    /**
		 *  subTables="t_order$1-2,t_order3"
		 *目前分表 1.6 开始支持 幵丏 dataNode 在分表条件下只能配置一个,分表条件下不支持join。
		 */
    if (rrs.isDistTable()) {
        return this.routeDisTable(statement, rrs);
    }
    return rrs;
}
Also used : DruidShardingParseInfo(io.mycat.route.parser.druid.DruidShardingParseInfo) RouteCalculateUnit(io.mycat.route.parser.druid.RouteCalculateUnit) SQLStatementParser(com.alibaba.druid.sql.parser.SQLStatementParser) MycatStatementParser(io.mycat.route.parser.druid.MycatStatementParser) MycatSchemaStatVisitor(io.mycat.route.parser.druid.MycatSchemaStatVisitor) SQLSyntaxErrorException(java.sql.SQLSyntaxErrorException) MySqlInsertStatement(com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement) SQLStatement(com.alibaba.druid.sql.ast.SQLStatement) SQLNonTransientException(java.sql.SQLNonTransientException) SQLSyntaxErrorException(java.sql.SQLSyntaxErrorException) SlotFunction(io.mycat.route.function.SlotFunction) DruidParser(io.mycat.route.parser.druid.DruidParser) TreeSet(java.util.TreeSet) RouteResultsetNode(io.mycat.route.RouteResultsetNode) RuleConfig(io.mycat.config.model.rule.RuleConfig) MySqlStatementParser(com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser) RouteResultset(io.mycat.route.RouteResultset)

Example 2 with MySqlInsertStatement

use of com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement in project Mycat_plus by coderczp.

the class DruidMysqlCreateTableTest method isInsertHasSlot.

private boolean isInsertHasSlot(String sql) {
    MySqlStatementParser parser = new MySqlStatementParser(sql);
    MySqlInsertStatement insertStatement = (MySqlInsertStatement) parser.parseStatement();
    List<SQLExpr> cc = insertStatement.getColumns();
    for (SQLExpr sqlExpr : cc) {
        SQLIdentifierExpr c = (SQLIdentifierExpr) sqlExpr;
        if ("_slot".equalsIgnoreCase(c.getName()) && cc.size() == insertStatement.getValues().getValues().size())
            return true;
    }
    return false;
}
Also used : SQLIdentifierExpr(com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr) MySqlInsertStatement(com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement) MySqlStatementParser(com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser) SQLExpr(com.alibaba.druid.sql.ast.SQLExpr)

Example 3 with MySqlInsertStatement

use of com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement in project Mycat_plus by coderczp.

the class DruidInsertParser method statementParse.

/**
 * 考虑因素:isChildTable、批量、是否分片
 */
@Override
public void statementParse(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt) throws SQLNonTransientException {
    MySqlInsertStatement insert = (MySqlInsertStatement) stmt;
    String tableName = StringUtil.removeBackquote(insert.getTableName().getSimpleName()).toUpperCase();
    ctx.addTable(tableName);
    if (RouterUtil.isNoSharding(schema, tableName)) {
        // 整个schema都不分库或者该表不拆分
        RouterUtil.routeForTableMeta(rrs, schema, tableName, rrs.getStatement());
        rrs.setFinishedRoute(true);
        return;
    }
    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);
    } else {
        // childTable的insert直接在解析过程中完成路由
        if (tc.isChildTable()) {
            parserChildTable(schema, rrs, tableName, insert);
            return;
        }
        String partitionColumn = tc.getPartitionColumn();
        if (partitionColumn != null) {
            // 拆分表必须给出column list,否则无法寻找分片字段的值
            if (insert.getColumns() == null || insert.getColumns().size() == 0) {
                throw new SQLSyntaxErrorException("partition table, insert must provide ColumnList");
            }
            // 批量insert
            if (isMultiInsert(insert)) {
                // String msg = "multi insert not provided" ;
                // LOGGER.warn(msg);
                // throw new SQLNonTransientException(msg);
                parserBatchInsert(schema, rrs, partitionColumn, tableName, insert);
            } else {
                parserSingleInsert(schema, rrs, partitionColumn, tableName, insert);
            }
        }
    }
}
Also used : SQLNonTransientException(java.sql.SQLNonTransientException) SQLSyntaxErrorException(java.sql.SQLSyntaxErrorException) TableConfig(io.mycat.config.model.TableConfig) MySqlInsertStatement(com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement)

Example 4 with MySqlInsertStatement

use of com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement in project Mycat_plus by coderczp.

the class DruidMycatRouteStrategy method directRoute.

/**
 *  直接结果路由
 * @param rrs
 * @param ctx
 * @param schema
 * @param druidParser
 * @param statement
 * @param cachePool
 * @return
 * @throws SQLNonTransientException
 */
private RouteResultset directRoute(RouteResultset rrs, DruidShardingParseInfo ctx, SchemaConfig schema, DruidParser druidParser, SQLStatement statement, LayerCachePool cachePool) throws SQLNonTransientException {
    // 改写sql:如insert语句主键自增长, 在直接结果路由的情况下,进行sql 改写处理
    druidParser.changeSql(schema, rrs, statement, cachePool);
    /**
     * DruidParser 解析过程中已完成了路由的直接返回
     */
    if (rrs.isFinishedRoute()) {
        return rrs;
    }
    /**
     * 没有from的select语句或其他
     */
    if ((ctx.getTables() == null || ctx.getTables().size() == 0) && (ctx.getTableAliasMap() == null || ctx.getTableAliasMap().isEmpty())) {
        return RouterUtil.routeToSingleNode(rrs, schema.getRandomDataNode(), druidParser.getCtx().getSql());
    }
    if (druidParser.getCtx().getRouteCalculateUnits().size() == 0) {
        RouteCalculateUnit routeCalculateUnit = new RouteCalculateUnit();
        druidParser.getCtx().addRouteCalculateUnit(routeCalculateUnit);
    }
    SortedSet<RouteResultsetNode> nodeSet = new TreeSet<RouteResultsetNode>();
    boolean isAllGlobalTable = RouterUtil.isAllGlobalTable(ctx, schema);
    for (RouteCalculateUnit unit : druidParser.getCtx().getRouteCalculateUnits()) {
        RouteResultset rrsTmp = RouterUtil.tryRouteForTables(schema, druidParser.getCtx(), unit, rrs, isSelect(statement), cachePool);
        if (rrsTmp != null && rrsTmp.getNodes() != null) {
            for (RouteResultsetNode node : rrsTmp.getNodes()) {
                nodeSet.add(node);
            }
        }
        if (isAllGlobalTable) {
            // 都是全局表时只计算一遍路由
            break;
        }
    }
    RouteResultsetNode[] nodes = new RouteResultsetNode[nodeSet.size()];
    int i = 0;
    for (RouteResultsetNode aNodeSet : nodeSet) {
        nodes[i] = aNodeSet;
        if (statement instanceof MySqlInsertStatement && ctx.getTables().size() == 1 && schema.getTables().containsKey(ctx.getTables().get(0))) {
            RuleConfig rule = schema.getTables().get(ctx.getTables().get(0)).getRule();
            if (rule != null && rule.getRuleAlgorithm() instanceof SlotFunction) {
                aNodeSet.setStatement(ParseUtil.changeInsertAddSlot(aNodeSet.getStatement(), aNodeSet.getSlot()));
            }
        }
        i++;
    }
    rrs.setNodes(nodes);
    /**
     *  subTables="t_order$1-2,t_order3"
     *目前分表 1.6 开始支持 幵丏 dataNode 在分表条件下只能配置一个,分表条件下不支持join。
     */
    if (rrs.isDistTable()) {
        return this.routeDisTable(statement, rrs);
    }
    return rrs;
}
Also used : RouteCalculateUnit(io.mycat.route.parser.druid.RouteCalculateUnit) TreeSet(java.util.TreeSet) RouteResultsetNode(io.mycat.route.RouteResultsetNode) MySqlInsertStatement(com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement) RuleConfig(io.mycat.config.model.rule.RuleConfig) SlotFunction(io.mycat.route.function.SlotFunction) RouteResultset(io.mycat.route.RouteResultset)

Example 5 with MySqlInsertStatement

use of com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement in project Mycat_plus by coderczp.

the class RouterUtil method processERChildTable.

/**
 * 该方法,返回是否是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));
    }
    // 是否是er表
    boolean erFlag = false;
    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);
                }
                // 继续处理下一条
                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 {
                    FetchStoreNodeOfChildTableHandler fetchHandler = new FetchStoreNodeOfChildTableHandler();
                    // return fetchHandler.execute(schema.getName(), findRootTBSql, tc.getRootParent().getDataNodes());
                    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;
}
Also used : FetchStoreNodeOfChildTableHandler(io.mycat.backend.mysql.nio.handler.FetchStoreNodeOfChildTableHandler) MySqlInsertStatement(com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement) SQLStatement(com.alibaba.druid.sql.ast.SQLStatement) SQLNonTransientException(java.sql.SQLNonTransientException) SQLSyntaxErrorException(java.sql.SQLSyntaxErrorException) SQLNonTransientException(java.sql.SQLNonTransientException) TableConfig(io.mycat.config.model.TableConfig) MySqlStatementParser(com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser) RouteResultset(io.mycat.route.RouteResultset)

Aggregations

MySqlInsertStatement (com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement)47 MySqlStatementParser (com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser)39 SQLStatement (com.alibaba.druid.sql.ast.SQLStatement)33 MySqlSchemaStatVisitor (com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor)14 SQLStatementParser (com.alibaba.druid.sql.parser.SQLStatementParser)8 TableConfig (io.mycat.config.model.TableConfig)8 SQLExpr (com.alibaba.druid.sql.ast.SQLExpr)7 SQLIdentifierExpr (com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr)7 ArrayList (java.util.ArrayList)7 List (java.util.List)7 SQLNonTransientException (java.sql.SQLNonTransientException)6 RouteResultset (io.mycat.route.RouteResultset)5 SQLSyntaxErrorException (java.sql.SQLSyntaxErrorException)5 SQLIntegerExpr (com.alibaba.druid.sql.ast.expr.SQLIntegerExpr)4 ValuesClause (com.alibaba.druid.sql.ast.statement.SQLInsertStatement.ValuesClause)4 RuleConfig (io.mycat.config.model.rule.RuleConfig)3 RouteResultsetNode (io.mycat.route.RouteResultsetNode)3 SlotFunction (io.mycat.route.function.SlotFunction)3 MycatStatementParser (io.mycat.route.parser.druid.MycatStatementParser)3 RouteCalculateUnit (io.mycat.route.parser.druid.RouteCalculateUnit)3