十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

drizzle-orm-sqlite 0.12.0-beta.21 变更解析:raw 查询统一、查询构建器直传与 INSERT 生成优化

drizzle-orm-sqlite 0.12.0-beta.21 变更解析:raw 查询统一、查询构建器直传与 INSERT 生成优化 drizzle-orm-sqlite 0.12.0-beta.21 变更解析raw 查询统一、查询构建器直传与 INSERT 生成优化【免费下载链接】drizzle-ormORM项目地址: https://gitcode.com/gh_mirrors/dr/drizzle-ormdrizzle-orm-sqlite 0.12.0-beta.21 是 SQLite 方言包在 0.12.0 系列 beta 阶段的一次聚焦型发布围绕四个内部行为收敛展开修复各驱动db.all的执行逻辑、允许把查询构建器直接传给 raw 执行方法、优化单值 INSERT 的 SQL 生成、并暴露索引配置中的table属性。本文以该版本变更日志changelogs/drizzle-orm-sqlite/0.12.0-beta.21.md为核心骨架结合当前仓库中 SQLite 方言与驱动层的源码实现逐条剖析每个变更的动机、落点与对使用者的实际影响帮助你在升级或使用该版本时准确理解行为差异。1. 版本背景与定位该文件位于changelogs/drizzle-orm-sqlite/目录属于 drizzle-orm 早期分包的变更记录与之相邻的还有0.12.0-beta.17至0.12.0-beta.21等多个 beta 版本以及后续0.13.0、0.14.x等正式版记录。beta 阶段的 changelog 通常以“修 bug 内部重构”为主本条四个条目全部属于运行时行为与 SQL 生成层面的改动不涉及公开 API 的破坏性变更因此对现有代码基本透明但会影响生成的 SQL 文本与 raw 查询的返回结构。从变更性质可以推断0.12.0-beta.21 的目标是统一各 SQLite 驱动的 raw 执行语义并减少无意义 SQL 输出。2. 修复db.all逻辑各驱动行为统一2.1 变更内容Fixeddb.alllogic for all drivers.db.all是 SQLite 方言 DB 实例上的 raw 查询方法之一用于执行原生 SQL 并返回多行结果。在 0.12.0-beta.21 中该方法的执行逻辑在所有 SQLite 驱动better-sqlite3、libsql、d1、bun-sqlite、op-sqlite、sqlite-proxy、expo-sqlite 等下被统一修复。2.2 源码印证db.all的统一入口在 drizzle-orm/src/sqlite-core/db.ts 中all的定义为allT unknown(query: SQLWrapper | string): DBResultTResultKind, T[] { const sequel typeof query string ? sql.raw(query) : query.getSQL(); if (this.session) { return this.execute((session) session.all(sequel), all) as DBResultTResultKind, T[]; } return this.session.all(sequel) as DBResultTResultKind, T[]; }可见db.all本身只做两件事把字符串包装为sql.raw(query)、把查询构建器转换为getSQL()然后委托给session.all()。因此“修复 all 逻辑”的实质是让各个驱动会话session的all实现保持一致。以 better-sqlite3 驱动为例drizzle-orm/src/better-sqlite3/session.ts 中PreparedQuery.all的逻辑是all(placeholderValues?: Recordstring, unknown): T[all] { const { fields, joinsNotNullableMap, query, logger, stmt, customResultMapper } this; if (!fields !customResultMapper) { const params fillPlaceholders(query.params, placeholderValues ?? {}); logger.logQuery(query.sql, params); return stmt.all(...params); } const rows this.values(placeholderValues) as unknown[][]; if (customResultMapper) { return customResultMapper(rows) as T[all]; } return rows.map((row) mapResultRow(fields!, row, joinsNotNullableMap)); }关键点在于当查询没有映射字段fields为空且没有自定义结果映射器时直接透传底层驱动的stmt.all()保持结果原样否则走values()原始行路径再按字段映射。这一“分支一致化”正是 beta.21 修复的核心——确保每个驱动的 rawall返回结构对齐不因驱动差异而出现字段映射或数组模式的偏差。其他驱动同样实现了session.all的等价方法例如 libsql/session.ts、d1/session.ts、sqlite-proxy/session.ts、op-sqlite/session.ts 等此次变更的目的就是让这些实现的行为口径统一。2.3 对使用者的影响db.all(select * from users)的返回值在各驱动下行为一致不再出现某些驱动返回原始行、某些驱动返回映射对象的不一致raw SQL 场景无 schema 映射下返回的就是底层驱动原生行数据便于与原生 API 兼容配合查询构建器时见下一节返回结构受fields是否存在影响需要留意。3. 允许向 raw 执行方法直接传入查询构建器3.1 变更内容Allowed passing query builders to raw query execution methods.此前 raw 执行方法db.all、db.get、db.run、db.values只接受string或SQLWrapper本次变更允许直接传入 Drizzle 的查询构建器对象如db.select().from(...)的产物。3.2 源码印证SQLWrapper统一收口在 drizzle-orm/src/sqlite-core/db.ts 中四个 raw 方法全部采用同一套“字符串转sql.raw、其余取getSQL()”的收口逻辑run(query: SQLWrapper | string): DBResultTResultKind, TRunResult { const sequel typeof query string ? sql.raw(query) : query.getSQL(); ... return this.session.run(sequel) as DBResultTResultKind, TRunResult; } allT unknown(query: SQLWrapper | string): DBResultTResultKind, T[] { const sequel typeof query string ? sql.raw(query) : query.getSQL(); ... } getT unknown(query: SQLWrapper | string): DBResultTResultKind, T { const sequel typeof query string ? sql.raw(query) : query.getSQL(); ... } valuesT extends unknown[] unknown[](query: SQLWrapper | string): DBResultTResultKind, T[] { const sequel typeof query string ? sql.raw(query) : query.getSQL(); ... }SQLite 的各类查询构建器select/insert/update/delete 的 builder 与最终执行对象都实现了SQLWrapper接口的getSQL()方法因此传入构建器后会被转换为对应的 SQL 文本与参数列表再交给 session 执行。3.3 实际用法示例import { sql } from drizzle-orm/sqlite-core; // 构造一个查询构建器 const qb db.select({ id: users.id, name: users.name }).from(users).where(sqlage 18); // 直接传给 raw 执行方法 const rows await db.all(qb); const first await db.get(qb);这一能力让“构建器生成的 SQL 再经 raw 通道执行”成为可能适合需要对 SQL 做二次包装、拼接或复用预构建查询片段的场景。注意传入构建器后结果的字段映射行为遵循上一节描述的fields分支逻辑。4. INSERT 生成优化单值插入跳过无值列4.1 变更内容Optimized INSERT query generation for single values by skipping columns without values.当插入的是单个值对象时SQLite 方言在生成 INSERT 语句时跳过没有提供值的列从而产出更精简、更贴近意图的 SQL。4.2 源码印证buildInsertQuery的列过滤在 drizzle-orm/src/sqlite-core/dialect.ts 的buildInsertQuery中参与 INSERT 的列先经过一次过滤const colEntries: [string, SQLiteColumn][] Object.entries(columns).filter( ([_, col]) !col.shouldDisableInsert(), ); const insertOrder colEntries.map(([, column]) sql.identifier(this.casing.getColumnCasing(column)));随后在组装单行值时对每个字段做“是否有值”的判断值为undefined或 Param 且值为undefined时按优先级回退到列的默认值default、默认函数defaultFn、更新函数onUpdateFn最终兜底为nullif ( colValue undefined || (is(colValue, Param) colValue.value undefined) ) { let defaultValue; if (col.default ! null col.default ! undefined) { defaultValue is(col.default, SQL) ? col.default : sql.param(col.default, col); } else if (col.defaultFn ! undefined) { const defaultFnResult col.defaultFn(); defaultValue is(defaultFnResult, SQL) ? defaultFnResult : sql.param(defaultFnResult, col); } else if (!col.default col.onUpdateFn ! undefined) { ... } else { defaultValue sqlnull; } valueList.push(defaultValue); } else { valueList.push(colValue); }结合 changelog 描述“skipping columns without values”此次优化针对的是**单值插入values.length 1**路径在没有显式值的列上不再重复输出无意义的列项/null占位而是让 SQL 更短、参数更少减少客户端与数据库间的无效传输。代码中const isSingleValue values.length 1的注释现被注释掉的default values分支也印证了这一演进方向——单值且全空时理论上可退化为insert into t default values。4.3 行为对照场景优化前示意优化后示意db.insert(users).values({ name: a })age无默认值可能输出age列并补null跳过无值列仅输出有值列多行批量插入values([...])每行补齐所有列保持每行列对齐语义不做跳过注意该优化只针对单值插入路径批量多行插入仍需保持列对齐因此不适用于跳过策略。若你的业务依赖“未提供的列显式写入 NULL”而非依赖列默认值请先确认目标列的default/defaultFn配置。5. 从索引配置中暴露table属性5.1 变更内容Exposedtableproperty from index config.SQLite 索引Index的配置对象config现在对外暴露table属性标明该索引所属的表。5.2 源码印证Index构造时写入 table在 drizzle-orm/src/sqlite-core/indexes.ts 中export class Index { static readonly [entityKind]: string SQLiteIndex; readonly config: IndexConfig { table: SQLiteTable }; constructor(config: IndexConfig, table: SQLiteTable) { this.config { ...config, table }; } }而IndexBuilder.build(table)在绑定表时调用new Index(this.config, table)indexes.ts从而把表引用写入config.table。IndexConfig本身包含name、columns、unique、where四个字段indexes.ts其中where用于部分索引partial index条件table是在Index层叠加的扩展属性。5.3 使用场景该暴露主要用于工具链与元数据场景例如迁移生成器、schema 内省、文档生成器需要从索引对象反查其所属表进而读取表的列定义、命名约定等。此前config.table仅存在于内部构造流程未对外可见beta.21 之后可以直接读取const idx index(users_email_idx).on(users.email); // 在绑定表之后例如 table 定义完成时 console.log(idx.config.table); // SQLiteTable 实例 console.log(idx.config.columns); // 索引列 console.log(idx.config.unique); // 是否唯一索引 console.log(idx.config.where); // 部分索引条件可为 undefined注意table只有在IndexBuilder.build(table)被调用即索引真正绑定到某张表之后才会存在直接index(...).on(...)阶段的IndexBuilder.config中并没有table字段。6. 总结与升级建议drizzle-orm-sqlite 0.12.0-beta.21 的四项变更可以归纳为三个主题raw 查询行为收敛第 2、3 条db.all在各驱动下语义统一且all/get/run/values均接受查询构建器统一以getSQL()收口SQL 生成精简第 4 条单值 INSERT 跳过无值列减少冗余输出元数据可访问性第 5 条索引配置暴露table方便下游工具消费。升级到该版本时建议若使用 raw SQL 多行查询回归测试db.all在不同驱动下的返回结构若使用db.insert(table).values(singleObject)且依赖未提供列写入 NULL确认列定义中的默认值设置符合预期工具类代码若需读取索引所属表可直接访问index.config.table。对于更新版本的完整变更可继续查看 changelogs/drizzle-orm-sqlite 目录下的0.13.0、0.14.x等后续记录了解该分包随 drizzle-orm 主版本演进的完整脉络。【免费下载链接】drizzle-ormORM项目地址: https://gitcode.com/gh_mirrors/dr/drizzle-orm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表