You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(connectors)!: return per-statement result sets, wire up frontend tabs (#389)
* fix(sqlserver): return every result set from multi-statement batches
executeSQL and executeReadOnly read result.recordset (singular), which
node-mssql defines as only the first statement's result set. A batch
like "SELECT 1 AS a; SELECT 2 AS b" silently dropped the second SELECT,
unlike MySQL/MariaDB which already concatenate all statements' rows.
Read result.recordsets (plural) instead, flattening every SELECT's
rows in order, and sum rowsAffected across all statements so rowCount
stays consistent with rows rather than reporting only the first
statement's count.
Closes#380
* fix(sqlserver): use nullish coalescing in recordsets/rowsAffected helpers
Addresses Copilot review feedback on #389: || masked a genuine 0
identically to a missing value, which was accidental rather than
intentional. Deliberately not adding Array.isArray guards — mssql's
IResult<T> type guarantees recordsets: Array<IRecordSet> and
rowsAffected: number[], the same contract extractPlanXml already
trusts without validation.
* feat(connectors)!: return per-statement result sets instead of merging them
SQLResult.rows/rowCount flattened every statement in a batch into one
array/count, so a caller had no way to tell "SELECT 1 AS a; SELECT 2 AS
b" apart from a single query that happened to return two rows - the
flattening the SQL Server fix (#389) added for MySQL/MariaDB-parity
was itself the wrong shape to standardize on.
SQLResult is now `{ resultSets: SQLResultSet[], messages? }`, one
entry per statement in execution order. execute_sql, explain_sql, and
custom tools already read connector-provided rows through this shape;
their JSON output changes to `resultSets: [{rows, count}, ...]`
instead of flat `rows`/`count` (except explain_sql, which only ever
executes one statement and keeps returning the flat shape internally).
Per connector:
- postgres/sqlite: already looped per-statement for multi-statement
batches: now push one result set per statement instead of merging.
- mysql/mariadb: multi-statement-result-parser.ts now builds a
SQLResultSet per statement (parseQueryResultSets) instead of
concatenating rows across statements.
- sqlserver: recordsets/rowsAffected aren't index-aligned per
statement in node-mssql's API (see buildResultSets' comment), so a
batch mixing writes with selects gets one result set per SELECT plus
a trailing result set summarizing the write-only statements - not
fully per-statement, but no read statement's rows are ever merged
with another's.
BREAKING CHANGE: execute_sql/custom tool JSON responses now nest rows
under `resultSets` instead of returning them at the top level.
* feat: rename resultSets to statements, add per-statement sql, wire up frontend tabs
The prior commit introduced SQLResult.resultSets but named the tool-facing
JSON key the same, and never attributed a statement's source text to its
result - both of which broke the local web frontend's query editor
(frontend/src/api/tools.ts), a real, shipped consumer of execute_sql that
reads the tool response directly. It was silently going to return empty
results for every query.
- SQLResultSet gains an optional `sql` field: the statement that produced
it, when a connector can attribute it unambiguously. postgres/sqlite/
mysql/mariadb populate it for every statement (they process statements
in a way that preserves reliable order/count alignment); SQL Server only
populates it for an unambiguous single-statement batch, since recordsets/
rowsAffected aren't index-aligned per statement there (same limitation
buildResultSets already documented).
- execute_sql/custom-tool JSON responses now key their statement array as
`statements` (previously `resultSets`), each entry `{sql, rows, count}`.
- Rebuilt the frontend's tab model around this: executeTool now returns one
QueryResult per statement instead of a single merged result, and
ToolDetailView creates one ResultTab per statement (labeled "(i/N)" for
batches of more than one) instead of forcing a whole batch into one tab.
- Also fixed an unrelated pre-existing bug this surfaced while verifying in
a browser: frontend/src/api/tools.ts called response.json() directly, but
the backend's stateless HTTP transport answers SSE-framed responses
(event:/data: lines) for this request shape - every query through the web
UI over HTTP transport was silently broken before this fix too.
Verified end-to-end in a browser against the --demo SQLite backend: a
two-statement batch produces two correctly-labeled, independently
scrollable tabs, each showing its own statement's SQL and rows; a
single-statement query still shows one untabbed result as before.
* refactor: dedupe resultSets->statements mapping, avoid re-parsing SQL in buildResultSets
- Extract toStatementsPayload() to tool-handler-helpers.ts, used by both
execute-sql.ts and custom-tool-handler.ts instead of each inlining the
same resultSets.map(...) - keeps their output contracts from silently
diverging if the shape changes later.
- SQLServerConnector.buildResultSets no longer re-parses the source SQL
internally just to gate the `sql` attribution. executeSQL now computes
isSingleStatement once and threads it through (directly, or via
executeReadOnly's new parameter) instead of calling splitSQLStatements
a second time on every query.
0 commit comments