1

本文主要介绍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.Leftast.Join.Rightast.Join.Tpast.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开始。


Destiny池鱼
28 声望6 粉丝

bug free