Skip to content

Commit 885229a

Browse files
committed
CXX-1116 result::insert_many needs to hold on to owned _id elements
Fixes a bug where the the return value of result::insert_many::inserted_ids() could contain dangling references.
1 parent 1891831 commit 885229a

File tree

5 files changed

+69
-14
lines changed

5 files changed

+69
-14
lines changed

src/mongocxx/insert_many_builder.cpp

+11-6
Original file line numberDiff line numberDiff line change
@@ -39,25 +39,30 @@ options::bulk_write make_bulk_write_options(const options::insert& insert_option
3939
} // namespace
4040

4141
insert_many_builder::insert_many_builder(const options::insert& options)
42-
: _writes{make_bulk_write_options(options)}, _inserted_ids{}, _index{0} {};
42+
: _writes{make_bulk_write_options(options)}, _inserted_ids{} {
43+
}
4344

4445
void insert_many_builder::operator()(const bsoncxx::document::view& doc) {
46+
bsoncxx::builder::stream::document id_doc;
4547
if (!doc["_id"]) {
46-
bsoncxx::builder::stream::document new_document;
47-
new_document << "_id" << bsoncxx::oid() << bsoncxx::builder::stream::concatenate(doc);
48+
id_doc << "_id" << bsoncxx::oid{};
4849

50+
bsoncxx::builder::stream::document new_document;
51+
new_document << bsoncxx::builder::stream::concatenate(id_doc.view())
52+
<< bsoncxx::builder::stream::concatenate(doc);
4953
_writes.append(model::insert_one{new_document.view()});
50-
_inserted_ids.emplace(_index++, new_document.view()["_id"]);
5154
} else {
55+
id_doc << "_id" << doc["_id"].get_value();
56+
5257
_writes.append(model::insert_one{doc});
53-
_inserted_ids.emplace(_index++, doc["_id"]);
5458
}
59+
_inserted_ids.append(id_doc.view());
5560
};
5661

5762
stdx::optional<result::insert_many> insert_many_builder::insert(collection* col) const {
5863
auto val = col->bulk_write(_writes).value();
5964
result::bulk_write res{std::move(val)};
60-
stdx::optional<result::insert_many> result{{std::move(res), std::move(_inserted_ids)}};
65+
stdx::optional<result::insert_many> result{{std::move(res), _inserted_ids.view()}};
6166
return result;
6267
};
6368

src/mongocxx/insert_many_builder.hpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#pragma once
1616

1717
#include <bsoncxx/stdx/optional.hpp>
18+
#include <bsoncxx/builder/basic/array.hpp>
1819
#include <mongocxx/bulk_write.hpp>
1920
#include <mongocxx/options/insert.hpp>
2021
#include <mongocxx/result/insert_many.hpp>
@@ -60,8 +61,7 @@ class MONGOCXX_API insert_many_builder {
6061

6162
private:
6263
bulk_write _writes;
63-
result::insert_many::id_map _inserted_ids;
64-
std::size_t _index;
64+
bsoncxx::builder::basic::array _inserted_ids;
6565
};
6666

6767
MONGOCXX_INLINE_NAMESPACE_END

src/mongocxx/result/insert_many.cpp

+9-3
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,20 @@
1414

1515
#include <mongocxx/result/insert_many.hpp>
1616

17+
#include <bsoncxx/types/value.hpp>
18+
1719
#include <mongocxx/config/private/prelude.hh>
1820

