Files
skills/docs/persistence/database/db-sqlite/src/unsafe/unsafe.md
2026-05-29 08:20:56 +01:00

69 lines
2.8 KiB
Markdown

# unsafe
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The `unsafe` subpath exports `rawQuery` — a direct SQL execution function that bypasses the Drizzle ORM layer entirely. It is intentionally segregated behind the `@suaveplan/db-sqlite/unsafe` subpath import to make its use explicit and auditable: any import of this path stands out in code review as a deliberate bypass of the normal query builder.
**Security warning:** `rawQuery` provides no SQL injection protection. Never pass user-supplied or externally-sourced strings as the `sql` argument. Use only for migration scripts, low-level tooling, or read-only administrative queries where the SQL is fully under developer control.
## Features
- `rawQuery<T>(db, sql, params?)` — executes a prepared statement on any `bun:sqlite`-compatible Database instance
- Returns rows typed as `T[]` (caller asserts the row shape)
- Maps execution errors to `SqliteError(SQLITE.QUERY_FAILED)`
- Supports parameterised queries via the `params` array — always use `?` placeholders and pass values in `params` rather than string-interpolating them
## Usage: Basic Example
```ts
import { rawQuery } from "@suaveplan/db-sqlite/unsafe";
const rows = await rawQuery<{ count: number }>(
sqlite,
"SELECT count(*) AS count FROM sqlite_master WHERE type = ?",
["table"]
);
console.log(rows[0]?.count);
```
## Usage: Advanced Example
```ts
import { rawQuery } from "@suaveplan/db-sqlite/unsafe";
// Used in a migration helper to read schema state before Drizzle is set up
async function tableExists(sqlite: unknown, name: string): Promise<boolean> {
const rows = await rawQuery<{ found: number }>(
sqlite,
"SELECT 1 AS found FROM sqlite_master WHERE type = 'table' AND name = ?",
[name]
);
return rows.length > 0;
}
```
## API Reference
### `rawQuery<T>(db, sql, params?): Promise<T[]>`
Executes `sql` as a prepared statement on `db`. Throws `SqliteError(SQLITE.QUERY_FAILED)` on error.
**Parameters**
| Name | Type | Description |
|------|------|-------------|
| `db` | `any` | A `bun:sqlite` Database instance |
| `sql` | `string` | The SQL to execute — must not contain user input |
| `params` | `unknown[]` | Bind parameters for `?` placeholders |
**Returns** `Promise<T[]>` — rows shaped as `T`
## Implementation Notes
`rawQuery` uses `db.prepare(sql).all(params)` rather than `db.query(sql).all(params)` to be consistent with the caching strategy used by `SqliteAdapter`. The `params` spread form (`stmt.all(...params)`) passes each parameter as a positional argument, matching `bun:sqlite`'s expected calling convention.
The `db` parameter is typed as `any` to accept both `bun:sqlite Database` instances and test doubles without requiring a concrete import of `bun:sqlite` types from this module.