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
Keep internal fetch metadata native and reuse stable column descriptions within the current result set. This removes the fetchmany() Python-dictionary roundtrip and repeated descriptions in fetchone()/iteration, small fetchmany() calls, and row-wise MAX/LOB fetching.
The cache is statement-owned and invalidated on execution, result transitions, relevant connection operations and cleanup. Public descriptions stay fresh and Unicode-name validation timing is preserved. Every declared SQL_VARIANT retains per-row descriptions and per-value probes. No persistent fetch buffers, binding reuse, hidden prefetch, fetch-size changes or cached decoding/converter results are introduced.
flowchart LR
subgraph Before
B1["Each fetch / row"] --> B2["Repeated descriptions; dict setup for fetchmany"] --> B3["Bind / fetch"]
end
subgraph After
A1["First fetch in result set"] --> A2["Owned native metadata"] --> A3["Reuse stable fields; same bind / fetch"]
end
Loading
Mechanism and correctness
Historical ON instrumentation at 252e9b69 counts actual ODBC descriptions for stable, error-free 10,000-row/24-column drains:
Workload
Main
This PR at 252e9b69
fetchmany(1)
240,024
24
fetchone()
240,000
24
MAX/LOB fetchall()
240,024
24
Ordinary fetchall() control
24
24
These are driver calls, not SQL network round-trips or elapsed-time savings. Mixed/NULL variant cases retain all 2,000 per-value NULL probes and 1,715 non-NULL subtype probes. Explicit public-description controls still perform fresh descriptions.
Historical OFF validation at 252e9b69: 338 passed, 9 skipped per arm across five invocations (metadata, temporal constructors, settings/NULL, Arrow/interleaving, lifetime). Candidate ON metadata/count checks: 55 passed, 3 skipped. Three cases require cursor preservation not advertised by this driver; native helper ownership/invalidation checks provide separate, limited coverage.
Follow-up c5fe1425 extracts the existing child-cache invalidation helper without changing its algorithm, requires scalar NULL success, and adds permanent native invariant tests with Windows/Linux/macOS CI. Bounded checks against 252e9b69 passed on both arms: OFF metadata/interleaving 52 passed, 9 skipped, OFF lifetime 10 passed, and ON metadata/counts 55 passed, 3 skipped, in three separate invocations per arm. The six new candidate-only native cases passed locally on Linux and Windows with active Release assertions and deliberately failing assertion controls. The new native CI matrix also passed all six cases on Linux, Windows, and macOS. This is not a full repository suite; broader CI remains pending.
The previous Windows timings and the user-cancelled earlier incremental study are not reused. The completed OFF study applies only to 252e9b69/tree 3187b527, not this follow-up: all 15 A/A gates failed and 13 A/B no-regression bounds remained unresolved. Performance acceptance is not met; no general-speedup or no-regression signoff is claimed.
Unix / SQL Server 2022: Python 3.12.3, x86_64, SQL 16.0.4295.3; 5 paired comparisons and 1 warmup.
Unix / SQL Server 2025: Python 3.12.3, x86_64, SQL 17.0.5005.3; 5 paired comparisons and 1 warmup.
A consistent change requires more than 20% median paired movement, at least 1 ms between the median runtimes, and at least 80% of pairs exceeding the relative threshold in the same direction. A slowdown without enough pair agreement is reported as inconsistent.
The displayed change is the median of paired before-and-after ratios. It is not recalculated from the two displayed median runtimes.
Both revisions use profiling-enabled builds on the same agent and database, with alternating order and discarded warmups. Results are diagnostic and do not represent production-wheel latency.
Raw samples and logs are attached to the ADO run as profiler-* artifacts.
The reason will be displayed to describe this comment to others. Learn more.
Copilot review overview
🟡 Changes recommended
The implementation persists metadata across fetch calls despite the PR description promising call-local metadata and unchanged fresh ODBC descriptions.
Get a fresh assessment by requesting another Copilot review.
Jahnvi Thakkar (jahnvi480)
changed the title
PERF: Reuse native column metadata across result-set fetches
PERF: Reuse stable native column metadata per result set
Sep 22, 2026
The reason will be displayed to describe this comment to others. Learn more.
No actionable findings.
Reviewed all seven changed files and affected Python/native paths, focusing on metadata ownership, cache invalidation, mixed fetch operations, Unicode handling, sql_variant, and cleanup.
The earlier comments are addressed: the public metadata wrapper no longer creates the unnecessary intermediate object, and the PR description now explicitly documents statement-owned caching.
Preserve reserve-before-locking-weak-handles and release retained handles outside the child-list mutex. Expose the unchanged native-only algorithm for direct invariant coverage.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Require successful scalar NULL handling and exact describe counts. Add production-header cache, failure, concurrency, allocation and lifetime tests with active Release assertions, plus Windows/Linux/macOS CTest CI and test guidance.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The Arrow fetch path still calls SQLDescribeCol_wrap here and then reads Python dictionaries for every arrow_batch() invocation, so repeated Arrow batches continue doing one ODBC description per column and the Python metadata roundtrip. That bypasses the new native cache; route this path through GetResultMetadata and the native metadata accessors (and add a description-count regression test) so Arrow batches also reuse stable result metadata.
Use C++ streams for test-runner output, document the line-scoped allocator rule exception, and trigger native invariant tests for production integration header and binding changes. Keep production fetch code and the allocation-failure probes unchanged.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
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
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.
Work Item / Issue Reference
Summary
Keep internal fetch metadata native and reuse stable column descriptions within the current result set. This removes the
fetchmany()Python-dictionary roundtrip and repeated descriptions infetchone()/iteration, smallfetchmany()calls, and row-wise MAX/LOB fetching.The cache is statement-owned and invalidated on execution, result transitions, relevant connection operations and cleanup. Public descriptions stay fresh and Unicode-name validation timing is preserved. Every declared
SQL_VARIANTretains per-row descriptions and per-value probes. No persistent fetch buffers, binding reuse, hidden prefetch, fetch-size changes or cached decoding/converter results are introduced.flowchart LR subgraph Before B1["Each fetch / row"] --> B2["Repeated descriptions; dict setup for fetchmany"] --> B3["Bind / fetch"] end subgraph After A1["First fetch in result set"] --> A2["Owned native metadata"] --> A3["Reuse stable fields; same bind / fetch"] endMechanism and correctness
Historical ON instrumentation at
252e9b69counts actual ODBC descriptions for stable, error-free 10,000-row/24-column drains:252e9b69fetchmany(1)fetchone()fetchall()fetchall()controlThese are driver calls, not SQL network round-trips or elapsed-time savings. Mixed/NULL variant cases retain all 2,000 per-value NULL probes and 1,715 non-NULL subtype probes. Explicit public-description controls still perform fresh descriptions.
Historical OFF validation at
252e9b69: 338 passed, 9 skipped per arm across five invocations (metadata, temporal constructors, settings/NULL, Arrow/interleaving, lifetime). Candidate ON metadata/count checks: 55 passed, 3 skipped. Three cases require cursor preservation not advertised by this driver; native helper ownership/invalidation checks provide separate, limited coverage.Follow-up
c5fe1425extracts the existing child-cache invalidation helper without changing its algorithm, requires scalar NULL success, and adds permanent native invariant tests with Windows/Linux/macOS CI. Bounded checks against252e9b69passed on both arms: OFF metadata/interleaving 52 passed, 9 skipped, OFF lifetime 10 passed, and ON metadata/counts 55 passed, 3 skipped, in three separate invocations per arm. The six new candidate-only native cases passed locally on Linux and Windows with active Release assertions and deliberately failing assertion controls. The new native CI matrix also passed all six cases on Linux, Windows, and macOS. This is not a full repository suite; broader CI remains pending.The previous Windows timings and the user-cancelled earlier incremental study are not reused. The completed OFF study applies only to
252e9b69/tree3187b527, not this follow-up: all 15 A/A gates failed and 13 A/B no-regression bounds remained unresolved. Performance acceptance is not met; no general-speedup or no-regression signoff is claimed.