@@ -19,7 +19,7 @@ def connection() -> npyodbc.Connection:
1919
2020@pytest .fixture (scope = "module" )
2121def cursor (connection ) -> npyodbc .Cursor :
22- """Return a cursor for a SQLite3 database."""
22+ """Return a new cursor for a SQLite3 database."""
2323 return connection .cursor ()
2424
2525
@@ -34,11 +34,13 @@ def cleanup(cursor: npyodbc.Cursor):
3434 cursor : npyodbc.Cursor
3535 Cursor for the database to clean up
3636 """
37- for table in cursor .execute ('SELECT tbl_name FROM sqlite_schema;' ).fetchall ():
37+ for table in cursor .execute (
38+ "SELECT tbl_name FROM sqlite_schema UNION ALL "
39+ "SELECT tbl_name FROM sqlite_temp_schema;"
40+ ).fetchall ():
3841 cursor .execute (f'DROP TABLE { table [0 ]} ;' )
3942 assert len (cursor .execute ('SELECT tbl_name FROM sqlite_schema;' ).fetchall ()) == 0
4043
41-
4244def assert_result_close (a : np .ndarray , b : np .ndarray , dtype : Optional [np .dtype ] = None ):
4345 """Assert that a is close to b.
4446
@@ -374,3 +376,41 @@ def test_null_strings(cursor):
374376 assert_array_equal (res ['f_isnull' ], [False , True ])
375377
376378 cleanup (cursor )
379+
380+
381+ @pytest .mark .parametrize (
382+ "dtype" , [
383+ "<S256" ,
384+ "<U256"
385+ ]
386+ )
387+ def test_string_dtype_itemsize_corruption (cursor , dtype ):
388+ """Test that the string and unicode dtype singletons don't get modified by npyodbc.
389+
390+ This happens if the PyArray_Descr for the string or unicode dtype has its itemsize
391+ modified; all modifications should happen on _copies_ of that singleton dtype, i.e.
392+ we should be calling
393+
394+ PyArray_DescrNewFromType(NPY_STRING) // <-- Returns a copy of the singleton
395+
396+ rather than
397+
398+ PyArray_DescrFromType(NPY_STRING) // <-- Returns the singleton string dtype
399+ """
400+ cursor .execute ("""
401+ create temp table binary_type(
402+ binary_type_n BLOB,
403+ binary_type BLOB NOT NULL
404+ )
405+ """ )
406+
407+ cursor .execute ("insert into binary_type values (1, 2)" )
408+ cursor .execute ("insert into binary_type values (NULL, 5)" )
409+ cursor .execute ("select * from binary_type" )
410+
411+ itemsize_before = np .array (["abcdef" ], dtype = dtype ).itemsize
412+ _ = cursor .fetchdictarray ()
413+ itemsize_after = np .array (["abcdef" ], dtype = dtype ).itemsize
414+
415+ assert itemsize_before == itemsize_after
416+ cleanup (cursor )
0 commit comments