Skip to content

CDRIVER-504 add bson_array_builder_t #1344

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Jul 25, 2023
Merged

CDRIVER-504 add bson_array_builder_t #1344

merged 17 commits into from
Jul 25, 2023

Conversation

kevinAlbs
Copy link
Collaborator

Summary

  • Add bson_array_builder_t to build BSON arrays without specifying keys.

Verified with this patch build: https://spruce.mongodb.com/version/64b146ad3627e06d3165c766

Other improvements

  • Add missing macros BSON_APPEND_ITER and BSON_APPEND_NOW_UTC.
  • Move updaetd documentation example code to source files to help verify the code compiles.
  • Document and test bson_uint32_to_string behavior when input buffer is too small.

Background & Motivation

https://bsonspec.org/spec.html describes the expectation of BSON array keys:

Array - The document for an array is a normal BSON document with integer values for the keys, starting with 0 and continuing sequentially. For example, the array ['red', 'blue'] would be encoded as the document {'0': 'red', '1': 'blue'}. The keys must be in ascending numerical order.

bson_append_* functions require the caller to specify the keys. Here is an example from the Tutorial:

/*
* Append array of strings. Generate keys "0", "1", "2".
*/
BSON_APPEND_ARRAY_BEGIN (document, "languages", &child);
for (i = 0; i < sizeof lang_names / sizeof (char *); ++i) {
    keylen = bson_uint32_to_string (i, &key, buf, sizeof buf);
    bson_append_utf8 (&child, key, (int) keylen, lang_names[i], -1);
}
bson_append_array_end (document, &child);

SERVER-21946 describes a case where misuse of the libbson API resulted in building a BSON array with repeated "0" keys, resulting in unexpected server behavior.

This PR provides an alternative API to build BSON arrays with bson_array_builder_t which does not require keys be specified:

/*
* Append array of strings. Generate keys "0", "1", "2".
*/
BSON_APPEND_ARRAY_BUILDER_BEGIN (document, "languages", &bab);
for (i = 0; i < sizeof lang_names / sizeof (char *); ++i) {
    bson_array_builder_append_utf8 (bab, lang_names[i], -1);
}
bson_append_array_builder_end (document, bab);

Rejected idea: reuse bson_t

This alternative was considered: modify bson_t to support tracking the last array index:

// bson_use_array_keys enables calling `bson_array_append_*` functions.
// `bson_array_append_*` functions append using index keys: "0", "1", "2", ...
// It is an error to call `bson_use_array_keys` on a non-empty `bson_t`.
// It is an error to append to the `bson_t` with functions other than
// `bson_array_append_*` after calling `bson_use_array_keys`.
BSON_EXPORT (bool)
bson_use_array_keys (bson_t *);

BSON_EXPORT(bool) bson_array_append_int32 (bson_t* b, int32_t val);

BSON_EXPORT (bool)
bson_array_append_utf8 (bson_t *b, const char *value, int length);

This idea was rejected due in favor of adding a separate bson_array_builder_type. Reusing bson_t may risk users more easily misuse the API.

@kevinAlbs kevinAlbs marked this pull request as ready for review July 14, 2023 20:07
@kevinAlbs kevinAlbs requested a review from adriandole July 17, 2023 20:18
Copy link
Contributor

@joshbsiegel joshbsiegel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@adriandole
Copy link
Contributor

adriandole commented Jul 18, 2023

Trying to append an array that already exists results in duplicate keys.

static void
test (void)
{
   bson_t *doc = tmp_bson ("{'a': 1, 'b': [3,2,1]}");

   bson_array_builder_t *bab;
   bson_append_array_builder_begin (doc, "b", 1, &bab);
   bson_array_builder_append_int32 (bab, 9);
   bson_array_builder_append_int32 (bab, 8);
   bson_append_array_builder_end (doc, bab);

   const char *s = bson_as_json (doc, NULL);
   printf ("%s\n", s); // { "a" : 1, "b" : [ 3, 2, 1 ], "b" : [ 9, 8 ] }
}

I see that we generally don't try to stop users from creating duplicate keys, but I feel like this one is particularly easy to do accidentally. Not sure if that warrants an assert.

@kevinAlbs
Copy link
Collaborator Author

I see that we generally don't try to stop users from creating duplicate keys

libbson does not attempt to stop users from creating duplicate keys. CDRIVER-2982 was resolved to document this as a warning to users.

Copy link
Contributor

@vector-of-bool vector-of-bool left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@kevinAlbs kevinAlbs merged commit 9affd70 into mongodb:master Jul 25, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants