Skip to content

PERF: Optimize fetchone, fetchmany(1) and fetchval paths - #829

Draft
Jahnvi Thakkar (jahnvi480) wants to merge 18 commits into
mainfrom
jahnvi/candidate-a-fetchone-column-count
Draft

Jahnvi Thakkar (jahnvi480) wants to merge 18 commits into
mainfrom
jahnvi/candidate-a-fetchone-column-count

Conversation

@jahnvi480

@jahnvi480 Jahnvi Thakkar (jahnvi480) commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Work Item / Issue Reference

AB#48364

Not applicable; the work item above is the single reference.


Summary

  • Consolidate fetchone(), fetchmany(1) and inherited fetchval()/iterator optimization work in this existing PR.
  • Retain generation-scoped full column-count caching, separate from prefix SQLGetData metadata. Direct column-count calls remain uncached; all nine original count-cache regressions remain.
  • Share native single-row fetching and reuse successful unbinds only within a valid generation. Invalidate before binding, including Arrow/partial binds, and during cleanup. The marker does not certify row-array attributes.
  • Route only all-numeric fetchmany(1) results through SQLFetchScroll and SQLGetData, preserving eager count/name validation and row-array configuration/cleanup. Mixed INT/NVARCHAR and other types retain their existing native paths.
  • Use direct one-row wrapping only for exact built-in integer size 1, one returned row, canonical Row/factory, and no converter/UUID work. Preserve larger-request tails, substituted factories, integer subclasses and actual fetchone() overrides.
  • Keep full-row construction and all-column converter callbacks for fetchval(). Replace Python single-row phase context managers with equivalent paired start/stop instrumentation.
  • Add numeric parity, EOF, override/factory, diagnostics, generation-change, mixed-API and fault-recovery regressions, plus attribution documentation.

Qualification status: source review accepted; source-only controls passed (17 methods, 144 fake-boundary variants and eight 10,000-row operation-count controls). These use fake native boundaries/textual checks and do not establish native correctness or latency.

Formatting: python -m pre_commit run black-check --all-files --hook-stage pre-push passed, using isolated pre-commit 4.5.1 and the repository-pinned Black 26.5.1 hook. Functional isolated commit/push hooks ran the same full check successfully. Black removed one extra blank line in the new test file; all 17 source controls passed again after formatting. git diff --cached --check also passed before commit.

Native qualification and performance testing remain pending. No combined native build/import, live SQL test or benchmark has run locally. Earlier original-head CI/profiler results do not qualify this combined change. No speedup is claimed. Fresh-main, original-PR, combined and pyodbc comparisons require a separately reviewed runtime plan; normal CI for this update is expected to provide broader regression coverage.

Normal CI update: ADO build 180509 on 4c43081a built Ubuntu SQL2025 Release with profiling enabled, then reported 11 failed, 5,696 passed, 132 skipped and 42 deselected. All nine original count-cache cases and both new generation-change cases passed. Ten failures used unprefixed native profiler keys; the partial-bind case incorrectly excluded the existing RuntimeError contract. Commit ac6e9880 corrects only those test expectations and counter-name documentation; production source is unchanged. All 19 source-only controls and the pinned full Black check passed before pushing the correction. A fresh native CI run is pending; the failed run is not presented as successful qualification.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 1, 2026 09:00
@github-actions github-actions Bot added the pr-size: medium Moderate update size label Oct 1, 2026
@github-actions

github-actions Bot commented Oct 1, 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

Row-by-row fetching: ddbc::SQLDescribeCol::driver_call +0.000 ms. Call changes: ddbc::FetchSingleRow::SQL_UNBIND (added, removed, or intermittent); ddbc::SQLNumResultCols_wrap (1000 -> 1 calls).
Repeated positional queries: py::execute::post_execute +0.043 ms; ddbc::FetchOne_wrap +0.021 ms; py::fetchone::cpp_call +0.021 ms. Call changes: ddbc::FetchSingleRow::SQL_UNBIND (added, removed, or intermittent).
Repeated named-parameter queries: ddbc::SQLExecute_wrap +0.543 ms; py::execute::cpp_call +0.516 ms; py::execute::param_prep +0.065 ms. Call changes: ddbc::FetchSingleRow::SQL_UNBIND (added, removed, or intermittent).
10,000 scalar values / fetchval() (debug disabled): no measured phase delta. Call changes: ddbc::FetchSingleRow::SQL_UNBIND (added, removed, or intermittent); ddbc::SQLNumResultCols_wrap (10000 -> 1 calls).

