Skip to content

FIX: Use proper C++ tokenizer for LOG statement joining

322402e
Select commit
Loading
Failed to load commit list.
Open

CHORE: Exclude diagnostic LOG statements from coverage metrics #556

FIX: Use proper C++ tokenizer for LOG statement joining
322402e
Select commit
Loading
Failed to load commit list.
Azure Pipelines / MSSQL-Python-PR-Validation succeeded May 19, 2026 in 48m 59s

Build #pr-validation-pipeline had test failures

Details

Tests

  • Failed: 1 (0.00%)
  • Passed: 29,717 (96.68%)
  • Other: 1,018 (3.31%)
  • Total: 30,736
Code coverage

  • 6629 of 8235 lines covered (80.50%)

Annotations

Check failure on line 1 in test_query_does_not_block_other_python_threads

See this annotation in the file changed.

@azure-pipelines azure-pipelines / MSSQL-Python-PR-Validation

test_query_does_not_block_other_python_threads

AssertionError: Heartbeat thread was starved during cursor.execute(WAITFOR). Got 17 ticks, expected >= 20. This indicates the GIL was not released around the blocking ODBC call.
assert 17 >= 20
Raw output
conn_str = 'Server=tcp:127.0.0.1,1433;Database=master;Uid=SA;Pwd=Azure@123!;TrustServerCertificate=yes'

    def test_query_does_not_block_other_python_threads(conn_str):
        """
        While one thread executes a 2-second ``WAITFOR DELAY``, a second pure-Python
        thread must continue to run. If the GIL were held across SQLExecDirect, the
        heartbeat would not advance until the WAITFOR returned.
        """
        mssql_python.pooling(enabled=False)
    
        heartbeat_interval = 0.05  # 50ms ticks
        expected_min_ticks = int(WAITFOR_SECONDS / heartbeat_interval * 0.5)  # 50% of theoretical max
    
        stop_event = threading.Event()
        tick_count = [0]
        query_error = []
    
        def heartbeat():
            while not stop_event.is_set():
                tick_count[0] += 1
                time.sleep(heartbeat_interval)
    
        def run_query():
            try:
                _run_waitfor(conn_str)
            except Exception as exc:
                query_error.append(str(exc))
    
        hb = threading.Thread(target=heartbeat, daemon=True)
        qt = threading.Thread(target=run_query, daemon=True)
    
        # Snapshot ticks just before/after the query so we measure ticks that
        # happened *during* the blocking ODBC call, not before/after.
        hb.start()
        time.sleep(0.1)  # let heartbeat warm up
        ticks_before = tick_count[0]
    
        qt.start()
        qt.join(timeout=WAITFOR_SECONDS + 30)
        ticks_after = tick_count[0]
        stop_event.set()
        hb.join(timeout=5)
    
        assert not qt.is_alive(), "Query thread did not finish in time"
        assert not query_error, f"Query thread error: {query_error}"
    
        ticks_during = ticks_after - ticks_before
        print(
            f"\n[HEARTBEAT] ticks during {WAITFOR_SECONDS}s WAITFOR: {ticks_during} "
            f"(expected >= {expected_min_ticks})"
        )
    
>       assert ticks_during >= expected_min_ticks, (
            f"Heartbeat thread was starved during cursor.execute(WAITFOR). "
            f"Got {ticks_during} ticks, expected >= {expected_min_ticks}. "
            f"This indicates the GIL was not released around the blocking ODBC call."
        )
E       AssertionError: Heartbeat thread was starved during cursor.execute(WAITFOR). Got 17 ticks, expected >= 20. This indicates the GIL was not released around the blocking ODBC call.
E       assert 17 >= 20

tests/test_022_concurrent_query_gil_release.py:123: AssertionError