Skip to content

Commit c4af4aa

Browse files
committed
CDRIVER-2664 prohibit arrayFilters w/ old MongoDB
1 parent bd44fdc commit c4af4aa

File tree

8 files changed

+190
-58
lines changed

8 files changed

+190
-58
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Next Release
66
error replies.
77
* All "destroy" functions such as bson_destroy or mongoc_collection_destroy
88
now ignore a NULL argument.
9+
* The driver now returns an error if you attempt to use "arrayFilters" in an
10+
update with a MongoDB server older than 3.6.
911
* Update functions include a new "upsertedCount" field in the reply document.
1012
* Functions incorrectly marked with the "const" compiler attribute are now
1113
marked as "pure".

src/libmongoc/src/mongoc/mongoc-bulk-operation.c

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -395,17 +395,24 @@ _mongoc_bulk_operation_update_append (
395395
const bson_t *selector,
396396
const bson_t *document,
397397
const mongoc_bulk_update_opts_t *update_opts,
398+
const bson_t *array_filters,
398399
const bson_t *extra_opts)
399400
{
400401
mongoc_write_command_t command = {0};
401402
mongoc_write_command_t *last;
402403
bson_t opts;
403404
bool has_collation;
405+
bool has_array_filters;
404406

405407
bson_init (&opts);
406408
bson_append_bool (&opts, "upsert", 6, update_opts->upsert);
407409
bson_append_bool (&opts, "multi", 5, update_opts->multi);
408410

411+
has_array_filters = !bson_empty0 (array_filters);
412+
if (has_array_filters) {
413+
bson_append_document (&opts, "arrayFilters", 12, array_filters);
414+
}
415+
409416
has_collation = !bson_empty (&update_opts->collation);
410417
if (has_collation) {
411418
bson_append_document (&opts, "collation", 9, &update_opts->collation);
@@ -430,6 +437,7 @@ _mongoc_bulk_operation_update_append (
430437
_mongoc_write_command_init_update (
431438
&command, selector, document, &opts, bulk->flags, bulk->operation_id);
432439

440+
command.flags.has_array_filters = has_array_filters;
433441
command.flags.has_collation = has_collation;
434442
command.flags.has_multi_write = update_opts->multi;
435443

@@ -443,6 +451,7 @@ _mongoc_bulk_operation_update_with_opts (
443451
const bson_t *selector,
444452
const bson_t *document,
445453
const mongoc_bulk_update_opts_t *update_opts,
454+
const bson_t *array_filters,
446455
const bson_t *extra_opts,
447456
bool multi,
448457
bson_error_t *error) /* OUT */
@@ -470,7 +479,7 @@ _mongoc_bulk_operation_update_with_opts (
470479
}
471480

472481
_mongoc_bulk_operation_update_append (
473-
bulk, selector, document, update_opts, extra_opts);
482+
bulk, selector, document, update_opts, array_filters, extra_opts);
474483

475484
RETURN (true);
476485
}
@@ -495,15 +504,11 @@ mongoc_bulk_operation_update_one_with_opts (mongoc_bulk_operation_t *bulk,
495504
RETURN (false);
496505
}
497506

498-
if (!bson_empty (&update_opts.arrayFilters)) {
499-
bson_append_array (
500-
&update_opts.extra, "arrayFilters", 12, &update_opts.arrayFilters);
501-
}
502-
503507
ret = _mongoc_bulk_operation_update_with_opts (bulk,
504508
selector,
505509
document,
506510
&update_opts.update,
511+
&update_opts.arrayFilters,
507512
&update_opts.extra,
508513
false /* multi */,
509514
error);
@@ -532,15 +537,11 @@ mongoc_bulk_operation_update_many_with_opts (mongoc_bulk_operation_t *bulk,
532537
RETURN (false);
533538
}
534539

535-
if (!bson_empty (&update_opts.arrayFilters)) {
536-
bson_append_array (
537-
&update_opts.extra, "arrayFilters", 12, &update_opts.arrayFilters);
538-
}
539-
540540
ret = _mongoc_bulk_operation_update_with_opts (bulk,
541541
selector,
542542
document,
543543
&update_opts.update,
544+
&update_opts.arrayFilters,
544545
&update_opts.extra,
545546
true /* multi */,
546547
error);
@@ -647,7 +648,7 @@ mongoc_bulk_operation_replace_one_with_opts (mongoc_bulk_operation_t *bulk,
647648
}
648649

649650
_mongoc_bulk_operation_update_append (
650-
bulk, selector, document, update_opts, &repl_opts.extra);
651+
bulk, selector, document, update_opts, NULL, &repl_opts.extra);
651652
ret = true;
652653

