Search in sources :

Example 6 with SQLNonTransientException

use of java.sql.SQLNonTransientException in project Mycat-Server by MyCATApache.

the class DruidUpdateParser method shardColCanBeUpdated.

/*
    * 遍历where子句的AST,寻找是否有与update子句中更新分片字段相同的条件,
    * o 如果发现有or或者xor,然后分片字段的条件在or或者xor中的,这种情况update也无法执行,比如
    *   update mytab set ptn_col = val, col1 = val1 where col1 = val11 or ptn_col = val;
    *   但是下面的这种update是可以执行的
    *   update mytab set ptn_col = val, col1 = val1 where ptn_col = val and (col1 = val11 or col2 = val2);
    * o 如果没有发现与update子句中更新分片字段相同的条件,则update也无法执行,比如
    *   update mytab set ptn_col = val, col1 = val1 where col1 = val11 and col2 = val22;
    * o 如果条件之间都是and,且有与更新分片字段相同的条件,这种情况是允许执行的。比如
    *   update mytab set ptn_col = val, col1 = val1 where ptn_col = val and col1 = val11 and col2 = val2;
    * o 对于一些特殊的运算符,比如between,not,或者子查询,遇到这些子句现在不会去检查分片字段是否在此类子句中,
    *  即使分片字段在此类子句中,现在也认为对应的update语句无法执行。
    *
    * @param whereClauseExpr   where子句的语法树AST
    * @param column   分片字段的名字
    * @param value    分片字段要被更新成的值
    * @hasOR          遍历到whereClauseExpr这个节点的时候,其上层路径中是否有OR/XOR关系运算
    *
    * @return         true,表示update不能执行,false表示可以执行
    */
private boolean shardColCanBeUpdated(SQLExpr whereClauseExpr, String column, SQLExpr value, boolean hasOR) throws SQLNonTransientException {
    boolean canUpdate = false;
    boolean parentHasOR = false;
    if (whereClauseExpr == null)
        return false;
    if (whereClauseExpr instanceof SQLBinaryOpExpr) {
        SQLBinaryOpExpr nodeOpExpr = (SQLBinaryOpExpr) whereClauseExpr;
        /*
            * 条件中有or或者xor的,如果分片字段出现在or/xor的一个子句中,则此update
            * 语句无法执行
             */
        if ((nodeOpExpr.getOperator() == SQLBinaryOperator.BooleanOr) || (nodeOpExpr.getOperator() == SQLBinaryOperator.BooleanXor)) {
            parentHasOR = true;
        }
        // 发现类似 col = value 的子句
        if (nodeOpExpr.getOperator() == SQLBinaryOperator.Equality) {
            boolean foundCol;
            SQLExpr leftExpr = nodeOpExpr.getLeft();
            SQLExpr rightExpr = nodeOpExpr.getRight();
            foundCol = columnInExpr(leftExpr, column);
            // 发现col = value子句,col刚好是分片字段,比较value与update要更新的值是否一样,并且是否在or/xor子句中
            if (foundCol) {
                if (rightExpr.getClass() != value.getClass()) {
                    throw new SQLNonTransientException("SQL AST nodes type mismatch!");
                }
                canUpdate = rightExpr.toString().equals(value.toString()) && (!hasOR) && (!parentHasOR);
            }
        } else if (nodeOpExpr.getOperator().isLogical()) {
            if (nodeOpExpr.getLeft() != null) {
                if (nodeOpExpr.getLeft() instanceof SQLBinaryOpExpr) {
                    canUpdate = shardColCanBeUpdated(nodeOpExpr.getLeft(), column, value, parentHasOR);
                }
            // else
            // 此子语句不是 =,>,<等关系运算符(对应的类是SQLBinaryOpExpr)。比如between X and Y
            // 或者 NOT,或者单独的子查询,这些情况,我们不做处理
            }
            if ((!canUpdate) && nodeOpExpr.getRight() != null) {
                if (nodeOpExpr.getRight() instanceof SQLBinaryOpExpr) {
                    canUpdate = shardColCanBeUpdated(nodeOpExpr.getRight(), column, value, parentHasOR);
                }
            // else
            // 此子语句不是 =,>,<等关系运算符(对应的类是SQLBinaryOpExpr)。比如between X and Y
            // 或者 NOT,或者单独的子查询,这些情况,我们不做处理
            }
        } else if (isSubQueryClause(nodeOpExpr)) {
            // 对于子查询的检查有点复杂,这里暂时不支持
            return false;
        }
    // else
    // 其他类型的子句,忽略, 如果分片字段在这类子句中,此类情况目前不做处理,将返回false
    }
    return canUpdate;
}
Also used : SQLNonTransientException(java.sql.SQLNonTransientException) SQLExpr(com.alibaba.druid.sql.ast.SQLExpr)

Example 7 with SQLNonTransientException

use of java.sql.SQLNonTransientException in project Mycat-Server by MyCATApache.

the class FunctionParser method parseFunction.

