@@ -28,13 +28,15 @@ def __init__(self, schema: pa.Schema, batches):
2828 self ._schema = schema
2929 self ._batches : Deque [pa .RecordBatch ] = deque (batches )
3030 self .closed = False
31+ self .fetch_calls = 0
3132
3233 def arrow_schema (self ) -> pa .Schema :
3334 return self ._schema
3435
3536 def fetch_next_batch (self ):
3637 if self .closed :
3738 raise RuntimeError ("fetched after close" )
39+ self .fetch_calls += 1
3840 if not self ._batches :
3941 return None
4042 return self ._batches .popleft ()
@@ -43,7 +45,7 @@ def close(self):
4345 self .closed = True
4446
4547
46- def _make_rs (handle ) -> KernelResultSet :
48+ def _make_rs (handle , row_limit = None ) -> KernelResultSet :
4749 # The base ResultSet __init__ takes a `connection` ref it never
4850 # actually dereferences during these buffer tests, so a Mock is
4951 # fine.
@@ -56,6 +58,7 @@ def _make_rs(handle) -> KernelResultSet:
5658 command_id = CommandId .from_sea_statement_id ("smoke-test" ),
5759 arraysize = 100 ,
5860 buffer_size_bytes = 1024 ,
61+ row_limit = row_limit ,
5962 )
6063
6164
@@ -140,6 +143,63 @@ def test_fetchall_rows(int_schema):
140143 assert [r [0 ] for r in rows ] == [1 , 2 , 3 ]
141144
142145
146+ @pytest .mark .parametrize ("row_limit" , [0 , 1 , 5 ])
147+ def test_row_limit_caps_fetchall (int_schema , row_limit ):
148+ handle = _FakeKernelHandle (
149+ int_schema ,
150+ [_batch (int_schema , [0 , 1 , 2 ]), _batch (int_schema , list (range (3 , 10 )))],
151+ )
152+ rs = _make_rs (handle , row_limit = row_limit )
153+
154+ rows = rs .fetchall ()
155+
156+ assert [row [0 ] for row in rows ] == list (range (row_limit ))
157+ assert rs .rownumber == row_limit
158+
159+
160+ def test_row_limit_applies_across_fetch_methods (int_schema ):
161+ handle = _FakeKernelHandle (
162+ int_schema ,
163+ [_batch (int_schema , [0 , 1 , 2 ]), _batch (int_schema , [3 , 4 , 5 , 6 ])],
164+ )
165+ rs = _make_rs (handle , row_limit = 5 )
166+
167+ first = rs .fetchmany (2 )
168+ third = rs .fetchone ()
169+ rest = rs .fetchall_arrow ()
170+
171+ assert [row [0 ] for row in first ] == [0 , 1 ]
172+ assert third is not None and third [0 ] == 2
173+ assert rest .column (0 ).to_pylist () == [3 , 4 ]
174+ assert rs .fetchone () is None
175+
176+
177+ def test_row_limit_stops_before_fetching_extra_batches (int_schema ):
178+ handle = _FakeKernelHandle (
179+ int_schema ,
180+ [_batch (int_schema , [0 , 1 , 2 ]), _batch (int_schema , [3 , 4 , 5 ])],
181+ )
182+ rs = _make_rs (handle , row_limit = 2 )
183+
184+ assert rs .fetchall_arrow ().column (0 ).to_pylist () == [0 , 1 ]
185+ assert handle .fetch_calls == 1
186+
187+
188+ def test_row_limit_exact_batch_boundary_skips_exhaustion_fetch (int_schema ):
189+ handle = _FakeKernelHandle (int_schema , [_batch (int_schema , [0 , 1 , 2 ])])
190+ rs = _make_rs (handle , row_limit = 3 )
191+
192+ assert rs .fetchall_arrow ().column (0 ).to_pylist () == [0 , 1 , 2 ]
193+ assert handle .fetch_calls == 1
194+
195+
196+ def test_row_limit_larger_than_result_returns_all_rows (int_schema ):
197+ handle = _FakeKernelHandle (int_schema , [_batch (int_schema , [1 , 2 , 3 ])])
198+ rs = _make_rs (handle , row_limit = 10 )
199+
200+ assert rs .fetchall_arrow ().column (0 ).to_pylist () == [1 , 2 , 3 ]
201+
202+
143203def test_fetchmany_negative_raises (int_schema ):
144204 rs = _make_rs (_FakeKernelHandle (int_schema , []))
145205 with pytest .raises (ValueError ):
0 commit comments