【Flink SQL】如何利用 Calcite 擴(kuò)展 Flink Function 語(yǔ)法

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è)繼承自 SqlAbstractParserImplSqlParserImpl 類邑雅,即 Calcite 中真正負(fù)責(zé)解析 SQL 語(yǔ)句并生成 SqlNode 樹(shù)的類。

codegen
├── config.fmpp
├── default_config.fmpp
├── includes
│   ├── compoundIdentifier.ftl
│   └── parserImpls.ftl
└── templates
    └── Parser.jj
calcite擴(kuò)展sql語(yǔ)法.JPG

擴(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市渺尘,隨后出現(xiàn)的幾起案子挫鸽,更是在濱河造成了極大的恐慌,老刑警劉巖鸥跟,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丢郊,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡医咨,警方通過(guò)查閱死者的電腦和手機(jī)枫匾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)拟淮,“玉大人干茉,你說(shuō)我怎么就攤上這事『懿矗” “怎么了角虫?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)委造。 經(jīng)常有香客問(wèn)我戳鹅,道長(zhǎng),這世上最難降的妖魔是什么昏兆? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任枫虏,我火速辦了婚禮,結(jié)果婚禮上爬虱,老公的妹妹穿的比我還像新娘隶债。我一直安慰自己,他們只是感情好饮潦,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布燃异。 她就那樣靜靜地躺著携狭,像睡著了一般继蜡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,255評(píng)論 1 308
  • 那天稀并,我揣著相機(jī)與錄音仅颇,去河邊找鬼。 笑死碘举,一個(gè)胖子當(dāng)著我的面吹牛忘瓦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播引颈,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼耕皮,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蝙场?” 一聲冷哼從身側(cè)響起凌停,我...
    開(kāi)封第一講書(shū)人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎售滤,沒(méi)想到半個(gè)月后罚拟,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡完箩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年赐俗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弊知。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡阻逮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出秩彤,到底是詐尸還是另有隱情夺鲜,我是刑警寧澤,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布呐舔,位于F島的核電站币励,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏珊拼。R本人自食惡果不足惜食呻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望澎现。 院中可真熱鬧仅胞,春花似錦、人聲如沸剑辫。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)妹蔽。三九已至椎眯,卻和暖如春挠将,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背编整。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工舔稀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人掌测。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓内贮,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親汞斧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子夜郁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容