@@ -49,6 +49,112 @@ PHONGO_API zend_class_entry *php_phongo_bulkwrite_ce;
4949
5050zend_object_handlers php_phongo_handler_bulkwrite ;
5151
52+ /* Returns whether any top-level field names in the document contain a "$". */
53+ static inline bool php_phongo_bulkwrite_update_has_operators (bson_t * bupdate ) /* {{{ */
54+ {
55+ bson_iter_t iter ;
56+
57+ if (bson_iter_init (& iter , bupdate )) {
58+ while (bson_iter_next (& iter )) {
59+ if (strchr (bson_iter_key (& iter ), '$' )) {
60+ return true;
61+ }
62+ }
63+ }
64+
65+ return false;
66+ } /* }}} */
67+
68+ /* Appends a document field for the given opts document and key. Returns true on
69+ * success; otherwise, false is returned and an exception is thrown. */
70+ static bool php_phongo_bulkwrite_opts_append_document (bson_t * opts , const char * opts_key , zval * zarr , const char * zarr_key TSRMLS_DC )
71+ {
72+ zval * value = php_array_fetch (zarr , zarr_key );
73+ bson_t b = BSON_INITIALIZER ;
74+
75+ if (Z_TYPE_P (value ) != IS_OBJECT && Z_TYPE_P (value ) != IS_ARRAY ) {
76+ phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Expected \"%s\" option to be array or object, %s given" , zarr_key , zend_get_type_by_const (Z_TYPE_P (value )));
77+ return false;
78+ }
79+
80+ phongo_zval_to_bson (value , PHONGO_BSON_NONE , & b , NULL TSRMLS_CC );
81+
82+ if (EG (exception )) {
83+ bson_destroy (& b );
84+ return false;
85+ }
86+
87+ if (!BSON_APPEND_DOCUMENT (opts , opts_key , & b )) {
88+ phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Error appending \"%s\" option" , opts_key );
89+ bson_destroy (& b );
90+ return false;
91+ }
92+
93+ bson_destroy (& b );
94+ return true;
95+ }
96+
97+ #define PHONGO_BULKWRITE_APPEND_BOOL (opt , value ) \
98+ if (!BSON_APPEND_BOOL(boptions, (opt), (value))) { \
99+ phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Error appending \"%s\" option", (opt)); \
100+ return false; \
101+ }
102+
103+ #define PHONGO_BULKWRITE_APPEND_INT32 (opt , value ) \
104+ if (!BSON_APPEND_INT32(boptions, (opt), (value))) { \
105+ phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Error appending \"%s\" option", (opt)); \
106+ return false; \
107+ }
108+
109+ #define PHONGO_BULKWRITE_OPT_DOCUMENT (opt ) \
110+ if (zoptions && php_array_existsc(zoptions, (opt))) { \
111+ if (!php_phongo_bulkwrite_opts_append_document(boptions, (opt), zoptions, (opt) TSRMLS_CC)) { \
112+ return false; \
113+ } \
114+ }
115+
116+ /* Applies options (including defaults) for an update operation. */
117+ static bool php_phongo_bulkwrite_update_apply_options (bson_t * boptions , zval * zoptions TSRMLS_DC )/* {{{ */
118+ {
119+ bool multi = false, upsert = false;
120+
121+ if (zoptions ) {
122+ if (php_array_existsc (zoptions , "multi" )) {
123+ multi = php_array_fetchc_bool (zoptions , "multi" );
124+ }
125+ if (php_array_existsc (zoptions , "upsert" )) {
126+ upsert = php_array_fetchc_bool (zoptions , "upsert" );
127+ }
128+ }
129+
130+ PHONGO_BULKWRITE_APPEND_BOOL ("multi" , multi );
131+ PHONGO_BULKWRITE_APPEND_BOOL ("upsert" , upsert );
132+ PHONGO_BULKWRITE_OPT_DOCUMENT ("collation" );
133+
134+ return true;
135+ } /* }}} */
136+
137+ /* Applies options (including defaults) for an delete operation. */
138+ static bool php_phongo_bulkwrite_delete_apply_options (bson_t * boptions , zval * zoptions TSRMLS_DC )/* {{{ */
139+ {
140+ int32_t limit = 0 ;
141+
142+ if (zoptions ) {
143+ if (php_array_existsc (zoptions , "limit" )) {
144+ limit = php_array_fetchc_bool (zoptions , "limit" ) ? 1 : 0 ;
145+ }
146+ }
147+
148+ PHONGO_BULKWRITE_APPEND_INT32 ("limit" , limit );
149+ PHONGO_BULKWRITE_OPT_DOCUMENT ("collation" );
150+
151+ return true;
152+ } /* }}} */
153+
154+ #undef PHONGO_BULKWRITE_APPEND_BOOL
155+ #undef PHONGO_BULKWRITE_APPEND_INT32
156+ #undef PHONGO_BULKWRITE_OPT_DOCUMENT
157+
52158/* {{{ proto void BulkWrite::__construct([array $options = array()])
53159 Constructs a new BulkWrite */
54160PHP_METHOD (BulkWrite , __construct )
@@ -135,92 +241,110 @@ PHP_METHOD(BulkWrite, insert)
135241 Adds an update operation to the BulkWrite */
136242PHP_METHOD (BulkWrite , update )
137243{
138- php_phongo_bulkwrite_t * intern ;
139- zval * query ;
140- zval * newObj ;
141- zval * updateOptions = NULL ;
142- mongoc_update_flags_t flags = MONGOC_UPDATE_NONE ;
143- bson_t * bquery ;
144- bson_t * bupdate ;
244+ php_phongo_bulkwrite_t * intern ;
245+ zval * zquery , * zupdate , * zoptions = NULL ;
246+ bson_t * bquery , * bupdate , * boptions = NULL ;
247+ bson_error_t error = {0 };
145248 SUPPRESS_UNUSED_WARNING (return_value_ptr ) SUPPRESS_UNUSED_WARNING (return_value ) SUPPRESS_UNUSED_WARNING (return_value_used )
146249
147250
148251 intern = Z_BULKWRITE_OBJ_P (getThis ());
149252
150- if (zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC , "AA|a!" , & query , & newObj , & updateOptions ) == FAILURE ) {
253+ if (zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC , "AA|a!" , & zquery , & zupdate , & zoptions ) == FAILURE ) {
151254 return ;
152255 }
153256
154-
155257 bquery = bson_new ();
156258 bupdate = bson_new ();
259+ boptions = bson_new ();
260+
261+ phongo_zval_to_bson (zquery , PHONGO_BSON_NONE , bquery , NULL TSRMLS_CC );
157262
158- phongo_zval_to_bson (query , PHONGO_BSON_NONE , bquery , NULL TSRMLS_CC );
159- phongo_zval_to_bson (newObj , PHONGO_BSON_NONE , bupdate , NULL TSRMLS_CC );
263+ if (EG (exception )) {
264+ goto cleanup ;
265+ }
266+
267+ phongo_zval_to_bson (zupdate , PHONGO_BSON_NONE , bupdate , NULL TSRMLS_CC );
268+
269+ if (EG (exception )) {
270+ goto cleanup ;
271+ }
160272
161- if (updateOptions ) {
162- flags |= php_array_fetchc_bool (updateOptions , "multi" ) ? MONGOC_UPDATE_MULTI_UPDATE : 0 ;
163- flags |= php_array_fetchc_bool (updateOptions , "upsert" ) ? MONGOC_UPDATE_UPSERT : 0 ;
273+ if (!php_phongo_bulkwrite_update_apply_options (boptions , zoptions TSRMLS_CC )) {
274+ goto cleanup ;
164275 }
165276
166- if (flags & MONGOC_UPDATE_MULTI_UPDATE ) {
167- mongoc_bulk_operation_update (intern -> bulk , bquery , bupdate , !!(flags & MONGOC_UPDATE_UPSERT ));
277+ if (php_phongo_bulkwrite_update_has_operators (bupdate )) {
278+ if (!mongoc_bulk_operation_update_with_opts (intern -> bulk , bquery , bupdate , boptions , & error )) {
279+ phongo_throw_exception_from_bson_error_t (& error TSRMLS_CC );
280+ goto cleanup ;
281+ }
168282 } else {
169- bson_iter_t iter ;
170- zend_bool replaced = 0 ;
171-
172- if (bson_iter_init (& iter , bupdate )) {
173- while (bson_iter_next (& iter )) {
174- if (!strchr (bson_iter_key (& iter ), '$' )) {
175- mongoc_bulk_operation_replace_one (intern -> bulk , bquery , bupdate , !!(flags & MONGOC_UPDATE_UPSERT ));
176- replaced = 1 ;
177- break ;
178- }
179- }
283+ if (!bson_validate (bupdate , BSON_VALIDATE_DOT_KEYS |BSON_VALIDATE_DOLLAR_KEYS , NULL )) {
284+ phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Replacement document may not contain \"$\" or \".\" in keys" );
285+ goto cleanup ;
286+ }
287+
288+ if (zoptions && php_array_existsc (zoptions , "multi" ) && php_array_fetchc_bool (zoptions , "multi" )) {
289+ phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Replacement document conflicts with true \"multi\" option" );
290+ goto cleanup ;
180291 }
181292
182- if (!replaced ) {
183- mongoc_bulk_operation_update_one (intern -> bulk , bquery , bupdate , !!(flags & MONGOC_UPDATE_UPSERT ));
293+ if (!mongoc_bulk_operation_replace_one_with_opts (intern -> bulk , bquery , bupdate , boptions , & error )) {
294+ phongo_throw_exception_from_bson_error_t (& error TSRMLS_CC );
295+ goto cleanup ;
184296 }
185297 }
186298
187299 intern -> num_ops ++ ;
188300
301+ cleanup :
189302 bson_clear (& bquery );
190303 bson_clear (& bupdate );
304+ bson_clear (& boptions );
191305}
192306/* }}} */
193307
194308/* {{{ proto void BulkWrite::delete(array|object $query[, array $deleteOptions = array()])
195309 Adds a delete operation to the BulkWrite */
196310PHP_METHOD (BulkWrite , delete )
197311{
198- php_phongo_bulkwrite_t * intern ;
199- zval * query ;
200- zval * deleteOptions = NULL ;
201- bson_t * bson ;
312+ php_phongo_bulkwrite_t * intern ;
313+ zval * zquery , * zoptions = NULL ;
314+ bson_t * bquery , * boptions = NULL ;
315+ bson_error_t error = { 0 } ;
202316 SUPPRESS_UNUSED_WARNING (return_value_ptr ) SUPPRESS_UNUSED_WARNING (return_value ) SUPPRESS_UNUSED_WARNING (return_value_used )
203317
204318
205319 intern = Z_BULKWRITE_OBJ_P (getThis ());
206320
207- if (zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC , "A|a!" , & query , & deleteOptions ) == FAILURE ) {
321+ if (zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC , "A|a!" , & zquery , & zoptions ) == FAILURE ) {
208322 return ;
209323 }
210324
325+ bquery = bson_new ();
326+ boptions = bson_new ();
327+
328+ phongo_zval_to_bson (zquery , PHONGO_BSON_NONE , bquery , NULL TSRMLS_CC );
211329
212- bson = bson_new ();
213- phongo_zval_to_bson (query , PHONGO_BSON_NONE , bson , NULL TSRMLS_CC );
330+ if (EG (exception )) {
331+ goto cleanup ;
332+ }
214333
215- if (deleteOptions && php_array_fetchc_bool (deleteOptions , "limit" )) {
216- mongoc_bulk_operation_remove_one (intern -> bulk , bson );
217- } else {
218- mongoc_bulk_operation_remove (intern -> bulk , bson );
334+ if (!php_phongo_bulkwrite_delete_apply_options (boptions , zoptions TSRMLS_CC )) {
335+ goto cleanup ;
336+ }
337+
338+ if (!mongoc_bulk_operation_remove_with_opts (intern -> bulk , bquery , boptions , & error )) {
339+ phongo_throw_exception_from_bson_error_t (& error TSRMLS_CC );
340+ goto cleanup ;
219341 }
220342
221343 intern -> num_ops ++ ;
222344
223- bson_clear (& bson );
345+ cleanup :
346+ bson_clear (& bquery );
347+ bson_clear (& boptions );
224348}
225349/* }}} */
226350
0 commit comments