数据库修化神篇四——简介和设置REPL

SQLite克隆|第一步:简介和制作REPL SQLite简介制作简单的REPL(read-execute-print loop) 因为实习工作原因,需要学习SQLite的源码。那我就寻思,干嘛不直接写一个呢,于是找了一个资源,准备从无到有来过一下SQLite的框架。于是在这里做一个记录。 简介 SQLite,是一款轻型的数据库,是遵守ACID的关系数据库管理系统,SQLite是D.RichardHipp用C语言编写的开源嵌入式数据库引擎。它是完全独立的,不具有外部依赖性。它是作PHPV4.3中的一个选项引入的,构建在PHPV5中。SQLite支持多数 阅读详情

第一章 简介和设置REPL

注: 本文翻译参考自db_tutorial.

作为一名Web开发人员,我每天都在工作中使用关系数据库,但它们对我来说是一个黑匣子。我有一些问题:

  • 数据以什么格式保存?(在内存和磁盘上)
  • 它何时从内存移动到磁盘?
  • 为什么每个表只能有一个主键?
  • 回滚事务如何工作?
  • 索引的格式是什么样的?
  • 什么进行全表扫描?如何进行全表扫描?
  • 预处理语句以什么格式保存?

为了弄清楚事情,我们将从头开始编写一个数据库。它是以sqlite为模型的,因为它被设计得很小,比MySQL或PostgreSQL的功能更少,我们更容易理解它。而且sqlite的整个数据库存储在一个文件中!

sqlite sqlite

sqlite网站上有很多关于sqlite内部的文档,另外还有一份SQLite数据库系统:设计和实现的文档(该文档可能无法访问)。

sqlite的架构大致如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bcItHO4V-1687694827023)(./img/arch1.gif)]

查询通过组件链来检索或修改数据。前端包括:

  • tokenizer 分词器
  • parser 解析器
  • code generator 代码生成器

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

后端包括:

  • virtual machine 虚拟机
  • B-tree B树
  • pager 页
  • os interface 界面

虚拟机将前端生成的字节码作为指令。然后,它可以对一个或多个表或索引执行操作,每个表或索引都存储在称为 B 树的数据结构中。VM 本质上是关于字节码指令类型的很大的switch语句。

每个 B 树由许多节点组成。每个节点的长度为一页。B树可以从磁盘检索页面,也可以通过向pager发出命令将其保存回磁盘。

pager接收用于读取或写入数据页的命令。它负责在数据库文件中以适当的偏移量读取/写入。它还在内存中保留最近访问的页面的缓存,并确定何时需要将这些页面写回磁盘。

操作系统接口是因编译 sqlite 所针对的操作系统而异的层。在本教程中,我不打算支持多个平台。

千里之行始于足下,所以让我们从更直接的东西开始:REPL。

制作一个简单的REPL

当您从命令行启动 Sqlite 时,它会启动读取-执行-打印循环:

~ sqlite3
SQLite version 3.16.0 2016-11-04 19:09:39
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> create table users (id int, username varchar(255), email varchar(255));
sqlite> .tables
users
sqlite> .exit
~

为此,我们的 main 函数将有一个无限循环,用于打印提示,获取一行输入,然后处理该行输入:

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);
    }
  }
}

我们将 InputBuffer 定义为我们需要存储以与 getline() 交互的状态的小包装器。(稍后会详细介绍)

typedef struct {
  char* buffer;
  size_t buffer_length;
  ssize_t input_length;
} InputBuffer;

InputBuffer* new_input_buffer() {
  InputBuffer* input_buffer = (InputBuffer*)malloc(sizeof(InputBuffer));
  input_buffer->buffer = NULL;
  input_buffer->buffer_length = 0;
  input_buffer->input_length = 0;

  return input_buffer;
}

接下来, print_prompt() 向用户打印提示。我们在读取每行输入之前执行此操作。

void print_prompt() {
     printf("db > "); 
}

要读取输入行,请使用 getline():

ssize_t getline(char **lineptr, size_t *n, FILE *stream);
  • lineptr :指向我们用来指向包含读取行的缓冲区的变量的指针。如果它设置为 NULL ,则它被 getline 错误定位,因此即使命令失败,用户也应释放它。

  • stream :要从中读取的输入流。我们将从标准输入中读取

  • return value :读取的字节数,可能小于缓冲区的大小。

