1.Calcite 如何实现 sql 语法的解析
Calcite 使用 javacc 作为语法解析器。如下所示,freemarker
将配置文件 config.fmpp
即指定文件为 codegen/data/Parser.tdd
、附加模板文件 codegen/includes/parserImpls.ftl
、模板文件 codegen/templates/Parser.jj
。上述文件输入 FMPP
后, 会组合生成一个可用的 Parser.jj
文件,即Calcite 的 SQL 解析器语法规则文件。Parser.jj
文件输入 javacc
会生成一个继承自 SqlAbstractParserImpl
的 SqlParserImpl
类,即 Calcite 中真正负责解析 SQL 语句并生成 SqlNode 树的类。
codegen
├── config.fmpp
├── default_config.fmpp
├── includes
│ ├── compoundIdentifier.ftl
│ └── parserImpls.ftl
└── templates
└── Parser.jj
扩展内容:JavaCC 语法解析并生成抽象语法树
2.如何扩展 Flink Function SQL
如何扩展语法 CREAT FUNCTION ?
CREATE [TEMPORARY|TEMPORARY SYSTEM] FUNCTION
[IF NOT EXISTS] [[catalog_name.]db_name.]function_name
AS identifier [LANGUAGE JAVA|SCALA|PYTHON]
在指定 catalog.database
中创建 function ,需指定一个 identifier
,可指定 language
。 若 catalog 中,已经有同名的函数注册了,则无法注册。
① 如果 language 是 JAVA 或者 SCALA ,则 identifier 是 UDF 实现类的全限定名。关于 JAVA/SCALA UDF 的实现,请参考 自定义函数。
② 如果 language 是 PYTHON,则 identifier 是 UDF 对象的全限定名,例如pyflink.table.tests.test_udf.add
。关于 PYTHON UDF 的实现,请参考 Python UDFs。
③ 如果 language 是 PYTHON,而当前程序是 Java/Scala 程序或者纯 SQL 程序,则需要配置 Python 相关的依赖。
步骤 1:添加 codegen 文件夹到 src/main 目录,如下所示。
codegen
├── config.fmpp
├── default_config.fmpp
├── includes
│ ├── compoundIdentifier.ftl
│ └── parserImpls.ftl
└── templates
└── Parser.jj
步骤 2:扩展语法 CREAT FUNCTION
① 在 codegen/includes/parserImpls.ftl
定义 create function 的语法规则,如下所示
SqlCreate SqlCreateFunction(Span s, boolean replace, boolean isTemporary) :
{
SqlIdentifier functionIdentifier = null;
SqlCharStringLiteral functionClassName = null;
String functionLanguage = null;
boolean ifNotExists = false;
boolean isSystemFunction = false;
}
{
(
ifNotExists = IfNotExistsOpt()
functionIdentifier = SimpleIdentifier()
{ isSystemFunction = true; }
|
ifNotExists = IfNotExistsOpt()
functionIdentifier = CompoundIdentifier()
)
{
String p = SqlParserUtil.parseString(token.image);
functionClassName = SqlLiteral.createCharString(p, getPos());
}
[
(
{ functionLanguage = "JAVA"; }
|
{ functionLanguage = "SCALA"; }
|
{ functionLanguage = "SQL"; }
|
{ functionLanguage = "PYTHON"; }
)
]
{
return new SqlCreateFunction(s.pos(), functionIdentifier, functionClassName, functionLanguage,
ifNotExists, isTemporary, isSystemFunction);
}
}
② 扩展 SqlCreate
规则匹配成功返回一个 SqlCreateFunction
节点,作为解析树中的 SqlNode
。
public class SqlCreateFunction extends SqlCreate {
public static final SqlSpecialOperator OPERATOR =
new SqlSpecialOperator("CREATE FUNCTION", SqlKind.CREATE_FUNCTION);
private final SqlIdentifier functionIdentifier;
private final SqlCharStringLiteral functionClassName;
private final String functionLanguage;
private final boolean isTemporary;
private final boolean isSystemFunction;
public SqlCreateFunction(
SqlParserPos pos,
SqlIdentifier functionIdentifier,
SqlCharStringLiteral functionClassName,
String functionLanguage,
boolean ifNotExists,
boolean isTemporary,
boolean isSystemFunction) {
super(OPERATOR, pos, false, ifNotExists);
this.functionIdentifier = requireNonNull(functionIdentifier);
this.functionClassName = requireNonNull(functionClassName);
this.isSystemFunction = isSystemFunction;
this.isTemporary = isTemporary;
this.functionLanguage = functionLanguage;
}
@Override
public SqlOperator getOperator() {
return OPERATOR;
}
@Nonnull
@Override
public List getOperandList() {
return ImmutableNullableList.of(functionIdentifier, functionClassName);
}
@Override
public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
writer.keyword("CREATE");
if (isTemporary) {
writer.keyword("TEMPORARY");
}
if (isSystemFunction) {
writer.keyword("SYSTEM");
}
writer.keyword("FUNCTION");
if (ifNotExists) {
writer.keyword("IF NOT EXISTS");
}
functionIdentifier.unparse(writer, leftPrec, rightPrec);
writer.keyword("AS");
functionClassName.unparse(writer, leftPrec, rightPrec);
if (functionLanguage != null) {
writer.keyword("LANGUAGE");
writer.keyword(functionLanguage);
}
}
//...省略
}
③ 将新增的语法规则,添加到配置文件 codegen/data/Parser.tdd
imports
增加 SqlCreateFunction
类,statementParserMethods
增加定义的规则方法
package: "org.apache.flink.sql.parser.impl",
class: "FlinkSqlParserImpl",
imports: [
"org.apache.flink.sql.parser.ddl.SqlCreateFunction"
]
statementParserMethods: [
"SqlCreateFunction()"
]
implementationFiles: [
"parserImpls.ftl"
]
④ mvn clean compile
编译后,自动生成 FlinkSqlParserImpl.java