Calcite中定制自已SQL解析器

不想看文章直接訪問https://github.com/yuqi1129/schema/tree/master/mysql-protocol
(Java版本的Mysql)涡相、https://github.com/yuqi1129/calcite-test,這里有關(guān)于JavaCC使用具體用例

1. 什么Sql Parser

所謂Sql Parser, 就是根據(jù)某種特定的定義而生成的Sql 語法解析器拆宛。 打個比方: 在計算器中輸入 1 + 2 = 之所以可以得到結(jié)果3是因為是計算器可以準(zhǔn)確地根據(jù)上述字符解析出相應(yīng)的輸入?yún)?shù)與算法,進而計算到最終的結(jié)果惭适。 如果輸入的是1 +- 2 = 計算器可能就會提示錯誤狈究, 這其實就是類似于SQL中提示語法錯誤亮钦,而在處理SQL的過程與處理上面的例子很類似, 可見我們需要定制相應(yīng)的語法規(guī)則進而解析SQL掩完。

2. Java CC

熟悉ANTRL的同學(xué)應(yīng)該知道.g文件的作用噪漾, 在Calcite中與之對就是JavaCC(關(guān)于什么是JavaCC,可以自行Google), 通過JavaCC文件Calcite可以定義如何去解析傳入的SQL語法

3. Calcite 內(nèi)置語法解析

現(xiàn)在就以一上簡單的例子介紹一下Calcite 默認(rèn)語法解析

    SchemaPlus rootSchema = Frameworks.createRootSchema(true);
    final FrameworkConfig config = Frameworks.newConfigBuilder()
            .parserConfig(SqlParser.configBuilder()
                    .setParserFactory(SqlParserImpl.FACTORY)
                    .setCaseSensitive(false)
                    .setQuoting(Quoting.BACK_TICK)
                    .setQuotedCasing(Casing.TO_UPPER)
                    .setUnquotedCasing(Casing.TO_UPPER)
                    .setConformance(SqlConformanceEnum.ORACLE_12)
                    .build())
            .build();
                
    
    
    String sql = "select ids, name from test where id < 5 and name = 'zhang'";
    SqlParser parser = SqlParser.create(sql, config.getParserConfig());
    try {
        SqlNode sqlNode = parser.parseStmt();
        System.out.println(sqlNode.toString());
    } catch (Exception e) {
        e.printStackTrace();
    }

以上為Calcite 內(nèi)置關(guān)于parser的過程且蓬,詳細代碼見代碼

現(xiàn)在簡要的介紹以上代碼:

  • parserConfig() 是設(shè)置ParserFactory, calcite內(nèi)置Parser類為SqlParserImpl欣硼, 這個類的代碼全部是由JavaCC生成,比較大恶阴,大約在7w行左右诈胜,不要試圖去看懂這個類,因為基本上不會有人會看懂(如果有人看懂了冯事,私下交流請你吃飯)耘斩,也沒有必要,后面我們會介紹如何用JavaCC生成對應(yīng)的Parser類

  • 語法參數(shù)設(shè)置

    • setCaseSensitive() 大小是寫否敏感桅咆,比如說列名括授、表名、函數(shù)名
    • setQuoting() 設(shè)置引用一個標(biāo)識符岩饼,比如說MySQL中的是``, Oracle中的""
    • setQuotedCasing Quoting策略荚虚,不變,變大寫或變成小寫籍茧,代碼中的全部設(shè)置成變大寫
    • setUnquotedCasing 當(dāng)標(biāo)識符沒有被Quoting后的策略版述,值同上

    更多可以更以參考Calcite類Lex, 你也可以直接設(shè)置成MySQL、Oracle寞冯、MySQL_ANSI語法渴析,如果需要定制化的話可以單獨設(shè)置上面4個參數(shù)

  • ParserConfig中其它需要注意的參數(shù)

    • setIdentifierMaxLength() 設(shè)置標(biāo)識符的最大長度,如果你的列名吮龄、表較長可以相應(yīng)的加大這個值
    • setConformance() 特定語法支持俭茧,比如是否支持差集等

日常使用中,一般使用默認(rèn)配置即可, 除非對語法有特殊需求

注意: Parser只會解析SQL, 不會去驗證SQL是否正確漓帚,可能這么說有點矛盾母债,有人會想parser難道不會檢查語法正確與否嗎?我的回答是尝抖、也不是毡们。上面的例子如果有人執(zhí)行了之后發(fā)現(xiàn)居然可以通過, 而在代碼中我們并沒有明確表名昧辽、列名衙熔、列信息之類,為什么不會報錯搅荞?
因為 Calcite parser 只會識別關(guān)鍵字(Keyword)與標(biāo)識符(Identifier)红氯, 上面Sql關(guān)鍵字有select框咙、from、where脖隶、<、=暇检,其他為標(biāo)識符产阱,即Parsr會規(guī)定關(guān)鍵字與標(biāo)識符的相對位置是否正確,不會關(guān)心標(biāo)識符的值是否存在块仆、是否正確构蹬, 至于什么時候會檢查標(biāo)識符--會在Validator階段

