|
| 1 | +# Native SQLite inspection — 0.20 development |
| 2 | + |
| 3 | +The native engine lists tables and queries existing SQLite files inside the |
| 4 | +selected project. It replaces the Python built-in reader with bundled SQLite; |
| 5 | +no Python, shell, database CLI or external MCP registration is required. The |
| 6 | +same reader serves model tools, the CLI, desktop IPC and the MCP server. |
| 7 | + |
| 8 | +## Use it |
| 9 | + |
| 10 | +```sh |
| 11 | +shadowcode --workspace /path/to/project sqlite app.db |
| 12 | +shadowcode --workspace /path/to/project --json sqlite app.db \ |
| 13 | + 'SELECT id, name FROM users WHERE id > ? ORDER BY id' \ |
| 14 | + --params '[100]' --limit 50 |
| 15 | +``` |
| 16 | + |
| 17 | +With no SQL, the response includes `tables` and their CREATE TABLE definitions |
| 18 | +in `rows`. Queries return `columns`, `rows`, |
| 19 | +`truncated`, `limit`, and `read_only`. Check `truncated`; narrow the predicate |
| 20 | +or use an ordered keyset query to retrieve further rows. Duplicate column names |
| 21 | +are rejected: use `AS` aliases rather than losing a value in a JSON object. |
| 22 | +NULL, integers, finite numbers and UTF-8 text retain their types. BLOBs return |
| 23 | +`{ "type": "blob", "hex": "00ff", "bytes": 2 }`. Invalid UTF-8 and non-finite |
| 24 | +numbers fail explicitly; `hex(column)` can produce a readable representation. |
| 25 | + |
| 26 | +Models use the legacy names `mcp_sqlite_tables(path)` and |
| 27 | +`mcp_sqlite_query(path, sql, params?, limit?)`. They are native tools available |
| 28 | +in Plan/Review and Build, with durable task results and bounded parallel reads. |
| 29 | +External MCP activation is unrelated to these built-ins. |
| 30 | + |
| 31 | +External clients use `shadow_sqlite` with the same arguments, optionally adding |
| 32 | +`timeout_ms`. Omit `sql` to list tables. The default read-only server supports it; |
| 33 | +the selected project remains fixed. Application clients use `POST /api/sqlite` |
| 34 | +with that JSON body through the native service. |
| 35 | + |
| 36 | +## Data safety and live databases |
| 37 | + |
| 38 | +The main file opens read-only and must already exist. SQLite's authorizer |
| 39 | +denies data/schema writes, ATTACH/DETACH, configuration PRAGMAs, transactions, |
| 40 | +extension loading and file-access functions. A query must be a single statement returning columns; |
| 41 | +SELECT, WITH and subqueries work. Read-only metadata PRAGMAs are allowed: |
| 42 | +`table_info`, `table_xinfo`, `index_list`, `index_info`, `index_xinfo`, |
| 43 | +`foreign_key_list`, and `table_list`, including their table-valued forms. |
| 44 | +Parameters are bound separately from SQL. |
| 45 | +A bounded preparation pass rejects multiple statements without recursively |
| 46 | +walking an arbitrary chain of SQL statements. |
| 47 | + |
| 48 | +Live WAL reads retain SQLite's normal locking and transaction semantics and |
| 49 | +see committed WAL data; the connection never asserts `immutable=1`. SQLite may |
| 50 | +create or update its `-wal`/`-shm` coordination files, including after a writer |
| 51 | +closes or leaves WAL data without shared memory. This bookkeeping does not |
| 52 | +grant SQL write access or change database rows/schema. Tests check database and |
| 53 | +existing WAL bytes remain unchanged while reading a separate process's commits. |
| 54 | +An exclusive lock, inaccessible coordination file, corruption, or recovery that |
| 55 | +requires a database write produces an error rather than bypassing SQLite locks. |
| 56 | + |
| 57 | +The Linux reader holds the workspace and every real parent directory open. A |
| 58 | +private VFS preserves the directory capability's `/proc/self/fd` path while |
| 59 | +delegating locking and I/O to SQLite's bundled Unix VFS. Parent/final symlinks, |
| 60 | +special files and existing sidecars with additional hard links are rejected. |
| 61 | +This shares the application's same-account trust boundary; it is not an OS |
| 62 | +sandbox against another process controlling the user's files. |
| 63 | + |
| 64 | +## Limits and cancellation |
| 65 | + |
| 66 | +- 200 rows by default, maximum 1,000; serialized output is capped at 1 MB. |
| 67 | +- Five seconds by default; CLI `--timeout-ms` and API `timeout_ms` accept |
| 68 | + 1–10,000 ms. Model tools also respect their configured tool timeout. |
| 69 | +- Eight simultaneous readers per process; excess requests fail with a retry hint. |
| 70 | +- SQL and parameter data each have a 64 KB limit, with at most 128 parameters |
| 71 | + and 128 result columns. SQLite values/rows are limited to 1 MB. |
| 72 | +- Ten million VM instructions, reduced parser/VM limits, an approximately 2 MB |
| 73 | + configured page cache, and no auxiliary SQLite threads. Queries requiring |
| 74 | + temporary disk spill fail instead of keeping an unbounded in-memory working |
| 75 | + set or creating temporary files. Use indexed or narrower queries. |
| 76 | + |
| 77 | +Cancellation/deadlines use SQLite's interrupt handle and progress callback. |
| 78 | +Abandoning a request cancels its worker, which retains its reader permit until |
| 79 | +the connection closes. SQLite interruption is cooperative; a stalled kernel |
| 80 | +filesystem operation may delay return. Tests check that cancellation releases |
| 81 | +locks for a subsequent writer. |
| 82 | + |
| 83 | +The locked native library bundles SQLite 3.53.2, including the upstream |
| 84 | +[WAL-reset fix](https://www.sqlite.org/wal.html#walreset). This also updates |
| 85 | +ShadowCode's history store; schema 24 and existing migration backups remain. |
| 86 | +The implementation follows SQLite's [WAL documentation](https://www.sqlite.org/wal.html), |
| 87 | +[VFS contract](https://www.sqlite.org/c3ref/vfs.html), and |
| 88 | +[untrusted-SQL guidance](https://www.sqlite.org/security.html). |
| 89 | + |
| 90 | +The [verification record](NATIVE_VERIFICATION.md#real-local-models) includes real |
| 91 | +gpt-oss and Qwen runs that discover a disposable database's schema, execute the |
| 92 | +correct aggregate query and report its result without changing database bytes. |
| 93 | +Successful SQLite reads also count toward required task inspection. |
0 commit comments