653654
done:

src/libmongoc/src/mongoc/mongoc-client-private.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ BSON_BEGIN_DECLS
6161
#define WIRE_VERSION_COLLATION 5
6262
/* first version to support OP_MSG */
6363
#define WIRE_VERSION_OP_MSG 6
64+
/* first version to support array filters for "update" command */
65+
#define WIRE_VERSION_ARRAY_FILTERS 6
6466
/* first version to support retryable writes */
6567
#define WIRE_VERSION_RETRY_WRITES 6
6668

src/libmongoc/src/mongoc/mongoc-collection.c

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2050,13 +2050,16 @@ _mongoc_collection_update_or_replace (
20502050
const bson_t *update,
20512051
mongoc_update_opts_t *update_opts,
20522052
mongoc_write_bypass_document_validation_t bypass,
2053+
const bson_t *array_filters,
20532054
bson_t *extra,
20542055
bson_t *reply,
20552056
bson_error_t *error)
20562057
{
20572058
mongoc_write_command_t command;
20582059
mongoc_write_result_t result;
2059-
bool ret;
2060+
mongoc_server_stream_t *server_stream = NULL;
2061+
bool reply_initialized = false;
2062+
bool ret = false;
20602063

20612064
ENTRY;
20622065

@@ -2073,6 +2076,13 @@ _mongoc_collection_update_or_replace (
20732076
bson_append_document (extra, "collation", 9, &update_opts->collation);
20742077
}
20752078

2079+
if (!bson_empty0 (array_filters)) {
2080+
bson_append_array (extra,
2081+
"arrayFilters",
2082+
12,
2083+
array_filters);
2084+
}
2085+
20762086
_mongoc_write_result_init (&result);
20772087
_mongoc_write_command_init_update_idl (
20782088
&command,
@@ -2086,8 +2096,42 @@ _mongoc_collection_update_or_replace (
20862096
command.flags.has_collation = true;
20872097
}
20882098

2089-
_mongoc_collection_write_command_execute_idl (
2090-
&command, collection, &update_opts->crud, &result);
2099+
server_stream =
2100+
mongoc_cluster_stream_for_writes (&collection->client->cluster, error);
2101+
2102+
if (!server_stream) {
2103+
GOTO (done);
2104+
}
2105+
2106+
if (!bson_empty0 (array_filters)) {
2107+
if (server_stream->sd->max_wire_version < WIRE_VERSION_ARRAY_FILTERS) {
2108+
bson_set_error (error,
2109+
MONGOC_ERROR_COMMAND,
2110+
MONGOC_ERROR_PROTOCOL_BAD_WIRE_VERSION,
2111+
"The selected server does not support array filters");
2112+
GOTO (done);
2113+
}
2114+
2115+
if (!mongoc_write_concern_is_acknowledged (
2116+
update_opts->crud.writeConcern)) {
2117+
bson_set_error (error,
2118+
MONGOC_ERROR_COMMAND,
2119+
MONGOC_ERROR_PROTOCOL_BAD_WIRE_VERSION,
2120+
"Cannot use array filters with unacknowledged writes");
2121+
GOTO (done);
2122+
}
2123+
}
2124+
2125+
_mongoc_write_command_execute_idl (&command,
2126+
collection->client,
2127+
server_stream,
2128+
collection->db,
2129+
collection->collection,
2130+
0 /* offset */,
2131+
&update_opts->crud,
2132+
&result);
2133+
2134+
reply_initialized = true;
20912135

20922136
/* set fields described in CRUD spec for the UpdateResult */
20932137
ret = MONGOC_WRITE_RESULT_COMPLETE (&result,
@@ -2102,9 +2146,15 @@ _mongoc_collection_update_or_replace (
21022146
"upsertedCount",
21032147
"upsertedId");
21042148

2149+
done:
21052150
_mongoc_write_result_destroy (&result);
2151+
mongoc_server_stream_cleanup (server_stream);
21062152
_mongoc_write_command_destroy (&command);
21072153

2154+
if (!reply_initialized) {
2155+
_mongoc_bson_init_if_set (reply);
2156+
}
2157+
21082158
RETURN (ret);
21092159
}
21102160

@@ -2138,18 +2188,12 @@ mongoc_collection_update_one (mongoc_collection_t *collection,
21382188
return false;
21392189
}
21402190

2141-
if (!bson_empty (&update_one_opts.arrayFilters)) {
2142-
bson_append_array (&update_one_opts.extra,
2143-
"arrayFilters",
2144-
12,
2145-
&update_one_opts.arrayFilters);
2146-
}
2147-
21482191
ret = _mongoc_collection_update_or_replace (collection,
21492192
selector,
21502193
update,
21512194
&update_one_opts.update,
21522195
update_one_opts.update.bypass,
2196+
&update_one_opts.arrayFilters,
21532197
&update_one_opts.extra,
21542198
reply,
21552199
error);
@@ -2190,18 +2234,13 @@ mongoc_collection_update_many (mongoc_collection_t *collection,
21902234
}
21912235

21922236
bson_append_bool (&update_many_opts.extra, "multi", 5, true);
2193-
if (!bson_empty (&update_many_opts.arrayFilters)) {
2194-
bson_append_array (&update_many_opts.extra,
2195-
"arrayFilters",
2196-
12,
2197-
&update_many_opts.arrayFilters);
2198-
}
21992237

22002238
ret = _mongoc_collection_update_or_replace (collection,
22012239
selector,
22022240
update,
22032241
&update_many_opts.update,
22042242
update_many_opts.update.bypass,
2243+
&update_many_opts.arrayFilters,
22052244
&update_many_opts.extra,
22062245
reply,
22072246
error);
@@ -2246,6 +2285,7 @@ mongoc_collection_replace_one (mongoc_collection_t *collection,
22462285
replacement,
22472286
&replace_one_opts.update,
22482287
replace_one_opts.update.bypass,
2288+
NULL,
22492289
&replace_one_opts.extra,
22502290
reply,
22512291
error);

src/libmongoc/src/mongoc/mongoc-write-command-private.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ struct _mongoc_bulk_write_flags_t {
5151
mongoc_write_bypass_document_validation_t bypass_document_validation;
5252
bool has_collation;
5353
bool has_multi_write;
54+
bool has_array_filters;
5455
};
5556

5657

src/libmongoc/src/mongoc/mongoc-write-command.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,26 @@ _mongoc_write_command_execute_idl (mongoc_write_command_t *command,
913913
}
914914
}
915915

916+
if (command->flags.has_array_filters) {
917+
if (!mongoc_write_concern_is_acknowledged (crud->writeConcern)) {
918+
result->failed = true;
919+
bson_set_error (&result->error,
920+
MONGOC_ERROR_COMMAND,
921+
MONGOC_ERROR_COMMAND_INVALID_ARG,
922+
"Cannot use array filters with unacknowledged writes");
923+
EXIT;
924+
}
925+
926+
if (server_stream->sd->max_wire_version < WIRE_VERSION_ARRAY_FILTERS) {
927+
bson_set_error (&result->error,
928+
MONGOC_ERROR_COMMAND,
929+
MONGOC_ERROR_PROTOCOL_BAD_WIRE_VERSION,
930+
"The selected server does not support array filters");
931+
result->failed = true;
932+
EXIT;
933+
}
934+
}
935+
916936
if (command->flags.bypass_document_validation !=
917937
MONGOC_BYPASS_DOCUMENT_VALIDATION_DEFAULT) {
918938
if (!mongoc_write_concern_is_acknowledged (crud->writeConcern)) {

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

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,18 @@
1313
#include "test-conveniences.h"
1414
#include "mock_server/mock-rs.h"
1515

16+
17+
typedef void (*update_fn) (mongoc_bulk_operation_t *bulk,
18+
const bson_t *selector,
19+
const bson_t *document,
20+
bool upsert);
21+
22+
typedef bool (*update_with_opts_fn) (mongoc_bulk_operation_t *bulk,
23+
const bson_t *selector,
24+
const bson_t *document,
25+
const bson_t *opts,
26+
bson_error_t *error);
27+
1628
/*--------------------------------------------------------------------------
1729
*
1830
* assert_error_count --
@@ -976,6 +988,52 @@ test_update_arrayfilters (void *ctx)
976988
}
977989

978990

991+
static void
992+
test_update_arrayfilters_unsupported (void *ctx)
993+
{
994+
mongoc_client_t *client;
995+
mongoc_collection_t *collection;
996+
mongoc_bulk_operation_t *bulk;
997+
bson_error_t err;
998+
bool ret;
999+
int i;
1000+
1001+
update_with_opts_fn fns[] = {
1002+
mongoc_bulk_operation_update_one_with_opts,
1003+
mongoc_bulk_operation_update_many_with_opts,
1004+
};
1005+
1006+
client = test_framework_client_new ();
1007+
collection = get_test_collection (client, "test_update_arrayfilters_err");
1008+
1009+
for (i = 0; i < 2; i++) {
1010+
bulk =
1011+
mongoc_collection_create_bulk_operation_with_opts (collection, NULL);
1012+
ret = fns[i](bulk,
1013+
tmp_bson ("{'_id': 1}"),
1014+
tmp_bson ("{'$set': {'a.$[i].x': 3}}"),
1015+
tmp_bson ("{'arrayFilters': [{'i.x': {'$gt': 1}}]}"),
1016+
&err);
1017+
1018+
/* adding the updateOne/updateMany operation to the bulk succeeds */
1019+
ASSERT_OR_PRINT (ret, err);
1020+
1021+
ret = mongoc_bulk_operation_execute (bulk, NULL, &err) > 0;
1022+
BSON_ASSERT (!ret);
1023+
ASSERT_ERROR_CONTAINS (
1024+
err,
1025+
MONGOC_ERROR_COMMAND,
1026+
MONGOC_ERROR_PROTOCOL_BAD_WIRE_VERSION,
1027+
"The selected server does not support array filters");
1028+
1029+
mongoc_bulk_operation_destroy (bulk);
1030+
}
1031+
1032+
mongoc_collection_destroy (collection);
1033+
mongoc_client_destroy (client);
1034+
}
1035+
1036+
9791037
static void
9801038
test_replace_one (bool ordered)
9811039
{
@@ -1477,17 +1535,6 @@ test_update_many_with_opts_check_keys (void)
14771535
}
14781536

14791537

1480-
typedef void (*update_fn) (mongoc_bulk_operation_t *bulk,
1481-
const bson_t *selector,
1482-
const bson_t *document,
1483-
bool upsert);
1484-
1485-
typedef bool (*update_with_opts_fn) (mongoc_bulk_operation_t *bulk,
1486-
const bson_t *selector,
1487-
const bson_t *document,
1488-
const bson_t *opts,
1489-
bson_error_t *error);
1490-
14911538
typedef struct {
14921539
const char *bad_update_json;
14931540
const char *good_update_json;
@@ -4586,6 +4633,12 @@ test_bulk_install (TestSuite *suite)
45864633
NULL,
45874634
NULL,
45884635
test_framework_skip_if_max_wire_version_less_than_6);
4636+
TestSuite_AddFull (suite,
4637+
"/BulkOperation/update_arrayfilters/unsupported",
4638+
test_update_arrayfilters_unsupported,
4639+
NULL,
4640+
NULL,
4641+
test_framework_skip_if_max_wire_version_more_than_5);
45894642
TestSuite_AddLive (
45904643
suite, "/BulkOperation/replace_one_ordered", test_replace_one_ordered);
45914644
TestSuite_AddLive (suite,

0 commit comments

Comments
 (0)