Unix / SQL Server 2025

Row-by-row fetching: ddbc::SQLGetData_wrap +0.065 ms; ddbc::AppendDiagRecords::SQLGetDiagRec_call +0.002 ms. Call changes: ddbc::FetchSingleRow::SQL_UNBIND (added, removed, or intermittent); ddbc::SQLNumResultCols_wrap (1000 -> 1 calls).
Repeated positional queries: py::fetchone::cpp_call +0.010 ms; py::execute::diag_records +0.010 ms; ddbc::FetchOne_wrap +0.005 ms. Call changes: ddbc::FetchSingleRow::SQL_UNBIND (added, removed, or intermittent).
Repeated named-parameter queries: py::fetchone::cpp_call +0.070 ms; ddbc::FetchOne_wrap +0.059 ms; ddbc::SQLGetData_wrap +0.011 ms. Call changes: ddbc::FetchSingleRow::SQL_UNBIND (added, removed, or intermittent).
10,000 scalar values / fetchval() (debug disabled): no measured phase delta. Call changes: ddbc::FetchSingleRow::SQL_UNBIND (added, removed, or intermittent); ddbc::SQLNumResultCols_wrap (10000 -> 1 calls).

All database tasks and timings

Unix / SQL Server 2022

Database task Before After Paired change Result
Connection opening 10.370 ms 10.300 ms -1.1% no signal
SELECT queries 1.105 ms 1.056 ms -4.4% no signal
Row insertion 34.534 ms 34.739 ms +0.3% no signal
Executemany inserts 154.482 ms 157.765 ms +1.9% no signal
Fetch-all queries 120.876 ms 123.121 ms +0.8% no signal
Row-by-row fetching 14.384 ms 13.153 ms -8.1% no signal
Batched row fetching 117.861 ms 119.168 ms +1.1% no signal
Transaction commit and rollback 114.492 ms 114.194 ms +0.2% no signal
Arrow row fetching 94.619 ms 94.322 ms -0.8% no signal
100,000-row insertion 448.097 ms 455.117 ms +0.8% no signal
Row fetching in batches of 100 121.983 ms 122.549 ms -0.1% no signal
Row fetching in batches of 10,000 125.832 ms 127.482 ms -1.3% no signal
Repeated positional queries 33.811 ms 33.326 ms -0.1% no signal
Repeated named-parameter queries 35.438 ms 35.809 ms +1.0% no signal
Legacy 100,000-row insertion 355.983 ms 354.557 ms +0.0% no signal
Insertion with explicit input sizes 492.867 ms 494.791 ms +0.1% no signal
Joined aggregation queries 181.108 ms 179.600 ms +0.4% no signal
Large joined-result fetching 177.876 ms 180.132 ms -0.9% no signal
1.2-million-row fetching 3465.164 ms 3442.376 ms -0.7% no signal
Common table expression queries 5.271 ms 5.319 ms +0.5% no signal
256 KiB VARCHAR(MAX) / fetchall() 1.262 ms 1.263 ms +2.5% no signal
10,000 scalar values / fetchval() (debug disabled) 107.386 ms 95.550 ms -11.3% no signal

Unix / SQL Server 2025

Database task Before After Paired change Result
Connection opening 97.547 ms 97.695 ms -0.0% no signal
SELECT queries 1.087 ms 1.143 ms +1.4% no signal
Row insertion 34.367 ms 35.083 ms -0.5% no signal
Executemany inserts 152.500 ms 151.927 ms -0.4% no signal
Fetch-all queries 123.522 ms 122.945 ms -0.7% no signal
Row-by-row fetching 14.585 ms 13.411 ms -8.1% no signal
Batched row fetching 120.523 ms 120.261 ms -0.2% no signal
Transaction commit and rollback 117.592 ms 114.824 ms -0.9% no signal
Arrow row fetching 98.403 ms 95.310 ms -3.8% no signal
100,000-row insertion 486.000 ms 436.989 ms -8.0% no signal
Row fetching in batches of 100 125.038 ms 121.917 ms -3.1% no signal
Row fetching in batches of 10,000 140.963 ms 128.784 ms -8.2% no signal
Repeated positional queries 33.903 ms 33.717 ms -0.5% no signal
Repeated named-parameter queries 36.029 ms 35.847 ms -0.7% no signal
Legacy 100,000-row insertion 361.185 ms 355.299 ms -2.1% no signal
Insertion with explicit input sizes 487.790 ms 499.970 ms +3.1% no signal
Joined aggregation queries 160.958 ms 162.247 ms +0.8% no signal
Large joined-result fetching 188.416 ms 192.536 ms +5.0% no signal
1.2-million-row fetching 3533.856 ms 3543.148 ms -1.2% no signal
Common table expression queries 5.343 ms 5.427 ms +2.5% no signal
256 KiB VARCHAR(MAX) / fetchall() 1.560 ms 1.492 ms -4.4% no signal
10,000 scalar values / fetchval() (debug disabled) 111.790 ms 97.092 ms -10.9% no signal
Build and measurement details

