static void _test_read_concern_wire_version (bool allow, bool explicit) { mongoc_read_concern_t *rc; bson_t opts = BSON_INITIALIZER; mock_server_t *server; mongoc_client_t *client; mongoc_collection_t *collection; mongoc_cursor_t *cursor; const bson_t *doc; future_t *future; request_t *request; bson_error_t error; rc = mongoc_read_concern_new (); mongoc_read_concern_set_level (rc, "foo"); server = mock_server_with_autoismaster ( allow ? WIRE_VERSION_READ_CONCERN : WIRE_VERSION_READ_CONCERN - 1); mock_server_run (server); client = mongoc_client_new_from_uri (mock_server_get_uri (server)); collection = mongoc_client_get_collection (client, "db", "collection"); if (explicit) { mongoc_read_concern_append (rc, &opts); } else {
static void test_read_concern_append (void) { mongoc_read_concern_t *rc; bson_t *cmd; cmd = tmp_bson ("{'foo': 1}"); /* append default readConcern */ rc = mongoc_read_concern_new (); ASSERT (mongoc_read_concern_is_default (rc)); ASSERT_MATCH (cmd, "{'foo': 1, 'readConcern': {'$exists': false}}"); /* append readConcern with level */ mongoc_read_concern_set_level (rc, MONGOC_READ_CONCERN_LEVEL_LOCAL); ASSERT (mongoc_read_concern_append (rc, cmd)); ASSERT_MATCH (cmd, "{'foo': 1, 'readConcern': {'level': 'local'}}"); mongoc_read_concern_destroy (rc); }
static void set_func_opt (bson_t *opts, mongoc_read_prefs_t **prefs_ptr, opt_type_t opt_type) { SET_OPT_PREAMBLE (function); switch (opt_type) { case OPT_READ_CONCERN: BSON_ASSERT (mongoc_read_concern_append (rc, opts)); break; case OPT_WRITE_CONCERN: BSON_ASSERT (mongoc_write_concern_append (wc, opts)); break; case OPT_READ_PREFS: *prefs_ptr = mongoc_read_prefs_copy (prefs); break; default: abort (); } SET_OPT_CLEANUP; }
/*--------------------------------------------------------------------------- * * _make_cursor -- * * Construct and send the aggregate command and create the resulting * cursor. On error, stream->cursor remains NULL, otherwise it is * created and must be destroyed. * * Return: * False on error and sets stream->err. * *-------------------------------------------------------------------------- */ static bool _make_cursor (mongoc_change_stream_t *stream) { mongoc_client_session_t *cs = NULL; bson_t command_opts; bson_t command; /* { aggregate: "coll", pipeline: [], ... } */ bson_t reply; bson_t getmore_opts = BSON_INITIALIZER; bson_iter_t iter; mongoc_server_description_t *sd; uint32_t server_id; int32_t max_wire_version = -1; BSON_ASSERT (stream); BSON_ASSERT (!stream->cursor); _make_command (stream, &command); bson_copy_to (&(stream->opts.extra), &command_opts); sd = mongoc_client_select_server ( stream->client, false /* for_writes */, stream->read_prefs, &stream->err); if (!sd) { goto cleanup; } server_id = mongoc_server_description_id (sd); bson_append_int32 (&command_opts, "serverId", 8, server_id); bson_append_int32 (&getmore_opts, "serverId", 8, server_id); max_wire_version = sd->max_wire_version; mongoc_server_description_destroy (sd); if (bson_iter_init_find (&iter, &command_opts, "sessionId")) { if (!_mongoc_client_session_from_iter ( stream->client, &iter, &cs, &stream->err)) { goto cleanup; } } else if (stream->implicit_session) { /* If an implicit session was created before, and this cursor is now * being recreated after resuming, then use the same session as before. */ cs = stream->implicit_session; if (!mongoc_client_session_append (cs, &command_opts, &stream->err)) { goto cleanup; } } else { /* Create an implicit session. This session lsid must be the same for the * agg command and the subsequent getMores. Thus, this implicit session is * passed as if it were an explicit session to * collection_read_command_with_opts and cursor_new_from_reply, but it is * still implicit and its lifetime is owned by this change_stream_t. */ mongoc_session_opt_t *session_opts; session_opts = mongoc_session_opts_new (); mongoc_session_opts_set_causal_consistency (session_opts, false); /* returns NULL if sessions aren't supported. ignore errors. */ cs = mongoc_client_start_session (stream->client, session_opts, NULL); stream->implicit_session = cs; mongoc_session_opts_destroy (session_opts); if (cs && !mongoc_client_session_append (cs, &command_opts, &stream->err)) { goto cleanup; } } if (cs && !mongoc_client_session_append (cs, &getmore_opts, &stream->err)) { goto cleanup; } if (stream->read_concern && !bson_has_field (&command_opts, "readConcern")) { mongoc_read_concern_append (stream->read_concern, &command_opts); } /* even though serverId has already been set, still pass the read prefs. * they are necessary for OP_MSG if sending to a secondary. */ if (!mongoc_client_read_command_with_opts (stream->client, stream->db, &command, stream->read_prefs, &command_opts, &reply, &stream->err)) { bson_destroy (&stream->err_doc); bson_copy_to (&reply, &stream->err_doc); bson_destroy (&reply); goto cleanup; } bson_append_bool ( &getmore_opts, MONGOC_CURSOR_TAILABLE, MONGOC_CURSOR_TAILABLE_LEN, true); bson_append_bool (&getmore_opts, MONGOC_CURSOR_AWAIT_DATA, MONGOC_CURSOR_AWAIT_DATA_LEN, true); /* maxTimeMS is only appended to getMores if these are set in cursor opts. */ if (stream->max_await_time_ms > 0) { bson_append_int64 (&getmore_opts, MONGOC_CURSOR_MAX_AWAIT_TIME_MS, MONGOC_CURSOR_MAX_AWAIT_TIME_MS_LEN, stream->max_await_time_ms); } if (stream->batch_size > 0) { bson_append_int32 (&getmore_opts, MONGOC_CURSOR_BATCH_SIZE, MONGOC_CURSOR_BATCH_SIZE_LEN, stream->batch_size); } /* Change streams spec: "If neither startAtOperationTime nor resumeAfter are * specified, and the max wire version is >= 7, and the initial aggregate * command does not return a resumeToken (indicating no results), the * ChangeStream MUST save the operationTime from the initial aggregate * command when it returns." */ if (bson_empty (&stream->resume_token) && _mongoc_timestamp_empty (&stream->operation_time) && max_wire_version >= 7 && bson_iter_init_find (&iter, &reply, "operationTime")) { _mongoc_timestamp_set_from_bson (&stream->operation_time, &iter); } /* steals reply. */ stream->cursor = mongoc_cursor_new_from_command_reply_with_opts ( stream->client, &reply, &getmore_opts); cleanup: bson_destroy (&command); bson_destroy (&command_opts); bson_destroy (&getmore_opts); return stream->err.code == 0; }