SIGs|ParseEngine 小组介绍

小组介绍

ParseEngine 小组主要负责 Apache ShardingSphere 社区 SQL 解析引擎的开发和维护

SQL 解析引擎是 Apache ShardingSphere 项目的基石,负责对用户输入的 SQL 进行解析,具体包括了词法分析、语法分析和 AST 遍历三个阶段,最终得到 SQLStatement 上下文对象。目前,SQL 解析引擎已经支持了 MySQL、PostgreSQL、Oracle、SQLServer、openGauss 以及其他遵循 SQL92 标准的多种数据库方言,其中 MySQL 和 PostgreSQL 的支持比较完善,其他数据库方言还在不断提升。ParseEngine 小组的主要目标是持续提升不同数据库方言的支持度,对于主流数据库,SQL 解析引擎要达到 100% 兼容,在此基础上,不断优化 SQL 解析引擎的性能,提升 SQL 解析引擎的用户体验。

核心成员

核心成员由组长提名,组员投票赞成通过

加入小组

只需点击左下角小铃铛,选择【关注】话题,并留下一条有趣的回复,即可快速加入小组。

加入小组后:

  • 及时收到小组消息(任务发布、话题讨论、会议信息)

  • 深度讨论技术话题,以最快的形式融入社区

  • 有机会成为核心成员,直接以语音的方式参与小组会议,共同探讨小组发展方向

1 个赞

go!go!go!

欢迎欢迎 :smiley::clap::fireworks:

不光是 ShardingSphere,对很多同类产品甚至数据库来说,解析都是最核心的功能之一。向大神们学习。

嗯嗯,确实,解析也是全面了解shardingSphere很好的入口呀 :v:

我们刚刚发布了一个新手任务,欢迎认领

:smiley:追随大佬们的脚步啦

欢迎大佬 :smiley: :v:

1 个赞

跟着大佬学习 :grimacing:

大佬们,antlr4解析长sql,例如:insert … values(),(),()…();这种语句很慢,有考虑在解析引擎里做优化吗?

嗯嗯,目前也在考虑 antlr4 解析的性能优化,不过还没开始推进。你那边如果有好的想法,欢迎提交代码 :smiley:

嗯,我写了个demo,基本想法是这样的,当sql长度大于1K且是insert语句,我就转到另外一个方法里去处理;这个方法采用druid解析,把druid解析的结果转成antlr4解析的结果,再交给sharding去处理;但是我对sharding代码很不熟悉,我觉得适配性会很差 :upside_down_face:,但目前在公司项目里测试也还够用 :joy:,比原先的快8到10倍,解析时间。
代码如下:

