手写sqlite数据库Part 1——介绍sqlite与REPL

 🌈hello,你好鸭,我是Ethan,西安电子科技大学大三在读,很高兴你能来阅读。

✔️目前博客主要更新Java系列、项目案例、计算机必学四件套等。
🏃人生之义,在于追求,不在成败,勤通大道。加油呀!

🔥个人主页:Ethan Yankang
🔥推荐:史上最强八股文 || 一分钟看完我的上千篇博客

🔥温馨提示:划到文末发现专栏彩蛋   点击这里直接传送

🔥本篇概览:数据库的前世今生 || 详细解了手写sqlite数据库Part 1——介绍sqlite与REPL。🌈⭕🔥


【计算机领域一切迷惑的源头都是基本概念的模糊,算法除外】


🌈序言

数据库乃我长久之志也,此关必过。今日既得之方向,应按此路学之习之,而长久不可懈怠。

前一系列文章详细讲解了XX,建议先将这部分知识掌握之后再来学习本篇内容,点击查看。


🔥 手写类sqlite数据库系列

🔥 手写底层系列


1.夺命7连问:

数据以什么格式存储?存在哪里?

数据什么时候从内存存到磁盘?

为什么每一个表只能有一个主键?

事务回滚机制是怎样工作的?

索引是是怎么组织的?

全表扫描什么时候发生?怎样扫描的?

我们的sql查询语句是怎样保存与工作的?

总而言之?数据库系统是怎么工作的?


以上现在不会没关系,会在终章给出详尽的解答!


2.sqlite架构

下面逐步解释之:

(1)前端架构:

标记器(Tokenizer):
解析器(Parser):
代码生成器(Code Generator):

前端的输入是一个 SQL 查询。输出是 SQLite 虚拟机字节码(本质上是一个可以在数据库上运行的编译程序)。

(2)后端架构:

虚拟机(virtual machine):

        虚拟机将前段生成的字节码作为指令,他够操作组织在B树上的一个或多个表或者索引。虚拟机本质上是一个关于字节码指令类型的大型 switch 语句

B树(B-tree):

        每一颗B树包含许多节点,每个节点的长度为一页。通过向页面管理器发送命令,B树能从磁盘上检索页面或者将页面存入磁盘

页面管理器(pager):

        页面管理器接受命令来读或写页面上的数据,他负责在数据库文件中的适当偏移处进行读取或写入,并且会在内存中保存最近使用的页面的缓存。他决定了这些页面什么时候需要被写回磁盘。

操作系统接口(os interface):

        os接口是更具编译sqlite的操作系统而有所不同的层,会调用操作系统接口,完成操作硬件的功能。


3.REPL:读取执行打印循环

实现过程:

主体是一个mian无限循环,一直会读取用户输入的文本,这里输入是使用的自定义的getline()完成的,该函数返回输入的字符长度。参数为一个专门保存命令的结构体,输入的文本就保存在结构体的char*里面。后续对这里面的char* 进行解析就好了。

本节仅仅实现了判断退出(.exit)与空输入的情况。

(1)主体函数:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<stdbool.h>
#include <errno.h>
#include <stdint.h>

//#include "getline.h"

ssize_t getline(char **lineptr, size_t *n, FILE *stream);

/*
 * the data structure is for input token
 */
typedef struct {
    char *buffer;
    size_t buffer_length;//size_t equals to unsigned long long int
    ssize_t input_length;//ssize_t equals to long long int
} InputBuffer;


/*
 * just initialised a new buffer
 * return the structure of buffer for containing the next input token
 */
InputBuffer *new_input_buffer() {//
    InputBuffer *input_buffer = malloc(sizeof(InputBuffer));//dynamic memory in the heap
    input_buffer->buffer = NULL;//the token chars haven't pointed to anything
    input_buffer->buffer_length = 0;
    input_buffer->input_length = 0;
    return input_buffer;
}


void print_prompt() {
    printf("EYKDB > ");//can be custom
}


/*
 * input the token now!
 * used the getline function
 * two details:
 * 1.replace the enter char with '\0' at the end
 * 2.the real length must minus 1
 */
void read_input(InputBuffer *inputBuffer) {
    ssize_t byte_read = getline(&(inputBuffer->buffer), &(inputBuffer->buffer_length), stdin);
    if (byte_read <= 0) {
        printf("Error reading input\n");
        exit(EXIT_FAILURE);
    }
    inputBuffer->input_length = byte_read - 1;
    inputBuffer->buffer[byte_read - 1] = 0;
}

/*
 * free the dynamic memory allocated in the input_buffer(all the point's data should be free)
 */
void close_input_buffer(InputBuffer *input_buffer) {
    free(input_buffer->buffer);
    free(input_buffer);
}

/*
 * main function: just a infinite loop
 */
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);
        }
    }
}

(2)getline函数:

/*
 * the getline function
 * lineptr :
 * a pointer to the variable we use to point to the buffer containing the read line.
 * If it set to NULL it is mallocatted by getline and should thus be freed by the user, even if the command fails.
 *
 * n :
 * a pointer to the variable we use to save the size of allocated buffer.
 *
 * stream :
 * the input stream to read from. We’ll be reading from standard input.
 *
 * return value :
 * the number of bytes read, which may be less than the size of the buffer.
 */

ssize_t getline(char **lineptr, size_t *n, FILE *stream) {
    size_t pos;
    int c;

    if (lineptr == NULL || stream == NULL || n == NULL) {
        errno = EINVAL;
        return -1;
    }

    c = getc(stream);
    if (c == EOF) {
        return -1;
    }

    if (*lineptr == NULL) {
        *lineptr = malloc(128);
        if (*lineptr == NULL) {
            return -1;
        }
        *n = 128;
    }

    pos = 0;
    while (c != EOF) {
        if (pos + 1 >= *n) {
            size_t new_size = *n + (*n >> 2);
            if (new_size < 128) {
                new_size = 128;
            }
            char *new_ptr = realloc(*lineptr, new_size);
            if (new_ptr == NULL) {
                return -1;
            }
            *n = new_size;
            *lineptr = new_ptr;
        }

        ((unsigned char *) (*lineptr))[pos++] = c;
        if (c == '\n') {
            break;
        }
        c = getc(stream);
    }

    (*lineptr)[pos] = '\0';
    return pos;
}


4.运行结果:



📣非常感谢你阅读到这里,如果这篇文章对你有帮助,希望能留下你的点赞👍 关注❤收藏✅ 评论💬,大佬三连必回哦!thanks!!!
📚愿大家都能学有所得,功不唐捐!

👇下面是专栏彩蛋系列,你会喜欢的!👇


💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖

热门专栏

🌈🌈专栏彩蛋系列

🌈🌈史上最全八股文,欢迎收藏

🌈🌈一篇文章了解我的上千篇博客

💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值