0305: 执行SQL语句

执行解析后的SQL

之前已经实现了一些关系型数据库的函数接口:

func (db *DB) Select(schema *Schema, row Row) (ok bool, err error)
func (db *DB) Insert(schema *Schema, row Row) (updated bool, err error)
func (db *DB) Update(schema *Schema, row Row) (updated bool, err error)
func (db *DB) Delete(schema *Schema, row Row) (deleted bool, err error)

现在又将SQL解析成了各种 StmtXXX,可以把这两部分打通:

type SQLResult struct {
    Updated int
    Header  []string
    Values  []Row
}

func (db *DB) ExecStmt(stmt interface{}) (r SQLResult, err error) {
    switch ptr := stmt.(type) {
    case *StmtCreatTable:
        err = db.execCreateTable(ptr)
    case *StmtSelect:
        r.Header = ptr.cols
        r.Values, err = db.execSelect(ptr)
    case *StmtInsert:
        r.Updated, err = db.execInsert(ptr)
    case *StmtUpdate:
        r.Updated, err = db.execUpdate(ptr)
    case *StmtDelete:
        r.Updated, err = db.execDelete(ptr)
    default:
        panic("unreachable")
    }
    return
}

SQLResult 返回一条语句的执行结果:

储存表定义

各种操作都依赖 schema,所以首先要实现 execCreateTable(),把表定义序列化后储存到一个 key 里。 DB 增加一个 map 缓存,key 是表名:

type DB struct {
    KV     KV
    tables map[string]Schema
}
func (db *DB) GetSchema(table string) (Schema, error) {
    schema, ok := db.tables[table]
    if !ok {
        val, ok, err := db.KV.Get([]byte("@schema_" + table))
        if err == nil && ok {
            err = json.Unmarshal(val, &schema)
        }
        if err != nil {
            return Schema{}, err
        }
        if !ok {
            return Schema{}, errors.New("table is not found")
        }
        db.tables[table] = schema
    }
    return schema, nil
}

实现 execCreateTable()

  1. StmtCreatTable 转化为 Schema
  2. Schema 储存到 @schema_ + 表名这个 key 里。
  3. 加到 DB.tables map 里。
func (db *DB) execCreateTable(stmt *StmtCreatTable) (err error)

执行 select 语句

建议拆分成几个小函数,其他语句可以复用:

  1. lookupColumns():检查列名(select a,b),返回 schema.Cols 数组下标。
  2. makePKey():检查 WHERE 是否是主键,返回一个填充了主键的 Row
  3. subsetRow():只返回 select a,b 里的列。
func (db *DB) execSelect(stmt *StmtSelect) ([]Row, error) {
    schema, ok := db.tables[stmt.table]
    if !ok {
        return nil, errors.New("table is not found")
    }
    indices, err := lookupColumns(schema.Cols, stmt.cols)
    if err != nil {
        return nil, err
    }

    row, err := makePKey(&schema, stmt.keys)
    if err != nil {
        return nil, err
    }
    if ok, err = db.Select(&schema, row); err != nil {
        return nil, err
    }
    if !ok {
        return nil, nil
    }

    row = subsetRow(row, indices)
    return []Row{row}, nil
}

执行更新语句

实现其他SQL语句(调用 DB.Insert()DB.Update()DB.Delete()):

func (db *DB) execInsert(stmt *StmtInsert) (count int, err error)
func (db *DB) execUpdate(stmt *StmtUpdate) (count int, err error)
func (db *DB) execDelete(stmt *StmtDelete) (count int, err error)

要求:

其中 update 语句不需要支持更新主键,也就是说 update 只是更新 KV 里的 V。如果要更新主键,相对于删除 KV 里的 key 再重新插入,实际应用上也很少这种需求。

您正在阅读免费版教程,从第4章起只有简单的指引,适合爱好挑战和自学的读者。
可以购买有详细指导+背景知识的完整版