Drizzle ORM 0.37.0 版本详解:SingleStore 新方言与 SQLite Durable Objects 驱动实战
2026/9/19 19:56:34 网站建设 项目流程

Drizzle ORM 0.37.0 版本详解:SingleStore 新方言与 SQLite Durable Objects 驱动实战

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

本篇基于 Drizzle ORM 0.37.0 版本的官方变更记录,深入解读该版本的三项核心更新:全新的 SingleStore 方言、SQLite Durable Objects(Cloudflare DO)驱动,以及两个关键 Bug 修复。读者读完本文后,将掌握 SingleStore 数据库在 Drizzle 中的接入方式、在 Cloudflare Durable Objects 中构建同步 SQLite 数据层的完整流程,并能理解withReplicas与 Neon$withAuth两个修复背后的实现原理。

版本概览:0.37.0 带来了什么

Drizzle ORM 0.37.0 是一次以"新数据库接入能力"为主的版本迭代,主要变化集中在三方面:

  1. 新增 SingleStore 方言:支持在 Drizzle 中以类型安全的方式操作 MySQL 兼容的 SingleStore 数据库;
  2. 新增 SQLite Durable Objects 驱动:允许在 Cloudflare Durable Objects 的DurableObjectStorage之上直接执行同步 SQLite 查询;
  3. 修复两处已知缺陷withReplicas$with未定义,以及 Neon serverless 驱动$withAuth不接受 Promise 类型 token。

下文分别展开。


一、SingleStore 方言:MySQL 兼容体系的新成员

1.1 能力来源与定位

SingleStore(原 MemSQL)是一款同时支持行存与列存的分布式数据库,其协议与 MySQL 高度兼容。0.37.0 版本中,SingleStore 团队为 Drizzle 提交了 PR,完成了对 SingleStore 中MySQL 兼容部分的完整支持,使得 Drizzle 的全部核心能力(类型安全的表定义、查询构建、关系查询等)可以直接作用于 SingleStore 实例。

从仓库源码结构看,SingleStore 支持被拆分为两层,与 MySQL/PostgreSQL 的既有架构完全一致:

  • singlestore-core(目录):与方言无关的核心层,包含表定义、列构建器、查询构建器、方言、会话抽象等;
  • singlestore(目录):基于mysql2驱动的实现层,包含驱动、会话与迁移器。

singlestore-core的公开入口 index.ts 统一导出了singlestoreTable、全部列构建器、SingleStoreDialect、索引、主键、唯一约束、视图等符号,说明 SingleStore 从第一天起就享受与 MySQL 同等的完整能力面。

1.2 快速上手示例

以下是官方变更记录给出的最小可用示例,它完整覆盖了"定义表结构 → 建立连接 → 发起查询"三步:

import { int, singlestoreTable, varchar } from 'drizzle-orm/singlestore-core'; import { drizzle } from 'drizzle-orm/singlestore'; export const usersTable = singlestoreTable('users_table', { id: int().primaryKey(), name: varchar({ length: 255 }).notNull(), age: int().notNull(), email: varchar({ length: 255 }).notNull().unique(), }); // ... 其余代码 const db = drizzle(process.env.DATABASE_URL!); db.select()...

几个值得注意的细节:

  • singlestoreTablesqliteTablepgTable一样,接收表名与列定义对象;
  • 列构建器(intvarchar等)从drizzle-orm/singlestore-core导入,与驱动实现解耦;
  • drizzle()直接接收连接字符串(DATABASE_URL),内部会自动创建连接池。

1.3 连接初始化:字符串、PoolOptions 与回调客户端

官方文档只展示了传入连接字符串的最简形式。从 singlestore/driver.ts 的源码可以看到,drizzle()实际支持三种调用形态:

调用形态说明
drizzle(connectionString)传入连接字符串,内部调用createPool({ uri, connectAttributes })创建回调风格连接池,并自动转为 Promise 风格
drizzle(connectionString, config)连接字符串 + Drizzle 配置(loggercacheschemacasing
drizzle({ connection \| client, ...config })传入PoolOptions对象,或直接传入已有的mysql2客户端(Pool/Connection/ 回调版本)

其中SingleStoreDriverOptions(源码)支持两个可选字段:

  • logger:传入自定义Logger实例,或设为true使用内置DefaultLogger
  • cache:传入缓存实例,用于查询结果缓存,且连接池会自动附带_connector_name: 'SingleStore Drizzle ORM Driver'_connector_version两个连接属性(源码),便于 SingleStore 侧识别 Drizzle 客户端。

此外,drizzle.mock()(源码)同样适用于 SingleStore 驱动,可在不连接真实数据库的情况下构建查询、进行类型与 SQL 断言测试。

1.4 完整的列类型体系

SingleStore 方言并非只支持intvarchar。查看 singlestore-core/columns/all.ts,共注册了 28 种列构建器,覆盖数值、字符串、时间、二进制、JSON 与向量等类型:

  • 数值bigintinttinyintsmallintmediumintdecimaldoublefloatrealserialyear
  • 字符串varcharchartexttinytextmediumtextlongtextbinaryvarbinary
  • 时间datedatetimetimetimestamp
  • 其他booleanjsonsinglestoreEnumcustomTypevector

其中特别值得一提的是vector列(vector.ts),其构建器接收dimensionselementType配置,底层数据类型为Array<number>、驱动参数为字符串——这为在 SingleStore 上构建向量检索场景预留了直接的类型支持,是 MySQL 方言所不具备的 SingleStore 特色能力。

1.5 迁移与测试支持

  • 集成测试目录 integration-tests/tests/singlestore/ 中包含了常规、自定义类型、前缀表名、代理(proxy)等全套测试用例;
  • drizzle-kit 的迁移快照目录 integration-tests/drizzle2/singlestore/ 下存在meta/0000_snapshot.json_journal.json,说明drizzle-kit的 generate/push/migrate 流程对 SingleStore 已可用;
  • 关系查询测试(relational/singlestore.test.ts)与副本读写测试(replicas/singlestore.test.ts)也一并就绪。

注意:本版本先支持 SingleStore 与 MySQL 兼容的部分。SingleStore 官方团队将持续迭代,后续版本会逐步引入更多 SingleStore 特有的能力。


二、SQLite Durable Objects 驱动:在 Cloudflare DO 中直接查询 SQLite

2.1 什么是 SQLite Durable Objects

Cloudflare Durable Objects(DO)是 Workers 平台提供的强一致性状态存储原语,每个 DO 拥有一个DurableObjectStorage实例。0.37.0 之前,开发者需要自行在 SQL 与 DO Storage API 之间做适配;0.37.0 起,Drizzle 直接提供了drizzle-orm/durable-sqlite驱动,把 DO 的存储层封装为同步 SQLite 会话,开发者可以直接用 Drizzle 的类型安全 API 读写数据。

2.2 官方示例:一个完整的 Durable Object

以下代码来自官方变更记录,展示了一个完整的 DO 类——在构造函数中通过drizzle(this.storage)建立数据库实例,并对外暴露migrateinsertselect三个方法:

/// <reference types="@cloudflare/workers-types" /> import { drizzle, DrizzleSqliteDODatabase } from 'drizzle-orm/durable-sqlite'; import { DurableObject } from 'cloudflare:workers' import { migrate } from 'drizzle-orm/durable-sqlite/migrator'; import migrations from '../drizzle/migrations'; import { usersTable } from './db/schema'; export class MyDurableObject1 extends DurableObject { storage: DurableObjectStorage; db: DrizzleSqliteDODatabase<any>; constructor(ctx: DurableObjectState, env: Env) { super(ctx, env); this.storage = ctx.storage; this.db = drizzle(this.storage, { logger: false }); } async migrate() { migrate(this.db, migrations); } async insert(user: typeof usersTable.$inferInsert) { await this.db.insert(usersTable).values(user); } async select() { return this.db.select().from(usersTable); } } export default { /** * This is the standard fetch handler for a Cloudflare Worker * * @param request - The request submitted to the Worker from the client * @param env - The interface to reference bindings declared in wrangler.toml * @param ctx - The execution context of the Worker * @returns The response to be sent back to the client */ async fetch(request: Request, env: Env): Promise<Response> { const id: DurableObjectId = env.MY_DURABLE_OBJECT1.idFromName('durable-object'); const stub = env.MY_DURABLE_OBJECT1.get(id); await stub.migrate(); await stub.insert({ name: 'John', age: 30, email: 'john@example.com', }) console.log('New user created!') const users = await stub.select(); console.log('Getting all users from the database: ', users) return new Response(); } }

示例中的关键信息:

  • 入口类型DrizzleSqliteDODatabase与工厂函数drizzle均从drizzle-orm/durable-sqlite导入;
  • 迁移器从drizzle-orm/durable-sqlite/migrator导入,migrations是 drizzle-kit 生成后编译进 Worker 的迁移对象;
  • Worker 的fetch处理器通过env.MY_DURABLE_OBJECT1.idFromName(...)获取 DO stub,随后依次执行迁移、插入与查询。

2.3 驱动实现原理:同步会话与底层存储

从源码看,这个驱动的设计非常克制:它完全复用了 SQLite 同步方言,只是替换了会话层

  • driver.ts 中,drizzle(client, config)接收DurableObjectStorage作为客户端,构造SQLiteSyncDialectSQLiteDOSession,并支持config.schema(关系查询)、config.casingconfig.loggertrue时使用DefaultLogger)等标准 Drizzle 配置,同时把原始客户端挂载到$client上;
  • session.ts 中的SQLiteDOPreparedQuery是核心执行单元:run/all/get/values四个方法最终都落到this.client.sql.exec(...)上——即 DO 存储层内置的 SQLite 执行引擎,并且全部为同步 API,返回SqlStorageCursor后再经toArray()/mapResultRow映射为行对象;
  • 事务能力由SQLiteDOTransaction提供(session.ts),底层调用client.transactionSync(() => ...)保证事务原子性,并支持嵌套事务(nestedIndex + 1)。

