void _mongoc_write_command_delete_append (mongoc_write_command_t *command, const bson_t *selector) { const char *key; char keydata [16]; bson_t doc; ENTRY; BSON_ASSERT (command); BSON_ASSERT (command->type == MONGOC_WRITE_COMMAND_DELETE); BSON_ASSERT (selector); BSON_ASSERT (selector->len >= 5); bson_init (&doc); BSON_APPEND_DOCUMENT (&doc, "q", selector); BSON_APPEND_INT32 (&doc, "limit", command->u.delete_.multi ? 0 : 1); key = NULL; bson_uint32_to_string (command->n_documents, &key, keydata, sizeof keydata); BSON_ASSERT (key); BSON_APPEND_DOCUMENT (command->documents, key, &doc); command->n_documents++; bson_destroy (&doc); EXIT; }
static void td_to_bson (const mongoc_topology_description_t *td, bson_t *bson) { size_t i; bson_t servers = BSON_INITIALIZER; bson_t server; char str[16]; const char *key; for (i = 0; i < td->servers->items_len; i++) { bson_uint32_to_string ((uint32_t) i, &key, str, sizeof str); sd_to_bson (mongoc_set_get_item (td->servers, (int) i), &server); BSON_APPEND_DOCUMENT (&servers, key, &server); bson_destroy (&server); } bson_init (bson); BSON_APPEND_UTF8 (bson, "topologyType", mongoc_topology_description_type (td)); if (td->set_name) { BSON_APPEND_UTF8 (bson, "setName", td->set_name); } BSON_APPEND_ARRAY (bson, "servers", &servers); bson_destroy (&servers); }
void _append_array_from_command (mongoc_write_command_t *command, bson_t *bson) { bson_t ar; bson_reader_t *reader; char str[16]; uint32_t i = 0; const char *key; bool eof; const bson_t *current; reader = bson_reader_new_from_data (command->payload.data, command->payload.len); bson_append_array_begin (bson, gCommandFields[command->type], gCommandFieldLens[command->type], &ar); while ((current = bson_reader_read (reader, &eof))) { bson_uint32_to_string (i, &key, str, sizeof str); BSON_APPEND_DOCUMENT (&ar, key, current); i++; } bson_append_array_end (bson, &ar); bson_reader_destroy (reader); }
void _mongoc_write_command_update_append (mongoc_write_command_t *command, const bson_t *selector, const bson_t *update, bool upsert, bool multi) { const char *key; char keydata [16]; bson_t doc; ENTRY; BSON_ASSERT (command); BSON_ASSERT (command->type == MONGOC_WRITE_COMMAND_UPDATE); BSON_ASSERT (selector && update); bson_init (&doc); BSON_APPEND_DOCUMENT (&doc, "q", selector); BSON_APPEND_DOCUMENT (&doc, "u", update); BSON_APPEND_BOOL (&doc, "upsert", upsert); BSON_APPEND_BOOL (&doc, "multi", multi); key = NULL; bson_uint32_to_string (command->n_documents, &key, keydata, sizeof keydata); BSON_ASSERT (key); BSON_APPEND_DOCUMENT (command->documents, key, &doc); command->n_documents++; bson_destroy (&doc); EXIT; }
static void copy_labels_plus_unknown_commit_result (const bson_t *src, bson_t *dst) { bson_iter_t iter; bson_iter_t src_label; bson_t dst_labels; char str[16]; uint32_t i = 0; const char *key; BSON_APPEND_ARRAY_BEGIN (dst, "errorLabels", &dst_labels); BSON_APPEND_UTF8 (&dst_labels, "0", UNKNOWN_COMMIT_RESULT); /* append any other errorLabels already in "src" */ if (bson_iter_init_find (&iter, src, "errorLabels") && bson_iter_recurse (&iter, &src_label)) { while (bson_iter_next (&src_label) && BSON_ITER_HOLDS_UTF8 (&src_label)) { if (strcmp (bson_iter_utf8 (&src_label, NULL), UNKNOWN_COMMIT_RESULT) != 0) { i++; bson_uint32_to_string (i, &key, str, sizeof str); BSON_APPEND_UTF8 ( &dst_labels, key, bson_iter_utf8 (&src_label, NULL)); } } } bson_append_array_end (dst, &dst_labels); }
/* Construct the aggregate command in cmd: * { aggregate: collname, pipeline: [], cursor: { batchSize: x } } */ static void _make_command (mongoc_change_stream_t *stream, bson_t *command) { bson_iter_t iter; bson_t change_stream_stage; /* { $changeStream: <change_stream_doc> } */ bson_t change_stream_doc; bson_t pipeline; bson_t cursor_doc; bson_init (command); bson_append_utf8 (command, "aggregate", 9, stream->coll->collection, stream->coll->collectionlen); bson_append_array_begin (command, "pipeline", 8, &pipeline); /* Append the $changeStream stage */ bson_append_document_begin (&pipeline, "0", 1, &change_stream_stage); bson_append_document_begin ( &change_stream_stage, "$changeStream", 13, &change_stream_doc); bson_concat (&change_stream_doc, &stream->full_document); if (!bson_empty (&stream->resume_token)) { bson_concat (&change_stream_doc, &stream->resume_token); } bson_append_document_end (&change_stream_stage, &change_stream_doc); bson_append_document_end (&pipeline, &change_stream_stage); /* Append user pipeline if it exists */ if (bson_iter_init_find (&iter, &stream->pipeline_to_append, "pipeline") && BSON_ITER_HOLDS_ARRAY (&iter)) { bson_iter_t child_iter; uint32_t key_int = 1; char buf[16]; const char *key_str; BSON_ASSERT (bson_iter_recurse (&iter, &child_iter)); while (bson_iter_next (&child_iter)) { /* The user pipeline may consist of invalid stages or non-documents. * Append anyway, and rely on the server error. */ size_t keyLen = bson_uint32_to_string (key_int, &key_str, buf, sizeof (buf)); bson_append_value ( &pipeline, key_str, (int) keyLen, bson_iter_value (&child_iter)); ++key_int; } } bson_append_array_end (command, &pipeline); /* Add batch size if needed */ bson_append_document_begin (command, "cursor", 6, &cursor_doc); if (stream->batch_size > 0) { bson_append_int32 (&cursor_doc, "batchSize", 9, stream->batch_size); } bson_append_document_end (command, &cursor_doc); }
static void _bson_json_read_fixup_key (bson_json_reader_bson_t *bson) /* IN */ { if (bson->n >= 0 && STACK_IS_ARRAY) { _bson_json_buf_ensure (&bson->key_buf, 12); bson->key_buf.len = bson_uint32_to_string (STACK_I, &bson->key, (char *)bson->key_buf.buf, 12); STACK_I++; } }
static bool append_array(bson_t* bson, object_t* object) { for (size_t i = 0; i < object->l; ++i) { char str[16]; const char* key; size_t len = bson_uint32_to_string(i, &key, str, sizeof(str)); if (!append_value(bson, key, len, object->children + i)) return false; } return true; }
static void context_append (context_t *context, bson_t *event) { char str[16]; const char *key; bson_uint32_to_string (context->n_events, &key, str, sizeof str); BSON_APPEND_DOCUMENT (&context->events, key, event); context->n_events++; bson_destroy (event); }
int32_t _mongoc_write_result_merge_arrays (uint32_t offset, mongoc_write_result_t *result, /* IN */ bson_t *dest, /* IN */ bson_iter_t *iter) /* IN */ { const bson_value_t *value; bson_iter_t ar; bson_iter_t citer; int32_t idx; int32_t count = 0; int32_t aridx; bson_t child; const char *keyptr = NULL; char key[12]; int len; ENTRY; BSON_ASSERT (result); BSON_ASSERT (dest); BSON_ASSERT (iter); BSON_ASSERT (BSON_ITER_HOLDS_ARRAY (iter)); aridx = bson_count_keys (dest); if (bson_iter_recurse (iter, &ar)) { while (bson_iter_next (&ar)) { if (BSON_ITER_HOLDS_DOCUMENT (&ar) && bson_iter_recurse (&ar, &citer)) { len = (int) bson_uint32_to_string (aridx++, &keyptr, key, sizeof key); bson_append_document_begin (dest, keyptr, len, &child); while (bson_iter_next (&citer)) { if (BSON_ITER_IS_KEY (&citer, "index")) { idx = bson_iter_int32 (&citer) + offset; BSON_APPEND_INT32 (&child, "index", idx); } else { value = bson_iter_value (&citer); BSON_APPEND_VALUE (&child, bson_iter_key (&citer), value); } } bson_append_document_end (dest, &child); count++; } } } RETURN (count); }
static void test_bson_writer_shared_buffer (void) { bson_writer_t *writer; bson_uint8_t *buf = bson_malloc0(32); bson_bool_t rolled_back = FALSE; size_t buflen = 32; size_t n_bytes; bson_t *b; const char *key; char keystr[32]; int i = 0; int j = 0; int n_docs = 10000; writer = bson_writer_new(&buf, &buflen, 0, bson_realloc); for (i = 0; i < n_docs; i++) { bson_writer_begin(writer, &b); for (j = 0; j < 1000; j++) { bson_uint32_to_string(j, &key, keystr, sizeof keystr); assert(bson_append_int64(b, key, -1, j)); } if (bson_writer_get_length(writer) > (48 * 1024 * 1024)) { rolled_back = TRUE; bson_writer_rollback(writer); break; } else { bson_writer_end(writer); } } n_bytes = bson_writer_get_length(writer); bson_writer_destroy(writer); assert_cmpint(n_bytes, <, (48 * 1024 * 1024)); assert_cmpint(rolled_back, ==, TRUE); assert(rolled_back); bson_free(buf); }
static void append_documents_from_cmd (const mongoc_cmd_t *cmd, mongoc_apm_command_started_t *event) { int32_t doc_len; bson_t doc; const uint8_t *pos; const char *field_name; bson_t bson; char str[16]; const char *key; uint32_t i; if (!cmd->payload || !cmd->payload_size) { return; } if (!event->command_owned) { event->command = bson_copy (event->command); event->command_owned = true; } /* make array from outgoing OP_MSG payload type 1 on an "insert", * "update", or "delete" command. */ field_name = _mongoc_get_documents_field_name (cmd->command_name); BSON_ASSERT (field_name); BSON_ASSERT (BSON_APPEND_ARRAY_BEGIN (event->command, field_name, &bson)); pos = cmd->payload; i = 0; while (pos < cmd->payload + cmd->payload_size) { memcpy (&doc_len, pos, sizeof (doc_len)); doc_len = BSON_UINT32_FROM_LE (doc_len); BSON_ASSERT (bson_init_static (&doc, pos, (size_t) doc_len)); bson_uint32_to_string (i, &key, str, sizeof (str)); BSON_APPEND_DOCUMENT (&bson, key, &doc); pos += doc_len; i++; } bson_append_array_end (event->command, &bson); }
void _mongoc_write_command_insert_append (mongoc_write_command_t *command, const bson_t *document) { const char *key; bson_iter_t iter; bson_oid_t oid; bson_t tmp; char keydata [16]; ENTRY; BSON_ASSERT (command); BSON_ASSERT (command->type == MONGOC_WRITE_COMMAND_INSERT); BSON_ASSERT (document); BSON_ASSERT (document->len >= 5); key = NULL; bson_uint32_to_string (command->n_documents, &key, keydata, sizeof keydata); BSON_ASSERT (key); /* * If the document does not contain an "_id" field, we need to generate * a new oid for "_id". */ if (!bson_iter_init_find (&iter, document, "_id")) { bson_init (&tmp); bson_oid_init (&oid, NULL); BSON_APPEND_OID (&tmp, "_id", &oid); bson_concat (&tmp, document); BSON_APPEND_DOCUMENT (command->documents, key, &tmp); bson_destroy (&tmp); } else { BSON_APPEND_DOCUMENT (command->documents, key, document); } command->n_documents++; EXIT; }
void _mongoc_write_result_append_upsert (mongoc_write_result_t *result, int32_t idx, const bson_value_t *value) { bson_t child; const char *keyptr = NULL; char key[12]; int len; BSON_ASSERT (result); BSON_ASSERT (value); len = (int) bson_uint32_to_string ( result->upsert_append_count, &keyptr, key, sizeof key); bson_append_document_begin (&result->upserted, keyptr, len, &child); BSON_APPEND_INT32 (&child, "index", idx); BSON_APPEND_VALUE (&child, "_id", value); bson_append_document_end (&result->upserted, &child); result->upsert_append_count++; }
static void _append_write_concern_err_legacy (mongoc_write_result_t *result, const char *err, int32_t code) { char str[16]; const char *key; size_t keylen; bson_t write_concern_error; /* don't set result->failed; record the write concern err and continue */ keylen = bson_uint32_to_string ( result->n_writeConcernErrors, &key, str, sizeof str); BSON_ASSERT (keylen < INT_MAX); bson_append_document_begin ( &result->writeConcernErrors, key, (int) keylen, &write_concern_error); bson_append_int32 (&write_concern_error, "code", 4, code); bson_append_utf8 (&write_concern_error, "errmsg", 6, err, -1); bson_append_document_end (&result->writeConcernErrors, &write_concern_error); result->n_writeConcernErrors++; }
/* Uses old way of querying system.namespaces. */ bson_t * _mongoc_database_get_collection_info_legacy (mongoc_database_t *database, const bson_t *filter, bson_error_t *error) { mongoc_collection_t *col; mongoc_cursor_t *cursor; mongoc_read_prefs_t *read_prefs; uint32_t dbname_len; const bson_t *doc; bson_t legacy_filter; bson_iter_t iter; const char *name; const char *col_filter; bson_t q = BSON_INITIALIZER; bson_t *ret = NULL; bson_t col_array = BSON_INITIALIZER; const char *key; char keystr[16]; uint32_t n_cols = 0; BSON_ASSERT (database); col = mongoc_client_get_collection (database->client, database->name, "system.namespaces"); BSON_ASSERT (col); dbname_len = (uint32_t)strlen (database->name); /* Filtering on name needs to be handled differently for old servers. */ if (filter && bson_iter_init_find (&iter, filter, "name")) { /* on legacy servers, this must be a string (i.e. not a regex) */ if (!BSON_ITER_HOLDS_UTF8 (&iter)) { bson_set_error (error, MONGOC_ERROR_NAMESPACE, MONGOC_ERROR_NAMESPACE_INVALID_FILTER_TYPE, "On legacy servers, a filter on name can only be a string."); goto cleanup_filter; } BSON_ASSERT (BSON_ITER_HOLDS_UTF8 (&iter)); col_filter = bson_iter_utf8 (&iter, NULL); bson_init (&legacy_filter); bson_copy_to_excluding_noinit (filter, &legacy_filter, "name", NULL); /* We must db-qualify filters on name. */ bson_string_t *buf = bson_string_new (database->name); bson_string_append_c (buf, '.'); bson_string_append (buf, col_filter); BSON_APPEND_UTF8 (&legacy_filter, "name", buf->str); bson_string_free (buf, true); filter = &legacy_filter; } read_prefs = mongoc_read_prefs_new (MONGOC_READ_PRIMARY); cursor = mongoc_collection_find (col, MONGOC_QUERY_NONE, 0, 0, 0, filter ? filter : &q, NULL, read_prefs); ret = bson_new(); BSON_APPEND_ARRAY_BEGIN (ret, "collections", &col_array); while (mongoc_cursor_more (cursor) && !mongoc_cursor_error (cursor, error)) { if (mongoc_cursor_next (cursor, &doc)) { /* 2 gotchas here. * 1. need to ignore any system collections (prefixed with $) * 2. need to remove the database name from the collection so that clients * don't need to specialize their logic for old versions of the server. */ if (bson_iter_init_find (&iter, doc, "name") && BSON_ITER_HOLDS_UTF8 (&iter) && (name = bson_iter_utf8 (&iter, NULL)) && !strchr (name, '$') && (0 == strncmp (name, database->name, dbname_len))) { bson_t nprefix_col = BSON_INITIALIZER; bson_copy_to_excluding_noinit (doc, &nprefix_col, "name", NULL); BSON_APPEND_UTF8 (&nprefix_col, "name", name + (dbname_len + 1)); /* +1 for the '.' */ /* need to construct a key for this array element. */ bson_uint32_to_string(n_cols, &key, keystr, sizeof (keystr)); BSON_APPEND_DOCUMENT (&col_array, key, &nprefix_col); ++n_cols; } } } bson_append_array_end (ret, &col_array); mongoc_cursor_destroy (cursor); mongoc_read_prefs_destroy (read_prefs); cleanup_filter: mongoc_collection_destroy (col); return ret; }
void _mongoc_write_result_merge (mongoc_write_result_t *result, /* IN */ mongoc_write_command_t *command, /* IN */ const bson_t *reply, /* IN */ uint32_t offset) { int32_t server_index = 0; const bson_value_t *value; bson_iter_t iter; bson_iter_t citer; bson_iter_t ar; int32_t n_upserted = 0; int32_t affected = 0; ENTRY; BSON_ASSERT (result); BSON_ASSERT (reply); if (bson_iter_init_find (&iter, reply, "n") && BSON_ITER_HOLDS_INT32 (&iter)) { affected = bson_iter_int32 (&iter); } if (bson_iter_init_find (&iter, reply, "writeErrors") && BSON_ITER_HOLDS_ARRAY (&iter) && bson_iter_recurse (&iter, &citer) && bson_iter_next (&citer)) { result->failed = true; } switch (command->type) { case MONGOC_WRITE_COMMAND_INSERT: result->nInserted += affected; break; case MONGOC_WRITE_COMMAND_DELETE: result->nRemoved += affected; break; case MONGOC_WRITE_COMMAND_UPDATE: /* server returns each upserted _id with its index into this batch * look for "upserted": [{"index": 4, "_id": ObjectId()}, ...] */ if (bson_iter_init_find (&iter, reply, "upserted")) { if (BSON_ITER_HOLDS_ARRAY (&iter) && (bson_iter_recurse (&iter, &ar))) { while (bson_iter_next (&ar)) { if (BSON_ITER_HOLDS_DOCUMENT (&ar) && bson_iter_recurse (&ar, &citer) && bson_iter_find (&citer, "index") && BSON_ITER_HOLDS_INT32 (&citer)) { server_index = bson_iter_int32 (&citer); if (bson_iter_recurse (&ar, &citer) && bson_iter_find (&citer, "_id")) { value = bson_iter_value (&citer); _mongoc_write_result_append_upsert (result, offset + server_index, value); n_upserted++; } } } } result->nUpserted += n_upserted; /* * XXX: The following addition to nMatched needs some checking. * I'm highly skeptical of it. */ result->nMatched += BSON_MAX (0, (affected - n_upserted)); } else { result->nMatched += affected; } /* * SERVER-13001 - in a mixed sharded cluster a call to update could * return nModified (>= 2.6) or not (<= 2.4). If any call does not * return nModified we can't report a valid final count so omit the * field completely. */ if (bson_iter_init_find (&iter, reply, "nModified") && BSON_ITER_HOLDS_INT32 (&iter)) { result->nModified += bson_iter_int32 (&iter); } else { /* * nModified could be BSON_TYPE_NULL, which should also be omitted. */ result->omit_nModified = true; } break; default: BSON_ASSERT (false); break; } if (bson_iter_init_find (&iter, reply, "writeErrors") && BSON_ITER_HOLDS_ARRAY (&iter)) { _mongoc_write_result_merge_arrays (offset, result, &result->writeErrors, &iter); } if (bson_iter_init_find (&iter, reply, "writeConcernError") && BSON_ITER_HOLDS_DOCUMENT (&iter)) { uint32_t len; const uint8_t *data; bson_t write_concern_error; char str[16]; const char *key; /* writeConcernError is a subdocument in the server response * append it to the result->writeConcernErrors array */ bson_iter_document (&iter, &len, &data); bson_init_static (&write_concern_error, data, len); bson_uint32_to_string (result->n_writeConcernErrors, &key, str, sizeof str); bson_append_document (&result->writeConcernErrors, key, -1, &write_concern_error); result->n_writeConcernErrors++; } EXIT; }
static void _mongoc_write_opquery (mongoc_write_command_t *command, mongoc_client_t *client, mongoc_server_stream_t *server_stream, const char *database, const char *collection, const mongoc_write_concern_t *write_concern, uint32_t offset, mongoc_write_result_t *result, bson_error_t *error) { mongoc_cmd_parts_t parts; bson_iter_t iter; const char *key; uint32_t len = 0; bson_t ar; bson_t cmd; bson_t reply; char str[16]; bool has_more; bool ret = false; uint32_t i; int32_t max_bson_obj_size; int32_t max_write_batch_size; uint32_t overhead; uint32_t key_len; int data_offset = 0; bson_reader_t *reader; const bson_t *bson; bool eof; ENTRY; BSON_ASSERT (command); BSON_ASSERT (client); BSON_ASSERT (database); BSON_ASSERT (server_stream); BSON_ASSERT (collection); bson_init (&cmd); max_bson_obj_size = mongoc_server_stream_max_bson_obj_size (server_stream); max_write_batch_size = mongoc_server_stream_max_write_batch_size (server_stream); again: has_more = false; i = 0; _mongoc_write_command_init (&cmd, command, collection); /* 1 byte to specify array type, 1 byte for field name's null terminator */ overhead = cmd.len + 2 + gCommandFieldLens[command->type]; reader = bson_reader_new_from_data (command->payload.data + data_offset, command->payload.len - data_offset); bson_append_array_begin (&cmd, gCommandFields[command->type], gCommandFieldLens[command->type], &ar); while ((bson = bson_reader_read (reader, &eof))) { key_len = (uint32_t) bson_uint32_to_string (i, &key, str, sizeof str); len = bson->len; /* 1 byte to specify document type, 1 byte for key's null terminator */ if (_mongoc_write_command_will_overflow (overhead, key_len + len + 2 + ar.len, i, max_bson_obj_size, max_write_batch_size)) { has_more = true; break; } BSON_APPEND_DOCUMENT (&ar, key, bson); data_offset += len; i++; } bson_append_array_end (&cmd, &ar); if (!i) { _mongoc_write_command_too_large_error (error, i, len, max_bson_obj_size); result->failed = true; result->must_stop = true; ret = false; if (bson) { data_offset += len; } } else { mongoc_cmd_parts_init (&parts, client, database, MONGOC_QUERY_NONE, &cmd); parts.is_write_command = true; parts.assembled.operation_id = command->operation_id; if (!mongoc_cmd_parts_set_write_concern ( &parts, write_concern, server_stream->sd->max_wire_version, error)) { bson_reader_destroy (reader); bson_destroy (&cmd); mongoc_cmd_parts_cleanup (&parts); EXIT; } BSON_ASSERT (bson_iter_init (&iter, &command->cmd_opts)); if (!mongoc_cmd_parts_append_opts ( &parts, &iter, server_stream->sd->max_wire_version, error)) { bson_reader_destroy (reader); bson_destroy (&cmd); mongoc_cmd_parts_cleanup (&parts); EXIT; } ret = mongoc_cmd_parts_assemble (&parts, server_stream, error); if (ret) { ret = mongoc_cluster_run_command_monitored ( &client->cluster, &parts.assembled, &reply, error); } else { /* assembling failed */ result->must_stop = true; bson_init (&reply); } if (!ret) { result->failed = true; if (bson_empty (&reply)) { /* assembling failed, or a network error running the command */ result->must_stop = true; } } _mongoc_write_result_merge (result, command, &reply, offset); offset += i; bson_destroy (&reply); mongoc_cmd_parts_cleanup (&parts); } bson_reader_destroy (reader); if (has_more && (ret || !command->flags.ordered) && !result->must_stop) { bson_reinit (&cmd); GOTO (again); } bson_destroy (&cmd); EXIT; }
/* construct the aggregate command in cmd. looks like one of the following: * for a collection change stream: * { aggregate: collname, pipeline: [], cursor: { batchSize: x } } * for a database change stream: * { aggregate: 1, pipeline: [], cursor: { batchSize: x } } * for a client change stream: * { aggregate: 1, pipeline: [{$changeStream: {allChangesForCluster: true}}], * cursor: { batchSize: x } } */ static void _make_command (mongoc_change_stream_t *stream, bson_t *command) { bson_iter_t iter; bson_t change_stream_stage; /* { $changeStream: <change_stream_doc> } */ bson_t change_stream_doc; bson_t pipeline; bson_t cursor_doc; bson_init (command); if (stream->change_stream_type == MONGOC_CHANGE_STREAM_COLLECTION) { bson_append_utf8 ( command, "aggregate", 9, stream->coll, (int) strlen (stream->coll)); } else { bson_append_int32 (command, "aggregate", 9, 1); } bson_append_array_begin (command, "pipeline", 8, &pipeline); /* append the $changeStream stage. */ bson_append_document_begin (&pipeline, "0", 1, &change_stream_stage); bson_append_document_begin ( &change_stream_stage, "$changeStream", 13, &change_stream_doc); bson_concat (&change_stream_doc, stream->full_document); if (!bson_empty (&stream->resume_token)) { bson_concat (&change_stream_doc, &stream->resume_token); } /* Change streams spec: "startAtOperationTime and resumeAfter are mutually * exclusive; if both startAtOperationTime and resumeAfter are set, the * server will return an error. Drivers MUST NOT throw a custom error, and * MUST defer to the server error." */ if (!_mongoc_timestamp_empty (&stream->operation_time)) { _mongoc_timestamp_append ( &stream->operation_time, &change_stream_doc, "startAtOperationTime"); } if (stream->change_stream_type == MONGOC_CHANGE_STREAM_CLIENT) { bson_append_bool (&change_stream_doc, "allChangesForCluster", 20, true); } bson_append_document_end (&change_stream_stage, &change_stream_doc); bson_append_document_end (&pipeline, &change_stream_stage); /* Append user pipeline if it exists */ if (bson_iter_init_find (&iter, &stream->pipeline_to_append, "pipeline") && BSON_ITER_HOLDS_ARRAY (&iter)) { bson_iter_t child_iter; uint32_t key_int = 1; char buf[16]; const char *key_str; BSON_ASSERT (bson_iter_recurse (&iter, &child_iter)); while (bson_iter_next (&child_iter)) { /* the user pipeline may consist of invalid stages or non-documents. * append anyway, and rely on the server error. */ size_t keyLen = bson_uint32_to_string (key_int, &key_str, buf, sizeof (buf)); bson_append_value ( &pipeline, key_str, (int) keyLen, bson_iter_value (&child_iter)); ++key_int; } } bson_append_array_end (command, &pipeline); /* Add batch size if needed */ bson_append_document_begin (command, "cursor", 6, &cursor_doc); if (stream->batch_size > 0) { bson_append_int32 (&cursor_doc, "batchSize", 9, stream->batch_size); } bson_append_document_end (command, &cursor_doc); }
void _mongoc_write_result_merge (mongoc_write_result_t *result, /* IN */ mongoc_write_command_t *command, /* IN */ const bson_t *reply, /* IN */ uint32_t offset) { int32_t server_index = 0; const bson_value_t *value; bson_iter_t iter; bson_iter_t citer; bson_iter_t ar; int32_t n_upserted = 0; int32_t affected = 0; ENTRY; BSON_ASSERT (result); BSON_ASSERT (reply); if (bson_iter_init_find (&iter, reply, "n") && BSON_ITER_HOLDS_INT32 (&iter)) { affected = bson_iter_int32 (&iter); } if (bson_iter_init_find (&iter, reply, "writeErrors") && BSON_ITER_HOLDS_ARRAY (&iter) && bson_iter_recurse (&iter, &citer) && bson_iter_next (&citer)) { result->failed = true; } switch (command->type) { case MONGOC_WRITE_COMMAND_INSERT: result->nInserted += affected; break; case MONGOC_WRITE_COMMAND_DELETE: result->nRemoved += affected; break; case MONGOC_WRITE_COMMAND_UPDATE: /* server returns each upserted _id with its index into this batch * look for "upserted": [{"index": 4, "_id": ObjectId()}, ...] */ if (bson_iter_init_find (&iter, reply, "upserted")) { if (BSON_ITER_HOLDS_ARRAY (&iter) && (bson_iter_recurse (&iter, &ar))) { while (bson_iter_next (&ar)) { if (BSON_ITER_HOLDS_DOCUMENT (&ar) && bson_iter_recurse (&ar, &citer) && bson_iter_find (&citer, "index") && BSON_ITER_HOLDS_INT32 (&citer)) { server_index = bson_iter_int32 (&citer); if (bson_iter_recurse (&ar, &citer) && bson_iter_find (&citer, "_id")) { value = bson_iter_value (&citer); _mongoc_write_result_append_upsert ( result, offset + server_index, value); n_upserted++; } } } } result->nUpserted += n_upserted; /* * XXX: The following addition to nMatched needs some checking. * I'm highly skeptical of it. */ result->nMatched += BSON_MAX (0, (affected - n_upserted)); } else { result->nMatched += affected; } if (bson_iter_init_find (&iter, reply, "nModified") && BSON_ITER_HOLDS_INT32 (&iter)) { result->nModified += bson_iter_int32 (&iter); } break; default: BSON_ASSERT (false); break; } if (bson_iter_init_find (&iter, reply, "writeErrors") && BSON_ITER_HOLDS_ARRAY (&iter)) { _mongoc_write_result_merge_arrays ( offset, result, &result->writeErrors, &iter); } if (bson_iter_init_find (&iter, reply, "writeConcernError") && BSON_ITER_HOLDS_DOCUMENT (&iter)) { uint32_t len; const uint8_t *data; bson_t write_concern_error; char str[16]; const char *key; /* writeConcernError is a subdocument in the server response * append it to the result->writeConcernErrors array */ bson_iter_document (&iter, &len, &data); bson_init_static (&write_concern_error, data, len); bson_uint32_to_string ( result->n_writeConcernErrors, &key, str, sizeof str); if (!bson_append_document ( &result->writeConcernErrors, key, -1, &write_concern_error)) { MONGOC_ERROR ("Error adding \"%s\" to writeConcernErrors.\n", key); } result->n_writeConcernErrors++; } /* inefficient if there are ever large numbers: for each label in each err, * we linear-search result->errorLabels to see if it's included yet */ _mongoc_bson_array_copy_labels_to (reply, &result->errorLabels); EXIT; }
bool mongoc_gridfs_remove_by_filename (mongoc_gridfs_t *gridfs, const char *filename, bson_error_t *error) { mongoc_bulk_operation_t *bulk_files = NULL; mongoc_bulk_operation_t *bulk_chunks = NULL; mongoc_cursor_t *cursor = NULL; bson_error_t files_error; bson_error_t chunks_error; const bson_t *doc; const char *key; char keybuf[16]; int count = 0; bool chunks_ret; bool files_ret; bool ret = false; bson_iter_t iter; bson_t *files_q = NULL; bson_t *chunks_q = NULL; bson_t q = BSON_INITIALIZER; bson_t fields = BSON_INITIALIZER; bson_t ar = BSON_INITIALIZER; BSON_ASSERT (gridfs); if (!filename) { bson_set_error (error, MONGOC_ERROR_GRIDFS, MONGOC_ERROR_GRIDFS_INVALID_FILENAME, "A non-NULL filename must be specified."); return false; } /* * Find all files matching this filename. Hopefully just one, but not * strictly required! */ BSON_APPEND_UTF8 (&q, "filename", filename); BSON_APPEND_INT32 (&fields, "_id", 1); cursor = _mongoc_cursor_new (gridfs->client, gridfs->files->ns, MONGOC_QUERY_NONE, 0, 0, 0, false /* is command */, &q, &fields, NULL, NULL); BSON_ASSERT (cursor); while (mongoc_cursor_next (cursor, &doc)) { if (bson_iter_init_find (&iter, doc, "_id")) { const bson_value_t *value = bson_iter_value (&iter); bson_uint32_to_string (count, &key, keybuf, sizeof keybuf); BSON_APPEND_VALUE (&ar, key, value); } } if (mongoc_cursor_error (cursor, error)) { goto failure; } bulk_files = mongoc_collection_create_bulk_operation (gridfs->files, false, NULL); bulk_chunks = mongoc_collection_create_bulk_operation (gridfs->chunks, false, NULL); files_q = BCON_NEW ("_id", "{", "$in", BCON_ARRAY (&ar), "}"); chunks_q = BCON_NEW ("files_id", "{", "$in", BCON_ARRAY (&ar), "}"); mongoc_bulk_operation_remove (bulk_files, files_q); mongoc_bulk_operation_remove (bulk_chunks, chunks_q); files_ret = mongoc_bulk_operation_execute (bulk_files, NULL, &files_error); chunks_ret = mongoc_bulk_operation_execute (bulk_chunks, NULL, &chunks_error); if (error) { if (!files_ret) { memcpy (error, &files_error, sizeof *error); } else if (!chunks_ret) { memcpy (error, &chunks_error, sizeof *error); } } ret = (files_ret && chunks_ret); failure: if (cursor) { mongoc_cursor_destroy (cursor); } if (bulk_files) { mongoc_bulk_operation_destroy (bulk_files); } if (bulk_chunks) { mongoc_bulk_operation_destroy (bulk_chunks); } bson_destroy (&q); bson_destroy (&fields); bson_destroy (&ar); if (files_q) { bson_destroy (files_q); } if (chunks_q) { bson_destroy (chunks_q); } return ret; }
static void _mongoc_write_command(mongoc_write_command_t *command, mongoc_client_t *client, mongoc_server_stream_t *server_stream, const char *database, const char *collection, const mongoc_write_concern_t *write_concern, uint32_t offset, mongoc_write_result_t *result, bson_error_t *error) { const uint8_t *data; bson_iter_t iter; const char *key; uint32_t len = 0; bson_t tmp; bson_t ar; bson_t cmd; bson_t reply; char str [16]; bool has_more; bool ret = false; uint32_t i; int32_t max_bson_obj_size; int32_t max_write_batch_size; int32_t min_wire_version; uint32_t key_len; ENTRY; BSON_ASSERT (command); BSON_ASSERT (client); BSON_ASSERT (database); BSON_ASSERT (server_stream); BSON_ASSERT (collection); max_bson_obj_size = mongoc_server_stream_max_bson_obj_size (server_stream); max_write_batch_size = mongoc_server_stream_max_write_batch_size (server_stream); /* * If we have an unacknowledged write and the server supports the legacy * opcodes, then submit the legacy opcode so we don't need to wait for * a response from the server. */ min_wire_version = server_stream->sd->min_wire_version; if ((min_wire_version == 0) && !_mongoc_write_concern_needs_gle (write_concern)) { if (command->flags.bypass_document_validation != MONGOC_BYPASS_DOCUMENT_VALIDATION_DEFAULT) { bson_set_error (error, MONGOC_ERROR_COMMAND, MONGOC_ERROR_COMMAND_INVALID_ARG, "Cannot set bypassDocumentValidation for unacknowledged writes"); EXIT; } gLegacyWriteOps[command->type] (command, client, server_stream, database, collection, write_concern, offset, result, error); EXIT; } if (!command->n_documents || !bson_iter_init (&iter, command->documents) || !bson_iter_next (&iter)) { _empty_error (command, error); result->failed = true; EXIT; } again: bson_init (&cmd); has_more = false; i = 0; BSON_APPEND_UTF8 (&cmd, gCommandNames[command->type], collection); BSON_APPEND_DOCUMENT (&cmd, "writeConcern", WRITE_CONCERN_DOC (write_concern)); BSON_APPEND_BOOL (&cmd, "ordered", command->flags.ordered); if (command->flags.bypass_document_validation != MONGOC_BYPASS_DOCUMENT_VALIDATION_DEFAULT) { BSON_APPEND_BOOL (&cmd, "bypassDocumentValidation", !!command->flags.bypass_document_validation); } if (!_mongoc_write_command_will_overflow (0, command->documents->len, command->n_documents, max_bson_obj_size, max_write_batch_size)) { /* copy the whole documents buffer as e.g. "updates": [...] */ BSON_APPEND_ARRAY (&cmd, gCommandFields[command->type], command->documents); i = command->n_documents; } else { bson_append_array_begin (&cmd, gCommandFields[command->type], -1, &ar); do { if (!BSON_ITER_HOLDS_DOCUMENT (&iter)) { BSON_ASSERT (false); } bson_iter_document (&iter, &len, &data); key_len = (uint32_t) bson_uint32_to_string (i, &key, str, sizeof str); if (_mongoc_write_command_will_overflow (ar.len, key_len + len + 2, i, max_bson_obj_size, max_write_batch_size)) { has_more = true; break; } if (!bson_init_static (&tmp, data, len)) { BSON_ASSERT (false); } BSON_APPEND_DOCUMENT (&ar, key, &tmp); bson_destroy (&tmp); i++; } while (bson_iter_next (&iter)); bson_append_array_end (&cmd, &ar); } if (!i) { too_large_error (error, i, len, max_bson_obj_size, NULL); result->failed = true; ret = false; } else { ret = mongoc_cluster_run_command (&client->cluster, server_stream->stream, MONGOC_QUERY_NONE, database, &cmd, &reply, error); if (!ret) { result->failed = true; } _mongoc_write_result_merge (result, command, &reply, offset); offset += i; bson_destroy (&reply); } bson_destroy (&cmd); if (has_more && (ret || !command->flags.ordered)) { GOTO (again); } EXIT; }