예제 #1
0
bool
bson_find_create_table (bson_t      *bson_schema,
                        const char  *table_name,
                        bson_iter_t *iter_col)
{
    bson_iter_t iter_json, iter_ary, iter_sql, iter_table_prop;

    bson_iter_init_find (&iter_json, bson_schema, "json") || DIE;
    BSON_ITER_HOLDS_ARRAY (&iter_json) || DIE;
    bson_iter_recurse (&iter_json, &iter_ary) || DIE;
    while (bson_iter_next (&iter_ary)) {
        (BSON_ITER_HOLDS_DOCUMENT (&iter_ary) || DIE);
        bson_iter_recurse (&iter_ary, &iter_sql) || DIE;
        if (bson_iter_find (&iter_sql, "create_table") &&
            (BSON_ITER_HOLDS_DOCUMENT (&iter_sql) || DIE) &&
            (bson_iter_recurse (&iter_sql, &iter_table_prop) || DIE) &&
            (bson_iter_find (&iter_table_prop, "table_name") || DIE) &&
            (BSON_ITER_HOLDS_UTF8 (&iter_table_prop) || DIE) &&
            (strcmp (bson_iter_utf8 (&iter_table_prop, NULL), table_name) == 0) &&
            (bson_iter_find (&iter_table_prop, "columns") || DIE) &&
            (BSON_ITER_HOLDS_ARRAY (&iter_table_prop) || DIE)) {
            bson_iter_recurse (&iter_table_prop, iter_col) || DIE;
            return true;
        }
    }
    return (false);
}
예제 #2
0
bool
_mongoc_convert_document (mongoc_client_t *client,
                          const bson_iter_t *iter,
                          bson_t *doc,
                          bson_error_t *error)
{
   uint32_t len;
   const uint8_t *data;
   bson_t value;

   if (!BSON_ITER_HOLDS_DOCUMENT (iter)) {
      CONVERSION_ERR ("Invalid field \"%s\" in opts, should contain document,"
                      " not %s",
                      bson_iter_key (iter),
                      _mongoc_bson_type_to_str (bson_iter_type (iter)));
   }

   bson_iter_document (iter, &len, &data);
   if (!bson_init_static (&value, data, len)) {
      BSON_ERR ("Corrupt BSON in field \"%s\" in opts", bson_iter_key (iter));
   }

   bson_destroy (doc);
   bson_copy_to (&value, doc);

   return true;
}
/*
 * Start iterating the reply to an "aggregate", "find", "getMore" etc. command:
 *
 *    {cursor: {id: 1234, ns: "db.collection", firstBatch: [...]}}
 */
bool
_mongoc_cursor_cursorid_start_batch (mongoc_cursor_t *cursor)
{
   mongoc_cursor_cursorid_t *cid;
   bson_iter_t iter;
   bson_iter_t child;
   const char *ns;
   uint32_t nslen;

   cid = (mongoc_cursor_cursorid_t *)cursor->iface_data;

   BSON_ASSERT (cid);

   if (bson_iter_init_find (&iter, &cid->array, "cursor") &&
       BSON_ITER_HOLDS_DOCUMENT (&iter) &&
       bson_iter_recurse (&iter, &child)) {
      while (bson_iter_next (&child)) {
         if (BSON_ITER_IS_KEY (&child, "id")) {
            cursor->rpc.reply.cursor_id = bson_iter_as_int64 (&child);
         } else if (BSON_ITER_IS_KEY (&child, "ns")) {
            ns = bson_iter_utf8 (&child, &nslen);
            _mongoc_set_cursor_ns (cursor, ns, nslen);
         } else if (BSON_ITER_IS_KEY (&child, "firstBatch") ||
                    BSON_ITER_IS_KEY (&child, "nextBatch")) {
            if (BSON_ITER_HOLDS_ARRAY (&child) &&
                bson_iter_recurse (&child, &cid->batch_iter)) {
               cid->in_batch = true;
            }
         }
      }
   }

   return cid->in_batch;
}
예제 #4
0
/*
 * If error is not set, set code from first document in array like
 * [{"code": 64, "errmsg": "duplicate"}, ...]. Format the error message
 * from all errors in array.
 */
static void
_set_error_from_response (bson_t *bson_array,
                          mongoc_error_domain_t domain,
                          const char *error_type,
                          bson_error_t *error /* OUT */)
{
   bson_iter_t array_iter;
   bson_iter_t doc_iter;
   bson_string_t *compound_err;
   const char *errmsg = NULL;
   int32_t code = 0;
   uint32_t n_keys, i;

   compound_err = bson_string_new (NULL);
   n_keys = bson_count_keys (bson_array);
   if (n_keys > 1) {
      bson_string_append_printf (
         compound_err, "Multiple %s errors: ", error_type);
   }

   if (!bson_empty0 (bson_array) && bson_iter_init (&array_iter, bson_array)) {
      /* get first code and all error messages */
      i = 0;

      while (bson_iter_next (&array_iter)) {
         if (BSON_ITER_HOLDS_DOCUMENT (&array_iter) &&
             bson_iter_recurse (&array_iter, &doc_iter)) {
            /* parse doc, which is like {"code": 64, "errmsg": "duplicate"} */
            while (bson_iter_next (&doc_iter)) {
               /* use the first error code we find */
               if (BSON_ITER_IS_KEY (&doc_iter, "code") && code == 0) {
                  code = bson_iter_int32 (&doc_iter);
               } else if (BSON_ITER_IS_KEY (&doc_iter, "errmsg")) {
                  errmsg = bson_iter_utf8 (&doc_iter, NULL);

                  /* build message like 'Multiple write errors: "foo", "bar"' */
                  if (n_keys > 1) {
                     bson_string_append_printf (compound_err, "\"%s\"", errmsg);
                     if (i < n_keys - 1) {
                        bson_string_append (compound_err, ", ");
                     }
                  } else {
                     /* single error message */
                     bson_string_append (compound_err, errmsg);
                  }
               }
            }

            i++;
         }
      }

      if (code && compound_err->len) {
         bson_set_error (
            error, domain, (uint32_t) code, "%s", compound_err->str);
      }
   }

   bson_string_free (compound_err, true);
}
예제 #5
0
/* {{{ proto WriteError[] WriteResult::getWriteErrors()
   Returns any write errors that occurred */
PHP_METHOD(WriteResult, getWriteErrors)
{
	bson_iter_t iter, child;
	php_phongo_writeresult_t *intern;
	SUPPRESS_UNUSED_WARNING(return_value_ptr) SUPPRESS_UNUSED_WARNING(return_value_used)


	intern = Z_WRITERESULT_OBJ_P(getThis());

	if (zend_parse_parameters_none() == FAILURE) {
		return;
	}


	array_init(return_value);

	if (bson_iter_init_find(&iter, intern->reply, "writeErrors") && BSON_ITER_HOLDS_ARRAY(&iter) && bson_iter_recurse(&iter, &child)) {
		while (bson_iter_next(&child)) {
			bson_t cbson;
			uint32_t len;
			const uint8_t *data;
#if PHP_VERSION_ID >= 70000
			zval writeerror;
#else
			zval *writeerror = NULL;
#endif

			if (!BSON_ITER_HOLDS_DOCUMENT(&child)) {
				continue;
			}

			bson_iter_document(&child, &len, &data);

			if (!bson_init_static(&cbson, data, len)) {
				continue;
			}

#if PHP_VERSION_ID >= 70000
			object_init_ex(&writeerror, php_phongo_writeerror_ce);

			if (!phongo_writeerror_init(&writeerror, &cbson TSRMLS_CC)) {
				zval_ptr_dtor(&writeerror);
				continue;
			}

			add_next_index_zval(return_value, &writeerror);
#else
			MAKE_STD_ZVAL(writeerror);
			object_init_ex(writeerror, php_phongo_writeerror_ce);

			if (!phongo_writeerror_init(writeerror, &cbson TSRMLS_CC)) {
				zval_ptr_dtor(&writeerror);
				continue;
			}

			add_next_index_zval(return_value, writeerror);
#endif
		}
	}
}
예제 #6
0
static bool hippo_cursor_next(MongoDBDriverCursorData* data)
{
	invalidate_current(data);

	data->next_after_rewind++;

	data->current++;
	if (data->is_command_cursor && bson_iter_next(&data->first_batch_iter)) {
		if (BSON_ITER_HOLDS_DOCUMENT(&data->first_batch_iter)) {
			const uint8_t *document = NULL;
			uint32_t document_len = 0;

			bson_iter_document(&data->first_batch_iter, &document_len, &document);

			data->zchild_active = true;
			return true;
		}
	}
	if (hippo_cursor_load_next(data)) {
		return true;
	} else {
		invalidate_current(data);
	}

	return false;
}
예제 #7
0
void _aggregate_recurse_fill(bson_iter_t *iter, bson_t* new_doc, bson_t* existing_aggregate_doc, bson_t* merged_aggregate_doc, const char *key) {
    bson_iter_t child_iter;
    bson_t child_doc;

    while (bson_iter_next (iter)) {
        int new_key_length = strlen(bson_iter_key(iter));
        if (strcmp("", key) != 0) {
            new_key_length += strlen(key) + 1;
        }

        char new_key[new_key_length];
        if (strcmp("", key) == 0) {
            strcpy(new_key, bson_iter_key(iter));
        } else {
            strcpy(new_key, key);
            strcat(new_key, ".");
            strcat(new_key, bson_iter_key(iter));
        }

        if (strcmp("_id", new_key) == 0) {
            bson_value_t *existing_id = _aggregate_get_value_at_key(existing_aggregate_doc, "_id");
            bson_append_value(merged_aggregate_doc, "_id", -1, existing_id);
            continue;
        }

        if (BSON_ITER_HOLDS_DOCUMENT (iter)) {

            const char *agg_key = NULL;
            const bson_value_t *agg_field = NULL;

            if (bson_iter_recurse (iter, &child_iter)) {
                if (bson_iter_next (&child_iter) &&
                    _aggregate_is_agg_operator(bson_iter_key(&child_iter))) {
                    agg_key = bson_iter_key(&child_iter);
                    agg_field = bson_iter_value(&child_iter);
                }

                if (agg_key && !bson_iter_next (&child_iter)) {
                    bson_value_t *existing_value = _aggregate_get_value_at_key(existing_aggregate_doc, new_key);
                    bson_value_t *new_doc_value = _aggregate_get_value_at_key(new_doc, (*agg_field).value.v_utf8.str + 1);
                    bson_value_t * agg_result = _aggregate(existing_value, new_doc_value, agg_key);
                    bson_append_value(merged_aggregate_doc, bson_iter_key(iter), -1, agg_result);
                    continue;

                }
            }

            bson_append_document_begin (merged_aggregate_doc, bson_iter_key(iter), -1, &child_doc);

            if (bson_iter_recurse (iter, &child_iter)) {
                _aggregate_recurse_fill (&child_iter, new_doc, existing_aggregate_doc, &child_doc, new_key);
            }

            bson_append_document_end (merged_aggregate_doc, &child_doc);
        } else {
            bson_append_value(merged_aggregate_doc, bson_iter_key(iter), -1, bson_iter_value(iter));
        }
    }
}
예제 #8
0
/* {{{ proto array WriteResult::getUpsertedIds()
   Returns the identifiers generated by the server for upsert operations. */
PHP_METHOD(WriteResult, getUpsertedIds)
{
	bson_iter_t iter, child;
	php_phongo_writeresult_t *intern;
	SUPPRESS_UNUSED_WARNING(return_value_ptr) SUPPRESS_UNUSED_WARNING(return_value_used)


	intern = Z_WRITERESULT_OBJ_P(getThis());

	if (zend_parse_parameters_none() == FAILURE) {
		return;
	}


	array_init(return_value);

	if (bson_iter_init_find(&iter, intern->reply, "upserted") && BSON_ITER_HOLDS_ARRAY(&iter) && bson_iter_recurse(&iter, &child)) {
		while (bson_iter_next(&child)) {
			int32_t index;
			bson_iter_t outer;

			if (!BSON_ITER_HOLDS_DOCUMENT(&child) || !bson_iter_recurse(&child, &outer)) {
				continue;
			}
			if (!bson_iter_find(&outer, "index") || !BSON_ITER_HOLDS_INT32(&outer)) {
				continue;
			}

			index = bson_iter_int32(&outer);

			if (!bson_iter_find(&outer, "_id")) {
				continue;
			}

			if (BSON_ITER_HOLDS_OID(&outer)) {
#if PHP_VERSION_ID >= 70000
				zval zid;

				php_phongo_objectid_new_from_oid(&zid, bson_iter_oid(&outer) TSRMLS_CC);
				add_index_zval(return_value, index, &zid);
#else
				zval *zid = NULL;
				MAKE_STD_ZVAL(zid);

				php_phongo_objectid_new_from_oid(zid, bson_iter_oid(&outer) TSRMLS_CC);
				add_index_zval(return_value, index, zid);
#endif
			} else if (BSON_ITER_HOLDS_INT32(&outer)) {
				int32_t val = bson_iter_int32(&outer);

				add_index_long(return_value, index, val);
			} else if (BSON_ITER_HOLDS_INT64(&outer)) {
				int64_t val = bson_iter_int64(&outer);

				ADD_INDEX_INT64(return_value, index, val);
			}
		}
	}
}
예제 #9
0
static int
_score_tags (const bson_t *read_tags,
             const bson_t *node_tags)
{
   uint32_t len;
   bson_iter_t iter;
   bson_iter_t sub_iter;
   const char *key;
   const char *str;
   int count;
   bool node_matches_set;

   bson_return_val_if_fail(read_tags, -1);
   bson_return_val_if_fail(node_tags, -1);

   count = bson_count_keys(read_tags);

   /* Execute this block if read tags were provided, else bail and return 0 (all nodes equal) */
   if (!bson_empty(read_tags) && bson_iter_init(&iter, read_tags)) {

      /*
       * Iterate over array of read tag sets provided (each element is a tag set)
       * Tag sets are provided in order of preference so return the count of the
       * first set that matches the node or -1 if no set matched the node.
       */
      while (count && bson_iter_next(&iter)) {
         if (BSON_ITER_HOLDS_DOCUMENT(&iter) && bson_iter_recurse(&iter, &sub_iter)) {
            node_matches_set = true;

            /* Iterate over the key/value pairs (tags) in the current set */
            while (bson_iter_next(&sub_iter) && BSON_ITER_HOLDS_UTF8(&sub_iter)) {
               key = bson_iter_key(&sub_iter);
               str = bson_iter_utf8(&sub_iter, &len);

               /* If any of the tags do not match, this node cannot satisfy this tag set. */
               if (!_contains_tag(node_tags, key, str, len)) {
                   node_matches_set = false;
                   break;
               }
            }

            /* This set matched, return the count as the score */
            if (node_matches_set) {
                return count;
            }

            /* Decrement the score and try to match the next set. */
            count--;
         }
      }
      return -1;
   }

   return 0;
}
예제 #10
0
파일: ha-test.c 프로젝트: hy/mongo-c-driver
void
ha_replica_set_wait_for_healthy (ha_replica_set_t *replica_set)
{
   bson_iter_t iter;
   bson_iter_t ar;
   bson_iter_t member;
   const char *stateStr;
   bson_t status;

again:
   sleep(1);

   if (!ha_replica_set_get_status(replica_set, &status)) {
      MONGOC_INFO("Failed to get replicaSet status. "
                  "Sleeping 1 second.");
      goto again;
   }

#if 0
   {
      char *str;

      str = bson_as_json(&status, NULL);
      printf("%s\n", str);
      bson_free(str);
   }
#endif

   if (!bson_iter_init_find(&iter, &status, "members") ||
       !BSON_ITER_HOLDS_ARRAY(&iter) ||
       !bson_iter_recurse(&iter, &ar)) {
      bson_destroy(&status);
      MONGOC_INFO("ReplicaSet has not yet come online. "
                  "Sleeping 1 second.");
      goto again;
   }

   while (bson_iter_next(&ar)) {
      if (BSON_ITER_HOLDS_DOCUMENT(&ar) &&
          bson_iter_recurse(&ar, &member) &&
          bson_iter_find(&member, "stateStr") &&
          (stateStr = bson_iter_utf8(&member, NULL))) {
         if (!!strcmp(stateStr, "PRIMARY") &&
             !!strcmp(stateStr, "SECONDARY") &&
             !!strcmp(stateStr, "ARBITER")) {
            bson_destroy(&status);
            MONGOC_INFO("Found unhealthy node. Sleeping 1 second.");
            goto again;
         }
      }
   }

   bson_destroy(&status);
}
예제 #11
0
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);
}
예제 #12
0
파일: mongoc-apm.c 프로젝트: cran/mongolite
void
mongoc_apm_command_started_init (mongoc_apm_command_started_t *event,
                                 const bson_t *command,
                                 const char *database_name,
                                 const char *command_name,
                                 int64_t request_id,
                                 int64_t operation_id,
                                 const mongoc_host_list_t *host,
                                 uint32_t server_id,
                                 void *context)
{
   bson_iter_t iter;
   uint32_t len;
   const uint8_t *data;

   /* Command Monitoring Spec:
    *
    * In cases where queries or commands are embedded in a $query parameter
    * when a read preference is provided, they MUST be unwrapped and the value
    * of the $query attribute becomes the filter or the command in the started
    * event. The read preference will subsequently be dropped as it is
    * considered metadata and metadata is not currently provided in the command
    * events.
    */
   if (bson_has_field (command, "$readPreference")) {
      if (bson_iter_init_find (&iter, command, "$query") &&
          BSON_ITER_HOLDS_DOCUMENT (&iter)) {
         bson_iter_document (&iter, &len, &data);
         event->command = bson_new_from_data (data, len);
         event->command_owned = true;
      } else {
         /* Got $readPreference without $query, probably OP_MSG */
         event->command = (bson_t *) command;
         event->command_owned = false;
      }
   } else {
      /* discard "const", we promise not to modify "command" */
      event->command = (bson_t *) command;
      event->command_owned = false;
   }

   event->database_name = database_name;
   event->command_name = command_name;
   event->request_id = request_id;
   event->operation_id = operation_id;
   event->host = host;
   event->server_id = server_id;
   event->context = context;
}
예제 #13
0
int
get_column_map (bson_t        *bson_schema,
                const char    *table_name,
                column_map_t **column_map,
                int           *column_map_size)
{
    bson_iter_t iter_col, iter_dup;
    int size;
    column_map_t *column_map_p;
    const char *data_type;
    data_type_map_t *data_type_map_p;

    bson_find_create_table (bson_schema, table_name, &iter_col) || DIE;
    for (iter_dup = iter_col, size = 0; bson_iter_next (&iter_dup); size++)
        ;
    *column_map = calloc (sizeof (column_map_t), size);
    *column_map_size = size;
    column_map_p = *column_map;
    while (bson_iter_next (&iter_col)) {
        bson_iter_t iter_col_prop;

        BSON_ITER_HOLDS_DOCUMENT (&iter_col) || DIE;
        bson_iter_recurse (&iter_col, &iter_col_prop) || DIE;
        bson_iter_find (&iter_col_prop, "column_name") || DIE;
        BSON_ITER_HOLDS_UTF8 (&iter_col_prop) || DIE;
        column_map_p->column_name = bson_iter_dup_utf8 (&iter_col_prop, NULL);
        bson_iter_find (&iter_col_prop, "data_type") || DIE;
        BSON_ITER_HOLDS_UTF8 (&iter_col_prop) || DIE;
        data_type = bson_iter_utf8 (&iter_col_prop, NULL);
        column_map_p->data_type = data_type;
        for (data_type_map_p = data_type_map;
             data_type_map_p < (data_type_map + sizeof (data_type_map)) &&
             strcmp (data_type_map_p->data_type, data_type) != 0;
             data_type_map_p++)
            ;
        if (data_type_map < (data_type_map + sizeof (data_type_map)))
            column_map_p->bson_append_from_s = data_type_map_p->bson_append_from_s ?
                data_type_map_p->bson_append_from_s : bson_append_utf8_from_s;
        else
            DIE;
        column_map_p++;
    }
    return true;
}
예제 #14
0
static void hippo_cursor_rewind(MongoDBDriverCursorData* data)
{
	if (data->next_after_rewind != 0) {
		if (data->zchild_active) {
			throw MongoDriver::Utils::throwLogicException("Cursors cannot rewind after starting iteration");
		} else { /* If we're not active, image we're now have fully iterated */
			throw MongoDriver::Utils::throwLogicException("Cursors cannot yield multiple iterators");
		}
	}

	invalidate_current(data);
	data->current = 0;
	data->zchild_active = false;

	if (data->first_batch) {
		if (data->is_command_cursor) {
			if (!bson_iter_init(&data->first_batch_iter, data->first_batch)) {
				return;
			}
			if (bson_iter_next(&data->first_batch_iter)) {
				if (BSON_ITER_HOLDS_DOCUMENT(&data->first_batch_iter)) {
					const uint8_t *document = NULL;
					uint32_t document_len = 0;
					Variant v;

					bson_iter_document(&data->first_batch_iter, &document_len, &document);

					BsonToVariantConverter convertor(document, document_len, data->bson_options);
					convertor.convert(&v);
					data->zchild_active = true;
					data->zchild = v;
				}
			}
		} else {
			Variant v;

			BsonToVariantConverter convertor(bson_get_data(data->first_batch), data->first_batch->len, data->bson_options);
			convertor.convert(&v);
			data->zchild_active = true;
			data->zchild = v;
		}
	}
}
bson_bool_t
_mongoc_cursor_cursorid_next (mongoc_cursor_t *cursor,
                              const bson_t   **bson)
{
   bson_bool_t ret;
   mongoc_cursor_cursorid_t *cid;
   bson_iter_t iter;
   bson_iter_t child;
   const char *ns;

   ENTRY;

   cid = cursor->interface_data;

   ret = _mongoc_cursor_next (cursor, bson);

   if (!cid->has_cursor) {
      cid->has_cursor = TRUE;

      if (ret &&
          bson_iter_init_find (&iter, *bson, "cursor") &&
          BSON_ITER_HOLDS_DOCUMENT (&iter) &&
          bson_iter_recurse (&iter, &child)) {
         while (bson_iter_next (&child)) {
            if (strcmp (bson_iter_key (&child), "id") == 0) {
               cursor->rpc.reply.cursor_id = bson_iter_int64 (&child);
            } else if (strcmp (bson_iter_key (&child), "ns") == 0) {
               ns = bson_iter_utf8 (&child, &cursor->nslen);
               strncpy (cursor->ns, ns, sizeof cursor->ns - 1);
            }
         }

         cursor->is_command = FALSE;

         ret = _mongoc_cursor_next (cursor, bson);
      }
   }


   RETURN (ret);
}
예제 #16
0
int monary_load_document_value(const bson_iter_t* bsonit,
                               monary_column_item* citem,
                               int idx)
{
    uint32_t document_len;      // The length of document in bytes.
    const uint8_t* document;    // Pointer to the immutable document buffer.
    uint8_t* dest;

    if (BSON_ITER_HOLDS_DOCUMENT(bsonit)) {
        bson_iter_document(bsonit, &document_len, &document);
        if (document_len > citem->type_arg) {
            document_len = citem->type_arg;
        }

        dest = ((uint8_t*) citem->storage) + (idx * document_len);
        memcpy(dest, document, document_len);
        return 1;
    } else {
        return 0;
    }
}
예제 #17
0
SEXP ConvertValue(bson_iter_t* iter){
  if(BSON_ITER_HOLDS_INT32(iter)){
    return ScalarInteger(bson_iter_int32(iter));
  } else if(BSON_ITER_HOLDS_NULL(iter)){
    return R_NilValue;
  } else if(BSON_ITER_HOLDS_BOOL(iter)){
    return ScalarLogical(bson_iter_bool(iter));
  } else if(BSON_ITER_HOLDS_DOUBLE(iter)){
    return ScalarReal(bson_iter_double(iter));
  } else if(BSON_ITER_HOLDS_INT64(iter)){
    return ScalarReal((double) bson_iter_int64(iter));
  } else if(BSON_ITER_HOLDS_UTF8(iter)){
    return mkStringUTF8(bson_iter_utf8(iter, NULL));
  } else if(BSON_ITER_HOLDS_CODE(iter)){
    return mkStringUTF8(bson_iter_code(iter, NULL));
  } else if(BSON_ITER_HOLDS_BINARY(iter)){
    return ConvertBinary(iter);
  } else if(BSON_ITER_HOLDS_DATE_TIME(iter)){
    return ConvertDate(iter);
  } else if(BSON_ITER_HOLDS_OID(iter)){
    const bson_oid_t *val = bson_iter_oid(iter);
    char str[25];
    bson_oid_to_string(val, str);
    return mkString(str);
  } else if(BSON_ITER_HOLDS_ARRAY(iter)){
    bson_iter_t child1;
    bson_iter_t child2;
    bson_iter_recurse (iter, &child1);
    bson_iter_recurse (iter, &child2);
    return ConvertArray(&child1, &child2);
  } else if(BSON_ITER_HOLDS_DOCUMENT(iter)){
    bson_iter_t child1;
    bson_iter_t child2;
    bson_iter_recurse (iter, &child1);
    bson_iter_recurse (iter, &child2);
    return ConvertObject(&child1, &child2);
  } else {
    stop("Unimplemented BSON type %d\n", bson_iter_type(iter));
  }
}
예제 #18
0
static request_t *
_check_op_query (mock_server_t                    *server,
                 test_collection_find_with_opts_t *test_data)
{
   mongoc_query_flags_t flags;
   request_t *request;
   const bson_t *doc;
   bson_iter_t iter;
   uint32_t len;
   const uint8_t *data;
   bson_t query;

   /* Server Selection Spec: all queries to standalone set slaveOk. */
   flags = test_data->expected_flags | MONGOC_QUERY_SLAVE_OK;

   request = mock_server_receives_query (
      server,
      "db.collection",
      flags,
      test_data->expected_skip,
      test_data->expected_n_return,
      test_data->expected_op_query,
      test_data->expected_op_query_projection);

   ASSERT (request);

   /* check that nothing unexpected is in $query */
   if (bson_empty (test_data->filter_bson)) {
      doc = request_get_doc (request, 0);

      if (bson_iter_init_find (&iter, doc, "$query")) {
         ASSERT (BSON_ITER_HOLDS_DOCUMENT (&iter));
         bson_iter_document (&iter, &len, &data);
         bson_init_static (&query, data, (size_t) len);
         ASSERT (bson_empty (&query));
      }
   }

   return request;
}
예제 #19
0
bool
mongoc_uri_get_mechanism_properties (const mongoc_uri_t *uri, bson_t *properties)
{
   bson_iter_t iter;

   if (!uri) {
      return false;
   }

   if (bson_iter_init_find_case (&iter, &uri->credentials, "mechanismProperties") &&
      BSON_ITER_HOLDS_DOCUMENT (&iter)) {
      uint32_t len = 0;
      const uint8_t *data = NULL;

      bson_iter_document (&iter, &len, &data);
      bson_init_static (properties, data, len);

      return true;
   }

   return false;
}
static void
_mongoc_cursor_cursorid_read_from_batch (mongoc_cursor_t *cursor,
                                         const bson_t   **bson)
{
   mongoc_cursor_cursorid_t *cid;
   const uint8_t *data = NULL;
   uint32_t data_len = 0;

   ENTRY;

   cid = (mongoc_cursor_cursorid_t *)cursor->iface_data;
   BSON_ASSERT (cid);

   if (bson_iter_next (&cid->batch_iter) &&
       BSON_ITER_HOLDS_DOCUMENT (&cid->batch_iter)) {
      bson_iter_document (&cid->batch_iter, &data_len, &data);

      if (bson_init_static (&cid->current_doc, data, data_len)) {
         *bson = &cid->current_doc;
      }
   }
}
예제 #21
0
파일: bson.c 프로젝트: CDC/mongolite
SEXP ConvertValue(bson_iter_t* iter){
  if(BSON_ITER_HOLDS_INT32(iter)){
    return ScalarInteger(bson_iter_int32(iter));
  } else if(BSON_ITER_HOLDS_NULL(iter)){
    return R_NilValue;
  } else if(BSON_ITER_HOLDS_BOOL(iter)){
    return ScalarLogical(bson_iter_bool(iter));
  } else if(BSON_ITER_HOLDS_DOUBLE(iter)){
    return ScalarReal(bson_iter_double(iter));
  } else if(BSON_ITER_HOLDS_INT64(iter)){
    return ScalarReal((double) bson_iter_int64(iter));
  } else if(BSON_ITER_HOLDS_UTF8(iter)){
    return mkStringUTF8(bson_iter_utf8(iter, NULL));
  } else if(BSON_ITER_HOLDS_CODE(iter)){
    return mkStringUTF8(bson_iter_code(iter, NULL));
  } else if(BSON_ITER_HOLDS_BINARY(iter)){
    return ConvertBinary(iter);
  } else if(BSON_ITER_HOLDS_DATE_TIME(iter)){
    return ConvertDate(iter);
  } else if(BSON_ITER_HOLDS_OID(iter)){
    //not sure if this casting works
    return mkRaw((unsigned char *) bson_iter_oid(iter), 12);
  } else if(BSON_ITER_HOLDS_ARRAY(iter)){
    bson_iter_t child1;
    bson_iter_t child2;
    bson_iter_recurse (iter, &child1);
    bson_iter_recurse (iter, &child2);
    return ConvertArray(&child1, &child2);
  } else if(BSON_ITER_HOLDS_DOCUMENT(iter)){
    bson_iter_t child1;
    bson_iter_t child2;
    bson_iter_recurse (iter, &child1);
    bson_iter_recurse (iter, &child2);
    return ConvertObject(&child1, &child2);
  } else {
    stop("Unimplemented BSON type %d\n", bson_iter_type(iter));
  }
}
예제 #22
0
const char *
_mongoc_get_command_name (const bson_t *command)
{
   bson_iter_t iter;
   const char *name;
   bson_iter_t child;
   const char *wrapper_name = NULL;

   BSON_ASSERT (command);

   if (!bson_iter_init (&iter, command) ||
       !bson_iter_next (&iter)) {
      return NULL;
   }

   name = bson_iter_key (&iter);

   /* wrapped in "$query" or "query"?
    *
    *   {$query: {count: "collection"}, $readPreference: {...}}
    */
   if (name[0] == '$') {
      wrapper_name = "$query";
   } else if (!strcmp (name, "query")) {
      wrapper_name = "query";
   }

   if (wrapper_name &&
       bson_iter_init_find (&iter, command, wrapper_name) &&
       BSON_ITER_HOLDS_DOCUMENT (&iter) &&
       bson_iter_recurse (&iter, &child) &&
       bson_iter_next (&child)) {

      name = bson_iter_key (&child);
   }

   return name;
}
/**
 * _mongoc_parse_wc_err:
 * @doc: (in): A bson document.
 * @error: (out): A bson_error_t.
 *
 * Parses a document, usually a server reply,
 * looking for a writeConcernError. Returns true if
 * there is a writeConcernError, false otherwise.
 */
bool
_mongoc_parse_wc_err (const bson_t *doc, bson_error_t *error) {
   bson_iter_t iter;
   bson_iter_t inner;

   if (bson_iter_init_find(&iter, doc, "writeConcernError") &&
       BSON_ITER_HOLDS_DOCUMENT (&iter))
   {
      const char *errmsg = NULL;
      int32_t code = 0;
      bson_iter_recurse(&iter, &inner);
      while (bson_iter_next(&inner)) {
         if (BSON_ITER_IS_KEY (&inner, "code")) {
            code = bson_iter_int32 (&inner);
         } else if (BSON_ITER_IS_KEY (&inner, "errmsg")) {
            errmsg = bson_iter_utf8 (&inner, NULL);
         }
      }
      bson_set_error(error, MONGOC_ERROR_WRITE_CONCERN, code,
                     "Write Concern error: %s", errmsg);
      return true;
   }
   return false;
}
예제 #24
0
static void
test_get_collection_info (void)
{
   mongoc_database_t *database;
   mongoc_collection_t *collection;
   mongoc_client_t *client;
   bson_error_t error = { 0 };
   bson_iter_t iter;
   bson_iter_t col_array;
   bson_iter_t col_iter;
   bson_t capped_options = BSON_INITIALIZER;
   bson_t autoindexid_options = BSON_INITIALIZER;
   bson_t noopts_options = BSON_INITIALIZER;
   bson_t name_filter = BSON_INITIALIZER;
   int r;
   int num_infos = 0;

   bson_t *infos = NULL;
   const char *name;
   char *dbname;
   char *capped_name;
   char *autoindexid_name;
   char *noopts_name;

   client = mongoc_client_new (gTestUri);
   assert (client);

   dbname = gen_collection_name ("dbtest");
   database = mongoc_client_get_database (client, dbname);

   assert (database);
   bson_free (dbname);

   capped_name = gen_collection_name ("capped");
   BSON_APPEND_BOOL (&capped_options, "capped", true);
   BSON_APPEND_INT32 (&capped_options, "size", 10000000);
   BSON_APPEND_INT32 (&capped_options, "max", 1024);

   autoindexid_name = gen_collection_name ("autoindexid");
   BSON_APPEND_BOOL (&autoindexid_options, "autoIndexId", false);

   noopts_name = gen_collection_name ("noopts");

   collection = mongoc_database_create_collection (database, capped_name,
                                                   &capped_options, &error);
   assert (collection);
   mongoc_collection_destroy (collection);

   collection = mongoc_database_create_collection (database, autoindexid_name,
                                                   &autoindexid_options,
                                                   &error);
   assert (collection);
   mongoc_collection_destroy (collection);

   collection = mongoc_database_create_collection (database, noopts_name,
                                                   &noopts_options, &error);
   assert (collection);
   mongoc_collection_destroy (collection);

   /* first we filter on collection name. */
   BSON_APPEND_UTF8 (&name_filter, "name", noopts_name);

   /* We only test with filters since get_collection_names will
    * test w/o filters for us. */

   /* Filter on an exact match of name */
   infos = mongoc_database_get_collection_info (database, &name_filter, &error);
   assert (infos);
   assert (!error.domain);
   assert (!error.code);

   if (bson_iter_init_find (&iter, infos, "collections") &&
       BSON_ITER_HOLDS_ARRAY (&iter) &&
       bson_iter_recurse (&iter, &col_array)) {
      while (bson_iter_next (&col_array)) {
         if (BSON_ITER_HOLDS_DOCUMENT (&col_array) &&
             bson_iter_recurse (&col_array, &col_iter) &&
             bson_iter_find (&col_iter, "name") &&
             BSON_ITER_HOLDS_UTF8 (&col_iter) &&
             (name = bson_iter_utf8 (&col_iter, NULL))) {
            ++num_infos;
            assert (0 == strcmp (name, noopts_name));
         } else {
            assert (false);
         }
       }
   }

   assert (1 == num_infos);

   num_infos = 0;
   bson_destroy (infos);
   infos = NULL;

   r = mongoc_database_drop (database, &error);
   assert (r);
   assert (!error.domain);
   assert (!error.code);

   bson_free (capped_name);
   bson_free (noopts_name);
   bson_free (autoindexid_name);

   mongoc_database_destroy (database);
   mongoc_client_destroy (client);
}
예제 #25
0
mongoc_collection_t *
mongoc_database_create_collection (mongoc_database_t *database,
                                   const char        *name,
                                   const bson_t      *options,
                                   bson_error_t      *error)
{
   mongoc_collection_t *collection = NULL;
   bson_iter_t iter;
   bson_t cmd;
   bool capped = false;

   bson_return_val_if_fail (database, NULL);
   bson_return_val_if_fail (name, NULL);

   if (strchr (name, '$')) {
      bson_set_error (error,
                      MONGOC_ERROR_NAMESPACE,
                      MONGOC_ERROR_NAMESPACE_INVALID,
                      "The namespace \"%s\" is invalid.",
                      name);
      return NULL;
   }

   if (options) {
      if (bson_iter_init_find (&iter, options, "capped")) {
         if (!BSON_ITER_HOLDS_BOOL (&iter)) {
            bson_set_error (error,
                            MONGOC_ERROR_COMMAND,
                            MONGOC_ERROR_COMMAND_INVALID_ARG,
                            "The argument \"capped\" must be a boolean.");
            return NULL;
         }
         capped = bson_iter_bool (&iter);
      }

      if (bson_iter_init_find (&iter, options, "autoIndexId") &&
          !BSON_ITER_HOLDS_BOOL (&iter)) {
         bson_set_error (error,
                         MONGOC_ERROR_COMMAND,
                         MONGOC_ERROR_COMMAND_INVALID_ARG,
                         "The argument \"autoIndexId\" must be a boolean.");
         return NULL;
      }

      if (bson_iter_init_find (&iter, options, "size")) {
         if (!BSON_ITER_HOLDS_INT32 (&iter) &&
             !BSON_ITER_HOLDS_INT64 (&iter)) {
            bson_set_error (error,
                            MONGOC_ERROR_COMMAND,
                            MONGOC_ERROR_COMMAND_INVALID_ARG,
                            "The argument \"size\" must be an integer.");
            return NULL;
         }
         if (!capped) {
            bson_set_error (error,
                            MONGOC_ERROR_COMMAND,
                            MONGOC_ERROR_COMMAND_INVALID_ARG,
                            "The \"size\" parameter requires {\"capped\": true}");
            return NULL;
         }
      }

      if (bson_iter_init_find (&iter, options, "max")) {
         if (!BSON_ITER_HOLDS_INT32 (&iter) &&
             !BSON_ITER_HOLDS_INT64 (&iter)) {
            bson_set_error (error,
                            MONGOC_ERROR_COMMAND,
                            MONGOC_ERROR_COMMAND_INVALID_ARG,
                            "The argument \"max\" must be an integer.");
            return NULL;
         }
         if (!capped) {
            bson_set_error (error,
                            MONGOC_ERROR_COMMAND,
                            MONGOC_ERROR_COMMAND_INVALID_ARG,
                            "The \"size\" parameter requires {\"capped\": true}");
            return NULL;
         }
      }

      if (bson_iter_init_find (&iter, options, "storage")) {
         if (!BSON_ITER_HOLDS_DOCUMENT (&iter)) {
            bson_set_error (error,
                            MONGOC_ERROR_COMMAND,
                            MONGOC_ERROR_COMMAND_INVALID_ARG,
                            "The \"storage\" parameter must be a document");

            return NULL;
         }

         if (bson_iter_find (&iter, "wiredtiger")) {
            if (!BSON_ITER_HOLDS_DOCUMENT (&iter)) {
               bson_set_error (error,
                               MONGOC_ERROR_COMMAND,
                               MONGOC_ERROR_COMMAND_INVALID_ARG,
                               "The \"wiredtiger\" option must take a document argument with a \"configString\" field");
               return NULL;
            }

            if (bson_iter_find (&iter, "configString")) {
               if (!BSON_ITER_HOLDS_UTF8 (&iter)) {
                  bson_set_error (error,
                                  MONGOC_ERROR_COMMAND,
                                  MONGOC_ERROR_COMMAND_INVALID_ARG,
                                  "The \"configString\" parameter must be a string");
                  return NULL;
               }
            } else {
               bson_set_error (error,
                               MONGOC_ERROR_COMMAND,
                               MONGOC_ERROR_COMMAND_INVALID_ARG,
                               "The \"wiredtiger\" option must take a document argument with a \"configString\" field");
               return NULL;
            }
         }
      }

   }


   bson_init (&cmd);
   BSON_APPEND_UTF8 (&cmd, "create", name);

   if (options) {
      if (!bson_iter_init (&iter, options)) {
         bson_set_error (error,
                         MONGOC_ERROR_COMMAND,
                         MONGOC_ERROR_COMMAND_INVALID_ARG,
                         "The argument \"options\" is corrupt or invalid.");
         bson_destroy (&cmd);
         return NULL;
      }

      while (bson_iter_next (&iter)) {
         if (!bson_append_iter (&cmd, bson_iter_key (&iter), -1, &iter)) {
            bson_set_error (error,
                            MONGOC_ERROR_COMMAND,
                            MONGOC_ERROR_COMMAND_INVALID_ARG,
                            "Failed to append \"options\" to create command.");
            bson_destroy (&cmd);
            return NULL;
         }
      }
   }

   if (mongoc_database_command_simple (database, &cmd, NULL, NULL, error)) {
      collection = _mongoc_collection_new (database->client,
                                           database->name,
                                           name,
                                           database->read_prefs,
                                           database->write_concern);
   }

   bson_destroy (&cmd);

   return collection;
}
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;
}
예제 #27
0
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;
}
static mongoc_matcher_op_t *
_mongoc_matcher_parse_logical (mongoc_matcher_opcode_t  opcode,  /* IN */
                               bson_iter_t             *iter,    /* IN */
                               bool                     is_root, /* IN */
                               bson_error_t            *error)   /* OUT */
{
   mongoc_matcher_op_t *left;
   mongoc_matcher_op_t *right;
   mongoc_matcher_op_t *more;
   mongoc_matcher_op_t *more_wrap;
   bson_iter_t child;

   BSON_ASSERT (opcode);
   BSON_ASSERT (iter);
   BSON_ASSERT (iter);

   if (!bson_iter_next (iter)) {
      bson_set_error (error,
                      MONGOC_ERROR_MATCHER,
                      MONGOC_ERROR_MATCHER_INVALID,
                      "Invalid logical operator.");
      return NULL;
   }

   if (is_root) {
      if (!(left = _mongoc_matcher_parse (iter, error))) {
         return NULL;
      }
   } else {
      if (!BSON_ITER_HOLDS_DOCUMENT (iter)) {
         bson_set_error (error,
                         MONGOC_ERROR_MATCHER,
                         MONGOC_ERROR_MATCHER_INVALID,
                         "Expected document in value.");
         return NULL;
      }

      bson_iter_recurse (iter, &child);
      bson_iter_next (&child);

      if (!(left = _mongoc_matcher_parse (&child, error))) {
         return NULL;
      }
   }

   if (!bson_iter_next (iter)) {
      return left;
   }

   if (is_root) {
      if (!(right = _mongoc_matcher_parse (iter, error))) {
         return NULL;
      }
   } else {
      if (!BSON_ITER_HOLDS_DOCUMENT (iter)) {
         bson_set_error (error,
                         MONGOC_ERROR_MATCHER,
                         MONGOC_ERROR_MATCHER_INVALID,
                         "Expected document in value.");
         return NULL;
      }

      bson_iter_recurse (iter, &child);
      bson_iter_next (&child);

      if (!(right = _mongoc_matcher_parse (&child, error))) {
         return NULL;
      }
   }

   more = _mongoc_matcher_parse_logical (opcode, iter, is_root, error);

   if (more) {
      more_wrap = _mongoc_matcher_op_logical_new (opcode, right, more);
      return _mongoc_matcher_op_logical_new (opcode, left, more_wrap);
   }

   return _mongoc_matcher_op_logical_new (opcode, left, right);
}
static void
test_find_and_modify (void)
{
   mongoc_collection_t *collection;
   mongoc_client_t *client;
   bson_error_t error;
   bson_iter_t iter;
   bson_iter_t citer;
   bson_t *update;
   bson_t doc = BSON_INITIALIZER;
   bson_t reply;
   mongoc_find_and_modify_opts_t *opts;

   client = test_framework_client_new ();
   ASSERT (client);

   collection = get_test_collection (client, "test_find_and_modify");
   ASSERT (collection);

   BSON_APPEND_INT32 (&doc, "superduper", 77889);

   ASSERT_OR_PRINT (mongoc_collection_insert (
                       collection, MONGOC_INSERT_NONE, &doc, NULL, &error),
                    error);

   update = BCON_NEW ("$set", "{", "superduper", BCON_INT32 (1234), "}");

   opts = mongoc_find_and_modify_opts_new ();
   mongoc_find_and_modify_opts_set_update (opts, update);
   mongoc_find_and_modify_opts_set_fields (opts,
                                           tmp_bson ("{'superduper': 1}"));
   mongoc_find_and_modify_opts_set_sort (opts, tmp_bson ("{'superduper': 1}"));
   mongoc_find_and_modify_opts_set_flags (opts,
                                          MONGOC_FIND_AND_MODIFY_RETURN_NEW);

   ASSERT_OR_PRINT (mongoc_collection_find_and_modify_with_opts (
                       collection, &doc, opts, &reply, &error),
                    error);

   assert (bson_iter_init_find (&iter, &reply, "value"));
   assert (BSON_ITER_HOLDS_DOCUMENT (&iter));
   assert (bson_iter_recurse (&iter, &citer));
   assert (bson_iter_find (&citer, "superduper"));
   assert (BSON_ITER_HOLDS_INT32 (&citer));
   assert (bson_iter_int32 (&citer) == 1234);

   assert (bson_iter_init_find (&iter, &reply, "lastErrorObject"));
   assert (BSON_ITER_HOLDS_DOCUMENT (&iter));
   assert (bson_iter_recurse (&iter, &citer));
   assert (bson_iter_find (&citer, "updatedExisting"));
   assert (BSON_ITER_HOLDS_BOOL (&citer));
   assert (bson_iter_bool (&citer));

   bson_destroy (&reply);
   bson_destroy (update);

   ASSERT_OR_PRINT (mongoc_collection_drop (collection, &error), error);

   mongoc_find_and_modify_opts_destroy (opts);
   mongoc_collection_destroy (collection);
   mongoc_client_destroy (client);
   bson_destroy (&doc);
}
/**
 * _mongoc_gridfs_file_new_from_bson:
 *
 * creates a gridfs file from a bson object
 *
 * This is only really useful for instantiating a gridfs file from a server
 * side object
 */
