fix(sqlserver): queries returning multiple record sets
Description of change
Fixes #11566, fixes #5051
What?
The change resolves the issue whereby any call to query() for mssql connections, returns only the first recordset, even when a query returns multiple recordsets. This change checks for recordsets and adds it to the QueryResult if useStructuredQuery = true is passed in. This change will allow the passing in of a boolean, anywhere query() is called, which will enable the full list of results to be accessed. This change does not change the default behaviour.
Why?
Without this change, you cannot access all recordsets in the return, you only get the first one.
Verification
Run a stored procedure that returns multiple recordsets i.e. .query('MY_SP').
Run two select statements within a single call to .query('SELECT 1; SELECT 2').
Pull-Request Checklist
- [x] Code is up-to-date with the
masterbranch - [x] This pull request links relevant issues as
Fixes #11566, #5051 - [x] There are new or updated unit tests validating the change
- [x] Documentation has been updated to reflect this change
Summary by CodeRabbit
-
New Features
- Introduced QueryOptions to request structured results from raw queries across data sources, entity managers, and query runners.
- getRawMany now accepts options to return structured results when requested.
- Query results now expose recordsets for multi-statement responses.
-
Deprecations
- The boolean overload for requesting structured results is deprecated; use QueryOptions instead.
-
Documentation
- Added guidance and examples for executing raw SQL with structured results, including migration notes and API usage.
Walkthrough
Introduces QueryOptions with useStructuredResult, updates query methods across DataSource, EntityManager, QueryRunner, and drivers to accept options or boolean, adds QueryResult.recordsets, threads options through query builders, updates docs and tests, and exports QueryOptions. Minor .gitignore addition and Sqljs flush visibility change.
Changes
| Cohort / File(s) | Summary |
|---|---|
Ignore config\.gitignore |
Added comment and ignore pattern /*TestProject. |
Public query options and result shapesrc/query-runner/QueryOptions.ts, src/query-runner/QueryResult.ts, src/index.ts |
Added QueryOptions interface (useStructuredResult?: boolean); QueryResult gains recordsets: T[][]. Re-export QueryOptions. |
QueryRunner API surfacesrc/query-runner/QueryRunner.ts, src/query-runner/BaseQueryRunner.ts |
Updated query signatures to accept options or boolean; added overloads and deprecation note for boolean; BaseQueryRunner abstract signature updated; imported QueryOptions. |
Core entry pointssrc/data-source/DataSource.ts, src/entity-manager/EntityManager.ts |
DataSource.query and EntityManager.query accept optionsOrUseStructuredResult; normalize and forward to QueryRunner; import QueryOptions. |
Driver query runnerssrc/driver/*/*QueryRunner.ts (aurora-mysql, aurora-postgres, better-sqlite3, capacitor, cockroachdb, cordova, expo/...*, mysql, nativescript, oracle, postgres, react-native, sap, spanner, sqlite, sqljs, sqlserver) |
Replaced boolean parameter with optionsOrUseStructuredResult (QueryOptions |
Query builderssrc/query-builder/SelectQueryBuilder.ts, src/query-builder/InsertQueryBuilder.ts, src/query-builder/DeleteQueryBuilder.ts, src/query-builder/SoftDeleteQueryBuilder.ts |
getRawMany accepts QueryOptions; loadRawResults now takes useStructuredResult; internal calls updated; replaced boolean true with { useStructuredResult: true } in DML builders. |
Documentationdocs/docs/query-runner.md, docs/docs/working-with-entity-manager/5-entity-manager-api.md, docs/docs/query-builder/1-select-query-builder.md |
Documented QueryOptions and structured results; deprecated boolean overload; examples updated to use options; noted structured results via getRawMany. |
Teststest/functional/query-runner/structured-result.ts, test/functional/repository/find-options-locking/find-options-locking.ts |
Added functional tests for structured results and forwarding; adjusted test casting for query override. |
Sequence Diagram(s)
sequenceDiagram
autonumber
participant App
participant DataSource
participant QueryRunner
Note over App,QueryRunner: Request structured results using QueryOptions
App->>DataSource: query(sql, params, { useStructuredResult: true })
DataSource->>QueryRunner: query(sql, params, { useStructuredResult: true })
alt useStructuredResult === true
QueryRunner->>QueryRunner: Execute SQL
QueryRunner-->>DataSource: QueryResult { raw, records, recordsets?, affected? }
DataSource-->>App: QueryResult
else default/raw
QueryRunner->>QueryRunner: Execute SQL
QueryRunner-->>DataSource: raw
DataSource-->>App: raw
end
sequenceDiagram
autonumber
participant App
participant SelectQB as SelectQueryBuilder
participant QR as QueryRunner
App->>SelectQB: getRawMany({ useStructuredResult: true })
SelectQB->>QR: loadRawResults(..., useStructuredResult=true)
QR-->>SelectQB: QueryResult { records, recordsets? }
SelectQB-->>App: QueryResult (structured)
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~60 minutes
Suggested labels
size-s
Suggested reviewers
- sgarner
- michaelbromley
- naorpeled
- gioboa
Poem
I thump my paw—structures bloom anew,
Options sprout where booleans once grew.
Recordsets multiply, hops in a row,
From runners to sources, the currents now flow.
I nibble docs, tests fresh as dew—
Query me carrots, I’ll return them all too! 🥕🐇
Pre-merge checks and finishing touches
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Out of Scope Changes Check | ⚠️ Warning | The addition of .gitignore entries for test/github-issues/issue-8975.ts and unrelated modifications to the find-options-locking functional test do not relate to SQL Server multi-recordset support or QueryOptions improvements and fall outside the scope of the linked issues and PR objectives. | Remove or relocate the .gitignore updates and revert the unrelated test modifications to a separate housekeeping PR to keep this changeset focused on SQL Server recordset support and structured-result enhancements. |
✅ Passed checks (4 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title Check | ✅ Passed | The title “fix(sqlserver): queries returning multiple record sets” clearly and concisely identifies the primary change—enabling SQL Server queries to return all record sets—and follows the conventional “fix(scope): description” format without extraneous detail. |
| Linked Issues Check | ✅ Passed | The PR fully implements the objectives from issues #11566 and #5051 by exposing the driver’s recordsets array in QueryResult when structured mode is requested, preserving backward compatibility via a boolean flag, adding QueryOptions for extensibility, updating DataSource.query and EntityManager.query to accept structured-result requests, and including tests and documentation for multi‐recordset scenarios. |
| Docstring Coverage | ✅ Passed | No functions found in the changes. Docstring coverage check skipped. |
✨ Finishing touches
🧪 Generate unit tests (beta)
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
📜 Recent review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📥 Commits
Reviewing files that changed from the base of the PR and between a5baaa34dd108950ec1c0fce4d411d5b77103c55 and c4593a4e48e06d1d0c75aa84fd68c8489a72716c.
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (2)
.gitignore(1 hunks)docs/docs/query-runner.md(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- .gitignore
🔇 Additional comments (1)
docs/docs/query-runner.md (1)
56-141: Structured result docs look solid.Nice job documenting the options flow and the expanded
QueryResultshape; the examples line up with the new API behavior.
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
coverage: 76.442% (+0.002%) from 76.44% when pulling ccddce2d3a7e2cb88109bf665e42630fd531be28 on lboonekamp:11566-sql-recordset-return-fix into d57fe3bd8578b0b8f9847647fd046bccf825a7ef on typeorm:master.
I wonder whether we should make the last parameter of query an object of type QueryOptions containing the property useStructuredResult?: boolean.
Currently, in QueryRunner.query the last parameter is boolean just for this option, but the database clients that TypeORM uses can accept options for executing statements (e.g. to return rows are arrays instead of objects).
I wonder whether we should make the last parameter of
queryan object of typeQueryOptionscontaining the propertyuseStructuredResult?: boolean.Currently, in
QueryRunner.querythe last parameter is boolean just for this option, but the database clients that TypeORM uses can accept options for executing statements (e.g. to return rows are arrays instead of objects).
Great idea. Objects are much more extendable. Let me know if you want me to make the changes.
Super annoying. Almost like I didn't commit properly. I'll push an update shortly to fix this up.
Love seeing the movement on this issue! Wondering what's holding up the merge...
PR Code Suggestions ✨
Latest suggestions up to 97826d2
| Category | Suggestion | Impact |
| Possible issue |
Handle multiple recordsets correctlyIn src/driver/sqlserver/SqlServerQueryRunner.ts [291-297]
Suggestion importance[1-10]: 6__ Why: The suggestion correctly points out that | Low |
| General |
Improve QueryRunner type detectionIn src/data-source/DataSource.ts [540-550]
Suggestion importance[1-10]: 5__ Why: The suggestion correctly identifies a weakness in the type guard used to differentiate a | Low |
| ||
Previous suggestions
Suggestions up to commit 9d798e6
| Category | Suggestion | Impact |
| General |
Improve QueryRunner type detectionImprove the type guard in src/data-source/DataSource.ts [540-550]
Suggestion importance[1-10]: 5__ Why: The suggestion correctly points out that checking for a | Low |
| Possible issue |
Handle multiple recordsets correctlyRefactor the logic in src/driver/sqlserver/SqlServerQueryRunner.ts [291-297]
Suggestion importance[1-10]: 4__ Why: The suggestion correctly identifies that | Low |
Suggestions up to commit 74d734e
| Category | Suggestion | Impact |
| Possible issue |
Handle multiple recordsets correctlyRefactor the logic to populate src/driver/sqlserver/SqlServerQueryRunner.ts [291-297]
Suggestion importance[1-10]: 6__ Why: The suggestion improves the logic for handling multiple recordsets from SQL Server by ensuring | Low |
| General |
Improve QueryRunner type guardReplace the unsafe src/data-source/DataSource.ts [540-550]
Suggestion importance[1-10]: 5__ Why: The suggestion correctly identifies a weak type guard for | Low |
Suggestions up to commit f9141f4
| Category | Suggestion | Impact |
| Possible issue |
Handle multiple recordsets correctlyIn src/driver/sqlserver/SqlServerQueryRunner.ts [291-297]
Suggestion importance[1-10]: 6__ Why: The suggestion correctly identifies that if | Low |
✅ Suggestions up to commit 09e3944
| Category | Suggestion | Impact |
| Possible issue |
✅
| High |
Handle multiple SQL Server result setsIn src/driver/sqlserver/SqlServerQueryRunner.ts [291-297]
Suggestion importance[1-10]: 7__ Why: The suggestion correctly identifies that | Medium | |
✅ Suggestions up to commit 3afd257
| Category | Suggestion | Impact |
| Possible issue |
✅
| High |
Handle multiple SQL Server result setsWhen multiple result sets are returned from SQL Server, flatten them into src/driver/sqlserver/SqlServerQueryRunner.ts [291-297]
Suggestion importance[1-10]: 7__ Why: The suggestion correctly identifies that | Medium | |
@sgarner - what are your thoughts on the last change suggested by coderabbit "Handle multiple SQL Server result sets"? I've fixed the first change (pending commit). If we flatten the array, it defeats the purpose of the recordsets property that was added to QueryResult.ts.
Yeah that code rabbit suggestion doesn't make any sense
Yeah that code rabbit suggestion doesn't make any sense
My thoughts exactly.