動(dòng)實(shí)戰(zhàn))
Drizzle ORM 0.37.0 版本詳解SingleStore 新方言與 SQLite Durable Objects 驅(qū)動(dòng)實(shí)戰(zhàn)【免費(fèi)下載鏈接】drizzle-ormORM項(xiàng)目地址: https://gitcode.com/gh_mirrors/dr/drizzle-orm本篇基于 Drizzle ORM 0.37.0 版本的官方變更記錄深入解讀該版本的三項(xiàng)核心更新全新的 SingleStore 方言、SQLite Durable ObjectsCloudflare DO驅(qū)動(dòng)以及兩個(gè)關(guān)鍵 Bug 修復(fù)。讀者讀完本文后將掌握 SingleStore 數(shù)據(jù)庫(kù)在 Drizzle 中的接入方式、在 Cloudflare Durable Objects 中構(gòu)建同步 SQLite 數(shù)據(jù)層的完整流程并能理解withReplicas與 Neon$withAuth兩個(gè)修復(fù)背后的實(shí)現(xiàn)原理。版本概覽0.37.0 帶來了什么Drizzle ORM 0.37.0 是一次以新數(shù)據(jù)庫(kù)接入能力為主的版本迭代主要變化集中在三方面新增 SingleStore 方言支持在 Drizzle 中以類型安全的方式操作 MySQL 兼容的 SingleStore 數(shù)據(jù)庫(kù)新增 SQLite Durable Objects 驅(qū)動(dòng)允許在 Cloudflare Durable Objects 的DurableObjectStorage之上直接執(zhí)行同步 SQLite 查詢修復(fù)兩處已知缺陷withReplicas中$with未定義以及 Neon serverless 驅(qū)動(dòng)$withAuth不接受 Promise 類型 token。下文分別展開。一、SingleStore 方言MySQL 兼容體系的新成員1.1 能力來源與定位SingleStore原 MemSQL是一款同時(shí)支持行存與列存的分布式數(shù)據(jù)庫(kù)其協(xié)議與 MySQL 高度兼容。0.37.0 版本中SingleStore 團(tuán)隊(duì)為 Drizzle 提交了 PR完成了對(duì) SingleStore 中MySQL 兼容部分的完整支持使得 Drizzle 的全部核心能力類型安全的表定義、查詢構(gòu)建、關(guān)系查詢等可以直接作用于 SingleStore 實(shí)例。從倉(cāng)庫(kù)源碼結(jié)構(gòu)看SingleStore 支持被拆分為兩層與 MySQL/PostgreSQL 的既有架構(gòu)完全一致singlestore-core目錄與方言無關(guān)的核心層包含表定義、列構(gòu)建器、查詢構(gòu)建器、方言、會(huì)話抽象等singlestore目錄基于mysql2驅(qū)動(dòng)的實(shí)現(xiàn)層包含驅(qū)動(dòng)、會(huì)話與遷移器。singlestore-core的公開入口 index.ts 統(tǒng)一導(dǎo)出了singlestoreTable、全部列構(gòu)建器、SingleStoreDialect、索引、主鍵、唯一約束、視圖等符號(hào)說明 SingleStore 從第一天起就享受與 MySQL 同等的完整能力面。1.2 快速上手示例以下是官方變更記錄給出的最小可用示例它完整覆蓋了定義表結(jié)構(gòu) → 建立連接 → 發(fā)起查詢?nèi)絠mport { 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()...幾個(gè)值得注意的細(xì)節(jié)singlestoreTable與sqliteTable、pgTable一樣接收表名與列定義對(duì)象列構(gòu)建器int、varchar等從drizzle-orm/singlestore-core導(dǎo)入與驅(qū)動(dòng)實(shí)現(xiàn)解耦drizzle()直接接收連接字符串DATABASE_URL內(nèi)部會(huì)自動(dòng)創(chuàng)建連接池。1.3 連接初始化字符串、PoolOptions 與回調(diào)客戶端官方文檔只展示了傳入連接字符串的最簡(jiǎn)形式。從 singlestore/driver.ts 的源碼可以看到drizzle()實(shí)際支持三種調(diào)用形態(tài)調(diào)用形態(tài)說明drizzle(connectionString)傳入連接字符串內(nèi)部調(diào)用createPool({ uri, connectAttributes })創(chuàng)建回調(diào)風(fēng)格連接池并自動(dòng)轉(zhuǎn)為 Promise 風(fēng)格drizzle(connectionString, config)連接字符串 Drizzle 配置logger、cache、schema、casingdrizzle({ connection \| client, ...config })傳入PoolOptions對(duì)象或直接傳入已有的mysql2客戶端Pool/Connection/ 回調(diào)版本其中SingleStoreDriverOptions源碼支持兩個(gè)可選字段logger傳入自定義Logger實(shí)例或設(shè)為true使用內(nèi)置DefaultLoggercache傳入緩存實(shí)例用于查詢結(jié)果緩存且連接池會(huì)自動(dòng)附帶_connector_name: SingleStore Drizzle ORM Driver與_connector_version兩個(gè)連接屬性源碼便于 SingleStore 側(cè)識(shí)別 Drizzle 客戶端。此外drizzle.mock()源碼同樣適用于 SingleStore 驅(qū)動(dòng)可在不連接真實(shí)數(shù)據(jù)庫(kù)的情況下構(gòu)建查詢、進(jìn)行類型與 SQL 斷言測(cè)試。1.4 完整的列類型體系SingleStore 方言并非只支持int與varchar。查看 singlestore-core/columns/all.ts共注冊(cè)了 28 種列構(gòu)建器覆蓋數(shù)值、字符串、時(shí)間、二進(jìn)制、JSON 與向量等類型數(shù)值bigint、int、tinyint、smallint、mediumint、decimal、double、float、real、serial、year字符串varchar、char、text、tinytext、mediumtext、longtext、binary、varbinary時(shí)間date、datetime、time、timestamp其他boolean、json、singlestoreEnum、customType、vector其中特別值得一提的是vector列vector.ts其構(gòu)建器接收dimensions與elementType配置底層數(shù)據(jù)類型為Arraynumber、驅(qū)動(dòng)參數(shù)為字符串——這為在 SingleStore 上構(gòu)建向量檢索場(chǎng)景預(yù)留了直接的類型支持是 MySQL 方言所不具備的 SingleStore 特色能力。1.5 遷移與測(cè)試支持集成測(cè)試目錄 integration-tests/tests/singlestore/ 中包含了常規(guī)、自定義類型、前綴表名、代理proxy等全套測(cè)試用例drizzle-kit 的遷移快照目錄 integration-tests/drizzle2/singlestore/ 下存在meta/0000_snapshot.json與_journal.json說明drizzle-kit的 generate/push/migrate 流程對(duì) SingleStore 已可用關(guān)系查詢測(cè)試relational/singlestore.test.ts與副本讀寫測(cè)試replicas/singlestore.test.ts也一并就緒。注意本版本先支持 SingleStore 與 MySQL 兼容的部分。SingleStore 官方團(tuán)隊(duì)將持續(xù)迭代后續(xù)版本會(huì)逐步引入更多 SingleStore 特有的能力。二、SQLite Durable Objects 驅(qū)動(dòng)在 Cloudflare DO 中直接查詢 SQLite2.1 什么是 SQLite Durable ObjectsCloudflare Durable ObjectsDO是 Workers 平臺(tái)提供的強(qiáng)一致性狀態(tài)存儲(chǔ)原語(yǔ)每個(gè) DO 擁有一個(gè)DurableObjectStorage實(shí)例。0.37.0 之前開發(fā)者需要自行在 SQL 與 DO Storage API 之間做適配0.37.0 起Drizzle 直接提供了drizzle-orm/durable-sqlite驅(qū)動(dòng)把 DO 的存儲(chǔ)層封裝為同步 SQLite 會(huì)話開發(fā)者可以直接用 Drizzle 的類型安全 API 讀寫數(shù)據(jù)。2.2 官方示例一個(gè)完整的 Durable Object以下代碼來自官方變更記錄展示了一個(gè)完整的 DO 類——在構(gòu)造函數(shù)中通過drizzle(this.storage)建立數(shù)據(jù)庫(kù)實(shí)例并對(duì)外暴露migrate、insert、select三個(gè)方法/// reference typescloudflare/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: DrizzleSqliteDODatabaseany; 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): PromiseResponse { 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: johnexample.com, }) console.log(New user created!) const users await stub.select(); console.log(Getting all users from the database: , users) return new Response(); } }示例中的關(guān)鍵信息入口類型DrizzleSqliteDODatabase與工廠函數(shù)drizzle均從drizzle-orm/durable-sqlite導(dǎo)入遷移器從drizzle-orm/durable-sqlite/migrator導(dǎo)入migrations是 drizzle-kit 生成后編譯進(jìn) Worker 的遷移對(duì)象Worker 的fetch處理器通過env.MY_DURABLE_OBJECT1.idFromName(...)獲取 DO stub隨后依次執(zhí)行遷移、插入與查詢。2.3 驅(qū)動(dòng)實(shí)現(xiàn)原理同步會(huì)話與底層存儲(chǔ)從源碼看這個(gè)驅(qū)動(dòng)的設(shè)計(jì)非常克制它完全復(fù)用了 SQLite 同步方言只是替換了會(huì)話層。driver.ts 中drizzle(client, config)接收DurableObjectStorage作為客戶端構(gòu)造SQLiteSyncDialect與SQLiteDOSession并支持config.schema關(guān)系查詢、config.casing、config.loggertrue時(shí)使用DefaultLogger等標(biāo)準(zhǔn) Drizzle 配置同時(shí)把原始客戶端掛載到$client上session.ts 中的SQLiteDOPreparedQuery是核心執(zhí)行單元run/all/get/values四個(gè)方法最終都落到this.client.sql.exec(...)上——即 DO 存儲(chǔ)層內(nèi)置的 SQLite 執(zhí)行引擎并且全部為同步 API返回SqlStorageCursor后再經(jīng)toArray()/mapResultRow映射為行對(duì)象事務(wù)能力由SQLiteDOTransaction提供session.ts底層調(diào)用client.transactionSync(() ...)保證事務(wù)原子性并支持嵌套事務(wù)nestedIndex 1。2.4 遷移器__drizzle_migrations表與斷點(diǎn)執(zhí)行durable-sqlite/migrator.ts 中的migrate(db, config)實(shí)現(xiàn)了與 SQLite 其他驅(qū)動(dòng)一致的遷移協(xié)議讀取journal.entries按序號(hào)拼接出每個(gè)遷移的 SQL鍵名規(guī)則為m 4 位零填充序號(hào)如m0000按-- statement-breakpoint將每個(gè)遷移文件拆分為多條語(yǔ)句在事務(wù)內(nèi)創(chuàng)建__drizzle_migrations記錄表id、hash、created_at讀取最近一條已應(yīng)用遷移的時(shí)間戳僅執(zhí)行比它更新的遷移并逐條寫入記錄。由于遷移文件最終會(huì)被打包進(jìn) Worker bundle示例中的import migrations from ../drizzle/migrations這套機(jī)制讓 DO 在首次初始化時(shí)即可自動(dòng)完成建表。2.5 集成測(cè)試佐證倉(cāng)庫(kù)中 integration-tests/tests/sqlite/durable-objects/index.ts 是一份超過 3600 行的 DO 集成測(cè)試覆蓋了 DO 場(chǎng)景下的完整 Drizzle 能力面遷移流程migrate1、插入/自增/默認(rèn)值$default全字段/部分字段/原始 SQL/類型化 SQL 查詢inArray、notInArray、selectDistinct、returning、JOIN 與aliasblob的 JSON 與 bigint 模式、timestamp 模式等類型映射。該測(cè)試文件同時(shí)配有 wrangler.toml 配置說明整個(gè) DO 數(shù)據(jù)層是通過真實(shí) Cloudflare 運(yùn)行環(huán)境驗(yàn)證的。三、Bug 修復(fù)兩處一致性缺陷的源碼級(jí)解讀3.1 修復(fù)一withReplicas中$with未定義問題背景對(duì)應(yīng) issue #1834withReplicas(primary, replicas)用于構(gòu)建主庫(kù)寫入、從庫(kù)讀取的副本分離實(shí)例。此前其返回對(duì)象缺失$with/withCTE 構(gòu)建入口導(dǎo)致在副本分離場(chǎng)景下使用 CTE 時(shí)拋出$with is undefined。修復(fù)方式在 mysql-core/db.ts 的withReplicas實(shí)現(xiàn)中新增了$with與with的轉(zhuǎn)發(fā)const $with: Q[with] (...args: []) getReplica(replicas).with(...args); // ... return { ...primary, select, selectDistinct, $count, with: $with, // 只讀查詢轉(zhuǎn)發(fā)到副本 // ... };與select、selectDistinct、$count一樣CTE 構(gòu)建被歸類為只讀操作因此統(tǒng)一轉(zhuǎn)發(fā)到隨機(jī)挑選的副本上執(zhí)行而update、insert、delete、execute、transaction依舊路由到主庫(kù)。同樣的修復(fù)模式也存在于pg-core、gel-core與singlestore-core各自的withReplicas實(shí)現(xiàn)中可從各目錄的db.ts中檢索withReplicas確認(rèn)。3.2 修復(fù)二Neon$withAuth的 token 類型對(duì)齊問題背景對(duì)應(yīng) issue #3597Neon serverless 驅(qū)動(dòng)的連接選項(xiàng)中authToken支持傳入 Promise但數(shù)據(jù)庫(kù)實(shí)例上的$withAuth()方法此前只接受字符串造成類型不一致與使用困惑。修復(fù)方式在 neon-http/driver.ts 中NeonHttpDatabase.$withAuth的入?yún)㈩愋捅桓臑榕cHTTPQueryOptions[authToken]保持一致$withAuth( token: ExcludeHTTPQueryOptionstrue, true[authToken], undefined, ): Omitthis, /* 保留查詢相關(guān)方法 */ { ... }同時(shí)該方法內(nèi)部通過 Proxy 包裝wrap(this, token, ...)實(shí)現(xiàn) token 的透?jìng)鲗?duì)with等鏈?zhǔn)椒祷氐闹虚g對(duì)象也會(huì)遞歸包裹確保后續(xù)所有查詢都攜帶新 token。修復(fù)后$withAuth返回的類型被收窄為僅包含$count、delete、select、selectDistinct、selectDistinctOn、update、insert、with、query、execute、refreshMaterializedView等查詢方法的實(shí)例避免誤用其他實(shí)例 API。四、升級(jí)與使用建議升級(jí)drizzle-orm至 0.37.0 或更高版本即可獲得上述能力配套使用drizzle-kit時(shí)SingleStore 項(xiàng)目的生成/推送流程可參考 drizzle2/singlestore 遷移快照 作為輸出形態(tài)參考SingleStore 場(chǎng)景下建議優(yōu)先從drizzle-orm/singlestore-core導(dǎo)入表與列定義、從drizzle-orm/singlestore導(dǎo)入drizzle保持核心層與驅(qū)動(dòng)層的清晰邊界Durable Objects 場(chǎng)景下需在tsconfig中引入cloudflare/workers-types示例頂部有/// reference typescloudflare/workers-types /且注意該驅(qū)動(dòng)當(dāng)前為同步 API不適合在需要異步游標(biāo)流式讀取的超大結(jié)果集場(chǎng)景使用若使用了withReplicas或 Neon$withAuth本次兩個(gè)修復(fù)為向后兼容的缺陷修復(fù)升級(jí)后無需改動(dòng)業(yè)務(wù)代碼直接獲得正確的$with行為與 Promise token 類型支持。結(jié)語(yǔ)0.37.0 通過 SingleStore 方言與 SQLite Durable Objects 驅(qū)動(dòng)將 Drizzle 的類型安全查詢體驗(yàn)擴(kuò)展到了分布式分析型數(shù)據(jù)庫(kù)與邊緣狀態(tài)存儲(chǔ)兩個(gè)新領(lǐng)域同時(shí)以兩個(gè)針對(duì)性的 Bug 修復(fù)收緊了副本分離與 Neon 認(rèn)證的類型邊界。對(duì)希望在 Cloudflare Workers 上構(gòu)建本地優(yōu)先local-first應(yīng)用的團(tuán)隊(duì)而言durable-sqlite驅(qū)動(dòng)提供了一條把 SQLite 遷移、CRUD 與關(guān)系查詢?nèi)考{入類型系統(tǒng)的捷徑?!久赓M(fèi)下載鏈接】drizzle-ormORM項(xiàng)目地址: https://gitcode.com/gh_mirrors/dr/drizzle-orm創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考