From 877d5db738ac9a649e80e8683ad7eebe5540d208 Mon Sep 17 00:00:00 2001 From: Kaitlin Mahar Date: Thu, 16 May 2019 18:38:25 -0400 Subject: [PATCH 1/3] use withMutableBSONPointer helper everywhere, rename internal properties --- Sources/MongoSwift/BSON/BSONValue.swift | 40 +++---- Sources/MongoSwift/BSON/Document.swift | 103 +++++++++--------- .../MongoSwift/BSON/DocumentIterator.swift | 24 ++-- Sources/MongoSwift/ClientSession.swift | 10 +- Sources/MongoSwift/MongoClient.swift | 2 +- .../MongoCollection+BulkWrite.swift | 34 +++--- .../MongoSwift/MongoCollection+Indexes.swift | 2 +- Sources/MongoSwift/MongoCollection+Read.swift | 4 +- Sources/MongoSwift/MongoDatabase.swift | 2 +- .../Operations/CountOperation.swift | 2 +- .../CreateCollectionOperation.swift | 2 +- .../Operations/CreateIndexesOperation.swift | 10 +- .../Operations/DistinctOperation.swift | 9 +- .../Operations/DropIndexesOperation.swift | 11 +- .../Operations/FindAndModifyOperation.swift | 26 +++-- .../Operations/RunCommandOperation.swift | 11 +- Sources/MongoSwift/ReadPreference.swift | 2 +- .../Document+CollectionTests.swift | 28 +---- Tests/MongoSwiftTests/DocumentTests.swift | 72 ++++++------ 19 files changed, 190 insertions(+), 204 deletions(-) diff --git a/Sources/MongoSwift/BSON/BSONValue.swift b/Sources/MongoSwift/BSON/BSONValue.swift index c9951b759..1d240595e 100644 --- a/Sources/MongoSwift/BSON/BSONValue.swift +++ b/Sources/MongoSwift/BSON/BSONValue.swift @@ -219,7 +219,7 @@ extension Array: BSONValue { try arr.setValue(for: String(i), to: val) } - guard bson_append_array(storage.pointer, key, Int32(key.utf8.count), arr.data) else { + guard bson_append_array(storage._bson, key, Int32(key.utf8.count), arr._bson) else { throw bsonTooLargeError(value: self, forKey: key) } } @@ -255,7 +255,7 @@ public struct BSONNull: BSONValue, Codable, Equatable { } public func encode(to storage: DocumentStorage, forKey key: String) throws { - guard bson_append_null(storage.pointer, key, Int32(key.utf8.count)) else { + guard bson_append_null(storage._bson, key, Int32(key.utf8.count)) else { throw bsonTooLargeError(value: self, forKey: key) } } @@ -356,7 +356,7 @@ public struct Binary: BSONValue, Equatable, Codable { let subtype = bson_subtype_t(UInt32(self.subtype)) let length = self.data.count let byteArray = [UInt8](self.data) - guard bson_append_binary(storage.pointer, key, Int32(key.utf8.count), subtype, byteArray, UInt32(length)) else { + guard bson_append_binary(storage._bson, key, Int32(key.utf8.count), subtype, byteArray, UInt32(length)) else { throw bsonTooLargeError(value: self, forKey: key) } } @@ -392,7 +392,7 @@ extension Bool: BSONValue { public var bsonType: BSONType { return .boolean } public func encode(to storage: DocumentStorage, forKey key: String) throws { - guard bson_append_bool(storage.pointer, key, Int32(key.utf8.count), self) else { + guard bson_append_bool(storage._bson, key, Int32(key.utf8.count), self) else { throw bsonTooLargeError(value: self, forKey: key) } } @@ -422,7 +422,7 @@ extension Date: BSONValue { public var msSinceEpoch: Int64 { return Int64((self.timeIntervalSince1970 * 1000.0).rounded()) } public func encode(to storage: DocumentStorage, forKey key: String) throws { - guard bson_append_date_time(storage.pointer, key, Int32(key.utf8.count), self.msSinceEpoch) else { + guard bson_append_date_time(storage._bson, key, Int32(key.utf8.count), self.msSinceEpoch) else { throw bsonTooLargeError(value: self, forKey: key) } } @@ -464,7 +464,7 @@ public struct DBPointer: BSONValue, Codable, Equatable { public func encode(to storage: DocumentStorage, forKey key: String) throws { try withUnsafePointer(to: id.oid) { oidPtr in - guard bson_append_dbpointer(storage.pointer, key, Int32(key.utf8.count), self.ref, oidPtr) else { + guard bson_append_dbpointer(storage._bson, key, Int32(key.utf8.count), self.ref, oidPtr) else { throw bsonTooLargeError(value: self, forKey: key) } } @@ -538,7 +538,7 @@ public struct Decimal128: BSONNumber, Equatable, Codable, CustomStringConvertibl public func encode(to storage: DocumentStorage, forKey key: String) throws { try withUnsafePointer(to: self.decimal128) { ptr in - guard bson_append_decimal128(storage.pointer, key, Int32(key.utf8.count), ptr) else { + guard bson_append_decimal128(storage._bson, key, Int32(key.utf8.count), ptr) else { throw bsonTooLargeError(value: self, forKey: key) } } @@ -601,7 +601,7 @@ extension Double: BSONNumber { public var bsonType: BSONType { return .double } public func encode(to storage: DocumentStorage, forKey key: String) throws { - guard bson_append_double(storage.pointer, key, Int32(key.utf8.count), self) else { + guard bson_append_double(storage._bson, key, Int32(key.utf8.count), self) else { throw bsonTooLargeError(value: self, forKey: key) } } @@ -681,7 +681,7 @@ extension Int32: BSONNumber { public var bsonType: BSONType { return .int32 } public func encode(to storage: DocumentStorage, forKey key: String) throws { - guard bson_append_int32(storage.pointer, key, Int32(key.utf8.count), self) else { + guard bson_append_int32(storage._bson, key, Int32(key.utf8.count), self) else { throw bsonTooLargeError(value: self, forKey: key) } } @@ -711,7 +711,7 @@ extension Int64: BSONNumber { public var bsonType: BSONType { return .int64 } public func encode(to storage: DocumentStorage, forKey key: String) throws { - guard bson_append_int64(storage.pointer, key, Int32(key.utf8.count), self) else { + guard bson_append_int64(storage._bson, key, Int32(key.utf8.count), self) else { throw bsonTooLargeError(value: self, forKey: key) } } @@ -764,11 +764,11 @@ public struct CodeWithScope: BSONValue, Equatable, Codable { public func encode(to storage: DocumentStorage, forKey key: String) throws { if let s = self.scope { - guard bson_append_code_with_scope(storage.pointer, key, Int32(key.utf8.count), self.code, s.data) else { + guard bson_append_code_with_scope(storage._bson, key, Int32(key.utf8.count), self.code, s._bson) else { throw bsonTooLargeError(value: self, forKey: key) } } else { - guard bson_append_code(storage.pointer, key, Int32(key.utf8.count), self.code) else { + guard bson_append_code(storage._bson, key, Int32(key.utf8.count), self.code) else { throw bsonTooLargeError(value: self, forKey: key) } } @@ -811,7 +811,7 @@ public struct MaxKey: BSONValue, Equatable, Codable { public var bsonType: BSONType { return .maxKey } public func encode(to storage: DocumentStorage, forKey key: String) throws { - guard bson_append_maxkey(storage.pointer, key, Int32(key.utf8.count)) else { + guard bson_append_maxkey(storage._bson, key, Int32(key.utf8.count)) else { throw bsonTooLargeError(value: self, forKey: key) } } @@ -842,7 +842,7 @@ public struct MinKey: BSONValue, Equatable, Codable { public var bsonType: BSONType { return .minKey } public func encode(to storage: DocumentStorage, forKey key: String) throws { - guard bson_append_minkey(storage.pointer, key, Int32(key.utf8.count)) else { + guard bson_append_minkey(storage._bson, key, Int32(key.utf8.count)) else { throw bsonTooLargeError(value: self, forKey: key) } } @@ -925,7 +925,7 @@ public struct ObjectId: BSONValue, Equatable, CustomStringConvertible, Codable { public func encode(to storage: DocumentStorage, forKey key: String) throws { // encode the bson_oid_t to the bson_t try withUnsafePointer(to: self.oid) { oidPtr in - guard bson_append_oid(storage.pointer, key, Int32(key.utf8.count), oidPtr) else { + guard bson_append_oid(storage._bson, key, Int32(key.utf8.count), oidPtr) else { throw bsonTooLargeError(value: self, forKey: key) } } @@ -1044,7 +1044,7 @@ public struct RegularExpression: BSONValue, Equatable, Codable { } public func encode(to storage: DocumentStorage, forKey key: String) throws { - guard bson_append_regex(storage.pointer, key, Int32(key.utf8.count), self.pattern, self.options) else { + guard bson_append_regex(storage._bson, key, Int32(key.utf8.count), self.pattern, self.options) else { throw bsonTooLargeError(value: self, forKey: key) } } @@ -1077,7 +1077,7 @@ extension String: BSONValue { public var bsonType: BSONType { return .string } public func encode(to storage: DocumentStorage, forKey key: String) throws { - guard bson_append_utf8(storage.pointer, key, Int32(key.utf8.count), self, Int32(self.utf8.count)) else { + guard bson_append_utf8(storage._bson, key, Int32(key.utf8.count), self, Int32(self.utf8.count)) else { throw bsonTooLargeError(value: self, forKey: key) } } @@ -1135,7 +1135,7 @@ public struct Symbol: BSONValue, CustomStringConvertible, Codable, Equatable { public func encode(to storage: DocumentStorage, forKey key: String) throws { guard bson_append_symbol( - storage.pointer, + storage._bson, key, Int32(key.utf8.count), self.stringValue, @@ -1191,7 +1191,7 @@ public struct Timestamp: BSONValue, Equatable, Codable { } public func encode(to storage: DocumentStorage, forKey key: String) throws { - guard bson_append_timestamp(storage.pointer, key, Int32(key.utf8.count), self.timestamp, self.increment) else { + guard bson_append_timestamp(storage._bson, key, Int32(key.utf8.count), self.timestamp, self.increment) else { throw bsonTooLargeError(value: self, forKey: key) } } @@ -1227,7 +1227,7 @@ public struct BSONUndefined: BSONValue, Equatable, Codable { } public func encode(to storage: DocumentStorage, forKey key: String) throws { - guard bson_append_undefined(storage.pointer, key, Int32(key.utf8.count)) else { + guard bson_append_undefined(storage._bson, key, Int32(key.utf8.count)) else { throw bsonTooLargeError(value: self, forKey: key) } } diff --git a/Sources/MongoSwift/BSON/Document.swift b/Sources/MongoSwift/BSON/Document.swift index a13c413bc..fd516f2ab 100644 --- a/Sources/MongoSwift/BSON/Document.swift +++ b/Sources/MongoSwift/BSON/Document.swift @@ -11,57 +11,51 @@ internal typealias MutableBSONPointer = UnsafeMutablePointer /// The storage backing a MongoSwift `Document`. public class DocumentStorage { - internal var pointer: MutableBSONPointer! - - // Normally, this would go under Document, but computed properties cannot be used before all stored properties are - // initialized. Putting this under DocumentStorage gives a correct count and use of it inside of an init() as long - // as we have initialized Document.storage beforehand. - internal var count: Int { - return Int(bson_count_keys(self.pointer)) - } + internal var _bson: MutableBSONPointer! /// Initializes a new, empty `DocumentStorage`. internal init() { - self.pointer = bson_new() + self._bson = bson_new() } /// Initializes a new `DocumentStorage` by copying over the data from the provided `bson_t`. internal init(copying pointer: BSONPointer) { - self.pointer = bson_copy(pointer) + self._bson = bson_copy(pointer) } /// Initializes a new `DocumentStorage` that uses the provided `bson_t` as its backing storage. /// The newly created instance will handle cleaning up the pointer upon deinitialization. internal init(stealing pointer: MutableBSONPointer) { - self.pointer = pointer + self._bson = pointer } /// Cleans up internal state. deinit { - guard let pointer = self.pointer else { + guard let bson = self._bson else { return } - bson_destroy(pointer) - self.pointer = nil + bson_destroy(bson) + self._bson = nil } } /// A struct representing the BSON document type. @dynamicMemberLookup public struct Document { - /// the storage backing this document - internal var storage: DocumentStorage - - /// Returns the number of (key, value) pairs stored at the top level of this `Document`. - public internal(set) var count: Int + /// the storage backing this document. + internal var _storage: DocumentStorage } /// An extension of `Document` containing its private/internal functionality. extension Document { - /// direct access to the storage's pointer to a bson_t - internal var data: MutableBSONPointer { - return storage.pointer + /// Read-only access to the storage's underlying bson_t. + internal var _bson: BSONPointer { +#if compiler(>=5.0) + return self._storage._bson +#else + return UnsafePointer(self._storage._bson) +#endif } /** @@ -74,8 +68,7 @@ extension Document { * - Returns: a new `Document` */ internal init(copying pointer: BSONPointer) { - self.storage = DocumentStorage(copying: pointer) - self.count = self.storage.count + self._storage = DocumentStorage(copying: pointer) } /** @@ -88,8 +81,7 @@ extension Document { * - Returns: a new `Document` */ internal init(stealing pointer: MutableBSONPointer) { - self.storage = DocumentStorage(stealing: pointer) - self.count = self.storage.count + self._storage = DocumentStorage(stealing: pointer) } /** @@ -101,8 +93,7 @@ extension Document { * - Returns: a new `Document` */ internal init(_ elements: [KeyValuePair]) { - self.storage = DocumentStorage() - self.count = 0 + self._storage = DocumentStorage() for (key, value) in elements { do { try self.setValue(for: key, to: value) @@ -123,8 +114,7 @@ extension Document { * - Returns: a new `Document` */ internal init(_ elements: [BSONValue]) { - self.storage = DocumentStorage() - self.count = 0 + self._storage = DocumentStorage() for (i, elt) in elements.enumerated() { do { try self.setValue(for: String(i), to: elt, checkForKey: false) @@ -182,8 +172,7 @@ extension Document { // otherwise, it's a new key } else { self.copyStorageIfRequired() - try newValue.encode(to: self.storage, forKey: key) - self.count += 1 + try newValue.encode(to: self._storage, forKey: key) } } @@ -231,12 +220,13 @@ extension Document { /// Appends the key/value pairs from the provided `doc` to this `Document`. /// Note: This function does not check for or clean away duplicate keys. internal mutating func merge(_ doc: Document) throws { - self.copyStorageIfRequired() - guard bson_concat(self.data, doc.data) else { + let success = withMutableBSONPointer(to: &self) { selfPtr in + bson_concat(selfPtr, doc._bson) + } + guard success else { throw RuntimeError.internalError(message: "Failed to merge \(doc) with \(self). This is likely due to " + "the merged document being too large.") } - self.count += doc.count } /** @@ -252,9 +242,9 @@ extension Document { * Therefore, this function should be called just before we are about to modify a document - either by * setting a value or merging in another doc. */ - internal mutating func copyStorageIfRequired() { - if !isKnownUniquelyReferenced(&self.storage) { - self.storage = DocumentStorage(copying: self.data) + fileprivate mutating func copyStorageIfRequired() { + if !isKnownUniquelyReferenced(&self._storage) { + self._storage = DocumentStorage(copying: self._bson) } } @@ -283,10 +273,15 @@ extension Document { return self.makeIterator().values } + /// Returns the number of (key, value) pairs stored at the top level of this `Document`. + public var count: Int { + return Int(bson_count_keys(self._bson)) + } + /// Returns the relaxed extended JSON representation of this `Document`. /// On error, an empty string will be returned. public var extendedJSON: String { - guard let json = bson_as_relaxed_extended_json(self.data, nil) else { + guard let json = bson_as_relaxed_extended_json(self._bson, nil) else { return "" } @@ -300,7 +295,7 @@ extension Document { /// Returns the canonical extended JSON representation of this `Document`. /// On error, an empty string will be returned. public var canonicalExtendedJSON: String { - guard let json = bson_as_canonical_extended_json(self.data, nil) else { + guard let json = bson_as_canonical_extended_json(self._bson, nil) else { return "" } @@ -314,7 +309,7 @@ extension Document { /// Returns a copy of the raw BSON data for this `Document`, represented as `Data`. public var rawBSON: Data { // swiftlint:disable:next force_unwrapping - let data = bson_get_data(self.data)! // documented as always returning a value. + let data = bson_get_data(self._bson)! // documented as always returning a value. /// BSON encodes the length in the first four bytes, so we can read it in from the /// raw data without needing to access the `len` field of the `bson_t`. @@ -325,8 +320,7 @@ extension Document { /// Initializes a new, empty `Document`. public init() { - self.storage = DocumentStorage() - self.count = 0 + self._storage = DocumentStorage() } /** @@ -341,7 +335,7 @@ extension Document { * - A `UserError.invalidArgumentError` if the data passed in is invalid JSON. */ public init(fromJSON: Data) throws { - self.storage = DocumentStorage(stealing: try fromJSON.withUnsafeBytes { (bytes: UnsafePointer) in + self._storage = DocumentStorage(stealing: try fromJSON.withUnsafeBytes { (bytes: UnsafePointer) in var error = bson_error_t() guard let bson = bson_new_from_json(bytes, fromJSON.count, &error) else { if error.domain == BSON_ERROR_JSON { @@ -352,7 +346,6 @@ extension Document { return bson }) - self.count = self.storage.count } /// Convenience initializer for constructing a `Document` from a `String`. @@ -366,15 +359,14 @@ extension Document { /// Constructs a `Document` from raw BSON `Data`. public init(fromBSON: Data) { - self.storage = DocumentStorage(stealing: fromBSON.withUnsafeBytes { (bytes: UnsafePointer) in + self._storage = DocumentStorage(stealing: fromBSON.withUnsafeBytes { (bytes: UnsafePointer) in bson_new_from_data(bytes, fromBSON.count) }) - self.count = self.storage.count } /// Returns a `Boolean` indicating whether this `Document` contains the provided key. public func hasKey(_ key: String) -> Bool { - return bson_has_field(self.data, key) + return bson_has_field(self._bson, key) } /** @@ -450,7 +442,7 @@ extension Document: BSONValue { public var bsonType: BSONType { return .document } public func encode(to storage: DocumentStorage, forKey key: String) throws { - guard bson_append_document(storage.pointer, key, Int32(key.utf8.count), self.data) else { + guard bson_append_document(storage._bson, key, Int32(key.utf8.count), self._bson) else { throw bsonTooLargeError(value: self, forKey: key) } } @@ -482,7 +474,7 @@ extension Document: BSONValue { /// An extension of `Document` to make it `Equatable`. extension Document: Equatable { public static func == (lhs: Document, rhs: Document) -> Bool { - return bson_compare(lhs.data, rhs.data) == 0 + return bson_compare(lhs._bson, rhs._bson) == 0 } } @@ -513,10 +505,7 @@ extension Document: ExpressibleByDictionaryLiteral { fatalError("Dictionary literal \(keyValuePairs) contains duplicate keys") } - self.storage = DocumentStorage() - // This is technically not consistent, but the only way this inconsistency can cause an issue is if we fail to - // setValue(), in which case we crash anyways. - self.count = 0 + self._storage = DocumentStorage() for (key, value) in keyValuePairs { do { try self.setValue(for: key, to: value, checkForKey: false) @@ -527,6 +516,12 @@ extension Document: ExpressibleByDictionaryLiteral { } } +internal func withMutableBSONPointer(to document: inout Document, + body: (MutableBSONPointer) throws -> T) rethrows -> T { + document.copyStorageIfRequired() + return try body(document._storage._bson) +} + /// An extension of `Document` to add the capability to be initialized with an array literal. extension Document: ExpressibleByArrayLiteral { /** diff --git a/Sources/MongoSwift/BSON/DocumentIterator.swift b/Sources/MongoSwift/BSON/DocumentIterator.swift index 8e54ae081..d8b3966d9 100644 --- a/Sources/MongoSwift/BSON/DocumentIterator.swift +++ b/Sources/MongoSwift/BSON/DocumentIterator.swift @@ -13,18 +13,18 @@ internal typealias MutableBSONIterPointer = UnsafeMutablePointer public class DocumentIterator: IteratorProtocol { /// the libbson iterator. it must be a `var` because we use it as /// an inout argument - internal var iter: bson_iter_t + internal var _iter: bson_iter_t /// a reference to the storage for the document we're iterating - internal let storage: DocumentStorage + internal let _storage: DocumentStorage /// Initializes a new iterator over the contents of `doc`. Returns `nil` if the key is not /// found, or if an iterator cannot be created over `doc` due to an error from e.g. corrupt data. internal init?(forDocument doc: Document) { - self.iter = bson_iter_t() - self.storage = doc.storage + self._iter = bson_iter_t() + self._storage = doc._storage let initialized = self.withMutableBSONIterPointer { iterPtr in - bson_iter_init(iterPtr, doc.data) + bson_iter_init(iterPtr, doc._bson) } guard initialized else { @@ -35,11 +35,11 @@ public class DocumentIterator: IteratorProtocol { /// Initializes a new iterator over the contents of `doc`. Returns `nil` if an iterator cannot /// be created over `doc` due to an error from e.g. corrupt data, or if the key is not found. internal init?(forDocument doc: Document, advancedTo key: String) { - self.iter = bson_iter_t() - self.storage = doc.storage + self._iter = bson_iter_t() + self._storage = doc._storage let initialized = self.withMutableBSONIterPointer { iterPtr in - bson_iter_init_find(iterPtr, doc.data, key.cString(using: .utf8)) + bson_iter_init_find(iterPtr, doc._bson, key.cString(using: .utf8)) } guard initialized else { @@ -168,11 +168,11 @@ public class DocumentIterator: IteratorProtocol { /// Internal helper function for explicitly accessing the `bson_iter_t` as an unsafe pointer internal func withBSONIterPointer(_ body: (BSONIterPointer) throws -> Result) rethrows -> Result { #if compiler(>=5.0) - return try withUnsafePointer(to: self.iter) { iterPtr in + return try withUnsafePointer(to: self._iter) { iterPtr in try body(BSONIterPointer(iterPtr)) } #else - return try withUnsafePointer(to: self.iter, body) + return try withUnsafePointer(to: self._iter, body) #endif } @@ -181,11 +181,11 @@ public class DocumentIterator: IteratorProtocol { _ body: (MutableBSONIterPointer) throws -> Result ) rethrows -> Result { #if compiler(>=5.0) - return try withUnsafeMutablePointer(to: &self.iter) { iterPtr in + return try withUnsafeMutablePointer(to: &self._iter) { iterPtr in try body(MutableBSONIterPointer(iterPtr)) } #else - return try withUnsafeMutablePointer(to: &self.iter, body) + return try withUnsafeMutablePointer(to: &self._iter, body) #endif } diff --git a/Sources/MongoSwift/ClientSession.swift b/Sources/MongoSwift/ClientSession.swift index b11363633..7b4930b5f 100644 --- a/Sources/MongoSwift/ClientSession.swift +++ b/Sources/MongoSwift/ClientSession.swift @@ -133,7 +133,7 @@ public final class ClientSession { * - clusterTime: The session's new cluster time, as a `Document` like `["cluster time": Timestamp(...)]` */ public func advanceClusterTime(to clusterTime: Document) { - mongoc_client_session_advance_cluster_time(self._session, clusterTime.storage.pointer) + mongoc_client_session_advance_cluster_time(self._session, clusterTime._storage._bson) } /** @@ -155,11 +155,11 @@ public final class ClientSession { throw ClientSession.SessionInactiveError } - doc.copyStorageIfRequired() var error = bson_error_t() - guard mongoc_client_session_append(self._session, doc.storage.pointer, &error) else { - throw parseMongocError(error) + try withMutableBSONPointer(to: &doc) { docPtr in + guard mongoc_client_session_append(self._session, docPtr, &error) else { + throw parseMongocError(error) + } } - doc.count = Int(bson_count_keys(doc.storage.pointer)) } } diff --git a/Sources/MongoSwift/MongoClient.swift b/Sources/MongoSwift/MongoClient.swift index b723f6e08..8e7714a8e 100644 --- a/Sources/MongoSwift/MongoClient.swift +++ b/Sources/MongoSwift/MongoClient.swift @@ -289,7 +289,7 @@ public class MongoClient { public func listDatabases(options: ListDatabasesOptions? = nil, session: ClientSession? = nil) throws -> MongoCursor { let opts = try encodeOptions(options: options, session: session) - guard let cursor = mongoc_client_find_databases_with_opts(self._client, opts?.data) else { + guard let cursor = mongoc_client_find_databases_with_opts(self._client, opts?._bson) else { fatalError("Couldn't get cursor from the server") } return try MongoCursor(from: cursor, client: self, decoder: self.decoder, session: session) diff --git a/Sources/MongoSwift/MongoCollection+BulkWrite.swift b/Sources/MongoSwift/MongoCollection+BulkWrite.swift index 153715137..995ae387c 100644 --- a/Sources/MongoSwift/MongoCollection+BulkWrite.swift +++ b/Sources/MongoSwift/MongoCollection+BulkWrite.swift @@ -71,7 +71,8 @@ extension MongoCollection { let opts = try bulk.encoder.encode(DeleteModelOptions(collation: self.collation)) var error = bson_error_t() - guard mongoc_bulk_operation_remove_one_with_opts(bulk.bulk, self.filter.data, opts.data, &error) else { + guard mongoc_bulk_operation_remove_one_with_opts( + bulk.bulk, self.filter._bson, opts._bson, &error) else { throw parseMongocError(error) // Should be invalidArgumentError } } @@ -108,7 +109,8 @@ extension MongoCollection { var error = bson_error_t() let opts = try bulk.encoder.encode(DeleteModelOptions(collation: self.collation)) - guard mongoc_bulk_operation_remove_many_with_opts(bulk.bulk, self.filter.data, opts.data, &error) else { + guard mongoc_bulk_operation_remove_many_with_opts( + bulk.bulk, self.filter._bson, opts._bson, &error) else { throw parseMongocError(error) // should be invalidArgumentError } } @@ -139,7 +141,7 @@ extension MongoCollection { public func addToBulkWrite(bulk: BulkWriteOperation, index: Int) throws { let document = try bulk.encoder.encode(self.document).withID() var error = bson_error_t() - guard mongoc_bulk_operation_insert_with_opts(bulk.bulk, document.data, nil, &error) else { + guard mongoc_bulk_operation_insert_with_opts(bulk.bulk, document._bson, nil, &error) else { throw parseMongocError(error) // should be invalidArgumentError } @@ -200,9 +202,9 @@ extension MongoCollection { var error = bson_error_t() guard mongoc_bulk_operation_replace_one_with_opts(bulk.bulk, - self.filter.data, - replacement.data, - opts.data, + self.filter._bson, + replacement._bson, + opts._bson, &error) else { throw parseMongocError(error) // should be invalidArgumentError } @@ -268,9 +270,9 @@ extension MongoCollection { var error = bson_error_t() guard mongoc_bulk_operation_update_one_with_opts(bulk.bulk, - self.filter.data, - self.update.data, - opts.data, + self.filter._bson, + self.update._bson, + opts._bson, &error) else { throw parseMongocError(error) // should be invalidArgumentError } @@ -330,9 +332,9 @@ extension MongoCollection { var error = bson_error_t() guard mongoc_bulk_operation_update_many_with_opts(bulk.bulk, - self.filter.data, - self.update.data, - opts.data, + self.filter._bson, + self.update._bson, + opts._bson, &error) else { throw parseMongocError(error) // should be invalidArgumentError } @@ -376,7 +378,7 @@ public class BulkWriteOperation: Operation { fileprivate init(collection: OpaquePointer?, opts: Document?, withEncoder: BSONEncoder) { // documented as always returning a value. // swiftlint:disable:next force_unwrapping - self.bulk = mongoc_collection_create_bulk_operation_with_opts(collection, opts?.data)! + self.bulk = mongoc_collection_create_bulk_operation_with_opts(collection, opts?._bson)! self.opts = opts self.encoder = withEncoder } @@ -390,10 +392,12 @@ public class BulkWriteOperation: Operation { * - `ServerError.bulkWriteError` if an error occurs while performing the writes. */ internal func execute() throws -> BulkWriteResult? { - let reply = Document() + var reply = Document() var error = bson_error_t() + let serverId = withMutableBSONPointer(to: &reply) { replyPtr in + mongoc_bulk_operation_execute(self.bulk, replyPtr, &error) + } - let serverId = mongoc_bulk_operation_execute(self.bulk, reply.data, &error) let result = try BulkWriteResult(reply: reply, insertedIds: self.insertedIds) guard serverId != 0 else { diff --git a/Sources/MongoSwift/MongoCollection+Indexes.swift b/Sources/MongoSwift/MongoCollection+Indexes.swift index 6dd2f55ea..7b19c6c11 100644 --- a/Sources/MongoSwift/MongoCollection+Indexes.swift +++ b/Sources/MongoSwift/MongoCollection+Indexes.swift @@ -332,7 +332,7 @@ extension MongoCollection { public func listIndexes(session: ClientSession? = nil) throws -> MongoCursor { let opts = try encodeOptions(options: Document(), session: session) - guard let cursor = mongoc_collection_find_indexes_with_opts(self._collection, opts?.data) else { + guard let cursor = mongoc_collection_find_indexes_with_opts(self._collection, opts?._bson) else { fatalError("Couldn't get cursor from the server") } diff --git a/Sources/MongoSwift/MongoCollection+Read.swift b/Sources/MongoSwift/MongoCollection+Read.swift index 106735d82..178666ed4 100644 --- a/Sources/MongoSwift/MongoCollection+Read.swift +++ b/Sources/MongoSwift/MongoCollection+Read.swift @@ -22,7 +22,7 @@ extension MongoCollection { let opts = try encodeOptions(options: options, session: session) let rp = options?.readPreference?._readPreference - guard let cursor = mongoc_collection_find_with_opts(self._collection, filter.data, opts?.data, rp) else { + guard let cursor = mongoc_collection_find_with_opts(self._collection, filter._bson, opts?._bson, rp) else { fatalError("Couldn't get cursor from the server") } return try MongoCursor(from: cursor, client: self._client, decoder: self.decoder, session: session) @@ -50,7 +50,7 @@ extension MongoCollection { let pipeline: Document = ["pipeline": pipeline] guard let cursor = mongoc_collection_aggregate( - self._collection, MONGOC_QUERY_NONE, pipeline.data, opts?.data, rp) else { + self._collection, MONGOC_QUERY_NONE, pipeline._bson, opts?._bson, rp) else { fatalError("Couldn't get cursor from the server") } return try MongoCursor(from: cursor, client: self._client, decoder: self.decoder, session: session) diff --git a/Sources/MongoSwift/MongoDatabase.swift b/Sources/MongoSwift/MongoDatabase.swift index 9bd788236..5b5a4d0d5 100644 --- a/Sources/MongoSwift/MongoDatabase.swift +++ b/Sources/MongoSwift/MongoDatabase.swift @@ -235,7 +235,7 @@ public class MongoDatabase { session: ClientSession? = nil) throws -> MongoCursor { let opts = try encodeOptions(options: options, session: session) - guard let collections = mongoc_database_find_collections_with_opts(self._database, opts?.data) else { + guard let collections = mongoc_database_find_collections_with_opts(self._database, opts?._bson) else { fatalError("Couldn't get cursor from the server") } diff --git a/Sources/MongoSwift/Operations/CountOperation.swift b/Sources/MongoSwift/Operations/CountOperation.swift index ae10b3c76..2c3ca8a32 100644 --- a/Sources/MongoSwift/Operations/CountOperation.swift +++ b/Sources/MongoSwift/Operations/CountOperation.swift @@ -69,7 +69,7 @@ internal struct CountOperation: Operation { // because we already encode skip and limit in the options, // pass in 0s so we don't get duplicate parameter errors. let count = mongoc_collection_count_with_opts( - self.collection._collection, MONGOC_QUERY_NONE, self.filter.data, 0, 0, opts?.data, rp, &error) + self.collection._collection, MONGOC_QUERY_NONE, self.filter._bson, 0, 0, opts?._bson, rp, &error) if count == -1 { throw parseMongocError(error) } diff --git a/Sources/MongoSwift/Operations/CreateCollectionOperation.swift b/Sources/MongoSwift/Operations/CreateCollectionOperation.swift index e305f8677..2a09e05ab 100644 --- a/Sources/MongoSwift/Operations/CreateCollectionOperation.swift +++ b/Sources/MongoSwift/Operations/CreateCollectionOperation.swift @@ -129,7 +129,7 @@ internal struct CreateCollectionOperation: Operation { var error = bson_error_t() guard let collection = mongoc_database_create_collection( - self.database._database, self.name, opts?.data, &error) else { + self.database._database, self.name, opts?._bson, &error) else { throw parseMongocError(error) } mongoc_collection_destroy(collection) diff --git a/Sources/MongoSwift/Operations/CreateIndexesOperation.swift b/Sources/MongoSwift/Operations/CreateIndexesOperation.swift index d28815741..db822bf72 100644 --- a/Sources/MongoSwift/Operations/CreateIndexesOperation.swift +++ b/Sources/MongoSwift/Operations/CreateIndexesOperation.swift @@ -42,11 +42,13 @@ internal struct CreateIndexesOperation: Operation { let opts = try encodeOptions(options: options, session: session) + var reply = Document() var error = bson_error_t() - let reply = Document() - - guard mongoc_collection_write_command_with_opts( - self.collection._collection, command.data, opts?.data, reply.data, &error) else { + let success = withMutableBSONPointer(to: &reply) { replyPtr in + mongoc_collection_write_command_with_opts( + self.collection._collection, command._bson, opts?._bson, replyPtr, &error) + } + guard success else { throw getErrorFromReply(bsonError: error, from: reply) } diff --git a/Sources/MongoSwift/Operations/DistinctOperation.swift b/Sources/MongoSwift/Operations/DistinctOperation.swift index 27846c1da..5f44ed9c4 100644 --- a/Sources/MongoSwift/Operations/DistinctOperation.swift +++ b/Sources/MongoSwift/Operations/DistinctOperation.swift @@ -60,10 +60,13 @@ internal struct DistinctOperation { let opts = try encodeOptions(options: self.options, session: self.session) let rp = self.options?.readPreference?._readPreference - let reply = Document() + var reply = Document() var error = bson_error_t() - guard mongoc_collection_read_command_with_opts( - self.collection._collection, command.data, rp, opts?.data, reply.data, &error) else { + let success = withMutableBSONPointer(to: &reply) { replyPtr in + mongoc_collection_read_command_with_opts( + self.collection._collection, command._bson, rp, opts?._bson, replyPtr, &error) + } + guard success else { throw parseMongocError(error, errorLabels: reply["errorLabels"] as? [String]) } diff --git a/Sources/MongoSwift/Operations/DropIndexesOperation.swift b/Sources/MongoSwift/Operations/DropIndexesOperation.swift index ee1467f81..c26dff9f1 100644 --- a/Sources/MongoSwift/Operations/DropIndexesOperation.swift +++ b/Sources/MongoSwift/Operations/DropIndexesOperation.swift @@ -31,13 +31,16 @@ internal struct DropIndexesOperation: Operation { internal func execute() throws -> Document { let command: Document = ["dropIndexes": self.collection.name, "index": self.index] let opts = try encodeOptions(options: self.options, session: self.session) - - let reply = Document() + var reply = Document() var error = bson_error_t() - guard mongoc_collection_write_command_with_opts( - self.collection._collection, command.data, opts?.data, reply.data, &error) else { + let success = withMutableBSONPointer(to: &reply) { replyPtr in + mongoc_collection_write_command_with_opts( + self.collection._collection, command._bson, opts?._bson, replyPtr, &error) + } + guard success else { throw getErrorFromReply(bsonError: error, from: reply) } + return reply } } diff --git a/Sources/MongoSwift/Operations/FindAndModifyOperation.swift b/Sources/MongoSwift/Operations/FindAndModifyOperation.swift index af4d57278..50dedcca0 100644 --- a/Sources/MongoSwift/Operations/FindAndModifyOperation.swift +++ b/Sources/MongoSwift/Operations/FindAndModifyOperation.swift @@ -40,7 +40,7 @@ internal class FindAndModifyOptions { } if let fields = projection { - guard mongoc_find_and_modify_opts_set_fields(self._options, fields.data) else { + guard mongoc_find_and_modify_opts_set_fields(self._options, fields._bson) else { throw UserError.invalidArgumentError(message: "Error setting fields to \(fields)") } } @@ -62,7 +62,7 @@ internal class FindAndModifyOptions { } if let sort = sort { - guard mongoc_find_and_modify_opts_set_sort(self._options, sort.data) else { + guard mongoc_find_and_modify_opts_set_sort(self._options, sort._bson) else { throw UserError.invalidArgumentError(message: "Error setting sort to \(sort)") } } @@ -90,7 +90,7 @@ internal class FindAndModifyOptions { } } - guard extra.isEmpty || mongoc_find_and_modify_opts_append(self._options, extra.data) else { + guard extra.isEmpty || mongoc_find_and_modify_opts_append(self._options, extra._bson) else { throw UserError.invalidArgumentError(message: "Error appending extra fields \(extra)") } } @@ -98,7 +98,7 @@ internal class FindAndModifyOptions { /// Sets the `update` value on a `mongoc_find_and_modify_opts_t`. We need to have this separate from the /// initializer because its value comes from the API methods rather than their options types. fileprivate func setUpdate(_ update: Document) throws { - guard mongoc_find_and_modify_opts_set_update(self._options, update.data) else { + guard mongoc_find_and_modify_opts_set_update(self._options, update._bson) else { throw UserError.invalidArgumentError(message: "Error setting update to \(update)") } } @@ -110,7 +110,7 @@ internal class FindAndModifyOptions { var doc = Document() try session.append(to: &doc) - guard mongoc_find_and_modify_opts_append(self._options, doc.data) else { + guard mongoc_find_and_modify_opts_append(self._options, doc._bson) else { throw RuntimeError.internalError(message: "Couldn't read session information") } } @@ -143,14 +143,16 @@ internal struct FindAndModifyOperation: Operation { if let session = self.session { try opts.setSession(session) } if let update = self.update { try opts.setUpdate(update) } - let reply = Document() + var reply = Document() var error = bson_error_t() - - guard mongoc_collection_find_and_modify_with_opts(self.collection._collection, - self.filter.data, - opts._options, - reply.data, - &error) else { + let success = withMutableBSONPointer(to: &reply) { replyPtr in + mongoc_collection_find_and_modify_with_opts(self.collection._collection, + self.filter._bson, + opts._options, + replyPtr, + &error) + } + guard success else { throw getErrorFromReply(bsonError: error, from: reply) } diff --git a/Sources/MongoSwift/Operations/RunCommandOperation.swift b/Sources/MongoSwift/Operations/RunCommandOperation.swift index e5151036d..5ddd6428c 100644 --- a/Sources/MongoSwift/Operations/RunCommandOperation.swift +++ b/Sources/MongoSwift/Operations/RunCommandOperation.swift @@ -43,12 +43,15 @@ internal struct RunCommandOperation: Operation { } internal func execute() throws -> Document { - let rp = self.options?.readPreference + let rp = self.options?.readPreference?._readPreference let opts = try encodeOptions(options: self.options, session: self.session) - let reply = Document() + var reply = Document() var error = bson_error_t() - guard mongoc_database_command_with_opts( - self.database._database, self.command.data, rp?._readPreference, opts?.data, reply.data, &error) else { + let success = withMutableBSONPointer(to: &reply) { replyPtr in + mongoc_database_command_with_opts( + self.database._database, self.command._bson, rp, opts?._bson, replyPtr, &error) + } + guard success else { throw getErrorFromReply(bsonError: error, from: reply) } return reply diff --git a/Sources/MongoSwift/ReadPreference.swift b/Sources/MongoSwift/ReadPreference.swift index 5a8c043ef..c71d84bf2 100644 --- a/Sources/MongoSwift/ReadPreference.swift +++ b/Sources/MongoSwift/ReadPreference.swift @@ -119,7 +119,7 @@ public final class ReadPreference { } let tags = try BSONEncoder().encode(Document(tagSets)) - mongoc_read_prefs_set_tags(self._readPreference, tags.data) + mongoc_read_prefs_set_tags(self._readPreference, tags._bson) } if let maxStalenessSeconds = maxStalenessSeconds { diff --git a/Tests/MongoSwiftTests/Document+CollectionTests.swift b/Tests/MongoSwiftTests/Document+CollectionTests.swift index baf14a6ab..cf91ada35 100644 --- a/Tests/MongoSwiftTests/Document+CollectionTests.swift +++ b/Tests/MongoSwiftTests/Document+CollectionTests.swift @@ -20,7 +20,7 @@ final class Document_CollectionTests: MongoSwiftTestCase { expect(doc[1].value).to(bsonEqual(4)) // doc.indices - expect(doc.indices.count).to(equal(doc.storage.count)) + expect(doc.indices.count).to(equal(doc.count)) expect(doc.indices.startIndex).to(equal(doc.startIndex)) expect(doc.indices[1]).to(equal(doc.index(after: doc.startIndex))) expect(doc.indices.endIndex).to(equal(doc.endIndex)) @@ -56,24 +56,20 @@ final class Document_CollectionTests: MongoSwiftTestCase { expect(firstElem.key).to(equal("a")) expect(firstElem.value).to(bsonEqual(3)) expect(doc).to(equal(["b": 2, "c": 5, "d": 4])) - expect(doc).to(haveCorrectCount()) // doc.removeFirst(k:) doc.removeFirst(2) expect(doc).to(equal(["d": 4])) - expect(doc).to(haveCorrectCount()) // doc.popFirst let lastElem = doc.popFirst() expect(lastElem?.key).to(equal("d")) expect(lastElem?.value).to(bsonEqual(4)) expect(doc).to(equal([:])) - expect(doc).to(haveCorrectCount()) // doc.merge let newDoc: Document = ["e": 4, "f": 2] try doc.merge(newDoc) - expect(doc).to(haveCorrectCount()) } func testPrefixSuffix() { @@ -85,33 +81,11 @@ final class Document_CollectionTests: MongoSwiftTestCase { // doc.prefix(upTo:) expect(upToPrefixDoc).to(equal(["a": 3, "b": 2])) - expect(upToPrefixDoc).to(haveCorrectCount()) // doc.prefix(through:) expect(throughPrefixDoc).to(equal(["a": 3, "b": 2])) - expect(throughPrefixDoc).to(haveCorrectCount()) // doc.suffix expect(suffixDoc).to(equal(["b": 2, "c": 5, "d": 4, "e": 3])) - expect(suffixDoc).to(haveCorrectCount()) - } -} - -/// A Nimble matcher for testing that the count of a Document is what it should be. This Nimble matcher is used in only -/// this file for verifying that Document.count (a bookkeeping number in Document) matches the count that is reported by -/// libbson. -private func haveCorrectCount() -> Predicate { - return Predicate.define("have the correct count") { actualExpression, msg in - let actualValue = try actualExpression.evaluate() - switch actualValue { - case nil: - return PredicateResult(status: .fail, message: msg) - case let actual?: - let expectedCount = actual.storage.count - let failMsg = ExpectationMessage.expectedCustomValueTo("equal a count of \(expectedCount)", - "\(actual.count)") - let matches = (actual.count == expectedCount) - return PredicateResult(bool: matches, message: matches ? msg : failMsg) - } } } diff --git a/Tests/MongoSwiftTests/DocumentTests.swift b/Tests/MongoSwiftTests/DocumentTests.swift index 4c268c96b..410b3121c 100644 --- a/Tests/MongoSwiftTests/DocumentTests.swift +++ b/Tests/MongoSwiftTests/DocumentTests.swift @@ -481,38 +481,38 @@ final class DocumentTests: MongoSwiftTestCase { // test replacing `Overwritable` types with values of their own type func testOverwritable() throws { // make a deep copy so we start off with uniquely referenced storage - var doc = Document(copying: DocumentTests.overwritables.data) + var doc = Document(copying: DocumentTests.overwritables._bson) // save a reference to original bson_t so we can verify it doesn't change - let pointer = doc.data + let pointer = doc._bson // overwrite int32 with int32 doc["int32"] = Int32(15) expect(doc["int32"]).to(bsonEqual(Int32(15))) - expect(doc.data).to(equal(pointer)) + expect(doc._bson).to(equal(pointer)) doc["bool"] = true - expect(doc.data).to(equal(pointer)) + expect(doc._bson).to(equal(pointer)) doc["double"] = 3.0 - expect(doc.data).to(equal(pointer)) + expect(doc._bson).to(equal(pointer)) doc["decimal"] = Decimal128("100")! - expect(doc.data).to(equal(pointer)) + expect(doc._bson).to(equal(pointer)) // overwrite int64 with int64 doc["int64"] = Int64.min - expect(doc.data).to(equal(pointer)) + expect(doc._bson).to(equal(pointer)) let newOid = ObjectId() doc["oid"] = newOid - expect(doc.data).to(equal(pointer)) + expect(doc._bson).to(equal(pointer)) doc["timestamp"] = Timestamp(timestamp: 5, inc: 10) - expect(doc.data).to(equal(pointer)) + expect(doc._bson).to(equal(pointer)) doc["datetime"] = Date(msSinceEpoch: 2000) - expect(doc.data).to(equal(pointer)) + expect(doc._bson).to(equal(pointer)) expect(doc).to(equal([ "double": 3.0, @@ -532,7 +532,7 @@ final class DocumentTests: MongoSwiftTestCase { let bigInt = Int(Int32.max) + 1 doc["int64"] = bigInt - expect(doc.data).to(equal(pointer)) + expect(doc._bson).to(equal(pointer)) // final values expect(doc).to(equal([ @@ -549,16 +549,16 @@ final class DocumentTests: MongoSwiftTestCase { // should not be able to overwrite an int32 with an int on a 64-bit system doc["int32"] = 20 expect(doc["int32"]).to(bsonEqual(20)) - expect(doc.data).toNot(equal(pointer)) + expect(doc._bson).toNot(equal(pointer)) } // test replacing some of the non-Overwritable types with values of their own types func testNonOverwritable() throws { // make a deep copy so we start off with uniquely referenced storage - var doc = Document(copying: DocumentTests.nonOverwritables.data) + var doc = Document(copying: DocumentTests.nonOverwritables._bson) // save a reference to original bson_t so we can verify it changes - var pointer = doc.data + var pointer = doc._bson // save these to compare to at the end let newDoc: Document = ["y": 1] @@ -568,8 +568,8 @@ final class DocumentTests: MongoSwiftTestCase { newPairs.forEach { k, v in doc[k] = v // the storage should change every time - expect(doc.data).toNot(equal(pointer)) - pointer = doc.data + expect(doc._bson).toNot(equal(pointer)) + pointer = doc._bson } expect(doc).to(equal(["string": "hi", "nil": BSONNull(), "doc": newDoc, "arr": [3, 4] as [Int]])) @@ -578,10 +578,10 @@ final class DocumentTests: MongoSwiftTestCase { // test replacing both overwritable and nonoverwritable values with values of different types func testReplaceValueWithNewType() throws { // make a deep copy so we start off with uniquely referenced storage - var overwritableDoc = Document(copying: DocumentTests.overwritables.data) + var overwritableDoc = Document(copying: DocumentTests.overwritables._bson) // save a reference to original bson_t so we can verify it changes - var overwritablePointer = overwritableDoc.data + var overwritablePointer = overwritableDoc._bson let newOid = ObjectId() let overwritablePairs: [(String, BSONValue)] = [ @@ -597,8 +597,8 @@ final class DocumentTests: MongoSwiftTestCase { overwritablePairs.forEach { k, v in overwritableDoc[k] = v - expect(overwritableDoc.data).toNot(equal(overwritablePointer)) - overwritablePointer = overwritableDoc.data + expect(overwritableDoc._bson).toNot(equal(overwritablePointer)) + overwritablePointer = overwritableDoc._bson } expect(overwritableDoc).to(equal([ @@ -613,17 +613,17 @@ final class DocumentTests: MongoSwiftTestCase { ])) // make a deep copy so we start off with uniquely referenced storage - var nonOverwritableDoc = Document(copying: DocumentTests.nonOverwritables.data) + var nonOverwritableDoc = Document(copying: DocumentTests.nonOverwritables._bson) // save a reference to original bson_t so we can verify it changes - var nonOverwritablePointer = nonOverwritableDoc.data + var nonOverwritablePointer = nonOverwritableDoc._bson let nonOverwritablePairs: [(String, BSONValue)] = [("string", 1), ("nil", "hello"), ("doc", "hi"), ("arr", 5)] nonOverwritablePairs.forEach { k, v in nonOverwritableDoc[k] = v - expect(nonOverwritableDoc.data).toNot(equal(nonOverwritablePointer)) - nonOverwritablePointer = nonOverwritableDoc.data + expect(nonOverwritableDoc._bson).toNot(equal(nonOverwritablePointer)) + nonOverwritablePointer = nonOverwritableDoc._bson } expect(nonOverwritableDoc).to(equal(["string": 1, "nil": "hello", "doc": "hi", "arr": 5])) @@ -631,24 +631,24 @@ final class DocumentTests: MongoSwiftTestCase { // test setting both overwritable and nonoverwritable values to nil func testReplaceValueWithNil() throws { - var overwritableDoc = Document(copying: DocumentTests.overwritables.data) - var overwritablePointer = overwritableDoc.data + var overwritableDoc = Document(copying: DocumentTests.overwritables._bson) + var overwritablePointer = overwritableDoc._bson ["double", "int32", "int64", "bool", "decimal", "oid", "timestamp", "datetime"].forEach { overwritableDoc[$0] = BSONNull() // the storage should change every time - expect(overwritableDoc.data).toNot(equal(overwritablePointer)) - overwritablePointer = overwritableDoc.data + expect(overwritableDoc._bson).toNot(equal(overwritablePointer)) + overwritablePointer = overwritableDoc._bson } - var nonOverwritableDoc = Document(copying: DocumentTests.nonOverwritables.data) - var nonOverwritablePointer = nonOverwritableDoc.data + var nonOverwritableDoc = Document(copying: DocumentTests.nonOverwritables._bson) + var nonOverwritablePointer = nonOverwritableDoc._bson ["string", "doc", "arr"].forEach { nonOverwritableDoc[$0] = BSONNull() // the storage should change every time - expect(nonOverwritableDoc.data).toNot(equal(nonOverwritablePointer)) - nonOverwritablePointer = nonOverwritableDoc.data + expect(nonOverwritableDoc._bson).toNot(equal(nonOverwritablePointer)) + nonOverwritablePointer = nonOverwritableDoc._bson } expect(nonOverwritableDoc).to( @@ -659,7 +659,7 @@ final class DocumentTests: MongoSwiftTestCase { func testReplaceValueNoop() throws { var noops: Document = ["null": BSONNull(), "maxkey": MaxKey(), "minkey": MinKey()] - var pointer = noops.data + var pointer = noops._bson // replace values with own types. these should all be no-ops let newPairs1: [(String, BSONValue)] = [("null", BSONNull()), ("maxkey", MaxKey()), ("minkey", MinKey())] @@ -667,7 +667,7 @@ final class DocumentTests: MongoSwiftTestCase { newPairs1.forEach { k, v in noops[k] = v // the storage should never change - expect(noops.data).to(equal(pointer)) + expect(noops._bson).to(equal(pointer)) } // we should still have exactly the same document we started with @@ -679,8 +679,8 @@ final class DocumentTests: MongoSwiftTestCase { newPairs2.forEach { k, v in noops[k] = v // the storage should change every time - expect(noops.data).toNot(equal(pointer)) - pointer = noops.data + expect(noops._bson).toNot(equal(pointer)) + pointer = noops._bson } expect(noops).to(equal(["null": 5, "maxkey": "hi", "minkey": false])) From 68d3fbdd1f665e17e229addce61b4d69dde9ebd1 Mon Sep 17 00:00:00 2001 From: Kaitlin Mahar Date: Fri, 17 May 2019 13:27:59 -0400 Subject: [PATCH 2/3] no need to copy error --- Sources/MongoSwift/MongoError.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Sources/MongoSwift/MongoError.swift b/Sources/MongoSwift/MongoError.swift index ee69ddece..d5584d822 100644 --- a/Sources/MongoSwift/MongoError.swift +++ b/Sources/MongoSwift/MongoError.swift @@ -307,8 +307,7 @@ internal func convertingBulkWriteErrors(_ body: () throws -> T) throws -> T { } internal func toErrorString(_ error: bson_error_t) -> String { - var e = error - return withUnsafeBytes(of: &e.message) { rawPtr -> String in + return withUnsafeBytes(of: error.message) { rawPtr -> String in // if baseAddress is nil, the buffer is empty. guard let baseAddress = rawPtr.baseAddress else { return "" From 5b10567437b28e61bab9d79e028ec4a8f538bccd Mon Sep 17 00:00:00 2001 From: Kaitlin Mahar Date: Fri, 17 May 2019 13:36:30 -0400 Subject: [PATCH 3/3] comment/cleanup --- Sources/MongoSwift/BSON/Document.swift | 1 + Sources/MongoSwift/MongoCollection+BulkWrite.swift | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/MongoSwift/BSON/Document.swift b/Sources/MongoSwift/BSON/Document.swift index fd516f2ab..b1dbdaaae 100644 --- a/Sources/MongoSwift/BSON/Document.swift +++ b/Sources/MongoSwift/BSON/Document.swift @@ -516,6 +516,7 @@ extension Document: ExpressibleByDictionaryLiteral { } } +/// Executes the provided closure using a mutable pointer to the document's underlying storage. internal func withMutableBSONPointer(to document: inout Document, body: (MutableBSONPointer) throws -> T) rethrows -> T { document.copyStorageIfRequired() diff --git a/Sources/MongoSwift/MongoCollection+BulkWrite.swift b/Sources/MongoSwift/MongoCollection+BulkWrite.swift index 995ae387c..afc9eeb4f 100644 --- a/Sources/MongoSwift/MongoCollection+BulkWrite.swift +++ b/Sources/MongoSwift/MongoCollection+BulkWrite.swift @@ -109,8 +109,7 @@ extension MongoCollection { var error = bson_error_t() let opts = try bulk.encoder.encode(DeleteModelOptions(collation: self.collation)) - guard mongoc_bulk_operation_remove_many_with_opts( - bulk.bulk, self.filter._bson, opts._bson, &error) else { + guard mongoc_bulk_operation_remove_many_with_opts(bulk.bulk, self.filter._bson, opts._bson, &error) else { throw parseMongocError(error) // should be invalidArgumentError } }