@@ -5433,6 +5433,279 @@ def test_empty_string_chunk(cursor, db_connection):
54335433 cursor .execute ("DROP TABLE IF EXISTS #pytest_empty_string" )
54345434 db_connection .commit ()
54355435
5436+ def test_empty_char_single_and_batch_fetch (cursor , db_connection ):
5437+ """Test that empty CHAR data is handled correctly in both single and batch fetch"""
5438+ try :
5439+ # Create test table with regular VARCHAR (CHAR is fixed-length and pads with spaces)
5440+ drop_table_if_exists (cursor , "#pytest_empty_char" )
5441+ cursor .execute ("CREATE TABLE #pytest_empty_char (id INT, char_col VARCHAR(100))" )
5442+ db_connection .commit ()
5443+
5444+ # Insert empty VARCHAR data
5445+ cursor .execute ("INSERT INTO #pytest_empty_char VALUES (1, '')" )
5446+ cursor .execute ("INSERT INTO #pytest_empty_char VALUES (2, '')" )
5447+ db_connection .commit ()
5448+
5449+ # Test single-row fetch (fetchone)
5450+ cursor .execute ("SELECT char_col FROM #pytest_empty_char WHERE id = 1" )
5451+ row = cursor .fetchone ()
5452+ assert row is not None , "Should return a row"
5453+ assert row [0 ] == '' , "Should return empty string, not None"
5454+
5455+ # Test batch fetch (fetchall)
5456+ cursor .execute ("SELECT char_col FROM #pytest_empty_char ORDER BY id" )
5457+ rows = cursor .fetchall ()
5458+ assert len (rows ) == 2 , "Should return 2 rows"
5459+ assert rows [0 ][0 ] == '' , "Row 1 should have empty string"
5460+ assert rows [1 ][0 ] == '' , "Row 2 should have empty string"
5461+
5462+ # Test batch fetch (fetchmany)
5463+ cursor .execute ("SELECT char_col FROM #pytest_empty_char ORDER BY id" )
5464+ many_rows = cursor .fetchmany (2 )
5465+ assert len (many_rows ) == 2 , "Should return 2 rows with fetchmany"
5466+ assert many_rows [0 ][0 ] == '' , "fetchmany row 1 should have empty string"
5467+ assert many_rows [1 ][0 ] == '' , "fetchmany row 2 should have empty string"
5468+
5469+ except Exception as e :
5470+ pytest .fail (f"Empty VARCHAR handling test failed: { e } " )
5471+ finally :
5472+ cursor .execute ("DROP TABLE #pytest_empty_char" )
5473+ db_connection .commit ()
5474+
5475+ def test_empty_varbinary_batch_fetch (cursor , db_connection ):
5476+ """Test that empty VARBINARY data is handled correctly in batch fetch operations"""
5477+ try :
5478+ # Create test table
5479+ drop_table_if_exists (cursor , "#pytest_empty_varbinary_batch" )
5480+ cursor .execute ("CREATE TABLE #pytest_empty_varbinary_batch (id INT, binary_col VARBINARY(100))" )
5481+ db_connection .commit ()
5482+
5483+ # Insert multiple rows with empty binary data
5484+ cursor .execute ("INSERT INTO #pytest_empty_varbinary_batch VALUES (1, 0x)" ) # Empty binary
5485+ cursor .execute ("INSERT INTO #pytest_empty_varbinary_batch VALUES (2, 0x)" ) # Empty binary
5486+ cursor .execute ("INSERT INTO #pytest_empty_varbinary_batch VALUES (3, 0x1234)" ) # Non-empty for comparison
5487+ db_connection .commit ()
5488+
5489+ # Test fetchall for batch processing
5490+ cursor .execute ("SELECT id, binary_col FROM #pytest_empty_varbinary_batch ORDER BY id" )
5491+ rows = cursor .fetchall ()
5492+ assert len (rows ) == 3 , "Should return 3 rows"
5493+
5494+ # Check empty binary rows
5495+ assert rows [0 ][1 ] == b'' , "Row 1 should have empty bytes"
5496+ assert rows [1 ][1 ] == b'' , "Row 2 should have empty bytes"
5497+ assert isinstance (rows [0 ][1 ], bytes ), "Should return bytes type for empty binary"
5498+ assert len (rows [0 ][1 ]) == 0 , "Should be zero-length bytes"
5499+
5500+ # Check non-empty row for comparison
5501+ assert rows [2 ][1 ] == b'\x12 \x34 ' , "Row 3 should have non-empty binary"
5502+
5503+ # Test fetchmany batch processing
5504+ cursor .execute ("SELECT binary_col FROM #pytest_empty_varbinary_batch WHERE id <= 2 ORDER BY id" )
5505+ many_rows = cursor .fetchmany (2 )
5506+ assert len (many_rows ) == 2 , "fetchmany should return 2 rows"
5507+ assert many_rows [0 ][0 ] == b'' , "fetchmany row 1 should have empty bytes"
5508+ assert many_rows [1 ][0 ] == b'' , "fetchmany row 2 should have empty bytes"
5509+
5510+ except Exception as e :
5511+ pytest .fail (f"Empty VARBINARY batch fetch test failed: { e } " )
5512+ finally :
5513+ cursor .execute ("DROP TABLE #pytest_empty_varbinary_batch" )
5514+ db_connection .commit ()
5515+
5516+ def test_empty_values_fetchmany (cursor , db_connection ):
5517+ """Test fetchmany with empty values for all string/binary types"""
5518+ try :
5519+ # Create comprehensive test table
5520+ drop_table_if_exists (cursor , "#pytest_fetchmany_empty" )
5521+ cursor .execute ("""
5522+ CREATE TABLE #pytest_fetchmany_empty (
5523+ id INT,
5524+ varchar_col VARCHAR(50),
5525+ nvarchar_col NVARCHAR(50),
5526+ binary_col VARBINARY(50)
5527+ )
5528+ """ )
5529+ db_connection .commit ()
5530+
5531+ # Insert multiple rows with empty values
5532+ for i in range (1 , 6 ): # 5 rows
5533+ cursor .execute ("""
5534+ INSERT INTO #pytest_fetchmany_empty
5535+ VALUES (?, '', '', 0x)
5536+ """ , [i ])
5537+ db_connection .commit ()
5538+
5539+ # Test fetchmany with different sizes
5540+ cursor .execute ("SELECT varchar_col, nvarchar_col, binary_col FROM #pytest_fetchmany_empty ORDER BY id" )
5541+
5542+ # Fetch 3 rows
5543+ rows = cursor .fetchmany (3 )
5544+ assert len (rows ) == 3 , "Should fetch 3 rows"
5545+ for i , row in enumerate (rows ):
5546+ assert row [0 ] == '' , f"Row { i + 1 } VARCHAR should be empty string"
5547+ assert row [1 ] == '' , f"Row { i + 1 } NVARCHAR should be empty string"
5548+ assert row [2 ] == b'' , f"Row { i + 1 } VARBINARY should be empty bytes"
5549+ assert isinstance (row [2 ], bytes ), f"Row { i + 1 } VARBINARY should be bytes type"
5550+
5551+ # Fetch remaining rows
5552+ remaining_rows = cursor .fetchmany (5 ) # Ask for 5 but should get 2
5553+ assert len (remaining_rows ) == 2 , "Should fetch remaining 2 rows"
5554+ for i , row in enumerate (remaining_rows ):
5555+ assert row [0 ] == '' , f"Remaining row { i + 1 } VARCHAR should be empty string"
5556+ assert row [1 ] == '' , f"Remaining row { i + 1 } NVARCHAR should be empty string"
5557+ assert row [2 ] == b'' , f"Remaining row { i + 1 } VARBINARY should be empty bytes"
5558+
5559+ except Exception as e :
5560+ pytest .fail (f"Empty values fetchmany test failed: { e } " )
5561+ finally :
5562+ cursor .execute ("DROP TABLE #pytest_fetchmany_empty" )
5563+ db_connection .commit ()
5564+
5565+ def test_sql_no_total_large_data_scenario (cursor , db_connection ):
5566+ """Test very large data that might trigger SQL_NO_TOTAL handling"""
5567+ try :
5568+ # Create test table for large data
5569+ drop_table_if_exists (cursor , "#pytest_large_data_no_total" )
5570+ cursor .execute ("CREATE TABLE #pytest_large_data_no_total (id INT, large_text NVARCHAR(MAX), large_binary VARBINARY(MAX))" )
5571+ db_connection .commit ()
5572+
5573+ # Create large data that might trigger SQL_NO_TOTAL
5574+ large_string = 'A' * (5 * 1024 * 1024 ) # 5MB string
5575+ large_binary = b'\x00 ' * (5 * 1024 * 1024 ) # 5MB binary
5576+
5577+ cursor .execute ("INSERT INTO #pytest_large_data_no_total VALUES (1, ?, ?)" , [large_string , large_binary ])
5578+ cursor .execute ("INSERT INTO #pytest_large_data_no_total VALUES (2, ?, ?)" , [large_string , large_binary ])
5579+ db_connection .commit ()
5580+
5581+ # Test single fetch - should not crash if SQL_NO_TOTAL occurs
5582+ cursor .execute ("SELECT large_text, large_binary FROM #pytest_large_data_no_total WHERE id = 1" )
5583+ row = cursor .fetchone ()
5584+
5585+ # If SQL_NO_TOTAL occurs, it should return None, not crash
5586+ # If it works normally, it should return the large data
5587+ if row [0 ] is not None :
5588+ assert isinstance (row [0 ], str ), "Text data should be str if not None"
5589+ assert len (row [0 ]) > 0 , "Text data should be non-empty if not None"
5590+ if row [1 ] is not None :
5591+ assert isinstance (row [1 ], bytes ), "Binary data should be bytes if not None"
5592+ assert len (row [1 ]) > 0 , "Binary data should be non-empty if not None"
5593+
5594+ # Test batch fetch - should handle SQL_NO_TOTAL consistently
5595+ cursor .execute ("SELECT large_text, large_binary FROM #pytest_large_data_no_total ORDER BY id" )
5596+ rows = cursor .fetchall ()
5597+ assert len (rows ) == 2 , "Should return 2 rows"
5598+
5599+ # Both rows should behave consistently
5600+ for i , row in enumerate (rows ):
5601+ if row [0 ] is not None :
5602+ assert isinstance (row [0 ], str ), f"Row { i + 1 } text should be str if not None"
5603+ if row [1 ] is not None :
5604+ assert isinstance (row [1 ], bytes ), f"Row { i + 1 } binary should be bytes if not None"
5605+
5606+ # Test fetchmany - should handle SQL_NO_TOTAL consistently
5607+ cursor .execute ("SELECT large_text FROM #pytest_large_data_no_total ORDER BY id" )
5608+ many_rows = cursor .fetchmany (2 )
5609+ assert len (many_rows ) == 2 , "fetchmany should return 2 rows"
5610+
5611+ for i , row in enumerate (many_rows ):
5612+ if row [0 ] is not None :
5613+ assert isinstance (row [0 ], str ), f"fetchmany row { i + 1 } should be str if not None"
5614+
5615+ except Exception as e :
5616+ # Should not crash with assertion errors about dataLen
5617+ assert "Data length must be" not in str (e ), "Should not fail with dataLen assertion"
5618+ assert "assert" not in str (e ).lower (), "Should not fail with assertion errors"
5619+ # If it fails for other reasons (like memory), that's acceptable
5620+ print (f"Large data test completed with expected limitation: { e } " )
5621+
5622+ finally :
5623+ try :
5624+ cursor .execute ("DROP TABLE #pytest_large_data_no_total" )
5625+ db_connection .commit ()
5626+ except :
5627+ pass # Table might not exist if test failed early
5628+
5629+ def test_batch_fetch_empty_values_no_assertion_failure (cursor , db_connection ):
5630+ """Test that batch fetch operations don't fail with assertions on empty values"""
5631+ try :
5632+ # Create comprehensive test table
5633+ drop_table_if_exists (cursor , "#pytest_batch_empty_assertions" )
5634+ cursor .execute ("""
5635+ CREATE TABLE #pytest_batch_empty_assertions (
5636+ id INT,
5637+ empty_varchar VARCHAR(100),
5638+ empty_nvarchar NVARCHAR(100),
5639+ empty_binary VARBINARY(100),
5640+ null_varchar VARCHAR(100),
5641+ null_nvarchar NVARCHAR(100),
5642+ null_binary VARBINARY(100)
5643+ )
5644+ """ )
5645+ db_connection .commit ()
5646+
5647+ # Insert rows with mix of empty and NULL values
5648+ cursor .execute ("""
5649+ INSERT INTO #pytest_batch_empty_assertions VALUES
5650+ (1, '', '', 0x, NULL, NULL, NULL),
5651+ (2, '', '', 0x, NULL, NULL, NULL),
5652+ (3, '', '', 0x, NULL, NULL, NULL)
5653+ """ )
5654+ db_connection .commit ()
5655+
5656+ # Test fetchall - should not trigger any assertions about dataLen
5657+ cursor .execute ("""
5658+ SELECT empty_varchar, empty_nvarchar, empty_binary,
5659+ null_varchar, null_nvarchar, null_binary
5660+ FROM #pytest_batch_empty_assertions ORDER BY id
5661+ """ )
5662+
5663+ rows = cursor .fetchall ()
5664+ assert len (rows ) == 3 , "Should return 3 rows"
5665+
5666+ for i , row in enumerate (rows ):
5667+ # Check empty values (should be empty strings/bytes, not None)
5668+ assert row [0 ] == '' , f"Row { i + 1 } empty_varchar should be empty string"
5669+ assert row [1 ] == '' , f"Row { i + 1 } empty_nvarchar should be empty string"
5670+ assert row [2 ] == b'' , f"Row { i + 1 } empty_binary should be empty bytes"
5671+
5672+ # Check NULL values (should be None)
5673+ assert row [3 ] is None , f"Row { i + 1 } null_varchar should be None"
5674+ assert row [4 ] is None , f"Row { i + 1 } null_nvarchar should be None"
5675+ assert row [5 ] is None , f"Row { i + 1 } null_binary should be None"
5676+
5677+ # Test fetchmany - should also not trigger assertions
5678+ cursor .execute ("""
5679+ SELECT empty_nvarchar, empty_binary
5680+ FROM #pytest_batch_empty_assertions ORDER BY id
5681+ """ )
5682+
5683+ # Fetch in batches
5684+ first_batch = cursor .fetchmany (2 )
5685+ assert len (first_batch ) == 2 , "First batch should return 2 rows"
5686+
5687+ second_batch = cursor .fetchmany (2 ) # Ask for 2, get 1
5688+ assert len (second_batch ) == 1 , "Second batch should return 1 row"
5689+
5690+ # All batches should have correct empty values
5691+ all_batch_rows = first_batch + second_batch
5692+ for i , row in enumerate (all_batch_rows ):
5693+ assert row [0 ] == '' , f"Batch row { i + 1 } empty_nvarchar should be empty string"
5694+ assert row [1 ] == b'' , f"Batch row { i + 1 } empty_binary should be empty bytes"
5695+ assert isinstance (row [1 ], bytes ), f"Batch row { i + 1 } should return bytes type"
5696+
5697+ except Exception as e :
5698+ # Should specifically not fail with dataLen assertion errors
5699+ error_msg = str (e ).lower ()
5700+ assert "data length must be" not in error_msg , f"Should not fail with dataLen assertion: { e } "
5701+ assert "assert" not in error_msg or "assertion" not in error_msg , f"Should not fail with assertion errors: { e } "
5702+ # Re-raise if it's a different kind of error
5703+ raise
5704+
5705+ finally :
5706+ cursor .execute ("DROP TABLE #pytest_batch_empty_assertions" )
5707+ db_connection .commit ()
5708+
54365709def test_close (db_connection ):
54375710 """Test closing the cursor"""
54385711 try :
0 commit comments