## 1.代码框架

### 1.1 文件列表

|      路径       |           重要类            |                     说明                     |
| :-------------: | :-------------------------: | :------------------------------------------: |
|     common      |            Init             |               初始化服务器配置               |
|      event      |   SessionEvent、SqlEvent    |           处理SQL请求、SQL调试信息           |
|       net       |           Server            |       网络模块，负责客户端与服务端通信       |
|     session     |        SessionStage         |   创建Session窗口（每次输入SQL语句时创建）   |
|   sql/parser    |  ParseStage、ResolveStage   | SQL解析处理阶段，将SQL解析为语法树和Stmt语义 |
|    sql/stmt     |            Stmt             |          定义各种SQL语句的Stmt语义           |
|  sql/operator   |              -              |               实现各种查询计划               |
|    sql/expr     |      Expression、Tuple      |         定义表达式类型、表示一行数据         |
|  sql/optimizer  |        OptimizeStage        |                 查询优化阶段                 |
|  sql/executor   |        ExecuteStage         |   SQL执行阶段，实现了各种SQL语句的Executor   |
| sql/plan_cache  |       PlanCacheStage        |          计划缓存阶段，本项目未实现          |
| sql/query_cache |       QueryCacheStage       |          查询缓存阶段，本项目未实现          |
|   storage/db    |             Db              |                 数据库实现类                 |
|  storage/table  |            Table            |                 数据表实现类                 |
| storage/record  |           Record            |               数据表中的记录类               |
|  storage/field  |      Field、FieldMeta       |               数据表中的字段类               |
|  storage/index  |        Index、B+Tree        |               索引类、B+树实现               |
|   storage/trx   |              -              |                    事务类                    |
|  storage/clog   |              -              |      定义事务操作的类型和日志文件的格式      |
| storage/buffer  | Page、Frame、DiskBufferPool |             定义页、页帧、缓冲池             |
| storage/persist |       PersistHandler        |    数据持久化类，定义打开、删除等文件操作    |
| storage/common  |          MetaUtil           |        工具类，指定数据库文件的扩展名        |

### 1.2 枚举类

|       类       |          路径           |      说明      |
| :------------: | :---------------------: | :------------: |
|       RC       |       common/rc.h       |   函数返回码   |
|     CompOp     | sql/parser/parse_defs.h | 描述比较运算符 |
| SqlCommandFlag | sql/parser/parse_defs.h |  SQL语句类型   |
|    ExprType    |  sql/expr/expression.h  |   表达式类型   |
|    AttrType    |   sql/parser/value.h    |    字段类型    |
|    StmtType    |     sql/stmt/stmt.h     |  Stmt语义类型  |
|    CLogType    |   storage/clog/clog.h   |  事务日志类型  |

## 2.运行与调试

### 2.1 运行Miniob

- 以监听TCP端口的方式启动

  - 运行服务端

    ```shell
    ./bin/observer -f ../etc/observer.ini -p 6789
    ```

  - 运行客户端

    ```shell
    ./bin/obclient -p 6789
    ```

- 以监听unix socket的方式启动

  - 运行服务端

    ```shell
    ./bin/observer -f ../etc/observer.ini -s miniob.sock
    ```

  - 运行客户端

    ```shell
    ./bin/obclient -s miniob.sock
    ```

- 以直接执行命令的方式启动

  - **单线程**模式，可以直接在服务端输入命令，不需要客户端

    ```shell
    ./bin/observer -f ../etc/observer.ini -P cli
    ```

## 3. 模块详解

### 3.1 SQL语句执行流程

#### 3.1.1 整体流程

|       类        |                             函数                             |                            服务端                            |
| :-------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
|        -        |                  main@src/observer/main.cpp                  |      启动服务端（解析命令行参数、加载配置文件、初始化）      |
|     Server      |               recv@src/observer/net/server.cpp               | 客户端输入SQL并回车后，Server接收客户端消息并创建SessionEvent |
|  SessionStage   |    handle_request@src/observer/session/session_stage.cpp     |             处理SessionEvent并创建SQLStageEvent              |
|  SessionStage   |      handle_sql@src/observer/session/session_stage.cpp       |                     执行各阶段的SQL处理                      |
| QueryCacheStage | handle_request@src/observer/sql/query_cache/query_cache_stage.cpp |                           查询缓存                           |
|   ParseStage    |    handle_request@src/observer/sql/parser/parse_stage.cpp    |                  解析SQL语句为ParsedSqlNode                  |
|  ResolveStage   |   handle_request@src/observer/sql/parser/resolve_stage.cpp   |                   解析ParsedSqlNode为Stmt                    |
|    分支操作     |                              -                               |             根据SQL语句是否进行查询计划分支操作              |
|  SessionStage   |    handle_request@src/observer/session/session_stage.cpp     |  向客户端返回结果（打印SessionEvent中SqlResult属性的内容）   |