我们告诉 getline 将读取行存储在 input_buffer->buffer 中,将分配的缓冲区的大小存储在 input_buffer->buffer_length 中。我们将返回值存储在 input_buffer->input_length 中。

buffer 以 null 开头,因此 getline 分配足够的内存来保存输入行并使其指向 buffer 。

void read_input(InputBuffer* input_buffer) {
#ifdef _WIN32
  size_t len;
  fgets(input_buffer->buffer, MAX_INPUT_BUFFER, stdin);
  len = strlen(input_buffer->buffer);
  if (len <= 0) {
    printf("Error reading input\n");
    exit(EXIT_FAILURE);
  }
  input_buffer->input_length = len - 1;
  input_buffer->buffer[len - 1] = '\0';
#else
  
  ssize_t bytes_read =
      getline(&(input_buffer->buffer), &(input_buffer->buffer_length), stdin);

  if (bytes_read <= 0) {
    printf("Error reading input\n");
    exit(EXIT_FAILURE);
  }

  // Ignore trailing newline
  input_buffer->input_length = bytes_read - 1;
  input_buffer->buffer[bytes_read - 1] = 0;
#endif
}

注意:windows下没有getline函数,这里使用fgets来代替。

现在定义一个函数来释放分配给 InputBuffer * 实例和相应结构的 buffer 元素的内存( getline 在read_input中为 input_buffer->buffer分配内存)。

void close_input_buffer(InputBuffer* input_buffer) {
    free(input_buffer->buffer);
    free(input_buffer);
}

最后,我们解析并执行命令。现在只有一个可识别的命令: .exit ,它终止程序。否则,我们将打印错误消息并继续循环。

if (strcmp(input_buffer->buffer, ".exit") == 0) {
  close_input_buffer(input_buffer);
  exit(EXIT_SUCCESS);
} else {
  printf("Unrecognized command '%s'.\n", input_buffer->buffer);
}

以下部分是完整程序代码:

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef _WIN32
#define MAX_INPUT_BUFFER 4096
#endif

typedef struct {
  char* buffer;
  size_t buffer_length;
  ssize_t input_length;
} InputBuffer;

InputBuffer* new_input_buffer() {
  InputBuffer* input_buffer = malloc(sizeof(InputBuffer));
  #ifdef _WIN32
  input_buffer->buffer = malloc(MAX_INPUT_BUFFER);
  #else
  input_buffer->buffer = NULL;
  #endif
  input_buffer->buffer_length = 0;
  input_buffer->input_length = 0;

  return input_buffer;
}

void print_prompt() { printf("db > "); }

void read_input(InputBuffer* input_buffer) {
#ifdef _WIN32
  size_t len;
  fgets(input_buffer->buffer, MAX_INPUT_BUFFER, stdin);
  len = strlen(input_buffer->buffer);
  if (len <= 0) {
    printf("Error reading input\n");
    exit(EXIT_FAILURE);
  }
  input_buffer->input_length = len - 1;
  input_buffer->buffer[len - 1] = '\0';
#else
  
  ssize_t bytes_read =
      getline(&(input_buffer->buffer), &(input_buffer->buffer_length), stdin);

  if (bytes_read <= 0) {
    printf("Error reading input\n");
    exit(EXIT_FAILURE);
  }

  // Ignore trailing newline
  input_buffer->input_length = bytes_read - 1;
  input_buffer->buffer[bytes_read - 1] = 0;
#endif
}

void close_input_buffer(InputBuffer* input_buffer) {
    free(input_buffer->buffer);
    free(input_buffer);
}

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);
    }
  }
}

对应的Makefile文件如下:

all:
	gcc -g -O0 main.c -o db
clean:
	rm -rf db

执行如下命令编译并执行,编译过程及执行结果如下:

PS D:\code\db021\code> make
gcc -g -O0 main.c -o db
PS D:\code\db021\code> .\db.exe
db > .help
Unrecognized command '.help'.
db > .exit
PS D:\code\db021\code>

下一篇世界上最简单的编译器和虚拟机

