feat: add ctx sql raw read-only SQL query surface#12
Merged
Conversation
Add a new top-level `ctx sql` command that runs read-only SQL against the code-intelligence index through DuckDB, exposing a versioned, stable `v1` view layer (v1.symbols, v1.edges, v1.files, v1.meta) over the physical tables. - Engine (src/analytics/duckdb_impl.rs): `open_sql_sandbox` attaches the SQLite index READ_ONLY, builds the `v1` schema, then hardens the connection (enable_external_access=false, lock_configuration=true, memory_limit) so untrusted SQL cannot touch the filesystem, load extensions, attach files, or re-enable any of it. Safety is enforced by engine configuration only — no SQL text inspection or keyword filtering. Streaming row reader caps output without materializing the full result; interrupt-handle watchdog enforces --timeout. - Handler (src/commands/sql.rs): input from arg/--file/stdin, table/csv/json output with a JSON envelope, --max-rows cap, --fail-on-rows gate mode, and the three-way exit convention (0 ok / 1 findings / 2 error). One-result-set rule allows `CREATE TEMP TABLE ...; SELECT ...` while rejecting multiple queries. - `--schema` prints the public schema reference from a single source (src/commands/sql_schema.md) that also feeds the docs site; a drift-guard test keeps them byte-identical. - Docs: docs/website/docs/sql-schema.md + commands/sql.md, sidebar entries, and a README "ctx sql" section. - Tests: tests/sql.rs (assert_cmd) covers the security, schema, execution, and gate acceptance criteria; inline unit tests cover statement splitting + drift. The command appears in --help in all builds; without the `duckdb` feature it exits 2 with a clear message. Note: DuckDB 1.4 has no engine toggle to reject a bare in-memory `ATTACH ':memory:'`; it is permitted but inert (no filesystem access, index stays read-only, and the one-result-set rule blocks chaining a write). File-based ATTACH, COPY, read_csv, INSTALL, and config changes are all blocked.
# Conflicts: # Cargo.toml # docs/website/sidebars.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a new top-level
ctx sqlcommand: direct, read-only SQL access to the code-intelligence index through DuckDB. This turns ctx from a fixed menu of canned analyses (ctx query find/callers/deps/...) into a queryable platform — agents can answer structural questions in one round trip, and teams can commit custom quality gates as SQL files.The public surface is a versioned
v1view layer (a stable contract) over the physical tables:v1.symbols(id, name, qualified_name, kind, file, line_start, line_end, is_public, complexity, fan_in, fan_out, doc)v1.edges(source_id, source_name, source_file, target_id, target_name, target_file, kind, line)v1.files(path, language, symbol_count, total_complexity, indexed_at)v1.meta(schema_version, ctx_version, index_created_at, index_root)Security (engine-level, no SQL text filtering)
The primary caller is an LLM agent with shell access, so every query is treated as potentially adversarial. Safety is enforced entirely by DuckDB configuration, never by inspecting/blocklisting SQL text:
enable_external_access = false+lock_configuration = true— disables filesystem access (COPY,read_csv), extension loading (INSTALL), file-basedATTACH, and prevents user SQL from re-enabling any of it.READ_ONLY— it cannot be mutated (verified by byte-for-byte checksum in tests).--timeoutenforced via a connection interrupt-handle watchdog;--max-rowsstreams and caps without materializing the full result;memory_limit(overridable viaCTX_SQL_MEMORY_LIMIT) bounds runaway joins.Behavior notes / deviations from the spec
--outputinstead of--format. ctx already has a global-f/--format(OutputFormatenum for context generation) inherited by every subcommand, so a subcommand-local--formatcollides at the clap level. I used--output table|csv|json(the repo's own convention forsearch/semantic/audit) plus the--jsonalias. No acceptance criterion tests the--formatflag name.ATTACH ':memory:'is permitted but inert. DuckDB 1.4 has no engine toggle to reject an in-memory attach, andaccess_mode=read_onlyis open-time-only (incompatible with building thev1views). Rather than add SQL text filtering (which the design explicitly forbids), this bare attach is allowed — it has no filesystem access, the index stays read-only, and the one-result-set rule blocks chaining a write onto it. File-basedATTACH(the actual threat) is blocked. This is the one acceptance criterion the engine can't meet without violating the "no text filtering" rule; documented in code and tests.Exit codes
0ran (with--fail-on-rows: zero rows) ·1--fail-on-rowsand ≥1 row ·2any error (SQL error, timeout, missing index, invalid flags, or a build without theduckdbfeature). In stub builds (--no-default-features) the command still appears in--helpand exits 2 with a clear message.Single-source schema reference
ctx sql --schemaanddocs/website/docs/sql-schema.mdare generated from one source (src/commands/sql_schema.mdviainclude_str!); a drift-guard unit test keeps them byte-identical.Tests
tests/sql.rs(assert_cmd, 17 tests): security (COPY / read_csv / INSTALL / locked config / file-ATTACH / index-bytes-unchanged / timeout), schema (meta, every view queryable,--schema), execution (--fail-on-rowscodes, row-cap truncation, JSON envelope, multi-statement semantics, missing index), and the.ctx/gates/gate pattern.cargo clippy --all-targetsclean; builds with and without theduckdbfeature.Docs
sql-schema.mdandcommands/sql.md(+ sidebar entries), and a README "ctx sql" section. Notes thatBash(ctx sql *)is safe to add to a Claude Code / harness plugin allow-list.🤖 Generated with Claude Code