共计 5979 个字符,预计需要花费 15 分钟才能阅读完成。
前文回顾:实现一个简略的 Database1(译文)
译注:cstsck 在 github 保护了一个简略的、相似 sqlite 的数据库实现,通过这个简略的我的项目,能够很好的了解数据库是如何运行的。本文是第二篇,次要是实现数据库的前端组件,编译器与虚拟机局部性能
Part 2 世界上最简略的 SQL 编译器与虚拟机
咱们正在实现一个 sqlite 的克隆版本。sqlite 的前端是 SQL 编译器,编译器用来解析字符串并输入一个外部的示意,叫做字节码。
这些字节码被传到虚拟机(virtual machine),在虚拟机中,字节码将被执行。
SQLite Architecture (https://www.sqlite.org/arch.html)
像这样把事件分成两个步骤(SQL 编译和虚拟机)有以下两个长处:
- 缩小各个局部的复杂性(例如:虚拟机不必关怀输出语句语法错误)
- 容许只编译通用查问一次,而后对生成的字节码进行缓存,以此来晋升性能
有了这些想法,让咱们来重构主函数,在程序中反对了两个新的关键字:
译注:上面代码中行结尾加减号是绝对与第一局部(part 1)的实现,减少或者删除的代码。代码对 main()重构以适宜辨认新关键字,在第一局部中,main()函数只能辨认“.exit”关键字,也就是程序退出命令。
int main(int argc, char* argv[]) {InputBuffer* input_buffer = new_input_buffer();
while (true) {print_prompt();
read_input(input_buffer);
- if (strcmp(input_buffer->buffer, ".exit") == 0) {- exit(EXIT_SUCCESS);
- } else {- printf("Unrecognized command'%s'.\n", input_buffer->buffer);
+ if (input_buffer->buffer[0] == '.') {+ switch (do_meta_command(input_buffer)) {+ case (META_COMMAND_SUCCESS):
+ continue;
+ case (META_COMMAND_UNRECOGNIZED_COMMAND):
+ printf("Unrecognized command'%s'\n", input_buffer->buffer);
+ continue;
+ }
}
+
+ Statement statement;
+ switch (prepare_statement(input_buffer, &statement)) {+ case (PREPARE_SUCCESS):
+ break;
+ case (PREPARE_UNRECOGNIZED_STATEMENT):
+ printf("Unrecognized keyword at start of'%s'.\n",
+ input_buffer->buffer);
+ continue;
+ }
+
+ execute_statement(&statement);
+ printf("Executed.\n");
}
}
非 SQL 语句,像“.exit”这样的命令被称为“meta-commands”。它们都是以“.”结尾,所以咱们在一个独立的函数中查看并且解决它们。
译注:在上边代码中应用了独自的 if+switch 来解决了以“.”结尾的“meta-commands”。
接下来,减少一个步骤,将输出行命令转换成外部示意的语句。这是 sqlite 前端的一个破解版本。
最初,我门将预编译语句传递到 execute_statement()函数,这个函数将最终变成咱们的虚拟机。
留神咱们的两个新函数返回 enum(枚举)类型的来示意胜利或者失败:
typedef enum {
META_COMMAND_SUCCESS,
META_COMMAND_UNRECOGNIZED_COMMAND
} MetaCommandResult;
typedef enum {PREPARE_SUCCESS, PREPARE_UNRECOGNIZED_STATEMENT} PrepareResult;
在输出命令行语句无奈辨认时,打印“Unrecognized statement”输入?这个看起来像是异样(exception)。我不喜爱应用 exception(并且 C 语言甚至不反对 exception),所以我在任何可行的中央都是用 enum 后果码做返回。如果我的 switch 语句没有解决 enum 成员,C 编译器会报错,所以咱们能感到小有信念,咱们能解决所有函数后果。预计未来会有更多的后果代码被退出。
do_meta_command()函数只是对已有的性能的一个封装,为更多的命令留出空间:
MetaCommandResult do_meta_command(InputBuffer* input_buffer) {if (strcmp(input_buffer->buffer, ".exit") == 0) {exit(EXIT_SUCCESS);
} else {return META_COMMAND_UNRECOGNIZED_COMMAND;}
}
咱们的“prepared statement”当初只蕴含一个 enum(有两个可能值)。在语句中将会蕴含更多的咱们容许的参数数据:
typedef enum {STATEMENT_INSERT, STATEMENT_SELECT} StatementType;
typedef struct {StatementType type;} Statement;
prepare_statement()函数(咱们的 SQL 编译器)当初还不能了解 SQL。事实上,它当初只能了解两个单词:
译注:上面的代码实现了对 insert 和 select 要害的解析。
PrepareResult prepare_statement(InputBuffer* input_buffer,
Statement* statement) {if (strncmp(input_buffer->buffer, "insert", 6) == 0) {
statement->type = STATEMENT_INSERT;
return PREPARE_SUCCESS;
}
if (strcmp(input_buffer->buffer, "select") == 0) {
statement->type = STATEMENT_SELECT;
return PREPARE_SUCCESS;
}
return PREPARE_UNRECOGNIZED_STATEMENT;
}
留神,因为“insert”关键字前面有追随数据,所以为“insert”应用了 strncmp()库函数来比对输出值。(例如输出语句为:insert 1 cstack [email protected])
译注:C 库函数 int strncmp(const char str1, const char str2, size_t n) 是把输出参数 str1 和 str2 进行比拟,最多比拟入参的前 n 个字节。
最初,execute_statement()函数中蕴含了一些桩(stubs):
译注:stubs(一小块代码),是为了实现测试代码进行,会硬编码一些输出和输入,即在 execute_statement()函数中对 prepare_statement()函数处理结果进行了援用并解决。
void execute_statement(Statement* statement) {switch (statement->type) {case (STATEMENT_INSERT):
printf("This is where we would do an insert.\n");
break;
case (STATEMENT_SELECT):
printf("This is where we would do a select.\n");
break;
}
}
留神这里没有返回任何错误码,这是因为在这里还不会有任何报错产生。
译注:目前为止,程序可解析“.exit”、“insert xxx”、”select xxx” 命令,其余不会辨认,只输入“Unrecognized command ‘xxx’”,所以不会有什么报错输入。参考上面的演示。
做了这些重构后,咱们的程序就能辨认两个新的关键字了。
~ ./db
db > insert foo bar
This is where we would do an insert.
Executed.
db > delete foo
Unrecognized keyword at start of 'delete foo'.
db > select
This is where we would do a select.
Executed.
db > .tables
Unrecognized command '.tables'
db > .exit
~
咱们的数据库骨架正在造成 … 如果它能存储数据不是很好吗?在下一部分,咱们会实现 insert 和 select,创立世界上最差劲的数据存储。
同时,上面是这部分重构的整个代码不同之处:
@@ -10,6 +10,23 @@ struct InputBuffer_t { } InputBuffer;
+typedef enum {
+ META_COMMAND_SUCCESS,
+ META_COMMAND_UNRECOGNIZED_COMMAND
+} MetaCommandResult;
+
+typedef enum {PREPARE_SUCCESS, PREPARE_UNRECOGNIZED_STATEMENT} PrepareResult;
+
+typedef enum {STATEMENT_INSERT, STATEMENT_SELECT} StatementType;
+
+typedef struct {
+ StatementType type;
+} Statement;
+
InputBuffer* new_input_buffer() {InputBuffer* input_buffer = malloc(sizeof(InputBuffer));
input_buffer->buffer = NULL;
@@ -40,17 +57,67 @@ void close_input_buffer(InputBuffer* input_buffer) {free(input_buffer);
}
+MetaCommandResult do_meta_command(InputBuffer* input_buffer) {+ if (strcmp(input_buffer->buffer, ".exit") == 0) {+ close_input_buffer(input_buffer);
+ exit(EXIT_SUCCESS);
+ } else {
+ return META_COMMAND_UNRECOGNIZED_COMMAND;
+ }
+}
+
+PrepareResult prepare_statement(InputBuffer* input_buffer,
+ Statement* statement) {+ if (strncmp(input_buffer->buffer, "insert", 6) == 0) {
+ statement->type = STATEMENT_INSERT;
+ return PREPARE_SUCCESS;
+ }
+ if (strcmp(input_buffer->buffer, "select") == 0) {
+ statement->type = STATEMENT_SELECT;
+ return PREPARE_SUCCESS;
+ }
+
+ return PREPARE_UNRECOGNIZED_STATEMENT;
+}
+
+void execute_statement(Statement* statement) {+ switch (statement->type) {+ case (STATEMENT_INSERT):
+ printf("This is where we would do an insert.\n");
+ break;
+ case (STATEMENT_SELECT):
+ printf("This is where we would do a select.\n");
+ break;
+ }
+}
+
int main(int argc, char* argv[]) {InputBuffer* input_buffer = new_input_buffer();
while (true) {print_prompt();
read_input(input_buffer);
- if (strcmp(input_buffer->buffer, ".exit") == 0) {- close_input_buffer(input_buffer);
- exit(EXIT_SUCCESS);
- } else {- printf("Unrecognized command'%s'.\n", input_buffer->buffer);
+ if (input_buffer->buffer[0] == '.') {+ switch (do_meta_command(input_buffer)) {+ case (META_COMMAND_SUCCESS):
+ continue;
+ case (META_COMMAND_UNRECOGNIZED_COMMAND):
+ printf("Unrecognized command'%s'\n", input_buffer->buffer);
+ continue;
+ }
}
+
+ Statement statement;
+ switch (prepare_statement(input_buffer, &statement)) {+ case (PREPARE_SUCCESS):
+ break;
+ case (PREPARE_UNRECOGNIZED_STATEMENT):
+ printf("Unrecognized keyword at start of'%s'.\n",
+ input_buffer->buffer);
+ continue;
+ }
+
+ execute_statement(&statement);
+ printf("Executed.\n");
}
}
Enjoy GreatSQL :)
## 对于 GreatSQL
GreatSQL 是由万里数据库保护的 MySQL 分支,专一于晋升 MGR 可靠性及性能,反对 InnoDB 并行查问个性,是实用于金融级利用的 MySQL 分支版本。
相干链接:GreatSQL 社区 Gitee GitHub Bilibili
GreatSQL 社区:
欢送来 GreatSQL 社区发帖发问
https://greatsql.cn/
技术交换群:
微信:扫码增加
GreatSQL 社区助手
微信好友,发送验证信息加群
。