Skip to content

PERF: Reuse stable native column metadata per result set - #796

Merged
Jahnvi Thakkar (jahnvi480) merged 16 commits into
mainfrom
jahnvi/perf-small-fetch-native-metadata
Sep 24, 2026
Merged

Jahnvi Thakkar (jahnvi480) merged 16 commits into
mainfrom
jahnvi/perf-small-fetch-native-metadata

Conversation

@jahnvi480

@jahnvi480 Jahnvi Thakkar (jahnvi480) commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Work Item / Issue Reference

GitHub Issue: #554

ADO Task: AB#48323


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

Current scope and validation

The 65081f06 scope correction, retained in be3efbb6, leaves five runtime files +332 / -56 (25 fewer net runtime lines than d0af2acc) and the related changelog entry. There is zero effective build/CI diff. Review follow-up be3efbb6 strengthens two existing test functions (+21 / -7) for metadata invalidation across re-execution/result transitions and row lifetime across connection/cursor cleanup, without changing production code. No test files, test functions or parameterized cases are added. PR-only test files, native CMake/workflow scaffolding, and added test guidance remain removed.

Fresh scoped Linux Release/OFF builds of pre-cleanup d0af2acc and candidate 65081f06 each passed 339 ordinary checks plus 3 isolated checks, with no skips, using the same existing base-test selection, not the removed metadata/native fixtures. This evidence qualifies the tested cleanup revision, not the later whole 4616f2c5 main merge, current be3efbb6 assertions, or a full repository suite. Current-head execution of the strengthened tests, performance qualification and reviewer signoff remain pending. Windows/macOS qualification, ON/count checks and latency measurements were not included. The older results below remain historical, not current-head qualification.

Historical mechanism and correctness

Historical ON instrumentation at 252e9b69 counts actual ODBC descriptions for stable, error-free 10,000-row/24-column drains:

Workload Baseline 8fb3c3b1 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 retained all 2,000 per-value NULL probes and 1,715 non-NULL subtype probes. Explicit public-description controls still performed 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 required cursor preservation not advertised by this driver.

Historical follow-up c5fe1425 added the now-removed native test/CI scaffolding. Its 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. Six candidate-only native cases passed locally on Linux and Windows with active Release assertions and deliberately failing assertion controls; the historical native CI matrix passed all six on Linux, Windows, and macOS. These were limited checks, not a full repository suite, and neither the tests nor that workflow remain in the effective PR diff.

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 the current head: all 15 A/A gates failed and 13 A/B no-regression bounds remained unresolved. Performance acceptance remains on HOLD; no general-speedup or no-regression signoff is claimed.

Remove native metadata dictionary roundtrips while preserving eager Unicode names and fresh per-call descriptions. Add behavior and profiling regression coverage. Performance acceptance remains unresolved after the bounded local study.

Co-authored-by: Copilot App <[email protected]>
Copilot AI lite review requested due to automatic review settings September 17, 2026 15:53

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 declared performance acceptance and no-regression gates remain unresolved, including failed A/A stability results.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Refactors fetchmany() metadata handling to avoid Python dictionary round-trips while preserving public descriptions and existing fetch behavior.

Changes:

  • Adds call-local native metadata structures and shared description logic.
  • Adds comprehensive fetch, metadata, lifecycle, and profiling tests.
  • Documents the behavior change in the changelog.
File summaries
File Description
mssql_python/pybind/ddbc_bindings.cpp Uses native metadata for fetchmany().
tests/test_040_fetch_native_metadata.py Adds regression and profiling coverage.
CHANGELOG.md Documents the metadata refactor.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@jahnvi480 Jahnvi Thakkar (jahnvi480) changed the title REFACTOR: Keep fetchmany column metadata native and call-local PERF: Keep fetchmany column metadata native and call-local Sep 17, 2026
@github-actions

github-actions Bot commented Sep 17, 2026 •

Copy link
Copy Markdown

📊 Code Coverage Report

🔥 Diff Coverage

89%


🎯 Overall Coverage

84%


📈 Total Lines Covered: 9229 out of 10895
📁 Project: mssql-python


