Skip to content

FEAT: TVP support for async queries - #814

Open
Subrata (subrata-ms) wants to merge 13 commits into
mainfrom
subrata-ms/AQEIntegration
Open

Subrata (subrata-ms) wants to merge 13 commits into
mainfrom
subrata-ms/AQEIntegration

Conversation

@subrata-ms

@subrata-ms Subrata (subrata-ms) commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Work Item / Issue Reference

AB#47998,48435

GitHub Issue: #<ISSUE_NUMBER>


Summary

This pull request introduces several improvements and enhancements to the mssql_python.async_query async API, focusing on better native type integration, more robust and consistent cursor behavior, and improved handling of parameter iterables in executemany. It also expands the test suite to cover these changes and edge cases.

Key highlights include:

  • Lazy exposure of native SQL type hints and the table-valued parameter constructor.
  • Improved type and error handling for parameter iterables in executemany.
  • New and clarified properties and behaviors for async cursors, including a closed property.
  • Expanded and refined test coverage for new and existing features.

Native type integration and exports

  • Added lazy exports for native SQL type hints (SQL_MONEY, SQL_SMALLMONEY, SQL_XML, SQL_JSON, SQL_VECTOR) and the internal _TableValuedParameter constructor, with dynamic loading and error reporting if the native dependency is missing. These are now included in __all__ and dir() as appropriate.
  • Added tests to verify that these native exports are correctly exposed, loaded only when accessed, and report missing features properly.

AsyncCursor API and behavior improvements

  • Added a closed property to async cursors to reflect both wrapper and parent connection state, with tests for idempotency and parent connection closure propagation.
  • Clarified and enhanced docstrings for nextset, close, description, and rowcount to document async-specific behaviors and differences from the synchronous API.
  • Added tests to ensure nextset correctly tracks rowcount and description per result, including edge cases.

Executemany and parameter iterable handling

  • Changed executemany signatures to accept any synchronous iterable (not just sequences), improved error handling for non-iterables, and ensured iterables are consumed only once. Streaming/asynchronous iterables are explicitly not supported.
  • Added tests for handling of empty generators, iterator failures, and correct consumption of parameter iterables.

Logging and test adjustments

  • Updated logging to remove reliance on batch_count for executemany and adjusted test expectations accordingly.
  • Minor imports and test harness updates to support new features.

These changes make the async query API more robust, Pythonic, and consistent with both native and DB-API expectations.

Expose native TVP and SQL type-hint helpers lazily. Support iterable executemany batches and preserve result state on iteration failure. Add cursor closed-state reporting, document async result contracts, and cover these behaviors with async integration tests.
Copilot AI lite review requested due to automatic review settings September 24, 2026 10:08
@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

PR Performance Report

✅ No regression detected

No consistent slowdowns detected across all 2 environments.

0 IMPROVEMENTS 0 SLOWDOWNS 2/2 ENVIRONMENTS

Coverage: 2 of 2 environments completed. Advisory result; does not block merging.

Performance diagnostics

Phase times are inclusive diagnostics and must not be added together. They identify where measured time changed, not why it changed.

No affected phases or call-count changes were recorded.

All database tasks and timings

Unix / SQL Server 2022

