news 2026/9/19 1:18:25

drizzle-orm 0.44.5 版本解析:durable-sqlite `.one()` 修复与 SQLite blob 列的跨环境支持强化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
drizzle-orm 0.44.5 版本解析:durable-sqlite `.one()` 修复与 SQLite blob 列的跨环境支持强化

drizzle-orm 0.44.5 版本解析:durable-sqlite.one()修复与 SQLite blob 列的跨环境支持强化

【免费下载链接】drizzle-ormORM项目地址: https://gitcode.com/gh_mirrors/dr/drizzle-orm

本篇技术指南围绕 drizzle-orm 0.44.5 发布说明(changelogs/drizzle-orm/0.44.5.md)中的四项修复展开,深入讲解 Durable Objects SQLite 驱动下.one()查询方法的正确用法,以及 SQLiteblob列在 Node.js、浏览器等不同运行时下的取值映射机制。读完本文,你将理解这四条修复背后的底层实现,掌握blob列三种模式(buffer/json/bigint)的选型原则,并能安全升级到该版本。

版本概览:一次聚焦于 SQLite 稳定性与兼容性的小版本

drizzle-orm 0.44.5 是紧随 0.44.4(该版本修复了DrizzleQueryError导出问题,见 changelogs/drizzle-orm/0.44.4.md)之后的一个补丁级版本,共包含四项变更,全部集中在 SQLite 相关能力上:

  1. 修复durable-sqlitesession 中.one()的无效用法(invalid usage);
  2. 修复 SQLiteblob列在 spread(展开)运算符场景下的崩溃问题;
  3. 提升 SQLiteblob列在浏览器环境中的支持;
  4. 改进 SQLiteblob列的值映射(mapping)逻辑。

四项变更中三项都围绕blob列,可见该版本的核心主题是"让blob列在不同运行时与不同输入形态下都能稳定工作",同时兼顾了 Cloudflare Durable Objects 上 SQLite 查询体验的一致性。

durable-sqlite session 的.one()修复:理解 execute 方法分发机制

修复背景:.one()与 SQLite execute 方法的对应关系

在 drizzle-orm 的查询构建体系中,SQLite 的预编译查询支持四种执行方法,定义在 drizzle-orm/src/sqlite-core/session.ts:

export type SQLiteExecuteMethod = 'run' | 'all' | 'get';

SQLitePreparedQuery基类的execute()会根据executeMethod动态分发到对应实现(见 sqlite-core/session.ts):

execute(placeholderValues?: Record<string, unknown>): ExecuteResult<T['type'], T['execute']> { return thisthis.executeMethod as ExecuteResult<T['type'], T['execute']>; }

查询构建器上的.one()方法约定语义为"恰好返回一条记录",在底层会映射为executeMethod === 'get',最终调用预编译查询对象的get()方法。因此,session 的get()实现是否正确、是否符合基类契约,直接决定了.one()能否正常工作。

修复内容:SQLiteDOPreparedQuery.get()的取值方式

在 drizzle-orm/src/durable-sqlite/session.ts 中,SQLiteDOPreparedQueryget()实现如下:

get(placeholderValues?: Record<string, unknown>): T['get'] { const params = fillPlaceholders(this.query.params, placeholderValues ?? {}); this.logger.logQuery(this.query.sql, params); const { fields, client, joinsNotNullableMap, customResultMapper, query } = this; if (!fields && !customResultMapper) { return (params.length > 0 ? client.sql.exec(query.sql, ...params) : client.sql.exec(query.sql)).next().value; } const rows = this.values(placeholderValues) as unknown[][]; const row = rows[0]; if (!row) { return undefined; } if (customResultMapper) { return customResultMapper(rows) as T['get']; } return mapResultRow(fields!, row, joinsNotNullableMap); }

该实现遵循了 SQLite 同步驱动('sync'类型)的取值契约:无字段映射时直接通过client.sql.exec(...)执行 SQL,并取结果集的next().value作为单行;有字段映射(如带fields的联表查询或自定义结果映射器)时则走values()拿到原始行数组,再取首行。旧版本中.one()在此 session 下无法正常工作,很可能与get()对无字段/有字段两种路径的处理不完整有关;0.44.5 将其修正为与 durable-sqlite/driver.ts 中drizzle()入口所创建的SQLiteDOSession(见 durable-sqlite/session.ts)一致的完整语义。

实际影响与验证方式

修复后,在 Cloudflare Durable Objects 中使用 SQLite(通过DurableObjectStoragesqlAPI)时,db.select().from(table).where(...).one()这类查询将稳定返回单条记录(无匹配时返回undefined)。从源码结构看,SQLiteDOPreparedQueryrun()(session.ts)、all()(session.ts)、values()(session.ts)分别对应写操作、多行查询与原始值数组查询,三者此前不受影响,本次修复补齐的是.one()这条单行取值链路。

修复 spread 运算符导致的 blob 崩溃:输入形态归一化

问题本质:驱动返回的二进制对象形态不一

