Skip to content

PERF: Optimize checked temporal fetch construction - #795

Open
Jahnvi Thakkar (jahnvi480) wants to merge 4 commits into
mainfrom
jahnvi/perf-fetch-temporal-construction
Open

Jahnvi Thakkar (jahnvi480) wants to merge 4 commits into
mainfrom
jahnvi/perf-fetch-temporal-construction

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


Summary

Optimize the construction of Python DATE, TIME, and TIMESTAMP objects during fetching. The previous implementation returned correct temporal values but paid generic Python-call overhead for every non-NULL value. This change removes that intermediate work for standard datetime types while preserving the original route for substituted constructors.

1. Before: what was slow?

ODBC already provides the date/time fields as native numbers. The driver nevertheless invoked the cached Python constructor through pybind11 for every value:

PyTypeCache::get_date_class_obj()(year, month, day)

The constructor was already cached; repeated imports were not the problem. The remaining work was converting C++ numbers to Python arguments, arranging positional arguments, dispatching a generic callable, parsing those arguments, and managing their references. DATE uses three arguments, TIME four, and TIMESTAMP seven. Small integers may be reused, so not every argument requires a fresh allocation. The standard datetime constructor itself is implemented in C; the avoidable cost is its general-purpose calling route.

flowchart TD
    A["ODBC temporal structure"] --> B["Existing success and NULL checks"]
    B -->|"Non-NULL"| C["Native numeric fields"]
    C --> D["Cached constructor through pybind11"]
    D --> E["Convert fields into Python arguments"]
    E --> F["Generic callable dispatch and argument parsing"]
    F --> G["Validate fields and allocate temporal object"]
    G --> H["Existing result-row insertion"]
    B -->|"NULL"| N["Existing None handling"]
Loading

2. After: what changed?

The new fetch_temporal.hpp helper checks the identity of the actual cached constructor. For the exact standard Python type, it constructs the object directly from native integers using:

SQL conversion Direct CPython API Original fallback arguments
DATE PyDate_FromDate 3 positional
TIME PyTime_FromTime 4 positional
TIMESTAMP PyDateTime_FromDateAndTime 7 positional

If the cached constructor has been substituted, the original callable still runs with the original arguments, return-object handling, and propagated exceptions.

flowchart TD
    A["Same ODBC temporal structure"] --> B["Unchanged success and NULL checks"]
    B -->|"Non-NULL"| C["Same native fields and fraction / 1000"]
    C --> D["Ensure this translation unit's datetime API is initialized"]
    D --> E{"Cached constructor is the exact standard type?"}
    E -->|"Yes: fast path"| F["Pass native integers directly to CPython construction API"]
    F --> G["Validate fields and allocate temporal object"]
    G --> H["Check result and own the new reference"]
    E -->|"No: compatibility path"| I["Original cached callable with 3/4/7 positional arguments"]
    H --> J["Existing result-row insertion"]
    I --> J
    B -->|"NULL"| N["Unchanged None handling"]
Loading

Why this is faster: standard-type conversion no longer packages native fields as Python call arguments or performs generic callable dispatch/argument parsing. Field validation and the final Python object allocation still happen. A small saving per value repeats thousands of times; bulk fetching amortizes other per-call overhead, whereas repeated fetchone still pays that surrounding overhead for every row.

For example, the frozen 4,000-row x 8-column DATE workload, with every seventh row NULL, contains 27,432 non-NULL DATE values. That is a calculated workload count, not a native instrumentation result. Each value still produces one Python object; only its construction route changes. There is no claim of fewer SQL executions, ODBC fetches, or network round trips.

3. Files changed and behavior preserved

File Change
mssql_python/pybind/fetch_temporal.hpp Checked direct construction, exact cached-type identity checks, and original-constructor fallback. Helpers are translation-unit-local so they safely initialize and use that translation unit's datetime API pointer.
mssql_python/pybind/ddbc_bindings.cpp Replace only six construction sites: DATE/TIME/TIMESTAMP in row-wise SQLGetData conversion and batch-buffer conversion.
tests/test_038_fetch_temporal.py Fresh-process coverage of values, exact types, constructor arguments, exceptions, recovery, and teardown across fetch APIs and both conversion paths.
CHANGELOG.md Document the optimization and preserved behavior.

Existing GIL scopes, SQL/NULL checks, buffer layouts, fraction/1000 nanosecond-to-microsecond truncation, naive timezone/fold behavior, and result positioning are unchanged. steal()/py::object own new references; batch insertion releases each exactly once. Construction failures propagate rather than returning an invalid object. DATETIMEOFFSET, UUID, Decimal, text, Arrow, Row construction, and settings/converter caches are unchanged.

