Array HHVM_METHOD(MongoDBDriverReadPreference, __debugInfo) { MongoDBDriverReadPreferenceData* data = Native::data<MongoDBDriverReadPreferenceData>(this_); Array retval = Array::Create(); Variant v_tags; const bson_t *tags = mongoc_read_prefs_get_tags(data->m_read_preference); mongoc_read_mode_t mode = mongoc_read_prefs_get_mode(data->m_read_preference); switch (mode) { case MONGOC_READ_PRIMARY: retval.set(s_mode, "primary"); break; case MONGOC_READ_PRIMARY_PREFERRED: retval.set(s_mode, "primaryPreferred"); break; case MONGOC_READ_SECONDARY: retval.set(s_mode, "secondary"); break; case MONGOC_READ_SECONDARY_PREFERRED: retval.set(s_mode, "secondaryPreferred"); break; case MONGOC_READ_NEAREST: retval.set(s_mode, "nearest"); break; default: /* Do nothing */ break; } if (!bson_empty(tags)) { hippo_bson_conversion_options_t options = HIPPO_TYPEMAP_INITIALIZER; BsonToVariantConverter convertor(bson_get_data(tags), tags->len, options); convertor.convert(&v_tags); retval.set(s_tags, v_tags.toArray()); } if (mongoc_read_prefs_get_max_staleness_ms(data->m_read_preference) != 0) { retval.set(s_maxStalenessMS, mongoc_read_prefs_get_max_staleness_ms(data->m_read_preference)); } return retval; }
Array HHVM_METHOD(MongoDBDriverReadPreference, getTagSets) { MongoDBDriverReadPreferenceData* data = Native::data<MongoDBDriverReadPreferenceData>(this_); Variant v_tags; const bson_t *tags = mongoc_read_prefs_get_tags(data->m_read_preference); hippo_bson_conversion_options_t options = HIPPO_TYPEMAP_DEBUG_INITIALIZER; BsonToVariantConverter convertor(bson_get_data(tags), tags->len, options); convertor.convert(&v_tags); return v_tags.toArray(); }
static void _mongoc_uri_assign_read_prefs_mode (mongoc_uri_t *uri) /* IN */ { const char *str; bson_iter_t iter; BSON_ASSERT(uri); if (mongoc_uri_get_option_as_bool (uri, "slaveok", false)) { mongoc_read_prefs_set_mode(uri->read_prefs, MONGOC_READ_SECONDARY_PREFERRED); } if (bson_iter_init_find_case(&iter, &uri->options, "readpreference") && BSON_ITER_HOLDS_UTF8(&iter)) { str = bson_iter_utf8(&iter, NULL); if (0 == strcasecmp("primary", str)) { mongoc_read_prefs_set_mode(uri->read_prefs, MONGOC_READ_PRIMARY); } else if (0 == strcasecmp("primarypreferred", str)) { mongoc_read_prefs_set_mode(uri->read_prefs, MONGOC_READ_PRIMARY_PREFERRED); } else if (0 == strcasecmp("secondary", str)) { mongoc_read_prefs_set_mode(uri->read_prefs, MONGOC_READ_SECONDARY); } else if (0 == strcasecmp("secondarypreferred", str)) { mongoc_read_prefs_set_mode(uri->read_prefs, MONGOC_READ_SECONDARY_PREFERRED); } else if (0 == strcasecmp("nearest", str)) { mongoc_read_prefs_set_mode(uri->read_prefs, MONGOC_READ_NEAREST); } else { MONGOC_WARNING("Unsupported readPreference value [readPreference=%s].", str); } } /* Warn on conflict, since read preference will be validated later */ if (mongoc_read_prefs_get_mode(uri->read_prefs) == MONGOC_READ_PRIMARY && !bson_empty(mongoc_read_prefs_get_tags(uri->read_prefs))) { MONGOC_WARNING("Primary read preference mode conflicts with tags."); } }
mongoc_cursor_t * _mongoc_cursor_new (mongoc_client_t *client, const char *db_and_collection, mongoc_query_flags_t flags, bson_uint32_t skip, bson_uint32_t limit, bson_uint32_t batch_size, bson_bool_t is_command, const bson_t *query, const bson_t *fields, const mongoc_read_prefs_t *read_prefs) { mongoc_read_mode_t mode; mongoc_cursor_t *cursor; const bson_t *tags; const char *mode_str; bson_t child; ENTRY; BSON_ASSERT(client); BSON_ASSERT(db_and_collection); BSON_ASSERT(query); /* we can't have exhaust queries with limits */ BSON_ASSERT (!((flags & MONGOC_QUERY_EXHAUST) && limit)); /* we can't have exhaust queries with sharded clusters */ BSON_ASSERT (!((flags & MONGOC_QUERY_EXHAUST) && client->cluster.isdbgrid)); /* * Cursors execute their query lazily. This sadly means that we must copy * some extra data around between the bson_t structures. This should be * small in most cases, so it reduces to a pure memcpy. The benefit to this * design is simplified error handling by API consumers. */ cursor = bson_malloc0(sizeof *cursor); cursor->client = client; strncpy(cursor->ns, db_and_collection, sizeof cursor->ns - 1); cursor->nslen = strlen(cursor->ns); cursor->flags = flags; cursor->skip = skip; cursor->limit = limit; cursor->batch_size = cursor->batch_size; cursor->is_command = is_command; if (!bson_has_field (query, "$query")) { bson_init (&cursor->query); bson_append_document (&cursor->query, "$query", 6, query); } else { bson_copy_to (query, &cursor->query); } if (read_prefs) { cursor->read_prefs = mongoc_read_prefs_copy (read_prefs); mode = mongoc_read_prefs_get_mode (read_prefs); tags = mongoc_read_prefs_get_tags (read_prefs); if (mode != MONGOC_READ_PRIMARY) { flags |= MONGOC_QUERY_SLAVE_OK; if ((mode != MONGOC_READ_SECONDARY_PREFERRED) || tags) { bson_append_document_begin (&cursor->query, "$readPreference", 15, &child); mode_str = _mongoc_cursor_get_read_mode_string (mode); bson_append_utf8 (&child, "mode", 4, mode_str, -1); if (tags) { bson_append_array (&child, "tags", 4, tags); } bson_append_document_end (&cursor->query, &child); } } } if (fields) { bson_copy_to(fields, &cursor->fields); } else { bson_init(&cursor->fields); } _mongoc_buffer_init(&cursor->buffer, NULL, 0, NULL); mongoc_counter_cursors_active_inc(); RETURN(cursor); }
/* Update result with the read prefs, following Server Selection Spec. * The driver must have discovered the server is a mongos. */ static void _apply_read_preferences_mongos (const mongoc_read_prefs_t *read_prefs, const bson_t *query_bson, mongoc_apply_read_prefs_result_t *result /* OUT */) { mongoc_read_mode_t mode; const bson_t *tags = NULL; bson_t child; const char *mode_str; mode = mongoc_read_prefs_get_mode (read_prefs); if (read_prefs) { tags = mongoc_read_prefs_get_tags (read_prefs); } /* Server Selection Spec says: * * For mode 'primary', drivers MUST NOT set the slaveOK wire protocol flag * and MUST NOT use $readPreference * * For mode 'secondary', drivers MUST set the slaveOK wire protocol flag and * MUST also use $readPreference * * For mode 'primaryPreferred', drivers MUST set the slaveOK wire protocol * flag and MUST also use $readPreference * * For mode 'secondaryPreferred', drivers MUST set the slaveOK wire protocol * flag. If the read preference contains a non-empty tag_sets parameter, * drivers MUST use $readPreference; otherwise, drivers MUST NOT use * $readPreference * * For mode 'nearest', drivers MUST set the slaveOK wire protocol flag and * MUST also use $readPreference */ if (mode == MONGOC_READ_SECONDARY_PREFERRED && bson_empty0 (tags)) { result->flags |= MONGOC_QUERY_SLAVE_OK; } else if (mode != MONGOC_READ_PRIMARY) { result->flags |= MONGOC_QUERY_SLAVE_OK; /* Server Selection Spec: "When any $ modifier is used, including the * $readPreference modifier, the query MUST be provided using the $query * modifier". * * This applies to commands, too. */ result->query_with_read_prefs = bson_new (); result->query_owned = true; if (bson_has_field (query_bson, "$query")) { bson_concat (result->query_with_read_prefs, query_bson); } else { bson_append_document (result->query_with_read_prefs, "$query", 6, query_bson); } bson_append_document_begin (result->query_with_read_prefs, "$readPreference", 15, &child); mode_str = _get_read_mode_string (mode); bson_append_utf8 (&child, "mode", 4, mode_str, -1); if (!bson_empty0 (tags)) { bson_append_array (&child, "tags", 4, tags); } bson_append_document_end (result->query_with_read_prefs, &child); } }
mongoc_cursor_t * _mongoc_cursor_new (mongoc_client_t *client, const char *db_and_collection, mongoc_query_flags_t flags, uint32_t skip, uint32_t limit, uint32_t batch_size, bool is_command, const bson_t *query, const bson_t *fields, const mongoc_read_prefs_t *read_prefs) { mongoc_read_prefs_t *local_read_prefs = NULL; mongoc_read_mode_t mode; mongoc_cursor_t *cursor; const bson_t *tags; bson_iter_t iter; const char *key; const char *mode_str; bson_t child; bool found = false; int i; ENTRY; BSON_ASSERT (client); BSON_ASSERT (db_and_collection); BSON_ASSERT (query); if (!read_prefs) { read_prefs = client->read_prefs; } cursor = bson_malloc0 (sizeof *cursor); /* * CDRIVER-244: * * If this is a command, we need to verify we can send it to the location * specified by the read preferences. Otherwise, log a warning that we * are rerouting to the primary instance. */ if (is_command && read_prefs && (mongoc_read_prefs_get_mode (read_prefs) != MONGOC_READ_PRIMARY) && bson_iter_init (&iter, query) && bson_iter_next (&iter) && (key = bson_iter_key (&iter))) { for (i = 0; gSecondaryOkCommands [i]; i++) { if (0 == strcasecmp (key, gSecondaryOkCommands [i])) { found = true; break; } } if (!found) { cursor->redir_primary = true; local_read_prefs = mongoc_read_prefs_new (MONGOC_READ_PRIMARY); read_prefs = local_read_prefs; MONGOC_INFO ("Database command \"%s\" rerouted to primary node", key); } } /* * Cursors execute their query lazily. This sadly means that we must copy * some extra data around between the bson_t structures. This should be * small in most cases, so it reduces to a pure memcpy. The benefit to this * design is simplified error handling by API consumers. */ cursor->client = client; bson_strncpy (cursor->ns, db_and_collection, sizeof cursor->ns); cursor->nslen = (uint32_t)strlen(cursor->ns); cursor->flags = flags; cursor->skip = skip; cursor->limit = limit; cursor->batch_size = batch_size; cursor->is_command = is_command; #define MARK_FAILED(c) \ do { \ (c)->failed = true; \ (c)->done = true; \ (c)->end_of_event = true; \ (c)->sent = true; \ } while (0) /* we can't have exhaust queries with limits */ if ((flags & MONGOC_QUERY_EXHAUST) && limit) { bson_set_error (&cursor->error, MONGOC_ERROR_CURSOR, MONGOC_ERROR_CURSOR_INVALID_CURSOR, "Cannot specify MONGOC_QUERY_EXHAUST and set a limit."); MARK_FAILED (cursor); GOTO (finish); } /* we can't have exhaust queries with sharded clusters */ if ((flags & MONGOC_QUERY_EXHAUST) && (client->cluster.mode == MONGOC_CLUSTER_SHARDED_CLUSTER)) { bson_set_error (&cursor->error, MONGOC_ERROR_CURSOR, MONGOC_ERROR_CURSOR_INVALID_CURSOR, "Cannot specify MONGOC_QUERY_EXHAUST with sharded cluster."); MARK_FAILED (cursor); GOTO (finish); } /* * Check types of various optional parameters. */ if (!is_command) { if (bson_iter_init_find (&iter, query, "$explain") && !(BSON_ITER_HOLDS_BOOL (&iter) || BSON_ITER_HOLDS_INT32 (&iter))) { bson_set_error (&cursor->error, MONGOC_ERROR_CURSOR, MONGOC_ERROR_CURSOR_INVALID_CURSOR, "$explain must be a boolean."); MARK_FAILED (cursor); GOTO (finish); } if (bson_iter_init_find (&iter, query, "$snapshot") && !BSON_ITER_HOLDS_BOOL (&iter) && !BSON_ITER_HOLDS_INT32 (&iter)) { bson_set_error (&cursor->error, MONGOC_ERROR_CURSOR, MONGOC_ERROR_CURSOR_INVALID_CURSOR, "$snapshot must be a boolean."); MARK_FAILED (cursor); GOTO (finish); } } if (!cursor->is_command && !bson_has_field (query, "$query")) { bson_init (&cursor->query); bson_append_document (&cursor->query, "$query", 6, query); } else { bson_copy_to (query, &cursor->query); } if (read_prefs) { cursor->read_prefs = mongoc_read_prefs_copy (read_prefs); mode = mongoc_read_prefs_get_mode (read_prefs); tags = mongoc_read_prefs_get_tags (read_prefs); if (mode != MONGOC_READ_PRIMARY) { flags |= MONGOC_QUERY_SLAVE_OK; if ((mode != MONGOC_READ_SECONDARY_PREFERRED) || tags) { bson_append_document_begin (&cursor->query, "$readPreference", 15, &child); mode_str = _mongoc_cursor_get_read_mode_string (mode); bson_append_utf8 (&child, "mode", 4, mode_str, -1); if (tags) { bson_append_array (&child, "tags", 4, tags); } bson_append_document_end (&cursor->query, &child); } } } if (fields) { bson_copy_to(fields, &cursor->fields); } else { bson_init(&cursor->fields); } _mongoc_buffer_init(&cursor->buffer, NULL, 0, NULL); finish: mongoc_counter_cursors_active_inc(); if (local_read_prefs) { mongoc_read_prefs_destroy (local_read_prefs); } RETURN (cursor); }
const bson_t * mongoc_uri_get_read_prefs (const mongoc_uri_t *uri) { BSON_ASSERT (uri); return mongoc_read_prefs_get_tags(uri->read_prefs); }
static bool hippo_mongo_driver_manager_apply_rp(mongoc_uri_t *uri, const Array options) { mongoc_read_prefs_t *new_rp; const mongoc_read_prefs_t *old_rp; const char *rp_str = NULL; bson_t *b_tags; if (!(old_rp = mongoc_uri_get_read_prefs_t(uri))) { throw MongoDriver::Utils::throwRunTimeException("mongoc_uri_t does not have a read preference"); return false; } if (options.size() == 0) { return true; } if ( !options.exists(s_MongoDBDriverManager_slaveok) && !options.exists(s_MongoDBDriverManager_readpreference) && !options.exists(s_MongoDBDriverManager_readpreferencetags) && !options.exists(s_MongoDBDriverManager_readPreference) && !options.exists(s_MongoDBDriverManager_readPreferenceTags) ) { return true; } new_rp = mongoc_read_prefs_copy(old_rp); if (options.exists(s_MongoDBDriverManager_slaveok) && options[s_MongoDBDriverManager_slaveok].isBoolean()) { mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_SECONDARY_PREFERRED); } if (options.exists(s_MongoDBDriverManager_readpreference) && options[s_MongoDBDriverManager_readpreference].isString()) { rp_str = options[s_MongoDBDriverManager_readpreference].toString().c_str(); } if (options.exists(s_MongoDBDriverManager_readPreference) && options[s_MongoDBDriverManager_readPreference].isString()) { rp_str = options[s_MongoDBDriverManager_readPreference].toString().c_str(); } if (rp_str) { if (0 == strcasecmp("primary", rp_str)) { mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_PRIMARY); } else if (0 == strcasecmp("primarypreferred", rp_str)) { mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_PRIMARY_PREFERRED); } else if (0 == strcasecmp("secondary", rp_str)) { mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_SECONDARY); } else if (0 == strcasecmp("secondarypreferred", rp_str)) { mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_SECONDARY_PREFERRED); } else if (0 == strcasecmp("nearest", rp_str)) { mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_NEAREST); } else { throw MongoDriver::Utils::throwInvalidArgumentException("Unsupported readPreference value: '" + Variant(rp_str).toString() + "'"); mongoc_read_prefs_destroy(new_rp); return false; } } if (options.exists(s_MongoDBDriverManager_readpreferencetags) && options[s_MongoDBDriverManager_readpreferencetags].isArray()) { VariantToBsonConverter converter(options[s_MongoDBDriverManager_readpreferencetags].toArray(), HIPPO_BSON_NO_FLAGS); b_tags = bson_new(); converter.convert(b_tags); mongoc_read_prefs_set_tags(new_rp, b_tags); } else if (options.exists(s_MongoDBDriverManager_readPreferenceTags) && options[s_MongoDBDriverManager_readPreferenceTags].isArray()) { VariantToBsonConverter converter(options[s_MongoDBDriverManager_readPreferenceTags].toArray(), HIPPO_BSON_NO_FLAGS); b_tags = bson_new(); converter.convert(b_tags); mongoc_read_prefs_set_tags(new_rp, b_tags); } if ( mongoc_read_prefs_get_mode(new_rp) == MONGOC_READ_PRIMARY && !bson_empty(mongoc_read_prefs_get_tags(new_rp)) ) { throw MongoDriver::Utils::throwInvalidArgumentException("Primary read preference mode conflicts with tags"); mongoc_read_prefs_destroy(new_rp); return false; } /* This may be redundant in light of the last check (primary with tags), * but we'll check anyway in case additional validation is implemented. */ if (!mongoc_read_prefs_is_valid(new_rp)) { throw MongoDriver::Utils::throwInvalidArgumentException("Read preference is not valid"); mongoc_read_prefs_destroy(new_rp); return false; } mongoc_uri_set_read_prefs_t(uri, new_rp); mongoc_read_prefs_destroy(new_rp); return true; }
size_t mongoc_server_description_filter_eligible ( mongoc_server_description_t **descriptions, size_t description_len, const mongoc_read_prefs_t *read_prefs) { const bson_t *rp_tags; bson_iter_t rp_tagset_iter; bson_iter_t rp_iter; bson_iter_t sd_iter; uint32_t rp_len; uint32_t sd_len; const char *rp_val; const char *sd_val; bool *sd_matched = NULL; size_t found; size_t i; size_t rval = 0; if (!read_prefs) { /* NULL read_prefs is PRIMARY, no tags to filter by */ return description_len; } rp_tags = mongoc_read_prefs_get_tags (read_prefs); if (bson_count_keys (rp_tags) == 0) { return description_len; } sd_matched = (bool *) bson_malloc0 (sizeof(bool) * description_len); bson_iter_init (&rp_tagset_iter, rp_tags); /* for each read preference tagset */ while (bson_iter_next (&rp_tagset_iter)) { found = description_len; for (i = 0; i < description_len; i++) { sd_matched[i] = true; bson_iter_recurse (&rp_tagset_iter, &rp_iter); while (bson_iter_next (&rp_iter)) { /* TODO: can we have non-utf8 tags? */ rp_val = bson_iter_utf8 (&rp_iter, &rp_len); if (bson_iter_init_find (&sd_iter, &descriptions[i]->tags, bson_iter_key (&rp_iter))) { /* If the server description has that key */ sd_val = bson_iter_utf8 (&sd_iter, &sd_len); if (! (sd_len == rp_len && (0 == memcmp(rp_val, sd_val, rp_len)))) { /* If the key value doesn't match, no match */ sd_matched[i] = false; found--; } } else { /* If the server description doesn't have that key, no match */ sd_matched[i] = false; found--; break; } } } if (found) { for (i = 0; i < description_len; i++) { if (! sd_matched[i]) { descriptions[i] = NULL; } } rval = found; goto CLEANUP; } } for (i = 0; i < description_len; i++) { if (! sd_matched[i]) { descriptions[i] = NULL; } } CLEANUP: bson_free (sd_matched); return rval; }
Array HHVM_METHOD(MongoDBDriverCursor, __debugInfo) { MongoDBDriverCursorData* data = Native::data<MongoDBDriverCursorData>(this_); Array retval = Array::Create(); if (data->cursor) { Array cretval = Array::Create(); const bson_t *tags = mongoc_read_prefs_get_tags(data->cursor->read_prefs); cretval.add(s_MongoDBDriverCursor_stamp, (int64_t) data->cursor->stamp); cretval.add(s_MongoDBDriverCursor_is_command, !!(data->cursor->is_command)); cretval.add(s_MongoDBDriverCursor_sent, !!data->cursor->sent); cretval.add(s_MongoDBDriverCursor_done, !!data->cursor->done); cretval.add(s_MongoDBDriverCursor_end_of_event, !!data->cursor->end_of_event); cretval.add(s_MongoDBDriverCursor_in_exhaust, !!data->cursor->in_exhaust); cretval.add(s_MongoDBDriverCursor_has_fields, !!data->cursor->has_fields); { Variant v_query; hippo_bson_conversion_options_t options = HIPPO_TYPEMAP_INITIALIZER; BsonToVariantConverter convertor(bson_get_data(&data->cursor->query), data->cursor->query.len, options); convertor.convert(&v_query); cretval.add(s_MongoDBDriverCursor_query, v_query); } { Variant v_fields; hippo_bson_conversion_options_t options = HIPPO_TYPEMAP_INITIALIZER; BsonToVariantConverter convertor(bson_get_data(&data->cursor->fields), data->cursor->fields.len, options); convertor.convert(&v_fields); cretval.add(s_MongoDBDriverCursor_fields, v_fields); } if (tags->len) { Array rp_retval = Array::Create(); Variant v_read_preference; rp_retval.add(s_MongoDBDriverCursor_read_preference_mode, (int64_t) mongoc_read_prefs_get_mode(data->cursor->read_prefs)); hippo_bson_conversion_options_t options = HIPPO_TYPEMAP_DEBUG_INITIALIZER; BsonToVariantConverter convertor(bson_get_data(tags), tags->len, options); convertor.convert(&v_read_preference); rp_retval.add(s_MongoDBDriverCursor_read_preference_tags, v_read_preference); cretval.add(s_MongoDBDriverCursor_read_preference, rp_retval); } else { cretval.add(s_MongoDBDriverCursor_read_preference, Variant()); } cretval.add(s_MongoDBDriverCursor_flags, (int64_t) data->cursor->flags); cretval.add(s_MongoDBDriverCursor_skip, (int64_t) data->cursor->skip); cretval.add(s_MongoDBDriverCursor_limit, (int64_t) data->cursor->limit); cretval.add(s_MongoDBDriverCursor_count, (int64_t) data->cursor->count); cretval.add(s_MongoDBDriverCursor_batch_size, (int64_t) data->cursor->batch_size); cretval.add(s_MongoDBDriverCursor_ns, data->cursor->ns); if (data->cursor->current) { Array doc_retval = Array::Create(); Variant v_doc; hippo_bson_conversion_options_t options = HIPPO_TYPEMAP_INITIALIZER; BsonToVariantConverter convertor(bson_get_data(data->cursor->current), data->cursor->current->len, options); convertor.convert(&v_doc); cretval.add(s_MongoDBDriverCursor_current_doc, v_doc); } retval.add(s_MongoDBDriverCursor_cursor, cretval); } else { retval.add(s_MongoDBDriverCursor_cursor, Variant()); } retval.add(s_MongoDBDriverCursor_server_id, data->m_server_id); return retval; }