@@ -988,6 +988,218 @@ def test_fetchmany_lob_with_arraysize(cursor, db_connection):
988988 assert len(rows) == 3, "fetchmany_lob with arraysize returned incorrect number of rows"
989989
990990
991+ def test_fetchmany_size_zero_lob(cursor, db_connection):
992+ """Test fetchmany with size=0 for LOB columns"""
993+ try:
994+ cursor.execute("DROP TABLE IF EXISTS #test_fetchmany_lob")
995+ cursor.execute(
996+ """
997+ CREATE TABLE #test_fetchmany_lob (
998+ id INT PRIMARY KEY,
999+ lob_data NVARCHAR(MAX)
1000+ )
1001+ """
1002+ )
1003+
1004+ # Insert test data
1005+ test_data = [(1, "First LOB data"), (2, "Second LOB data"), (3, "Third LOB data")]
1006+ cursor.executemany(
1007+ "INSERT INTO #test_fetchmany_lob (id, lob_data) VALUES (?, ?)", test_data
1008+ )
1009+ db_connection.commit()
1010+
1011+ # Test fetchmany with size=0
1012+ cursor.execute("SELECT * FROM #test_fetchmany_lob ORDER BY id")
1013+ rows = cursor.fetchmany(0)
1014+
1015+ assert isinstance(rows, list), "fetchmany should return a list"
1016+ assert len(rows) == 0, "fetchmany(0) should return empty list"
1017+
1018+ finally:
1019+ cursor.execute("DROP TABLE IF EXISTS #test_fetchmany_lob")
1020+ db_connection.commit()
1021+
1022+
1023+ def test_fetchmany_more_than_exist_lob(cursor, db_connection):
1024+ """Test fetchmany requesting more rows than exist with LOB columns"""
1025+ try:
1026+ cursor.execute("DROP TABLE IF EXISTS #test_fetchmany_lob_more")
1027+ cursor.execute(
1028+ """
1029+ CREATE TABLE #test_fetchmany_lob_more (
1030+ id INT PRIMARY KEY,
1031+ lob_data NVARCHAR(MAX)
1032+ )
1033+ """
1034+ )
1035+
1036+ # Insert only 3 rows
1037+ test_data = [(1, "First LOB data"), (2, "Second LOB data"), (3, "Third LOB data")]
1038+ cursor.executemany(
1039+ "INSERT INTO #test_fetchmany_lob_more (id, lob_data) VALUES (?, ?)", test_data
1040+ )
1041+ db_connection.commit()
1042+
1043+ # Request 10 rows but only 3 exist
1044+ cursor.execute("SELECT * FROM #test_fetchmany_lob_more ORDER BY id")
1045+ rows = cursor.fetchmany(10)
1046+
1047+ assert isinstance(rows, list), "fetchmany should return a list"
1048+ assert len(rows) == 3, "fetchmany should return all 3 available rows"
1049+
1050+ # Verify data
1051+ for i, row in enumerate(rows):
1052+ assert row[0] == i + 1, f"Row {i} id mismatch"
1053+ assert row[1] == test_data[i][1], f"Row {i} LOB data mismatch"
1054+
1055+ # Second call should return empty
1056+ rows2 = cursor.fetchmany(10)
1057+ assert len(rows2) == 0, "Second fetchmany should return empty list"
1058+
1059+ finally:
1060+ cursor.execute("DROP TABLE IF EXISTS #test_fetchmany_lob_more")
1061+ db_connection.commit()
1062+
1063+
1064+ def test_fetchmany_empty_result_lob(cursor, db_connection):
1065+ """Test fetchmany on empty result set with LOB columns"""
1066+ try:
1067+ cursor.execute("DROP TABLE IF EXISTS #test_fetchmany_lob_empty")
1068+ cursor.execute(
1069+ """
1070+ CREATE TABLE #test_fetchmany_lob_empty (
1071+ id INT PRIMARY KEY,
1072+ lob_data NVARCHAR(MAX)
1073+ )
1074+ """
1075+ )
1076+ db_connection.commit()
1077+
1078+ # Query empty table
1079+ cursor.execute("SELECT * FROM #test_fetchmany_lob_empty")
1080+ rows = cursor.fetchmany(5)
1081+
1082+ assert isinstance(rows, list), "fetchmany should return a list"
1083+ assert len(rows) == 0, "fetchmany on empty result should return empty list"
1084+
1085+ # Multiple calls on empty result
1086+ rows2 = cursor.fetchmany(5)
1087+ assert len(rows2) == 0, "Subsequent fetchmany should also return empty list"
1088+
1089+ finally:
1090+ cursor.execute("DROP TABLE IF EXISTS #test_fetchmany_lob_empty")
1091+ db_connection.commit()
1092+
1093+
1094+ def test_fetchmany_very_large_lob(cursor, db_connection):
1095+ """Test fetchmany with very large LOB column data"""
1096+ try:
1097+ cursor.execute("DROP TABLE IF EXISTS #test_fetchmany_large_lob")
1098+ cursor.execute(
1099+ """
1100+ CREATE TABLE #test_fetchmany_large_lob (
1101+ id INT PRIMARY KEY,
1102+ large_lob NVARCHAR(MAX)
1103+ )
1104+ """
1105+ )
1106+
1107+ # Create very large data (10000 characters)
1108+ large_data = "x" * 10000
1109+
1110+ # Insert multiple rows with large LOB data
1111+ test_data = [
1112+ (1, large_data),
1113+ (2, large_data + "y" * 100), # Slightly different
1114+ (3, large_data + "z" * 200),
1115+ (4, "Small data"),
1116+ (5, large_data),
1117+ ]
1118+ cursor.executemany(
1119+ "INSERT INTO #test_fetchmany_large_lob (id, large_lob) VALUES (?, ?)", test_data
1120+ )
1121+ db_connection.commit()
1122+
1123+ # Test fetchmany with large LOB data
1124+ cursor.execute("SELECT * FROM #test_fetchmany_large_lob ORDER BY id")
1125+
1126+ # Fetch 2 rows at a time
1127+ batch1 = cursor.fetchmany(2)
1128+ assert len(batch1) == 2, "First batch should have 2 rows"
1129+ assert len(batch1[0][1]) == 10000, "First row LOB size mismatch"
1130+ assert len(batch1[1][1]) == 10100, "Second row LOB size mismatch"
1131+ assert batch1[0][1] == large_data, "First row LOB data mismatch"
1132+
1133+ batch2 = cursor.fetchmany(2)
1134+ assert len(batch2) == 2, "Second batch should have 2 rows"
1135+ assert len(batch2[0][1]) == 10200, "Third row LOB size mismatch"
1136+ assert batch2[1][1] == "Small data", "Fourth row data mismatch"
1137+
1138+ batch3 = cursor.fetchmany(2)
1139+ assert len(batch3) == 1, "Third batch should have 1 remaining row"
1140+ assert len(batch3[0][1]) == 10000, "Fifth row LOB size mismatch"
1141+
1142+ # Verify no more data
1143+ batch4 = cursor.fetchmany(2)
1144+ assert len(batch4) == 0, "Should have no more rows"
1145+
1146+ finally:
1147+ cursor.execute("DROP TABLE IF EXISTS #test_fetchmany_large_lob")
1148+ db_connection.commit()
1149+
1150+
1151+ def test_fetchmany_mixed_lob_sizes(cursor, db_connection):
1152+ """Test fetchmany with mixed LOB sizes including empty and NULL"""
1153+ try:
1154+ cursor.execute("DROP TABLE IF EXISTS #test_fetchmany_mixed_lob")
1155+ cursor.execute(
1156+ """
1157+ CREATE TABLE #test_fetchmany_mixed_lob (
1158+ id INT PRIMARY KEY,
1159+ mixed_lob NVARCHAR(MAX)
1160+ )
1161+ """
1162+ )
1163+
1164+ # Mix of sizes: empty, NULL, small, medium, large
1165+ test_data = [
1166+ (1, ""), # Empty string
1167+ (2, None), # NULL
1168+ (3, "Small"),
1169+ (4, "x" * 1000), # Medium
1170+ (5, "y" * 10000), # Large
1171+ (6, ""), # Empty again
1172+ (7, "z" * 5000), # Another large
1173+ ]
1174+ cursor.executemany(
1175+ "INSERT INTO #test_fetchmany_mixed_lob (id, mixed_lob) VALUES (?, ?)", test_data
1176+ )
1177+ db_connection.commit()
1178+
1179+ # Fetch all with fetchmany
1180+ cursor.execute("SELECT * FROM #test_fetchmany_mixed_lob ORDER BY id")
1181+ rows = cursor.fetchmany(3)
1182+
1183+ assert len(rows) == 3, "First batch should have 3 rows"
1184+ assert rows[0][1] == "", "First row should be empty string"
1185+ assert rows[1][1] is None, "Second row should be NULL"
1186+ assert rows[2][1] == "Small", "Third row should be 'Small'"
1187+
1188+ rows2 = cursor.fetchmany(3)
1189+ assert len(rows2) == 3, "Second batch should have 3 rows"
1190+ assert len(rows2[0][1]) == 1000, "Fourth row LOB size mismatch"
1191+ assert len(rows2[1][1]) == 10000, "Fifth row LOB size mismatch"
1192+ assert rows2[2][1] == "", "Sixth row should be empty string"
1193+
1194+ rows3 = cursor.fetchmany(3)
1195+ assert len(rows3) == 1, "Third batch should have 1 remaining row"
1196+ assert len(rows3[0][1]) == 5000, "Seventh row LOB size mismatch"
1197+
1198+ finally:
1199+ cursor.execute("DROP TABLE IF EXISTS #test_fetchmany_mixed_lob")
1200+ db_connection.commit()
1201+
1202+
9911203def test_fetchall(cursor):
9921204 """Test fetching all rows"""
9931205 cursor.execute(
0 commit comments