Database task Before After Paired change Result
Connection opening 10.956 ms 10.719 ms -4.1% no signal
SELECT queries 1.087 ms 1.060 ms -0.0% no signal
Row insertion 34.920 ms 34.951 ms +0.8% no signal
Executemany inserts 158.816 ms 160.687 ms -0.2% no signal
Fetch-all queries 123.126 ms 123.628 ms +1.8% no signal
Row-by-row fetching 14.498 ms 14.441 ms -0.4% no signal
Batched row fetching 120.472 ms 120.416 ms +0.7% no signal
Transaction commit and rollback 115.668 ms 115.344 ms -0.8% no signal
Arrow row fetching 94.488 ms 96.387 ms +2.0% no signal
100,000-row insertion 472.726 ms 475.767 ms +3.8% no signal
Row fetching in batches of 100 122.375 ms 124.273 ms +3.1% no signal
Row fetching in batches of 10,000 144.279 ms 144.633 ms -3.0% no signal
Repeated positional queries 33.958 ms 34.209 ms +1.6% no signal
Repeated named-parameter queries 37.045 ms 35.357 ms -2.6% no signal
Legacy 100,000-row insertion 375.668 ms 375.936 ms +0.7% no signal
Insertion with explicit input sizes 534.217 ms 518.672 ms -4.9% no signal
Joined aggregation queries 181.157 ms 181.699 ms +0.3% no signal
Large joined-result fetching 191.215 ms 191.175 ms +0.5% no signal
1.2-million-row fetching 3531.683 ms 3504.500 ms -0.6% no signal
Common table expression queries 5.536 ms 5.539 ms +0.1% no signal
256 KiB VARCHAR(MAX) / fetchall() 1.272 ms 1.261 ms +0.5% no signal
10,000 scalar values / fetchval() (debug disabled) 108.371 ms 107.696 ms -0.0% no signal

Unix / SQL Server 2025

Database task Before After Paired change Result
Connection opening 99.592 ms 98.177 ms -0.2% no signal
SELECT queries 1.107 ms 1.107 ms +0.3% no signal
Row insertion 34.817 ms 34.623 ms -0.9% no signal
Executemany inserts 153.619 ms 157.880 ms +3.2% no signal
Fetch-all queries 126.651 ms 125.667 ms -1.6% no signal
Row-by-row fetching 14.612 ms 14.700 ms +1.3% no signal
Batched row fetching 126.257 ms 124.611 ms +1.2% no signal
Transaction commit and rollback 117.862 ms 115.106 ms +2.3% no signal
Arrow row fetching 97.171 ms 97.012 ms -0.3% no signal
100,000-row insertion 479.600 ms 475.832 ms -4.8% no signal
Row fetching in batches of 100 122.823 ms 123.899 ms +0.8% no signal
Row fetching in batches of 10,000 145.456 ms 144.622 ms -2.9% no signal
Repeated positional queries 33.441 ms 33.577 ms +0.5% no signal
Repeated named-parameter queries 36.454 ms 36.254 ms -0.8% no signal
Legacy 100,000-row insertion 394.276 ms 384.380 ms -2.5% no signal
Insertion with explicit input sizes 513.088 ms 555.997 ms +7.8% no signal
Joined aggregation queries 162.829 ms 161.191 ms -1.2% no signal
Large joined-result fetching 198.502 ms 195.239 ms -1.5% no signal
1.2-million-row fetching 3651.546 ms 3565.800 ms -1.0% no signal
Common table expression queries 5.329 ms 5.449 ms +1.8% no signal
256 KiB VARCHAR(MAX) / fetchall() 1.541 ms 1.553 ms +1.6% no signal
10,000 scalar values / fetchval() (debug disabled) 109.823 ms 110.651 ms +0.8% no signal
Build and measurement details

ADO build 182210

PR head: c40e92530b03f834f6412ec369c2e0ab6079fc92
Base: acdb42cfd39c9c59824272170286ca9bb1220052
Measured merge: 1e6fa6975bf4281481a525e2c81208c843354029

  • 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.

@subrata-ms Subrata (subrata-ms) changed the title FEAT: Add preview TVP support for async queries FEAT: Add TVP support for async queries Sep 24, 2026
@github-actions github-actions Bot added the pr-size: medium Moderate update size label Sep 24, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Guard the optional native dependency test and complete the required PR metadata.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 Medium severity

Open (1)
What changed in this PR

Adds preview async TVP/type-hint support, iterable executemany, cursor state reporting, and expanded integration tests.

Changes:

  • Adds lazy native TVP and SQL type-hint exports.
  • Supports synchronous iterable batches and failure-result preservation.
  • Documents and tests async cursor/result behavior.
