|
17 | 17 | #include "operation.h"
|
18 | 18 |
|
19 | 19 | #include "mongoc-array-private.h"
|
| 20 | +#include "mongoc-bulkwrite.h" |
20 | 21 | #include "mongoc-util-private.h" // hex_to_bin
|
21 | 22 | #include "result.h"
|
22 | 23 | #include "test-diagnostics.h"
|
@@ -221,6 +222,286 @@ operation_list_database_names (test_t *test, operation_t *op, result_t *result,
|
221 | 222 | return ret;
|
222 | 223 | }
|
223 | 224 |
|
| 225 | +static bool |
| 226 | +append_client_bulkwritemodel (mongoc_bulkwrite_t *bw, bson_t *model_wrapper, bson_error_t *error) |
| 227 | +{ |
| 228 | + bool ok = false; |
| 229 | + // Example `model_wrapper`: |
| 230 | + // { "insertOne": { "namespace": "db.coll", "document": { "_id": 1 } }} |
| 231 | + char *namespace = NULL; |
| 232 | + bson_t *document = NULL; |
| 233 | + bson_t *filter = NULL; |
| 234 | + bson_t *update = NULL; |
| 235 | + bson_t *replacement = NULL; |
| 236 | + bson_t *collation = NULL; |
| 237 | + bson_val_t *hint = NULL; |
| 238 | + bool *upsert = NULL; |
| 239 | + bson_t *arrayFilters = NULL; |
| 240 | + bson_parser_t *parser = bson_parser_new (); |
| 241 | + |
| 242 | + // Expect exactly one root key to identify the model (e.g. "insertOne"): |
| 243 | + if (bson_count_keys (model_wrapper) != 1) { |
| 244 | + test_set_error (error, |
| 245 | + "expected exactly one key in model, got %" PRIu32 " : %s", |
| 246 | + bson_count_keys (model_wrapper), |
| 247 | + tmp_json (model_wrapper)); |
| 248 | + goto done; |
| 249 | + } |
| 250 | + bson_iter_t model_wrapper_iter; |
| 251 | + BSON_ASSERT (bson_iter_init (&model_wrapper_iter, model_wrapper)); |
| 252 | + BSON_ASSERT (bson_iter_next (&model_wrapper_iter)); |
| 253 | + const char *model_name = bson_iter_key (&model_wrapper_iter); |
| 254 | + bson_t model_bson; |
| 255 | + bson_iter_bson (&model_wrapper_iter, &model_bson); |
| 256 | + |
| 257 | + if (0 == strcmp ("insertOne", model_name)) { |
| 258 | + // Parse an "insertOne". |
| 259 | + bson_parser_utf8 (parser, "namespace", &namespace); |
| 260 | + bson_parser_doc (parser, "document", &document); |
| 261 | + if (!bson_parser_parse (parser, &model_bson, error)) { |
| 262 | + goto done; |
| 263 | + } |
| 264 | + |
| 265 | + if (!mongoc_bulkwrite_append_insertone (bw, namespace, document, NULL, error)) { |
| 266 | + goto done; |
| 267 | + } |
| 268 | + } else if (0 == strcmp ("updateOne", model_name)) { |
| 269 | + // Parse an "updateOne". |
| 270 | + bson_parser_utf8 (parser, "namespace", &namespace); |
| 271 | + bson_parser_doc (parser, "filter", &filter); |
| 272 | + bson_parser_array_or_doc (parser, "update", &update); |
| 273 | + bson_parser_array_optional (parser, "arrayFilters", &arrayFilters); |
| 274 | + bson_parser_doc_optional (parser, "collation", &collation); |
| 275 | + bson_parser_any_optional (parser, "hint", &hint); |
| 276 | + bson_parser_bool_optional (parser, "upsert", &upsert); |
| 277 | + if (!bson_parser_parse (parser, &model_bson, error)) { |
| 278 | + goto done; |
| 279 | + } |
| 280 | + |
| 281 | + mongoc_bulkwrite_updateoneopts_t *opts = mongoc_bulkwrite_updateoneopts_new (); |
| 282 | + mongoc_bulkwrite_updateoneopts_set_arrayfilters (opts, arrayFilters); |
| 283 | + mongoc_bulkwrite_updateoneopts_set_collation (opts, collation); |
| 284 | + if (hint) { |
| 285 | + mongoc_bulkwrite_updateoneopts_set_hint (opts, bson_val_to_value (hint)); |
| 286 | + } |
| 287 | + if (upsert) { |
| 288 | + mongoc_bulkwrite_updateoneopts_set_upsert (opts, *upsert); |
| 289 | + } |
| 290 | + |
| 291 | + if (!mongoc_bulkwrite_append_updateone (bw, namespace, filter, update, opts, error)) { |
| 292 | + mongoc_bulkwrite_updateoneopts_destroy (opts); |
| 293 | + goto done; |
| 294 | + } |
| 295 | + mongoc_bulkwrite_updateoneopts_destroy (opts); |
| 296 | + } else if (0 == strcmp ("updateMany", model_name)) { |
| 297 | + // Parse an "updateMany". |
| 298 | + bson_parser_utf8 (parser, "namespace", &namespace); |
| 299 | + bson_parser_doc (parser, "filter", &filter); |
| 300 | + bson_parser_array_or_doc (parser, "update", &update); |
| 301 | + bson_parser_array_optional (parser, "arrayFilters", &arrayFilters); |
| 302 | + bson_parser_doc_optional (parser, "collation", &collation); |
| 303 | + bson_parser_any_optional (parser, "hint", &hint); |
| 304 | + bson_parser_bool_optional (parser, "upsert", &upsert); |
| 305 | + if (!bson_parser_parse (parser, &model_bson, error)) { |
| 306 | + goto done; |
| 307 | + } |
| 308 | + |
| 309 | + mongoc_bulkwrite_updatemanyopts_t *opts = mongoc_bulkwrite_updatemanyopts_new (); |
| 310 | + mongoc_bulkwrite_updatemanyopts_set_arrayfilters (opts, arrayFilters); |
| 311 | + mongoc_bulkwrite_updatemanyopts_set_collation (opts, collation); |
| 312 | + if (hint) { |
| 313 | + mongoc_bulkwrite_updatemanyopts_set_hint (opts, bson_val_to_value (hint)); |
| 314 | + } |
| 315 | + if (upsert) { |
| 316 | + mongoc_bulkwrite_updatemanyopts_set_upsert (opts, *upsert); |
| 317 | + } |
| 318 | + |
| 319 | + if (!mongoc_bulkwrite_append_updatemany (bw, namespace, filter, update, opts, error)) { |
| 320 | + mongoc_bulkwrite_updatemanyopts_destroy (opts); |
| 321 | + goto done; |
| 322 | + } |
| 323 | + mongoc_bulkwrite_updatemanyopts_destroy (opts); |
| 324 | + } else if (0 == strcmp ("deleteOne", model_name)) { |
| 325 | + // Parse a "deleteOne". |
| 326 | + bson_parser_utf8 (parser, "namespace", &namespace); |
| 327 | + bson_parser_doc (parser, "filter", &filter); |
| 328 | + bson_parser_doc_optional (parser, "collation", &collation); |
| 329 | + bson_parser_any_optional (parser, "hint", &hint); |
| 330 | + if (!bson_parser_parse (parser, &model_bson, error)) { |
| 331 | + goto done; |
| 332 | + } |
| 333 | + |
| 334 | + mongoc_bulkwrite_deleteoneopts_t *opts = mongoc_bulkwrite_deleteoneopts_new (); |
| 335 | + mongoc_bulkwrite_deleteoneopts_set_collation (opts, collation); |
| 336 | + if (hint) { |
| 337 | + mongoc_bulkwrite_deleteoneopts_set_hint (opts, bson_val_to_value (hint)); |
| 338 | + } |
| 339 | + |
| 340 | + if (!mongoc_bulkwrite_append_deleteone (bw, namespace, filter, opts, error)) { |
| 341 | + mongoc_bulkwrite_deleteoneopts_destroy (opts); |
| 342 | + goto done; |
| 343 | + } |
| 344 | + mongoc_bulkwrite_deleteoneopts_destroy (opts); |
| 345 | + } else if (0 == strcmp ("deleteMany", model_name)) { |
| 346 | + // Parse a "deleteMany". |
| 347 | + bson_parser_utf8 (parser, "namespace", &namespace); |
| 348 | + bson_parser_doc (parser, "filter", &filter); |
| 349 | + bson_parser_doc_optional (parser, "collation", &collation); |
| 350 | + bson_parser_any_optional (parser, "hint", &hint); |
| 351 | + if (!bson_parser_parse (parser, &model_bson, error)) { |
| 352 | + goto done; |
| 353 | + } |
| 354 | + |
| 355 | + mongoc_bulkwrite_deletemanyopts_t *opts = mongoc_bulkwrite_deletemanyopts_new (); |
| 356 | + mongoc_bulkwrite_deletemanyopts_set_collation (opts, collation); |
| 357 | + if (hint) { |
| 358 | + mongoc_bulkwrite_deletemanyopts_set_hint (opts, bson_val_to_value (hint)); |
| 359 | + } |
| 360 | + |
| 361 | + if (!mongoc_bulkwrite_append_deletemany (bw, namespace, filter, opts, error)) { |
| 362 | + mongoc_bulkwrite_deletemanyopts_destroy (opts); |
| 363 | + goto done; |
| 364 | + } |
| 365 | + mongoc_bulkwrite_deletemanyopts_destroy (opts); |
| 366 | + } else if (0 == strcmp ("replaceOne", model_name)) { |
| 367 | + // Parse a "replaceOne". |
| 368 | + bson_parser_utf8 (parser, "namespace", &namespace); |
| 369 | + bson_parser_doc (parser, "filter", &filter); |
| 370 | + bson_parser_doc (parser, "replacement", &replacement); |
| 371 | + bson_parser_doc_optional (parser, "collation", &collation); |
| 372 | + bson_parser_bool_optional (parser, "upsert", &upsert); |
| 373 | + bson_parser_any_optional (parser, "hint", &hint); |
| 374 | + if (!bson_parser_parse (parser, &model_bson, error)) { |
| 375 | + goto done; |
| 376 | + } |
| 377 | + |
| 378 | + mongoc_bulkwrite_replaceoneopts_t *opts = mongoc_bulkwrite_replaceoneopts_new (); |
| 379 | + mongoc_bulkwrite_replaceoneopts_set_arrayfilters (opts, arrayFilters); |
| 380 | + mongoc_bulkwrite_replaceoneopts_set_collation (opts, collation); |
| 381 | + if (hint) { |
| 382 | + mongoc_bulkwrite_replaceoneopts_set_hint (opts, bson_val_to_value (hint)); |
| 383 | + } |
| 384 | + if (upsert) { |
| 385 | + mongoc_bulkwrite_replaceoneopts_set_upsert (opts, *upsert); |
| 386 | + } |
| 387 | + |
| 388 | + if (!mongoc_bulkwrite_append_replaceone (bw, namespace, filter, replacement, opts, error)) { |
| 389 | + mongoc_bulkwrite_replaceoneopts_destroy (opts); |
| 390 | + goto done; |
| 391 | + } |
| 392 | + mongoc_bulkwrite_replaceoneopts_destroy (opts); |
| 393 | + } else { |
| 394 | + test_set_error (error, "unsupported model: %s", model_name); |
| 395 | + goto done; |
| 396 | + } |
| 397 | + |
| 398 | + ok = true; |
| 399 | +done: |
| 400 | + bson_parser_destroy_with_parsed_fields (parser); |
| 401 | + return ok; |
| 402 | +} |
| 403 | + |
| 404 | +static bool |
| 405 | +operation_client_bulkwrite (test_t *test, operation_t *op, result_t *result, bson_error_t *error) |
| 406 | +{ |
| 407 | + bool ret = false; |
| 408 | + mongoc_client_t *client = NULL; |
| 409 | + mongoc_bulkwrite_t *bw = NULL; |
| 410 | + mongoc_bulkwriteopts_t *opts = mongoc_bulkwriteopts_new (); |
| 411 | + |
| 412 | + client = entity_map_get_client (test->entity_map, op->object, error); |
| 413 | + if (!client) { |
| 414 | + goto done; |
| 415 | + } |
| 416 | + |
| 417 | + int64_t nmodels = 0; |
| 418 | + |
| 419 | + // Parse arguments. |
| 420 | + { |
| 421 | + bool parse_ok = false; |
| 422 | + bson_t *args_models = NULL; |
| 423 | + bool *args_verboseResults = NULL; |
| 424 | + bool *args_ordered = NULL; |
| 425 | + bson_t *args_comment = NULL; |
| 426 | + bool *args_bypassDocumentValidation = NULL; |
| 427 | + bson_t *args_let = NULL; |
| 428 | + mongoc_write_concern_t *args_wc = NULL; |
| 429 | + bson_parser_t *parser = bson_parser_new (); |
| 430 | + |
| 431 | + bson_parser_array (parser, "models", &args_models); |
| 432 | + bson_parser_bool_optional (parser, "verboseResults", &args_verboseResults); |
| 433 | + bson_parser_bool_optional (parser, "ordered", &args_ordered); |
| 434 | + bson_parser_doc_optional (parser, "comment", &args_comment); |
| 435 | + bson_parser_bool_optional (parser, "bypassDocumentValidation", &args_bypassDocumentValidation); |
| 436 | + bson_parser_doc_optional (parser, "let", &args_let); |
| 437 | + bson_parser_write_concern_optional (parser, &args_wc); |
| 438 | + if (!bson_parser_parse (parser, op->arguments, error)) { |
| 439 | + goto parse_done; |
| 440 | + } |
| 441 | + if (args_verboseResults && *args_verboseResults) { |
| 442 | + mongoc_bulkwriteopts_set_verboseresults (opts, true); |
| 443 | + } |
| 444 | + if (args_ordered) { |
| 445 | + mongoc_bulkwriteopts_set_ordered (opts, *args_ordered); |
| 446 | + } |
| 447 | + if (args_comment) { |
| 448 | + mongoc_bulkwriteopts_set_comment (opts, args_comment); |
| 449 | + } |
| 450 | + if (args_bypassDocumentValidation) { |
| 451 | + mongoc_bulkwriteopts_set_bypassdocumentvalidation (opts, *args_bypassDocumentValidation); |
| 452 | + } |
| 453 | + if (args_let) { |
| 454 | + mongoc_bulkwriteopts_set_let (opts, args_let); |
| 455 | + } |
| 456 | + if (args_wc) { |
| 457 | + mongoc_bulkwriteopts_set_writeconcern (opts, args_wc); |
| 458 | + } |
| 459 | + |
| 460 | + // Parse models. |
| 461 | + bson_iter_t args_models_iter; |
| 462 | + BSON_ASSERT (bson_iter_init (&args_models_iter, args_models)); |
| 463 | + bw = mongoc_client_bulkwrite_new (client); |
| 464 | + while (bson_iter_next (&args_models_iter)) { |
| 465 | + nmodels++; |
| 466 | + bson_t model_wrapper; |
| 467 | + bson_iter_bson (&args_models_iter, &model_wrapper); |
| 468 | + if (!append_client_bulkwritemodel (bw, &model_wrapper, error)) { |
| 469 | + if (error->domain != TEST_ERROR_DOMAIN) { |
| 470 | + // Propagate error as a test result. |
| 471 | + result_from_val_and_reply (result, NULL, NULL, error); |
| 472 | + // Return with a success (to not abort test runner) and propagate |
| 473 | + // the error as a result. |
| 474 | + ret = true; |
| 475 | + *error = (bson_error_t){0}; |
| 476 | + bson_parser_destroy_with_parsed_fields (parser); |
| 477 | + goto done; |
| 478 | + } |
| 479 | + goto parse_done; |
| 480 | + } |
| 481 | + } |
| 482 | + |
| 483 | + parse_ok = true; |
| 484 | + parse_done: |
| 485 | + bson_parser_destroy_with_parsed_fields (parser); |
| 486 | + if (!parse_ok) { |
| 487 | + goto done; |
| 488 | + } |
| 489 | + } |
| 490 | + |
| 491 | + // Do client bulk write. |
| 492 | + mongoc_bulkwrite_set_session (bw, op->session); |
| 493 | + mongoc_bulkwritereturn_t bwr = mongoc_bulkwrite_execute (bw, opts); |
| 494 | + |
| 495 | + result_from_bulkwritereturn (result, bwr, nmodels); |
| 496 | + mongoc_bulkwriteexception_destroy (bwr.exc); |
| 497 | + mongoc_bulkwriteresult_destroy (bwr.res); |
| 498 | + ret = true; |
| 499 | +done: |
| 500 | + mongoc_bulkwriteopts_destroy (opts); |
| 501 | + mongoc_bulkwrite_destroy (bw); |
| 502 | + return ret; |
| 503 | +} |
| 504 | + |
224 | 505 | static bool
|
225 | 506 | operation_create_datakey (test_t *test, operation_t *op, result_t *result, bson_error_t *error)
|
226 | 507 | {
|
@@ -2609,6 +2890,9 @@ operation_start_transaction (test_t *test, operation_t *op, result_t *result, bs
|
2609 | 2890 | bson_parser_read_concern_optional (bp, &rc);
|
2610 | 2891 | bson_parser_write_concern_optional (bp, &wc);
|
2611 | 2892 | bson_parser_read_prefs_optional (bp, &rp);
|
| 2893 | + if (!bson_parser_parse (bp, op->arguments, error)) { |
| 2894 | + goto done; |
| 2895 | + } |
2612 | 2896 | if (rc) {
|
2613 | 2897 | mongoc_transaction_opts_set_read_concern (opts, rc);
|
2614 | 2898 | }
|
@@ -3520,6 +3804,7 @@ operation_run (test_t *test, bson_t *op_bson, bson_error_t *error)
|
3520 | 3804 | {"createChangeStream", operation_create_change_stream},
|
3521 | 3805 | {"listDatabases", operation_list_databases},
|
3522 | 3806 | {"listDatabaseNames", operation_list_database_names},
|
| 3807 | + {"clientBulkWrite", operation_client_bulkwrite}, |
3523 | 3808 |
|
3524 | 3809 | /* ClientEncryption operations */
|
3525 | 3810 | {"createDataKey", operation_create_datakey},
|
|
0 commit comments