public static Function parseFunction(String function) throws SQLNonTransientException {
    StringBuilder buffer = new StringBuilder();
    Stack<Function> functions = new Stack<>();
    int flag = 0;
    for (int i = 0; i < function.length(); i++) {
        char current = function.charAt(i);
        switch(current) {
            case Commons.LEFT_BRACKET:
                if (flag == 0) {
                    String currentIdentifier = buffer.toString().trim();
                    buffer = new StringBuilder();
                    if (!StringUtil.isEmpty(currentIdentifier)) {
                        Function function1 = new Function(currentIdentifier);
                        if (!functions.empty() && functions.peek() != null) {
                            functions.peek().getArguments().add(function1);
                        }
                        functions.push(function1);
                    }
                    break;
                }
                buffer.append(current);
                break;
            case Commons.ARGUMENT_SEPARATOR:
                if (flag == 0 || flag == 3) {
                    String currentIdentifier = buffer.toString().trim();
                    buffer = new StringBuilder();
                    if (!StringUtil.isEmpty(currentIdentifier)) {
                        if (flag == 3) {
                            flag = 0;
                            Identifier identifier = new Identifier(currentIdentifier);
                            functions.peek().getArguments().add(identifier);
                        } else {
                            Field field = new Field(currentIdentifier);
                            functions.peek().getArguments().add(field);
                        }
                    }
                    break;
                }
                buffer.append(current);
                break;
            case Commons.RIGHT_BRACKET:
                if (flag != 1 && flag != 2) {
                    String currentIdentifier = buffer.toString().trim();
                    buffer = new StringBuilder();
                    if (!StringUtil.isEmpty(currentIdentifier)) {
                        if (flag == 3) {
                            flag = 0;
                            Identifier identifier = new Identifier(currentIdentifier);
                            functions.peek().getArguments().add(identifier);
                        } else {
                            Field field = new Field(currentIdentifier);
                            functions.peek().getArguments().add(field);
                        }
                    }
                    if (flag == 0) {
                        if (functions.size() == 1) {
                            return functions.pop();
                        } else {
                            functions.pop();
                        }
                    }
                    break;
                }
                buffer.append(current);
                break;
            case Commons.QUOTE:
                if (flag == 0) {
                    flag = 1;
                } else if (flag == 1) {
                    flag = 3;
                }
            case Commons.DOUBLE_QUOTE:
                if (flag == 0) {
                    flag = 2;
                } else if (flag == 2) {
                    flag = 3;
                }
            default:
                buffer.append(current);
        }
    }
    throw new SQLNonTransientException("Function is not in right format!");
}
Also used : Function(io.mycat.route.parser.primitive.Model.Function) Field(io.mycat.route.parser.primitive.Model.Field) SQLNonTransientException(java.sql.SQLNonTransientException) Identifier(io.mycat.route.parser.primitive.Model.Identifier) Stack(java.util.Stack)

Example 8 with SQLNonTransientException

use of java.sql.SQLNonTransientException 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 9 with SQLNonTransientException

use of java.sql.SQLNonTransientException in project Mycat-Server by MyCATApache.

the class RouterUtil method ruleByJoinValueCalculate.

/**
	 * @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);
        //			}
        return retNodeSet;
    } else {
        retNodeSet.addAll(tc.getParentTC().getDataNodes());
    }
    return retNodeSet;
}
Also used : SQLNonTransientException(java.sql.SQLNonTransientException) ColumnRoutePair(io.mycat.sqlengine.mpp.ColumnRoutePair)

Example 10 with SQLNonTransientException

use of java.sql.SQLNonTransientException in project Mycat-Server by MyCATApache.

the class RouterUtil method processERChildTable.

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);
    //判断是否为子表,如果不是,只会返回false
    if (null != tc && tc.isChildTable()) {
        final RouteResultset rrs = new RouteResultset(origSQL, 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);
        }
        String sql = insertStmt.toString();
        // 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;
        }
        // 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());
        return true;
    }
    return false;
}
Also used : FetchStoreNodeOfChildTableHandler(io.mycat.backend.mysql.nio.handler.FetchStoreNodeOfChildTableHandler) MySqlInsertStatement(com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement) 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

SQLNonTransientException (java.sql.SQLNonTransientException)49 Test (org.testng.annotations.Test)19 BaseTest (util.BaseTest)19 TableConfig (io.mycat.config.model.TableConfig)12 RouteResultset (io.mycat.route.RouteResultset)8 SQLException (java.sql.SQLException)8 RouteResultsetNode (io.mycat.route.RouteResultsetNode)7 ColumnRoutePair (io.mycat.sqlengine.mpp.ColumnRoutePair)6 SlotFunction (io.mycat.route.function.SlotFunction)5 MySqlStatementParser (com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser)4 AbstractPartitionAlgorithm (io.mycat.route.function.AbstractPartitionAlgorithm)4 RouteCalculateUnit (io.mycat.route.parser.druid.RouteCalculateUnit)4 SQLExpr (com.alibaba.druid.sql.ast.SQLExpr)3 SQLStatement (com.alibaba.druid.sql.ast.SQLStatement)3 MySqlInsertStatement (com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement)3 SchemaConfig (io.mycat.config.model.SchemaConfig)3 SQLSyntaxErrorException (java.sql.SQLSyntaxErrorException)3 SchemaConfig (com.alibaba.cobar.config.model.SchemaConfig)2 SQLCharExpr (com.alibaba.druid.sql.ast.expr.SQLCharExpr)2 MySqlUpdateStatement (com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement)2