mirror of
https://github.com/qwibitai/nanoclaw.git
synced 2026-06-04 10:14:47 +08:00
18635e7c7d
The first-keyword check (`WITH` → SELECT path) was wrong for CTEs that precede mutations (e.g. `WITH stale AS (...) DELETE FROM t WHERE ...`). These would be routed through `db.prepare().all()` instead of executing the mutation. Use better-sqlite3's `stmt.reader` property, which asks SQLite's own parser whether the statement returns data. Single mutations go through `stmt.run()`; compound statements (which `prepare()` rejects) fall back to `db.exec()`. Add a regression test for WITH...DELETE. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
59 lines
1.9 KiB
TypeScript
59 lines
1.9 KiB
TypeScript
/**
|
|
* scripts/q.ts — sqlite3 CLI replacement for skill SQL invocations.
|
|
*
|
|
* Usage:
|
|
* pnpm exec tsx scripts/q.ts <db-path> "<sql>"
|
|
*
|
|
* Uses better-sqlite3's stmt.reader property to distinguish queries
|
|
* (SELECT / WITH...SELECT) from mutations. Queries print rows in
|
|
* sqlite3 CLI default ("list") format — pipe-separated, no header —
|
|
* so existing skill text reads identically. Mutations run via
|
|
* stmt.run() (single statement) or db.exec() (compound).
|
|
*
|
|
* Why this exists: setup/verify.ts:5 codifies that NanoClaw avoids
|
|
* depending on the sqlite3 CLI binary; setup never installs or probes
|
|
* for it. Skills that shell out to `sqlite3` therefore fail on hosts
|
|
* where it isn't preinstalled (common on fresh Ubuntu — see #2191).
|
|
* This wrapper preserves the skill-text shape (path then SQL string)
|
|
* while routing through the better-sqlite3 dep that setup already
|
|
* installs and verifies.
|
|
*/
|
|
import Database from 'better-sqlite3';
|
|
|
|
const [, , dbPath, sql] = process.argv;
|
|
|
|
if (!dbPath || sql === undefined) {
|
|
console.error('Usage: pnpm exec tsx scripts/q.ts <db-path> "<sql>"');
|
|
process.exit(2);
|
|
}
|
|
|
|
const db = new Database(dbPath);
|
|
try {
|
|
try {
|
|
const stmt = db.prepare(sql);
|
|
if (stmt.reader) {
|
|
const rows = stmt.all() as Record<string, unknown>[];
|
|
for (const row of rows) {
|
|
console.log(
|
|
Object.values(row)
|
|
.map((v) => (v === null ? '' : String(v)))
|
|
.join('|'),
|
|
);
|
|
}
|
|
} else {
|
|
stmt.run();
|
|
}
|
|
} catch (e: unknown) {
|
|
// better-sqlite3 throws on compound statements ("contains more than
|
|
// one statement"). Compound SQL in skills is always mutations
|
|
// (e.g. "DELETE ...; INSERT ...;"), so fall back to db.exec().
|
|
if (e instanceof Error && /more than one statement/i.test(e.message)) {
|
|
db.exec(sql);
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
} finally {
|
|
db.close();
|
|
}
|