1.Calcite 如何實(shí)現(xiàn) sql 語(yǔ)法的解析
Calcite 使用 javacc 作為語(yǔ)法解析器。如下所示桑滩,freemarker
將配置文件 config.fmpp
即指定文件為 codegen/data/Parser.tdd
失球、附加模板文件 codegen/includes/parserImpls.ftl
、模板文件 codegen/templates/Parser.jj
沥寥。上述文件輸入 FMPP
后, 會(huì)組合生成一個(gè)可用的 Parser.jj
文件,即Calcite 的 SQL 解析器語(yǔ)法規(guī)則文件柠座。Parser.jj
文件輸入 javacc
會(huì)生成一個(gè)繼承自 SqlAbstractParserImpl
的 SqlParserImpl
類邑雅,即 Calcite 中真正負(fù)責(zé)解析 SQL 語(yǔ)句并生成 SqlNode 樹(shù)的類。
codegen
├── config.fmpp
├── default_config.fmpp
├── includes
│ ├── compoundIdentifier.ftl
│ └── parserImpls.ftl
└── templates
└── Parser.jj
擴(kuò)展內(nèi)容:JavaCC 語(yǔ)法解析并生成抽象語(yǔ)法樹(shù)
2.如何擴(kuò)展 Flink Function SQL
如何擴(kuò)展語(yǔ)法 CREAT FUNCTION 妈经?
CREATE [TEMPORARY|TEMPORARY SYSTEM] FUNCTION
[IF NOT EXISTS] [[catalog_name.]db_name.]function_name
AS identifier [LANGUAGE JAVA|SCALA|PYTHON]
在指定 catalog.database
中創(chuàng)建 function 淮野,需指定一個(gè) identifier
,可指定 language
吹泡。 若 catalog 中骤星,已經(jīng)有同名的函數(shù)注冊(cè)了,則無(wú)法注冊(cè)爆哑。
① 如果 language 是 JAVA 或者 SCALA 洞难,則 identifier 是 UDF 實(shí)現(xiàn)類的全限定名。關(guān)于 JAVA/SCALA UDF 的實(shí)現(xiàn)揭朝,請(qǐng)參考 自定義函數(shù)队贱。
② 如果 language 是 PYTHON,則 identifier 是 UDF 對(duì)象的全限定名潭袱,例如pyflink.table.tests.test_udf.add
柱嫌。關(guān)于 PYTHON UDF 的實(shí)現(xiàn),請(qǐng)參考 Python UDFs屯换。
③ 如果 language 是 PYTHON编丘,而當(dāng)前程序是 Java/Scala 程序或者純 SQL 程序,則需要配置 Python 相關(guān)的依賴。
步驟 1:添加 codegen 文件夾到 src/main 目錄嘉抓,如下所示索守。
codegen
├── config.fmpp
├── default_config.fmpp
├── includes
│ ├── compoundIdentifier.ftl
│ └── parserImpls.ftl
└── templates
└── Parser.jj
步驟 2:擴(kuò)展語(yǔ)法 CREAT FUNCTION
① 在 codegen/includes/parserImpls.ftl
定義 create function 的語(yǔ)法規(guī)則,如下所示
SqlCreate SqlCreateFunction(Span s, boolean replace, boolean isTemporary) :
{
SqlIdentifier functionIdentifier = null;
SqlCharStringLiteral functionClassName = null;
String functionLanguage = null;
boolean ifNotExists = false;
boolean isSystemFunction = false;
}
{
(
<SYSTEM> <FUNCTION>
ifNotExists = IfNotExistsOpt()
functionIdentifier = SimpleIdentifier()
{ isSystemFunction = true; }
|
<FUNCTION>
ifNotExists = IfNotExistsOpt()
functionIdentifier = CompoundIdentifier()
)
<AS> <QUOTED_STRING> {
String p = SqlParserUtil.parseString(token.image);
functionClassName = SqlLiteral.createCharString(p, getPos());
}
[<LANGUAGE>
(
<JAVA> { functionLanguage = "JAVA"; }
|
<SCALA> { functionLanguage = "SCALA"; }
|
<SQL> { functionLanguage = "SQL"; }
|
<PYTHON> { functionLanguage = "PYTHON"; }
)
]
{
return new SqlCreateFunction(s.pos(), functionIdentifier, functionClassName, functionLanguage,
ifNotExists, isTemporary, isSystemFunction);
}
}
② 擴(kuò)展 SqlCreate
規(guī)則匹配成功返回一個(gè) SqlCreateFunction
節(jié)點(diǎn)抑片,作為解析樹(shù)中的 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<SqlNode> 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);
}
}
//...省略
}
③ 將新增的語(yǔ)法規(guī)則,添加到配置文件 codegen/data/Parser.tdd
imports
增加 SqlCreateFunction
類蓝丙,statementParserMethods
增加定義的規(guī)則方法
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
編譯后级遭,自動(dòng)生成 FlinkSqlParserImpl.java