sqlsage
v0.2.1
Published
Correctness-first PostgreSQL query explainer and optimizer CLI
Maintainers
Readme
SQLSage
SQLSage is a command-line PostgreSQL query explainer and optimizer. Give it a
SELECT statement plus schema metadata and it will:
- explain the result in plain English;
- flag wrong-result and business-intent risks before performance issues;
- describe predicted or observed execution behavior;
- recommend conservative indexes and rewrites as explicit change sets; and
- label every claim as predicted, plan-observed, measured, or unverified.
SQLSage can run completely offline. A database connection is optional.
Install
Requirements: Node.js 22.18 or newer (Node 22 LTS and Node 24 are both tested in CI) and npm.
npm install --global sqlsage
sqlsage --versionOr run it without installing:
npx sqlsage demoEach GitHub release also carries a tarball
and SHA256SUMS.txt if you would rather install from a verified download.
Try it in 30 seconds
One command, no files and no database:
sqlsage demoIt analyzes a bundled query with a nullable NOT IN correctness bug: SQLSage explains
why the query can return no rows, proposes a NOT EXISTS repair, labels the intentional
result change, and then prints the commands for analyzing your own query.
SQLSage also includes a demonstration catalog and twelve realistic queries:
sqlsage list
sqlsage analyze --corpus q05 --format textTutorials
Three complete walkthroughs, each with commands and output verified against a live PostgreSQL 16 database:
- Catching a query that returns the wrong answer
— a nullable
NOT INthat silently returns zero rows instead of 196,000. No database needed. - Fixing a date filter an index cannot help — why adding the index alone changes nothing, and the rewrite plus index is 5.5x faster.
- Analyzing a real plan from your own database
—
pg_dump,EXPLAIN (ANALYZE, FORMAT JSON), and reading where the planner was wrong.
Check your environment
sqlsage doctor validates the runtime, any input files you pass, and — if you supply a
connection string — database connectivity and permissions. Every failure prints the exact
command that fixes it.
sqlsage doctor
sqlsage doctor --catalog catalog.json --schema schema.sql
sqlsage doctor --database-url "$DATABASE_URL" --schema-name publicIt is strictly read-only: it never issues DDL or DML, never runs EXPLAIN ANALYZE, and
never executes your query. It exits 0 when every check passes and 1 when any check fails,
so it is safe to use as a CI preflight step.
Analyze your own query
With a PostgreSQL schema file:
sqlsage analyze --query query.sql --schema schema.sqlWith SQLSage catalog JSON:
sqlsage analyze --query query.sql --catalog catalog.jsonFrom standard input, producing machine-readable output:
cat query.sql | sqlsage analyze --schema schema.sql --format json > analysis.jsonAvailable formats are text, markdown, and json. JSON output is written only to
stdout; diagnostics are written to stderr.
Use a saved PostgreSQL plan
Pair raw EXPLAIN (FORMAT JSON) output with schema metadata:
sqlsage analyze \
--query query.sql \
--catalog catalog.json \
--plan plan.jsonPlan-aware JSON output is a reusable evidence bundle:
sqlsage analyze --query query.sql --catalog catalog.json --plan plan.json \
--format json > evidence.json
sqlsage analyze --query query.sql --plan evidence.jsonIf a bundle contains SQL, it must match the query being analyzed. SQLSage rejects mismatched evidence.
Connect to PostgreSQL safely
Connected mode collects a plan without executing the query by default:
sqlsage analyze \
--query query.sql \
--database-url "$DATABASE_URL" \
--schema-name publicThis uses EXPLAIN (VERBOSE, SETTINGS, FORMAT JSON) inside a timed, read-only
transaction and rolls back. The query must parse and bind as one supported SELECT
before PostgreSQL receives EXPLAIN.
Execution is explicit:
sqlsage analyze \
--query query.sql \
--database-url "$DATABASE_URL" \
--schema-name public \
--analyze \
--statement-timeout 5000--analyze executes one EXPLAIN (ANALYZE, BUFFERS, VERBOSE, SETTINGS, FORMAT
JSON). Read-only mode prevents database writes, but it cannot prevent external side
effects inside an unfamiliar user-defined volatile function. Review the query before
opting in.
SQLSage never executes recommended rewrites or candidate index DDL.
Compare two plans
sqlsage compare --before before.json --after after.jsonReports what changed between two captured plans — access paths, indexes, join strategies, spills, row-estimate accuracy — and whether it measurably improved.
It refuses verdicts the evidence cannot support: no timing comparison when either side
is plan-only, no "improvement" claimed below 1.2x since two single runs cannot separate
that from noise, and a prominent flag when the two captures describe different
statements. compare never executes a query.
Exit codes
0: analysis completed and output was written;1: usage, input, connection, plan, or output failure;2: unsupported, unparseable, unbound, or incomplete analysis.
Findings do not make the process fail; they are the product output.
Supported scope
The current release supports PostgreSQL SELECT statements, including common CTE,
subquery, join, aggregation, ordering, pagination, and window-function shapes.
The offline schema importer supports common CREATE SCHEMA, SET search_path,
CREATE TABLE, primary/foreign keys, nullability, and PostgreSQL index definitions.
It rejects unsupported DDL rather than silently returning partial metadata.
See Supported constructs for the measured list of what is accepted and rejected, Usage and inputs for the full input contract, and Architecture for the analysis pipeline.
Build from source
git clone https://github.com/Figo5/sqlsage.git
cd sqlsage
npm ci
npm run check
npm linkThe package ships built JavaScript in dist/. The install smoke test packs the
project, installs it into a fresh temporary prefix, imports the package API, and runs
the CLI:
npm run test:installContributing and security
Bug reports and focused pull requests are welcome. Read CONTRIBUTING.md before changing analysis rules, and report security issues according to SECURITY.md.
