Skip to content

Merge branch 'main' into bewithgaurav/gh534-bulkcopy-msi

7ad716d
Select commit
Loading
Failed to load commit list.
Merged

FEAT: Add ActiveDirectoryMSI support for bulk copy #573

Merge branch 'main' into bewithgaurav/gh534-bulkcopy-msi
7ad716d
Select commit
Loading
Failed to load commit list.
Azure Pipelines / MSSQL-Python-PR-Validation succeeded May 20, 2026 in 57m 15s

Build #pr-validation-pipeline had test failures

Details

Tests

  • Failed: 1 (0.00%)
  • Passed: 31,735 (96.71%)
  • Other: 1,078 (3.29%)
  • Total: 32,814
Code coverage

  • 7024 of 27138 lines covered (25.88%)

Annotations

Check failure on line 1 in test_aggressive_dbc_segfault_reproduction

See this annotation in the file changed.

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

test_aggressive_dbc_segfault_reproduction

subprocess.TimeoutExpired: Command '['/Library/Frameworks/Python.framework/Versions/3.13/bin/python', '-c', '\nimport sys\nimport gc\nfrom mssql_python import connect\n\nprint("=== AGGRESSIVE DBC SEGFAULT TEST ===")\nprint("Creating many DBC handles and forcing shutdown...")\n\n# Create many connections without closing them\n# This maximizes the chance of DBC handles being finalized\n# AFTER the static ENV handle has destructed\nconnections = []\nfor i in range(5):  # Reduced for faster execution\n    conn = connect("Server=tcp:127.0.0.1,1433;Database=master;Uid=SA;Pwd=Azure@123!;TrustServerCertificate=yes")\n    # Don\'t even create cursors - just DBC handles\n    connections.append(conn)\n\nprint(f"Created {len(connections)} DBC handles")\nprint("Forcing GC to ensure objects are tracked...")\ngc.collect()\n\n# Delete the list but objects are still alive in GC\ndel connections\n\nprint("WARNING: About to exit with unclosed DBC handles")\nprint("If Type 2 (DBC) handles are not protected, this may SEGFAULT")\nprint("Stack trace will show: SQLFreeHandle -> SqlHandle::free() -> finalize_garbage")\n\n# Force immediate exit - this triggers finalize_garbage\nsys.exit(0)\n']' timed out after 5 seconds
Raw output
self = <test_013_SqlHandle_free_shutdown.TestHandleFreeShutdown object at 0x10dfba850>
conn_str = 'Server=tcp:127.0.0.1,1433;Database=master;Uid=SA;Pwd=Azure@123!;TrustServerCertificate=yes'

    def test_aggressive_dbc_segfault_reproduction(self, conn_str):
        """
        AGGRESSIVE TEST: Try to reproduce DBC handle segfault during shutdown.
    
        This test aggressively attempts to trigger the segfault described in the stack trace
        by creating many DBC handles and forcing Python to shut down while they're still alive.
    
        Current vulnerability: DBC handles (Type 2) are NOT protected during shutdown,
        so they will call SQLFreeHandle during finalization, potentially accessing
        the already-destructed static ENV handle.
    
        Expected with CURRENT CODE: May segfault (this is the bug we're testing for)
        Expected with FIXED CODE: No segfault
        """
        script = textwrap.dedent(f"""
            import sys
            import gc
            from mssql_python import connect
    
            print("=== AGGRESSIVE DBC SEGFAULT TEST ===")
            print("Creating many DBC handles and forcing shutdown...")
    
            # Create many connections without closing them
            # This maximizes the chance of DBC handles being finalized
            # AFTER the static ENV handle has destructed
            connections = []
            for i in range(5):  # Reduced for faster execution
                conn = connect("{conn_str}")
                # Don't even create cursors - just DBC handles
                connections.append(conn)
    
            print(f"Created {{len(connections)}} DBC handles")
            print("Forcing GC to ensure objects are tracked...")
            gc.collect()
    
            # Delete the list but objects are still alive in GC
            del connections
    
            print("WARNING: About to exit with unclosed DBC handles")
            print("If Type 2 (DBC) handles are not protected, this may SEGFAULT")
            print("Stack trace will show: SQLFreeHandle -> SqlHandle::free() -> finalize_garbage")
    
            # Force immediate exit - this triggers finalize_garbage
            sys.exit(0)
        """)
    
>       result = subprocess.run(
            [sys.executable, "-c", script], capture_output=True, text=True, timeout=5
        )

tests/test_013_SqlHandle_free_shutdown.py:87: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/subprocess.py:556: in run
    stdout, stderr = process.communicate(input, timeout=timeout)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/subprocess.py:1222: in communicate
    stdout, stderr = self._communicate(input, endtime, timeout)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/subprocess.py:2155: in _communicate
    self._check_timeout(endtime, orig_timeout, stdout, stderr)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Popen: returncode: -9 args: ['/Library/Frameworks/Python.framework/Versions...>
endtime = 2430.846398861, orig_timeout = 5, stdout_seq = [], stderr_seq = []
skip_check_and_raise = False

    def _check_timeout(self, endtime, orig_timeout, stdout_seq, stderr_seq,
                       skip_check_and_raise=False):
        """Convenience for checking if a timeout has expired."""
        if endtime is None:
            return
        if skip_check_and_raise or _time() > endtime:
>           raise TimeoutExpired(
                    self.args, orig_timeout,
                    output=b''.join(stdout_seq) if stdout_seq else 
... [The stack trace has been truncated as it exceeded the maximum allowed size. Please refer to the complete log available in the Test Run attachments for full details.]