ADO build 181436

PR head: 1468a231315c98cec4289fd7bba52e450a087f80
Base: 666f3cb6d23981bb23cd182ec273df10a7b2c805
Measured merge: aef5658ddbd20aba09124734302b37f136029312

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

This headline uses the original 22-task profiling-enabled diagnostics; separate OFF/OFF latency and ON/OFF route measurements, when available, are retained in the raw artifacts and are not headline inputs.

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 native hot-path change still requires live correctness and performance validation, as acknowledged by the draft description.

Review effort: Balanced
Findings: None

What changed in this PR

Introduces an experimental native cache to avoid repeated column-count queries during fetchone.

Changes:

  • Caches full column counts by metadata generation.
  • Reuses cached counts while preserving invalidation and error handling.
  • Adds nine subprocess-isolated integration scenarios.
File Description
mssql_python/​pybind/​ddbc_bindings.cpp Uses the cached count in FetchOne_wrap.
mssql_python/​pybind/​result_metadata.hpp Stores and invalidates full column counts.
tests/​test_fetch_settings_cache.py Tests reuse, invalidation, failures, and mixed fetching.

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

Use a separate connection for the cross-handle assertion without requiring MARS. Preserve all fetch and native call-count assertions.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 1, 2026 10:13

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 native hot-path change remains an explicitly unvalidated draft with correctness tests and performance measurements still pending.

Review effort: Balanced
Findings: None

Copilot AI balanced review requested due to automatic review settings October 1, 2026 10:16

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 native cache behavior and performance impact remain unvalidated by runtime execution.

Review effort: Balanced
Findings: None

@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

📊 Code Coverage Report

🔥 Diff Coverage

81%


🎯 Overall Coverage

84%


📈 Total Lines Covered: 9622 out of 11343
📁 Project: mssql-python


Diff Coverage

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

  • mssql_python/cursor.py (82.3%): Missing lines 2902,2920-2921,2933-2936,2960,3004,3042-3045,3086
  • mssql_python/pybind/ddbc_bindings.cpp (91.8%): Missing lines 3725-3727,3744-3746,3816-3818,3838,3892,5472
  • mssql_python/pybind/result_metadata.hpp (100%)
  • mssql_python/pybind/row_factory.hpp (52.5%): Missing lines 18,70-90,94-95,103-104,107-108

Summary

  • Total: 292 lines
  • Missing: 54 lines
  • Coverage: 81%

mssql_python/cursor.py

Lines 2898-2906

  2898                     self.messages,
  2899                 )
  2900             finally:
  2901                 if started:
! 2902                     perf_stop("py::fetchone::cpp_call", started)
  2903 
  2904             return self._finish_fetchone(ret, row_data)
  2905         except Exception:
  2906             # On error, don't increment rownumber - rethrow the error

Lines 2916-2925

  2916             return None
  2917 
  2918         # Update internal position after successful fetch
  2919         if self._skip_increment_for_next_fetch:
! 2920             self._skip_increment_for_next_fetch = False
! 2921             self._next_row_index += 1
  2922         else:
  2923             self._increment_rownumber()
  2924 
  2925         self.rowcount = self._next_row_index

Lines 2929-2940

  2929         started = perf_start()
  2930         try:
  2931             if not converter_map and not self._uuid_str_indices:
  2932                 if native and not started and _native_row_eligible(Row):
! 2933                     row_type = Row
! 2934                     factory = row_type._fast_create
! 2935                     column_names = self._cached_result_columns
! 2936                     return (
  2937                         _NATIVE_ROW_PLAN,
  2938                         row_type,
  2939                         column_map,
  2940                         self,

Lines 2956-2964

  2956                 column_names=self._cached_result_columns,
  2957             )
  2958         finally:
  2959             if started:
! 2960                 perf_stop("py::fetchone::row_wrap", started)
  2961 
  2962     def fetchmany(self, size: Optional[int] = None) -> List[Row]:
  2963         """
  2964         Fetch the next set of rows of a query result.

Lines 3000-3008

  3000                     self.messages,
  3001                 )
  3002             finally:
  3003                 if started:
! 3004                     perf_stop("py::fetchmany::cpp_call", started)
  3005 
  3006             return self._finish_fetchmany(ret, rows_data, size=size)
  3007         except Exception:
  3008             # On error, don't increment rownumber - rethrow the error

Lines 3038-3049

  3038                     and Row is _DEFAULT_ROW_TYPE
  3039                     and Row._fast_create is _DEFAULT_FAST_ROW_CREATE
  3040                 ):
  3041                     if native and not started and _native_row_eligible(Row):
! 3042                         row_type = Row
! 3043                         factory = row_type._fast_create
! 3044                         column_names = self._cached_result_columns
! 3045                         return (
  3046                             _NATIVE_ROW_PLAN,
  3047                             row_type,
  3048                             column_map,
  3049                             self,

Lines 3082-3090

  3082                 for row_data in rows_data
  3083             ]
  3084         finally:
  3085             if started:
! 3086                 perf_stop("py::fetchmany::row_wrap", started)
  3087 
  3088     def fetchall(self) -> List[Row]:
  3089         """
  3090         Fetch all (remaining) rows of a query result.

mssql_python/pybind/ddbc_bindings.cpp