File Summary
tests/​AsyncTest/​test_006_async_execute.py Tests iterable batches, TVPs, and type hints.
tests/​AsyncTest/​test_005_async_cursor.py Tests cursor state and result behavior.
tests/​AsyncTest/​test_004_async_logging.py Updates execution logging expectations.
tests/​AsyncTest/​test_001_async_query_native.py Tests native exports. Finding: moderate, 2 votes—add an importorskip guard for the optional dependency at lines 55 and 65.
mssql_python/​async_query/​async_execute.py Adds iterable batch execution and failure handling.
mssql_python/​async_query/​async_cursor.py Adds cursor state and result-contract documentation.
mssql_python/​async_query/​__init__.py Adds lazy native exports. Finding: nit, 1 vote—replace PR placeholders and empty summary with valid metadata.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tests/AsyncTest/test_001_async_query_native.py Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 24, 2026 10:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Fix the indentation error that prevents the native async test module from being collected.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity

Open (1)
Resolved since last review (1)

Comment thread tests/AsyncTest/test_001_async_query_native.py Outdated
Copilot AI review requested due to automatic review settings September 24, 2026 11:05
@github-actions github-actions Bot added pr-size: large Substantial code update and removed pr-size: medium Moderate update size labels Sep 24, 2026
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Fix the indentation error that prevents the native async test module from being collected.

Review effort: Lite
Findings: None

Resolved since last review (1)

Copilot AI review requested due to automatic review settings September 24, 2026 11:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Cancellation handling and version-specific collation test behavior need correction.

Review effort: Lite
Findings: None

@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

📊 Code Coverage Report

🔥 Diff Coverage

83%


🎯 Overall Coverage

85%


📈 Total Lines Covered: 9466 out of 11120
📁 Project: mssql-python


Diff Coverage

Diff: main...HEAD, staged and unstaged changes

  • mssql_python/async_query/init.py (100%)
  • mssql_python/async_query/async_cursor.py (100%)
  • mssql_python/async_query/async_execute.py (50.0%): Missing lines 102-106

Summary

  • Total: 31 lines
  • Missing: 5 lines
  • Coverage: 83%

mssql_python/async_query/async_execute.py

Lines 98-110

   98     iteration_failed = False
   99 
  100     def parameter_rows():
  101         nonlocal iteration_failed
! 102         try:
! 103             yield from seq_of_parameters
! 104         except BaseException:
! 105             iteration_failed = True
! 106             raise
  107 
  108     logger.debug(
  109         "AsyncCursor.executemany: starting; use_prepare=%s",
  110         use_prepare,


📋 Files Needing Attention

📉 Files with overall lowest coverage (click to expand)
mssql_python.pybind.performance_counter.hpp: 0.7%
mssql_python.pybind.logger_bridge.cpp: 57.9%
mssql_python.pybind.ddbc_bindings.h: 62.6%
mssql_python.pybind.logger_bridge.hpp: 70.8%
mssql_python.pybind.connection.connection_pool.cpp: 82.3%
mssql_python.pybind.connection.connection.cpp: 83.1%
mssql_python.logging.py: 86.2%
mssql_python.pooling.py: 90.1%
mssql_python.async_query.async_execute.py: 91.5%
mssql_python.pybind.fetch_temporal.hpp: 92.1%

🔗 Quick Links

⚙️ Build Summary 📋 Coverage Details

View Azure DevOps Build

Browse Full Coverage Report

Copilot AI review requested due to automatic review settings September 24, 2026 11:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

The pinned native core dependency must be updated or the new exports gated before approval.

Review effort: Lite
Findings: None

@subrata-ms Subrata (subrata-ms) changed the title FEAT: Add TVP support for async queries FEAT: TVP support for async queries Sep 24, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Correctness depends on external Rust-core behavior and live SQL Server integration that could not be exercised here.

Review effort: Balanced
Findings: None

Resolved since last review (1)

Copilot AI review requested due to automatic review settings September 24, 2026 13:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Native-backed TVP behavior and the extensive live-SQL execution matrix require final human validation.

Review effort: Balanced
Findings: None

Copilot AI review requested due to automatic review settings September 24, 2026 15:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

A new fetch test relies on undefined SQL row ordering and may fail across execution plans or server versions.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)