mongoc_gridfs_file_t *
_mongoc_gridfs_file_new_from_bson (mongoc_gridfs_t *gridfs,
                                   const bson_t    *data)
{
   mongoc_gridfs_file_t *file;
   const bson_value_t *value;
   const char *key;
   bson_iter_t iter;
   const uint8_t *buf;
   uint32_t buf_len;

   ENTRY;

   BSON_ASSERT (gridfs);
   BSON_ASSERT (data);

   file = (mongoc_gridfs_file_t *)bson_malloc0 (sizeof *file);

   file->gridfs = gridfs;
   bson_copy_to (data, &file->bson);

   bson_iter_init (&iter, &file->bson);

   while (bson_iter_next (&iter)) {
      key = bson_iter_key (&iter);

      if (0 == strcmp (key, "_id")) {
         value = bson_iter_value (&iter);
         bson_value_copy (value, &file->files_id);
      } else if (0 == strcmp (key, "length")) {
         if (!BSON_ITER_HOLDS_INT32 (&iter) &&
             !BSON_ITER_HOLDS_INT64 (&iter) &&
             !BSON_ITER_HOLDS_DOUBLE (&iter)) {
            GOTO (failure);
         }
         file->length = bson_iter_as_int64 (&iter);
      } else if (0 == strcmp (key, "chunkSize")) {
         if (!BSON_ITER_HOLDS_INT32 (&iter) &&
             !BSON_ITER_HOLDS_INT64 (&iter) &&
             !BSON_ITER_HOLDS_DOUBLE (&iter)) {
            GOTO (failure);
         }
         if (bson_iter_as_int64 (&iter) > INT32_MAX) {
            GOTO (failure);
         }
         file->chunk_size = (int32_t)bson_iter_as_int64 (&iter);
      } else if (0 == strcmp (key, "uploadDate")) {
         if (!BSON_ITER_HOLDS_DATE_TIME (&iter)){
            GOTO (failure);
         }
         file->upload_date = bson_iter_date_time (&iter);
      } else if (0 == strcmp (key, "md5")) {
         if (!BSON_ITER_HOLDS_UTF8 (&iter)) {
            GOTO (failure);
         }
         file->bson_md5 = bson_iter_utf8 (&iter, NULL);
      } else if (0 == strcmp (key, "filename")) {
         if (!BSON_ITER_HOLDS_UTF8 (&iter)) {
            GOTO (failure);
         }
         file->bson_filename = bson_iter_utf8 (&iter, NULL);
      } else if (0 == strcmp (key, "contentType")) {
         if (!BSON_ITER_HOLDS_UTF8 (&iter)) {
            GOTO (failure);
         }
         file->bson_content_type = bson_iter_utf8 (&iter, NULL);
      } else if (0 == strcmp (key, "aliases")) {
         if (!BSON_ITER_HOLDS_ARRAY (&iter)) {
            GOTO  (failure);
         }
         bson_iter_array (&iter, &buf_len, &buf);
         bson_init_static (&file->bson_aliases, buf, buf_len);
      } else if (0 == strcmp (key, "metadata")) {
         if (!BSON_ITER_HOLDS_DOCUMENT (&iter)) {
            GOTO (failure);
         }
         bson_iter_document (&iter, &buf_len, &buf);
         bson_init_static (&file->bson_metadata, buf, buf_len);
      }
   }

   /* TODO: is there are a minimal object we should be verifying that we
    * actually have here? */

   RETURN (file);

failure:
   bson_destroy (&file->bson);

   RETURN (NULL);
}