告别昂贵硬件!用Unity3D 2023 + ROS2 Humble搭建你的第一个SLAM仿真实验室(保姆级教程) 本文提供了一份详细的教程,介绍如何使用Unity3D 2023和ROS2 Humble搭建SLAM仿真实验室,无需昂贵硬件即可实现虚拟传感器模拟和算法测试。通过Docker容器配置、ROS2安装优化及Unity插件集成,开发者可以快速构建高精度的仿真环境,适用于自主导航、多传感器标定等场景。 阅读详情

相关推荐

ArcMap 10.7/10.8启动加载界面后闪退?可能是这个隐藏的Normal.mxt模板文件在搞鬼

本文深入分析了ArcMap 10.7/10.8启动加载界面后闪退的问题,指出Normal.mxt模板文件损坏是常见原因。提供了从定位、删除模板文件到系统级修复的完整解决方案,帮助GIS工程师快速恢复工作流程。

weixin_28523493的博客 332

常见编程语言对REPL支持情况小结[转]

文章转载自http://www.nowamagic.net/librarys/veda/detail/2462 最近跟一个朋友聊起编程语言的一些特性,他有个言论让我略有所思:“不能REPL的都是渣”。当然这个观点有点偏激,但我们可以探究一下,我们常用的编程语言里面,哪些支持REPL,哪些不支持,还有REPL的一些概况。 在一般的脚本语言中,有REPL是常态,因为REPL非常的方便。编程术语R...

weixin_30654583的博客 651

【windows】【系统还原】亦是美kms执行一键关闭defender 之后,windows defender 被卸载了,无论如何都打不开

还是执行 都没有明显效果 那么,我最后是怎么解决得呢? 一、打开控制面板(任务栏搜索kzmb)

weixin_41899098的博客 6910

REPL 工具到底是干什么的?使用场景是什么?底层原理是什么?

以下是一个简单的 PHP REPL 工具实现,展示其工作原理。

长风破浪会有时的博客 1545

手把手教你用STM32CubeMX和HAL库驱动DW3000:从SPI配置到第一个测距Demo

本文详细介绍了如何使用STM32CubeMX和HAL库驱动DW3000 UWB定位通信模块,从SPI配置到实现厘米级测距Demo的全过程。涵盖硬件连接、SPI通信调试、驱动移植及双向测距(TWR)算法实现,帮助开发者快速掌握UWB高精度定位技术,适用于仓储物流、智能家居等场景。

weixin_27038245的博客 485

编译opencv4.2时出现undefined reference to `jpeg_default_qtables‘错误的解决方案

操作系统:kylinV10 opencv版本:4.2.0 在编译opencv4.2.0的版本时,出现undefined reference to `jpeg_default_qtables’的错误提示,导致openc编译中断,不能继续编译,所以这个错误必须解决。 解决方案 查看错误的名字,根据名字分析,很有可能是当前系统中没有安装jpeg第三方库,到 http://www.ijg.org/上下载最新的源码包 jpegsrc.v9e.tar.gz,按如下顺序开始编译安装: tar -xzvf jpegsr.

jndingxin的专栏 5108

Python解释器、REPL与脚本的区别

Python解释器、REPL与脚本的区别——理解运行机制的本质,掌控编程的灵魂

测试者家园 1422

小孙学变频学习笔记(二)变频器的逆变电路

本文主要介绍了变频器中逆变电路的核心器件IGBT及其应用。IGBT是一种电压控制型三端半导体开关器件,具有开关频率高、功耗低的优点,在变频器中仅使用截止和饱和导通状态。文章详细分析了IGBT的结构特性、与晶闸管的区别,以及由四个IGBT组成的直流转交流逆变电路原理。重点阐述了IGBT并联反向二极管的作用:提供电流反向通路、避免反向电压损坏器件、提高开关效率等。最后提供了相关学习参考网址供读者进一步研究。

qq_43091070的博客 1197

REPL简介

REPL(Read-Eval-Print-Loop)是一种交互式编程环境,允许用户逐行输入代码并立即查看执行结果。它以"读取-求值-打印-循环"的方式工作,适合快速测试代码片段、学习语言特性和调试。Python的REPL通过命令行输入"python"启动,支持基本计算、函数测试、模块导入等操作,并提供历史命令调取、自动补全等功能。与普通脚本相比,REPL自动显示表达式结果但不保存代码,适合临时实验而非开发完整程序。

taotiezhengfeng的博客 922

京东豆自动领取脚本配置教程

腾讯云/阿里云服务器(Docker环境):全自动跑脚本领京豆(无需本地代理,使用加速源)