Comment thread tests/AsyncTest/test_007_async_fetch.py Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 24, 2026 15:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Correctness depends heavily on external native bindings and live SQL Server behavior requiring final human validation.

Review effort: Balanced
Findings: None

Resolved since last review (1)

Copilot AI balanced review requested due to automatic review settings October 8, 2026 10:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Native-backed async behavior and live SQL integration require final human validation despite no confirmed defect.

0 open findings

🧠 Review effort: Balanced


Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.

SQL_VECTOR: int

_NATIVE_EXPORTS = {
"_TableValuedParameter": "TableValuedParameter",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #814 - High: This exposes a TVP constructor whose pinned dependency, mssql-python-rs==0.3.0, rejects valid numeric and temporal column metadata. Definitions such as decimal(12,2), numeric(38,10), and temporal scale 3 fail during construction, even for populated or empty tables, because the native builder incorrectly applies scalar-NULL restrictions.

Valid TVPs therefore cannot reach async execution. Please correct native column-template and NULL-cell conversion, pin the corrected dependency, and add regression coverage for populated, NULL-cell, and empty TVPs.

Copilot AI balanced review requested due to automatic review settings October 12, 2026 11:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The pinned native dependency still rejects valid numeric and scaled temporal TVP metadata, leaving advertised TVP support incomplete.

0 open findings

🧠 Review effort: Balanced

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For context, I have narrowed #789 to the shared TableValuedParameter definition, synchronous ODBC streaming, and sync documentation/tests. I removed the async adapter and async TVP tests from that PR to avoid duplicating the integration here.

The design direction remains one public TVP value with separate ODBC and TDS implementations. Could we build the async support here around that shared class, while retaining the native work already in this PR?

Shared interface

  • Use mssql_python.TableValuedParameter(type_name, columns, rows, *, schema=None) as the application-facing value. Keep _TableValuedParameter internal.
  • Preserve the shared column format (name, sql_type[, size_or_precision[, scale]]), positional binding, and explicit metadata. Translate this into native column triples inside async_execute.py, including the shared 0 = MAX convention.
  • Preserve the existing identity and empty-input rules: schema.Type or a separate schema argument; [] for an empty table; None for nullable cells, not for the table itself. Reuse the shared validation rather than adding a parallel contract.

Initial async scope

  • Start with materialized list/tuple rows. Reject synchronous iterators before consumption rather than silently materializing them. Native streaming remains a separate follow-up.
  • Support the shared value through execute(), including named parameters, surrounding scalars and multiple TVPs. Keep TVP executemany() and TVP setinputsizes() overrides outside this initial slice; this does not affect ordinary non-TVP batch support.
  • Document the current Rust 0.3.0 metadata restrictions: DECIMAL/NUMERIC(18,10) and temporal scale 7. Broader support needs a native fix and dependency update, not silent changes to the caller's declarations.

Landing and coverage

  • Land the shared definition through #789, then integrate the async adapter here without copying the class. The independent cursor and type-hint improvements do not need to be rewritten.
  • Use the shared wrapper in customer examples and async acceptance tests. Mirror the applicable cases in tests/test_028_table_valued_parameters.py: empty/NULL input, supported types, Unicode/binary/MAX values, mixed parameters, reuse and error recovery.
  • Include rejection-before-consumption and caller-input preservation. Keep native-constructor tests as internal coverage and large memory workloads in stress coverage.

This keeps the implementation ownership separate while giving callers the same TVP interface.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-size: large Substantial code update

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants