ReviewOS

also looking at this

stacks/bun-query-builder

fix(client): unsafe() refuses ?-style bindings on Postgres

#1172
Merged glennmichael123 wants to merge fix/unsafe-placeholder-style into main
3 files +247 -13

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

docs/raw-queries.mdmodified+19-12
Changes to docs/raw-queries.md
@@ -258,24 +258,31 @@ stats.forEach((s) => {
258258
259259```
260260
261## Prepared Statements
261## Repeated Queries
262262
263Use prepared statements for repeated queries:
263`unsafe` is not a prepared-statement handle. It returns a query that runs when you
264await it and resolves to an array of rows, so there is no `.get()` to call per
265execution and nothing to finalize:
264266
265267```typescript
268// Run it once per set of values, awaiting each. Postgres placeholders shown —
269// see "Placeholder style is the connection's, not the builder's" above.
270const user1 = await db.unsafe('SELECT * FROM users WHERE id = $1', [1])
271const user2 = await db.unsafe('SELECT * FROM users WHERE id = $1', [2])
266272
267// Prepare a statement
268const stmt = db.unsafe('SELECT * FROM users WHERE id = ?')
269
270// Execute multiple times efficiently
271const user1 = await stmt.get([1])
272const user2 = await stmt.get([2])
273const user3 = await stmt.get([3])
273// Each resolves to an array of rows — index in, or use the builder's first().
274console.log(user1[0]?.name)
275```
274276
275// Finalize when done
276stmt.finalize()
277Keep the SQL text identical across those calls and vary only the bindings. That is
278what lets the driver reuse its plan, and it is also what keeps the values
279parameterized rather than concatenated in.
277280
278```
281::: tip
282The object `unsafe` returns before you await it is the driver's own, and its shape
283differs between `bun:sqlite` and `Bun.sql`. Await it and work with the rows; don't
284reach for methods on the unawaited value.
285:::
279286
280287## Transaction with Raw Queries
281288