前言
calcite是一個能夠將任意數據查詢轉換成基於sql查詢的引擎,引擎特性也有不少,好比支持sql樹的解析,udf的擴展,sql執行優化器的擴展等等。目前已經被不少頂級apache項目引用,好比hive,kylin等。在這個SQL做爲主流的數據查詢語言大數據世界裏,calcite的做用會愈來愈大。html
理解calcite的核心流程
如圖1所示。calcite核心步驟有兩個,數據關係化, SQL解析執行。java
圖1 calcite核心流程node
數據協議關係化
要想使用sql進行查詢,首先要把非結構化數據結構化,而calcite號稱支持全部數據協議,則必然得將這部份內容抽象出來。git
在calcite的接口中,Schema和Table是數據關係化中最重要的兩個接口。Schema是對catalog或者是database的抽象,以兼容已經存在的各種數據庫,Table是對錶,視圖,流的抽象,以兼容數據的各類場景,下面詳細描述一下這兩種抽象。github
schema
calcite利用schema的層級關係,構造出來namespace的概念,如圖2所示,schema自身是一個樹形結構,這樣設計的優勢很明顯,能夠兼容全部已知和未知的數據庫,基於namespace結構,schema不管是橫向仍是縱向均可以無限擴展。sql
圖2 schema的namespace數據庫
在實際應用中,RootSchema是根全部schema的路徑,全部註冊在RootSchema上的table或者是udf都是全局的,意思就是說能夠被SubSchema直接使用,而註冊在SubSchema裏的table或者是udf,則在使用中必須聲明是哪一個SubSchema擁有的。apache
table
table是schema的核心屬性,一個schema擁有多個table,這就像一個數據庫中有不少表同樣。而table的概念更爲普遍,爲了兼容到各種數據庫或者消息隊列,calcite將table類型細分爲TableType,基本的相似傳統關係型數據庫中的表或者視圖,流式的Stream等。json
另外對數據協議的兼容是很是重要的,像json,csv,xml等等,table抽象出了RelDataType接口,目的是將應用層的數據協議轉關係化,從而能夠爲sql服務。數據結構
拿csv格式的數據來講,假設csv數據的每一行數據和table中的每一行一一對應,那麼在關係化的過程當中,必須將csv中每一個字段的類型及一些元數據定義清晰,好比字段是int類型仍是long類型,主鍵是哪一個字段,外鍵是哪一個字段等,calcite提供了幾乎全部已存在的字段類型。
關於嵌套數據,calcite也考慮的很周全,提供MapSqlType或者ArraySqlType的形式來兼容這些結構。
拿json格式的數據來講,字段嵌套是很常見的,若是想把這類結構數據關係化,一般有兩種選擇(1)數據扁平化,將嵌套的字段提上來,造成a.b的形式(2)經過calcite引擎聲明嵌套字段及規則,在使用sql查詢的時候再經過calcite的表達式提取這些字段。
SQL解析執行
到了這一步,其實和傳統數據庫很像了,如圖3所示,calcite採用了該方案來解決從sql的輸入到輸出。
calcite經過關係代數來實現對sql的執行,而關係代數之間經過樹形結構做爲載體,每個輸入的sql命令都會被轉換成樹形結構的關係代數也就是關係表達式樹。calcite支持直接構建關係表達式樹,經過RelBuilder接口。
注:關係代數,常見的有(交,並,差,投影,選擇,笛卡爾積,鏈接)
圖3
案例
完整案例
calcite提供了基於json和csv的案例,在calcite-example模塊下,另外在該模塊的單元測試中,有一些完整的例子。
sql解析案例
// 能夠經過SqlParser直接對sql語句做解析,返回的就是sql樹。 SqlParser sqlParser = SqlParser.create("select * from \"table\" where \"column\" > 1 limit 1"); SqlNode sqlNode = sqlParser.parseQuery(); if(sqlNode instanceof SqlCall){ if(sqlNode instanceof SqlBasicCall){ SqlBasicCall basicCall = (SqlBasicCall) sqlNode; System.out.println(((SqlIdentifier)basicCall.operand(0)).getSimple()); System.out.println(((SqlNumericLiteral)basicCall.operand(1)).getValue()); System.out.println(basicCall.getKind()); } System.out.println(sqlNode.getKind()+" -> "+sqlNode.getClass()); SqlCall call = (SqlCall) sqlNode; for(SqlNode node: call.getOperandList()){ parse(node); } }
引用
// calcite官網
http://calcite.apache.org/docs/tutorial.html
// calcite github