Skip to content

Commit 0ddbd4d

Browse files
authored
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 930d171 commit 0ddbd4d

File tree

2 files changed

+57
-4
lines changed

2 files changed

+57
-4
lines changed

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

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,14 @@ _mongoc_cursor_op_getmore (mongoc_cursor_t *cursor, mongoc_cursor_response_legac
222222

223223
cursor->cursor_id = mcd_rpc_op_reply_get_cursor_id (response->rpc);
224224

225-
response->reader = bson_reader_new_from_data (mcd_rpc_op_reply_get_documents (response->rpc),
226-
mcd_rpc_op_reply_get_documents_len (response->rpc));
225+
const void *documents = mcd_rpc_op_reply_get_documents (response->rpc);
226+
if (documents == NULL) {
227+
// Use a non-NULL pointer to satisfy precondition of
228+
// `bson_reader_new_from_data`:
229+
documents = "";
230+
}
231+
232+
response->reader = bson_reader_new_from_data (documents, mcd_rpc_op_reply_get_documents_len (response->rpc));
227233

228234
_mongoc_cursor_monitor_succeeded (cursor,
229235
response,
@@ -584,8 +590,14 @@ _mongoc_cursor_op_query_find (mongoc_cursor_t *cursor, bson_t *filter, mongoc_cu
584590

585591
cursor->cursor_id = mcd_rpc_op_reply_get_cursor_id (response->rpc);
586592

587-
response->reader = bson_reader_new_from_data (mcd_rpc_op_reply_get_documents (response->rpc),
588-
mcd_rpc_op_reply_get_documents_len (response->rpc));
593+
const void *documents = mcd_rpc_op_reply_get_documents (response->rpc);
594+
if (documents == NULL) {
595+
// Use a non-NULL pointer to satisfy precondition of
596+
// `bson_reader_new_from_data`:
597+
documents = "";
598+
}
599+
600+
response->reader = bson_reader_new_from_data (documents, mcd_rpc_op_reply_get_documents_len (response->rpc));
589601

590602
if (_mongoc_cursor_get_opt_bool (cursor, MONGOC_CURSOR_EXHAUST)) {
591603
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
@@ -345,6 +345,45 @@ test_exhaust_cursor_works (void *context)
345345
mongoc_client_destroy (client);
346346
}
347347

348+
// `test_exhaust_cursor_no_match` is a regression test for CDRIVER-5515
349+
static void
350+
test_exhaust_cursor_no_match (void *context)
351+
{
352+
BSON_UNUSED (context);
353+
bson_error_t error;
354+
mongoc_client_t *client = test_framework_new_default_client ();
355+
mongoc_collection_t *coll = mongoc_client_get_collection (client, "db", "coll");
356+
357+
// Drop collection to remove prior test data.
358+
mongoc_collection_drop (coll, NULL /* ignore error */);
359+
360+
mongoc_cursor_t *cursor =
361+
mongoc_collection_find_with_opts (coll, tmp_bson ("{}"), tmp_bson ("{'exhaust': true }"), NULL /* read_prefs */);
362+
const bson_t *result;
363+
size_t count = 0;
364+
while (mongoc_cursor_next (cursor, &result)) {
365+
count++;
366+
}
367+
// Expect an error if exhaust cursors are not supported.
368+
const bool sharded = _mongoc_topology_get_type (cursor->client->topology) == MONGOC_TOPOLOGY_SHARDED;
369+
int64_t wire_version;
370+
test_framework_get_max_wire_version (&wire_version);
371+
if (sharded && wire_version < WIRE_VERSION_MONGOS_EXHAUST) {
372+
ASSERT (mongoc_cursor_error (cursor, &error));
373+
ASSERT_ERROR_CONTAINS (error, MONGOC_ERROR_CURSOR, MONGOC_ERROR_CURSOR_INVALID_CURSOR, "exhaust cursors require");
374+
} else {
375+
// Expect no error.
376+
ASSERT_OR_PRINT (!mongoc_cursor_error (cursor, &error), error);
377+
// Expect no results.
378+
ASSERT_CMPSIZE_T (count, ==, 0);
379+
}
380+
381+
mongoc_cursor_destroy (cursor);
382+
mongoc_collection_destroy (coll);
383+
mongoc_client_destroy (client);
384+
}
385+
386+
348387
static void
349388
test_exhaust_cursor_single (void *context)
350389
{
@@ -709,6 +748,8 @@ void
709748
test_exhaust_install (TestSuite *suite)
710749
{
711750
TestSuite_AddFull (suite, "/Client/exhaust_cursor/works", test_exhaust_cursor_works, NULL, NULL, skip_if_no_exhaust);
751+
TestSuite_AddFull (
752+
suite, "/Client/exhaust_cursor/no_match", test_exhaust_cursor_no_match, NULL, NULL, skip_if_no_exhaust);
712753
TestSuite_AddFull (
713754
suite, "/Client/exhaust_cursor/single", test_exhaust_cursor_single, NULL, NULL, skip_if_no_exhaust);
714755
TestSuite_AddFull (suite, "/Client/exhaust_cursor/pool", test_exhaust_cursor_pool, NULL, NULL, skip_if_no_exhaust);

0 commit comments

Comments
 (0)