예제 #1
0
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;
}
예제 #2
0
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();
}
예제 #3
0
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.");
   }
}
예제 #4
0
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);
   }
}
예제 #6
0
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);
}
예제 #7
0
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);
}
예제 #8
0
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;
}
예제 #10
0
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;
}