4. 創(chuàng)建自已parser

在3中我們使用Calcite內(nèi)置的Parser Class, 假如有這樣一個需求,要支持"submit job as 'select * from test'", 如果仍使用默認(rèn)Parser悔据,上述代碼就會執(zhí)行有問題庄敛,見代碼, 那么如何支持該語法?

第一步: 工程中引入Calcite 的JavaCC文件parser.jj, 如下圖

parser.jj

修改config.fmpp中關(guān)class 名為自已近parser class 名,如YuqiSqlParserImpl

第二步: 添加對應(yīng)的SqlSubmit SqlNode, 關(guān)于如何擴展SqlNode, 請仔細讀閱讀 SqlSelect等SqlNode類

public class SqlSubmit extends SqlNode {

    String jobString;

    public SqlSubmit(SqlParserPos pos, String jobString) {
        super(pos);
        this.jobString = jobString;
    }
    
    public String getJobString() {
        return jobString;
    }
}
    

第三步: 修改parser.jj 文件, 添加以下內(nèi)容

...
import org.apache.calcite.sql.SqlSubmit;
...
...


SqlNode SqlSubmit() :
{
     SqlNode stringNode;
}
{
    <SUBMIT> <JOB> <AS>
    stringNode = StringLiteral()
    {
        return new SqlSubmit(getPos(), token.image);
    }
}


...

SqlNode SqlStmt() :
{
    SqlNode stmt;
}
{
    ...
    
    | stmt = SqlSubmit()
    ...
}


<DEFAULT, DQID, BTID> TOKEN :
{
    ...
    | <SUBMIT: "SUBMIT">
    | <JOB: "JOB">
    ...
}

    

第四步: 引入JavaCC編譯插件

詳細參考代碼中的pom文件

第五步:在代碼引入剛剛設(shè)置的parser 類

import org.apache.calcite.sql.parser.impl.YuqiSqlParserImpl;
...

public class ParserTest {
...
    .setParserFactory(YuqiSqlParserImpl.FACTORY))
}


第六步:編譯整個項目科汗,最終可以在Target目錄下可以看到以下文件, 然后將javacc 目錄設(shè)置成Generated Source Root, 現(xiàn)在你可以愉快的進行測試了


生成的類

最終的結(jié)果可以參考文件, 運行時請先mvn編譯一下藻烤,以后只要修改了Parser.jj文件都要重新編譯才能生效

5. 相關(guān)問題說明

  1. 由于知道JavaCC讀者可以比較少,關(guān)于JavaCC头滔,我會專門針對這個出一個分享,如何在Calcite使用JavaCC
  2. 全部的代碼在我的github項目中怖亭,有需要的讀者請自行去fork與閱讀(覺得本文有用不要忘了star一下哈)
  3. 由于本人使用Calcite時間不長,其中難免有錯誤之處坤检,請讀者不吝指出兴猩,相互學(xué)習(xí),也歡迎來交流Calcite早歇, 本人郵件: yuqi4733@gmail.com
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末倾芝,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子箭跳,更是在濱河造成了極大的恐慌晨另,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谱姓,死亡現(xiàn)場離奇詭異拯刁,居然都是意外死亡,警方通過查閱死者的電腦和手機逝段,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門垛玻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人奶躯,你說我怎么就攤上這事帚桩。” “怎么了嘹黔?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵账嚎,是天一觀的道長莫瞬。 經(jīng)常有香客問我,道長郭蕉,這世上最難降的妖魔是什么疼邀? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮召锈,結(jié)果婚禮上旁振,老公的妹妹穿的比我還像新娘。我一直安慰自己涨岁,他們只是感情好拐袜,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著梢薪,像睡著了一般蹬铺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秉撇,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天甜攀,我揣著相機與錄音,去河邊找鬼琐馆。 笑死赴邻,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的啡捶。 我是一名探鬼主播姥敛,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瞎暑!你這毒婦竟也來了彤敛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤了赌,失蹤者是張志新(化名)和其女友劉穎墨榄,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體勿她,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡袄秩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了逢并。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片之剧。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖砍聊,靈堂內(nèi)的尸體忽然破棺而出背稼,到底是詐尸還是另有隱情,我是刑警寧澤玻蝌,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布蟹肘,位于F島的核電站词疼,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏帘腹。R本人自食惡果不足惜贰盗,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望阳欲。 院中可真熱鬧舵盈,春花似錦、人聲如沸胸完。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赊窥。三九已至,卻和暖如春狸页,著一層夾襖步出監(jiān)牢的瞬間锨能,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工芍耘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留址遇,地道東北人。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓斋竞,卻偏偏與公主長得像倔约,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子坝初,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345