@@ -70,9 +70,15 @@ _mongoc_cursor_new (mongoc_client_t *client,
7070
7171 ENTRY ;
7272
73- bson_return_val_if_fail (client , NULL );
74- bson_return_val_if_fail (db_and_collection , NULL );
75- bson_return_val_if_fail (query , NULL );
73+ BSON_ASSERT (client );
74+ BSON_ASSERT (db_and_collection );
75+ BSON_ASSERT (query );
76+
77+ /* we can't have exhaust queries with limits */
78+ BSON_ASSERT (!((flags & MONGOC_QUERY_EXHAUST ) && limit ));
79+
80+ /* we can't have exhaust queries with sharded clusters */
81+ BSON_ASSERT (!((flags & MONGOC_QUERY_EXHAUST ) && client -> cluster .isdbgrid ));
7682
7783 /*
7884 * Cursors execute their query lazily. This sadly means that we must copy
@@ -180,7 +186,15 @@ _mongoc_cursor_destroy (mongoc_cursor_t *cursor)
180186
181187 bson_return_if_fail (cursor );
182188
183- if (cursor -> rpc .reply .cursor_id ) {
189+ if (cursor -> in_exhaust ) {
190+ cursor -> client -> in_exhaust = FALSE;
191+
192+ if (!cursor -> done ) {
193+ _mongoc_cluster_disconnect_node (
194+ & cursor -> client -> cluster ,
195+ & cursor -> client -> cluster .nodes [cursor -> hint - 1 ]);
196+ }
197+ } else if (cursor -> rpc .reply .cursor_id ) {
184198 _mongoc_cursor_kill_cursor (cursor , cursor -> rpc .reply .cursor_id );
185199 }
186200
@@ -361,6 +375,11 @@ _mongoc_cursor_query (mongoc_cursor_t *cursor)
361375 cursor -> reader = bson_reader_new_from_data (cursor -> rpc .reply .documents ,
362376 cursor -> rpc .reply .documents_len );
363377
378+ if (cursor -> flags & MONGOC_QUERY_EXHAUST ) {
379+ cursor -> in_exhaust = TRUE;
380+ cursor -> client -> in_exhaust = TRUE;
381+ }
382+
364383 cursor -> done = FALSE;
365384 cursor -> end_of_event = FALSE;
366385 cursor -> sent = TRUE;
@@ -382,53 +401,57 @@ _mongoc_cursor_get_more (mongoc_cursor_t *cursor)
382401
383402 ENTRY ;
384403
385- bson_return_val_if_fail (cursor , FALSE );
404+ BSON_ASSERT (cursor );
386405
387- if (!_mongoc_client_warm_up (cursor -> client , & cursor -> error )) {
388- cursor -> failed = TRUE;
389- RETURN (FALSE);
390- }
406+ if (! cursor -> in_exhaust ) {
407+ if (!_mongoc_client_warm_up (cursor -> client , & cursor -> error )) {
408+ cursor -> failed = TRUE;
409+ RETURN (FALSE);
410+ }
391411
392- if (!(cursor_id = cursor -> rpc .reply .cursor_id )) {
393- bson_set_error (& cursor -> error ,
394- MONGOC_ERROR_CURSOR ,
395- MONGOC_ERROR_CURSOR_INVALID_CURSOR ,
396- "No valid cursor was provided." );
397- goto failure ;
398- }
412+ if (!(cursor_id = cursor -> rpc .reply .cursor_id )) {
413+ bson_set_error (& cursor -> error ,
414+ MONGOC_ERROR_CURSOR ,
415+ MONGOC_ERROR_CURSOR_INVALID_CURSOR ,
416+ "No valid cursor was provided." );
417+ goto failure ;
418+ }
419+
420+ rpc .get_more .msg_len = 0 ;
421+ rpc .get_more .request_id = 0 ;
422+ rpc .get_more .response_to = 0 ;
423+ rpc .get_more .opcode = MONGOC_OPCODE_GET_MORE ;
424+ rpc .get_more .zero = 0 ;
425+ rpc .get_more .collection = cursor -> ns ;
426+ if ((cursor -> flags & MONGOC_QUERY_TAILABLE_CURSOR )) {
427+ rpc .get_more .n_return = 0 ;
428+ } else {
429+ /*
430+ * TODO: We need to apply the limit to this so we don't
431+ * overshoot our target.
432+ */
433+ rpc .get_more .n_return = cursor -> batch_size ;
434+ }
435+ rpc .get_more .cursor_id = cursor_id ;
399436
400- rpc .get_more .msg_len = 0 ;
401- rpc .get_more .request_id = 0 ;
402- rpc .get_more .response_to = 0 ;
403- rpc .get_more .opcode = MONGOC_OPCODE_GET_MORE ;
404- rpc .get_more .zero = 0 ;
405- rpc .get_more .collection = cursor -> ns ;
406- if ((cursor -> flags & MONGOC_QUERY_TAILABLE_CURSOR )) {
407- rpc .get_more .n_return = 0 ;
408- } else {
409437 /*
410- * TODO: We need to apply the limit to this so we don't
411- * overshoot our target.
438+ * TODO: Stamp protections for disconnections.
412439 */
413- rpc .get_more .n_return = cursor -> batch_size ;
414- }
415- rpc .get_more .cursor_id = cursor_id ;
416440
417- /*
418- * TODO: Stamp protections for disconnections.
419- */
441+ if (!_mongoc_client_sendv (cursor -> client , & rpc , 1 , cursor -> hint ,
442+ NULL , cursor -> read_prefs , & cursor -> error )) {
443+ cursor -> done = TRUE;
444+ cursor -> failed = TRUE;
445+ RETURN (FALSE);
446+ }
420447
421- if (!_mongoc_client_sendv (cursor -> client , & rpc , 1 , cursor -> hint ,
422- NULL , cursor -> read_prefs , & cursor -> error )) {
423- cursor -> done = TRUE;
424- cursor -> failed = TRUE;
425- RETURN (FALSE);
448+ request_id = BSON_UINT32_FROM_LE (rpc .header .request_id );
449+ } else {
450+ request_id = BSON_UINT32_FROM_LE (cursor -> rpc .header .request_id );
426451 }
427452
428453 _mongoc_buffer_clear (& cursor -> buffer , FALSE);
429454
430- request_id = BSON_UINT32_FROM_LE (rpc .header .request_id );
431-
432455 if (!_mongoc_client_recv (cursor -> client ,
433456 & cursor -> rpc ,
434457 & cursor -> buffer ,
@@ -536,7 +559,17 @@ _mongoc_cursor_next (mongoc_cursor_t *cursor,
536559
537560 ENTRY ;
538561
539- bson_return_val_if_fail (cursor , FALSE);
562+ BSON_ASSERT (cursor );
563+
564+
565+ if (cursor -> client -> in_exhaust && ! cursor -> in_exhaust ) {
566+ bson_set_error (& cursor -> error ,
567+ MONGOC_ERROR_CLIENT ,
568+ MONGOC_ERROR_CLIENT_IN_EXHAUST ,
569+ "Another cursor derived from this client is in exhaust." );
570+ cursor -> failed = TRUE;
571+ RETURN (FALSE);
572+ }
540573
541574 if (bson ) {
542575 * bson = NULL ;
@@ -568,9 +601,11 @@ _mongoc_cursor_next (mongoc_cursor_t *cursor,
568601 eof = FALSE;
569602 b = bson_reader_read (cursor -> reader , & eof );
570603 cursor -> end_of_event = eof ;
571- cursor -> done = (cursor -> end_of_event &&
572- !b &&
573- !(cursor -> flags & MONGOC_QUERY_TAILABLE_CURSOR ));
604+
605+ cursor -> done = cursor -> end_of_event && (
606+ (cursor -> in_exhaust && !cursor -> rpc .reply .cursor_id ) ||
607+ (!b && !(cursor -> flags & MONGOC_QUERY_TAILABLE_CURSOR ))
608+ );
574609
575610 /*
576611 * Do a supplimental check to see if we had a corrupted reply in the
0 commit comments