@@ -106,23 +106,61 @@ internal struct ListCollectionsOperation: Operation {
106106 }
107107
108108 internal func execute( using connection: Connection , session: ClientSession ? ) throws -> ListCollectionsResults {
109- var opts = try encodeOptions ( options: self . options, session: session) ?? BSONDocument ( )
110- opts [ " nameOnly " ] = . bool( self . nameOnly)
109+ // Drivers MUST run listCollections on the primary node when in a replica set topology, unless directly
110+ // connected to a secondary node in Single topology.
111+ let readPref = ReadPreference . primary
112+
113+ var cmd : BSONDocument = [ " listCollections " : 1 , " nameOnly " : . bool( self . nameOnly) ]
111114 if let filterDoc = self . filter {
112- opts [ " filter " ] = . document( filterDoc)
115+ cmd [ " filter " ] = . document( filterDoc)
113116
114117 // If `listCollectionNames` is called with a non-name filter key, change server-side nameOnly flag to false.
118+ // per spec: drivers MUST NOT set nameOnly if a filter specifies any keys other than name.
115119 if self . nameOnly && filterDoc. keys. contains ( where: { $0 != " name " } ) {
116- opts [ " nameOnly " ] = false
120+ cmd [ " nameOnly " ] = false
121+ }
122+ }
123+
124+ var cursorOpts : BSONDocument = [ : ]
125+
126+ if let batchSize = options? . batchSize {
127+ guard let i32 = Int32 ( exactly: batchSize) else {
128+ throw MongoError . InvalidArgumentError (
129+ message: " batchSize option must be representable as an Int32. Got: \( batchSize) "
130+ )
131+ }
132+ cursorOpts = [ " batchSize " : . int32( i32) ]
133+ }
134+
135+ let commandOpts = try encodeOptions ( options: nil as BSONDocument ? , session: session) ?? BSONDocument ( )
136+ cursorOpts = try encodeOptions ( options: cursorOpts, session: session) ?? BSONDocument ( )
137+ cmd [ " cursor " ] = . document( cursorOpts)
138+
139+ // We don't need to clean up this reply ourselves, as `mongoc_cursor_new_from_command_reply_with_opts` will
140+ // consume it.
141+ var reply = try self . database. withMongocDatabase ( from: connection) { dbPtr in
142+ try readPref. withMongocReadPreference { rpPtr in
143+ try runMongocCommandWithCReply (
144+ command: cmd,
145+ options: commandOpts
146+ ) { cmdPtr, optsPtr, replyPtr, error in
147+ mongoc_database_read_command_with_opts ( dbPtr, cmdPtr, rpPtr, optsPtr, replyPtr, & error)
148+ }
117149 }
118150 }
119151
120- let collections : OpaquePointer = self . database. withMongocDatabase ( from: connection) { dbPtr in
121- opts. withBSONPointer { optsPtr in
122- guard let collections = mongoc_database_find_collections_with_opts ( dbPtr, optsPtr) else {
123- fatalError ( failedToRetrieveCursorMessage)
152+ let collections = connection. withMongocConnection { connPtr in
153+ withUnsafeMutablePointer ( to: & reply) { replyPtr -> OpaquePointer in
154+ withOptionalBSONPointer ( to: cursorOpts) { cursorOptsPtr in
155+ guard let result = mongoc_cursor_new_from_command_reply_with_opts (
156+ connPtr,
157+ replyPtr,
158+ cursorOptsPtr
159+ ) else {
160+ fatalError ( failedToRetrieveCursorMessage)
161+ }
162+ return result
124163 }
125- return collections
126164 }
127165 }
128166
0 commit comments