Diff Coverage

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

  • mssql_python/pybind/connection/connection.cpp (100%)
  • mssql_python/pybind/ddbc_bindings.cpp (87.0%): Missing lines 1601,1844,3094-3095,3107,3111,3117,3373,3393-3400,3406-3408,4364-4365,4646-4647,5027-5028
  • mssql_python/pybind/result_metadata.hpp (100%)

Summary

  • Total: 234 lines
  • Missing: 25 lines
  • Coverage: 89%

mssql_python/pybind/ddbc_bindings.cpp

Lines 1597-1605

  1597 }
  1598 
  1599 SQLRETURN SqlHandle::freeHandle() {
  1600     PERF_TIMER("SqlHandle::free");
! 1601     resultMetadata.clear();
  1602     bool pythonShuttingDown = is_python_finalizing();
  1603     bool skipDuringShutdown = _type == SQL_HANDLE_STMT || _type == SQL_HANDLE_DBC;
  1604 #ifdef _WIN32
  1605     // The static ENV is destroyed during DLL_PROCESS_DETACH, after Python

Lines 1840-1848

  1840 SQLRETURN SQLColumns_wrap(SqlHandlePtr StatementHandle, const py::object& catalogObj,
  1841                           const py::object& schemaObj, const py::object& tableObj,
  1842                           const py::object& columnObj) {
  1843     PERF_TIMER("SQLColumns_wrap");
! 1844     StatementHandle->resultMetadata.clear();
  1845     if (!SQLColumns_ptr) {
  1846         ThrowStdException("SQLColumns function not loaded");
  1847     }

Lines 3090-3099

  3090         }
  3091 
  3092         if (SQL_SUCCEEDED(retcode)) {
  3093             auto name = dupeSqlWCharAsUtf16Le(
! 3094                 ColumnName, std::min(static_cast<size_t>(NameLength),
! 3095                                      (sizeof(ColumnName) / sizeof(SQLWCHAR)) - 1));
  3096             appendColumn(std::move(name), DataType, ColumnSize, DecimalDigits, Nullable);
  3097         } else {
  3098             return retcode;
  3099         }

Lines 3103-3115

  3103 
  3104 }  // namespace
  3105 
  3106 // Wrap SQLDescribeCol