4. Measured improvement: actual baseline versus this change

Platform/build: Windows x64; CPython 3.13.15; pybind11 3.0.1; SQL Server 15.0.4382.1; ODBC 18.6.2.1. Both builds use matching MSVC Release x64/C++17 settings, /O2 /Ob2 /DNDEBUG, and ENABLE_PROFILING=OFF.

Revisions: pristine baseline e279a4f6c12241603b978602acf01e64520a819f; measured temporal-only candidate subsequently committed unchanged as f8e455677f64eb558a9ecc6c0ff3a8721cbaca9e. These measurements are not from a combined optimization branch.

Method: 12 counterbalanced paired rounds, nine samples per case per round, one untimed warmup per case, and fresh worker processes. Every result's values, types, and row count are checked outside timing. All ordered raw samples are retained, without outlier removal. One predeclared primary confirmation followed noisy calibration. The table reports complete fetch/drain time excluding execute and validation; repeated fetchone includes the caller's collection loop. Positive reduction means less time.

Every listed workload returns 4,000 rows, with every seventh row NULL. Mixed temporal has four columns: DATE, TIME(7), DATETIME2(7), and unchanged DATETIMEOFFSET. Pure-type workloads have eight columns. Requested rows/call is a public API setting, not a claim about internal native rowset size.

Study / workload API Rows x columns Requested rows/call Before ms After ms Time reduction Paired 95% reduction interval
Confirmation: DATE fetchone 4,000 x 8 1 44.977 38.488 14.43% 6.20% to 24.87%
Confirmation: DATE fetchmany 4,000 x 8 1,000 12.935 9.732 24.76% 17.14% to 31.12%
Confirmation: DATE fetchall 4,000 x 8 All remaining 11.675 8.961 23.25% -3.30% to 29.43%
Confirmation: mixed temporal fetchone 4,000 x 4 1 42.111 37.962 9.85% 3.51% to 12.96%
Confirmation: mixed temporal fetchmany 4,000 x 4 1,000 17.298 14.663 15.23% 6.25% to 18.81%
Confirmation: mixed temporal fetchall 4,000 x 4 All remaining 17.423 13.622 21.82% 9.04% to 26.59%
Supplemental: pure TIME(7) fetchmany 4,000 x 8 1,000 14.552 11.596 20.31% 9.51% to 40.63%
Supplemental: pure TIME(7) fetchall 4,000 x 8 All remaining 14.520 11.589 20.19% -7.99% to 30.83%
Supplemental: pure DATETIME2(7) fetchmany 4,000 x 8 1,000 17.784 12.033 32.34% 19.60% to 42.92%
Supplemental: pure DATETIME2(7) fetchall 4,000 x 8 All remaining 16.944 12.789 24.52% 2.39% to 34.99%

Point reductions are ratios of medians. Intervals use paired per-round median reductions with 5,000 bootstrap resamples and seed 558, a distinct estimator. Supplemental cases use the same builds and 12x9 protocol but are separate measurements, not another primary confirmation. Their percentages are not added to the primary results. DATE fetchall and pure TIME fetchall intervals include zero: those point gains remain inconclusive.

Across the confirmation's six temporal cases and nine unchanged narrow/converter controls:

Window Sum of 15 baseline case medians Sum of 15 candidate case medians Reduction
Fetch/drain 531.062 ms 506.612 ms 4.60%
Execute plus drain 543.121 ms 518.142 ms 4.60%

These are fixed-suite sums, not one query's latency or an application-wide speedup. First-result windows are retained in the study artifacts; no first-result improvement is claimed here.

Measured constructor counts: fresh-process correctness tests assert the same fallback behavior on both builds. Each main control query makes three substituted DATE calls with three positional arguments, three TIME calls with four, and three TIMESTAMP calls with seven. Unchanged DTO makes three eight-argument calls; UUID makes three bytes-keyword calls. NULLs invoke none. These are measured fallback-parity counts, not an instrumented count of standard-path generic-call elimination. Timing builds contain no instrumentation.

Uncertainty and negative controls: identical-build A/A calibration was very noisy, with a paired score interval of 0.828x to 1.951x. Unchanged confirmation controls include negative point reductions: narrow fetchone -1.19%, many(1) -1.14%, fetchall -2.79%, converter many -4.63%, and converter all -2.39%; their intervals span zero. No further timing retries were used to select a favorable outcome. The results support scoped temporal gains, but do not establish a blanket no-regression guarantee, universal speedup, or performance improvement on other operating systems.