#### 3.1.2 无需查询计划

|       类        |                            函数                            |            功能类             |
| :-------------: | :--------------------------------------------------------: | :---------------------------: |
|  ExecuteStage   | handle_request@src/observer/sql/executor/execute_stage.cpp |    调用CommandExecutor执行    |
| CommandExecutor |   execute@src/observer/sql/executor/command_executor.cpp   |     根据StmtType分发执行      |
|    Executor     |                             -                              | 调用db->调用table执行相关操作 |

#### 3.1.3 需要查询计划

|      类       |                             函数                             |                            功能类                            |
| :-----------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
| OptimizeStage | handle_request@src/observer/sql/optimizer/optimize_stage.cpp | 创建查询计划，并进行优化（在select、insert、update、delete时使用） |
| ExecuteStage  |  handle_request@src/observer/sql/executor/execute_stage.cpp  | 根据StmtType执行查询计划，将结果写入SessionEvent的SqlResult属性 |

### 3.2 查询计划

#### 3.2.1 逻辑计划

- 逻辑算子描述当前执行计划要做什么，比如从表中获取数据，过滤，投影，连接等

- 属性列表

  |     属性     |         类型          |                             说明                             |
  | :----------: | :-------------------: | :----------------------------------------------------------: |
  |    type()    |  LogicalOperatorType  | 指定算子类型，如从表中获取数据、过滤、投影、连接、插入、删除、查看执行计划、更新 |
  |   children   | List<LogicalOperator> |                          子算子列表                          |
  | expressions_ |   List<Expression>    |                  子算子列表对应的表达式列表                  |

#### 3.2.2 物理计划

- 物理算子描述如何执行当前计划，这是与逻辑算子不同之处

- 算子是最小执行单位，是事务性的

- Mniob采用流水线模型（火山模型）执行查询计划，即从根节点开始**递归**调用孩子结点的next()函数，**逐行**获取元组并由下至上进行相应的运算，直到处理完表中的所有元组，同时生成要返回的结果集字符串

- 属性列表

  |      属性       |           类型           |                             说明                             |
  | :-------------: | :----------------------: | :----------------------------------------------------------: |
  |     type()      | 返回PhysicalOperatorType | 指定算子类型，如扫描表、扫描索引、嵌套连接、查看执行计划、过滤、投影、计算、删除等 |
  |    children     |  List<PhysicalOperator>  |                          子算子列表                          |
  |     open()      |        参数为Trx         |                开始执行一个算子时需要开启事务                |
  |     next()      |            -             |       逐行调用子算子的next()，获取其current_tuple()，        |
  |     close()     |            -             |                           关闭算子                           |
  | current_tuple() |        返回Tuple         |                          当前Tuple                           |

## 4. 实验题目

### 4.1 必做题

#### 4.1.1 题目列表

|       名称       | 分值 |   说明   |
| :--------------: | :--: | :------: |
|      basic       |  10  | 基本功能 |
|    drop-table    |  10  |  删除表  |
|      update      |  10  | 更新字段 |
|       date       |  10  | 日期字段 |
|  select-tables   |  10  | 多表查询 |
|   select-meta    |  10  |  表结构  |
| aggregation-func |  10  | 聚合函数 |

#### 4.1.2 drop-table

假设表名为demo

- 分析

  - 需要删除demo.table文件、demo.data文件和关联的索引文件

  - 由于具体执行阶段在CommandExecutor进行分发，因此需要实现DropTableExecutor
  - 相应地，需要实现DropTableStmt

- 实现
  - 实现DropTableExecutor
    - db中实现delete_table接口
    - table中实现destroy接口
      - 删除Meta
      - 删除Data
      - 删除索引
      - 清空buffer pool
  - CommandExecutor增加对StmtType::DROP_TABLE的执行分支

#### 4.1.3 update

- 分析
  - 更新语句涉及修改数据库，可能会用到where语句，因此需要实现查询计划
- 实现
  - 实现UpdateLogicalOperator、UpdatePhysicalOperator
  - 在LogicalPlanGenerator、PhysicalPlanGenerator中分别实现生成UpdateLogicalOperator、UpdatePhysicalOperator的逻辑
  - 实现UpdateStmt::create函数，从而实现Update语句的处理逻辑
    - 重写type
    - 检查参数合法性（表名、字段名不为空）
    - 检查表是否存在
    - 检查字段是否存在
  - 由于可能会用到where语句，因此需要在UpdateStmt中实现FilterStmt

