Skip to content

Commit 3c8419f

Browse files
author
Christian Hergert
committed
bulk: Handle upsert on MongoDB < 2.6 with non-objectid _id fields.
Server versions before 2.6 only return the 'upserted' field in an update result when the upserted _id is an ObjectId. If the user provides any other value for _id in the query spec, or update document (for a replace operation) the upserted field is not returned. This causes invalid counts for nUpserted in the Bulk API result set. This fixes CDRIVER-372.
1 parent 09d4ff4 commit 3c8419f

File tree

2 files changed

+155
-0
lines changed

2 files changed

+155
-0
lines changed

src/mongoc/mongoc-write-command.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,23 @@ _mongoc_write_result_merge_legacy (mongoc_write_result_t *result, /* IN */
933933
}
934934
}
935935
}
936+
} else if ((n == 1) &&
937+
bson_iter_init_find (&iter, reply, "updatedExisting") &&
938+
BSON_ITER_HOLDS_BOOL (&iter) &&
939+
!bson_iter_bool (&iter)) {
940+
/*
941+
* CDRIVER-372:
942+
*
943+
* Versions of MongoDB before 2.6 don't return the _id for an
944+
* upsert if _id is not an ObjectId.
945+
*/
946+
result->nUpserted += 1;
947+
if (bson_iter_init_find (&iter, command->u.update.update, "_id") ||
948+
bson_iter_init_find (&iter, command->u.update.selector, "_id")) {
949+
value = bson_iter_value (&iter);
950+
_mongoc_write_result_append_upsert (result, result->n_commands,
951+
value);
952+
}
936953
} else {
937954
result->nMatched += n;
938955
}

tests/test-bulk.c

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,143 @@ test_index_offset (void)
292292
}
293293

294294