Local validation

  • Pristine baseline and candidate both pass six default/custom/throwing constructor modes, with 24 configurations per mode. Coverage includes DATE boundaries 0001/9999/leap day, TIME(7)/DATETIME2(7) truncation, datetime/smalldatetime boundaries, NULLs, exact types, timezone/fold, and unchanged scalar controls.
  • Bounded and MAX-forced row-wise results are exercised through fetchone, fetchmany, fetchall, and iteration. Throwing modes preserve the exact exception instance and verify cursor recovery 16 times per mode; process teardown succeeds.
  • 40 selected existing temporal/fetch regression tests pass on each build. Black checks pass for 91 Python files; git diff whitespace checks pass.
  • These local results do not claim full-suite, other-platform, sanitizer, or allocation-failure-injection success. The Azure DevOps pipeline defines Windows/macOS/Linux validation and Linux ARM64 jobs. Cross-platform correctness requires review of actual jobs and test execution; this description update does not certify their outcomes.

Retained validation limitations

  • The original shared scalar probe passed its first 12 controls, then failed an unrelated baseline leading-BOM assertion. This PR does not include the independent BOM fix; scoped temporal tests retain scalar controls without that unrelated requirement.
  • An initial existing-test selection omitted the table-setup tests and produced eight missing-table failures; including the required setup gives 40 passes on both builds.
  • Both native builds retained the pre-existing LNK4044 warning. No unrelated build-system changes are included.

Use direct CPython date/time/datetime construction for exact cached standard types, preserving substituted constructors and exception behavior. Cover row-wise and batch fetch contracts in isolated subprocesses.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings September 17, 2026 14:26
@jahnvi480 Jahnvi Thakkar (jahnvi480) changed the title REFACTOR: Optimize checked temporal fetch construction PERF: Optimize checked temporal fetch construction Sep 17, 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.

🔵 Needs a closer look

Native extension changes span multiple fetch paths, with limited platform and validation coverage.

Pull request overview

Optimizes native SQL temporal fetch construction while preserving custom constructors and conversion behavior.

Changes:

  • Adds checked datetime construction helpers.
  • Integrates them into six temporal fetch paths.
  • Adds regression tests and changelog documentation.
File summaries
File Reviewed changes
tests/test_038_fetch_temporal.py Temporal parity, constructor, exception, and fetch API coverage
mssql_python/pybind/fetch_temporal.hpp Translation-unit-local checked construction helpers
mssql_python/pybind/ddbc_bindings.cpp Integration into row-wise and batch temporal fetch paths
CHANGELOG.md Documents the optimization and preserved behavior
Review details
  • Files reviewed: 4/4 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.

@github-actions github-actions Bot added the pr-size: medium Moderate update size label Sep 17, 2026
@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown

📊 Code Coverage Report

🔥 Diff Coverage

90%


🎯 Overall Coverage

83%


📈 Total Lines Covered: 8493 out of 10129
📁 Project: mssql-python


Diff Coverage

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

  • mssql_python/pybind/ddbc_bindings.cpp (84.6%): Missing lines 4450-4451
  • mssql_python/pybind/fetch_temporal.hpp (92.1%): Missing lines 17-19

Summary

  • Total: 51 lines
  • Missing: 5 lines
  • Coverage: 90%

mssql_python/pybind/ddbc_bindings.cpp