#### 4.1.4 date

要求：增加date字段。字段不会超过2038年2月，不会小于1970年1月1号

- 分析
  - 在parser中添加对date关键字、date取值、date字段的词法语法定义
  - 对于insert/update语句，需要检查插入或更新的date字段值是否合法
  3.	对于select语句，需要检查where条件中出现的date值是否合法

- 实现

  - 在lex中添加date的正则表达式，并在此处将字符串转换为date类型，如果字符串不合法，则该date值为0，便于后续判断
  - 在value.cpp中添加DATES的属性类型、date类型的构造函数、set_date、get_date、实现to_string、实现compare、在联合体中添加date字段date_value_
  - 在filter_stmt中添加对date为0的特判：select/update中检查date是否合法
  - 在insert_stmt中添加对date为0的特判：insert中检查date是否合法
  - 在B+树中添加对compare类型的比较（实际上由于Miniob底层代码逻辑缺陷，此处调用value.cpp中的compare方法实现为好，扩展性更强）

#### 4.1.5 select-tables

- 分析
  - 可以分解为三个操作：两张表分别针对要select的字段进行TableSelect算子、进行Join连接算子、根据where子句进行过滤算子
- 实现
  - 与Update类似，需要在LogicalPlanGenerator、PhysicalPlanGenerator中分别实现生成select逻辑计划、select物理计划的逻辑，此处无需单独实现SelectLogicalOperator、SelectPhyicalOperator，利用已有算子即可实现

#### 4.1.6 aggregation-func

 要求：实现max/min/count/avg函数；包含聚合字段时，只会出现聚合字段；聚合函数中的参数不会是表达式，比如age+1

- 分析

  - 首先在lex和yacc中添加各聚合函数的token
  - select的语法解析树中，select_attrs需要改为select_exprs，即引入聚合函数后，select的内容由字段列表变为表达式列表
  - 在yacc中定义select_expr、select_expr_list、select_exprs、aggr_func
    - select_expr可以是rel_attr、也可以是aggr_func
    - select_expr_list参照attr_list
    - select_exprs可以是select_expr_list，也可以是*表示所有字段
    - aggr_func由aggr_fun_name和select_attr组成
  - 将yacc中定义的语义变量转换为相应数据结构
    - select_expr >> SelectExprNode（指定表达式类型SelectExprType、字段名/聚合函数）
    - aggr_func >> AggrFuncNode（指定聚合函数类型AggrFuncType、字段列表）
    - 修改SelectSqlNode的属性（去除attributes，添加select_exprs）
  - 修改SelectStmt的create逻辑
    - SelectStmt中删除原属性query_fields，添加表达式Expression列表query_exprs
    - 遍历SelectSqlNode中的select_exprs
      - 如果是聚合函数，那么根据SelectExprType的arrgfunc属性创建AggrStmt并转换为Expression
      - 如果是字段
        - 表名为空，列属性为*，添加所有表的所有列
        - 表名为空，列属性不为*，默认为第一个表，如果存在多个表报错
        - 表名不为空
          - 表名为\*，属性不为\*，不合法
          - 表名不为\*，属性为\*，添加该表的所有列（判断表是否存在）
          - 表名不为\*，属性不为\*，添加该列（判断表和列是否存在）

  - 在表达式Expression中添加AggrExpr表示聚合函数表达式
    - 添加ExprType
    - 定义各种聚合函数的操作
  - 修改select逻辑计划、select物理计划的逻辑
    - 实现AggrLogicalOperator、AggrPhysicalOperator

### 4.2 选做题

#### 4.2.1 题目列表

|       名称        | 分值 |     说明     |
| :---------------: | :--: | :----------: |
|      insert       |  10  | 插入多条数据 |
|      unique       |  10  |  唯一性索引  |
|       null        |  10  |     空值     |
|     order-by      |  10  |     排序     |
| simple-sub-query  |  10  |  简单子查询  |
| complex-sub-query |  20  |  复杂子查询  |
|     group-by      |  20  |     分组     |
|    join-tables    |  20  |   连接查询   |
|    expression     |  20  |    表达式    |
|       text        |  20  |   TEXT字段   |
|    multi-index    |  20  |    多索引    |

#### 4.2.2 insert

要求：支持插入多条数据，且要同时失败或成功

- 分析
  - 修改parser模块，使其支持插入多条数据
  - 修改InsertStmt
  - 修改InsertLogicalOperator、InsertPhysicalOperator

#### 4.2.3 null

#### 4.2.4 unique

#### 4.2.5 join-tables

#### 4.2.6 group-by

#### 4.2.7 order-by

