@@ -7358,6 +7358,97 @@ def test_extreme_uuids(cursor, db_connection):
73587358 cursor .execute (f"DROP TABLE IF EXISTS { table_name } " )
73597359 db_connection .commit ()
73607360
7361+ def test_executemany_uuid_insert_and_select (cursor , db_connection ):
7362+ """Test inserting multiple UUIDs using executemany and verifying retrieval."""
7363+ table_name = "#pytest_uuid_executemany"
7364+
7365+ try :
7366+ # Drop and create a temporary table for the test
7367+ cursor .execute (f"DROP TABLE IF EXISTS { table_name } " )
7368+ cursor .execute (f"""
7369+ CREATE TABLE { table_name } (
7370+ id UNIQUEIDENTIFIER PRIMARY KEY,
7371+ description NVARCHAR(50)
7372+ )
7373+ """ )
7374+ db_connection .commit ()
7375+
7376+ # Generate data for insertion
7377+ data_to_insert = [(uuid .uuid4 (), f"Item { i } " ) for i in range (5 )]
7378+
7379+ # Insert all data with a single call to executemany
7380+ sql = f"INSERT INTO { table_name } (id, description) VALUES (?, ?)"
7381+ cursor .executemany (sql , data_to_insert )
7382+ db_connection .commit ()
7383+
7384+ # Verify the number of rows inserted
7385+ assert cursor .rowcount == 5 , f"Expected 5 rows inserted, but got { cursor .rowcount } "
7386+
7387+ # Fetch all data from the table
7388+ cursor .execute (f"SELECT id, description FROM { table_name } ORDER BY description" )
7389+ rows = cursor .fetchall ()
7390+
7391+ # Verify the number of fetched rows
7392+ assert len (rows ) == len (data_to_insert ), "Number of fetched rows does not match."
7393+
7394+ # Compare inserted and retrieved rows by index
7395+ for i , (retrieved_uuid , retrieved_desc ) in enumerate (rows ):
7396+ expected_uuid , expected_desc = data_to_insert [i ]
7397+
7398+ # Assert the type is correct
7399+ if isinstance (retrieved_uuid , str ):
7400+ retrieved_uuid = uuid .UUID (retrieved_uuid ) # convert if driver returns str
7401+
7402+ assert isinstance (retrieved_uuid , uuid .UUID ), f"Expected uuid.UUID, got { type (retrieved_uuid )} "
7403+ assert retrieved_uuid == expected_uuid , f"UUID mismatch for '{ retrieved_desc } ': expected { expected_uuid } , got { retrieved_uuid } "
7404+ assert retrieved_desc == expected_desc , f"Description mismatch: expected { expected_desc } , got { retrieved_desc } "
7405+
7406+ finally :
7407+ # Clean up the temporary table
7408+ cursor .execute (f"DROP TABLE IF EXISTS { table_name } " )
7409+ db_connection .commit ()
7410+
7411+ def test_executemany_uuid_roundtrip_fixed_value (cursor , db_connection ):
7412+ """Ensure a fixed canonical UUID round-trips exactly."""
7413+ table_name = "#pytest_uuid_fixed"
7414+ try :
7415+ cursor .execute (f"DROP TABLE IF EXISTS { table_name } " )
7416+ cursor .execute (f"""
7417+ CREATE TABLE { table_name } (
7418+ id UNIQUEIDENTIFIER,
7419+ description NVARCHAR(50)
7420+ )
7421+ """ )
7422+ db_connection .commit ()
7423+
7424+ fixed_uuid = uuid .UUID ("12345678-1234-5678-1234-567812345678" )
7425+ description = "FixedUUID"
7426+
7427+ # Insert via executemany
7428+ cursor .executemany (
7429+ f"INSERT INTO { table_name } (id, description) VALUES (?, ?)" ,
7430+ [(fixed_uuid , description )]
7431+ )
7432+ db_connection .commit ()
7433+
7434+ # Fetch back
7435+ cursor .execute (f"SELECT id, description FROM { table_name } WHERE description = ?" , description )
7436+ row = cursor .fetchone ()
7437+ retrieved_uuid , retrieved_desc = row
7438+
7439+ # Ensure type and value are correct
7440+ if isinstance (retrieved_uuid , str ):
7441+ retrieved_uuid = uuid .UUID (retrieved_uuid )
7442+
7443+ assert isinstance (retrieved_uuid , uuid .UUID )
7444+ assert retrieved_uuid == fixed_uuid
7445+ assert str (retrieved_uuid ) == str (fixed_uuid )
7446+ assert retrieved_desc == description
7447+
7448+ finally :
7449+ cursor .execute (f"DROP TABLE IF EXISTS { table_name } " )
7450+ db_connection .commit ()
7451+
73617452def test_decimal_separator_with_multiple_values (cursor , db_connection ):
73627453 """Test decimal separator with multiple different decimal values"""
73637454 original_separator = mssql_python .getDecimalSeparator ()
@@ -10898,6 +10989,59 @@ def test_decimal_separator_calculations(cursor, db_connection):
1089810989
1089910990 # Cleanup
1090010991 cursor .execute ("DROP TABLE IF EXISTS #pytest_decimal_calc_test" )
10992+ db_connection .commit ()
10993+
10994+ def test_executemany_with_uuids (cursor , db_connection ):
10995+ """Test inserting multiple rows with UUIDs and None using executemany."""
10996+ table_name = "#pytest_uuid_batch"
10997+ try :
10998+ cursor .execute (f"DROP TABLE IF EXISTS { table_name } " )
10999+ cursor .execute (f"""
11000+ CREATE TABLE { table_name } (
11001+ id UNIQUEIDENTIFIER,
11002+ description NVARCHAR(50)
11003+ )
11004+ """ )
11005+ db_connection .commit ()
11006+
11007+ # Prepare test data: mix of UUIDs and None
11008+ test_data = [
11009+ [uuid .uuid4 (), "Item 1" ],
11010+ [uuid .uuid4 (), "Item 2" ],
11011+ [None , "Item 3" ],
11012+ [uuid .uuid4 (), "Item 4" ],
11013+ [None , "Item 5" ]
11014+ ]
11015+
11016+ # Map descriptions to original UUIDs for O(1) lookup
11017+ uuid_map = {desc : uid for uid , desc in test_data }
11018+
11019+ # Execute batch insert
11020+ cursor .executemany (f"INSERT INTO { table_name } (id, description) VALUES (?, ?)" , test_data )
11021+ cursor .connection .commit ()
11022+
11023+ # Fetch and verify
11024+ cursor .execute (f"SELECT id, description FROM { table_name } " )
11025+ rows = cursor .fetchall ()
11026+
11027+ assert len (rows ) == len (test_data ), "Number of fetched rows does not match inserted rows."
11028+
11029+ for retrieved_uuid , retrieved_desc in rows :
11030+ expected_uuid = uuid_map [retrieved_desc ]
11031+
11032+ if expected_uuid is None :
11033+ assert retrieved_uuid is None , f"Expected None for '{ retrieved_desc } ', got { retrieved_uuid } "
11034+ else :
11035+ # Convert string to UUID if needed
11036+ if isinstance (retrieved_uuid , str ):
11037+ retrieved_uuid = uuid .UUID (retrieved_uuid )
11038+
11039+ assert isinstance (retrieved_uuid , uuid .UUID ), f"Expected UUID, got { type (retrieved_uuid )} "
11040+ assert retrieved_uuid == expected_uuid , f"UUID mismatch for '{ retrieved_desc } '"
11041+
11042+ finally :
11043+ cursor .execute (f"DROP TABLE IF EXISTS { table_name } " )
11044+ db_connection .commit ()
1090111045
1090211046def test_nvarcharmax_executemany_streaming (cursor , db_connection ):
1090311047 """Streaming insert + fetch > 4k NVARCHAR(MAX) using executemany with all fetch modes."""
0 commit comments