Lines 3721-3731

  3721                                         PyErr_SetString(PyExc_ValueError, "embedded null character");
  3722                                         throw py::error_already_set();
  3723                                     }
  3724                                     decoded = steal(PyUnicode_Decode(
! 3725                                         reinterpret_cast<const char*>(dataBuffer.data()),
! 3726                                         static_cast<Py_ssize_t>(dataLen),
! 3727                                         decodeEncoding.c_str(), "strict"));
  3728                                     if (!decoded) throw py::error_already_set();
  3729                                     if (PyList_Append(row.ptr(), decoded.ptr()) < 0)
  3730                                         throw py::error_already_set();
  3731                                     LOG("SQLGetData: CHAR column %d decoded with '%s', %zu bytes "

Lines 3740-3750

  3740                                     // Preserve the existing codec-error bytes fallback.
  3741                                     decoded = steal(PyBytes_FromStringAndSize(
  3742                                         reinterpret_cast<const char*>(dataBuffer.data()),
  3743                                         static_cast<Py_ssize_t>(dataLen)));
! 3744                                     if (!decoded) throw py::error_already_set();
! 3745                                     if (PyList_Append(row.ptr(), decoded.ptr()) < 0)
! 3746                                         throw py::error_already_set();
  3747                                 }
  3748                             } else {
  3749                                 // Buffer too small, fallback to streaming
  3750                                 LOG("SQLGetData: CHAR column %d data truncated "

Lines 3812-3822

  3812                     SQLWCHAR* dataBuffer;
  3813                     if (bufferChars <= std::size(inlineBuffer)) {
  3814                         std::fill_n(inlineBuffer, bufferChars, SQLWCHAR{});
  3815                         dataBuffer = inlineBuffer;
! 3816                     } else {
! 3817                         heapBuffer.resize(bufferChars);
! 3818                         dataBuffer = heapBuffer.data();
  3819                     }
  3820                     SQLLEN dataLen;
  3821                     ret = SQLGetData_ptr(hStmt, i, SQL_C_WCHAR, dataBuffer, fetchBufferSize,
  3822                                          &dataLen);

Lines 3834-3842

  3834                                 // null termination. This preserves embedded NULs and avoids
  3835                                 // any risk of reading past the valid range if the driver
  3836                                 // omits the terminator.
  3837                                 row.append(FetchText::from_utf16_native(
! 3838                                     reinterpret_cast<const char*>(dataBuffer),
  3839                                     static_cast<Py_ssize_t>(numCharsInData * sizeof(SQLWCHAR))));
  3840                                 LOG("SQLGetData: Appended NVARCHAR string "
  3841                                     "length=%lu for column %d",
  3842                                     (unsigned long)numCharsInData, i);

Lines 3888-3896

  3888                 SQLLEN indicator = 0;
  3889                 ret = SQLGetData_ptr(hStmt, i, SQL_C_LONG, &intValue, 0, &indicator);
  3890                 CaptureFetchDiagnostics(hStmt, ret, messages);
  3891                 if (SQL_SUCCEEDED(ret) && indicator != SQL_NULL_DATA) {
! 3892                     AppendFetchedCell(row, PyLong_FromLong(intValue));
  3893                 } else {
  3894                     row.append(py::none());
  3895                 }
  3896                 break;

Lines 5468-5476

  5468     FetchStateGuard fetchStateGuard(StatementHandle, messages);
  5469 
  5470     if (!hasLobColumns && fetchSize > 0) {
  5471         ret = SQLBindColums(StatementHandle, buffers, columnNames, numCols, fetchSize, charCtype,
! 5472                             messages);
  5473         if (!SQL_SUCCEEDED(ret)) {
  5474             LOG("Error when binding columns");
  5475             return ret;
  5476         }

mssql_python/pybind/row_factory.hpp

Lines 14-22

  14     const py::handle& attr_cursor, const py::handle& attr_column_map_lower,
  15     const py::handle& attr_column_names,
  16     int (*set_attr)(PyObject*, PyObject*, PyObject*) = PyObject_GenericSetAttr) {
  17     if (!row)
! 18         throw py::error_already_set();
  19 
  20     if (set_attr(row.ptr(), attr_values.ptr(), row_data) < 0 ||
  21         set_attr(row.ptr(), attr_column_map.ptr(), column_map.ptr()) < 0 ||
  22         set_attr(row.ptr(), attr_cursor.ptr(), cursor_obj.ptr()) < 0 ||

Lines 66-99

  66 }
  67 
  68 // Passive final eligibility check: do not execute newly installed class descriptors.
  69 inline bool has_default_row_allocation(PyObject* row_class, const py::str& new_name,
! 70                                        const py::str& setattr_name) {
! 71     if (!PyUnicode_CheckExact(new_name.ptr()) || !PyUnicode_CheckExact(setattr_name.ptr())) {
! 72         throw py::type_error("Row allocation guard names must be exact strings");
! 73     }
! 74     if (!row_class || Py_TYPE(row_class) != &PyType_Type) {
! 75         return false;
! 76     }
! 77     auto* row_type = reinterpret_cast<PyTypeObject*>(row_class);
! 78     if (!row_type->tp_bases || !PyTuple_CheckExact(row_type->tp_bases) ||
! 79         PyTuple_GET_SIZE(row_type->tp_bases) != 1 ||
! 80         PyTuple_GET_ITEM(row_type->tp_bases, 0) != reinterpret_cast<PyObject*>(&PyBaseObject_Type) ||
! 81         !row_type->tp_dict) {
! 82         return false;
! 83     }
! 84     PyObject* names[] = {new_name.ptr(), setattr_name.ptr()};
! 85     for (PyObject* name : names) {
! 86         PyObject* member = PyDict_GetItemWithError(row_type->tp_dict, name);
! 87         if (member) {
! 88             return false;
! 89         }
! 90         if (PyErr_Occurred()) {
  91             throw py::error_already_set();
  92         }
  93     }
! 94     return true;
! 95 }
  96 
  97 // Attribute names are prepared once as binding defaults, not allocated for each row.
  98 inline py::object construct_row(const py::object& values, const py::type& row_class,
  99                                 const py::object& column_map, const py::object& cursor_obj,

Lines 99-112

   99                                 const py::object& column_map, const py::object& cursor_obj,
  100                                 const py::object& column_map_lower, const py::object& column_names,
  101                                 const py::tuple& attributes) {
  102     if (attributes.size() != 5) {
! 103         throw py::value_error("Row construction requires five attribute names");
! 104     }
  105     for (py::handle name : attributes) {
  106         if (!PyUnicode_Check(name.ptr())) {
! 107             throw py::type_error("Row attribute names must be strings");
! 108         }
  109     }
  110     const py::object new_method = row_class.attr("__new__");
  111     py::object row = steal(PyObject_CallOneArg(new_method.ptr(), row_class.ptr()));
  112     initialize_row(row, values.ptr(), column_map, cursor_obj, column_map_lower, column_names,


📋 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.row_factory.hpp: 62.3%
mssql_python.pybind.ddbc_bindings.h: 62.6%
mssql_python.pybind.ddbc_bindings.cpp: 79.9%
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.pybind.logger_bridge.hpp: 87.5%
mssql_python.pooling.py: 90.1%

🔗 Quick Links

⚙️ Build Summary 📋 Coverage Details

View Azure DevOps Build

Browse Full Coverage Report

Copilot AI balanced review requested due to automatic review settings October 5, 2026 06: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.

Copilot review overview

🔵 Needs a closer look

Native correctness tests and uninstrumented Release performance measurements remain unrun.

Review effort: Balanced
Findings: None

@jahnvi480
Jahnvi Thakkar (jahnvi480) marked this pull request as ready for review October 5, 2026 07:56
Copilot AI balanced review requested due to automatic review settings October 5, 2026 07:56

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 native hot-path changes and fault-injection tests remain unexecuted, with no Release-OFF performance result available.

Review effort: Balanced
Findings: None

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 5, 2026 10:54
@github-actions github-actions Bot added pr-size: large Substantial code update and removed pr-size: medium Moderate update size labels Oct 5, 2026
@jahnvi480 Jahnvi Thakkar (jahnvi480) changed the title PERF: Cache full column counts for fetchone PERF: Optimize fetchone, fetchmany(1) and fetchval paths Oct 5, 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

It substantially changes native fetch, caching, and Python-object ownership paths while fresh native CI and performance qualification remain pending.

Review effort: Balanced
Findings: None

Preserve bounded CI budgets and report unavailable modes explicitly.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 6, 2026 14:36

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 primary latency mode currently builds non-Release Unix binaries, making its performance verdict unreliable.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)

Comment on lines +110 to +115
def build(path, log, timeout=900, profiling=True):
env = dict(os.environ, ENABLE_PROFILING="1" if profiling else "0")
# build scripts find Python via PATH; keep the controller's interpreter.
env["PATH"] = str(Path(sys.executable).parent) + os.pathsep + env["PATH"]
command = ["cmd", "/c", "build.bat"] if os.name == "nt" else ["bash", "build.sh"]
run_process(command, log, timeout, cwd=path / "mssql_python/pybind", env=env)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 6, 2026 16:32
Comment thread eng/profiler_benchmarks/controller.py Fixed
Comment thread eng/profiler_benchmarks/controller.py Fixed
Comment thread eng/profiler_benchmarks/controller.py Fixed

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 primary latency comparison currently uses an unoptimized Unix build rather than the documented Release configuration.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)

Select the native bridge used by each public fetch route while retaining
all failure and recovery assertions. Mark three reviewed SHA256 source
fingerprints with narrow DevSkim rule-specific suppressions.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 7, 2026 02:40

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 primary latency verdict currently uses builds that are not explicitly configured as Release builds.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)

Restore the original 22-task diagnostic headline and report layout.
Keep separate OFF/OFF latency and ON/OFF route artifacts, validation,
thresholds and collection unchanged, with an explicit scope caveat.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 7, 2026 03:44

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 ODBC state, Python ownership, and CI orchestration changed substantially, while exact-head native qualification remains pending.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)

Keep logical buffer capacity, initialization and conversion unchanged,
with heap fallback for larger bounded values. Preserve existing tests
and append boundary, strict-error and isolated buffer-contract cases.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 7, 2026 06:10

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 latency benchmark currently builds unoptimized Unix binaries instead of the documented Release configuration.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)

