执行
上一篇讲述了如何通过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也进入了大众视野。