@@ -1178,6 +1178,83 @@ TYPED_TEST(StatementTest, TestSQLExecDirectIgnoreInvalidBufLen) {
11781178 EXPECT_EQ (0 , timestamp_var.fraction );
11791179}
11801180
1181+ TYPED_TEST (StatementTest, TestSQLExtendedFetchRowFetching) {
1182+ // Set SQL_ROWSET_SIZE to fetch 3 rows at once
1183+
1184+ constexpr SQLULEN rows = 3 ;
1185+ SQLINTEGER val[rows];
1186+ SQLLEN buf_len = sizeof (val);
1187+ SQLLEN ind[rows];
1188+
1189+ // Same variable will be used for column 1, the value of `val`
1190+ // should be updated after every SQLFetch call.
1191+ ASSERT_EQ (SQL_SUCCESS , SQLBindCol (this ->stmt , 1 , SQL_C_LONG , val, buf_len, ind));
1192+
1193+ ASSERT_EQ (SQL_SUCCESS , SQLSetStmtAttr (this ->stmt , SQL_ROWSET_SIZE ,
1194+ reinterpret_cast <SQLPOINTER >(rows), 0 ));
1195+
1196+ std::wstring wsql =
1197+ LR"(
1198+ SELECT 1 AS small_table
1199+ UNION ALL
1200+ SELECT 2
1201+ UNION ALL
1202+ SELECT 3;
1203+ )" ;
1204+ std::vector<SQLWCHAR > sql0 (wsql.begin (), wsql.end ());
1205+
1206+ ASSERT_EQ (SQL_SUCCESS ,
1207+ SQLExecDirect (this ->stmt , &sql0[0 ], static_cast <SQLINTEGER >(sql0.size ())));
1208+
1209+ // Fetch row 1-3.
1210+ SQLULEN row_count;
1211+ SQLUSMALLINT row_status[rows];
1212+
1213+ ASSERT_EQ (SQL_SUCCESS ,
1214+ SQLExtendedFetch (this ->stmt , SQL_FETCH_NEXT , 0 , &row_count, row_status));
1215+ EXPECT_EQ (3 , row_count);
1216+
1217+ for (int i = 0 ; i < rows; i++) {
1218+ EXPECT_EQ (SQL_SUCCESS , row_status[i]);
1219+ }
1220+
1221+ // Verify 1 is returned for row 1
1222+ EXPECT_EQ (1 , val[0 ]);
1223+ // Verify 2 is returned for row 2
1224+ EXPECT_EQ (2 , val[1 ]);
1225+ // Verify 3 is returned for row 3
1226+ EXPECT_EQ (3 , val[2 ]);
1227+
1228+ // Verify result set has no more data beyond row 3
1229+ SQLULEN row_count2;
1230+ SQLUSMALLINT row_status2[rows];
1231+ EXPECT_EQ (SQL_NO_DATA ,
1232+ SQLExtendedFetch (this ->stmt , SQL_FETCH_NEXT , 0 , &row_count2, row_status2));
1233+ }
1234+
1235+ TEST_F (StatementRemoteTest, DISABLED_TestSQLExtendedFetchQueryNullIndicator) {
1236+ // GH-47110: SQLExtendedFetch should return SQL_SUCCESS_WITH_INFO for 22002
1237+ // Limitation on mock test server prevents null from working properly, so use remote
1238+ // server instead. Mock server has type `DENSE_UNION` for null column data.
1239+ SQLINTEGER val;
1240+
1241+ ASSERT_EQ (SQL_SUCCESS , SQLBindCol (this ->stmt , 1 , SQL_C_LONG , &val, 0 , 0 ));
1242+
1243+ std::wstring wsql = L" SELECT null as null_col;" ;
1244+ std::vector<SQLWCHAR > sql0 (wsql.begin (), wsql.end ());
1245+
1246+ ASSERT_EQ (SQL_SUCCESS ,
1247+ SQLExecDirect (this ->stmt , &sql0[0 ], static_cast <SQLINTEGER >(sql0.size ())));
1248+
1249+ SQLULEN row_count1;
1250+ SQLUSMALLINT row_status1[1 ];
1251+
1252+ // SQLExtendedFetch should return SQL_SUCCESS_WITH_INFO for 22002 state
1253+ ASSERT_EQ (SQL_SUCCESS_WITH_INFO ,
1254+ SQLExtendedFetch (this ->stmt , SQL_FETCH_NEXT , 0 , &row_count1, row_status1));
1255+ VerifyOdbcErrorState (SQL_HANDLE_STMT , this ->stmt , kErrorState22002 );
1256+ }
1257+
11811258TYPED_TEST (StatementTest, TestSQLNativeSqlReturnsInputString) {
11821259 SQLWCHAR buf[1024 ];
11831260 SQLINTEGER buf_char_len = sizeof (buf) / GetSqlWCharSize ();
0 commit comments