SQLite 的blob列在各驱动与运行时下,读回的值可能是BufferUint8ArrayArrayBuffer中的任意一种。若列映射代码以"展开"(spread)方式处理这些二进制对象(例如对Uint8Array执行Buffer.from(...value)或对类数组结构做展开),在不同形态之间切换时就会触发运行时崩溃——这正是 0.44.5 修复的第二项问题。

修复方式:在mapFromDriverValue中统一转换为 Buffer

查看 drizzle-orm/src/sqlite-core/columns/blob.ts,默认(buffer模式)的取值映射已经改为基于类型判断的安全转换:

override mapFromDriverValue(value: Buffer | Uint8Array | ArrayBuffer): T['data'] { if (Buffer.isBuffer(value)) { return value; } return Buffer.from(value as Uint8Array); }

不再对二进制对象做逐元素展开,而是直接通过Buffer.from()整体拷贝,从根源上消除了 spread 运算符在二进制对象上可能引发的崩溃。从代码结构可以推断,此前版本可能对Uint8Array/ArrayBuffer使用了展开或逐个元素拷贝的写法,遇到非 Buffer 输入时就会出错;现在所有输入都被归一化为Buffer,后续逻辑只依赖一种稳定形态。

浏览器环境下的 blob 支持:textDecoder兜底路径

问题背景:浏览器没有全局 Buffer

SQLite 的blob映射逻辑(尤其是jsonbigint两种需要把字节解码为文本的模式)在 Node.js 中依赖全局Buffer。但在浏览器(如 D1 HTTP 调用、libsql wasm、浏览器内运行的 SQLite 场景)中,全局Buffer并不存在,直接调用Buffer.from(...)会抛错。0.44.5 的"Better browser support"即针对此问题。

源码实现:环境探测 + 双路径解码

json模式(SQLiteBlobJson)的取值为例,blob.ts 的实现为:

override mapFromDriverValue(value: Buffer | Uint8Array | ArrayBuffer): T['data'] { if (typeof Buffer !== 'undefined' && Buffer.from) { const buf = Buffer.isBuffer(value) ? value // eslint-disable-next-line no-instanceof/no-instanceof : value instanceof ArrayBuffer ? Buffer.from(value) : value.buffer ? Buffer.from(value.buffer, value.byteOffset, value.byteLength) : Buffer.from(value); return JSON.parse(buf.toString('utf8')); } return JSON.parse(textDecoder!.decode(value)); }

关键点有三:

  1. 环境探测:先判断typeof Buffer !== 'undefined' && Buffer.from,存在才走 Node 路径;
  2. 形态归一:Node 路径下再区分BufferArrayBuffer、带buffer视图的Uint8Array(注意通过byteOffset/byteLength精确切分子视图),最终统一为Buffer
  3. 兜底解码:浏览器环境回退到textDecoder.decode(value)——该textDecoder从 drizzle-orm/src/utils.ts 引入(textDecoder常量,见 blob.ts 第 5 行的导入),同样是bigint模式(SQLiteBigInt,blob.ts)的兜底方案。

这一"先探测、再归一、终兜底"的三层结构,正是该版本把 blob 支持扩展到浏览器运行时的实现基础。

blob 映射的全面改进:三种模式选型指南

blob()工厂函数与三种模式

blob()是 SQLite 列构造器,定义于 drizzle-orm/src/sqlite-core/columns/blob.ts,根据配置返回不同的列类型:

export function blob(): SQLiteBlobJsonBuilderInitial<''>; export function blob<TMode extends BlobMode = BlobMode>( config?: BlobConfig<TMode>, ): Equal<TMode, 'bigint'> extends true ? SQLiteBigIntBuilderInitial<''> : Equal<TMode, 'buffer'> extends true ? SQLiteBlobBufferBuilderInitial<''> : SQLiteBlobJsonBuilderInitial<''>; // ... 带列名的重载 ... export function blob(a?: string | BlobConfig, b?: BlobConfig) { const { name, config } = getColumnNameAndConfig<BlobConfig | undefined>(a, b); if (config?.mode === 'json') { return new SQLiteBlobJsonBuilder(name); } if (config?.mode === 'bigint') { return new SQLiteBigIntBuilder(name); } return new SQLiteBlobBufferBuilder(name); }

三种模式(BlobMode,见 blob.ts)对比如下:

模式列类型TypeScript 数据类型驱动参数类型写入映射读取映射
buffer(默认)SQLiteBlobBufferBufferBuffer原样传递Buffer.isBuffer(value) ? value : Buffer.from(value)
jsonSQLiteBlobJsonunknown(JSON 值)BufferBuffer.from(JSON.stringify(value))JSON.parse(buf.toString('utf8'))
bigintSQLiteBigIntbigintBufferBuffer.from(value.toString())BigInt(buf.toString('utf8'))

使用建议

  • 默认不传mode时得到buffer模式,适合存储原始二进制数据(文件内容、哈希值等);
  • 需要把对象/数组以 JSON 形式存入 blob 时使用blob('data', { mode: 'json' })。不过官方在 blob.ts 的注释中明确提示:推荐优先使用text('...', { mode: 'json' })替代 JSON 模式的 blob,因为 SQLite 的 JSON 函数会对 BLOB 参数抛错(BLOB 被保留用于未来 JSON 的二进制编码,见 SQLite json1 文档说明);
  • 需要把bigint存入 blob(SQLite 本身无原生 bigint 类型)时使用blob('id', { mode: 'bigint' }),写入时按 UTF-8 编码数字字符串,读取时通过BigInt()还原。

本次映射改进的要点

0.44.5 对映射逻辑的改进主要体现在"输入归一化"与"视图精确处理"两点:所有模式的mapFromDriverValue都接受Buffer | Uint8Array | ArrayBuffer三种输入;对于Uint8Array子视图(value.buffer存在且偏移非零的情况),通过Buffer.from(value.buffer, value.byteOffset, value.byteLength)精确拷贝,避免因偏移量导致数据错位。这些改动共同保证了不同驱动、不同运行时下 blob 读写结果的一致性。

升级建议与验证路径

0.44.5 是纯修复性质的补丁版本,不包含破坏性变更,可以放心升级:

  1. 若你使用了 durable-sqlite 驱动(Cloudflare Durable Objects 上的 SQLite),升级后重点回归select ... .one()单行查询路径,确认返回单条记录或undefined,且不再报错;
  2. 若你使用了 blob 列,升级后请在目标运行时(Node.js 或浏览器环境)分别执行读写冒烟测试,覆盖三种模式(buffer/json/bigint),并特别验证Uint8Array子视图数据(如bytes.subarray(...)的读取结果)与 JSON/bigint 文本解码是否正确;
  3. 相关实现均可直接查阅源码继续深入:durable-sqlite 会话与查询在 drizzle-orm/src/durable-sqlite/session.ts,入口在 drizzle-orm/src/durable-sqlite/driver.ts;blob 列三种模式的完整实现与文档注释在 drizzle-orm/src/sqlite-core/columns/blob.ts。

结语

drizzle-orm 0.44.5 是一个典型的"小版本大修内功"的补丁:修复了 durable-sqlite 上.one()查询的可用性问题,并为 SQLiteblob列建立了"环境探测 → 输入归一 → 文本兜底"的三层取值管线,使其在 Node 与浏览器环境下都能稳定映射buffer/json/bigint三种模式。对于在 Cloudflare Durable Objects、浏览器端 SQLite 场景中使用 drizzle-orm 的开发者,这是一个值得关注的稳定性更新。

【免费下载链接】drizzle-ormORM项目地址: https://gitcode.com/gh_mirrors/dr/drizzle-orm

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 1:17:32

Unity与ABB机器人EGM实时通信实战:从坐标对齐到64字节数据包解析

1. 项目概述&#xff1a;为什么要在Unity里“牵着”ABB CRB 15000走&#xff1f;你有没有试过站在车间现场&#xff0c;看着一台CRB 15000机械臂在产线上精准抓取、装配、码垛&#xff0c;心里却想着——要是能把它“请”进Unity里&#xff0c;用鼠标拖一拖就让它动起来&#x…

作者头像 李华
网站建设 2026/9/19 1:15:37

LabVIEW实时图像采集实战:从丢帧到30fps稳定输出

简介&#xff1a;本资源是一份面向LabVIEW初学者与自动化控制课程实践者的教学文档&#xff0c;聚焦USB摄像头视频图像的实时采集、显示、录像与拍照功能实现&#xff0c;解决图像采集系统开发中硬件调用、控件集成与界面交互等典型问题。文档为单个313KB的Word文件&#xff08…

作者头像 李华
网站建设 2026/9/19 1:12:33

Linux Suspend/Resume 内核级深度解析:从用户态到ACPI固件的全流程拆解

1. 这不是“按个键就休眠”的黑箱——它是一场横跨用户空间与内核空间的精密协同作战Linux 的 Suspend/Resume&#xff0c;远不止是笔记本合盖后屏幕一黑、再开盖就恢复工作的简单动作。它是一套覆盖整个软件栈的系统级状态迁移机制&#xff0c;涉及从桌面环境&#xff08;如 G…

作者头像 李华
网站建设 2026/9/19 0:59:43

pyasc 中 set_load_data_boundary 详解:配置 load_3d 指令的 A1/B1 边界值

pyasc 中 set_load_data_boundary 详解&#xff1a;配置 load_3d 指令的 A1/B1 边界值 【免费下载链接】pyasc 本项目为Python用户提供算子编程接口&#xff0c;支持在昇腾AI处理器上加速计算&#xff0c;接口与Ascend C一一对应并遵守Python原生语法。 项目地址: https://gi…

作者头像 李华
网站建设 2026/9/19 0:49:44

走 Cursor 的 K3 调用做成可回滚,TaoToken 的 Key 怎么切

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华