背景
calcite 会把SQL 解析成AST,那是如何进行解析的,实际上是用到了java cc
JAVA CC 语法
一个典型的 JavaCC 文件(.jj)主要由以下几个部分组成:
头部和导入:包含包声明和需要导入的 Java 类。
PARSER_BEGIN 和 PARSER_END:定义解析器类的开始和结束。
跳过规则(SKIP 或 IGNORE):定义在解析过程中需要跳过的字符,如空格、注释等。
TOKEN 定义:定义词法分析器(Lexer)的标记,包括关键词、标识符、字面量、运算符等。
语法规则:使用类似于巴克斯-诺尔范式(BNF)的语法描述语言,定义 SQL 的语法结构。
PARSER_BEGIN 和 PARSER_END
这两个宏用于定义解析器类的开始和结束,以及包含在解析器类中的自定义 Java 代码。
PARSER_BEGIN(SqlParser)
public class SqlParser {
public static void main(String[] args) throws ParseException {
SqlParser parser = new SqlParser(System.in);
parser.Statement();
System.out.println("SQL Parsed Successfully!");
}
}
PARSER_END(SqlParser)
跳过和忽略的字符
SKIP : { " " | "\t" | "\n" | "\r" }
TOKEN 定义
TOKEN : {
< SELECT: "SELECT" >
| < FROM: "FROM" >
| < WHERE: "WHERE" >
| < INSERT: "INSERT" >
| < UPDATE: "UPDATE" >
| < DELETE: "DELETE" >
| < CREATE: "CREATE" >
| < DROP: "DROP" >
| < JOIN: "JOIN" >
| < INNER: "INNER" >
| < LEFT: "LEFT" >
| < RIGHT: "RIGHT" >
| < FULL: "FULL" >
| < ON: "ON" >
| < AS: "AS" >
| < IDENTIFIER: (["a"-"z","A"-"Z"] | "_") (["a"-"z","A"-"Z","0"-"9"] | "_")* >
| < STRING_LITERAL: "'" (~["'"])* "'" >
| < NUMBER: (["0"-"9"])+ ( "." (["0"-"9"])+ )? >
| < COMMA: "," >
| < SEMICOLON: ";" >
| < STAR: "*" >
| < DOT: "." >
| < LPAREN: "(" >
| < RPAREN: ")" >
| < EQ: "=" >
| < NEQ: "!=" | "<>" >
| < LT: "<" >
| < GT: ">" >
| < LE: "<=" >
| < GE: ">=" >
}
语法规则
void Statement() :
{}
{
( SelectStatement() | InsertStatement() | UpdateStatement() | DeleteStatement() ) <SEMICOLON>
}
void SelectStatement() :
{}
{
<SELECT> SelectList() <FROM> TableSource() [WhereClause()] [GroupByClause()] [OrderByClause()]
}
void SelectList() :
{}
{
SelectItem() ( <COMMA> SelectItem() )*
}
void SelectItem() :
{}
{
( <IDENTIFIER> | <STAR> | FunctionCall() )
}
void FunctionCall() :
{}
{
<IDENTIFIER> <LPAREN> [ SelectList() ] <RPAREN>
}
void TableSource() :
{}
{
<IDENTIFIER> [ <AS> <IDENTIFIER> ]
}
void WhereClause() :
{}
{
<WHERE> Condition()
}
void Condition() :
{}
{
Expression()
}
void Expression() :
{}
{
Term() ( ( <EQ> | <NEQ> | <LT> | <GT> | <LE> | <GE> ) Term() )*
}
void Term() :
{}
{
<IDENTIFIER> | <NUMBER> | <STRING_LITERAL>
}
void GroupByClause() :
{}
{
<GROUP> <BY> SelectList()
}
void OrderByClause() :
{}
{
<ORDER> <BY> OrderList()
}
void OrderList() :
{}
{
OrderItem() ( <COMMA> OrderItem() )*
}
void OrderItem() :
{}
{
<IDENTIFIER> [ <ASC> | <DESC> ]
}
解析示例
SELECT name, age, COUNT(*)
FROM users AS u
WHERE age >= 18
GROUP BY name, age
ORDER BY age DESC;
解析过程
Statement 规则识别整个 SQL 语句,并确定它是一个 SelectStatement,以分号 ; 结束。
SelectStatement 规则解析 SELECT 关键字,调用 SelectList() 解析选择列表 name, age, COUNT(*)。
SelectList 规则识别三个 SelectItem:
<IDENTIFIER>: name
<IDENTIFIER>: age
FunctionCall(): COUNT(*)
FunctionCall 规则解析 COUNT(*):
<IDENTIFIER>: COUNT
<LPAREN>: (
<STAR>: *
<RPAREN>: )
FROM 关键字后,调用 TableSource() 解析表来源 users AS u:
<IDENTIFIER>: users
<AS>: AS
<IDENTIFIER>: u
WHERE 子句调用 WhereClause(),进一步调用 Condition() 和 Expression() 解析条件 age >= 18:
<IDENTIFIER>: age
<GE>: >=
<NUMBER>: 18
GROUP BY 子句调用 GroupByClause(),解析 name, age:
两个 <IDENTIFIER> 分别为 name 和 age
ORDER BY 子句调用 OrderByClause(),解析 age DESC:
<IDENTIFIER>: age
<DESC>: DESC
结合转化SqlNode 来看
对应的JAVA CC语法
/**
* Parses a DELETE statement.
*/
SqlNode SqlDelete() :
{
final SqlIdentifier tableName;
SqlNode tableRef;
final SqlIdentifier alias;
final SqlNode where;
final Span s;
}
{
<DELETE> {
s = span();
}
<FROM> tableName = CompoundTableIdentifier()
( tableRef = TableHints(tableName) | { tableRef = tableName; } )
[ tableRef = ExtendTable(tableRef) ]
( [ <AS> ] alias = SimpleIdentifier() | { alias = null; } )
( where = Where() | { where = null; } )
{
return new SqlDelete(s.add(tableRef).addIf(alias).addIf(where).pos(),
tableRef, where, null, alias);
}
}
对应SQL语句
DELETE FROM employees AS e WHERE e.id = 10;
解析过程
解析过程如下:
匹配 <DELETE>:识别 DELETE 关键字,并记录位置。
匹配 <FROM>:识别 FROM 关键字。
调用 CompoundTableIdentifier():解析表名 employees,赋值给 tableName。
解析表提示或直接引用表:
假设没有表提示,tableRef 被赋值为 tableName 即 employees。
可选的扩展表:
假设没有扩展表,tableRef 保持为 employees。
解析别名:
匹配 AS 关键字。
调用 SimpleIdentifier() 解析别名 e,赋值给 alias。
解析 WHERE 子句:
调用 Where() 解析条件 e.id = 10,赋值给 where。
构建 SqlDelete 对象:
创建一个 SqlDelete 实例,包含表引用 employees、别名 e、WHERE 条件 e.id = 10,以及语句的位置和范围