Description
Execute a read-only SQL query against the database and return results as a JSON array of objects. A server-side row limit and query timeout are enforced. Optionally, request the execution plan instead of results using theexplain parameter.
Always use specific column names instead of SELECT *. Use JOINs based on foreign keys discovered via describe_table. Check column cardinality from describe_table to write efficient WHERE and GROUP BY clauses.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
sql | string | Yes | SQL query to execute (SELECT statements only, without the EXPLAIN keyword) |
explain | boolean | No | Return the execution plan instead of results. Defaults to false. |
analyze | boolean | No | Include actual execution statistics in the plan (requires explain: true). When true, the query is executed inside a read-only transaction. Defaults to false. |
Response schema
Returns an array of row objects. Each object maps column names to values:Example
Request:Execution plans
Use theexplain and analyze parameters to inspect query performance without writing a separate tool call.
EXPLAIN only (no execution):
Safety
- Read-only — all queries run inside a read-only transaction.
INSERT,UPDATE,DELETE,DROP, and all other write operations are rejected. - AST validation — SQL is parsed using PostgreSQL’s actual parser (
pg_query). OnlySELECTstatements pass validation. See SQL Validation. - Row limit — results are capped at
MAX_ROWS(default: 100). Add your ownLIMITclause for smaller result sets. - Timeout — queries are cancelled after
QUERY_TIMEOUT(default: 10s). - Single statement — multi-statement queries (separated by
;) are rejected.
Column masking
If a policy file defines column masks, masked columns are automatically transformed before results are returned. For example, withemail masked as redact:
Notes
- If
--explain-onlymode is enabled,querycalls automatically return theEXPLAINplan instead of executing the query. - When using
explain: true, provide theSELECTquery only — Isthmus prependsEXPLAINorEXPLAIN ANALYZEautomatically. Do not include theEXPLAINkeyword in thesqlparameter. - With
explain: trueandanalyze: false(default), only the planner’s estimates are shown. The query is not executed. - With
explain: trueandanalyze: true, the query is executed inside a read-only transaction, and actual row counts, timing, and buffer usage are included. - Column values are JSON-serialized: timestamps become ISO 8601 strings, UUIDs become strings, numeric types preserve precision.
- Masked columns may change type (e.g. an integer column with
mask: "redact"returns the string"***"). - For large tables, always use
LIMITand filter withWHEREto avoid hitting the row cap or timeout.