1921
namespace mongocxx {
2022
MONGOCXX_INLINE_NAMESPACE_BEGIN
2123
namespace result {
2224

23-
insert_many::insert_many(result::bulk_write result, insert_many::id_map inserted_ids)
24-
: _result(std::move(result)), _generated_ids(std::move(inserted_ids)) {
25+
insert_many::insert_many(result::bulk_write result, bsoncxx::array::view inserted_ids)
26+
: _result(std::move(result)), _inserted_ids_owned(inserted_ids) {
27+
std::size_t index = 0;
28+
for (auto&& ele : _inserted_ids_owned.view()) {
29+
_inserted_ids.emplace(index++, ele.get_document().value["_id"]);
30+
}
2531
}
2632

2733
const result::bulk_write& insert_many::result() const {
@@ -33,7 +39,7 @@ std::int32_t insert_many::inserted_count() const {
3339
}
3440

3541
insert_many::id_map insert_many::inserted_ids() {
36-
return _generated_ids;
42+
return _inserted_ids;
3743
}
3844

3945
} // namespace result

src/mongocxx/result/insert_many.hpp

+12-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <cstdint>
1818
#include <map>
1919

20+
#include <bsoncxx/array/value.hpp>
2021
#include <bsoncxx/types.hpp>
2122
#include <mongocxx/result/bulk_write.hpp>
2223

@@ -55,18 +56,26 @@ class MONGOCXX_API insert_many {
5556
///
5657
/// Gets the _ids of the inserted documents.
5758
///
58-
/// @return The values of the _id field for inserted documents.
59+
/// @note The returned id_map must not be accessed after the result::insert_many object is
60+
/// destroyed.
61+
/// @return Map of the index of the operation to the _id of the inserted document.
5962
///
6063
id_map inserted_ids();
6164

6265
private:
6366
friend collection;
6467
friend insert_many_builder;
6568

66-
MONGOCXX_PRIVATE insert_many(result::bulk_write result, id_map inserted_ids);
69+
MONGOCXX_PRIVATE insert_many(result::bulk_write result, bsoncxx::array::view inserted_ids);
6770

6871
result::bulk_write _result;
69-
id_map _generated_ids;
72+
73+
// Array containing documents with the values of the _id field for the inserted documents. This
74+
// array is in the following format: [{"_id": ...}, {"_id": ...}, ...].
75+
bsoncxx::array::value _inserted_ids_owned;
76+
77+
// Points into _inserted_ids_owned.
78+
id_map _inserted_ids;
7079
};
7180

7281
} // namespace result

src/mongocxx/test/collection.cpp

+35
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,41 @@ TEST_CASE("CRUD functionality", "[driver::collection]") {
147147
REQUIRE(i == 4);
148148
}
149149

150+
SECTION("insert_many returns correct result object", "[collection]") {
151+
document b1;
152+
document b2;
153+
154+
b1 << "_id"
155+
<< "foo"
156+
<< "x" << 1;
157+
b2 << "x" << 2;
158+
159+
std::vector<bsoncxx::document::view> docs{};
160+
docs.push_back(b1.view());
161+
docs.push_back(b2.view());
162+
163+
auto result = coll.insert_many(docs);
164+
165+
REQUIRE(result);
166+
167+
// Verify result->result() is correct:
168+
REQUIRE(result->result().inserted_count() == 2);
169+
170+
// Verify result->inserted_count() is correct:
171+
REQUIRE(result->inserted_count() == 2);
172+
173+
// Verify result->inserted_ids() is correct:
174+
auto id_map = result->inserted_ids();
175+
REQUIRE(id_map[0].type() == bsoncxx::type::k_utf8);
176+
REQUIRE(id_map[0].get_utf8().value == stdx::string_view{"foo"});
177+
REQUIRE(id_map[1].type() == bsoncxx::type::k_oid);
178+
auto second_inserted_doc = coll.find_one(document{} << "x" << 2 << finalize);
179+
REQUIRE(second_inserted_doc);
180+
REQUIRE(second_inserted_doc->view()["_id"]);
181+
REQUIRE(second_inserted_doc->view()["_id"].type() == bsoncxx::type::k_oid);
182+
REQUIRE(id_map[1].get_oid().value == second_inserted_doc->view()["_id"].get_oid().value);
183+
}
184+
150185
SECTION("insert and update single document", "[collection]") {
151186
auto b1 = document{} << "_id" << 1 << finalize;
152187

0 commit comments

Comments
 (0)