一、概述

ExtendedParser 用于在不增加 CalciteParser 复杂性的前提下(不用修改Calcite,增加新的关键字),让 Flink SQL 支持更多专用的语法。

二、实现

2.1. 策略

https://www.jianshu.com/p/e4956652cfcb

ExtendedParseStrategy

ExtendedParser 包含如下解析策略:

1
2
3
4
5
6
7
private static final List<ExtendedParseStrategy> PARSE_STRATEGIES =
Arrays.asList(
ClearOperationParseStrategy.INSTANCE,
HelpOperationParseStrategy.INSTANCE,
QuitOperationParseStrategy.INSTANCE,
ResetOperationParseStrategy.INSTANCE,
SetOperationParseStrategy.INSTANCE);

这 5 条策略分别对应:

  1. CLEAR 语句,清空输出
  2. HELP 语句,打印帮助信息
  3. EXITQUIT 语句,退出执行环境
  4. RESET 语句,重设一个变量的值
  5. SET 语句,设置一个变量的值

2.1.1. SetOperationParseStrategy

SET 语句解析为 operation

SetOperationParseStrategy 继承了 AbstractRegexParseStrategy,它包含 statement 正则匹配的逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 默认的正则匹配方式,忽略大小写,点可以匹配行结束标志
protected static final int DEFAULT_PATTERN_FLAGS = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;

// 这个pattern的用于匹配语句,如果语句和pattern匹配,则使用这个strategy解析
protected Pattern pattern;

protected AbstractRegexParseStrategy(Pattern pattern) {
this.pattern = pattern;
}

// 用于验证语句是否和pattern匹配的方法
@Override
public boolean match(String statement) {
return pattern.matcher(statement.trim()).matches();
}

紧接着我们分析SetOperationParseStrategy。SET语句的pattern如下:

1
2
3
4
5
6
protected SetOperationParseStrategy() {
super(
Pattern.compile(
"SET(\\s+(?<key>[^'\\s]+)\\s*=\\s*('(?<quotedVal>[^']*)'|(?<val>\\S+)))?",
DEFAULT_PATTERN_FLAGS));
}

从正则表达式可知,SET语句的格式为:SET key=’quotedVal’或者SET key=val。

转换SET语句为Operation的过程位于convert方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Override
public Operation convert(String statement) {
// 匹配statement
Matcher matcher = pattern.matcher(statement.trim());
// 创建保存操作符的集合
final List<String> operands = new ArrayList<>();
if (matcher.find()) {
if (matcher.group("key") != null) {
// 获取key和对应的quotedVal或者val,加入操作符集合
operands.add(matcher.group("key"));
operands.add(
matcher.group("quotedVal") != null
? matcher.group("quotedVal")
: matcher.group("val"));
}
}

// only capture SET
// 如果只有SET个单词,operands为空,创建一个空的SetOperation
if (operands.isEmpty()) {
return new SetOperation();
} else if (operands.size() == 2) {
// 如果operands的大小为2,说明解析到了key和val或者quotedVal,创建SetOperation
return new SetOperation(operands.get(0), operands.get(1));
} else {
// impossible
// 其他情况,抛出异常
throw new TableException(
String.format(
"Failed to convert the statement to SET operation: %s.", statement));
}
}