2.4 迁移器:__drizzle_migrations表与断点执行

durable-sqlite/migrator.ts 中的migrate(db, config)实现了与 SQLite 其他驱动一致的迁移协议:

  1. 读取journal.entries,按序号拼接出每个迁移的 SQL(键名规则为m+ 4 位零填充序号,如m0000);
  2. --> statement-breakpoint将每个迁移文件拆分为多条语句;
  3. 在事务内创建__drizzle_migrations记录表(idhashcreated_at);
  4. 读取最近一条已应用迁移的时间戳,仅执行比它更新的迁移,并逐条写入记录。

由于迁移文件最终会被打包进 Worker bundle(示例中的import migrations from '../drizzle/migrations'),这套机制让 DO 在首次初始化时即可自动完成建表。

2.5 集成测试佐证

仓库中 integration-tests/tests/sqlite/durable-objects/index.ts 是一份超过 3600 行的 DO 集成测试,覆盖了 DO 场景下的完整 Drizzle 能力面:

  • 迁移流程(migrate1)、插入/自增/默认值($default);
  • 全字段/部分字段/原始 SQL/类型化 SQL 查询;
  • inArraynotInArrayselectDistinctreturning、JOIN 与alias
  • blob的 JSON 与 bigint 模式、timestamp 模式等类型映射。

该测试文件同时配有 wrangler.toml 配置,说明整个 DO 数据层是通过真实 Cloudflare 运行环境验证的。


三、Bug 修复:两处一致性缺陷的源码级解读

3.1 修复一:withReplicas$with未定义

问题背景(对应 issue #1834):withReplicas(primary, replicas)用于构建"主库写入、从库读取"的副本分离实例。此前其返回对象缺失$with/with(CTE 构建入口),导致在副本分离场景下使用 CTE 时抛出$with is undefined

修复方式:在 mysql-core/db.ts 的withReplicas实现中,新增了$withwith的转发:

const $with: Q['with'] = (...args: []) => getReplica(replicas).with(...args); // ... return { ...primary, select, selectDistinct, $count, with: $with, // 只读查询转发到副本 // ... };

selectselectDistinct$count一样,CTE 构建被归类为只读操作,因此统一转发到随机挑选的副本上执行;而updateinsertdeleteexecutetransaction依旧路由到主库。同样的修复模式也存在于pg-coregel-coresinglestore-core各自的withReplicas实现中(可从各目录的db.ts中检索withReplicas确认)。

3.2 修复二:Neon$withAuth的 token 类型对齐

问题背景(对应 issue #3597):Neon serverless 驱动的连接选项中authToken支持传入 Promise,但数据库实例上的$withAuth()方法此前只接受字符串,造成类型不一致与使用困惑。

修复方式:在 neon-http/driver.ts 中,NeonHttpDatabase.$withAuth的入参类型被改为与HTTPQueryOptions['authToken']保持一致:

$withAuth( token: Exclude<HTTPQueryOptions<true, true>['authToken'], undefined>, ): Omit<this, /* 保留查询相关方法 */> { ... }

同时,该方法内部通过 Proxy 包装(wrap(this, token, ...))实现 token 的透传:对with等链式返回的中间对象也会递归包裹,确保后续所有查询都携带新 token。修复后,$withAuth返回的类型被收窄为仅包含$countdeleteselectselectDistinctselectDistinctOnupdateinsertwithqueryexecuterefreshMaterializedView等查询方法的实例,避免误用其他实例 API。


四、升级与使用建议

  • 升级drizzle-orm至 0.37.0 或更高版本即可获得上述能力;配套使用drizzle-kit时,SingleStore 项目的生成/推送流程可参考 drizzle2/singlestore 迁移快照 作为输出形态参考;
  • SingleStore 场景下建议优先从drizzle-orm/singlestore-core导入表与列定义、从drizzle-orm/singlestore导入drizzle,保持核心层与驱动层的清晰边界;
  • Durable Objects 场景下需在tsconfig中引入@cloudflare/workers-types(示例顶部有/// <reference types="@cloudflare/workers-types" />),且注意该驱动当前为同步 API,不适合在需要异步游标流式读取的超大结果集场景使用;
  • 若使用了withReplicas或 Neon$withAuth,本次两个修复为向后兼容的缺陷修复,升级后无需改动业务代码,直接获得正确的$with行为与 Promise token 类型支持。

结语

0.37.0 通过 SingleStore 方言与 SQLite Durable Objects 驱动,将 Drizzle 的类型安全查询体验扩展到了"分布式分析型数据库"与"边缘状态存储"两个新领域,同时以两个针对性的 Bug 修复收紧了副本分离与 Neon 认证的类型边界。对希望在 Cloudflare Workers 上构建本地优先(local-first)应用的团队而言,durable-sqlite驱动提供了一条把 SQLite 迁移、CRUD 与关系查询全部纳入类型系统的捷径。

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

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询