2020#include "mongoc-cursor-private.h"
2121#include "mongoc-collection-private.h"
2222#include "mongoc-client-session-private.h"
23+ #include "mongoc-rpc-private.h"
2324
2425#define CHANGE_STREAM_ERR (_str ) \
2526 bson_set_error (&stream->err, \
@@ -88,7 +89,7 @@ _mongoc_change_stream_make_cursor (mongoc_change_stream_t *stream)
8889 size_t keyLen =
8990 bson_uint32_to_string (key_int , & key_str , buf , sizeof (buf ));
9091 bson_append_value (
91- & pipeline , key_str , keyLen , bson_iter_value (& child_iter ));
92+ & pipeline , key_str , ( int ) keyLen , bson_iter_value (& child_iter ));
9293 ++ key_int ;
9394 }
9495 }
@@ -120,6 +121,31 @@ _mongoc_change_stream_make_cursor (mongoc_change_stream_t *stream)
120121 stream -> coll -> client , & iter , & cs , & stream -> err )) {
121122 goto cleanup ;
122123 }
124+ } else if (stream -> implicit_session ) {
125+ /* If an implicit session was created before, and this cursor is now
126+ * being recreated after resuming, then use the same session as before. */
127+ cs = stream -> implicit_session ;
128+ if (!mongoc_client_session_append (cs , & command_opts , & stream -> err )) {
129+ goto cleanup ;
130+ }
131+ } else {
132+ /* Create an implicit session. This session lsid must be the same for the
133+ * agg command and the subsequent getMores. Thus, this implicit session is
134+ * passed as if it were an explicit session to
135+ * collection_read_command_with_opts and cursor_new_from_reply, but it is
136+ * still implicit and its lifetime is owned by this change_stream_t. */
137+ mongoc_session_opt_t * session_opts ;
138+ session_opts = mongoc_session_opts_new ();
139+ mongoc_session_opts_set_causal_consistency (session_opts , false);
140+ /* returns NULL if sessions aren't supported. ignore errors. */
141+ cs =
142+ mongoc_client_start_session (stream -> coll -> client , session_opts , NULL );
143+ stream -> implicit_session = cs ;
144+ mongoc_session_opts_destroy (session_opts );
145+ if (cs &&
146+ !mongoc_client_session_append (cs , & command_opts , & stream -> err )) {
147+ goto cleanup ;
148+ }
123149 }
124150
125151 server_id = mongoc_server_description_id (sd );
@@ -161,7 +187,8 @@ _mongoc_change_stream_make_cursor (mongoc_change_stream_t *stream)
161187 }
162188
163189 if (stream -> batch_size > 0 ) {
164- mongoc_cursor_set_batch_size (stream -> cursor , stream -> batch_size );
190+ mongoc_cursor_set_batch_size (stream -> cursor ,
191+ (uint32_t ) stream -> batch_size );
165192 }
166193
167194cleanup :
@@ -177,7 +204,7 @@ _mongoc_change_stream_new (const mongoc_collection_t *coll,
177204{
178205 bool full_doc_set = false;
179206 mongoc_change_stream_t * stream =
180- (mongoc_change_stream_t * ) bson_malloc (sizeof (mongoc_change_stream_t ));
207+ (mongoc_change_stream_t * ) bson_malloc0 (sizeof (mongoc_change_stream_t ));
181208
182209 BSON_ASSERT (coll );
183210 BSON_ASSERT (pipeline );
@@ -190,8 +217,6 @@ _mongoc_change_stream_new (const mongoc_collection_t *coll,
190217 bson_init (& stream -> opts );
191218 bson_init (& stream -> resume_token );
192219 bson_init (& stream -> err_doc );
193- memset (& stream -> err , 0 , sizeof (bson_error_t ));
194- stream -> cursor = NULL ;
195220
196221 /*
197222 * The passed options may consist of:
@@ -260,12 +285,13 @@ bool
260285mongoc_change_stream_next (mongoc_change_stream_t * stream , const bson_t * * bson )
261286{
262287 bson_iter_t iter ;
288+ bool ret = false;
263289
264290 BSON_ASSERT (stream );
265291 BSON_ASSERT (bson );
266292
267293 if (stream -> err .code != 0 ) {
268- return false ;
294+ goto end ;
269295 }
270296
271297 if (!mongoc_cursor_next (stream -> cursor , bson )) {
@@ -275,7 +301,7 @@ mongoc_change_stream_next (mongoc_change_stream_t *stream, const bson_t **bson)
275301
276302 if (!mongoc_cursor_error_document (stream -> cursor , & err , & err_doc )) {
277303 /* No error occurred, just no documents left */
278- return false ;
304+ goto end ;
279305 }
280306
281307 /* Change Streams Spec: An error is resumable if it is not a server error,
@@ -311,7 +337,7 @@ mongoc_change_stream_next (mongoc_change_stream_t *stream, const bson_t **bson)
311337 !mongoc_cursor_error_document (stream -> cursor , & err , & err_doc );
312338 if (resumable ) {
313339 /* Empty batch. */
314- return false ;
340+ goto end ;
315341 }
316342 }
317343 }
@@ -320,7 +346,7 @@ mongoc_change_stream_next (mongoc_change_stream_t *stream, const bson_t **bson)
320346 stream -> err = err ;
321347 bson_destroy (& stream -> err_doc );
322348 bson_copy_to (err_doc , & stream -> err_doc );
323- return false ;
349+ goto end ;
324350 }
325351 }
326352
@@ -332,14 +358,25 @@ mongoc_change_stream_next (mongoc_change_stream_t *stream, const bson_t **bson)
332358 MONGOC_ERROR_CHANGE_STREAM_NO_RESUME_TOKEN ,
333359 "Cannot provide resume functionality when the resume "
334360 "token is missing" );
335- return false ;
361+ goto end ;
336362 }
337363
338364 /* Copy the resume token */
339365 bson_reinit (& stream -> resume_token );
340366 BSON_APPEND_VALUE (
341367 & stream -> resume_token , "resumeAfter" , bson_iter_value (& iter ));
342- return true;
368+ ret = true;
369+
370+ end :
371+ /* Driver Sessions Spec: "When an implicit session is associated with a
372+ * cursor for use with getMore operations, the session MUST be returned to
373+ * the pool immediately following a getMore operation that indicates that the
374+ * cursor has been exhausted." */
375+ if (stream -> implicit_session && stream -> cursor -> rpc .reply .cursor_id == 0 ) {
376+ mongoc_client_session_destroy (stream -> implicit_session );
377+ stream -> implicit_session = NULL ;
378+ }
379+ return ret ;
343380}
344381
345382bool
@@ -373,6 +410,9 @@ mongoc_change_stream_destroy (mongoc_change_stream_t *stream)
373410 if (stream -> cursor ) {
374411 mongoc_cursor_destroy (stream -> cursor );
375412 }
413+ if (stream -> implicit_session ) {
414+ mongoc_client_session_destroy (stream -> implicit_session );
415+ }
376416 mongoc_collection_destroy (stream -> coll );
377417 bson_free (stream );
378418}
0 commit comments