Consolidate essential fetch regressions into existing suites and remove
PR-added benchmark-only and unified-coverage-skipped tests.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 8, 2026 05:55

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 numeric route can mask SQLGetData failures, and latency CI does not produce optimized Release builds.

3 open findings

🧠 Review effort: Balanced


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

Comment on lines +5097 to +5098
ret = FetchSingleRow(StatementHandle, row, charEncoding, wcharEncoding, charCtype,
messages, numCols, true);
temporary.replace(path)


def run_ci_report(args):
Use existing Python Row completion while retaining native fetch behavior.
Keep route provenance accurate and update existing regression expectations.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 8, 2026 08:12

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

Latency builds are not verified Release builds, route documentation conflicts with source, and new CI orchestration lacks automated coverage.

4 open findings

🧠 Review effort: Balanced


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

Comment thread profiler/README.md
Comment on lines +297 to +301
must record 1,001 native fetch calls. The version-1 default route contract requires
1,000 native Row constructions for `fetchmany(1)` and none for `fetchone` or
`fetchval` in the successor. Here `1` is a built-in integer for both numeric and
mixed shapes; this is not the numeric-column-only native fetching shortcut.
Legacy f539 requires 1,000 constructions for all three APIs; main666 requires none. This is route attribution, not production latency.
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.

3 participants