0303: 解析select语句

struct 定义

目前只实现了一小部分功能,所以 select 语句只支持一种固定的形式:通过主键来查询某一行数据。比如一个主键是 (c, d) 的表,只能做这种查询:

select a,b from t where c=1 and d='e';

用数据结构来表示:

StmtSelect{
    table: "t",
    cols: []string{"a", "b"},
    keys: []NamedCell{
        {column: "c", value: Cell{Type: TypeI64, I64: 1}},
        {column: "d", value: Cell{Type: TypeStr, Str: []byte("e")}},
    },
}

暂时定义 struct 如下,以后会逐步增加功能:

type StmtSelect struct {
    table string
    cols  []string
    keys  []NamedCell
}
type NamedCell struct {
    column string
    value  Cell
}

语法解析

语法解析就是从左到右消耗 token,同时构建数据结构的过程。比如要解析 a = 123 这部分语法,输出 NamedCell。依次调用 tryName()tryPunctuation()parseValue()

func (p *Parser) parseEqual(out *NamedCell) error {
    var ok bool
    out.column, ok = p.tryName()
    if !ok {
        return errors.New("expect column")
    }
    if !p.tryPunctuation("=") {
        return errors.New("expect =")
    }
    return p.parseValue(&out.value)
}

这里新加了 tryPunctuation() 函数。类似 tryKeyword(),但是消耗的一个符号。规则比 keyword 简单。

在解析 WHERE 部分时,循环调用 parseEqual()。即使是复杂的语法,拆分后都很容易。

解析 select 语句

首先开头的 select 关键字可以区分不同的SQL语句,不过现在只实现一个语句。然后是一串逗号分隔的列名,直到碰到 from 终止。

func (p *Parser) parseSelect(out *StmtSelect) error {
    if !p.tryKeyword("SELECT") {
        return errors.New("expect keyword")
    }
    for !p.tryKeyword("FROM") {
        if len(out.cols) > 0 && !p.tryPunctuation(",") {
            return errors.New("expect comma")
        }
        if name, ok := p.tryName(); ok {
            out.cols = append(out.cols, name)
        } else {
            return errors.New("expect column")
        }
    }
    if len(out.cols) == 0 {
        return errors.New("expect column list")
    }
    var ok bool
    if out.table, ok = p.tryName(); !ok {
        return errors.New("expect table name")
    }
    return p.parseWhere(&out.keys)
}

解析 WHERE

这部分暂时只支持用 AND 分隔的 a = 123。跟 SELECT a,b 类似,只不过逗号换成 ANDtryName() 换成 parseEqual()。读者自己完成吧:

func (p *Parser) parseWhere(out *[]NamedCell) error