Calcite针对SQL parse提供了很多的配置项,可以针对不同的SQL方言进行解析。相关的配置项都存储在SqlParser.Config这个结构中,常见的用法如下所示:
SqlParser.Config config = SqlParser.config();
String sql = xxx;
SqlParser sqlParser = SqlParser.create(sql, config);
SqlNode sqlNode = sqlParser.parseStmt();
最终,我们就可以将一个字符串的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);
可以看到,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;
}
}
使用方法如下所示:
SqlParser.Config config = SqlParser.config().withQuoting(Quoting.BACK_TICK)
此时,我们就可以针对列名、表名等,使用反引号包围起来,如下所示:
select `c1` from `t`
select `c1` as `id` from t
select c1,`sum`(c2) from t group by c1
上面的三种值,分别表示双引号、反引号和括号。因此,如果我们设置quoting为BACK_TICK,那么使用双引号则会报错,如下所示:
select "c1" from "t"
select c1 as "id" from t
select c1,"sum"(c2) from t group by c1
相关的结构如下所示:
/** Policy for converting case of identifiers before storing them.
*
* <p>A database often has policies for quoted versus unquoted identifiers.
* For example, Oracle converts unquoted identifiers to upper-case, but
* quoted identifiers are unchanged.</p> */
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
}
这是针对引用标识符可以设置是否进行大小写转换,通过SqlParser.Config的两个方法可以进行设置,如下所示:
//针对使用了引用标识符包围的列、表名等,进行大小写转换
Config withQuotedCasing(Casing casing);
//针对没有引用标识符包围的列、表名等,进行大小写转换
Config withUnquotedCasing(Casing casing);
我们看如下所示的例子:
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());
最终输出的SQL如下所示:
SELECT `Col1`, SUM(`COL2`)
FROM `T`
GROUP BY `COL1`
可以看到,被反引号包围的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
}
这里指的主要就是字符串的格式,包括转义字符,例如STANDARD的格式就是单引号包围,如果字符串包含单引号,则使用单引号进行转移,如下所示:
// Config配置如下:
SqlParser.config().withCharLiteralStyles(ImmutableSet.of(CharLiteralStyle.STANDARD))
// 解析成功
select 'I''m super man'
// 解析失败
select 'I\'m super man'
select "I''m super man"
而BQ_SINGLE和BQ_DOUBLE分别表示使用单引号和双引号来包围字符串,但是转义符号用的则是反斜杠,这两种格式是BigQuery的语法。
除了上述的配置项,SqlParser.Config还提供了一些额外的配置,如下所示:
// 匹配时,大小写是否敏感
Config withCaseSensitive(boolean caseSensitive);
// 标识符最大长度
Config withIdentifierMaxLength(int identifierMaxLength);
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),
// 省略其余部分代码
这里设置的配置项就是我们上面提到的几种,例如我们要创建BigQuery的语法,可以这样使用:
SqlParser.Config config = SqlParser.config().withLex(Lex.BIG_QUERY);
除了上面提到的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;
}
}
这个枚举里面也定义了一系列的常用方言的SQL行为,如下所示:
public enum SqlConformanceEnum implements SqlConformance {
DEFAULT,
LENIENT,
BABEL,
// 省略其余部分代码
因此,我们通常就可以结合上面的Lex来使用,如下所示:
SqlParser.Config config = SqlParser.config()
.withLex(Lex.BIG_QUERY)
.withConformance(SqlConformanceEnum.BIG_QUERY);
基本到这里Calcite得parser config的配置就基本已经设置介绍完了。通过上面的介绍我们可以发现,Calcite提供了比较多的配置项组合,可以解析不同的SQL方言,还是很强大的。除了parse,Calcite还有一个unparse的过程,可以将RelNode转换成不同方言的sql,后续有时间再做介绍。