AIOT魔法师 863

【Python百日进阶-Web开发-Feffery】Day385 -“一起Dash”训练营Lesson-07_利用fac构建表单(上)

文章目录前言:fac是什么?“人生苦短,我用Python;Web开发,首选Feffery!”↓↓↓ 今日笔记 ↓↓↓一、费老师课堂1.1 什么是表单1.2 页面效果1.3 课堂源码二、课后作业2.1 页面效果2.2 项目源码 前言:fac是什么? feffery-antd-components(简称fac),是国内大佬费弗里(Feffery)老师基于著名的React UI组件库ant design进行二次开发,将ant design中的诸多实用组件及特性引入Dash,帮助开发者使用极低的纯Python代

专注于医院数据分析技术与系统开发的创作与分享。 251

python repl是什么?如何模拟?

REPL 是 “Read–Eval–Print Loop”(读取-求值-打印-循环)的缩写,它是一种简单的、交互式的编程环境。在 REPL 环境中,用户可以输入一条或多条编程语句,系统会立即执行这些语句并输出结果。这种方式非常适合进行快速的代码试验和调试。Python REPL 就是 Python 的交互式命令行界面,也就是说,当你在命令行输入python或python3并回车后进入的界面。

m0_57236802的博客 5638

PyTorch深度学习实战(29)——变分自编码器(Variational Autoencoder, VAE)

变分自编码器 (Variational Autoencoder, VAE) 是一种结合了自编码器和概率建模的生成模型,通过编码器将输入数据映射到潜在空间中的概率分布,并通过解码器将从潜在空间采样得到的潜在变量映射回原始数据空间,实现了数据的生成和特征学习。

盼小辉丶的博客 1万+

简单数据库实现——Part1 - REPL的简介和设置

