Skip to content

Commit 5ea44ed

Browse files
committed
CDRIVER-901 update bson_error_t from write concern err
1 parent 971f6fa commit 5ea44ed

File tree

3 files changed

+192
-48
lines changed

3 files changed

+192
-48
lines changed

src/mongoc/mongoc-error.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ typedef enum
4444
MONGOC_ERROR_GRIDFS,
4545
MONGOC_ERROR_SCRAM,
4646
MONGOC_ERROR_SERVER_SELECTION,
47+
MONGOC_ERROR_WRITE_CONCERN,
4748
} mongoc_error_domain_t;
4849

4950

src/mongoc/mongoc-write-command.c

Lines changed: 86 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17+
#include <bson.h>
1718

1819
#include "mongoc-client-private.h"
1920
#include "mongoc-error.h"
@@ -1330,26 +1331,86 @@ _mongoc_write_result_merge (mongoc_write_result_t *result, /* IN */
13301331
}
13311332

13321333

1334+
/*
1335+
* If error is not set, set code from first document in array like
1336+
* [{"code": 64, "errmsg": "duplicate"}, ...]. Format the error message
1337+
* from all errors in array.
1338+
*/
1339+
static void
1340+
_set_error_from_response (bson_t *bson_array,
1341+
mongoc_error_domain_t domain,
1342+
const char *error_type,
1343+
bson_error_t *error /* OUT */)
1344+
{
1345+
bson_iter_t array_iter;
1346+
bson_iter_t doc_iter;
1347+
bson_string_t *compound_err;
1348+
const char *errmsg = NULL;
1349+
int32_t code = 0;
1350+
uint32_t n_keys, i;
1351+
1352+
compound_err = bson_string_new (NULL);
1353+
n_keys = bson_count_keys (bson_array);
1354+
if (n_keys > 1) {
1355+
bson_string_append_printf (compound_err,
1356+
"Multiple %s errors: ",
1357+
error_type);
1358+
}
1359+
1360+
if (!bson_empty0 (bson_array) && bson_iter_init (&array_iter, bson_array)) {
1361+
1362+
/* get first code and all error messages */
1363+
i = 0;
1364+
1365+
while (bson_iter_next (&array_iter)) {
1366+
if (BSON_ITER_HOLDS_DOCUMENT (&array_iter) &&
1367+
bson_iter_recurse (&array_iter, &doc_iter)) {
1368+
1369+
/* parse doc, which is like {"code": 64, "errmsg": "duplicate"} */
1370+
while (bson_iter_next (&doc_iter)) {
1371+
1372+
/* use the first error code we find */
1373+
if (BSON_ITER_IS_KEY (&doc_iter, "code") && code == 0) {
1374+
code = bson_iter_int32 (&doc_iter);
1375+
} else if (BSON_ITER_IS_KEY (&doc_iter, "errmsg")) {
1376+
errmsg = bson_iter_utf8 (&doc_iter, NULL);
1377+
1378+
/* build message like 'Multiple write errors: "foo", "bar"' */
1379+
if (n_keys > 1) {
1380+
bson_string_append_printf (compound_err, "\"%s\"", errmsg);
1381+
if (i < n_keys - 1) {
1382+
bson_string_append (compound_err, ", ");
1383+
}
1384+
} else {
1385+
/* single error message */
1386+
bson_string_append (compound_err, errmsg);
1387+
}
1388+
}
1389+
}
1390+
1391+
i++;
1392+
}
1393+
}
1394+
1395+
if (code && compound_err->len) {
1396+
bson_set_error (error, domain, (uint32_t) code,
1397+
"%s", compound_err->str);
1398+
}
1399+
}
1400+
1401+
bson_string_free (compound_err, true);
1402+
}
1403+
1404+
13331405
bool
13341406
_mongoc_write_result_complete (mongoc_write_result_t *result,
13351407
bson_t *bson,
13361408
bson_error_t *error)
13371409
{
1338-
bson_iter_t iter;
1339-
bson_iter_t citer;
1340-
const char *err = NULL;
1341-
uint32_t code = 0;
1342-
bool ret;
1343-
13441410
ENTRY;
13451411

13461412
BSON_ASSERT (result);
13471413

1348-
ret = (!result->failed &&
1349-
/* TODO: not to spec */
1350-
bson_empty0 (&result->writeConcernErrors) &&
1351-
bson_empty0 (&result->writeErrors));
1352-
13531414
if (bson) {
13541415
BSON_APPEND_INT32 (bson, "nInserted", result->nInserted);
13551416
BSON_APPEND_INT32 (bson, "nMatched", result->nMatched);
@@ -1368,27 +1429,22 @@ _mongoc_write_result_complete (mongoc_write_result_t *result,
13681429
}
13691430
}
13701431

1371-
if (error) {
1372-
memcpy (error, &result->error, sizeof *error);
1432+
/* set bson_error_t from first write error or write concern error */
1433+
_set_error_from_response (&result->writeErrors,
1434+
MONGOC_ERROR_COMMAND,
1435+
"write",
1436+
&result->error);
1437+
1438+
if (!result->error.code) {
1439+
_set_error_from_response (&result->writeConcernErrors,
1440+
MONGOC_ERROR_WRITE_CONCERN,
1441+
"write concern",
1442+
&result->error);
13731443
}
13741444

1375-
if (!ret &&
1376-
!bson_empty0 (&result->writeErrors) &&
1377-
bson_iter_init (&iter, &result->writeErrors) &&
1378-
bson_iter_next (&iter) &&
1379-
BSON_ITER_HOLDS_DOCUMENT (&iter) &&
1380-
bson_iter_recurse (&iter, &citer)) {
1381-
while (bson_iter_next (&citer)) {
1382-
if (BSON_ITER_IS_KEY (&citer, "errmsg")) {
1383-
err = bson_iter_utf8 (&citer, NULL);
1384-
} else if (BSON_ITER_IS_KEY (&citer, "code")) {
1385-
code = bson_iter_int32 (&citer);
1386-
}
1387-
}
1388-
if (err && code) {
1389-
bson_set_error (error, MONGOC_ERROR_COMMAND, code, "%s", err);
1390-
}
1445+
if (error) {
1446+
memcpy (error, &result->error, sizeof *error);
13911447
}
13921448

1393-
RETURN (ret);
1449+
RETURN (!result->failed && result->error.code == 0);
13941450
}

tests/test-bulk.c

Lines changed: 105 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1528,7 +1528,7 @@ test_single_error_unordered_bulk ()
15281528

15291529

15301530
static void
1531-
_test_write_concern (bool has_write_commands, bool ordered)
1531+
_test_write_concern (bool has_write_commands, bool ordered, bool multi_err)
15321532
{
15331533
mock_server_t *mock_server;
15341534
mongoc_client_t *client;
@@ -1539,6 +1539,8 @@ _test_write_concern (bool has_write_commands, bool ordered)
15391539
bson_error_t error;
15401540
future_t *future;
15411541
request_t *request;
1542+
int32_t first_err;
1543+
int32_t second_err;
15421544

15431545
/* set wire protocol version for legacy writes or write commands */
15441546
mock_server = mock_server_with_autoismaster (has_write_commands ? 3 : 0);
@@ -1550,6 +1552,7 @@ _test_write_concern (bool has_write_commands, bool ordered)
15501552
mongoc_write_concern_set_wtimeout (wc, 100);
15511553
bulk = mongoc_collection_create_bulk_operation (collection, ordered, wc);
15521554
mongoc_bulk_operation_insert (bulk, tmp_bson ("{'_id': 1}"));
1555+
mongoc_bulk_operation_remove (bulk, tmp_bson ("{'_id': 2}"));
15531556

15541557
future = future_bulk_operation_execute (bulk, &reply, &error);
15551558

@@ -1565,43 +1568,109 @@ _test_write_concern (bool has_write_commands, bool ordered)
15651568
ordered ? "true" : "false");
15661569

15671570
assert (request);
1568-
mock_server_replies (
1569-
request, 0, 0, 0, 1,
1571+
mock_server_replies_simple (
1572+
request,
15701573
"{'ok': 1.0, 'n': 1, "
15711574
" 'writeConcernError': {'code': 17, 'errmsg': 'foo'}}");
1575+
1576+
request_destroy (request);
1577+
request = mock_server_receives_command (
1578+
mock_server,
1579+
"test",
1580+
MONGOC_QUERY_NONE,
1581+
"{'delete': 'test',"
1582+
" 'writeConcern': {'w': 2, 'wtimeout': 100},"
1583+
" 'ordered': %s,"
1584+
" 'deletes': [{'q': {'_id': 2}, 'limit': 0}]}",
1585+
ordered ? "true" : "false");
1586+
1587+
if (multi_err) {
1588+
mock_server_replies_simple (
1589+
request,
1590+
"{'ok': 1.0, 'n': 1, "
1591+
" 'writeConcernError': {'code': 42, 'errmsg': 'bar'}}");
1592+
} else {
1593+
mock_server_replies_simple (request, "{'ok': 1.0, 'n': 1}");
1594+
}
1595+
1596+
request_destroy (request);
1597+
1598+
/* server fictionally returns 17 and 42; expect driver to use first one */
1599+
first_err = 17;
1600+
second_err = 42;
15721601
} else {
15731602
request = mock_server_receives_insert (
15741603
mock_server, "test.test", MONGOC_INSERT_NONE, "{'_id': 1}");
15751604

15761605
request_destroy (request);
1577-
15781606
request = mock_server_receives_command (
15791607
mock_server,
15801608
"test",
15811609
MONGOC_QUERY_NONE,
15821610
"{'getLastError': 1, 'w': 2, 'wtimeout': 100}");
15831611

15841612
assert (request);
1585-
mock_server_replies (
1586-
request, 0, 0, 0, 1,
1587-
"{'ok': 1.0, 'n': 0, 'err': 'foo', 'wtimeout': true}");
1613+
mock_server_replies_simple (
1614+
request, "{'ok': 1.0, 'n': 0, 'err': 'foo', 'wtimeout': true}");
1615+
1616+
request = mock_server_receives_delete (
1617+
mock_server, "test.test", MONGOC_REMOVE_NONE, "{'_id': 1}");
1618+
1619+
request_destroy (request);
1620+
request = mock_server_receives_command (
1621+
mock_server,
1622+
"test",
1623+
MONGOC_QUERY_NONE,
1624+
"{'getLastError': 1, 'w': 2, 'wtimeout': 100}");
1625+
1626+
if (multi_err) {
1627+
mock_server_replies_simple (
1628+
request, "{'ok': 1.0, 'n': 0, 'err': 'bar', 'wtimeout': true}");
1629+
} else {
1630+
mock_server_replies_simple (request, "{'ok': 1.0, 'n': 1}");
1631+
}
1632+
1633+
request_destroy (request);
1634+
1635+
/* The client makes up the error code for legacy writes */
1636+
first_err = second_err = 64;
15881637
}
15891638

15901639
/* join thread, assert mongoc_bulk_operation_execute () returned 0 */
15911640
assert (!future_get_uint32_t (future));
15921641

1593-
ASSERT_MATCH (&reply, "{'nInserted': 1,"
1594-
" 'nMatched': 0,"
1595-
" 'nRemoved': 0,"
1596-
" 'nUpserted': 0,"
1597-
" 'writeErrors': [],"
1598-
" 'writeConcernErrors': ["
1599-
" {'code': %d, 'errmsg': 'foo'}]}",
1600-
has_write_commands ? 17 : 64);
1642+
if (multi_err) {
1643+
ASSERT_MATCH (&reply,
1644+
"{'nInserted': 1,"
1645+
" 'nMatched': 0,"
1646+
" 'nRemoved': 1,"
1647+
" 'nUpserted': 0,"
1648+
" 'writeErrors': [],"
1649+
" 'writeConcernErrors': ["
1650+
" {'code': %d, 'errmsg': 'foo'},"
1651+
" {'code': %d, 'errmsg': 'bar'}]}",
1652+
first_err, second_err);
1653+
1654+
ASSERT_CMPSTR ("Multiple write concern errors: \"foo\", \"bar\"",
1655+
error.message);
1656+
} else {
1657+
ASSERT_MATCH (&reply,
1658+
"{'nInserted': 1,"
1659+
" 'nMatched': 0,"
1660+
" 'nRemoved': 1,"
1661+
" 'nUpserted': 0,"
1662+
" 'writeErrors': [],"
1663+
" 'writeConcernErrors': ["
1664+
" {'code': %d, 'errmsg': 'foo'}]}",
1665+
first_err);
1666+
ASSERT_CMPSTR ("foo", error.message);
1667+
}
16011668

16021669
check_n_modified (has_write_commands, &reply, 0);
16031670

1604-
request_destroy (request);
1671+
ASSERT_CMPINT (MONGOC_ERROR_WRITE_CONCERN, ==, error.domain);
1672+
ASSERT_CMPINT (first_err, ==, error.code);
1673+
16051674
future_destroy (future);
16061675
bson_destroy (&reply);
16071676
mongoc_bulk_operation_destroy (bulk);
@@ -1630,14 +1699,28 @@ test_write_concern_legacy_unordered (void)
16301699
static void
16311700
test_write_concern_write_command_ordered (void)
16321701
{
1633-
_test_write_concern (true, true);
1702+
_test_write_concern (true, true, false);
1703+
}
1704+
1705+
1706+
static void
1707+
test_write_concern_write_command_ordered_multi_err (void)
1708+
{
1709+
_test_write_concern (true, true, true);
16341710
}
16351711

16361712

16371713
static void
16381714
test_write_concern_write_command_unordered (void)
16391715
{
1640-
_test_write_concern (true, false);
1716+
_test_write_concern (true, false, false);
1717+
}
1718+
1719+
1720+
static void
1721+
test_write_concern_write_command_unordered_multi_err (void)
1722+
{
1723+
_test_write_concern (true, false, true);
16411724
}
16421725

16431726

@@ -2549,8 +2632,12 @@ test_bulk_install (TestSuite *suite)
25492632
#endif
25502633
TestSuite_Add (suite, "/BulkOperation/write_concern/write_command/ordered",
25512634
test_write_concern_write_command_ordered);
2635+
TestSuite_Add (suite, "/BulkOperation/write_concern/write_command/ordered/multi_err",
2636+
test_write_concern_write_command_ordered_multi_err);
25522637
TestSuite_Add (suite, "/BulkOperation/write_concern/write_command/unordered",
25532638
test_write_concern_write_command_unordered);
2639+
TestSuite_Add (suite, "/BulkOperation/write_concern/write_command/unordered/multi_err",
2640+
test_write_concern_write_command_unordered_multi_err);
25542641
TestSuite_Add (suite, "/BulkOperation/multiple_error_unordered_bulk",
25552642
test_multiple_error_unordered_bulk);
25562643
#ifdef TODO_CDRIVER_707

0 commit comments

Comments
 (0)