SQLite 中的内部数据类型和记录格式

发布: (2026年2月28日 GMT+8 02:20)
8 分钟阅读
原文: Dev.to

Source: Dev.to

请提供您想要翻译的正文内容,我将把它翻译成简体中文并保留原有的格式、Markdown 语法以及技术术语。代码块和 URL 将保持不变。

git‑lrc:用于检查 AI 生成代码的 Git Hook

你好,我是 Maneshwar。我正在开发 git‑lrc,这是一款在代码进入你的仓库之前审查 AI 代理生成代码的 Git Hook。

Source:

SQLite 虚拟机(VM)内部

到目前为止,我们已经了解了 VM 如何执行指令、管理游标以及在字节码程序中移动。
今天我们深入探讨更为基础的内容:

在 VM 中到底什么是“值”?

在一个值被写入磁盘、在 WHERE 子句中进行比较,或通过 sqlite3_step() 返回之前,它都以 内存对象 的形式存在于 VM 中。理解这种表示方式是把握 SQLite 灵活性的关键。

  • VM 使用任意数量的编号内存位置(寄存器)。
  • 每个寄存器一次只能保存 一个值

所有在 VM 中处理的值必须属于 五种原始存储类型 之一:

类型描述
INTEGER有符号整数
REAL有符号浮点数(IEEE)
TEXT带有编码元信息的字符字符串
BLOB原始字节序列
NULLSQL NULL

SQLite 的执行引擎内部 没有其他原始类型。无论值来源于磁盘、表达式还是绑定参数,最终都会归为这五类之一。

物理表示

存储类物理表示方式
INTEGER以整数形式存储
REAL以 IEEE 浮点格式存储
TEXT带有编码元信息存储
BLOB原始字节
NULL特殊标记

缓存的备用表示

某些值可以 同时缓存多种表示
例如,值 123 可能以以下形式存在:

  • INTEGER 123
  • REAL 123.0
  • TEXT "123"

当需要时,VM 会保持这些表示同步,从而在表达式求值过程中避免重复转换。

BLOBNULL 不能拥有备用表示;它们始终保持原样。

如果需要进行转换(例如比较 TEXTINTEGER),VM 会 按需进行隐式类型转换。SQLite 的动态类型模型在很大程度上依赖这种灵活性。

检查存储类型

SELECT a, typeof(a) FROM t1;

在 C‑API 层面,sqlite3_column_type() 会返回由 sqlite3_step() 返回的值的存储类。

Mem 对象

在内部,几乎所有 VM 中的值都以 Mem 对象的形式表示。Vdbe.aMem 数组的每个元素就是这样一个结构体。

Mem 对象的概念布局

  • 实际的值
  • 描述当前存储类型的标志位
  • 编码相关的元数据
  • 缓存的备用表示

不变式: 一个值始终只有 唯一的规范存储类,即使缓存了多种表示。存储类标志指示哪种表示是规范的。

这种 存储类缓存表示 的区分实现了:

  • 显式类型
  • 动态类型强制转换
  • 高效的比较语义

—全部在运行时无需严格的模式约束即可完成。

记录 vs. 寄存器

寄存器中的单个值 并不是 直接存入 B‑tree 的内容。VM 首先将它们组合成 记录——逻辑上连续的字节串,包含:

  • 可选的负载(值部分)

虽然树模块可能在页面或溢出区之间物理拆分记录,但在 VM 看来它仍是一个连续的字节序列。记录的格式化完全由 VM 负责,树模块仅负责存取这些字节串。

两种概念化的记录格式化方式

方法特点
(此处表格在原文中未完成)

---------------------------------------------------------------------| | Fixed‑length | 每列占用预定的空间;记录大小固定;可能需要填充;列大小在创建表时已确定。 | | Variable‑length | 列大小随记录而变化;无需填充;记录大小取决于实际数据;偏移量必须动态计算。 |

SQLite 使用 可变长度格式的变体,因为它提供了:

  • 更小的数据库文件(无多余填充)
  • 更快的 I/O(移动的字节更少)
  • 支持动态的显式类型

这点至关重要:SQLite 允许 INTEGER 列存储 TEXTTEXT 列存储数字,或任何列存储 NULL。可变长度格式使得在没有结构约束的情况下实现这一点成为可能。

显式类型

传统数据库在 模式 层面强制类型。SQLite 在 层面强制类型——显式类型。每个值携带其自己的存储类,独立于列声明。这也是 Mem 对象会将类型标志与值本身一起存储的原因。

示例

INSERT INTO t1(c1) VALUES (123);   -- stores an INTEGER
INSERT INTO t1(c1) VALUES ('abc'); -- stores a TEXT

即使 c1 被声明为 TEXT,这两行也可以共存。

在接下来的章节中,我们将探讨这种显式类型模型如何与以下内容交互:

  • 记录头部
  • 序列类型
  • 磁盘上的编码

理解 SQLite 的字节级记录编码将使得虚拟机、Mem 对象以及 B‑tree 层的设计恰到好处。

关于 git‑lrc

AI 代理写代码很快,但它们可能会悄悄地:

  • 删除逻辑
  • 改变行为
  • 引入错误

通常你只能在生产环境中发现这些问题。

git‑lrc 解决了这个问题。它挂钩到 git commit,在代码提交前审查每个 diff。

  • 免费 – 在每次提交时运行的无限 AI 代码审查
  • 简便 – 60 秒快速设置
  • 开源 – 源码可用,任何人都可以使用

实际演示

检测严重的安全问题,例如泄露的凭证、高耗费的云操作以及日志语句中的敏感信息。

Watch demo video (git‑lrc‑intro‑60s.mp4)

为什么使用 git‑lrc?

🤖AI 代理会悄悄破坏代码——代码被删除、逻辑被更改、边缘情况消失。你直到生产环境才会注意到。
🔍在代码发布前捕获问题。

欢迎任何反馈或贡献!

仓库: git‑lrc – 免费、无限的 AI 代码审查,在提交时运行。

fore it ships. AI-powered inline comments show you exactly what changed and what looks wrong.

🔁 **Build a habit, ship better code.**  
Regular review → fewer bugs → more robust code → better results in your team.

🔗 **Why git?**  
Git is universal. Every editor, every IDE, every AI…

[View on GitHub](#)
0 浏览
Back to Blog

相关文章

阅读更多 »

SQL 连接和窗口函数

markdown !Musungu Ruth Ambogohttps://media2.dev.to/dynamic/image/width=50,height=50,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws...

SQL 连接和窗口函数

SQL 连接和窗口函数 !tonny otieno https://media2.dev.to/dynamic/image/width=50,height=50,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uplo...

精通SQL连接和窗口函数

介绍:SQL 连接用于基于相关列合并两个或多个表中的行,而窗口函数在一组行上执行计算,……