public final class SQLStatementParserExecutor {
    //......
    public SQLStatement parse(final String sql) {
        if (sql.length() > 1024 && ("insert".equals(sql.trim().substring(0, 6)) || "INSERT".equals(sql.trim().substring(0, 6))) ) {
            log.info("你进入的是Druid SQL parser");
            return druidPar(sql);
        }
        SQLStatement sqlStatement =  visitorEngine.visit(parserEngine.parse(sql, false));
        return visitorEngine.visit(parserEngine.parse(sql, false));
    }

public SQLStatement druidPar(final String sql) {

        //druid
        MySqlStatementParser druidParser = new MySqlStatementParser(sql);
        com.powersi.sqlparser.sql.ast.SQLStatement druidSQLStatement = druidParser.parseStatement();
        MySqlInsertStatement insert = (MySqlInsertStatement)druidSQLStatement;
        SQLExprTableSource tableSource = insert.getTableSource();
        String tableName = insert.getTableSource().getTableName();
        List<SQLExpr> columns = insert.getColumns();
        //  子查询
        SQLSelect query = insert.getQuery();
        //如果有子查询,则全部交给sharding
        if (query != null){
            return visitorEngine.visit(parserEngine.parse(sql, false));
        }

        List<SQLInsertStatement.ValuesClause> valuesList = insert.getValuesList();
        List<SQLExpr> values = valuesList.get(0).getValues();

        MySQLInsertStatement insertStatement = new MySQLInsertStatement();

        //替换setAssignment
        replaceAssignment(insertStatement);
        //替换setOnDuplicateKeyColumns
        replaceOnDuplicatekeyColumns(insertStatement);

        //替换table
        replaceTable(tableSource, tableName, insertStatement);

        //替换insertColumns
        replaceInsertColumns(insert, columns, insertStatement);

        //替换insertSelect
        replaceInsertSelect(insertStatement);

        //替换values
        replaceValues(valuesList, values, insertStatement);

        //替换parameterCount
        replaceParameterCount(insertStatement);

        //替换commentSegments
        replaceCommentSegments(insertStatement);

        return insertStatement;

    }


//替换values
    private void replaceValues(List<SQLInsertStatement.ValuesClause> valuesList, List<SQLExpr> values, MySQLInsertStatement insertStatement) {
        int valuesListSize = valuesList.size();
        int valuesSize = values.size();
        List<List<ExpressionSegment>> expressionSegmentsList = new LinkedList<>();
        ExpressionSegment expressionSegment = null;
        //记录起始位置
        int valuesStartIndex;
        //记录结束位置
        int valuesStopIndex;
        InsertValuesSegment insertValuesSegment = null;
        //遍历插入的记录
        for (int i = 0; i < valuesListSize; i++) {
            valuesStartIndex = valuesList.get(i).getStartIndex();
            valuesStopIndex = valuesList.get(i).getEndIndex();
            List<ExpressionSegment> expressionSegments = new LinkedList<>();
            //遍历每条插入记录里的字段值
            for (int j = 0;j < valuesSize;j++){
                expressionSegments.add(null);
                SQLExpr sqlExpr = valuesList.get(i).getValues().get(j);
                if (sqlExpr instanceof SQLNullExpr){
                    expressionSegment =  new CommonExpressionSegment(sqlExpr.getStartIndex(), sqlExpr.getEndIndex(), sqlExpr.toString());
                } else if (sqlExpr instanceof SQLValuableExpr){
                    expressionSegment =  new LiteralExpressionSegment(sqlExpr.getStartIndex(), sqlExpr.getEndIndex(), ((SQLValuableExpr) sqlExpr).getValue());
                } else {
                    throw new RuntimeException("这个值是未知类型-->" + "valuesListSize:" + i + "valuesSize:" + j );
                }
                expressionSegments.set(j,expressionSegment);
            }
            expressionSegmentsList.add(expressionSegments);
            insertValuesSegment = new InsertValuesSegment(valuesStartIndex, valuesStopIndex, expressionSegmentsList.get(i));
            insertStatement.getValues().add(insertValuesSegment);
        }
    }


//替换insertColumns
    private void replaceInsertColumns(MySqlInsertStatement insert, List<SQLExpr> columns, MySQLInsertStatement insertStatement) {
        int columnsStartIndex;
        int columnsStopIndex;
        LinkedList<ColumnSegment> columnList = null;
        if (columns.size() == 0){
            columnList = new LinkedList<>();
            columnsStartIndex = columnsStopIndex = insert.getColumnIndex(VALUES) - 1;
        }else {
            ColumnSegment column = null;
            columnList = new LinkedList<>();
            //左括号的位置
            columnsStartIndex = insert.getColumnIndex(LPAREN);
            //右括号的位置
            columnsStopIndex = insert.getColumnIndex(RPAREN);
            for (SQLExpr sqlExpr : columns) {
                //判断字段名是否带了反引号
                if (sqlExpr.isBackticks()){
                    column = new ColumnSegment(sqlExpr.getStartIndex(), sqlExpr.getEndIndex(), new IdentifierValue(sqlExpr.toString(), QuoteCharacter.BACK_QUOTE));
                }else {
                    column = new ColumnSegment(sqlExpr.getStartIndex(), sqlExpr.getEndIndex(), new IdentifierValue(sqlExpr.toString(), QuoteCharacter.NONE));
                }
                columnList.add(column);
            }
        }
        InsertColumnsSegment insertColumnsSegment = new InsertColumnsSegment(columnsStartIndex, columnsStopIndex, columnList);
        insertStatement.setInsertColumns(insertColumnsSegment);
    }



//替换table
    private void replaceTable(SQLExprTableSource tableSource, String tableName, MySQLInsertStatement insertStatement) {
        int tableStartIndex = tableSource.getExpr().getStartIndex();
        int tableStopIndex = tableSource.getExpr().getEndIndex();

        //  判断表名是否带了反引号
        if (tableSource.getExpr().isBackticks()){
            IdentifierValue table = new IdentifierValue(tableName, QuoteCharacter.BACK_QUOTE);
            TableNameSegment tableNameSegment = new TableNameSegment(tableStartIndex,tableStopIndex,table);
            SimpleTableSegment simpleTableSegment = new SimpleTableSegment(tableNameSegment);
            insertStatement.setTable(simpleTableSegment);
        }else {
            IdentifierValue table = new IdentifierValue(tableName, QuoteCharacter.NONE);
            TableNameSegment tableNameSegment = new TableNameSegment(tableStartIndex,tableStopIndex,table);
            SimpleTableSegment simpleTableSegment = new SimpleTableSegment(tableNameSegment);
            insertStatement.setTable(simpleTableSegment);
        }
    }

1 个赞

druid parse 在最开始的 shardingsphere 版本中被使用,不过后面为了提升兼容性,就放弃了,改用 antlr4 了。 druid 的性能确实会好很多,不过这对于shardingsphere来说可能不是一个好的方法,可能还是主要考虑 如何提升 antlr4 的性能。

1 个赞

有什么好的想法吗?我觉得,由于antlr4的特性,在面对长字符串时,不管它怎么优化,都不太可能像druid一样快 :upside_down_face:,但是,我也喜欢antlr4优雅的g4语法和它的超强兼容性 :grinning:

哈哈,还没开始这一块的内容,由于跟 druid 的方式不同,所以肯定是不可能达到跟 druid 一样快的。不过应该是还有不小的性能提升空间。如果你有好的想法或者有兴趣,也可以尝试改造。 :smiley:

论坛的小伙伴们,目前有一些任务大家有空可以长期参与 :grinning:
对Oracle 的 antlr4 语法进行进行校对和扩充。具体可以参见这个issue,在上诉 issue 回复即可参与。
对 sqlServer 感兴趣的也可以参与这个 issue

parseEngine 维护了新的一批任务列表,有兴趣的小伙伴欢迎查看认领。

本次新增了两个任务 校对并扩充 mysql 和 postgreSQL 的 antlr4 语法,感兴趣的小伙伴欢迎留言认领。

对pg的方言支持是不是越来越完善了呢

嗯,是的,目前 pg 和 mysql 的支持度是最好的,社区也在持续努力

京ICP备2021015875号