Skip to content

Commit 79964c9

Browse files
committed
CDRIVER-5515 fix assert when legacy exhaust cursor receives no documents (#1562)
* add regression test Create an exhaust cursor with empty results. Fails an assertion when using legacy op codes. * CDRIVER-5515 handle NULL for OP_REPLY documents Avoids an assertion failure in bson_reader_new_from_data if a legacy exhaust cursor receives an empty batch of documents.
1 parent cc8754a commit 79964c9

File tree

2 files changed

+57
-6
lines changed

2 files changed

+57
-6
lines changed

src/libmongoc/src/mongoc/mongoc-cursor-legacy.c

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,14 @@ _mongoc_cursor_op_getmore (mongoc_cursor_t *cursor,
244244

245245
cursor->cursor_id = mcd_rpc_op_reply_get_cursor_id (response->rpc);
246246

247-
response->reader = bson_reader_new_from_data (
248-
mcd_rpc_op_reply_get_documents (response->rpc),
249-
mcd_rpc_op_reply_get_documents_len (response->rpc));
247+
const void *documents = mcd_rpc_op_reply_get_documents (response->rpc);
248+
if (documents == NULL) {
249+
// Use a non-NULL pointer to satisfy precondition of
250+
// `bson_reader_new_from_data`:
251+
documents = "";
252+
}
253+
254+
response->reader = bson_reader_new_from_data (documents, mcd_rpc_op_reply_get_documents_len (response->rpc));
250255

251256
_mongoc_cursor_monitor_succeeded (cursor,
252257
response,
@@ -634,9 +639,14 @@ _mongoc_cursor_op_query_find (mongoc_cursor_t *cursor,
634639

635640
cursor->cursor_id = mcd_rpc_op_reply_get_cursor_id (response->rpc);
636641

637-
response->reader = bson_reader_new_from_data (
638-
mcd_rpc_op_reply_get_documents (response->rpc),
639-
mcd_rpc_op_reply_get_documents_len (response->rpc));
642+
const void *documents = mcd_rpc_op_reply_get_documents (response->rpc);
643+
if (documents == NULL) {
644+
// Use a non-NULL pointer to satisfy precondition of
645+
// `bson_reader_new_from_data`:
646+
documents = "";
647+
}
648+
649+
response->reader = bson_reader_new_from_data (documents, mcd_rpc_op_reply_get_documents_len (response->rpc));
640650

641651
if (_mongoc_cursor_get_opt_bool (cursor, MONGOC_CURSOR_EXHAUST)) {
642652
cursor->in_exhaust = true;

src/libmongoc/tests/test-mongoc-exhaust.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,45 @@ test_exhaust_cursor_works (void *context)
375375
mongoc_client_destroy (client);
376376
}
377377

378+
// `test_exhaust_cursor_no_match` is a regression test for CDRIVER-5515
379+
static void
380+
test_exhaust_cursor_no_match (void *context)
381+
{
382+
BSON_UNUSED (context);
383+
bson_error_t error;
384+
mongoc_client_t *client = test_framework_new_default_client ();
385+
mongoc_collection_t *coll = mongoc_client_get_collection (client, "db", "coll");
386+
387+
// Drop collection to remove prior test data.
388+
mongoc_collection_drop (coll, NULL /* ignore error */);
389+
390+
mongoc_cursor_t *cursor =
391+
mongoc_collection_find_with_opts (coll, tmp_bson ("{}"), tmp_bson ("{'exhaust': true }"), NULL /* read_prefs */);
392+
const bson_t *result;
393+
size_t count = 0;
394+
while (mongoc_cursor_next (cursor, &result)) {
395+
count++;
396+
}
397+
// Expect an error if exhaust cursors are not supported.
398+
const bool sharded = _mongoc_topology_get_type (cursor->client->topology) == MONGOC_TOPOLOGY_SHARDED;
399+
int64_t wire_version;
400+
test_framework_get_max_wire_version (&wire_version);
401+
if (sharded && wire_version < WIRE_VERSION_MONGOS_EXHAUST) {
402+
ASSERT (mongoc_cursor_error (cursor, &error));
403+
ASSERT_ERROR_CONTAINS (error, MONGOC_ERROR_CURSOR, MONGOC_ERROR_CURSOR_INVALID_CURSOR, "exhaust cursors require");
404+
} else {
405+
// Expect no error.
406+
ASSERT_OR_PRINT (!mongoc_cursor_error (cursor, &error), error);
407+
// Expect no results.
408+
ASSERT_CMPSIZE_T (count, ==, 0);
409+
}
410+
411+
mongoc_cursor_destroy (cursor);
412+
mongoc_collection_destroy (coll);
413+
mongoc_client_destroy (client);
414+
}
415+
416+
378417
static void
379418
test_exhaust_cursor_single (void *context)
380419
{
@@ -795,6 +834,8 @@ test_exhaust_install (TestSuite *suite)
795834
NULL,
796835
NULL,
797836
skip_if_no_exhaust);
837+
TestSuite_AddFull (
838+
suite, "/Client/exhaust_cursor/no_match", test_exhaust_cursor_no_match, NULL, NULL, skip_if_no_exhaust);
798839
TestSuite_AddFull (suite,
799840
"/Client/exhaust_cursor/single",
800841
test_exhaust_cursor_single,

0 commit comments

Comments
 (0)