20
20
#include "mongoc-cursor-private.h"
21
21
#include "mongoc-collection-private.h"
22
22
#include "mongoc-client-session-private.h"
23
- #include "mongoc-rpc-private.h"
24
23
25
24
#define CHANGE_STREAM_ERR (_str ) \
26
25
bson_set_error (&stream->err, \
36
35
} \
37
36
} while (0);
38
37
38
+ /* Construct the aggregate command in cmd:
39
+ * { aggregate: collname, pipeline: [], cursor: { batchSize: x } } */
39
40
static void
40
- _mongoc_change_stream_make_cursor (mongoc_change_stream_t * stream )
41
+ _make_command (mongoc_change_stream_t * stream , bson_t * command )
41
42
{
42
- mongoc_client_session_t * cs = NULL ;
43
+ bson_iter_t iter ;
43
44
bson_t change_stream_stage ; /* { $changeStream: <change_stream_doc> } */
44
45
bson_t change_stream_doc ;
45
46
bson_t pipeline ;
46
47
bson_t cursor_doc ;
47
- bson_t command_opts ;
48
- bson_t command ; /* { aggregate: "coll", pipeline: [], ... } */
49
- bson_t reply ;
50
- bson_iter_t iter ;
51
- bson_error_t err = {0 };
52
- mongoc_server_description_t * sd ;
53
- uint32_t server_id ;
54
-
55
- BSON_ASSERT (stream );
56
48
57
- /* Construct the aggregate command */
58
- /* { aggregate: collname, pipeline: [], cursor: { batchSize: x } } */
59
- bson_init (& command );
60
- bson_append_utf8 (& command ,
49
+ bson_init (command );
50
+ bson_append_utf8 (command ,
61
51
"aggregate" ,
62
52
9 ,
63
53
stream -> coll -> collection ,
64
54
stream -> coll -> collectionlen );
65
- bson_append_array_begin (& command , "pipeline" , 8 , & pipeline );
55
+ bson_append_array_begin (command , "pipeline" , 8 , & pipeline );
66
56
67
57
/* Append the $changeStream stage */
68
58
bson_append_document_begin (& pipeline , "0" , 1 , & change_stream_stage );
@@ -85,36 +75,54 @@ _mongoc_change_stream_make_cursor (mongoc_change_stream_t *stream)
85
75
86
76
bson_iter_recurse (& iter , & child_iter );
87
77
while (bson_iter_next (& child_iter )) {
88
- if ( BSON_ITER_HOLDS_DOCUMENT ( & child_iter )) {
89
- size_t keyLen =
90
- bson_uint32_to_string ( key_int , & key_str , buf , sizeof ( buf ));
91
- bson_append_value (
92
- & pipeline , key_str , ( int ) keyLen , bson_iter_value ( & child_iter ));
93
- ++ key_int ;
94
- }
78
+ /* The user pipeline may consist of invalid stages or non-documents.
79
+ * Append anyway, and rely on the server error. */
80
+ size_t keyLen =
81
+ bson_uint32_to_string ( key_int , & key_str , buf , sizeof ( buf ));
82
+ bson_append_value (
83
+ & pipeline , key_str , ( int ) keyLen , bson_iter_value ( & child_iter )) ;
84
+ ++ key_int ;
95
85
}
96
86
}
97
87
98
- bson_append_array_end (& command , & pipeline );
88
+ bson_append_array_end (command , & pipeline );
99
89
100
90
/* Add batch size if needed */
101
- bson_append_document_begin (& command , "cursor" , 6 , & cursor_doc );
91
+ bson_append_document_begin (command , "cursor" , 6 , & cursor_doc );
102
92
if (stream -> batch_size > 0 ) {
103
93
bson_append_int32 (& cursor_doc , "batchSize" , 9 , stream -> batch_size );
104
94
}
105
- bson_append_document_end (& command , & cursor_doc );
95
+ bson_append_document_end (command , & cursor_doc );
96
+ }
106
97
107
- bson_copy_to (& stream -> opts , & command_opts );
98
+ /* Construct and send the aggregate command and create the resulting cursor.
99
+ * Returns false on error, and sets stream->err. On error, stream->cursor
100
+ * remains NULL, otherwise it is created and must be destroyed. */
101
+ static bool
102
+ _make_cursor (mongoc_change_stream_t * stream )
103
+ {
104
+ mongoc_client_session_t * cs = NULL ;
105
+ bson_t command_opts ;
106
+ bson_t command ; /* { aggregate: "coll", pipeline: [], ... } */
107
+ bson_t reply ;
108
+ bson_iter_t iter ;
109
+ mongoc_server_description_t * sd ;
110
+ uint32_t server_id ;
108
111
112
+ BSON_ASSERT (stream );
113
+ BSON_ASSERT (!stream -> cursor );
114
+ _make_command (stream , & command );
115
+ bson_copy_to (& stream -> opts , & command_opts );
109
116
sd = mongoc_client_select_server (stream -> coll -> client ,
110
117
false /* for_writes */ ,
111
118
stream -> coll -> read_prefs ,
112
- & err );
113
-
119
+ & stream -> err );
114
120
if (!sd ) {
115
- stream -> err = err ;
116
121
goto cleanup ;
117
122
}
123
+ server_id = mongoc_server_description_id (sd );
124
+ bson_append_int32 (& command_opts , "serverId" , 8 , server_id );
125
+ mongoc_server_description_destroy (sd );
118
126
119
127
if (bson_iter_init_find (& iter , & command_opts , "sessionId" )) {
120
128
if (!_mongoc_client_session_from_iter (
@@ -148,16 +156,12 @@ _mongoc_change_stream_make_cursor (mongoc_change_stream_t *stream)
148
156
}
149
157
}
150
158
151
- server_id = mongoc_server_description_id (sd );
152
- bson_append_int32 (& command_opts , "serverId" , 8 , server_id );
153
-
154
159
/* use inherited read preference and read concern of the collection */
155
160
if (!mongoc_collection_read_command_with_opts (
156
- stream -> coll , & command , NULL , & command_opts , & reply , & err )) {
161
+ stream -> coll , & command , NULL , & command_opts , & reply , & stream -> err )) {
157
162
bson_destroy (& stream -> err_doc );
158
163
bson_copy_to (& reply , & stream -> err_doc );
159
164
bson_destroy (& reply );
160
- stream -> err = err ;
161
165
goto cleanup ;
162
166
}
163
167
@@ -194,7 +198,7 @@ _mongoc_change_stream_make_cursor (mongoc_change_stream_t *stream)
194
198
cleanup :
195
199
bson_destroy (& command );
196
200
bson_destroy (& command_opts );
197
- mongoc_server_description_destroy ( sd ) ;
201
+ return stream -> err . code == 0 ;
198
202
}
199
203
200
204
mongoc_change_stream_t *
@@ -275,7 +279,7 @@ _mongoc_change_stream_new (const mongoc_collection_t *coll,
275
279
}
276
280
277
281
if (stream -> err .code == 0 ) {
278
- _mongoc_change_stream_make_cursor (stream );
282
+ ( void ) _make_cursor (stream );
279
283
}
280
284
281
285
return stream ;
@@ -294,6 +298,7 @@ mongoc_change_stream_next (mongoc_change_stream_t *stream, const bson_t **bson)
294
298
goto end ;
295
299
}
296
300
301
+ BSON_ASSERT (stream -> cursor );
297
302
if (!mongoc_cursor_next (stream -> cursor , bson )) {
298
303
const bson_t * err_doc ;
299
304
bson_error_t err ;
@@ -330,8 +335,12 @@ mongoc_change_stream_next (mongoc_change_stream_t *stream, const bson_t **bson)
330
335
}
331
336
332
337
if (resumable ) {
338
+ /* recreate the cursor */
333
339
mongoc_cursor_destroy (stream -> cursor );
334
- _mongoc_change_stream_make_cursor (stream );
340
+ stream -> cursor = NULL ;
341
+ if (!_make_cursor (stream )) {
342
+ goto end ;
343
+ }
335
344
if (!mongoc_cursor_next (stream -> cursor , bson )) {
336
345
resumable =
337
346
!mongoc_cursor_error_document (stream -> cursor , & err , & err_doc );
@@ -372,9 +381,12 @@ mongoc_change_stream_next (mongoc_change_stream_t *stream, const bson_t **bson)
372
381
* cursor for use with getMore operations, the session MUST be returned to
373
382
* the pool immediately following a getMore operation that indicates that the
374
383
* 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 ;
384
+ if (stream -> implicit_session ) {
385
+ /* If creating the change stream cursor errored, it may be null. */
386
+ if (!stream -> cursor || stream -> cursor -> rpc .reply .cursor_id == 0 ) {
387
+ mongoc_client_session_destroy (stream -> implicit_session );
388
+ stream -> implicit_session = NULL ;
389
+ }
378
390
}
379
391
return ret ;
380
392
}
0 commit comments