Skip to content

Commit

Permalink
📃 docs: blog update
Browse files Browse the repository at this point in the history
  • Loading branch information
SaZiKK committed Dec 12, 2024
1 parent 80e486e commit 485786c
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 28 deletions.
2 changes: 1 addition & 1 deletion _data/locales/zh-CN.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ tabs:
categories: 分类
tags: 标签
archives: 时间线
about: 关于
about: 友链

# the text displayed in the search bar & search results
search:
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -163,20 +163,15 @@ struct ConditionSqlNode {
filter 会记录表达式左右的值以及运算符,显然这里也需要支持子查询,因此新增了一种子查询类型。从这里就可以看出,如果先前先把所有子查询、`Value` 等都统一解析为表达式,那么这里的适配就会简单很多,统一当作 `Expression` 处理即可。
### 2024.10.13
### 2024.10.15
#### 完成支持子查询
#### 继续支持子查询
为了保证子查询能够被成功的执行,除了支持递归解析子查询外,还要提前将子查询的结果查出来并记录、外传,这样才能保证外层查询正常进行。
具体的实现方法参考了正常查询的实现,但是把查询结果的过程提前到了对子查询类型 `get_value` 时。由于所有的值都是通过 `get_value` 函数取出并进行比较的,因此在这里实现子查询变量的实际查询可以保证不重不漏,同时这也近似一种懒分配的思想,即需要使用时才查询出实际的值。
```c++
// src/observer/sql/expr/expression.cpp
RC SubQueryExpr::get_value(const Tuple &tuple, Value &value) const {
LOG_WARN("invalid operation. cannot get single value from subquery expression");
return RC::INVALID_ARGUMENT;
}
RC SubQueryExpr::get_value_list(std::vector<Value> &value_list) {
LogicalPlanGenerator generator;
SelectStmt *selectstmt = this->sub_query_;
Expand Down Expand Up @@ -214,12 +209,106 @@ RC SubQueryExpr::get_value_list(std::vector<Value> &value_list) {
return RC::SUCCESS;
}
```
void SubQueryExpr::set_logical_operator(std::unique_ptr<LogicalOperator, void (*)(LogicalOperator *)> logical_op) {
logical_operator = std::move(logical_op);
}
### 2024.10.16
#### 支持`exists` `in`
`exists` 和 `not exists` 的实现较为简单,因为仅需要判断后面的表达式是否为 true 即可。`in` 和 `not in` 的实现稍复杂,因为需要考虑左右是子查询的情况,在子查询的实现基础上稍加适配即可。
### 2024.10.19
#### 重写比较,完成子查询
`value` 之间的比较是数据库查询的基础,因为我们引入了子查询,子查询的结果可能是一组数据即 `value_list` 甚至 `tuple_list` ,因此比较函数需要大改。
我定义了一系列比较标识符用于定义每个类型的比较:
```c++
// src/observer/sql/expr/expression.h
enum CompType {
VAL_VAL, ///< 值-值
VAL_LIST, ///< 值-列表
LIST_VAL, ///< 列表-值
LIST_LIST, ///< 列表-列表
VAL_TUPLES, ///< 值-元组列
TUPLES_VAL, ///< 元组列-值
TUPLES_TUPLES, ///< 元组列-元组列
TUPLES_LIST, ///< 元组列-列表
LIST_TUPLES, ///< 列表-元组列
VAL_TUPLE, ///< 值-元组
LIST_TUPLE, ///< 列表-元组
TUPLE_TUPLE, ///< 元组-元组
TUPLES_TUPLE, ///< 元组列-元组
TUPLE_VAL, ///< 元组-值
TUPLE_LIST, ///< 元组-列表
TUPLE_TUPLES, ///< 元组-元组列
};
```
随后我在 `ComparisonExpr` 的 `get_value` 函数中对于比较进行预处理并分类,确定 CompType 然后再调用 `compare_value` 函数进行实际比较。 `get_value` 函数获取 `ComparisonExpr` 的结果,即比较的结果。
修改完`get_value`引入比较标识符之后,就可以使用新的标识符统一的重构`compare_value`函数:
![重构后的 compare_value](../assets/figures/miniob/image.png)
重构后的比较函数架构清晰,大大便利了后续开发。
### 2024.10.20
#### 完成 `multi-index` 前端支持
`multi-index` 即允许一次索引多个字段,实现需要深入修改索引实现的B+树,不仅要让B+树的各个节点支持存储多个字段的值,还要为多个字段支持比较,确保B+树可以正常排序。
由于对于索引设计完全不了解,对于B+树也不太熟悉,因此先实现前端的支持,再一步步根据已实现的 `create_index` 等 stmt 去逐步修改和了解索引。
### 2024.10.21
#### 基本完成 `multi-index` 支持
这一步需要做的修改非常的复杂和分散,但是基本有迹可循。
![multi-index](../assets/figures/miniob/multi.png)
void SubQueryExpr::set_physical_operator(std::unique_ptr<PhysicalOperator, void (*)(PhysicalOperator *)> physical_op) {
physical_operator = std::move(physical_op);
因为我不熟悉B+树相关代码,因此我采取了一种比较投机取巧的做法:哪里错了改哪里。我将 `create index stmt` 传入的字段 `Field` 改为了 `Vector<Field>` 以便一次传入多个字段进行索引,随后我根据 `clangd` 的报错进行修改,把所有相关函数的参数全部修改为支持 `const std::vector<const char *>`,将 vector 逐步传递并修改相关实现,如计算字段长度改为计算多个字段长度总和;修改 `memcpy` 的长度以复制全部的字段等等。
这种方法相当的有效,因为大部分修改都仅是函数参数和函数调用的修改,同时这样逐层深入的方式也更加有利于我理解索引部分的代码。整体没有报错之后, `multi-index` 的实现便基本完成。
### 2024.10.22
#### 修复 `IndexFileHeader`
`IndexFileHeader` 结构体用于记录B+树的元数据,由于 `multi-index` 需要修改B+树以支持存储多个字段,因此相应的 `header` 也要修改。
![IndexFileHeader](../assets/figures/miniob/IndexFileHeader.png)
#### 检查 `Field` 是否存在
测例可能出现创建索引的字段不存在的情况,需要加以判断。到这里 `multi-index` 的实现就结束了。
### 2024.10.23
#### 初步实现 `unique`
`unique` 即支持唯一索引。由于其设计与 `multi-index` 有很大相关性,因此一起实现。
`unique` 的实现思路非常巧妙。miniob 的源码实现中就包含了查重,且对于两个节点的存储的键值对,他的查重策略是从前到后每个字段逐一对比,使用专门的对比器记录了索引中各个字段的长度,确保对比的时候不会错位(因为是暴力的指针加 offset ),如果完全相同,就会返回一个错误 `RC::RECORD_DUPLICATE_KEY`。在实现 `unique` 之前,索引的字段之所以可以插入/更新重复值,是因为在对比时,键值对的开头是自己的唯一的 rid,因此绝对不会出现重复的情况。
综合以上的查重逻辑,`unique` 的实现逻辑就非常明晰了:对于唯一索引,把所有键值对的 rid 置零。
```c++
// src/observer/storage/index/bplus_tree.cpp
MemPoolItem::item_unique_ptr BplusTreeHandler::make_keys(const std::vector<const char *> user_keys, const RID &rid) {
MemPoolItem::item_unique_ptr key = mem_pool_item_->alloc_unique_ptr();
if (key == nullptr) {
LOG_WARN("Failed to alloc memory for key.");
return nullptr;
}
int offset = 0;
// 创建字段与rid的键值对
for (int i = 0; i < file_header_.keys_num; i++) {
memcpy(static_cast<char *>(key.get()) + offset, user_keys[i], file_header_.attr_lengths[i]);
offset += file_header_.attr_lengths[i];
}
if (file_header_.is_unique) {
// 对于重复唯一索引,由于键值的比较机制,我们只要保证所有的rid都相等,那最后就会被查重掉,因此这里给rid赋值为固定值
memset(static_cast<char *>(key.get()) + offset, 0, sizeof(rid));
} else {
memcpy(static_cast<char *>(key.get()) + offset, &rid, sizeof(rid));
}
return key;
}
```
支持了查重之后,还有另一个问题就是 recovery,即如果在更新和插入的过程中遇到问题,需要回滚所有修改。在这里 `insert` 的实现略为简单,遇到问题仅需要直接删除所有同批次插入的值即可,但是由于 `update` 还需要保证变回原来的值,所以实现较为简单粗暴。我记录了整个索引列的所有内容,也就是存了一份 old_data,如果更新遇到问题,就使用 old_data 整个覆盖回去,这样就实现了 table 级的数据回滚。实现比较粗糙,处于时间考虑只能先这样了。
### 2024.10.24
#### `unique` 支持 `NULL` 值
`NULL` 是 `unique` 实现的一个难点,因为所有 `NULL` 都要保证可以重复插入,在这里由于我的队友dmx实现 `NULL` 的方式非常难崩,所以我的实现也非常难崩。我们的 `NULL` 值是直接使用特殊字符特判,从ASCII码角落里翻出来那种超级抽象的字符,所以我的实现就是在最底层的查重函数那里特判,如果有这个字符直接返回。
这种属于赛博案底的代码写在博客里是不是算赛博自首。
Binary file added assets/figures/miniob/IndexFileHeader.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/figures/miniob/image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/figures/miniob/multi.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 485786c

Please sign in to comment.