$ Apache Calcite学习笔记
$ Parser代码生成
一般数据库里都是用DSL文件描述语法,然后通过代码生成的方式生成运行时代码文件。Java中常见的生成Parser的工具有antlr等,Calcite使用了JavaCC,除此之外为了方便扩展语法,还使用了fmpp模板工具。
fmpp基于FreeMarker的预处理工具,为什么不直接使用FreeMarker呢?大概是因为FreeMarker的变量是以代码的形式存在,而fmpp可以从文件中加载变量,更方便使用吧。
Calcite的相关语法描述文件在core模块的src/main/codegen
目录中,相关的文件有:
src/main/codegen/
├── config.fmpp
├── default_config.fmpp
├── includes
│ └── parserImpls.ftl
└── templates
└── Parser.jj
其中templates/Parser.jj
是JavaCC语法的模板文件,config.fmpp
是fmpp的配置文件,default_config.fmpp
是默认的配置文件,inlcudes
目录下是为了扩展语法而添加的模板文件,会被config.fmpp
引用。
fmpp替换之后的模板文件输出在build/fmpp/fmppMain/javacc/Parser.jj
,最终JavaCC生成的运行时的代码在build/javacc/javaCCMain
目录下。这两个步骤是由core模块的build.gradle.kts (opens new window)的FmppTask (opens new window)和JavaCCTask (opens new window)完成的。
$ 扩展Parser
如果想要用Calcite解析一种自定义SQL语法,就需要扩展core模块的语法文件,参考server模块。calcite是用gradle组织的,在maven项目中如何使用呢?参考FmppTask (opens new window)和JavaCCTask (opens new window),和Fmpp的Ant Task (opens new window)文档,如下:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<taskdef name="fmpp" classname="fmpp.tools.AntTask" />
<fmpp
configuration="src/main/codegen/config.fmpp"
sourceRoot="src/main/codegen/templates"
outputRoot="${project.build.directory}/generated-sources/fmpp"
>
<data>
tdd(${project.basedir}/src/main/codegen/config.fmpp),default:tdd(${project.basedir}/src/main/codegen/default_config.fmpp)</data>
</fmpp>
</target>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.29</version>
</dependency>
<dependency>
<groupId>net.sourceforge.fmpp</groupId>
<artifactId>fmpp</artifactId>
<version>0.9.16</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>javacc-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>javacc</id>
<goals>
<goal>javacc</goal>
</goals>
<configuration>
<sourceDirectory>${project.build.directory}/generated-sources/fmpp</sourceDirectory>
<includes>
<include>**/Parser.jj</include>
</includes>
<lookAhead>1</lookAhead>
<isStatic>false</isStatic>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>net.java.dev.javacc</groupId>
<artifactId>javacc</artifactId>
<version>4.0</version>
</dependency>
</dependencies>
</plugin>
其中<data>
标签那一段比较难理解,tdd()是TDD functions (opens new window),用来把文件confg.fmpp解析成为FreeMarker用的数据模型,如果tdd()前带上default:
标签,则数据模型的key会统一带上标签,这就是Parser.jj里面default (opens new window)的来源。
$ 参考
Extending the parserPer (opens new window)