@@ -368,21 +368,42 @@ bool phongo_execute_bulkwritecommand(zval* manager, php_phongo_bulkwritecommand_
368368
369369 bwcr = phongo_bulkwritecommandresult_init (return_value , & bw_ret , manager );
370370
371+ /* Error handling for mongoc_bulkwrite_execute differs significantly from
372+ * mongoc_bulk_operation_execute.
373+ *
374+ * - There may or may not be a top-level error. Top-level errors include
375+ * both logical errors (invalid arguments) and runtime errors (e.g. server
376+ * selection failure). A bulk write fails due to write or write concern
377+ * errors will typically not have a top-level error.
378+ *
379+ * - There may or may not be an error reply document. This document could be
380+ * the response of a failed bulkWrite command, but it may also originate
381+ * from libmongoc (e.g. server selection, appending a session, iterating
382+ * BSON). This function only uses it to extrapolate error labels and it is
383+ * otherwise accessible to the user through BulkWriteCommandResult.
384+ *
385+ * - InvalidArgumentException may be thrown directly for a basic top-level
386+ * error (assuming BulkWriteCommandResult would also be irrelevant).
387+ * Otherwise, BulkWriteCommandException is thrown with an attached
388+ * BulkWriteCommandResult that collects any error reply, write errors, and
389+ * write concern errors, along with a possible partial write result.
390+ */
371391 if (bw_ret .exc ) {
372392 success = false;
373393 bson_error_t error = { 0 };
394+ const bson_t * error_reply = mongoc_bulkwriteexception_errorreply (bw_ret .exc );
374395
375- // Check if there is a top-level error
396+ // Consult any top-level error to throw the first exception
376397 if (mongoc_bulkwriteexception_error (bw_ret .exc , & error )) {
377- phongo_throw_exception_from_bson_error_t_and_reply (& error , mongoc_bulkwriteexception_errorreply ( bw_ret . exc ) );
398+ phongo_throw_exception_from_bson_error_t_and_reply (& error , error_reply );
378399 }
379400
380401 /* Unlike mongoc_bulk_operation_execute, mongoc_bulkwrite_execute may
381- * report COMMAND_INVALID_ARG alongside a partial result (CDRIVER-5842).
382- * If there is no result, we can throw InvalidArgumentException without
383- * proxying it behind a BulkWriteException. */
384- if (! bw_ret . res && error . domain == MONGOC_ERROR_COMMAND && error . code == MONGOC_ERROR_COMMAND_INVALID_ARG ) {
385- // TODO: Do we care about other mongoc_bulkwriteexception_t fields?
402+ * report MONGOC_ERROR_COMMAND_INVALID_ARG alongside a partial result
403+ * (CDRIVER-5842). Throw InvalidArgumentException directly iff there is
404+ * neither a partial write result nor an error reply (we can assume
405+ * there are no write or write concern errors for this case). */
406+ if ( EG ( exception ) && EG ( exception ) -> ce == php_phongo_invalidargumentexception_ce && ! bw_ret . res && ! error_reply ) {
386407 goto cleanup ;
387408 }
388409
@@ -393,14 +414,14 @@ bool phongo_execute_bulkwritecommand(zval* manager, php_phongo_bulkwritecommand_
393414 zend_throw_exception (php_phongo_bulkwritecommandexception_ce , message , 0 );
394415 efree (message );
395416 } else {
396- // TODO: Determine appropriate message w/o top-level error
397417 zend_throw_exception (php_phongo_bulkwritecommandexception_ce , "Bulk write failed" , 0 );
398418 }
399419
400- /* Ensure error labels are added to the final BulkWriteCommandException. If a
401- * previous exception was also thrown, error labels will already have
402- * been added by phongo_throw_exception_from_bson_error_t_and_reply. */
403- phongo_exception_add_error_labels (mongoc_bulkwriteexception_errorreply (bw_ret .exc ));
420+ /* Ensure error labels are added to the final BulkWriteCommandException.
421+ * If RuntimeException was previously thrown, labels may also have been
422+ * added to it by phongo_throw_exception_from_bson_error_t_and_reply. */
423+ phongo_exception_add_error_labels (error_reply );
424+
404425 phongo_add_exception_prop (ZEND_STRL ("bulkWriteCommandResult" ), return_value );
405426 }
406427
0 commit comments