• Calcite parser config介绍


    Calcite针对SQL parse提供了很多的配置项,可以针对不同的SQL方言进行解析。相关的配置项都存储在SqlParser.Config这个结构中,常见的用法如下所示:

    SqlParser.Config config = SqlParser.config();
    String sql = xxx;
    SqlParser sqlParser = SqlParser.create(sql, config);
    SqlNode sqlNode = sqlParser.parseStmt();
    
    • 1
    • 2
    • 3
    • 4

    最终,我们就可以将一个字符串的sql,转换成一个SqlNode,这是一个Calcite中抽象语法树的代码标识。而这个config()就是Calcite默认提供的一个配置集合,如下所示:

    public static Config config() {
      return Config.DEFAULT;
    }
    
    /** Default configuration. */
    Config DEFAULT = ImmutableBeans.create(Config.class)
        .withLex(Lex.ORACLE)
        .withIdentifierMaxLength(DEFAULT_IDENTIFIER_MAX_LENGTH)
        .withConformance(SqlConformanceEnum.DEFAULT)
        .withParserFactory(SqlParserImpl.FACTORY);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    可以看到,Calcite默认设置了四个属性,除了这四个,还有其他的属性也可以设置,下面我们就看一下常用的一些配置项。

    引用标识符

    相关的结构如下所示:

    /** Syntax for quoting identifiers in SQL statements. */
    public enum Quoting {
      /** Quote identifiers in double-quotes. For example, {@code "my id"}. */
      DOUBLE_QUOTE("\""),
    
      /** Quote identifiers in back-quotes. For example, {@code `my id`}. */
      BACK_TICK("`"),
    
      /** Quote identifiers in brackets. For example, {@code [my id]}. */
      BRACKET("[");
    
      public String string;
    
      Quoting(String string) {
        this.string = string;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    使用方法如下所示:

    SqlParser.Config config = SqlParser.config().withQuoting(Quoting.BACK_TICK)
    
    • 1

    此时,我们就可以针对列名、表名等,使用反引号包围起来,如下所示:

    select `c1` from `t`
    select `c1` as `id` from t
    select c1,`sum`(c2) from t group by c1
    
    • 1
    • 2
    • 3

    上面的三种值,分别表示双引号、反引号和括号。因此,如果我们设置quoting为BACK_TICK,那么使用双引号则会报错,如下所示:

    select "c1" from "t"
    select c1 as "id" from t
    select c1,"sum"(c2) from t group by c1
    
    • 1
    • 2
    • 3

    标识符大小写转换

    相关的结构如下所示:

    /** Policy for converting case of identifiers before storing them.
     *
     * 

    A database often has policies for quoted versus unquoted identifiers. * For example, Oracle converts unquoted identifiers to upper-case, but * quoted identifiers are unchanged.

    */ public enum Casing { /** The case of identifiers is not changed. */ UNCHANGED, /** Identifiers are converted to upper-case. */ TO_UPPER, /** Identifiers are converted to lower-case. */ TO_LOWER }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这是针对引用标识符可以设置是否进行大小写转换,通过SqlParser.Config的两个方法可以进行设置,如下所示:

    //针对使用了引用标识符包围的列、表名等,进行大小写转换
    Config withQuotedCasing(Casing casing);
    //针对没有引用标识符包围的列、表名等,进行大小写转换
    Config withUnquotedCasing(Casing casing);
    
    • 1
    • 2
    • 3
    • 4

    我们看如下所示的例子:

    SqlParser.Config config = SqlParser.config()
      .withQuoting(Quoting.BACK_TICK)
      .withQuotedCasing(Casing.UNCHANGED)
      .withUnquotedCasing(Casing.TO_UPPER);
     
    String sql = "select `Col1`,sum(col2) from t group by Col1";
    SqlParser sqlParser = SqlParser.create(sql, config);
    SqlNode sqlNode = sqlParser.parseQuery();
    System.out.println(sqlNode.toString());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    最终输出的SQL如下所示:

    SELECT `Col1`, SUM(`COL2`)
    FROM `T`
    GROUP BY `COL1`
    
    • 1
    • 2
    • 3

    可以看到,被反引号包围的Col1保持了大小写不变,而没有标识符包围的col2和Col1则都被转换成了大写。

    字符常量值格式

    格式如下所示:

    /** Styles of character literal.
     *
     * @see Lex#charLiteralStyles */
    public enum CharLiteralStyle {
      /** Standard character literal. Enclosed in single quotes, using single quotes
       * to escape. Example: {@code 'Won''t'}. */
      STANDARD,
      /** Single-quoted character literal with backslash escapes, as in BigQuery.
       * Example: {@code 'Won\'t'}. */
      BQ_SINGLE,
      /** Double-quoted character literal with backslash escapes, as in BigQuery.
       * Example: {@code "Won\'t"}. */
      BQ_DOUBLE
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    这里指的主要就是字符串的格式,包括转义字符,例如STANDARD的格式就是单引号包围,如果字符串包含单引号,则使用单引号进行转移,如下所示:

    // Config配置如下:
    SqlParser.config().withCharLiteralStyles(ImmutableSet.of(CharLiteralStyle.STANDARD))
    // 解析成功
    select 'I''m super man'
    // 解析失败
    select 'I\'m super man'
    select "I''m super man"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    而BQ_SINGLE和BQ_DOUBLE分别表示使用单引号和双引号来包围字符串,但是转义符号用的则是反斜杠,这两种格式是BigQuery的语法。

    其他配置

    除了上述的配置项,SqlParser.Config还提供了一些额外的配置,如下所示:

    // 匹配时,大小写是否敏感
    Config withCaseSensitive(boolean caseSensitive);
    // 标识符最大长度
    Config withIdentifierMaxLength(int identifierMaxLength);
    
    • 1
    • 2
    • 3
    • 4

    使用模板进行配置

    语法特性Lex

    Calcite针对当前主流的一些方言,构造了专门的模板,我们可以使用这些模板快速创建对应的config,如下所示:

    public enum Lex {
      BIG_QUERY(Quoting.BACK_TICK, Casing.UNCHANGED, Casing.UNCHANGED, true,
          CharLiteralStyle.BQ_SINGLE, CharLiteralStyle.BQ_DOUBLE),
          
      ORACLE(Quoting.DOUBLE_QUOTE, Casing.TO_UPPER, Casing.UNCHANGED, true,
          CharLiteralStyle.STANDARD),
      // 省略其余部分代码
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这里设置的配置项就是我们上面提到的几种,例如我们要创建BigQuery的语法,可以这样使用:

    SqlParser.Config config = SqlParser.config().withLex(Lex.BIG_QUERY);
    
    • 1
    SqlConformance

    除了上面提到的Lex,还有一个与之搭配使用的变量就是SqlConformanceEnum,这个枚举里面定义了一系列的SQL行为模式,例如是否支持group by alias,group by ordinal等,如下所示:

    public boolean isGroupByAlias() {
      switch (this) {
      case BABEL:
      case LENIENT:
      case BIG_QUERY:
      case MYSQL_5:
        return true;
      default:
        return false;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这个枚举里面也定义了一系列的常用方言的SQL行为,如下所示:

    public enum SqlConformanceEnum implements SqlConformance {
      DEFAULT,
      LENIENT,
      BABEL,
      // 省略其余部分代码
    
    • 1
    • 2
    • 3
    • 4
    • 5

    因此,我们通常就可以结合上面的Lex来使用,如下所示:

    SqlParser.Config config = SqlParser.config()
      .withLex(Lex.BIG_QUERY)
      .withConformance(SqlConformanceEnum.BIG_QUERY);
    
    • 1
    • 2
    • 3

    小结

    基本到这里Calcite得parser config的配置就基本已经设置介绍完了。通过上面的介绍我们可以发现,Calcite提供了比较多的配置项组合,可以解析不同的SQL方言,还是很强大的。除了parse,Calcite还有一个unparse的过程,可以将RelNode转换成不同方言的sql,后续有时间再做介绍。

  • 相关阅读:
    整合easy-es后。调接口能返回,但是一直会报错
    链式队列的基本操作与实现(数据结构与算法)
    【Svelte】-(4)If 条件判断语句 / Each 循环语句 / Await 异步处理块
    【C++】vector笔记+模拟实现
    架构权衡分析ATAM-架构真题(六十)
    JVM——内存区域与内存溢出
    网络安全 记录
    Guava中这些Map的骚操作,让我的代码量减少了50%
    JavaWeb(文件上传 异步文件上传 文件下载)
    Hive数据表操作--学习笔记
  • 原文地址:https://blog.csdn.net/skyyws/article/details/127919547