Skip to content

Commit 388a3c3

Browse files
committed
restore connection tests
1 parent fb1289f commit 388a3c3

File tree

3 files changed

+11
-178
lines changed

3 files changed

+11
-178
lines changed

mssql_python/pybind/ddbc_bindings.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -678,12 +678,14 @@ DriverHandle LoadDriverOrThrowException() {
678678
if (fs::exists(authDllPath)) {
679679
HMODULE hAuth = LoadLibraryW(std::wstring(authDllPath.native().begin(), authDllPath.native().end()).c_str());
680680
if (hAuth) {
681-
LOG("Authentication DLL loaded: {}", authDllPath.string());
681+
LOG("mssql-auth.dll loaded: {}", authDllPath.string());
682682
} else {
683683
LOG("Failed to load mssql-auth.dll: {}", GetLastErrorMessage());
684+
ThrowStdException("Failed to load mssql-auth.dll. Please ensure it is present in the expected directory.");
684685
}
685686
} else {
686687
LOG("Note: mssql-auth.dll not found. This is OK if Entra ID is not in use.");
688+
ThrowStdException("mssql-auth.dll not found. If you are using Entra ID, please ensure it is present.");
687689
}
688690
#endif
689691

@@ -694,6 +696,10 @@ DriverHandle LoadDriverOrThrowException() {
694696
DriverHandle handle = LoadDriverLibrary(driverPath.string());
695697
if (!handle) {
696698
LOG("Failed to load driver: {}", GetLastErrorMessage());
699+
// If this happens in linux, suggest installing libltdl7
700+
#ifdef __linux__
701+
ThrowStdException("Failed to load ODBC driver. If you are on Linux, please install libltdl7 package.");
702+
#endif
697703
ThrowStdException("Failed to load ODBC driver. Please check installation.");
698704
}
699705
LOG("Driver library successfully loaded.");

tests/test_003_connection.py

Lines changed: 0 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -224,172 +224,3 @@ def test_connection_pooling_basic(conn_str):
224224

225225
conn1.close()
226226
conn2.close()
227-
228-
# Add these tests at the end of the file
229-
230-
def test_cursor_cleanup_on_connection_close(conn_str):
231-
"""Test that cursors are properly cleaned up when connection is closed"""
232-
# Create a new connection for this test
233-
conn = connect(conn_str)
234-
235-
# Create multiple cursors
236-
cursor1 = conn.cursor()
237-
cursor2 = conn.cursor()
238-
cursor3 = conn.cursor()
239-
240-
# Execute something on each cursor to ensure they have statement handles
241-
# Option 1: Fetch results immediately to free the connection
242-
cursor1.execute("SELECT 1")
243-
cursor1.fetchall()
244-
245-
cursor2.execute("SELECT 2")
246-
cursor2.fetchall()
247-
248-
cursor3.execute("SELECT 3")
249-
cursor3.fetchall()
250-
251-
# Close one cursor explicitly
252-
cursor1.close()
253-
assert cursor1.closed is True, "Cursor1 should be closed"
254-
255-
# Close the connection (should clean up remaining cursors)
256-
conn.close()
257-
258-
# Verify all cursors are closed
259-
assert cursor1.closed is True, "Cursor1 should remain closed"
260-
assert cursor2.closed is True, "Cursor2 should be closed by connection.close()"
261-
assert cursor3.closed is True, "Cursor3 should be closed by connection.close()"
262-
263-
def test_cursor_weakref_cleanup(conn_str):
264-
"""Test that WeakSet properly removes garbage collected cursors"""
265-
conn = connect(conn_str)
266-
267-
# Create cursors
268-
cursor1 = conn.cursor()
269-
cursor2 = conn.cursor()
270-
271-
# Check initial cursor count
272-
assert len(conn._cursors) == 2, "Should have 2 cursors"
273-
274-
# Delete reference to cursor1 (should be garbage collected)
275-
cursor1_id = id(cursor1)
276-
del cursor1
277-
278-
# Force garbage collection
279-
import gc
280-
gc.collect()
281-
282-
# Check cursor count after garbage collection
283-
assert len(conn._cursors) == 1, "Should have 1 cursor after garbage collection"
284-
285-
# Verify cursor2 is still there
286-
assert cursor2 in conn._cursors, "Cursor2 should still be in the set"
287-
288-
conn.close()
289-
290-
def test_cursor_cleanup_order_no_segfault(conn_str):
291-
"""Test that proper cleanup order prevents segfaults"""
292-
# This test ensures cursors are cleaned before connection
293-
conn = connect(conn_str)
294-
295-
# Create multiple cursors with active statements
296-
cursors = []
297-
for i in range(5):
298-
cursor = conn.cursor()
299-
cursor.execute(f"SELECT {i}")
300-
cursor.fetchall()
301-
cursors.append(cursor)
302-
303-
# Don't close any cursors explicitly
304-
# Just close the connection - it should handle cleanup properly
305-
conn.close()
306-
307-
# Verify all cursors were closed
308-
for cursor in cursors:
309-
assert cursor.closed is True, "All cursors should be closed"
310-
311-
def test_cursor_close_removes_from_connection(conn_str):
312-
"""Test that closing a cursor properly cleans up references"""
313-
conn = connect(conn_str)
314-
315-
# Create cursors
316-
cursor1 = conn.cursor()
317-
cursor2 = conn.cursor()
318-
cursor3 = conn.cursor()
319-
320-
assert len(conn._cursors) == 3, "Should have 3 cursors"
321-
322-
# Close cursor2
323-
cursor2.close()
324-
325-
# cursor2 should still be in the WeakSet (until garbage collected)
326-
# but it should be marked as closed
327-
assert cursor2.closed is True, "Cursor2 should be closed"
328-
329-
# Delete the reference and force garbage collection
330-
del cursor2
331-
import gc
332-
gc.collect()
333-
334-
# Now should have 2 cursors
335-
assert len(conn._cursors) == 2, "Should have 2 cursors after closing and GC"
336-
337-
conn.close()
338-
339-
def test_connection_close_idempotent(conn_str):
340-
"""Test that calling close() multiple times is safe"""
341-
conn = connect(conn_str)
342-
cursor = conn.cursor()
343-
cursor.execute("SELECT 1")
344-
345-
# First close
346-
conn.close()
347-
assert conn._closed is True, "Connection should be closed"
348-
349-
# Second close (should not raise exception)
350-
conn.close()
351-
assert conn._closed is True, "Connection should remain closed"
352-
353-
# Cursor should also be closed
354-
assert cursor.closed is True, "Cursor should be closed"
355-
356-
def test_cursor_after_connection_close(conn_str):
357-
"""Test that creating cursor after connection close raises error"""
358-
conn = connect(conn_str)
359-
conn.close()
360-
361-
# Should raise exception when trying to create cursor on closed connection
362-
with pytest.raises(InterfaceError) as excinfo:
363-
cursor = conn.cursor()
364-
365-
assert "closed connection" in str(excinfo.value).lower(), "Should mention closed connection"
366-
367-
def test_multiple_cursor_operations_cleanup(conn_str):
368-
"""Test cleanup with multiple cursor operations"""
369-
conn = connect(conn_str)
370-
371-
# Create table for testing
372-
cursor_setup = conn.cursor()
373-
drop_table_if_exists(cursor_setup, "#test_cleanup")
374-
cursor_setup.execute("CREATE TABLE #test_cleanup (id INT, value VARCHAR(50))")
375-
cursor_setup.close()
376-
377-
# Create multiple cursors doing different operations
378-
cursor_insert = conn.cursor()
379-
cursor_insert.execute("INSERT INTO #test_cleanup VALUES (1, 'test1'), (2, 'test2')")
380-
381-
cursor_select1 = conn.cursor()
382-
cursor_select1.execute("SELECT * FROM #test_cleanup WHERE id = 1")
383-
cursor_select1.fetchall()
384-
385-
cursor_select2 = conn.cursor()
386-
cursor_select2.execute("SELECT * FROM #test_cleanup WHERE id = 2")
387-
cursor_select2.fetchall()
388-
389-
# Close connection without closing cursors
390-
conn.close()
391-
392-
# All cursors should be closed
393-
assert cursor_insert.closed is True
394-
assert cursor_select1.closed is True
395-
assert cursor_select2.closed is True

tests/test_005_connection_cursor_lifecycle.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,9 @@ def test_no_segfault_on_gc(conn_str):
9595
assert result.returncode == 0, f"Expected no segfault, but got: {result.stderr}"
9696

9797
def test_multiple_connections_interleaved_cursors(conn_str):
98-
# Properly escape the connection string for embedding in code
99-
escaped_conn_str = conn_str.replace('\\', '\\\\').replace('"', '\\"')
100-
code = f"""
98+
code = """
10199
from mssql_python import connect
102-
conns = [connect("{escaped_conn_str}") for _ in range(3)]
100+
conns = [connect(\"""" + conn_str + """\") for _ in range(3)]
103101
cursors = []
104102
for conn in conns:
105103
# Create a cursor for each connection and execute a simple query
@@ -117,11 +115,9 @@ def test_multiple_connections_interleaved_cursors(conn_str):
117115
assert result.returncode == 0, f"Expected no segfault, but got: {result.stderr}"
118116

119117
def test_cursor_outlives_connection(conn_str):
120-
# Properly escape the connection string for embedding in code
121-
escaped_conn_str = conn_str.replace('\\', '\\\\').replace('"', '\\"')
122-
code = f"""
118+
code = """
123119
from mssql_python import connect
124-
conn = connect("{escaped_conn_str}")
120+
conn = connect(\"""" + conn_str + """\")
125121
cursor = conn.cursor()
126122
cursor.execute("SELECT 1")
127123
cursor.fetchall()

0 commit comments

Comments
 (0)