Skip to content

Use sync-first TDS cursor reads in ODBC - #307

Draft
Saurabh Singh (saurabh500) wants to merge 1 commit into
dev/saurabh500/tds-sync-first-cursorfrom
dev/saurabh500/odbc-sync-first-cursor
Draft

Use sync-first TDS cursor reads in ODBC#307
Saurabh Singh (saurabh500) wants to merge 1 commit into
dev/saurabh500/tds-sync-first-cursorfrom
dev/saurabh500/odbc-sync-first-cursor

Conversation

@saurabh500

Copy link
Copy Markdown
Contributor

Description

Layer 4 (top) of a separate experimental performance stack.

Updates the existing row-at-a-time / column-at-a-time ODBC paths to use layer 3's explicit sync-first TDS cursor APIs:

  • SQLFetch calls try_next_row_cursor before the async fallback.
  • SQLGetData calls try_read_row_column before the async fallback.
  • CursorPoll::Ready bypasses Tokio; CursorPoll::Pending invokes the existing async method exactly once.

All existing client ownership, DBC/statement locking, diagnostics, INFO handling, active-statement state, PLP behavior, timeout/cancellation behavior, and error recovery remain unchanged. This does not batch, prefetch, or materialize whole rows.

Stack parent: #306

Linux benchmark

SQL Server 2022 in Podman, unixODBC, Encrypt=No, TrustServerCertificate=Yes, CPUs 8-15. Each process used two warmups and seven measured passes over 200,000 rows with SQLExecDirect + SQLFetch + four sequential SQLGetData calls (INT, BIGINT, NVARCHAR, BIT). Runs were counterbalanced parent/candidate/candidate/parent.

Revision Aggregate median Throughput
Parent #306 602.788 ms 331,792 rows/s
This PR 198.371 ms 1,008,212 rows/s
  • Throughput: +203.87%
  • Latency: -67.09%
  • Paired process gains: +206.65% and +198.50%
  • Identical checksum on every run
  • Approximately 39.9% of the previously measured patched raw-TDS throughput (2.528M rows/s)

Raw result data is retained in the authoring session artifacts.

Correctness coverage

The resolve_cursor_poll tests prove:

  • Ready skips fallback entirely.
  • Pending invokes fallback exactly once.
  • A sync-attempt error does not invoke fallback.

The full loadable-driver e2e suite passes against SQL Server, covering SQLFetch, SQLGetData, PLP, result navigation, transactions, and recovery through unixODBC.

Validation

  • cargo fmt --all -- --check
  • cargo nextest run -p mssql-odbc --no-fail-fast — 551 passed
  • cargo clippy -p mssql-odbc --frozen --all-features --all-targets -- -D warnings
  • mssql-odbc/tests/e2e/run_e2e.sh --release --driver=... — 19/19 test binaries passed

Limitations

The sync-first coverage is defined by layer 3. PLP, encrypted, skipped, unsupported, incomplete, and control-token paths continue through the existing async implementation.

Related Issues

Related to #306.

Checklist

  • Formatting passes
  • Clippy passes with warnings denied
  • Unit and loadable-driver e2e tests pass
  • Strict per-row/per-column streaming is preserved
  • No batching, prefetch, or whole-row materialization

@saurabh500
Saurabh Singh (saurabh500) changed the base branch from dev/saurabh500/tds-cursor-hot-path to dev/saurabh500/tds-sync-first-cursor August 15, 2026 18:41
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@saurabh500
Saurabh Singh (saurabh500) force-pushed the dev/saurabh500/odbc-sync-first-cursor branch from c7fe816 to 983a858 Compare August 15, 2026 19:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant