SQLite 中的内部数据类型和记录格式
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 | 原始字节序列 |
| NULL | SQL NULL |
SQLite 的执行引擎内部 没有其他原始类型。无论值来源于磁盘、表达式还是绑定参数,最终都会归为这五类之一。
物理表示
| 存储类 | 物理表示方式 |
|---|---|
| INTEGER | 以整数形式存储 |
| REAL | 以 IEEE 浮点格式存储 |
| TEXT | 带有编码元信息存储 |
| BLOB | 原始字节 |
| NULL | 特殊标记 |
缓存的备用表示
某些值可以 同时缓存多种表示。
例如,值 123 可能以以下形式存在:
INTEGER 123REAL 123.0TEXT "123"
当需要时,VM 会保持这些表示同步,从而在表达式求值过程中避免重复转换。
BLOB 和 NULL 不能拥有备用表示;它们始终保持原样。
如果需要进行转换(例如比较 TEXT 与 INTEGER),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 列存储 TEXT,TEXT 列存储数字,或任何列存储 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](#)