fix(scripts/q): use stmt.reader instead of keyword sniffing for SELECT detection
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>
This commit is contained in:
@@ -84,6 +84,17 @@ describe('scripts/q.ts', () => {
|
||||
expect(ids).toEqual([2, 9]);
|
||||
});
|
||||
|
||||
it('WITH...DELETE is treated as a mutation, not a query', () => {
|
||||
const r = run("WITH stale AS (SELECT id FROM t WHERE name = 'alice') DELETE FROM t WHERE id IN (SELECT id FROM stale)");
|
||||
expect(r.status).toBe(0);
|
||||
expect(r.stdout).toBe('');
|
||||
|
||||
const db = new Database(dbPath, { readonly: true });
|
||||
const rows = db.prepare('SELECT name FROM t').all() as { name: string }[];
|
||||
db.close();
|
||||
expect(rows).toEqual([{ name: 'bob' }]);
|
||||
});
|
||||
|
||||
it('exits 2 with usage when args are missing', () => {
|
||||
const r = spawnSync('pnpm', ['exec', 'tsx', Q], {
|
||||
encoding: 'utf-8',
|
||||
|
||||
Reference in New Issue
Block a user