執行
上一篇講述了如何通過scala提供的內置DSL支持,實現一個可以解析sql的解析器,這篇講如何拿到了解析結果-AST以后,如何在數據上進行操作,得到我們想要的結果。之前說到,為什么選擇scala作為這個引擎的實現,之一是scala提供了方便的DSL實現支持,其二是因為作為一門函數式編程語言,scala提供了豐富對於集合操作的函數。此外,函數在scala中是一個獨立的類型,所以能夠把現有的函數進行組合,得到更為強大的函數(和上一篇提到的用解析組合子組合已有的解析器得到更強大的解析器一樣)。
首先,我們需要弄清楚普通sql語句的執行順序,一般來說,sql的書寫順序為
- SELECT[DISTINCT]
- FROM
- WHERE
- GROUP BY
- HAVING
- UNION
- ORDER BY
但是,執行順序為
- FROM
- WHERE
- GROUP BY
- HAVING
- SELECT
- DISTINCT
- UNION
- ORDER BY
本次實現的sql執行不支持join,所以省去了union部分,但是大致順序差不多,如果把一個List<Map>想象成一個數據庫的表,那么執行順序可以用下圖所示,圖中標綠色的箭頭表示可以並發執行,聚合函數的執行是不能並發的,但是因為已經把數據給分組了,故可以在更高的一個層次並發。
了解了大致的執行流程,下面說一下各個流程執行所用到的函數。
- where子句
where子句的執行,利用了 filter函數,將不符合條件的數據給過濾掉。舉個例子,下面這個段代碼,將列表中的奇數給過濾掉。
scala> val L = List(1,2,3,4,5,6) L: List[Int] = List(1, 2, 3, 4, 5, 6) scala> L filter(_%2==0) res2: List[Int] = List(2, 4, 6)
對每個元素進行判斷這個步驟其實是可以並發執行的,你只需要這樣寫,就能進行安全的並發操作。
scala> L.par.filter(_%2==0).toList res4: List[Int] = List(2, 4, 6)
在scala_sql引擎中,實現where的函數為
def where(where: Option[SqlExpr]): Table = { where match { case None => table case Some(x: SqlExpr) => table filter (evalWhereEachRow(_, x)) } }
其中evalWhereEachRow(_,x)是另外一個函數,第一個參數是table中的一列,即一個Map對象,第二個參數是由sql解析得到的AST中,對應where子句的部分。
- groupBy子句
scala中也提供了對於集合的groupBy操作,接着上個例子
scala> L groupBy(_%2) res6: scala.collection.immutable.Map[Int,List[Int]] = Map(1 -> List(1, 3, 5), 0 -> List(2, 4, 6))
上面這個函數,把L這個list根據奇偶分組。
在scala _sql引擎中,實現groupBy的函數為def evalGroupBy(table: Table, groupby: SqlGroupBy): Seq[Table] = { val keys: Seq[String] = groupby.keys map { case x: FieldIdent => x.name } table.groupBy(row => keys.map(row(_))).map(_._2).toSeq }
結論
DSL主要有兩種,內部DSL和外部DSL,對於外部DSL,需要一個解析器來解析DSL的腳本,得到能夠讓程序處理的數據結構,這種數據結構通常是一種AST。實現需要的解析器,大體有兩種方式:
近年來隨着函數式編程慢慢得到工業界的關注,利用parser combinator(解析組合子)的方式來編寫解析器實現DSL也進入了大眾視野。