Part1 - REPL的简介和设置 思考目录中的问题,简而言之就是数据库是如何工作的,为了搞清楚这些问题,我正在从头开始编写一个数据库,他模仿了sqlite,因此他更小而且功能更少。为了更好的理解,整个数据库被存储在一个文件中。 sqlite sqlite的网站上有很多文档,这里我给出一个sqlite的设计与实现的架构图。 一个查询(query)通过一系列的组件来实现检索或修改数据。 前端(f...

Radium_1209 494

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

数据库修化神篇三——数据库是如何工作的

数据库是如何工作的

growdu的网络博客 400

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

gosql实战教程:使用database/sql驱动轻松操作Go数据库

想要在Go项目中快速上手SQL数据库操作吗?gosql是一个用Go语言编写的轻量级PostgreSQL兼容数据库实现,它完美支持Go标准库中的database/sql驱动接口,让数据库操作变得简单高效。无论你是Go语言新手还是经验丰富的开发者,gosql都能为你提供完整的SQL数据库体验。 ## 🚀 gosql是什么? gosql是一个早期但功能完整的PostgreSQL兼容数据库实现,完全

gitblog_01047的博客 1145

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

re-frame 应用状态检查指南:从 REPL 到 GUI 的 app-db 探查方案

`app-db` 是 re-frame 应用中全部应用状态的唯一存放处,也是事件流、订阅信号图的"根数据源"。在实际开发中,无论排查状态异常、验证事件处理器(event handler)的修改结果,还是调试订阅数据流,开发者最常遇到的第一个问题都是:"我该怎么查看 `app-db` 里到底有什么?" 本文以 re-frame 官方 FAQ《Inspecting-app-db》为骨架,结合本仓库的源

gitblog_00172的博客 737

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

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

做一个善良的人,做好自己的事,如果愿意,再发一份光。 1361

Flutter 组件 cli_repl 的适配 鸿蒙Harmony 实战 - 驾驭交互式终端开发、实现鸿蒙端强大 REPL 调试环境方案

在鸿蒙(OpenHarmony)系统的高级开发与生产力工具构建中,“交互式控制台”是一个能够极大提升极客感的特性。想象一下,用户通过鸿蒙平板物理键盘输入指令,系统能够实时反馈计算结果,并支持像 Linux 终端一样的“向上滚动查看历史记录”和“Tab 键自动补全”。这种被称为 REPL(Read-Eval-Print Loop)的交互模式,不仅是调试脚本的利器,更是构建鸿蒙版 IDE、远程运维终端或专业数学计算器的核心底座。cli_repl为 Dart 环境提供了一套标准、轻量的交互环实现。

大山哥 1897

Cayley 高级使用指南:数据库初始化、数据导入、REPL 交互与 HTTP 服务部署

本篇指南面向已经完成 Cayley 安装与基础体验的开发者,围绕图数据库从"创建"到"对外提供查询服务"的完整生命周期展开:通过 `init` 子命令初始化存储后端,用 `load` 与 `conv` 批量导入数据,借助 `repl` 交互式执行 Gizmo 查询与增删四元组,最后用 `http` 子命令把图数据以 Web UI + HTTP API 的形式提供给本机或局域网内的其他机器。读完本文

gitblog_00115的博客 723

如何用scope-capture的ep-repl启动子REPL:一个只属于你的运行时调试环境

**scope-capture** 是一个专为 Clojure / ClojureScript 打造的 REPL 调试库,核心能力是把某段代码运行时「当时的局部变量环境」拍成一个快照,再让你在 REPL 里随时还原它。其中 `sc.repl/ep-repl` 是它最趁手的功能之一:一键启动一个「子 REPL」,把你传送进代码执行的现场,让所有局部变量都躺在作用域里,随你求值、实验、验证——一个**

gitblog_00505的博客 898

Prisma-repl 安装与配置指南

Prisma-repl 安装与配置指南 1. 项目基础介绍 Prisma-repl 是一个为 Prisma 数据库设计的交互式命令行界面(REPL)。它允许开发者在命令行环境中直接与 Prisma 客户端交互,执行数据库操作,这在调试和测试数据库查询时非常有用。该项目主要使用 TypeScript 和 JavaScript 编写。 2. 项目使用的关键技术和框架 TypeScript/JavaS...

gitblog_00233的博客 730

Node.js REPL 使用教程

Node.js REPL 使用教程 项目介绍 Node.js REPL(Read-Eval-Print Loop)是一个交互式的编程环境,允许用户直接在命令行中输入JavaScript代码并立即看到结果。REPL是Node.js的核心功能之一,非常适合快速测试代码片段、学习JavaScript和Node.js API。 项目快速启动 安装Node.js 首先,确保你已经安装了Node.js。如果没...

gitblog_00623的博客 651

交互即共识:AI 操作系统的 REPL 化演进

Lisp 的 REPL 是确定性的求值器,而 AI 的“REPL”更像一个意图解释器,它处理的是不完备信息,而不是精确表达式。在这个过程中,用户不再只是输入源,而更像一个高延迟、不稳定的节点,参与到系统状态的确定之中。用户的输入本身是不完备的,系统的中间状态也是不可完全展开的。把这些放在一起,可以得到一个比较朴素的结论:AI 操作系统很可能会以类似 REPL 的方式作为入口,但不会退回到 Lisp 机那种“REPL 即一切”的形态。这里的“正确”,不再是形式上的严格一致,而是语义上的基本认同。

tq1086的博客 248

5分钟上手Asami:在REPL中创建你的第一个图数据库(完整示例代码)

想不想在5分钟内,用纯 Clojure 代码在 REPL 里创建并查询一个真正的图数据库?**Clojure 图数据库 Asami** 是一个为 Clojure 和 ClojureScript 打造的开源图数据库,无需安装任何服务端程序,一条命令即可完成建库、写入和查询。本文提供一份可直接复制粘贴的 Asami 完整示例代码,带你从零开始跑通第一个图数据库。 [![Asami REPL 帮助导航

gitblog_00809的博客 256

Click-REPL开源项目教程

Click-REPL开源项目教程 项目介绍 Click-REPL是Click库的一个扩展,Click是一个用于Python命令行应用程序的框架。它使得创建复杂的命令行界面变得简单直观。Click-REPL(Read-Eval-Print Loop)提供了在基于Click的应用程序中交互式地执行命令的能力,这对于开发和测试命令行脚本尤其有用。通过集成REPL环境,开发者和终端用户能够更方便地探索应用...

gitblog_00262的博客 463
上一篇: 数据库修化神篇三——数据库是如何工作的
下一篇: 数据库修仙金丹篇十——vpp使用kni
我在数据库世界里修仙
博客等级 码龄10年 2844粉丝 61原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我在数据库世界里修仙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值