本文主要介绍TinySQL的proj2的具体思路以及实现方式
文件tinysql/parser/parser.y:3806
JoinTable:
/* Use %prec to evaluate production TableRef before cross join */
TableRef CrossOpt TableRef %prec tableRefPriority
{
$$ = &ast.Join{Left: $1.(ast.ResultSetNode), Right: $3.(ast.ResultSetNode), Tp: ast.CrossJoin}
}
/* Your code here. */
在上述文件位置追加写入JoinTable的语法定义,测试不通过的case为
select * from t1 join t2 left join t3 on t2.id = t3.id
报错提示为
FAIL: parser_test.go:300: testParserSuite.TestDMLStmt
parser_test.go:426:
s.RunTest(c, table)
parser_test.go:283:
c.Assert(err, IsNil, comment)
... value *errors.withStack = line 1 column 29 near "left join t3 on t2.id = t3.id" ("line 1 column 29 near \"left join t3 on t2.id = t3.id\" ")
... source select * from t1 join t2 left join t3 on t2.id = t3.id
可以看到错误起始位置为left join
部分,因此需要观测Join语句的定义位置,一个一个项拆解看看需要如何补充。原始定义为TableRef CrossOpt TableRef %prec tableRefPriority
,拆解出来包括了TableRef
/ CrossOpt
/ %prec
/ tableRefPriority
这四项
其中TableRef
的定义位于tinysql/parser/parser.y:3674
TableRef:
TableFactor
{
$$ = $1
}
| JoinTable
{
$$ = $1
}
因此表引用要么就是TableFactor
,要么就是JoinTable
。关于TableFactor
的定义,可以看tinysql/parser/parser.y:3684,这里不在进一步展开,这个结构主要是对单表进行定义。结合JoinTable
的定义可以看到这两个语法结构的定义是相互引用的。回到上面不通过的case
t1 join t2 left join t3 on t2.id = t3.id
其中,t1 join t2
部分可以被已经定义的JoinTable
结构给正确解释,主要的问题在于根据当前的定义,没有办法解释后面的left join t3 on t2.id = t3.id
这部分内容,包括了连接符和连接条件。把t1 join t2
看做是一个TableRef
表引用,对这部分进行字面意义的拆解的话,这里可以解释为
TableRef $连接类型 "JOIN" TableRef "ON" $连接条件
那么,找到上下文中的关于连接类型的定义,JoinType
JoinType:
"LEFT"
{
$$ = ast.LeftJoin
}
| "RIGHT"
{
$$ = ast.RightJoin
}
关于连接条件,其实表现形式是和Select语句中的 WHERE t2.id = t3.id
类似的,而参考与Select处理相关的WhereClause
的定义tinysql/parser/parser.y:5290
WhereClause:
"WHERE" Expression
{
$$ = $2
}
可以看到,条件部分部分被定义为了表达式Expression
,那么在JoinTable
里面也可以直接用表达式Expression
来定义,因此拆解完毕之后,需要补充的定义如下:
TableRef JoinType "JOIN" TableRef "ON" Expression
这样我们就完成了对于t1 join t2 left join t3 on t2.id = t3.id
的适配,那么接下来就是要确定如何处理这部分的语法,从JoinTable
的原定义中,可以看到语法中的JoinTable
对应的是go中的ast.Join
结构,那么在tinysql/parser/ast/dml.go:56可以找到定义如下:
// Join represents table join.
type Join struct {
node
// Left table can be TableSource or JoinNode.
Left ResultSetNode
// Right table can be TableSource or JoinNode or nil.
Right ResultSetNode
// Tp represents join type.
Tp JoinType
// On represents join on condition.
On *OnCondition
}
这里我们需要对ast.Join.Left
、ast.Join.Right
、ast.Join.Tp
、ast.Join.On
进行赋值,参考原始定义中的写法进行定义如下:
JoinTable:
/* Use %prec to evaluate production TableRef before cross join */
TableRef CrossOpt TableRef %prec tableRefPriority
{
$$ = &ast.Join{Left: $1.(ast.ResultSetNode), Right: $3.(ast.ResultSetNode), Tp: ast.CrossJoin}
}
/* Your code here. */
| TableRef JoinType "JOIN" TableRef "ON" Expression %prec tableRefPriority
{
$$ = &ast.Join{
Left: $1.(ast.ResultSetNode), // 这里取出第1个部分TableRef的值作为Left
Right: $4.(ast.ResultSetNode), // 这里取出第4个部分TableRef的值作为Right
Tp: $2.(ast.JoinType), // 这里取出第2部分的JoinType的值作为Tp
On: &ast.OnCondition{Expr: $6.(ast.ExprNode)}, // 这里是我们新增的部分,标识条件,即第6部分Expression的值
}
}
$$
表示本节点的值,$[n]
标识结构中第n的部分的值,从1开始。