Repository navigation
feat: run on drizzle databases and their transactions - #23
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
ℹ️ No critical issues. Two small suggestions: one inline, one below.
Reviewed changes
This review covers how Reylo now runs on Drizzle databases, transactions and savepoints, plus the postgres.js JSON change that came with it. I checked underneath() against the published drizzle-orm@0.45.4 source. Only libsql/session.js keeps a tx field. Every other session, PlanetScale's and TiDB's included, puts the transaction's connection on client, so session.tx ?? session.client picks the right one everywhere.
- Unwrapping:
underneath()swaps a Drizzle database, transaction or savepoint for the driver client its session holds. BothrunandrunSynccall it first, before they pick a path. - Types:
DrizzleDatabase<C>is matched by$client. Each dialect's transaction is matched by a method only it has.runSynctakes Drizzle over a synchronous SQLite driver. - postgres.js JSON: every client with
typed()(all but Bun.sql) now gets a JSON parameter astyped(JSON.stringify(v), 705). Drizzle replaces the client'sjson/jsonbserializers with ones that pass the value through unchanged. - Error hints: the old "pass
db.$client" hint is gone. New hints tell Kysely and Prisma users which client to pass, including from inside a transaction. - Lab and docs: five Drizzle driver-matrix jobs, Drizzle type-check cases, and Drizzle tabs on the Supported drivers page (checked by twoslash). The dialect pages and the README mention Drizzle.
ℹ️ The transaction types accept a Drizzle transaction from any driver of the dialect
DrizzleMysqlTransaction and the other transaction types match by dialect, not by driver. So .run(tx) also type-checks for a transaction from a Drizzle driver Reylo doesn't run on, even though $client rejects that driver's database. Most such drivers fail loudly at run time. A drizzle-orm/tidb-serverless transaction fails silently: its session.client is TiDB's native transaction, which has execute() but no query(), so the call goes to planetscale(). TiDB returns a plain array of rows, and planetscale() returns [] whenever the result has no .rows. This affects only a niche driver, so you may decide it's not worth handling.
Technical details
# A Drizzle tidb-serverless transaction type-checks and returns no rows
## Affected sites
- src/execute.ts:118 — `DrizzleMysqlTransaction` matches every Drizzle `MySqlTransaction`, `TiDBServerlessTransaction` included.
- src/decode-mysql.ts:284 — `if (!Array.isArray(result?.rows)) return [];` turns TiDB's row array into no rows.
## Required outcome
- A client whose `execute()` answers in a shape `planetscale()` doesn't recognize should fail with an error, not return no rows. Alternatively, document that only the Drizzle drivers listed in the docs are supported.
## Open questions for the human
- Is a run-time refusal enough, or should the types try to narrow the transaction to its driver (probably not feasible structurally)?claude-opus-5-5 | 𝕏
814a2d9 to
c0ef9bf
Compare
|
On the review's note that a Drizzle transaction from any driver of the dialect type-checks: the run-time refusal is fixed in c0ef9bf ( Before, New test in The types are left as they are. A transaction's type carries no mark of its driver, so narrowing it would mean naming every driver's transaction class. |
There was a problem hiding this comment.
✅ No new issues found. Both notes from the last review are addressed.
Reviewed changes
This run covers what changed since the last review at 814a2d9. The branch was rebased onto main, and the new work is in the postgres.js JSON tests and in the MySQL planetscale() path. tests/execute.test.ts passes locally (42 of 42).
- postgres.js JSON coverage: added a
{ unsafe, typed }fake to the array/JSON test and to thejsonQuerytest. They assert that each JSON value,jsonb[]included, arrives as{ value: <JSON text>, type: 705 }and thatnullis still bound asnull. Updated the comment abovejson()and the test title to match. planetscale()refuses results without rows: anexecute()answer with norowsarray now throws aTypeErrorthat describes what came back. Before, it was read as no rows. So adrizzle-orm/tidb-serverlesstransaction now fails loudly instead of returning[], and a new test covers this. Existing MySQL fakes that meant mysql2 now havequerybesideexecute, so they still take the mysql2 path.
claude-opus-5-5 | 𝕏

.run()now takes a Drizzle database, and the transactions and savepoints Drizzle hands out, on every dialect. Reylo runs its statement on the driver client Drizzle's session holds, so the values are Reylo's own for that driver, exactly as when the client is passed directly.session.client(the pool for a database, the transaction's own connection inside one), orsession.txinside a libSQL transaction, whoseclientstays the base client. Run on the base client there, a libSQL transaction failed 13 driver-matrix rows withSQLITE_BUSY.$client, sodrizzle(pool)takes the dialectspooltakes. A transaction is told apart by what only its dialect's has:setTransaction()on Postgres,get()andall()on SQLite, neither on MySQL..runSync()takes a Drizzle database over a synchronous SQLite driver and its transaction (whoseall()returns rows), and refuses libSQL's.jsonandjsonbserializers with ones that pass the value through, and a transaction shows neither. So Reylo now gives every postgres.js client a JSON parameter as its JSON text declaredunknown(sql.typed(text, 705)), which the server reads as the parameter's type.timestampcomes back as a localDate; Prisma by its own rules) before Reylo could see it. Their error now says which client to pass, and inside their transactions that the connection is not handed out.drizzle-orm0.45.4), and a line in each dialect page's drivers list and the README.Tests:
tests/execute.test.ts: a Drizzle database and transaction on each dialect, libSQL'ssession.tx, and the Kysely and Prisma refusals.