! 3107 SQLRETURN SQLDescribeCol_wrap(SqlHandlePtr StatementHandle, py::list& ColumnMetadata) {
  3108     PERF_TIMER("SQLDescribeCol_wrap");
  3109     SQLRETURN ret = SQL_ERROR;
  3110     ResultMetadataFailureGuard metadataFailure(StatementHandle->resultMetadata, ret);
! 3111     ret = DescribeColumns(StatementHandle, [&](std::u16string name, SQLSMALLINT type,
  3112                                               SQLULEN size, SQLSMALLINT digits,
  3113                                               SQLSMALLINT nullable) {
  3114         ColumnMetadata.append(
  3115             py::dict("ColumnName"_a = name, "DataType"_a = type, "ColumnSize"_a = size,

Lines 3113-3121

  3113                                               SQLSMALLINT nullable) {
  3114         ColumnMetadata.append(
  3115             py::dict("ColumnName"_a = name, "DataType"_a = type, "ColumnSize"_a = size,
  3116                      "DecimalDigits"_a = digits, "Nullable"_a = nullable));
! 3117     });
  3118     return ret;
  3119 }
  3120 
  3121 SQLRETURN SQLSpecialColumns_wrap(SqlHandlePtr StatementHandle, SQLSMALLINT identifierType,

Lines 3369-3377

  3369                               : nullptr;
  3370     auto pending = metadata ? nullptr : std::make_shared<ResultMetadata>();
  3371     bool complete = true;
  3372     if (pending) {
! 3373         pending->columns.reserve(colCount);
  3374     }
  3375 
  3376     for (SQLSMALLINT i = 1; i <= colCount; ++i) {
  3377         SQLWCHAR uncachedColumnName[256];

Lines 3389-3404

  3389             columnName = reinterpretU16stringAsSqlWChar(column.name);
  3390             ret = SQL_SUCCESS;
  3391         } else {
  3392             {
! 3393                 PERF_TIMER("SQLDescribeCol::driver_call");
! 3394                 ret = SQLDescribeCol_ptr(hStmt, i, uncachedColumnName,
! 3395                                          sizeof(uncachedColumnName) / sizeof(SQLWCHAR),
! 3396                                          &columnNameLen, &dataType, &columnSize, &decimalDigits,
! 3397                                          &nullable);
! 3398             }
! 3399             if (!SQL_SUCCEEDED(ret)) {
! 3400                 LOG("SQLGetData: Error retrieving metadata for column %d - "
  3401                     "SQLDescribeCol SQLRETURN=%d",
  3402                     i, ret);
  3403                 complete = false;
  3404                 row.append(py::none());

Lines 3402-3412

  3402                     i, ret);
  3403                 complete = false;
  3404                 row.append(py::none());
  3405                 continue;
! 3406             }
! 3407             if (pending) {
! 3408                 // Capture declared metadata before probing a variant's current value.
  3409                 pending->columns.push_back({
  3410                     dupeSqlWCharAsUtf16Le(
  3411                         uncachedColumnName, std::min(static_cast<size_t>(columnNameLen),
  3412                                                     std::size(uncachedColumnName) - 1)),

Lines 4360-4369

  4360 
  4361     {
  4362         PERF_TIMER("FetchBatchData::cache_column_metadata");
  4363         for (SQLUSMALLINT col = 0; col < numCols; col++) {
! 4364             const auto& columnMeta = GetFetchColumnMetadata(columnNames, col);
! 4365             columnInfos[col].dataType = GetFetchColumnType(columnMeta);
  4366             columnInfos[col].columnSize = GetFetchColumnSize(columnMeta);
  4367             columnInfos[col].isLob =
  4368                 std::find(lobColumns.begin(), lobColumns.end(), col + 1) != lobColumns.end();
  4369             columnInfos[col].processedColumnSize = columnInfos[col].columnSize;

Lines 4642-4651

  4642                     PyList_SET_ITEM(row, col - 1, uuid_obj.release().ptr());
  4643                     break;
  4644                 }
  4645                 default: {
! 4646                     const auto& columnMeta = GetFetchColumnMetadata(columnNames, col - 1);
! 4647                     std::string columnName = GetFetchColumnName(columnMeta);
  4648                     std::ostringstream errorString;
  4649                     errorString << "Unsupported data type for column - " << columnName.c_str()
  4650                                 << ", Type - " << dataType << ", column ID - " << col;
  4651                     LOG("FetchBatchData: %s", errorString.str().c_str());

Lines 5023-5032

  5023 
  5024     // An overly large fetch size doesn't seem to help performance
  5025     int fetchSize = 64;
  5026 
! 5027     SQLRETURN ret = SQL_ERROR;
! 5028     ResultMetadataFailureGuard metadataFailure(StatementHandle->resultMetadata, ret);
  5029     SQLHSTMT hStmt = StatementHandle->get();
  5030     // Retrieve column count
  5031     SQLSMALLINT numCols = SQLNumResultCols_wrap(StatementHandle);
  5032     if (numCols <= 0) {


📋 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: 64.1%
mssql_python.pybind.logger_bridge.hpp: 70.8%
mssql_python.pybind.ddbc_bindings.cpp: 78.7%
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.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 21, 2026 08:51
@github-actions

github-actions Bot commented Sep 21, 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.

Unix / SQL Server 2022

SELECT queries: ddbc::SQLExecDirect_wrap +0.011 ms; py::fetchall::cpp_call +0.011 ms; py::execute::cpp_call +0.011 ms. Call changes: ddbc::SQLDescribeCol::driver_call (added, removed, or intermittent).
Fetch-all queries: ddbc::FetchBatchData::SQLFetchScroll_call +1.482 ms; ddbc::FetchAll_wrap +1.400 ms; ddbc::FetchBatchData +1.365 ms. Call changes: ddbc::SQLDescribeCol::driver_call (added, removed, or intermittent).
Row-by-row fetching: no measured phase delta. Call changes: ddbc::SQLDescribeCol::driver_call (added, removed, or intermittent).
Batched row fetching: ddbc::FetchBatchData +0.373 ms; ddbc::FetchBatchData::construct_rows +0.287 ms. Call changes: ddbc::SQLDescribeCol::driver_call (added, removed, or intermittent); ddbc::SQLDescribeCol_wrap (added, removed, or intermittent).
Transaction commit and rollback: ddbc::Connection::rollback +0.711 ms; ddbc::ConnectionHandle::rollback +0.704 ms; py::execute::cpp_call +0.625 ms. Call changes: ddbc::SQLDescribeCol::driver_call (added, removed, or intermittent).
Arrow row fetching: ddbc::FetchArrowBatch_wrap +0.084 ms; ddbc::SQLDescribeCol_wrap +0.000 ms; ddbc::SQLNumResultCols_wrap +0.000 ms. Call changes: ddbc::SQLDescribeCol::driver_call (added, removed, or intermittent).
Row fetching in batches of 100: no measured phase delta. Call changes: ddbc::SQLDescribeCol::driver_call (added, removed, or intermittent); ddbc::SQLDescribeCol_wrap (added, removed, or intermittent).
Row fetching in batches of 10,000: no measured phase delta. Call changes: ddbc::SQLDescribeCol::driver_call (added, removed, or intermittent); ddbc::SQLDescribeCol_wrap (added, removed, or intermittent).
Repeated positional queries: py::execute::cpp_call +0.601 ms; ddbc::SQLExecute_wrap +0.564 ms; py::fetchone::diag_records +0.178 ms. Call changes: ddbc::SQLDescribeCol::driver_call (added, removed, or intermittent).
Repeated named-parameter queries: py::execute::cpp_call +0.261 ms; ddbc::SQLExecute_wrap +0.258 ms; ddbc::SQLGetAllDiagRecords +0.080 ms. Call changes: ddbc::SQLDescribeCol::driver_call (added, removed, or intermittent).
Joined aggregation queries: py::fetchall::row_wrap +0.006 ms; ddbc::SQLDescribeCol_wrap +0.005 ms; ddbc::FetchBatchData::cache_column_metadata +0.001 ms. Call changes: ddbc::SQLDescribeCol::driver_call (added, removed, or intermittent).
Large joined-result fetching: ddbc::FetchAll_wrap +3.546 ms; ddbc::FetchBatchData +3.542 ms; py::fetchall::cpp_call +3.342 ms. Call changes: ddbc::SQLDescribeCol::driver_call (added, removed, or intermittent).
1.2-million-row fetching: ddbc::FetchBatchData::SQLFetchScroll_call +17.240 ms; ddbc::FetchBatchData +0.010 ms; py::fetchall::diag_records +0.004 ms. Call changes: ddbc::SQLDescribeCol::driver_call (added, removed, or intermittent).
Common table expression queries: py::execute::cpp_call +0.016 ms; ddbc::SQLExecDirect_wrap +0.013 ms; py::execute::post_execute +0.005 ms. Call changes: ddbc::SQLDescribeCol::driver_call (added, removed, or intermittent).

Unix / SQL Server 2025

SELECT queries: py::fetchall::cpp_call +0.006 ms; ddbc::FetchAll_wrap +0.006 ms; ddbc::SQLDescribeCol_wrap +0.002 ms. Call changes: ddbc::SQLDescribeCol::driver_call (added, removed, or intermittent).
Fetch-all queries: py::fetchall::cpp_call +4.278 ms; ddbc::FetchBatchData +3.419 ms; ddbc::FetchAll_wrap +3.401 ms. Call changes: ddbc::SQLDescribeCol::driver_call (added, removed, or intermittent).
Row-by-row fetching: ddbc::SQLGetAllDiagRecords +0.213 ms; py::fetchone::diag_records +0.138 ms; ddbc::SQLNumResultCols_wrap +0.014 ms. Call changes: ddbc::SQLDescribeCol::driver_call (added, removed, or intermittent).
Batched row fetching: ddbc::FetchBatchData::SQLFetchScroll_call +1.314 ms. Call changes: ddbc::SQLDescribeCol::driver_call (added, removed, or intermittent); ddbc::SQLDescribeCol_wrap (added, removed, or intermittent).
Transaction commit and rollback: ddbc::SQLDescribeCol_wrap +0.152 ms; py::execute::post_execute +0.141 ms; ddbc::SQLRowCount_wrap +0.002 ms. Call changes: ddbc::SQLDescribeCol::driver_call (added, removed, or intermittent).
Arrow row fetching: ddbc::SQLDescribeCol_wrap +0.000 ms. Call changes: ddbc::SQLDescribeCol::driver_call (added, removed, or intermittent).

8 additional diagnostic rows are available in the raw ADO artifacts.

All database tasks and timings

Unix / SQL Server 2022

Database task Before After Paired change Result
Connection opening 10.417 ms 10.570 ms +1.9% no signal
SELECT queries 1.092 ms 1.098 ms +4.0% no signal
Row insertion 35.247 ms 36.961 ms +2.5% no signal
Executemany inserts 158.025 ms 159.548 ms +1.0% no signal
Fetch-all queries 122.303 ms 123.729 ms +1.9% no signal
Row-by-row fetching 56.444 ms 55.149 ms -1.9% no signal
Batched row fetching 124.650 ms 122.754 ms -1.7% no signal
Transaction commit and rollback 117.129 ms 119.584 ms +3.0% no signal
Arrow row fetching 94.901 ms 95.215 ms +0.4% no signal
100,000-row insertion 455.482 ms 461.127 ms +1.7% no signal
Row fetching in batches of 100 183.975 ms 158.238 ms -14.0% no signal
Row fetching in batches of 10,000 147.200 ms 126.029 ms -13.1% no signal
Repeated positional queries 42.317 ms 43.138 ms +0.6% no signal
Repeated named-parameter queries 44.739 ms 45.148 ms +0.2% no signal
Legacy 100,000-row insertion 355.689 ms 362.711 ms +2.0% no signal
Insertion with explicit input sizes 493.376 ms 506.591 ms +2.8% no signal
Joined aggregation queries 179.983 ms 178.741 ms -0.7% no signal
Large joined-result fetching 183.368 ms 185.801 ms +2.6% no signal
1.2-million-row fetching 3499.526 ms 3505.144 ms +0.1% no signal
Common table expression queries 5.400 ms 5.395 ms +1.2% no signal

Unix / SQL Server 2025

Database task Before After Paired change Result
Connection opening 98.120 ms 97.654 ms +0.2% no signal
SELECT queries 1.079 ms 1.097 ms -0.3% no signal
Row insertion 34.702 ms 34.626 ms -1.8% no signal
Executemany inserts 154.485 ms 152.659 ms -0.9% no signal
Fetch-all queries 123.704 ms 128.335 ms +4.2% no signal
Row-by-row fetching 56.113 ms 55.290 ms -1.0% no signal
Batched row fetching 127.013 ms 122.443 ms -1.7% no signal
Transaction commit and rollback 115.808 ms 115.111 ms -0.4% no signal
Arrow row fetching 95.414 ms 93.775 ms -1.8% no signal
100,000-row insertion 482.506 ms 479.156 ms -5.2% no signal
Row fetching in batches of 100 176.285 ms 159.670 ms -9.6% no signal
Row fetching in batches of 10,000 144.574 ms 131.362 ms -11.2% no signal
Repeated positional queries 42.119 ms 41.935 ms +0.1% no signal
Repeated named-parameter queries 44.893 ms 44.635 ms -0.6% no signal
Legacy 100,000-row insertion 365.310 ms 365.877 ms -2.1% no signal
Insertion with explicit input sizes 499.300 ms 495.143 ms +0.2% no signal
Joined aggregation queries 163.014 ms 160.210 ms -1.7% no signal
Large joined-result fetching 193.742 ms 188.098 ms +0.8% no signal
1.2-million-row fetching 3571.476 ms 3550.197 ms -0.2% no signal
Common table expression queries 5.308 ms 5.367 ms +1.1% no signal
Build and measurement details

ADO build 177871

PR head: ae8432bb88d1660bd611fd12ee6b95b79a77e487
Base: 527c37fc8fd67a32725c2605c2c338865f63cee0
Measured merge: c636b2a70d0da6cc64adaf6839679bb152694f8f

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

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 performance-sensitive native ODBC path has unstable calibration and no established no-regression result.

Review effort: Lite
Findings: None

@github-actions github-actions Bot added the pr-size: large Substantial code update label Sep 21, 2026
Copilot AI review requested due to automatic review settings September 22, 2026 05: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

🟡 Changes recommended

The shared metadata wrapper adds intermediate work to fetchall, Arrow, and execute paths, with the reported fetchall regression unresolved.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 Medium severity

Open (1)

Comment thread mssql_python/pybind/ddbc_bindings.cpp Outdated
Copilot AI review requested due to automatic review settings September 22, 2026 07:53

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

It changes the native fetch hot path and reports a significant fetchall() regression, requiring broader cross-platform performance validation.

Review effort: Lite
Findings: 1 Medium severity

Open (1)

Copilot AI review requested due to automatic review settings September 22, 2026 09:22
@jahnvi480 Jahnvi Thakkar (jahnvi480) changed the title PERF: Keep fetchmany column metadata native and call-local PERF: Reuse native column metadata across result-set fetches Sep 22, 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

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.

Review effort: Lite
Findings: 1 Medium severity

Open (1)
Resolved since last review (1)

Comment thread mssql_python/pybind/ddbc_bindings.cpp Outdated
@jahnvi480 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
@jahnvi480
Jahnvi Thakkar (jahnvi480) marked this pull request as ready for review September 22, 2026 11:53

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

Arrow multi-batch fetches bypass the cache, and focused generation/invalidation coverage is still needed.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 Low severity

Open (1)
Resolved since last review (1)

Comment thread mssql_python/pybind/result_metadata.hpp
Copilot AI review requested due to automatic review settings September 24, 2026 05:52

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 teardown deadlock risk, connection-operation scalability concern, and missing native failure/lifetime coverage remain unresolved.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity · 1 Low severity

Open (2)

Comment thread mssql_python/pybind/ddbc_bindings.cpp

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.

the cache direction looks sound, and I did not reproduce a runtime regression.
please retain focused cache-lifetime regression coverage and complete the current-head performance qualification before merging.
requesting changes.

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

The cursor-GC regression test retains cursor references through fetched rows and must detach scalar values before forcing garbage collection.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 Medium severity

Open (1)
Resolved since last review (2)

Comment thread tests/test_016_connection_invalidation_segfault.py
Copilot AI review requested due to automatic review settings September 24, 2026 07:24

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

Three unresolved moderate findings remain.

Review effort: Lite
Findings: None

Resolved since last review (1)

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

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 Arrow path still repeats metadata descriptions, and the cleanup test retains Row objects and cursors.

Review effort: Lite
Findings: None

Copilot AI review requested due to automatic review settings September 24, 2026 09:28

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

Required operation-count coverage is absent, and the cleanup test no longer exercises cursor collection after connection closure.

Review effort: Lite
Findings: 1 Medium severity

Open (1)

Comment thread mssql_python/pybind/ddbc_bindings.cpp
@jahnvi480
Jahnvi Thakkar (jahnvi480) merged commit 9048f55 into main Sep 24, 2026
32 of 33 checks passed
Jahnvi Thakkar (jahnvi480) added a commit that referenced this pull request Sep 24, 2026
Preserve the existing fetch-buffer reuse changes and integrate the merged
metadata optimization plus current main without rewriting branch history.

Co-authored-by: Copilot App <[email protected]>
Jahnvi Thakkar (jahnvi480) added a commit that referenced this pull request Sep 24, 2026
Resolve the #796 metadata-cache integration while retaining immediate diagnostic capture, mixed-record filtering, and error propagation from #809. Forward the message sink through the new shared metadata-description helper and templated binding path.

Co-authored-by: Copilot App <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-size: medium Moderate update size

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants