天天看點

看這篇就夠了丨基于Calcite架構的SQL文法擴充探索

作者:數棧DTinsight

Calcite在大資料系統中有着廣泛的運用, 比如Apache Flink, Apache Drill等都大量使用了Calcite,了解Calcite的原理可以說已經成為了解大資料系統中SQL通路層實作原理的必備條件之一。

但是不少人在學習Calcite的過程中都發現關于Calcite的實踐案例其實很少,本文就将為大家詳細介紹如何基于Calcite架構的SQL文法擴充探索使之更符合你的業務需求,以及擴充SQL在數棧産品的應用實踐。

Calcite介紹及用途

Calcite介紹

Apache Calcite是一個動态的資料管理架構,本身不涉及任何實體存儲資訊,而是專注在SQL解析、基于關系代數的查詢優化,通過擴充方式來對接底層存儲。

目前Apache Calcite被應用在廣泛的資料開源系統中,比如Apache Hive、Apache Phoenix、Apache Flink等。

看這篇就夠了丨基于Calcite架構的SQL文法擴充探索

Calcite的用途

Calcite提供了ANSI标準SQL的解析,以及各種SQL 方言,針對來自于不同資料源的複雜SQL,在Calcite中會把SQL解析成SqlNode文法樹結構,然後根據得到的文法樹轉換成自定義Node,通過自定義Node解析擷取到表的字段資訊、以及表資訊、血緣等相關資訊。

下圖展示了一部分對外提供的接口資訊:

看這篇就夠了丨基于Calcite架構的SQL文法擴充探索

sqlparser 解析子產品主要提供了以下幾種功能 :

• 解析SQL包含的所有表、字段資訊

• 解析SQL的udf函數

• 解析SQL的血緣資訊,包括表級血緣、字段血緣

• 解析自定義SqlNode

• api服務變量解析替換

SQL文法擴充

了解完Calcite是什麼以及用途後,下面為大家分享Calcite SQL文法擴充的相關内容。

SQL文法擴充背景

在 sqlparser 中進行sql解析的場景中,有兩種情況需要使用到自定義擴充,一是Calcite不支援的一些文法;二是在一些場景中存在sql中帶有${var}自定義變量文法。

那麼針對上面的這兩種情況,Calcite的自定義擴充是如何實作的呢?自定義擴充主要涉及到以下三個檔案:

• Parser.jj:Parser.jj是一個Calcite核心的文法和詞法檔案,基于Apache FreeMaker模版,該模版包含着變量,這些變量在編譯時可以被替換

• parserImpl.ftl:提供自定義SQL語句、literals、dataType的實作方法

• config.fmpp:該檔案是FMPP的配置檔案,提供了SQL語句、literals、dataType的接口擴充入口

Calcite使用javacc作為文法解析器,freemaker作為模版,把parserImpls.ftl、config.fmpp、Parser.jj模版合成最終的文法詞法檔案,最終通過javacc編譯成自定義的解析器源碼,整體流程如下圖所示:

看這篇就夠了丨基于Calcite架構的SQL文法擴充探索

擴充SQL實作

● 工程目錄

看這篇就夠了丨基于Calcite架構的SQL文法擴充探索

● 擴充sql實作案例

支援以下limit相關文法以及數字可以寫成${var}形式:

-> limit count, limit start count

-> limit count offset start

-> offset start limit count

在原生的Calcite解析是支援limit count文法的,但是由于傳回SqlOrderBy對象内部類Operator的unparse方法在SQL輸出過程中對原始SQL進行了改寫,是以需要使用擴充SQL得到正确的SQL。

下面介紹一個limit offset文法擴充樣例,擴充SQL如下:

select id, name from test where id > 3 order by id desc limit 1 offset ${offset_val}

整體流程如下:

01

Parser.jj 定義${var}變量的token詞法DOLLAR_VARIABLE:

看這篇就夠了丨基于Calcite架構的SQL文法擴充探索

02

Parser.jj 擴充的變量方法接入,下面方法會在解析到limit、offset關鍵字後面的一個詞時進行調用:

看這篇就夠了丨基于Calcite架構的SQL文法擴充探索

03

Parser.jj limit offset在select文法的核心處理邏輯:

-> 定義變量

看這篇就夠了丨基于Calcite架構的SQL文法擴充探索

主要定義了三個boolean類型的變量,isOffsetLimit表示offset limit 文法,isLimitOffset表示limit offset文法,isOnlyLimit表示limit count、limit start count文法。

-> 定義處理邏輯

看這篇就夠了丨基于Calcite架構的SQL文法擴充探索

-> 傳回自定義SqlNode

看這篇就夠了丨基于Calcite架構的SQL文法擴充探索

針對符合上面的三個boolean條件時,使用自定義ExtendSqlOrderBy的擴充類。

04

parserImpl.ftl 定義擴充的SqlNode ExtendDollarVariable:

看這篇就夠了丨基于Calcite架構的SQL文法擴充探索

05

config.fmpp 定義包以及擴充實作類的import:

看這篇就夠了丨基于Calcite架構的SQL文法擴充探索

06

擴充SqlNode實作:

-> 變量實作sqlNode

看這篇就夠了丨基于Calcite架構的SQL文法擴充探索

-> 擴充limit實作類ExtendSqlOrderBy,該類實作了SqlOrderBy,并在此基礎擴充了limit的SqlNode,以及isOffsetLimit、isLimitOffset、isOnlyLimit三個boolean辨別limit的不同文法

看這篇就夠了丨基于Calcite架構的SQL文法擴充探索
看這篇就夠了丨基于Calcite架構的SQL文法擴充探索

通過上面的這些步驟後,最後解析生成的SqlNode文法樹如下所示:

看這篇就夠了丨基于Calcite架構的SQL文法擴充探索

擴充SQL在數棧的應用

目前袋鼠雲的底層sqlparser sql解析涉及的子産品應用包括API資料服務、離線開發、客戶資料洞察(标簽)、實時開發等,雖然大部分針對Calcite的SQL文法擴充相對于上層的産品應用感覺不是很明顯,但是擴充SQL還是解決了一些痛點,主要如下:

• 逐漸替換底層采用了多種解析工具解析的情況,使維護更簡單,減少bug的産生

• 解決一些不支援的文法,避免在上層業務層做處理或者在底層做一些特殊處理

以在API資料服務後續接入的like文法改造為例為大家進行分享,目前的API資料服務中支援like ${var}文法,在執行測試中通過傳遞like文法來确定執行的模糊比對方式,例如%xx、xx%、%xx%。

收到客戶提出的優化like文法場景,袋鼠雲本着客戶第一的原則,這種合理的優化需求是采納的。SQL支援like%${var}、${var}%、%${var}%,這樣在執行測試中就不需要輸入%了,目前擴充SQL文法已經支援這種優化的like文法,預計在2023年上半年會接入進去,下面通過API資料服務展示目前like SQL和擴充後的SQL差異:

● 目前like ${var}處理

-> 生成API

看這篇就夠了丨基于Calcite架構的SQL文法擴充探索

-> 測試執行,模糊比對需要輸入%

看這篇就夠了丨基于Calcite架構的SQL文法擴充探索

● 擴充like %${var}%

-> 生成API

看這篇就夠了丨基于Calcite架構的SQL文法擴充探索

-> 測試執行,由于在SQL階段已經寫了模糊比對方式,是以可以直接輸入值

看這篇就夠了丨基于Calcite架構的SQL文法擴充探索

總結規劃

相信通過上面的案例後,大家對于Calcite擴充SQL文法的流程應該有了大緻的了解,目前在袋鼠雲的業務場景中已經擴充了許多文法,在未來還有一些工作需要進行優化:

• 豐富SQL文法,實作不同資料源擴充SQL文法的隔離

• 逐漸通過SQL文法擴充替換掉底層Calcite和druid共同解析的場景,避免維護多套相同的解析,減少線上問題産生

最後如果是初步接觸Calcite SQL文法擴充的同學們,建議先熟悉javacc文法。

位址:https://javacc.github.io/javacc/

想了解或咨詢更多有關袋鼠雲大資料産品、行業解決方案、客戶案例的朋友,浏覽袋鼠雲官網:https://www.dtstack.com/?src=sztth

開源項目位址:https://github.com/DTStack