• 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

初探jsqlparser-sql解析器(二)

大数据
2023-07-27 06:55:30
38
0

初探jsqlparser-sql解析器(二)

在大数据系统中,用户错误的sql输入,往往会给后端系统带来压力,更有甚者,会拖垮系统,为防止错误输入,又不想让用户进行过多操作,则需要包容用户的错误,对其输入进行适当的改写。

在《初探jsqlparser》一文中,我们通过使用jsqlparser提取出了sql的信息,那能否在此基础上对sql语句进行改写呢?答案是肯定的。
在把sqlL语句解析成ast(抽象语法树)后,我们通过替换其中的节点元素,便可以实现改写sql语句。

jsqlparser代码结构

jsqlparser源码结构如下:

想要改写select语句,则重点关注schema包下的类,以及statement.select包下的类(其它语句对应具体的包)

-schema包主要包括了SQL语句中的零部件,如库、表、列、参数等元素

-statement.select包则包括了SQL语句中的表达,如order By表达,group By表达等

示例1:替换表名

SQL语句中的表名主要在SelectBody的FromItem表达中(查看源码可知,join表达中也包含FromItem表达),替换表名只需把新的表名set到FromItem即可,如下:

CCJSqlParserManager pm = new CCJSqlParserManager(); Statement statement = pm.parse(new StringReader(sql)); if(statement instanceof Select) { Select select = (Select) statement; PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); plainSelect.setFromItem(new Table("sc_example","replace_table1").withAlias(new Alias("t1"))); System.out.println(statement.toString());

示例2:替换查询列

查询列为SelectBody的SelectItem,通过替换List<SelectItem>,可达到替换查询列的效果,如下,把select * 替换成具体列名:

        List<String> fields = new ArrayList<>();  //具体列名
        fields.add("a");
        fields.add("b");
        fields.add("c");
        CCJSqlParserManager pm = new CCJSqlParserManager();
        Statement statement = pm.parse(new StringReader(sql));
        if(statement instanceof Select) {
            Select select = (Select) statement;
            PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
            List<SelectItem> selectItems = plainSelect.getSelectItems();
            List<SelectItem> newSelectItems = selectItems.stream()
                    .flatMap(t -> {
                        if(t instanceof AllColumns) {  //*转成具体fields
                            return fields.stream().map(field ->
                                    new SelectExpressionItem()
                                            .withExpression(new Column(field)));
                        }else if(t instanceof AllTableColumns){  //table.*转成具体fields
                            return fields.stream().map(field ->
                                    new SelectExpressionItem()
                                            .withExpression(new Column(((AllTableColumns) t).getTable(), field)));
                        }else{
                            return Stream.of(t);
                    .collect(Collectors.toList());
            plainSelect.setSelectItems(newSelectItems);
        System.out.println(statement.toString());

示例3:增加where条件

where条件被解析为SelectBody中的Where表达。

若需替换的where条件过长,一一组装where条件较为麻烦,可利用CCJSqlParserUtil.parseExpression(String expression)方法,直接把String转为Expression,如下:

String str = "last_time >= '2023-07-25 00:00:00' and last_time < '2023-07-27 00:00:00'"
       + " AND " +
       "first_time <= '2023-07-25 00:00:00' and first_time < '2023-07-27 00:00:00'";
Expression addExp = CCJSqlParserUtil.parseExpression(str);

当语句中已有where条件,不希望替换原有条件,而是想添加条件,则需要用到AndExpression或者OrExpression拼装,注:为防止拼上去的条件影响到源条件,需用()把加上去的条件和原条件分别包起来,此时需用到Parenthesis,如下:

plainSelect.setWhere(new OrExpression(new Parenthesis(addExp), new Parenthesis(where)));  //or
plainSelect.setWhere(new AndExpression(new Parenthesis(addExp), new Parenthesis(where)));   //and

整体代码如下:

CCJSqlParserManager pm = new CCJSqlParserManager(); Statement statement = pm.parse(new StringReader(sql)); if(statement instanceof Select) { Select select = (Select) statement; PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); String str = "last_time >= '2023-07-25 00:00:00' and last_time < '2023-07-27 00:00:00'" + " AND " + "first_time <= '2023-07-25 00:00:00' and first_time < '2023-07-27 00:00:00'"; Expression addExp = CCJSqlParserUtil.parseExpression(str); Expression where = plainSelect.getWhere(); if(null != where){ // plainSelect.setWhere(new OrExpression(new Parenthesis(addExp), new Parenthesis(where))); //or plainSelect.setWhere(new AndExpression(new Parenthesis(addExp), new Parenthesis(where))); //and }else{ plainSelect.setWhere(addExp); System.out.println(statement.toString());

示例4:增加排序语句

同样,通过修改SelectBody中的orderByElement,可以为SQL语句增加order表达,如下

CCJSqlParserManager pm = new CCJSqlParserManager(); Statement statement = pm.parse(new StringReader(sql)); if(statement instanceof Select) { Select select = (Select) statement; PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); plainSelect.addOrderByElements(new OrderByElement() .withExpression(new Column("last_time")) .withAsc(false));