前文回顾:实现一个简略的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'”,所以不会有什么报错输入。参考上面的演示。

做了这些重构后,咱们的程序就能辨认两个新的关键字了。

~ ./dbdb > insert foo barThis is where we would do an insert.Executed.db > delete fooUnrecognized keyword at start of 'delete foo'.db > selectThis is where we would do a select.Executed.db > .tablesUnrecognized 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社区助手微信好友,发送验证信息加群