Lines 4446-4455

  4446                 }
  4447                 case SQL_SS_TIME2: {
  4448                     const SQL_SS_TIME2_STRUCT& t2 = buffers.timeBuffers[col - 1][i];
  4449                     py::object timeObj =
! 4450                         FetchTemporal::time(t2.hour, t2.minute, t2.second, t2.fraction / 1000);
! 4451                     PyList_SET_ITEM(row, col - 1, timeObj.release().ptr());
  4452                     break;
  4453                 }
  4454                 case SQL_SS_TIMESTAMPOFFSET: {
  4455                     SQLULEN rowIdx = i;

mssql_python/pybind/fetch_temporal.hpp

Lines 13-23

  13 
  14 // datetime.h keeps PyDateTimeAPI per translation unit, so these helpers must too.
  15 static inline void ensure_datetime_api() {
  16     if (PyDateTimeAPI == nullptr) {
! 17         PyDateTime_IMPORT;
! 18         if (PyDateTimeAPI == nullptr) throw py::error_already_set();
! 19     }
  20 }
  21 
  22 static inline py::object date(int year, int month, int day) {
  23     ensure_datetime_api();


📋 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.row.py: 77.6%
mssql_python.pybind.ddbc_bindings.cpp: 77.9%
mssql_python.pybind.connection.connection_pool.cpp: 81.8%
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 18, 2026 07:48
@github-actions

github-actions Bot commented Sep 18, 2026

Copy link
Copy Markdown

PR Performance Report

No consistent slowdowns detected. 2 inconsistent comparisons need review across 2 database tasks and 1 environments.

Inconsistent slowdowns to review:

Environment Affected task Before After Change
Windows / SQL Server 2025 Legacy 100,000-row insertion 645.291 ms 719.292 ms +60.2%
Windows / SQL Server 2025 Joined aggregation queries 226.202 ms 293.257 ms +31.2%

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

Environment Status
Windows / SQL Server 2022 Completed
Windows / SQL Server 2025 Completed
Unix / SQL Server 2022 Completed
Unix / SQL Server 2025 Completed
Affected phases and call counts

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

Windows / SQL Server 2025

Legacy 100,000-row insertion: py::execute::cpp_call +71.059 ms; ddbc::SQLExecute_wrap +70.540 ms; ddbc::BindParameters +34.841 ms.
Joined aggregation queries: py::execute::cpp_call +66.609 ms; ddbc::SQLExecDirect_wrap +66.605 ms; ddbc::FetchBatchData +0.202 ms.

All database tasks and timings

Windows / SQL Server 2022

Database task Before After Paired change Result
Connection opening 19.808 ms 21.674 ms +8.0% no signal
SELECT queries 1.918 ms 1.440 ms -8.1% no signal
Row insertion 30.720 ms 29.752 ms -2.1% no signal
Executemany inserts 242.062 ms 220.158 ms -0.2% no signal
Fetch-all queries 271.913 ms 234.331 ms -13.8% no signal
Row-by-row fetching 35.193 ms 29.882 ms -14.3% no signal
Batched row fetching 262.270 ms 246.898 ms -9.9% no signal
Transaction commit and rollback 93.090 ms 92.870 ms -0.2% no signal
Arrow row fetching 187.846 ms 167.906 ms -6.9% no signal
100,000-row insertion 791.033 ms 810.484 ms +2.1% no signal
Row fetching in batches of 100 331.481 ms 274.366 ms -13.7% no signal
Row fetching in batches of 10,000 273.708 ms 265.930 ms -12.1% no signal
Repeated positional queries 29.463 ms 29.967 ms +1.7% no signal
Repeated named-parameter queries 32.502 ms 32.706 ms +0.7% no signal
Legacy 100,000-row insertion 508.695 ms 514.878 ms -0.7% no signal
Insertion with explicit input sizes 3136.263 ms 3226.530 ms +2.5% no signal
Joined aggregation queries 197.194 ms 197.372 ms +1.3% no signal
Large joined-result fetching 306.051 ms 277.161 ms -14.1% no signal
1.2-million-row fetching 7382.461 ms 8202.514 ms +1.6% no signal
Common table expression queries 5.410 ms 5.427 ms -3.8% no signal

Windows / SQL Server 2025

Database task Before After Paired change Result
Connection opening 186.561 ms 233.513 ms +7.3% no signal
SELECT queries 2.043 ms 2.506 ms +22.7% no signal
Row insertion 31.647 ms 31.998 ms -2.0% no signal
Executemany inserts 280.124 ms 277.489 ms -0.2% no signal
Fetch-all queries 360.889 ms 330.646 ms -16.6% no signal
Row-by-row fetching 46.435 ms 42.225 ms -13.8% no signal
Batched row fetching 429.568 ms 317.624 ms -9.6% no signal
Transaction commit and rollback 91.669 ms 97.288 ms -0.3% no signal
Arrow row fetching 204.189 ms 213.677 ms +14.9% no signal
100,000-row insertion 862.116 ms 740.134 ms -2.6% no signal
Row fetching in batches of 100 455.996 ms 335.861 ms -11.0% no signal
Row fetching in batches of 10,000 379.196 ms 397.346 ms -9.6% no signal
Repeated positional queries 30.096 ms 30.965 ms +0.5% no signal
Repeated named-parameter queries 34.385 ms 34.344 ms -0.7% no signal
Legacy 100,000-row insertion 645.291 ms 719.292 ms +60.2% inconsistent slowdown
Insertion with explicit input sizes 4521.210 ms 5593.396 ms +1.2% no signal
Joined aggregation queries 226.202 ms 293.257 ms +31.2% inconsistent slowdown
Large joined-result fetching 482.972 ms 395.279 ms -18.2% no signal
1.2-million-row fetching 10832.084 ms 8356.509 ms +1.3% no signal
Common table expression queries 7.391 ms 5.266 ms -13.4% no signal

Unix / SQL Server 2022

Database task Before After Paired change Result
Connection opening 10.641 ms 10.715 ms +1.1% no signal
SELECT queries 1.175 ms 1.116 ms -6.1% no signal
Row insertion 34.384 ms 34.303 ms +0.2% no signal
Executemany inserts 156.901 ms 157.221 ms -1.0% no signal
Fetch-all queries 171.661 ms 148.223 ms -13.3% no signal
Row-by-row fetching 61.272 ms 58.950 ms -3.8% no signal
Batched row fetching 180.756 ms 142.133 ms -21.8% no signal
Transaction commit and rollback 113.889 ms 115.014 ms +1.0% no signal
Arrow row fetching 95.597 ms 94.938 ms -0.8% no signal
100,000-row insertion 443.536 ms 447.536 ms +2.2% no signal
Row fetching in batches of 100 225.411 ms 198.782 ms -13.1% no signal
Row fetching in batches of 10,000 176.839 ms 161.817 ms -8.5% no signal
Repeated positional queries 42.064 ms 41.997 ms -0.4% no signal
Repeated named-parameter queries 45.238 ms 44.861 ms -1.0% no signal
Legacy 100,000-row insertion 352.269 ms 352.587 ms +0.1% no signal
Insertion with explicit input sizes 2417.525 ms 2346.654 ms -2.1% no signal
Joined aggregation queries 177.658 ms 177.166 ms -0.3% no signal
Large joined-result fetching 207.830 ms 196.186 ms -6.6% no signal
1.2-million-row fetching 4968.175 ms 4999.420 ms +0.6% no signal
Common table expression queries 5.399 ms 5.348 ms -0.2% no signal

Unix / SQL Server 2025

Database task Before After Paired change Result
Connection opening 96.400 ms 97.383 ms +1.2% no signal
SELECT queries 1.126 ms 1.117 ms -0.8% no signal
Row insertion 33.982 ms 33.725 ms -0.6% no signal
Executemany inserts 164.274 ms 154.149 ms +1.0% no signal
Fetch-all queries 166.563 ms 151.019 ms -9.9% no signal
Row-by-row fetching 61.433 ms 58.266 ms -3.0% no signal
Batched row fetching 159.853 ms 143.379 ms -10.6% no signal
Transaction commit and rollback 113.082 ms 113.700 ms -0.2% no signal
Arrow row fetching 92.617 ms 94.817 ms +2.7% no signal
100,000-row insertion 467.399 ms 456.315 ms +3.6% no signal
Row fetching in batches of 100 218.278 ms 198.597 ms -9.0% no signal
Row fetching in batches of 10,000 171.721 ms 163.357 ms -4.4% no signal
Repeated positional queries 41.224 ms 41.304 ms +0.2% no signal
Repeated named-parameter queries 43.799 ms 43.610 ms -0.3% no signal
Legacy 100,000-row insertion 345.537 ms 347.330 ms +0.1% no signal
Insertion with explicit input sizes 2384.710 ms 2353.538 ms +0.4% no signal
Joined aggregation queries 171.719 ms 168.659 ms +0.3% no signal
Large joined-result fetching 209.949 ms 192.764 ms -8.1% no signal
1.2-million-row fetching 4942.560 ms 4984.847 ms +0.8% no signal
Common table expression queries 5.112 ms 5.113 ms +0.3% no signal
Build, commits and measurement details

ADO build 176453

PR head: a5fe9a49f71f4e0ddec287220fc61d366390f1ab
Base: d8b11f88c04bc2e7dfba334edb6d15034da6638e
Measured merge: 776a934aaa3726d3c711e37935b7709310daa7c5

  • Windows / SQL Server 2022: Python 3.13.15, amd64, SQL 16.0.1000.6; 5 paired comparisons and 1 warmup.
  • Windows / SQL Server 2025: Python 3.14.7, amd64, SQL 17.0.1000.7; 5 paired comparisons and 1 warmup.
  • 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 slowdown requires more than 20% median paired slowdown, at least 1 ms between the median runtimes, and at least 80% of pairs exceeding the relative threshold. An inconsistent slowdown crosses the first two thresholds without enough pair agreement.

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.

🔵 Needs a closer look

Native C++/CPython changes and stated cross-platform validation limitations warrant final human review.

Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Copilot AI review requested due to automatic review settings September 18, 2026 09:21

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 isolated Linux wheel-validation job fails on a source-tree-relative module path assertion.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Lite

sys.path.insert(0, str(root))
import mssql_python

assert Path(mssql_python.ddbc_bindings.module.__file__).resolve().is_relative_to(root)
Copilot AI review requested due to automatic review settings September 18, 2026 10:32

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 fetch-path and temporal-construction changes warrant final human review.

Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

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.

3 participants