bool mongoc_gridfs_file_remove (mongoc_gridfs_file_t *file, bson_error_t *error) { bson_t sel = BSON_INITIALIZER; bool ret = false; BSON_ASSERT (file); BSON_APPEND_VALUE (&sel, "_id", &file->files_id); if (!mongoc_collection_delete_one ( file->gridfs->files, &sel, NULL, NULL, error)) { goto cleanup; } bson_reinit (&sel); BSON_APPEND_VALUE (&sel, "files_id", &file->files_id); if (!mongoc_collection_delete_many ( file->gridfs->chunks, &sel, NULL, NULL, error)) { goto cleanup; } ret = true; cleanup: bson_destroy (&sel); return ret; }
static void mongoc_uri_bson_append_or_replace_key (bson_t *options, const char *option, const char *value) { bson_iter_t iter; bool found = false; if (bson_iter_init (&iter, options)) { bson_t tmp = BSON_INITIALIZER; while (bson_iter_next (&iter)) { const bson_value_t *bvalue; if (!strcasecmp(bson_iter_key (&iter), option)) { bson_append_utf8(&tmp, option, -1, value, -1); found = true; continue; } bvalue = bson_iter_value (&iter); BSON_APPEND_VALUE (&tmp, bson_iter_key (&iter), bvalue); } if (! found) { bson_append_utf8(&tmp, option, -1, value, -1); } bson_destroy (options); bson_copy_to (&tmp, options); bson_destroy (&tmp); } }
/*--------------------------------------------------------------------------- * * _change_stream_init -- * * Called after @stream has the collection name, database name, read * preferences, and read concern set. Creates the change streams * cursor. * *-------------------------------------------------------------------------- */ void _change_stream_init (mongoc_change_stream_t *stream, const bson_t *pipeline, const bson_t *opts) { BSON_ASSERT (pipeline); stream->max_await_time_ms = -1; stream->batch_size = -1; bson_init (&stream->pipeline_to_append); bson_init (&stream->resume_token); bson_init (&stream->err_doc); if (!_mongoc_change_stream_opts_parse ( stream->client, opts, &stream->opts, &stream->err)) { return; } stream->full_document = BCON_NEW ("fullDocument", stream->opts.fullDocument); if (!bson_empty (&(stream->opts.resumeAfter))) { bson_append_document ( &stream->resume_token, "resumeAfter", 11, &(stream->opts.resumeAfter)); } _mongoc_timestamp_set (&stream->operation_time, &(stream->opts.startAtOperationTime)); stream->batch_size = stream->opts.batchSize; stream->max_await_time_ms = stream->opts.maxAwaitTimeMS; /* Accept two forms of user pipeline: * 1. A document like: { "pipeline": [...] } * 2. An array-like document: { "0": {}, "1": {}, ... } * If the passed pipeline is invalid, we pass it along and let the server * error instead. */ if (!bson_empty (pipeline)) { bson_iter_t iter; if (bson_iter_init_find (&iter, pipeline, "pipeline") && BSON_ITER_HOLDS_ARRAY (&iter)) { if (!BSON_APPEND_VALUE (&stream->pipeline_to_append, "pipeline", bson_iter_value (&iter))) { CHANGE_STREAM_ERR ("pipeline"); } } else { if (!BSON_APPEND_ARRAY ( &stream->pipeline_to_append, "pipeline", pipeline)) { CHANGE_STREAM_ERR ("pipeline"); } } } if (stream->err.code == 0) { (void) _make_cursor (stream); } }
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); }
bool mongoc_gridfs_file_remove (mongoc_gridfs_file_t *file, bson_error_t *error) { bson_t sel = BSON_INITIALIZER; bool ret = false; bson_return_val_if_fail (file, false); BSON_APPEND_VALUE (&sel, "_id", &file->files_id); if (!mongoc_collection_remove (file->gridfs->files, MONGOC_REMOVE_SINGLE_REMOVE, &sel, NULL, error)) { goto cleanup; } bson_reinit (&sel); BSON_APPEND_VALUE (&sel, "files_id", &file->files_id); if (!mongoc_collection_remove (file->gridfs->chunks, MONGOC_REMOVE_NONE, &sel, NULL, error)) { goto cleanup; } ret = true; cleanup: bson_destroy (&sel); return ret; }
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++; }
/* complete a write result, including only certain fields */ bool _mongoc_write_result_complete ( mongoc_write_result_t *result, /* IN */ int32_t error_api_version, /* IN */ const mongoc_write_concern_t *wc, /* IN */ mongoc_error_domain_t err_domain_override, /* IN */ bson_t *bson, /* OUT */ bson_error_t *error, /* OUT */ ...) { mongoc_error_domain_t domain; va_list args; const char *field; int n_args; bson_iter_t iter; bson_iter_t child; ENTRY; BSON_ASSERT (result); if (error_api_version >= MONGOC_ERROR_API_VERSION_2) { domain = MONGOC_ERROR_SERVER; } else if (err_domain_override) { domain = err_domain_override; } else if (result->error.domain) { domain = (mongoc_error_domain_t) result->error.domain; } else { domain = MONGOC_ERROR_COLLECTION; } /* produce either old fields like nModified from the deprecated Bulk API Spec * or new fields like modifiedCount from the CRUD Spec, which we partly obey */ if (bson && mongoc_write_concern_is_acknowledged (wc)) { n_args = 0; va_start (args, error); while ((field = va_arg (args, const char *))) { n_args++; if (!strcmp (field, "nInserted")) { BSON_APPEND_INT32 (bson, field, result->nInserted); } else if (!strcmp (field, "insertedCount")) { BSON_APPEND_INT32 (bson, field, result->nInserted); } else if (!strcmp (field, "nMatched")) { BSON_APPEND_INT32 (bson, field, result->nMatched); } else if (!strcmp (field, "matchedCount")) { BSON_APPEND_INT32 (bson, field, result->nMatched); } else if (!strcmp (field, "nModified")) { BSON_APPEND_INT32 (bson, field, result->nModified); } else if (!strcmp (field, "modifiedCount")) { BSON_APPEND_INT32 (bson, field, result->nModified); } else if (!strcmp (field, "nRemoved")) { BSON_APPEND_INT32 (bson, field, result->nRemoved); } else if (!strcmp (field, "deletedCount")) { BSON_APPEND_INT32 (bson, field, result->nRemoved); } else if (!strcmp (field, "nUpserted")) { BSON_APPEND_INT32 (bson, field, result->nUpserted); } else if (!strcmp (field, "upsertedCount")) { BSON_APPEND_INT32 (bson, field, result->nUpserted); } else if (!strcmp (field, "upserted") && !bson_empty0 (&result->upserted)) { BSON_APPEND_ARRAY (bson, field, &result->upserted); } else if (!strcmp (field, "upsertedId") && !bson_empty0 (&result->upserted) && bson_iter_init_find (&iter, &result->upserted, "0") && bson_iter_recurse (&iter, &child) && bson_iter_find (&child, "_id")) { /* "upsertedId", singular, for update_one() */ BSON_APPEND_VALUE (bson, "upsertedId", bson_iter_value (&child)); } } va_end (args); /* default: a standard result includes all Bulk API fields */ if (!n_args) { BSON_APPEND_INT32 (bson, "nInserted", result->nInserted); BSON_APPEND_INT32 (bson, "nMatched", result->nMatched); BSON_APPEND_INT32 (bson, "nModified", result->nModified); BSON_APPEND_INT32 (bson, "nRemoved", result->nRemoved); BSON_APPEND_INT32 (bson, "nUpserted", result->nUpserted); if (!bson_empty0 (&result->upserted)) { BSON_APPEND_ARRAY (bson, "upserted", &result->upserted); } } /* always append errors if there are any */ if (!n_args || !bson_empty (&result->writeErrors)) { BSON_APPEND_ARRAY (bson, "writeErrors", &result->writeErrors); } if (result->n_writeConcernErrors) { BSON_APPEND_ARRAY ( bson, "writeConcernErrors", &result->writeConcernErrors); } }
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; }
/** * _mongoc_gridfs_file_refresh_page: * * Refresh a GridFS file's underlying page. This recalculates the current * page number based on the file's stream position, then fetches that page * from the database. * * Note that this fetch is unconditional and the page is queried from the * database even if the current page covers the same theoretical chunk. * * * Side Effects: * * file->page is loaded with the appropriate buffer, fetched from the * database. If the file position is at the end of the file and on a new * chunk boundary, a new page is created. If the position is far past the * end of the file, _mongoc_gridfs_file_extend is responsible for creating * chunks to file the gap. * * file->n is set based on file->pos. file->error is set on error. */ static bool _mongoc_gridfs_file_refresh_page (mongoc_gridfs_file_t *file) { bson_t query; bson_t child; bson_t opts; const bson_t *chunk; const char *key; bson_iter_t iter; int64_t existing_chunks; int64_t required_chunks; const uint8_t *data = NULL; uint32_t len; ENTRY; BSON_ASSERT (file); file->n = (int32_t) (file->pos / file->chunk_size); if (file->page) { _mongoc_gridfs_file_page_destroy (file->page); file->page = NULL; } /* if the file pointer is past the end of the current file (i.e. pointing to * a new chunk), we'll pass the page constructor a new empty page. */ existing_chunks = divide_round_up (file->length, file->chunk_size); required_chunks = divide_round_up (file->pos + 1, file->chunk_size); if (required_chunks > existing_chunks) { data = (uint8_t *) ""; len = 0; } else { /* if we have a cursor, but the cursor doesn't have the chunk we're going * to need, destroy it (we'll grab a new one immediately there after) */ if (file->cursor && !_mongoc_gridfs_file_keep_cursor (file)) { mongoc_cursor_destroy (file->cursor); file->cursor = NULL; } if (!file->cursor) { bson_init (&query); BSON_APPEND_VALUE (&query, "files_id", &file->files_id); BSON_APPEND_DOCUMENT_BEGIN (&query, "n", &child); BSON_APPEND_INT32 (&child, "$gte", file->n); bson_append_document_end (&query, &child); bson_init (&opts); BSON_APPEND_DOCUMENT_BEGIN (&opts, "sort", &child); BSON_APPEND_INT32 (&child, "n", 1); bson_append_document_end (&opts, &child); BSON_APPEND_DOCUMENT_BEGIN (&opts, "projection", &child); BSON_APPEND_INT32 (&child, "n", 1); BSON_APPEND_INT32 (&child, "data", 1); BSON_APPEND_INT32 (&child, "_id", 0); bson_append_document_end (&opts, &child); /* find all chunks greater than or equal to our current file pos */ file->cursor = mongoc_collection_find_with_opts ( file->gridfs->chunks, &query, &opts, NULL); file->cursor_range[0] = file->n; file->cursor_range[1] = (uint32_t) (file->length / file->chunk_size); bson_destroy (&query); bson_destroy (&opts); BSON_ASSERT (file->cursor); } /* we might have had a cursor before, then seeked ahead past a chunk. * iterate until we're on the right chunk */ while (file->cursor_range[0] <= file->n) { if (!mongoc_cursor_next (file->cursor, &chunk)) { /* copy cursor error; if there's none, we're missing a chunk */ if (!mongoc_cursor_error (file->cursor, &file->error)) { missing_chunk (file); } RETURN (0); } file->cursor_range[0]++; } BSON_ASSERT (bson_iter_init (&iter, chunk)); /* grab out what we need from the chunk */ while (bson_iter_next (&iter)) { key = bson_iter_key (&iter); if (strcmp (key, "n") == 0) { if (file->n != bson_iter_int32 (&iter)) { missing_chunk (file); RETURN (0); } } else if (strcmp (key, "data") == 0) { bson_iter_binary (&iter, NULL, &len, &data); } else { /* Unexpected key. This should never happen */ RETURN (0); } } if (file->n != file->pos / file->chunk_size) { return 0; } } if (!data) { bson_set_error (&file->error, MONGOC_ERROR_GRIDFS, MONGOC_ERROR_GRIDFS_CHUNK_MISSING, "corrupt chunk number %" PRId32, file->n); RETURN (0); } file->page = _mongoc_gridfs_file_page_new (data, len, file->chunk_size); /* seek in the page towards wherever we're supposed to be */ RETURN ( _mongoc_gridfs_file_page_seek (file->page, file->pos % file->chunk_size)); }
bool mongoc_change_stream_next (mongoc_change_stream_t *stream, const bson_t **bson) { bson_iter_t iter; bool ret = false; BSON_ASSERT (stream); BSON_ASSERT (bson); if (stream->err.code != 0) { goto end; } BSON_ASSERT (stream->cursor); if (!mongoc_cursor_next (stream->cursor, bson)) { const bson_t *err_doc; bson_error_t err; bool resumable = false; if (!mongoc_cursor_error_document (stream->cursor, &err, &err_doc)) { /* no error occurred, just no documents left. */ goto end; } resumable = _is_resumable_error (err_doc); if (resumable) { /* recreate the cursor. */ mongoc_cursor_destroy (stream->cursor); stream->cursor = NULL; if (!_make_cursor (stream)) { goto end; } if (!mongoc_cursor_next (stream->cursor, bson)) { resumable = !mongoc_cursor_error_document (stream->cursor, &err, &err_doc); if (resumable) { /* empty batch. */ goto end; } } } if (!resumable) { stream->err = err; bson_destroy (&stream->err_doc); bson_copy_to (err_doc, &stream->err_doc); goto end; } } /* we have received documents, either from the first call to next or after a * resume. */ if (!bson_iter_init_find (&iter, *bson, "_id")) { bson_set_error (&stream->err, MONGOC_ERROR_CURSOR, MONGOC_ERROR_CHANGE_STREAM_NO_RESUME_TOKEN, "Cannot provide resume functionality when the resume " "token is missing"); goto end; } /* copy the resume token. */ bson_reinit (&stream->resume_token); BSON_APPEND_VALUE ( &stream->resume_token, "resumeAfter", bson_iter_value (&iter)); /* clear out the operation time, since we no longer need it to resume. */ _mongoc_timestamp_clear (&stream->operation_time); ret = true; end: /* Driver Sessions Spec: "When an implicit session is associated with a * cursor for use with getMore operations, the session MUST be returned to * the pool immediately following a getMore operation that indicates that the * cursor has been exhausted." */ if (stream->implicit_session) { /* if creating the change stream cursor errored, it may be null. */ if (!stream->cursor || stream->cursor->cursor_id == 0) { mongoc_client_session_destroy (stream->implicit_session); stream->implicit_session = NULL; } } return ret; }