295+
static void
296+
test_bulk_edge_case_372 (void)
297+
{
298+
mongoc_client_t *client;
299+
mongoc_collection_t *collection;
300+
mongoc_bulk_operation_t *bulk;
301+
bson_error_t error;
302+
bson_iter_t iter;
303+
bson_iter_t citer;
304+
bson_iter_t child;
305+
const char *str;
306+
bson_t *selector;
307+
bson_t *update;
308+
bson_t reply;
309+
bool r;
310+
int count;
311+
int vmaj = 0;
312+
int vmin = 0;
313+
int vmic = 0;
314+
315+
client = mongoc_client_new (gTestUri);
316+
assert (client);
317+
318+
collection = get_test_collection (client, "CDRIVER_372");
319+
assert (collection);
320+
321+
bulk = mongoc_collection_create_bulk_operation (collection, true, NULL);
322+
assert (bulk);
323+
324+
selector = BCON_NEW ("_id", BCON_INT32 (0));
325+
update = BCON_NEW ("$set", "{", "a", BCON_INT32 (0), "}");
326+
mongoc_bulk_operation_update_one (bulk, selector, update, true);
327+
bson_destroy (selector);
328+
bson_destroy (update);
329+
330+
selector = BCON_NEW ("a", BCON_INT32 (1));
331+
update = BCON_NEW ("_id", BCON_INT32 (1));
332+
mongoc_bulk_operation_replace_one (bulk, selector, update, true);
333+
bson_destroy (selector);
334+
bson_destroy (update);
335+
336+
r = mongoc_client_get_server_status (client, NULL, &reply, &error);
337+
if (!r) fprintf (stderr, "%s\n", error.message);
338+
assert (r);
339+
340+
if (bson_iter_init_find (&iter, &reply, "version") &&
341+
BSON_ITER_HOLDS_UTF8 (&iter) &&
342+
(str = bson_iter_utf8 (&iter, NULL))) {
343+
sscanf (str, "%d.%d.%d", &vmaj, &vmin, &vmic);
344+
}
345+
346+
bson_destroy (&reply);
347+
348+
if (vmaj >=2 || (vmaj == 2 && vmin >= 6)) {
349+
/* This is just here to make the counts right in all cases. */
350+
selector = BCON_NEW ("_id", BCON_INT32 (2));
351+
update = BCON_NEW ("_id", BCON_INT32 (2));
352+
mongoc_bulk_operation_replace_one (bulk, selector, update, true);
353+
bson_destroy (selector);
354+
bson_destroy (update);
355+
} else {
356+
/* This case is only possible in MongoDB versions before 2.6. */
357+
selector = BCON_NEW ("_id", BCON_INT32 (3));
358+
update = BCON_NEW ("_id", BCON_INT32 (2));
359+
mongoc_bulk_operation_replace_one (bulk, selector, update, true);
360+
bson_destroy (selector);
361+
bson_destroy (update);
362+
}
363+
364+
r = mongoc_bulk_operation_execute (bulk, &reply, &error);
365+
if (!r) fprintf (stderr, "%s\n", error.message);
366+
assert (r);
367+
368+
#if 0
369+
printf ("%s\n", bson_as_json (&reply, NULL));
370+
#endif
371+
372+
assert (bson_iter_init_find (&iter, &reply, "nMatched") &&
373+
BSON_ITER_HOLDS_INT32 (&iter) &&
374+
(0 == bson_iter_int32 (&iter)));
375+
assert (bson_iter_init_find (&iter, &reply, "nUpserted") &&
376+
BSON_ITER_HOLDS_INT32 (&iter) &&
377+
(3 == bson_iter_int32 (&iter)));
378+
assert (bson_iter_init_find (&iter, &reply, "nInserted") &&
379+
BSON_ITER_HOLDS_INT32 (&iter) &&
380+
(0 == bson_iter_int32 (&iter)));
381+
assert (bson_iter_init_find (&iter, &reply, "nRemoved") &&
382+
BSON_ITER_HOLDS_INT32 (&iter) &&
383+
(0 == bson_iter_int32 (&iter)));
384+
385+
assert (bson_iter_init_find (&iter, &reply, "upserted") &&
386+
BSON_ITER_HOLDS_ARRAY (&iter) &&
387+
bson_iter_recurse (&iter, &citer));
388+
389+
assert (bson_iter_next (&citer));
390+
assert (BSON_ITER_HOLDS_DOCUMENT (&citer));
391+
assert (bson_iter_recurse (&citer, &child));
392+
assert (bson_iter_find (&child, "_id"));
393+
assert (BSON_ITER_HOLDS_INT32 (&child));
394+
assert (0 == bson_iter_int32 (&child));
395+
assert (bson_iter_recurse (&citer, &child));
396+
assert (bson_iter_find (&child, "index"));
397+
assert (BSON_ITER_HOLDS_INT32 (&child));
398+
assert (0 == bson_iter_int32 (&child));
399+
400+
assert (bson_iter_next (&citer));
401+
assert (BSON_ITER_HOLDS_DOCUMENT (&citer));
402+
assert (bson_iter_recurse (&citer, &child));
403+
assert (bson_iter_find (&child, "_id"));
404+
assert (BSON_ITER_HOLDS_INT32 (&child));
405+
assert (1 == bson_iter_int32 (&child));
406+
assert (bson_iter_recurse (&citer, &child));
407+
assert (bson_iter_find (&child, "index"));
408+
assert (BSON_ITER_HOLDS_INT32 (&child));
409+
assert (1 == bson_iter_int32 (&child));
410+
411+
assert (bson_iter_next (&citer));
412+
assert (BSON_ITER_HOLDS_DOCUMENT (&citer));
413+
assert (bson_iter_recurse (&citer, &child));
414+
assert (bson_iter_find (&child, "_id"));
415+
assert (BSON_ITER_HOLDS_INT32 (&child));
416+
assert (2 == bson_iter_int32 (&child));
417+
assert (bson_iter_recurse (&citer, &child));
418+
assert (bson_iter_find (&child, "index"));
419+
assert (BSON_ITER_HOLDS_INT32 (&child));
420+
assert (2 == bson_iter_int32 (&child));
421+
422+
assert (!bson_iter_next (&citer));
423+
424+
bson_destroy (&reply);
425+
426+
mongoc_bulk_operation_destroy (bulk);
427+
mongoc_collection_destroy (collection);
428+
mongoc_client_destroy (client);
429+
}
430+
431+
295432
void
296433
test_bulk_install (TestSuite *suite)
297434
{
@@ -300,6 +437,7 @@ test_bulk_install (TestSuite *suite)
300437
TestSuite_Add (suite, "/BulkOperation/basic", test_bulk);
301438
TestSuite_Add (suite, "/BulkOperation/update_upserted", test_update_upserted);
302439
TestSuite_Add (suite, "/BulkOperation/index_offset", test_index_offset);
440+
TestSuite_Add (suite, "/BulkOperation/CDRIVER-372", test_bulk_edge_case_372);
303441

304442
atexit (cleanup_globals);
305443
}

0 commit comments

Comments
 (0)