From 54fdee45c9561f31794fee5eb3df459728ec415f Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 10 Dec 2024 16:23:00 -0500 Subject: [PATCH 01/13] Bump libmongoc to 1.30-dev for bulkwrite APIs --- sbom.json | 14 +++++++------- src/LIBMONGOC_VERSION_CURRENT | 2 +- src/libmongoc | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sbom.json b/sbom.json index 7ed391e81..3e3bb9851 100644 --- a/sbom.json +++ b/sbom.json @@ -19,22 +19,22 @@ "version": "1.12.0" }, { - "bom-ref": "pkg:github/mongodb/mongo-c-driver@1.29.2", + "bom-ref": "pkg:github/mongodb/mongo-c-driver@1.30.0-20241210%2Bgit9ed337b634", "externalReferences": [ { "type": "distribution", - "url": "https://github.com/mongodb/mongo-c-driver/archive/refs/tags/1.29.2.tar.gz" + "url": "https://github.com/mongodb/mongo-c-driver/archive/refs/tags/1.30.0-20241210+git9ed337b634.tar.gz" }, { "type": "website", - "url": "https://github.com/mongodb/mongo-c-driver/tree/1.29.2" + "url": "https://github.com/mongodb/mongo-c-driver/tree/1.30.0-20241210+git9ed337b634" } ], "group": "mongodb", "name": "mongo-c-driver", - "purl": "pkg:github/mongodb/mongo-c-driver@1.29.2", + "purl": "pkg:github/mongodb/mongo-c-driver@1.30.0-20241210%2Bgit9ed337b634", "type": "library", - "version": "1.29.2" + "version": "1.30.0-20241210+git9ed337b634" } ], "dependencies": [ @@ -42,11 +42,11 @@ "ref": "pkg:github/mongodb/libmongocrypt@1.12.0" }, { - "ref": "pkg:github/mongodb/mongo-c-driver@1.29.2" + "ref": "pkg:github/mongodb/mongo-c-driver@1.30.0-20241210%2Bgit9ed337b634" } ], "metadata": { - "timestamp": "2025-01-14T14:24:24.250261+00:00", + "timestamp": "2024-12-10T21:23:50.248055+00:00", "tools": [ { "externalReferences": [ diff --git a/src/LIBMONGOC_VERSION_CURRENT b/src/LIBMONGOC_VERSION_CURRENT index 412114252..0d716c6d3 100644 --- a/src/LIBMONGOC_VERSION_CURRENT +++ b/src/LIBMONGOC_VERSION_CURRENT @@ -1 +1 @@ -1.29.2 +1.30.0-20241210+git9ed337b634 diff --git a/src/libmongoc b/src/libmongoc index 48011c65d..9ed337b63 160000 --- a/src/libmongoc +++ b/src/libmongoc @@ -1 +1 @@ -Subproject commit 48011c65d759bc288a02a583c378a224c1141bbb +Subproject commit 9ed337b634ef18089bfa02390b6c662417b87382 From 1858d0a35d257ffb4aedae15fe6607d36c8a9c31 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 10 Dec 2024 14:58:11 -0500 Subject: [PATCH 02/13] PHPC-2495, PHPC-2490, PHPC-2491, PHPC-2492: BulkWriteCommand ctor and ops --- config.m4 | 1 + config.w32 | 2 +- php_phongo.c | 1 + src/MongoDB/BulkWriteCommand.c | 836 ++++++++++++++++++ src/MongoDB/BulkWriteCommand.stub.php | 30 + src/MongoDB/BulkWriteCommand_arginfo.h | 79 ++ src/phongo_classes.h | 8 + src/phongo_structs.h | 13 + .../bulkwritecommand-insertone-001.phpt | 25 + 9 files changed, 994 insertions(+), 1 deletion(-) create mode 100644 src/MongoDB/BulkWriteCommand.c create mode 100644 src/MongoDB/BulkWriteCommand.stub.php create mode 100644 src/MongoDB/BulkWriteCommand_arginfo.h create mode 100644 tests/bulkwritecommand/bulkwritecommand-insertone-001.phpt diff --git a/config.m4 b/config.m4 index 28998f5e8..091d5f9cd 100644 --- a/config.m4 +++ b/config.m4 @@ -168,6 +168,7 @@ if test "$PHP_MONGODB" != "no"; then src/BSON/UTCDateTimeInterface.c \ src/BSON/functions.c \ src/MongoDB/BulkWrite.c \ + src/MongoDB/BulkWriteCommand.c \ src/MongoDB/ClientEncryption.c \ src/MongoDB/Command.c \ src/MongoDB/Cursor.c \ diff --git a/config.w32 b/config.w32 index dbded6599..f5db82bc0 100644 --- a/config.w32 +++ b/config.w32 @@ -116,7 +116,7 @@ if (PHP_MONGODB != "no") { EXTENSION("mongodb", "php_phongo.c", null, PHP_MONGODB_CFLAGS); MONGODB_ADD_SOURCES("/src", "phongo_apm.c phongo_atomic.c phongo_bson.c phongo_bson_encode.c phongo_client.c phongo_compat.c phongo_error.c phongo_execute.c phongo_ini.c phongo_log.c phongo_util.c"); MONGODB_ADD_SOURCES("/src/BSON", "Binary.c BinaryInterface.c Document.c Iterator.c DBPointer.c Decimal128.c Decimal128Interface.c Int64.c Javascript.c JavascriptInterface.c MaxKey.c MaxKeyInterface.c MinKey.c MinKeyInterface.c ObjectId.c ObjectIdInterface.c PackedArray.c Persistable.c Regex.c RegexInterface.c Serializable.c Symbol.c Timestamp.c TimestampInterface.c Type.c Undefined.c Unserializable.c UTCDateTime.c UTCDateTimeInterface.c functions.c"); - MONGODB_ADD_SOURCES("/src/MongoDB", "BulkWrite.c ClientEncryption.c Command.c Cursor.c CursorId.c CursorInterface.c Manager.c Query.c ReadConcern.c ReadPreference.c Server.c ServerApi.c ServerDescription.c Session.c TopologyDescription.c WriteConcern.c WriteConcernError.c WriteError.c WriteResult.c"); + MONGODB_ADD_SOURCES("/src/MongoDB", "BulkWrite.c BulkWriteCommand.c ClientEncryption.c Command.c Cursor.c CursorId.c CursorInterface.c Manager.c Query.c ReadConcern.c ReadPreference.c Server.c ServerApi.c ServerDescription.c Session.c TopologyDescription.c WriteConcern.c WriteConcernError.c WriteError.c WriteResult.c"); MONGODB_ADD_SOURCES("/src/MongoDB/Exception", "AuthenticationException.c BulkWriteException.c CommandException.c ConnectionException.c ConnectionTimeoutException.c EncryptionException.c Exception.c ExecutionTimeoutException.c InvalidArgumentException.c LogicException.c RuntimeException.c ServerException.c SSLConnectionException.c UnexpectedValueException.c WriteException.c"); MONGODB_ADD_SOURCES("/src/MongoDB/Monitoring", "CommandFailedEvent.c CommandStartedEvent.c CommandSubscriber.c CommandSucceededEvent.c LogSubscriber.c SDAMSubscriber.c Subscriber.c ServerChangedEvent.c ServerClosedEvent.c ServerHeartbeatFailedEvent.c ServerHeartbeatStartedEvent.c ServerHeartbeatSucceededEvent.c ServerOpeningEvent.c TopologyChangedEvent.c TopologyClosedEvent.c TopologyOpeningEvent.c functions.c"); MONGODB_ADD_SOURCES("/src/libmongoc/src/common/src", PHP_MONGODB_COMMON_SOURCES); diff --git a/php_phongo.c b/php_phongo.c index ad93ff75e..9e008d05e 100644 --- a/php_phongo.c +++ b/php_phongo.c @@ -252,6 +252,7 @@ PHP_MINIT_FUNCTION(mongodb) /* {{{ */ php_phongo_cursor_interface_init_ce(INIT_FUNC_ARGS_PASSTHRU); php_phongo_bulkwrite_init_ce(INIT_FUNC_ARGS_PASSTHRU); + php_phongo_bulkwritecommand_init_ce(INIT_FUNC_ARGS_PASSTHRU); php_phongo_clientencryption_init_ce(INIT_FUNC_ARGS_PASSTHRU); php_phongo_command_init_ce(INIT_FUNC_ARGS_PASSTHRU); php_phongo_cursor_init_ce(INIT_FUNC_ARGS_PASSTHRU); diff --git a/src/MongoDB/BulkWriteCommand.c b/src/MongoDB/BulkWriteCommand.c new file mode 100644 index 000000000..653fb29b3 --- /dev/null +++ b/src/MongoDB/BulkWriteCommand.c @@ -0,0 +1,836 @@ +/* + * Copyright 2024-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "bson/bson.h" +#include "mongoc/mongoc.h" + +#include +#include + +#include "php_array_api.h" + +#include "php_phongo.h" +#include "phongo_bson_encode.h" +#include "phongo_error.h" +#include "phongo_util.h" +#include "BulkWriteCommand_arginfo.h" + +#include "MongoDB/WriteConcern.h" + +#define PHONGO_BULKWRITECOMMAND_BYPASS_UNSET -1 + +zend_class_entry* php_phongo_bulkwritecommand_ce; + +// TODO: Make this a common utility function to share with BulkWrite.c +/* Extracts the "_id" field of a BSON document into a return value. */ +static void phongo_bwc_extract_id(bson_t* doc, zval** return_value) +{ + zval* id = NULL; + php_phongo_bson_state state; + + PHONGO_BSON_INIT_STATE(state); + state.map.root.type = PHONGO_TYPEMAP_NATIVE_ARRAY; + + /* TODO: Instead of converting the entire document, iterate BSON to obtain + * the bson_value_t of the _id field and then use phongo_bson_value_to_zval + * or phongo_bson_value_to_zval_legacy to populate the return value. */ + if (!php_phongo_bson_to_zval_ex(doc, &state)) { + goto cleanup; + } + + id = php_array_fetchc(&state.zchild, "_id"); + + if (id) { + ZVAL_ZVAL(*return_value, id, 1, 0); + } + +cleanup: + zval_ptr_dtor(&state.zchild); +} + +// TODO: Make this a common utility function to share with BulkWrite.c +/* Returns whether the BSON array's keys are a sequence of integer strings + * starting with "0". */ +static inline bool phongo_bwc_bson_array_has_valid_keys(bson_t* array) +{ + bson_iter_t iter; + + if (bson_empty(array)) { + return true; + } + + if (bson_iter_init(&iter, array)) { + char key[12]; + int count = 0; + + while (bson_iter_next(&iter)) { + bson_snprintf(key, sizeof(key), "%d", count); + + if (0 != strcmp(key, bson_iter_key(&iter))) { + return false; + } + + count++; + } + } + + return true; +} + +/* Constructs a new BulkWriteCommand */ +static PHP_METHOD(MongoDB_Driver_BulkWriteCommand, __construct) +{ + php_phongo_bulkwritecommand_t* intern; + zval* zoptions = NULL; + + intern = Z_BULKWRITECOMMAND_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_OR_NULL(zoptions) + PHONGO_PARSE_PARAMETERS_END(); + + // TODO: Consider removing initialization for zero values + intern->bw = mongoc_bulkwrite_new(); + intern->bypass = PHONGO_BULKWRITECOMMAND_BYPASS_UNSET; + intern->comment = NULL; + intern->let = NULL; + intern->num_ops = 0; + intern->ordered = true; + intern->verbose = false; + intern->write_concern = NULL; + + if (!zoptions) { + return; + } + + if (php_array_existsc(zoptions, "bypassDocumentValidation")) { + intern->bypass = php_array_fetchc_bool(zoptions, "bypassDocumentValidation") ? 1 : 0; + } + + if (php_array_existsc(zoptions, "comment")) { + zval* value = php_array_fetchc_deref(zoptions, "comment"); + + intern->comment = ecalloc(1, sizeof(bson_value_t)); + phongo_zval_to_bson_value(value, intern->comment); + + if (EG(exception)) { + return; + } + } + + if (php_array_existsc(zoptions, "let")) { + zval* value = php_array_fetchc_deref(zoptions, "let"); + + if (Z_TYPE_P(value) != IS_OBJECT && Z_TYPE_P(value) != IS_ARRAY) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected \"let\" option to be array or object, %s given", zend_get_type_by_const(Z_TYPE_P(value))); + return; + } + + intern->let = bson_new(); + php_phongo_zval_to_bson(value, PHONGO_BSON_NONE, intern->let, NULL); + + if (EG(exception)) { + return; + } + } + + if (php_array_existsc(zoptions, "ordered")) { + intern->ordered = php_array_fetchc_bool(zoptions, "ordered"); + } + + if (php_array_existsc(zoptions, "verboseResults")) { + intern->verbose = php_array_fetchc_bool(zoptions, "verboseResults"); + } + + if (php_array_existsc(zoptions, "writeConcern")) { + zval* value = php_array_fetchc_deref(zoptions, "writeConcern"); + + if (Z_TYPE_P(value) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(value), php_phongo_writeconcern_ce)) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected \"writeConcern\" option to be %s, %s given", ZSTR_VAL(php_phongo_writeconcern_ce->name), zend_zval_type_name(value)); + return; + } + + intern->write_concern = mongoc_write_concern_copy(phongo_write_concern_from_zval(value)); + } +} + +static PHP_METHOD(MongoDB_Driver_BulkWriteCommand, count) +{ + php_phongo_bulkwritecommand_t* intern; + + intern = Z_BULKWRITECOMMAND_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_NONE(); + + RETURN_LONG(intern->num_ops); +} + +static bool phongo_bwc_parse_hint(zval* zhint, bson_value_t* bhint) +{ + if (Z_TYPE_P(zhint) != IS_STRING && Z_TYPE_P(zhint) != IS_OBJECT && Z_TYPE_P(zhint) != IS_ARRAY) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected \"hint\" option to be string, array, or object, %s given", zend_get_type_by_const(Z_TYPE_P(zhint))); + return false; + } + + phongo_zval_to_bson_value(zhint, bhint); + + if (EG(exception)) { + return false; + } + + // Catch the edge case where the option yields a BSON array + if (bhint->value_type != BSON_TYPE_UTF8 && bhint->value_type != BSON_TYPE_DOCUMENT) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected \"hint\" option to yield string or document but got \"%s\"", php_phongo_bson_type_to_string(bhint->value_type)); + return false; + } + + return true; +} + +static bool phongo_bwc_parse_document(zval* zdocument, bson_t* bdocument, const char* key) +{ + if (Z_TYPE_P(zdocument) != IS_OBJECT && Z_TYPE_P(zdocument) != IS_ARRAY) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected \"%s\" option to be array or object, %s given", key, zend_get_type_by_const(Z_TYPE_P(zdocument))); + return false; + } + + php_phongo_zval_to_bson(zdocument, PHONGO_BSON_NONE, bdocument, NULL); + + if (EG(exception)) { + return false; + } + + return true; +} + +static bool phongo_bwc_parse_array(zval* zarray, bson_t* barray, const char* key) +{ + if (Z_TYPE_P(zarray) != IS_OBJECT && Z_TYPE_P(zarray) != IS_ARRAY) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected \"%s\" option to be array or object, %s given", key, zend_get_type_by_const(Z_TYPE_P(zarray))); + return false; + } + + // Explicitly allow MongoDB\BSON\PackedArray for array values + php_phongo_zval_to_bson(zarray, PHONGO_BSON_ALLOW_ROOT_ARRAY, barray, NULL); + + if (EG(exception)) { + return false; + } + + if (!phongo_bwc_bson_array_has_valid_keys(barray)) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected \"%s\" option to yield array but got non-sequential keys", key); + return false; + } + + return true; +} + +static PHP_METHOD(MongoDB_Driver_BulkWriteCommand, deleteMany) +{ + php_phongo_bulkwritecommand_t* intern; + char* ns; + size_t ns_len; + zval* zfilter; + zval* zoptions = NULL; + bson_t bfilter = BSON_INITIALIZER; + mongoc_bulkwrite_deletemanyopts_t* opts = NULL; + bson_error_t error = { 0 }; + + intern = Z_BULKWRITECOMMAND_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_STRING(ns, ns_len) + Z_PARAM_ARRAY_OR_OBJECT(zfilter) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_OR_NULL(zoptions) + PHONGO_PARSE_PARAMETERS_END(); + + if (strlen(ns) != ns_len) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Namespace string should not contain null bytes"); + return; + } + + php_phongo_zval_to_bson(zfilter, PHONGO_BSON_NONE, &bfilter, NULL); + + if (EG(exception)) { + goto cleanup; + } + + if (zoptions) { + opts = mongoc_bulkwrite_deletemanyopts_new(); + + if (php_array_existsc(zoptions, "hint")) { + bson_value_t bhint = { 0 }; + + if (!phongo_bwc_parse_hint(php_array_fetchc_deref(zoptions, "hint"), &bhint)) { + bson_value_destroy(&bhint); + goto cleanup; + } + + mongoc_bulkwrite_deletemanyopts_set_hint(opts, &bhint); + bson_value_destroy(&bhint); + } + + if (php_array_existsc(zoptions, "collation")) { + bson_t bcollation = BSON_INITIALIZER; + + if (!phongo_bwc_parse_document(php_array_fetchc_deref(zoptions, "collation"), &bcollation, "collation")) { + bson_destroy(&bcollation); + goto cleanup; + } + + mongoc_bulkwrite_deletemanyopts_set_collation(opts, &bcollation); + bson_destroy(&bcollation); + } + } + + if (!mongoc_bulkwrite_append_deletemany(intern->bw, ns, &bfilter, opts, &error)) { + phongo_throw_exception_from_bson_error_t(&error); + goto cleanup; + } + + intern->num_ops++; + +cleanup: + bson_destroy(&bfilter); + mongoc_bulkwrite_deletemanyopts_destroy(opts); +} + +static PHP_METHOD(MongoDB_Driver_BulkWriteCommand, deleteOne) +{ + php_phongo_bulkwritecommand_t* intern; + char* ns; + size_t ns_len; + zval* zfilter; + zval* zoptions = NULL; + bson_t bfilter = BSON_INITIALIZER; + mongoc_bulkwrite_deleteoneopts_t* opts = NULL; + bson_error_t error = { 0 }; + + intern = Z_BULKWRITECOMMAND_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_STRING(ns, ns_len) + Z_PARAM_ARRAY_OR_OBJECT(zfilter) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_OR_NULL(zoptions) + PHONGO_PARSE_PARAMETERS_END(); + + if (strlen(ns) != ns_len) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Namespace string should not contain null bytes"); + return; + } + + php_phongo_zval_to_bson(zfilter, PHONGO_BSON_NONE, &bfilter, NULL); + + if (EG(exception)) { + goto cleanup; + } + + if (zoptions) { + opts = mongoc_bulkwrite_deleteoneopts_new(); + + if (php_array_existsc(zoptions, "hint")) { + bson_value_t bhint = { 0 }; + + if (!phongo_bwc_parse_hint(php_array_fetchc_deref(zoptions, "hint"), &bhint)) { + bson_value_destroy(&bhint); + goto cleanup; + } + + mongoc_bulkwrite_deleteoneopts_set_hint(opts, &bhint); + bson_value_destroy(&bhint); + } + + if (php_array_existsc(zoptions, "collation")) { + bson_t bcollation = BSON_INITIALIZER; + + if (!phongo_bwc_parse_document(php_array_fetchc_deref(zoptions, "collation"), &bcollation, "collation")) { + bson_destroy(&bcollation); + goto cleanup; + } + + mongoc_bulkwrite_deleteoneopts_set_collation(opts, &bcollation); + bson_destroy(&bcollation); + } + } + + if (!mongoc_bulkwrite_append_deleteone(intern->bw, ns, &bfilter, opts, &error)) { + phongo_throw_exception_from_bson_error_t(&error); + goto cleanup; + } + + intern->num_ops++; + +cleanup: + bson_destroy(&bfilter); + mongoc_bulkwrite_deleteoneopts_destroy(opts); +} + +static PHP_METHOD(MongoDB_Driver_BulkWriteCommand, execute) +{ + php_phongo_bulkwritecommand_t* intern; + + intern = Z_BULKWRITECOMMAND_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_NONE(); + + RETURN_LONG(intern->num_ops); +} + +static PHP_METHOD(MongoDB_Driver_BulkWriteCommand, insertOne) +{ + php_phongo_bulkwritecommand_t* intern; + char* ns; + size_t ns_len; + zval* zdocument; + bson_t bdocument = BSON_INITIALIZER; + bson_t* bson_out = NULL; + bson_error_t error = { 0 }; + + intern = Z_BULKWRITECOMMAND_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(ns, ns_len) + Z_PARAM_ARRAY_OR_OBJECT(zdocument) + PHONGO_PARSE_PARAMETERS_END(); + + if (strlen(ns) != ns_len) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Namespace string should not contain null bytes"); + return; + } + + php_phongo_zval_to_bson(zdocument, (PHONGO_BSON_ADD_ID | PHONGO_BSON_RETURN_ID), &bdocument, &bson_out); + + if (EG(exception)) { + goto cleanup; + } + + if (!mongoc_bulkwrite_append_insertone(intern->bw, ns, &bdocument, NULL, &error)) { + phongo_throw_exception_from_bson_error_t(&error); + goto cleanup; + } + + intern->num_ops++; + + if (!bson_out) { + phongo_throw_exception(PHONGO_ERROR_LOGIC, "php_phongo_zval_to_bson() did not return document identifier. Please file a bug report."); + goto cleanup; + } + + phongo_bwc_extract_id(bson_out, &return_value); + +cleanup: + bson_destroy(&bdocument); + bson_clear(&bson_out); +} + +static PHP_METHOD(MongoDB_Driver_BulkWriteCommand, replaceOne) +{ + php_phongo_bulkwritecommand_t* intern; + char* ns; + size_t ns_len; + zval* zfilter; + zval* zupdate; + zval* zoptions = NULL; + bson_t bfilter = BSON_INITIALIZER; + bson_t bupdate = BSON_INITIALIZER; + mongoc_bulkwrite_replaceoneopts_t* opts = NULL; + bson_error_t error = { 0 }; + + intern = Z_BULKWRITECOMMAND_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_STRING(ns, ns_len) + Z_PARAM_ARRAY_OR_OBJECT(zfilter) + Z_PARAM_ARRAY_OR_OBJECT(zupdate) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_OR_NULL(zoptions) + PHONGO_PARSE_PARAMETERS_END(); + + if (strlen(ns) != ns_len) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Namespace string should not contain null bytes"); + return; + } + + php_phongo_zval_to_bson(zfilter, PHONGO_BSON_NONE, &bfilter, NULL); + + if (EG(exception)) { + goto cleanup; + } + + // Explicitly allow MongoDB\BSON\PackedArray for update pipelines + php_phongo_zval_to_bson(zupdate, PHONGO_BSON_ALLOW_ROOT_ARRAY, &bupdate, NULL); + + if (EG(exception)) { + goto cleanup; + } + + if (zoptions) { + opts = mongoc_bulkwrite_replaceoneopts_new(); + + if (php_array_existsc(zoptions, "collation")) { + bson_t bcollation = BSON_INITIALIZER; + + if (!phongo_bwc_parse_document(php_array_fetchc_deref(zoptions, "collation"), &bcollation, "collation")) { + bson_destroy(&bcollation); + goto cleanup; + } + + mongoc_bulkwrite_replaceoneopts_set_collation(opts, &bcollation); + bson_destroy(&bcollation); + } + + if (php_array_existsc(zoptions, "hint")) { + bson_value_t bhint = { 0 }; + + if (!phongo_bwc_parse_hint(php_array_fetchc_deref(zoptions, "hint"), &bhint)) { + bson_value_destroy(&bhint); + goto cleanup; + } + + mongoc_bulkwrite_replaceoneopts_set_hint(opts, &bhint); + bson_value_destroy(&bhint); + } + + if (php_array_existsc(zoptions, "upsert")) { + mongoc_bulkwrite_replaceoneopts_set_upsert(opts, php_array_fetchc_bool(zoptions, "upsert")); + } + } + + if (!mongoc_bulkwrite_append_replaceone(intern->bw, ns, &bfilter, &bupdate, opts, &error)) { + phongo_throw_exception_from_bson_error_t(&error); + goto cleanup; + } + + intern->num_ops++; + +cleanup: + bson_destroy(&bfilter); + bson_destroy(&bupdate); + mongoc_bulkwrite_replaceoneopts_destroy(opts); +} + +static PHP_METHOD(MongoDB_Driver_BulkWriteCommand, updateMany) +{ + php_phongo_bulkwritecommand_t* intern; + char* ns; + size_t ns_len; + zval* zfilter; + zval* zupdate; + zval* zoptions = NULL; + bson_t bfilter = BSON_INITIALIZER; + bson_t bupdate = BSON_INITIALIZER; + mongoc_bulkwrite_updatemanyopts_t* opts = NULL; + bson_error_t error = { 0 }; + + intern = Z_BULKWRITECOMMAND_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_STRING(ns, ns_len) + Z_PARAM_ARRAY_OR_OBJECT(zfilter) + Z_PARAM_ARRAY_OR_OBJECT(zupdate) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_OR_NULL(zoptions) + PHONGO_PARSE_PARAMETERS_END(); + + if (strlen(ns) != ns_len) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Namespace string should not contain null bytes"); + return; + } + + php_phongo_zval_to_bson(zfilter, PHONGO_BSON_NONE, &bfilter, NULL); + + if (EG(exception)) { + goto cleanup; + } + + // Explicitly allow MongoDB\BSON\PackedArray for update pipelines + php_phongo_zval_to_bson(zupdate, PHONGO_BSON_ALLOW_ROOT_ARRAY, &bupdate, NULL); + + if (EG(exception)) { + goto cleanup; + } + + if (zoptions) { + opts = mongoc_bulkwrite_updatemanyopts_new(); + + if (php_array_existsc(zoptions, "arrayFilters")) { + bson_t barrayfilters = BSON_INITIALIZER; + + if (!phongo_bwc_parse_array(php_array_fetchc_deref(zoptions, "arrayFilters"), &barrayfilters, "arrayFilters")) { + bson_destroy(&barrayfilters); + goto cleanup; + } + + mongoc_bulkwrite_updatemanyopts_set_arrayfilters(opts, &barrayfilters); + bson_destroy(&barrayfilters); + } + + if (php_array_existsc(zoptions, "collation")) { + bson_t bcollation = BSON_INITIALIZER; + + if (!phongo_bwc_parse_document(php_array_fetchc_deref(zoptions, "collation"), &bcollation, "collation")) { + bson_destroy(&bcollation); + goto cleanup; + } + + mongoc_bulkwrite_updatemanyopts_set_collation(opts, &bcollation); + bson_destroy(&bcollation); + } + + if (php_array_existsc(zoptions, "hint")) { + bson_value_t bhint = { 0 }; + + if (!phongo_bwc_parse_hint(php_array_fetchc_deref(zoptions, "hint"), &bhint)) { + bson_value_destroy(&bhint); + goto cleanup; + } + + mongoc_bulkwrite_updatemanyopts_set_hint(opts, &bhint); + bson_value_destroy(&bhint); + } + + if (php_array_existsc(zoptions, "upsert")) { + mongoc_bulkwrite_updatemanyopts_set_upsert(opts, php_array_fetchc_bool(zoptions, "upsert")); + } + } + + if (!mongoc_bulkwrite_append_updatemany(intern->bw, ns, &bfilter, &bupdate, opts, &error)) { + phongo_throw_exception_from_bson_error_t(&error); + goto cleanup; + } + + intern->num_ops++; + +cleanup: + bson_destroy(&bfilter); + bson_destroy(&bupdate); + mongoc_bulkwrite_updatemanyopts_destroy(opts); +} + +static PHP_METHOD(MongoDB_Driver_BulkWriteCommand, updateOne) +{ + php_phongo_bulkwritecommand_t* intern; + char* ns; + size_t ns_len; + zval* zfilter; + zval* zupdate; + zval* zoptions = NULL; + bson_t bfilter = BSON_INITIALIZER; + bson_t bupdate = BSON_INITIALIZER; + mongoc_bulkwrite_updateoneopts_t* opts = NULL; + bson_error_t error = { 0 }; + + intern = Z_BULKWRITECOMMAND_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_STRING(ns, ns_len) + Z_PARAM_ARRAY_OR_OBJECT(zfilter) + Z_PARAM_ARRAY_OR_OBJECT(zupdate) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_OR_NULL(zoptions) + PHONGO_PARSE_PARAMETERS_END(); + + if (strlen(ns) != ns_len) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Namespace string should not contain null bytes"); + return; + } + + php_phongo_zval_to_bson(zfilter, PHONGO_BSON_NONE, &bfilter, NULL); + + if (EG(exception)) { + goto cleanup; + } + + // Explicitly allow MongoDB\BSON\PackedArray for update pipelines + php_phongo_zval_to_bson(zupdate, PHONGO_BSON_ALLOW_ROOT_ARRAY, &bupdate, NULL); + + if (EG(exception)) { + goto cleanup; + } + + if (zoptions) { + opts = mongoc_bulkwrite_updateoneopts_new(); + + if (php_array_existsc(zoptions, "arrayFilters")) { + bson_t barrayfilters = BSON_INITIALIZER; + + if (!phongo_bwc_parse_array(php_array_fetchc_deref(zoptions, "arrayFilters"), &barrayfilters, "arrayFilters")) { + bson_destroy(&barrayfilters); + goto cleanup; + } + + mongoc_bulkwrite_updateoneopts_set_arrayfilters(opts, &barrayfilters); + bson_destroy(&barrayfilters); + } + + if (php_array_existsc(zoptions, "collation")) { + bson_t bcollation = BSON_INITIALIZER; + + if (!phongo_bwc_parse_document(php_array_fetchc_deref(zoptions, "collation"), &bcollation, "collation")) { + bson_destroy(&bcollation); + goto cleanup; + } + + mongoc_bulkwrite_updateoneopts_set_collation(opts, &bcollation); + bson_destroy(&bcollation); + } + + if (php_array_existsc(zoptions, "hint")) { + bson_value_t bhint = { 0 }; + + if (!phongo_bwc_parse_hint(php_array_fetchc_deref(zoptions, "hint"), &bhint)) { + bson_value_destroy(&bhint); + goto cleanup; + } + + mongoc_bulkwrite_updateoneopts_set_hint(opts, &bhint); + bson_value_destroy(&bhint); + } + + if (php_array_existsc(zoptions, "upsert")) { + mongoc_bulkwrite_updateoneopts_set_upsert(opts, php_array_fetchc_bool(zoptions, "upsert")); + } + } + + if (!mongoc_bulkwrite_append_updateone(intern->bw, ns, &bfilter, &bupdate, opts, &error)) { + phongo_throw_exception_from_bson_error_t(&error); + goto cleanup; + } + + intern->num_ops++; + +cleanup: + bson_destroy(&bfilter); + bson_destroy(&bupdate); + mongoc_bulkwrite_updateoneopts_destroy(opts); +} + +/* MongoDB\Driver\BulkWriteCommand object handlers */ +static zend_object_handlers php_phongo_handler_bulkwritecommand; + +static void php_phongo_bulkwritecommand_free_object(zend_object* object) +{ + php_phongo_bulkwritecommand_t* intern = Z_OBJ_BULKWRITECOMMAND(object); + + zend_object_std_dtor(&intern->std); + + if (intern->bw) { + mongoc_bulkwrite_destroy(intern->bw); + } + + if (intern->let) { + bson_clear(&intern->let); + } + + if (intern->comment) { + bson_value_destroy(intern->comment); + efree(intern->comment); + } + + if (!Z_ISUNDEF(intern->session)) { + zval_ptr_dtor(&intern->session); + } + + if (intern->write_concern) { + mongoc_write_concern_destroy(intern->write_concern); + } +} + +static zend_object* php_phongo_bulkwritecommand_create_object(zend_class_entry* class_type) +{ + php_phongo_bulkwritecommand_t* intern = zend_object_alloc(sizeof(php_phongo_bulkwritecommand_t), class_type); + + zend_object_std_init(&intern->std, class_type); + object_properties_init(&intern->std, class_type); + + intern->std.handlers = &php_phongo_handler_bulkwritecommand; + + return &intern->std; +} + +static HashTable* php_phongo_bulkwritecommand_get_debug_info(zend_object* object, int* is_temp) +{ + zval retval = ZVAL_STATIC_INIT; + php_phongo_bulkwritecommand_t* intern = NULL; + + *is_temp = 1; + intern = Z_OBJ_BULKWRITECOMMAND(object); + array_init(&retval); + + if (intern->bypass != PHONGO_BULKWRITECOMMAND_BYPASS_UNSET) { + ADD_ASSOC_BOOL_EX(&retval, "bypassDocumentValidation", intern->bypass); + } else { + ADD_ASSOC_NULL_EX(&retval, "bypassDocumentValidation"); + } + + if (intern->comment) { + zval zv; + + if (!phongo_bson_value_to_zval_legacy(intern->comment, &zv)) { + zval_ptr_dtor(&zv); + goto done; + } + + ADD_ASSOC_ZVAL_EX(&retval, "comment", &zv); + } + + if (intern->let) { + zval zv; + + if (!php_phongo_bson_to_zval(intern->let, &zv)) { + zval_ptr_dtor(&zv); + goto done; + } + + ADD_ASSOC_ZVAL_EX(&retval, "let", &zv); + } + + ADD_ASSOC_BOOL_EX(&retval, "ordered", intern->ordered); + ADD_ASSOC_BOOL_EX(&retval, "verboseResults", intern->verbose); + + if (!Z_ISUNDEF(intern->session)) { + ADD_ASSOC_ZVAL_EX(&retval, "session", &intern->session); + Z_ADDREF(intern->session); + } else { + ADD_ASSOC_NULL_EX(&retval, "session"); + } + + if (intern->write_concern) { + zval write_concern; + + php_phongo_write_concern_to_zval(&write_concern, intern->write_concern); + ADD_ASSOC_ZVAL_EX(&retval, "write_concern", &write_concern); + } else { + ADD_ASSOC_NULL_EX(&retval, "write_concern"); + } + +done: + return Z_ARRVAL(retval); +} + +void php_phongo_bulkwritecommand_init_ce(INIT_FUNC_ARGS) +{ + php_phongo_bulkwritecommand_ce = register_class_MongoDB_Driver_BulkWriteCommand(zend_ce_countable); + php_phongo_bulkwritecommand_ce->create_object = php_phongo_bulkwritecommand_create_object; + + memcpy(&php_phongo_handler_bulkwritecommand, phongo_get_std_object_handlers(), sizeof(zend_object_handlers)); + php_phongo_handler_bulkwritecommand.get_debug_info = php_phongo_bulkwritecommand_get_debug_info; + php_phongo_handler_bulkwritecommand.free_obj = php_phongo_bulkwritecommand_free_object; + php_phongo_handler_bulkwritecommand.offset = XtOffsetOf(php_phongo_bulkwritecommand_t, std); +} diff --git a/src/MongoDB/BulkWriteCommand.stub.php b/src/MongoDB/BulkWriteCommand.stub.php new file mode 100644 index 000000000..d26f9f2ca --- /dev/null +++ b/src/MongoDB/BulkWriteCommand.stub.php @@ -0,0 +1,30 @@ +ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NOT_SERIALIZABLE; + zend_class_implements(class_entry, 1, class_entry_Countable); + + return class_entry; +} diff --git a/src/phongo_classes.h b/src/phongo_classes.h index 0bb513f08..91cf7be31 100644 --- a/src/phongo_classes.h +++ b/src/phongo_classes.h @@ -26,6 +26,10 @@ static inline php_phongo_bulkwrite_t* php_bulkwrite_fetch_object(zend_object* ob { return (php_phongo_bulkwrite_t*) ((char*) obj - XtOffsetOf(php_phongo_bulkwrite_t, std)); } +static inline php_phongo_bulkwritecommand_t* php_bulkwritecommand_fetch_object(zend_object* obj) +{ + return (php_phongo_bulkwritecommand_t*) ((char*) obj - XtOffsetOf(php_phongo_bulkwritecommand_t, std)); +} static inline php_phongo_clientencryption_t* php_clientencryption_fetch_object(zend_object* obj) { return (php_phongo_clientencryption_t*) ((char*) obj - XtOffsetOf(php_phongo_clientencryption_t, std)); @@ -221,6 +225,7 @@ static inline php_phongo_topologyopeningevent_t* php_topologyopeningevent_fetch_ #define Z_SESSION_OBJ_P(zv) (php_session_fetch_object(Z_OBJ_P(zv))) #define Z_TOPOLOGYDESCRIPTION_OBJ_P(zv) (php_topologydescription_fetch_object(Z_OBJ_P(zv))) #define Z_BULKWRITE_OBJ_P(zv) (php_bulkwrite_fetch_object(Z_OBJ_P(zv))) +#define Z_BULKWRITECOMMAND_OBJ_P(zv) (php_bulkwritecommand_fetch_object(Z_OBJ_P(zv))) #define Z_WRITECONCERN_OBJ_P(zv) (php_writeconcern_fetch_object(Z_OBJ_P(zv))) #define Z_WRITECONCERNERROR_OBJ_P(zv) (php_writeconcernerror_fetch_object(Z_OBJ_P(zv))) #define Z_WRITEERROR_OBJ_P(zv) (php_writeerror_fetch_object(Z_OBJ_P(zv))) @@ -268,6 +273,7 @@ static inline php_phongo_topologyopeningevent_t* php_topologyopeningevent_fetch_ #define Z_OBJ_SESSION(zo) (php_session_fetch_object(zo)) #define Z_OBJ_TOPOLOGYDESCRIPTION(zo) (php_topologydescription_fetch_object(zo)) #define Z_OBJ_BULKWRITE(zo) (php_bulkwrite_fetch_object(zo)) +#define Z_OBJ_BULKWRITECOMMAND(zo) (php_bulkwritecommand_fetch_object(zo)) #define Z_OBJ_WRITECONCERN(zo) (php_writeconcern_fetch_object(zo)) #define Z_OBJ_WRITECONCERNERROR(zo) (php_writeconcernerror_fetch_object(zo)) #define Z_OBJ_WRITEERROR(zo) (php_writeerror_fetch_object(zo)) @@ -315,6 +321,7 @@ extern zend_class_entry* php_phongo_serverdescription_ce; extern zend_class_entry* php_phongo_session_ce; extern zend_class_entry* php_phongo_topologydescription_ce; extern zend_class_entry* php_phongo_bulkwrite_ce; +extern zend_class_entry* php_phongo_bulkwritecommand_ce; extern zend_class_entry* php_phongo_writeconcern_ce; extern zend_class_entry* php_phongo_writeconcernerror_ce; extern zend_class_entry* php_phongo_writeerror_ce; @@ -418,6 +425,7 @@ extern void php_phongo_timestamp_interface_init_ce(INIT_FUNC_ARGS); extern void php_phongo_utcdatetime_interface_init_ce(INIT_FUNC_ARGS); extern void php_phongo_bulkwrite_init_ce(INIT_FUNC_ARGS); +extern void php_phongo_bulkwritecommand_init_ce(INIT_FUNC_ARGS); extern void php_phongo_clientencryption_init_ce(INIT_FUNC_ARGS); extern void php_phongo_command_init_ce(INIT_FUNC_ARGS); extern void php_phongo_cursor_init_ce(INIT_FUNC_ARGS); diff --git a/src/phongo_structs.h b/src/phongo_structs.h index 488e192db..fba8b7782 100644 --- a/src/phongo_structs.h +++ b/src/phongo_structs.h @@ -38,6 +38,19 @@ typedef struct { zend_object std; } php_phongo_bulkwrite_t; +typedef struct { + mongoc_bulkwrite_t* bw; + int bypass; + bson_value_t* comment; + bson_t* let; + size_t num_ops; + bool ordered; + zval session; + bool verbose; + mongoc_write_concern_t* write_concern; + zend_object std; +} php_phongo_bulkwritecommand_t; + typedef struct { mongoc_client_encryption_t* client_encryption; zval key_vault_client_manager; diff --git a/tests/bulkwritecommand/bulkwritecommand-insertone-001.phpt b/tests/bulkwritecommand/bulkwritecommand-insertone-001.phpt new file mode 100644 index 000000000..3698a1c3e --- /dev/null +++ b/tests/bulkwritecommand/bulkwritecommand-insertone-001.phpt @@ -0,0 +1,25 @@ +--TEST-- +MongoDB\Driver\BulkWriteCommand::insertOne() returns document ID +--SKIPIF-- + +--FILE-- +insertOne(NS, ['_id' => 1])); +var_dump($bulk->insertOne(NS, [])); + + +?> +===DONE=== + +--EXPECTF-- +int(1) +object(MongoDB\BSON\ObjectId)#%d (%d) { + ["oid"]=> + string(24) "%x" +} +===DONE=== From 3207d6274dde41fa6472d305f215ce2ef1e2febd Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Thu, 2 Jan 2025 16:47:24 -0500 Subject: [PATCH 03/13] PHPC-2494: BulkWriteCommandResult and BulkWriteCommandException It was possible to reuse WriteConcernError and WriteError with slight changes to their init functions. --- config.m4 | 2 + config.w32 | 4 +- php_phongo.c | 2 + src/MongoDB/BulkWriteCommandResult.c | 455 ++++++++++++++++++ src/MongoDB/BulkWriteCommandResult.h | 28 ++ src/MongoDB/BulkWriteCommandResult.stub.php | 40 ++ src/MongoDB/BulkWriteCommandResult_arginfo.h | 82 ++++ .../Exception/BulkWriteCommandException.c | 41 ++ .../BulkWriteCommandException.stub.php | 15 + .../BulkWriteCommandException_arginfo.h | 31 ++ src/MongoDB/WriteConcernError.c | 8 +- src/MongoDB/WriteError.c | 19 +- src/MongoDB/WriteError.h | 1 + src/phongo_classes.h | 10 + src/phongo_structs.h | 18 + 15 files changed, 748 insertions(+), 8 deletions(-) create mode 100644 src/MongoDB/BulkWriteCommandResult.c create mode 100644 src/MongoDB/BulkWriteCommandResult.h create mode 100644 src/MongoDB/BulkWriteCommandResult.stub.php create mode 100644 src/MongoDB/BulkWriteCommandResult_arginfo.h create mode 100644 src/MongoDB/Exception/BulkWriteCommandException.c create mode 100644 src/MongoDB/Exception/BulkWriteCommandException.stub.php create mode 100644 src/MongoDB/Exception/BulkWriteCommandException_arginfo.h diff --git a/config.m4 b/config.m4 index 091d5f9cd..2f9f85893 100644 --- a/config.m4 +++ b/config.m4 @@ -169,6 +169,7 @@ if test "$PHP_MONGODB" != "no"; then src/BSON/functions.c \ src/MongoDB/BulkWrite.c \ src/MongoDB/BulkWriteCommand.c \ + src/MongoDB/BulkWriteCommandResult.c \ src/MongoDB/ClientEncryption.c \ src/MongoDB/Command.c \ src/MongoDB/Cursor.c \ @@ -189,6 +190,7 @@ if test "$PHP_MONGODB" != "no"; then src/MongoDB/WriteResult.c \ src/MongoDB/Exception/AuthenticationException.c \ src/MongoDB/Exception/BulkWriteException.c \ + src/MongoDB/Exception/BulkWriteCommandException.c \ src/MongoDB/Exception/CommandException.c \ src/MongoDB/Exception/ConnectionException.c \ src/MongoDB/Exception/ConnectionTimeoutException.c \ diff --git a/config.w32 b/config.w32 index f5db82bc0..b575191eb 100644 --- a/config.w32 +++ b/config.w32 @@ -116,8 +116,8 @@ if (PHP_MONGODB != "no") { EXTENSION("mongodb", "php_phongo.c", null, PHP_MONGODB_CFLAGS); MONGODB_ADD_SOURCES("/src", "phongo_apm.c phongo_atomic.c phongo_bson.c phongo_bson_encode.c phongo_client.c phongo_compat.c phongo_error.c phongo_execute.c phongo_ini.c phongo_log.c phongo_util.c"); MONGODB_ADD_SOURCES("/src/BSON", "Binary.c BinaryInterface.c Document.c Iterator.c DBPointer.c Decimal128.c Decimal128Interface.c Int64.c Javascript.c JavascriptInterface.c MaxKey.c MaxKeyInterface.c MinKey.c MinKeyInterface.c ObjectId.c ObjectIdInterface.c PackedArray.c Persistable.c Regex.c RegexInterface.c Serializable.c Symbol.c Timestamp.c TimestampInterface.c Type.c Undefined.c Unserializable.c UTCDateTime.c UTCDateTimeInterface.c functions.c"); - MONGODB_ADD_SOURCES("/src/MongoDB", "BulkWrite.c BulkWriteCommand.c ClientEncryption.c Command.c Cursor.c CursorId.c CursorInterface.c Manager.c Query.c ReadConcern.c ReadPreference.c Server.c ServerApi.c ServerDescription.c Session.c TopologyDescription.c WriteConcern.c WriteConcernError.c WriteError.c WriteResult.c"); - MONGODB_ADD_SOURCES("/src/MongoDB/Exception", "AuthenticationException.c BulkWriteException.c CommandException.c ConnectionException.c ConnectionTimeoutException.c EncryptionException.c Exception.c ExecutionTimeoutException.c InvalidArgumentException.c LogicException.c RuntimeException.c ServerException.c SSLConnectionException.c UnexpectedValueException.c WriteException.c"); + MONGODB_ADD_SOURCES("/src/MongoDB", "BulkWrite.c BulkWriteCommand.c BulkWriteCommandResult.c ClientEncryption.c Command.c Cursor.c CursorId.c CursorInterface.c Manager.c Query.c ReadConcern.c ReadPreference.c Server.c ServerApi.c ServerDescription.c Session.c TopologyDescription.c WriteConcern.c WriteConcernError.c WriteError.c WriteResult.c"); + MONGODB_ADD_SOURCES("/src/MongoDB/Exception", "AuthenticationException.c BulkWriteException.c BulkWriteCommandException.c CommandException.c ConnectionException.c ConnectionTimeoutException.c EncryptionException.c Exception.c ExecutionTimeoutException.c InvalidArgumentException.c LogicException.c RuntimeException.c ServerException.c SSLConnectionException.c UnexpectedValueException.c WriteException.c"); MONGODB_ADD_SOURCES("/src/MongoDB/Monitoring", "CommandFailedEvent.c CommandStartedEvent.c CommandSubscriber.c CommandSucceededEvent.c LogSubscriber.c SDAMSubscriber.c Subscriber.c ServerChangedEvent.c ServerClosedEvent.c ServerHeartbeatFailedEvent.c ServerHeartbeatStartedEvent.c ServerHeartbeatSucceededEvent.c ServerOpeningEvent.c TopologyChangedEvent.c TopologyClosedEvent.c TopologyOpeningEvent.c functions.c"); MONGODB_ADD_SOURCES("/src/libmongoc/src/common/src", PHP_MONGODB_COMMON_SOURCES); MONGODB_ADD_SOURCES("/src/libmongoc/src/libbson/src/bson", PHP_MONGODB_BSON_SOURCES); diff --git a/php_phongo.c b/php_phongo.c index 9e008d05e..3fdd090c7 100644 --- a/php_phongo.c +++ b/php_phongo.c @@ -253,6 +253,7 @@ PHP_MINIT_FUNCTION(mongodb) /* {{{ */ php_phongo_bulkwrite_init_ce(INIT_FUNC_ARGS_PASSTHRU); php_phongo_bulkwritecommand_init_ce(INIT_FUNC_ARGS_PASSTHRU); + php_phongo_bulkwritecommandresult_init_ce(INIT_FUNC_ARGS_PASSTHRU); php_phongo_clientencryption_init_ce(INIT_FUNC_ARGS_PASSTHRU); php_phongo_command_init_ce(INIT_FUNC_ARGS_PASSTHRU); php_phongo_cursor_init_ce(INIT_FUNC_ARGS_PASSTHRU); @@ -280,6 +281,7 @@ PHP_MINIT_FUNCTION(mongodb) /* {{{ */ php_phongo_authenticationexception_init_ce(INIT_FUNC_ARGS_PASSTHRU); php_phongo_bulkwriteexception_init_ce(INIT_FUNC_ARGS_PASSTHRU); + php_phongo_bulkwritecommandexception_init_ce(INIT_FUNC_ARGS_PASSTHRU); php_phongo_commandexception_init_ce(INIT_FUNC_ARGS_PASSTHRU); php_phongo_connectiontimeoutexception_init_ce(INIT_FUNC_ARGS_PASSTHRU); php_phongo_encryptionexception_init_ce(INIT_FUNC_ARGS_PASSTHRU); diff --git a/src/MongoDB/BulkWriteCommandResult.c b/src/MongoDB/BulkWriteCommandResult.c new file mode 100644 index 000000000..9c61197f8 --- /dev/null +++ b/src/MongoDB/BulkWriteCommandResult.c @@ -0,0 +1,455 @@ +/* + * Copyright 2014-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "bson/bson.h" +#include "mongoc/mongoc.h" + +#include +#include + +#include "php_array_api.h" + +#include "php_phongo.h" +#include "phongo_error.h" + +#include "BSON/Document.h" +#include "MongoDB/Server.h" +#include "MongoDB/WriteConcernError.h" +#include "MongoDB/WriteError.h" +#include "BulkWriteCommandResult_arginfo.h" + +#define PHONGO_BULKWRITECOMMANDRESULT_CHECK_ACKNOWLEDGED(method) \ + if (!intern->is_acknowledged) { \ + phongo_throw_exception(PHONGO_ERROR_LOGIC, "MongoDB\\Driver\\BulkWriteCommandResult::" method "() should not be called for an unacknowledged write result"); \ + return; \ + } + +zend_class_entry* php_phongo_bulkwritecommandresult_ce; + +static bool php_phongo_bulkwritecommandresult_get_writeconcernerrors(php_phongo_bulkwritecommandresult_t* intern, zval* return_value) +{ + bson_iter_t iter; + + array_init(return_value); + + for (bson_iter_init(&iter, intern->write_concern_errors); bson_iter_next(&iter);) { + bson_t bson; + uint32_t len; + const uint8_t* data; + zval write_concern_error; + + if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) { + continue; + } + + bson_iter_document(&iter, &len, &data); + + if (!bson_init_static(&bson, data, len)) { + continue; + } + + if (!phongo_writeconcernerror_init(&write_concern_error, &bson)) { + zval_ptr_dtor(&write_concern_error); + continue; + } + + add_next_index_zval(return_value, &write_concern_error); + } + + return true; +} + +static bool php_phongo_bulkwritecommandresult_get_writeerrors(php_phongo_bulkwritecommandresult_t* intern, zval* return_value) +{ + bson_iter_t iter; + + array_init(return_value); + + for (bson_iter_init(&iter, intern->write_errors); bson_iter_next(&iter);) { + bson_t bson; + uint32_t len; + const uint8_t* data; + zval write_error; + + if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) { + continue; + } + + bson_iter_document(&iter, &len, &data); + + if (!bson_init_static(&bson, data, len)) { + continue; + } + + if (!phongo_writeerror_init_ex(&write_error, &bson, atoi(bson_iter_key(&iter)))) { + zval_ptr_dtor(&write_error); + continue; + } + + add_next_index_zval(return_value, &write_error); + } + + return true; +} + +PHONGO_DISABLED_CONSTRUCTOR(MongoDB_Driver_BulkWriteCommandResult) + +/* Returns the number of documents that were inserted */ +static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, getInsertedCount) +{ + php_phongo_bulkwritecommandresult_t* intern; + + intern = Z_BULKWRITECOMMANDRESULT_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_NONE(); + + PHONGO_BULKWRITECOMMANDRESULT_CHECK_ACKNOWLEDGED("getInsertedCount"); + + RETURN_LONG(intern->inserted_count); +} + +/* Returns the number of documents that matched the update criteria */ +static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, getMatchedCount) +{ + php_phongo_bulkwritecommandresult_t* intern; + + intern = Z_BULKWRITECOMMANDRESULT_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_NONE(); + + PHONGO_BULKWRITECOMMANDRESULT_CHECK_ACKNOWLEDGED("getMatchedCount"); + + RETURN_LONG(intern->matched_count); +} + +/* Returns the number of documents that were actually modified by an update */ +static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, getModifiedCount) +{ + php_phongo_bulkwritecommandresult_t* intern; + + intern = Z_BULKWRITECOMMANDRESULT_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_NONE(); + + PHONGO_BULKWRITECOMMANDRESULT_CHECK_ACKNOWLEDGED("getModifiedCount"); + + RETURN_LONG(intern->modified_count); +} + +/* Returns the number of documents that were deleted */ +static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, getDeletedCount) +{ + php_phongo_bulkwritecommandresult_t* intern; + + intern = Z_BULKWRITECOMMANDRESULT_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_NONE(); + + PHONGO_BULKWRITECOMMANDRESULT_CHECK_ACKNOWLEDGED("getDeletedCount"); + + RETURN_LONG(intern->deleted_count); +} + +/* Returns the number of documents that were upserted */ +static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, getUpsertedCount) +{ + php_phongo_bulkwritecommandresult_t* intern; + + intern = Z_BULKWRITECOMMANDRESULT_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_NONE(); + + PHONGO_BULKWRITECOMMANDRESULT_CHECK_ACKNOWLEDGED("getUpsertedCount"); + + RETURN_LONG(intern->upserted_count); +} + +/* Returns the last Server used to execute a command for the bulk write */ +static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, getServer) +{ + php_phongo_bulkwritecommandresult_t* intern; + + intern = Z_BULKWRITECOMMANDRESULT_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_NONE(); + + PHONGO_BULKWRITECOMMANDRESULT_CHECK_ACKNOWLEDGED("getServer"); + + // TODO: null handling + phongo_server_init(return_value, &intern->manager, intern->server_id); +} + +static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, getInsertResults) +{ + php_phongo_bulkwritecommandresult_t* intern; + + intern = Z_BULKWRITECOMMANDRESULT_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_NONE(); + + PHONGO_BULKWRITECOMMANDRESULT_CHECK_ACKNOWLEDGED("getInsertResults"); + + if (intern->insert_results) { + phongo_document_new(return_value, intern->insert_results, true); + } +} + +static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, getUpdateResults) +{ + php_phongo_bulkwritecommandresult_t* intern; + + intern = Z_BULKWRITECOMMANDRESULT_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_NONE(); + + PHONGO_BULKWRITECOMMANDRESULT_CHECK_ACKNOWLEDGED("getUpdateResults"); + + if (intern->update_results) { + phongo_document_new(return_value, intern->update_results, true); + } +} + +static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, getDeleteResults) +{ + php_phongo_bulkwritecommandresult_t* intern; + + intern = Z_BULKWRITECOMMANDRESULT_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_NONE(); + + PHONGO_BULKWRITECOMMANDRESULT_CHECK_ACKNOWLEDGED("getDeleteResults"); + + if (intern->delete_results) { + phongo_document_new(return_value, intern->delete_results, true); + } +} + +/* Return any write concern errors that occurred */ +static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, getWriteConcernErrors) +{ + php_phongo_bulkwritecommandresult_t* intern; + + intern = Z_BULKWRITECOMMANDRESULT_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_NONE(); + + // TODO: null handling + php_phongo_bulkwritecommandresult_get_writeconcernerrors(intern, return_value); +} + +/* Returns any write errors that occurred */ +static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, getWriteErrors) +{ + php_phongo_bulkwritecommandresult_t* intern; + + intern = Z_BULKWRITECOMMANDRESULT_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_NONE(); + + // TODO: null handling + php_phongo_bulkwritecommandresult_get_writeerrors(intern, return_value); +} + +static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, getErrorReply) +{ + php_phongo_bulkwritecommandresult_t* intern; + + intern = Z_BULKWRITECOMMANDRESULT_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_NONE(); + + // TODO: null handling + phongo_document_new(return_value, intern->error_reply, true); +} + +/* Returns whether the write operation was acknowledged (based on the write + concern). */ +static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, isAcknowledged) +{ + php_phongo_bulkwritecommandresult_t* intern; + + intern = Z_BULKWRITECOMMANDRESULT_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_NONE(); + + RETURN_BOOL(intern->is_acknowledged); +} + +/* MongoDB\Driver\BulkWriteCommandResult object handlers */ +static zend_object_handlers php_phongo_handler_bulkwritecommandresult; + +static void php_phongo_bulkwritecommandresult_free_object(zend_object* object) +{ + php_phongo_bulkwritecommandresult_t* intern = Z_OBJ_BULKWRITECOMMANDRESULT(object); + + zend_object_std_dtor(&intern->std); + + bson_destroy(intern->insert_results); + bson_destroy(intern->update_results); + bson_destroy(intern->delete_results); + bson_destroy(intern->error_reply); + bson_destroy(intern->write_errors); + bson_destroy(intern->write_concern_errors); + + if (!Z_ISUNDEF(intern->manager)) { + zval_ptr_dtor(&intern->manager); + } +} + +static zend_object* php_phongo_bulkwritecommandresult_create_object(zend_class_entry* class_type) +{ + php_phongo_bulkwritecommandresult_t* intern = zend_object_alloc(sizeof(php_phongo_bulkwritecommandresult_t), class_type); + + zend_object_std_init(&intern->std, class_type); + object_properties_init(&intern->std, class_type); + + intern->std.handlers = &php_phongo_handler_bulkwritecommandresult; + + return &intern->std; +} + +static HashTable* php_phongo_bulkwritecommandresult_get_debug_info(zend_object* object, int* is_temp) +{ + php_phongo_bulkwritecommandresult_t* intern; + zval retval = ZVAL_STATIC_INIT; + + intern = Z_OBJ_BULKWRITECOMMANDRESULT(object); + *is_temp = 1; + array_init_size(&retval, 12); + + ADD_ASSOC_BOOL_EX(&retval, "isAcknowledged", intern->is_acknowledged); + ADD_ASSOC_LONG_EX(&retval, "insertedCount", intern->inserted_count); + ADD_ASSOC_LONG_EX(&retval, "matchedCount", intern->matched_count); + ADD_ASSOC_LONG_EX(&retval, "modifiedCount", intern->modified_count); + ADD_ASSOC_LONG_EX(&retval, "upsertedCount", intern->upserted_count); + ADD_ASSOC_LONG_EX(&retval, "deletedCount", intern->deleted_count); + + if (intern->insert_results) { + zval insert_results; + + phongo_document_new(&insert_results, intern->insert_results, true); + ADD_ASSOC_ZVAL_EX(&retval, "insertResults", &insert_results); + } else { + ADD_ASSOC_NULL_EX(&retval, "insertResults"); + } + + if (intern->update_results) { + zval update_results; + + phongo_document_new(&update_results, intern->update_results, true); + ADD_ASSOC_ZVAL_EX(&retval, "updateResults", &update_results); + } else { + ADD_ASSOC_NULL_EX(&retval, "updateResults"); + } + + if (intern->delete_results) { + zval delete_results; + + phongo_document_new(&delete_results, intern->delete_results, true); + ADD_ASSOC_ZVAL_EX(&retval, "deleteResults", &delete_results); + } else { + ADD_ASSOC_NULL_EX(&retval, "deleteResults"); + } + + if (intern->write_errors) { + zval writeerrors; + + php_phongo_bulkwritecommandresult_get_writeerrors(intern, &writeerrors); + ADD_ASSOC_ZVAL_EX(&retval, "writeErrors", &writeerrors); + } else { + ADD_ASSOC_NULL_EX(&retval, "writeErrors"); + } + + if (intern->write_concern_errors) { + zval writeconcernerrors; + + php_phongo_bulkwritecommandresult_get_writeconcernerrors(intern, &writeconcernerrors); + ADD_ASSOC_ZVAL_EX(&retval, "writeConcernErrors", &writeconcernerrors); + } else { + ADD_ASSOC_NULL_EX(&retval, "writeConcernErrors"); + } + + if (intern->error_reply) { + zval error_reply; + + phongo_document_new(&error_reply, intern->error_reply, true); + ADD_ASSOC_ZVAL_EX(&retval, "errorReply", &error_reply); + } else { + ADD_ASSOC_NULL_EX(&retval, "errorReply"); + } + + if (intern->server_id) { + zval server; + + phongo_server_init(&server, &intern->manager, intern->server_id); + ADD_ASSOC_ZVAL_EX(&retval, "server", &server); + } else { + /* TODO: Determine if this path is only reached when a partial result is + * attached to a BulkWriteCommandException on an unacknowledged write. */ + ADD_ASSOC_NULL_EX(&retval, "server"); + } + + return Z_ARRVAL(retval); +} + +void php_phongo_bulkwritecommandresult_init_ce(INIT_FUNC_ARGS) +{ + php_phongo_bulkwritecommandresult_ce = register_class_MongoDB_Driver_BulkWriteCommandResult(); + php_phongo_bulkwritecommandresult_ce->create_object = php_phongo_bulkwritecommandresult_create_object; + + memcpy(&php_phongo_handler_bulkwritecommandresult, phongo_get_std_object_handlers(), sizeof(zend_object_handlers)); + php_phongo_handler_bulkwritecommandresult.get_debug_info = php_phongo_bulkwritecommandresult_get_debug_info; + php_phongo_handler_bulkwritecommandresult.free_obj = php_phongo_bulkwritecommandresult_free_object; + php_phongo_handler_bulkwritecommandresult.offset = XtOffsetOf(php_phongo_bulkwritecommandresult_t, std); +} + +php_phongo_bulkwritecommandresult_t* phongo_bulkwritecommandresult_init(zval* return_value, mongoc_bulkwritereturn_t* bw_ret, zval* manager) +{ + php_phongo_bulkwritecommandresult_t* bwcr; + + object_init_ex(return_value, php_phongo_bulkwritecommandresult_ce); + + bwcr = Z_BULKWRITECOMMANDRESULT_OBJ_P(return_value); + bwcr->is_acknowledged = !!bw_ret->res; + + // Copy mongoc_bulkwriteresult_t fields + if (bw_ret->res) { + bwcr->inserted_count = mongoc_bulkwriteresult_insertedcount(bw_ret->res); + bwcr->upserted_count = mongoc_bulkwriteresult_upsertedcount(bw_ret->res); + bwcr->matched_count = mongoc_bulkwriteresult_matchedcount(bw_ret->res); + bwcr->modified_count = mongoc_bulkwriteresult_modifiedcount(bw_ret->res); + bwcr->deleted_count = mongoc_bulkwriteresult_deletedcount(bw_ret->res); + +#define BSON_COPY_OR_NULL(bson) ((bson) ? bson_copy(bson) : NULL) + bwcr->insert_results = BSON_COPY_OR_NULL(mongoc_bulkwriteresult_insertresults(bw_ret->res)); + bwcr->update_results = BSON_COPY_OR_NULL(mongoc_bulkwriteresult_updateresults(bw_ret->res)); + bwcr->delete_results = BSON_COPY_OR_NULL(mongoc_bulkwriteresult_deleteresults(bw_ret->res)); +#undef BSON_COPY_OR_NULL + + bwcr->server_id = mongoc_bulkwriteresult_serverid(bw_ret->res); + } + + // Copy mongoc_bulkwriteexception_t fields + if (bw_ret->exc) { + bwcr->error_reply = bson_copy(mongoc_bulkwriteexception_errorreply(bw_ret->exc)); + bwcr->write_errors = bson_copy(mongoc_bulkwriteexception_writeerrors(bw_ret->exc)); + bwcr->write_concern_errors = bson_copy(mongoc_bulkwriteexception_writeconcernerrors(bw_ret->exc)); + } + + ZVAL_ZVAL(&bwcr->manager, manager, 1, 0); + + return bwcr; +} diff --git a/src/MongoDB/BulkWriteCommandResult.h b/src/MongoDB/BulkWriteCommandResult.h new file mode 100644 index 000000000..567e2a1fc --- /dev/null +++ b/src/MongoDB/BulkWriteCommandResult.h @@ -0,0 +1,28 @@ +/* + * Copyright 2024-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PHONGO_BULKWRITECOMMANDRESULT_H +#define PHONGO_BULKWRITECOMMANDRESULT_H + +#include "mongoc/mongoc.h" + +#include + +#include "phongo_structs.h" + +php_phongo_bulkwritecommandresult_t* phongo_bulkwritecommandresult_init(zval* return_value, mongoc_bulkwritereturn_t* bw_ret, zval* manager); + +#endif /* PHONGO_BULKWRITECOMMANDRESULT_H */ diff --git a/src/MongoDB/BulkWriteCommandResult.stub.php b/src/MongoDB/BulkWriteCommandResult.stub.php new file mode 100644 index 000000000..e316ee8b1 --- /dev/null +++ b/src/MongoDB/BulkWriteCommandResult.stub.php @@ -0,0 +1,40 @@ +ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NOT_SERIALIZABLE; + + return class_entry; +} diff --git a/src/MongoDB/Exception/BulkWriteCommandException.c b/src/MongoDB/Exception/BulkWriteCommandException.c new file mode 100644 index 000000000..739e458f8 --- /dev/null +++ b/src/MongoDB/Exception/BulkWriteCommandException.c @@ -0,0 +1,41 @@ +/* + * Copyright 2024-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "php_phongo.h" +#include "phongo_error.h" +#include "BulkWriteCommandException_arginfo.h" + +zend_class_entry* php_phongo_bulkwritecommandexception_ce; + +/* Returns the BulkWriteCommandResult from the failed write operation. */ +static PHP_METHOD(MongoDB_Driver_Exception_BulkWriteCommandException, getBulkWriteCommandResult) +{ + zval* bwcr; + zval rv; + + PHONGO_PARSE_PARAMETERS_NONE(); + + bwcr = zend_read_property(php_phongo_bulkwritecommandexception_ce, Z_OBJ_P(getThis()), ZEND_STRL("bulkWriteCommandResult"), 0, &rv); + + RETURN_ZVAL(bwcr, 1, 0); +} + +void php_phongo_bulkwritecommandexception_init_ce(INIT_FUNC_ARGS) +{ + php_phongo_bulkwritecommandexception_ce = register_class_MongoDB_Driver_Exception_BulkWriteCommandException(php_phongo_serverexception_ce); +} diff --git a/src/MongoDB/Exception/BulkWriteCommandException.stub.php b/src/MongoDB/Exception/BulkWriteCommandException.stub.php new file mode 100644 index 000000000..9d0053462 --- /dev/null +++ b/src/MongoDB/Exception/BulkWriteCommandException.stub.php @@ -0,0 +1,15 @@ +code = bson_iter_int32(&iter); } - if (bson_iter_init_find(&iter, bson, "errmsg") && BSON_ITER_HOLDS_UTF8(&iter)) { + // Additionally check for field name used by mongoc_bulkwriteexception_t + if ((bson_iter_init_find(&iter, bson, "errmsg") && BSON_ITER_HOLDS_UTF8(&iter)) || + (bson_iter_init_find(&iter, bson, "message") && BSON_ITER_HOLDS_UTF8(&iter))) { uint32_t errmsg_len; const char* err_msg = bson_iter_utf8(&iter, &errmsg_len); intern->message = estrndup(err_msg, errmsg_len); } - if (bson_iter_init_find(&iter, bson, "errInfo") && BSON_ITER_HOLDS_DOCUMENT(&iter)) { + // Additionally check for field name used by mongoc_bulkwriteexception_t + if ((bson_iter_init_find(&iter, bson, "errInfo") && BSON_ITER_HOLDS_DOCUMENT(&iter)) || + (bson_iter_init_find(&iter, bson, "details") && BSON_ITER_HOLDS_DOCUMENT(&iter))) { uint32_t len; const uint8_t* data = NULL; diff --git a/src/MongoDB/WriteError.c b/src/MongoDB/WriteError.c index 5db082d20..5befa1d2e 100644 --- a/src/MongoDB/WriteError.c +++ b/src/MongoDB/WriteError.c @@ -144,6 +144,11 @@ void php_phongo_writeerror_init_ce(INIT_FUNC_ARGS) } zend_bool phongo_writeerror_init(zval* return_value, bson_t* bson) +{ + return phongo_writeerror_init_ex(return_value, bson, 0); +} + +zend_bool phongo_writeerror_init_ex(zval* return_value, bson_t* bson, int32_t index) { bson_iter_t iter; php_phongo_writeerror_t* intern; @@ -152,20 +157,24 @@ zend_bool phongo_writeerror_init(zval* return_value, bson_t* bson) intern = Z_WRITEERROR_OBJ_P(return_value); intern->code = 0; - intern->index = 0; + intern->index = index; if (bson_iter_init_find(&iter, bson, "code") && BSON_ITER_HOLDS_INT32(&iter)) { intern->code = bson_iter_int32(&iter); } - if (bson_iter_init_find(&iter, bson, "errmsg") && BSON_ITER_HOLDS_UTF8(&iter)) { + // Additionally check for field name used by mongoc_bulkwriteexception_t + if ((bson_iter_init_find(&iter, bson, "errmsg") && BSON_ITER_HOLDS_UTF8(&iter)) || + (bson_iter_init_find(&iter, bson, "message") && BSON_ITER_HOLDS_UTF8(&iter))) { uint32_t errmsg_len; const char* err_msg = bson_iter_utf8(&iter, &errmsg_len); intern->message = estrndup(err_msg, errmsg_len); } - if (bson_iter_init_find(&iter, bson, "errInfo") && BSON_ITER_HOLDS_DOCUMENT(&iter)) { + // Additionally check for field name used by mongoc_bulkwriteexception_t + if ((bson_iter_init_find(&iter, bson, "errInfo") && BSON_ITER_HOLDS_DOCUMENT(&iter)) || + (bson_iter_init_find(&iter, bson, "details") && BSON_ITER_HOLDS_DOCUMENT(&iter))) { uint32_t len; const uint8_t* data = NULL; @@ -179,7 +188,9 @@ zend_bool phongo_writeerror_init(zval* return_value, bson_t* bson) } } - if (bson_iter_init_find(&iter, bson, "index") && BSON_ITER_HOLDS_INT32(&iter)) { + /* If the WriteError is initialized from mongoc_bulkwriteexception_t, an + * index will already have been specified. */ + if (!intern->index && bson_iter_init_find(&iter, bson, "index") && BSON_ITER_HOLDS_INT32(&iter)) { intern->index = bson_iter_int32(&iter); } diff --git a/src/MongoDB/WriteError.h b/src/MongoDB/WriteError.h index b2334f98e..3cb641bf5 100644 --- a/src/MongoDB/WriteError.h +++ b/src/MongoDB/WriteError.h @@ -22,5 +22,6 @@ #include zend_bool phongo_writeerror_init(zval* return_value, bson_t* bson); +zend_bool phongo_writeerror_init_ex(zval* return_value, bson_t* bson, int32_t index); #endif /* PHONGO_WRITEERROR_H */ diff --git a/src/phongo_classes.h b/src/phongo_classes.h index 91cf7be31..17412f6b9 100644 --- a/src/phongo_classes.h +++ b/src/phongo_classes.h @@ -30,6 +30,10 @@ static inline php_phongo_bulkwritecommand_t* php_bulkwritecommand_fetch_object(z { return (php_phongo_bulkwritecommand_t*) ((char*) obj - XtOffsetOf(php_phongo_bulkwritecommand_t, std)); } +static inline php_phongo_bulkwritecommandresult_t* php_bulkwritecommandresult_fetch_object(zend_object* obj) +{ + return (php_phongo_bulkwritecommandresult_t*) ((char*) obj - XtOffsetOf(php_phongo_bulkwritecommandresult_t, std)); +} static inline php_phongo_clientencryption_t* php_clientencryption_fetch_object(zend_object* obj) { return (php_phongo_clientencryption_t*) ((char*) obj - XtOffsetOf(php_phongo_clientencryption_t, std)); @@ -226,6 +230,7 @@ static inline php_phongo_topologyopeningevent_t* php_topologyopeningevent_fetch_ #define Z_TOPOLOGYDESCRIPTION_OBJ_P(zv) (php_topologydescription_fetch_object(Z_OBJ_P(zv))) #define Z_BULKWRITE_OBJ_P(zv) (php_bulkwrite_fetch_object(Z_OBJ_P(zv))) #define Z_BULKWRITECOMMAND_OBJ_P(zv) (php_bulkwritecommand_fetch_object(Z_OBJ_P(zv))) +#define Z_BULKWRITECOMMANDRESULT_OBJ_P(zv) (php_bulkwritecommandresult_fetch_object(Z_OBJ_P(zv))) #define Z_WRITECONCERN_OBJ_P(zv) (php_writeconcern_fetch_object(Z_OBJ_P(zv))) #define Z_WRITECONCERNERROR_OBJ_P(zv) (php_writeconcernerror_fetch_object(Z_OBJ_P(zv))) #define Z_WRITEERROR_OBJ_P(zv) (php_writeerror_fetch_object(Z_OBJ_P(zv))) @@ -274,6 +279,7 @@ static inline php_phongo_topologyopeningevent_t* php_topologyopeningevent_fetch_ #define Z_OBJ_TOPOLOGYDESCRIPTION(zo) (php_topologydescription_fetch_object(zo)) #define Z_OBJ_BULKWRITE(zo) (php_bulkwrite_fetch_object(zo)) #define Z_OBJ_BULKWRITECOMMAND(zo) (php_bulkwritecommand_fetch_object(zo)) +#define Z_OBJ_BULKWRITECOMMANDRESULT(zo) (php_bulkwritecommandresult_fetch_object(zo)) #define Z_OBJ_WRITECONCERN(zo) (php_writeconcern_fetch_object(zo)) #define Z_OBJ_WRITECONCERNERROR(zo) (php_writeconcernerror_fetch_object(zo)) #define Z_OBJ_WRITEERROR(zo) (php_writeerror_fetch_object(zo)) @@ -322,6 +328,7 @@ extern zend_class_entry* php_phongo_session_ce; extern zend_class_entry* php_phongo_topologydescription_ce; extern zend_class_entry* php_phongo_bulkwrite_ce; extern zend_class_entry* php_phongo_bulkwritecommand_ce; +extern zend_class_entry* php_phongo_bulkwritecommandresult_ce; extern zend_class_entry* php_phongo_writeconcern_ce; extern zend_class_entry* php_phongo_writeconcernerror_ce; extern zend_class_entry* php_phongo_writeerror_ce; @@ -344,6 +351,7 @@ extern zend_class_entry* php_phongo_executiontimeoutexception_ce; extern zend_class_entry* php_phongo_connectiontimeoutexception_ce; extern zend_class_entry* php_phongo_writeexception_ce; extern zend_class_entry* php_phongo_bulkwriteexception_ce; +extern zend_class_entry* php_phongo_bulkwritecommandexception_ce; extern zend_class_entry* php_phongo_type_ce; extern zend_class_entry* php_phongo_persistable_ce; @@ -426,6 +434,7 @@ extern void php_phongo_utcdatetime_interface_init_ce(INIT_FUNC_ARGS); extern void php_phongo_bulkwrite_init_ce(INIT_FUNC_ARGS); extern void php_phongo_bulkwritecommand_init_ce(INIT_FUNC_ARGS); +extern void php_phongo_bulkwritecommandresult_init_ce(INIT_FUNC_ARGS); extern void php_phongo_clientencryption_init_ce(INIT_FUNC_ARGS); extern void php_phongo_command_init_ce(INIT_FUNC_ARGS); extern void php_phongo_cursor_init_ce(INIT_FUNC_ARGS); @@ -448,6 +457,7 @@ extern void php_phongo_cursor_interface_init_ce(INIT_FUNC_ARGS); extern void php_phongo_authenticationexception_init_ce(INIT_FUNC_ARGS); extern void php_phongo_bulkwriteexception_init_ce(INIT_FUNC_ARGS); +extern void php_phongo_bulkwritecommandexception_init_ce(INIT_FUNC_ARGS); extern void php_phongo_commandexception_init_ce(INIT_FUNC_ARGS); extern void php_phongo_connectionexception_init_ce(INIT_FUNC_ARGS); extern void php_phongo_connectiontimeoutexception_init_ce(INIT_FUNC_ARGS); diff --git a/src/phongo_structs.h b/src/phongo_structs.h index fba8b7782..534e07f63 100644 --- a/src/phongo_structs.h +++ b/src/phongo_structs.h @@ -51,6 +51,24 @@ typedef struct { zend_object std; } php_phongo_bulkwritecommand_t; +typedef struct { + bool is_acknowledged; + int64_t inserted_count; + int64_t matched_count; + int64_t modified_count; + int64_t upserted_count; + int64_t deleted_count; + bson_t* insert_results; + bson_t* update_results; + bson_t* delete_results; + bson_t* write_errors; + bson_t* write_concern_errors; + bson_t* error_reply; + zval manager; + uint32_t server_id; + zend_object std; +} php_phongo_bulkwritecommandresult_t; + typedef struct { mongoc_client_encryption_t* client_encryption; zval key_vault_client_manager; From 4c3d8461de33f9eb7b737091165bcf3f0a311c83 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Thu, 2 Jan 2025 16:49:24 -0500 Subject: [PATCH 04/13] Include missing header in WriteResult.h This was not necessary for compilation, but it makes the header internally consistent. --- src/MongoDB/WriteResult.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/MongoDB/WriteResult.h b/src/MongoDB/WriteResult.h index 1a7acd9f7..50125ef7e 100644 --- a/src/MongoDB/WriteResult.h +++ b/src/MongoDB/WriteResult.h @@ -21,6 +21,8 @@ #include +#include "phongo_structs.h" + php_phongo_writeresult_t* phongo_writeresult_init(zval* return_value, bson_t* reply, zval* manager, uint32_t server_id); #endif /* PHONGO_WRITERESULT_H */ From d21dadd1a5d5ca0c91ce069d68bf87a375447bca Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Thu, 2 Jan 2025 16:52:05 -0500 Subject: [PATCH 05/13] PHPC-2493: Manager and Server::executeBulkWriteCommand() --- src/MongoDB/BulkWriteCommand.c | 39 +++++++++---- src/MongoDB/BulkWriteCommand.h | 26 +++++++++ src/MongoDB/BulkWriteCommand.stub.php | 2 - src/MongoDB/BulkWriteCommand_arginfo.h | 8 +-- src/MongoDB/Manager.c | 36 ++++++++++++ src/MongoDB/Manager.stub.php | 2 + src/MongoDB/Manager_arginfo.h | 9 ++- src/MongoDB/Server.c | 24 ++++++++ src/MongoDB/Server.stub.php | 2 + src/MongoDB/Server_arginfo.h | 9 ++- src/phongo_execute.c | 79 ++++++++++++++++++++++++++ src/phongo_execute.h | 3 + 12 files changed, 217 insertions(+), 22 deletions(-) create mode 100644 src/MongoDB/BulkWriteCommand.h diff --git a/src/MongoDB/BulkWriteCommand.c b/src/MongoDB/BulkWriteCommand.c index 653fb29b3..022149534 100644 --- a/src/MongoDB/BulkWriteCommand.c +++ b/src/MongoDB/BulkWriteCommand.c @@ -34,6 +34,34 @@ zend_class_entry* php_phongo_bulkwritecommand_ce; +/* Creates a mongoc_bulkwriteopts_t from internal options, which should be freed + * by the caller. */ +mongoc_bulkwriteopts_t* phongo_bwc_assemble_opts(php_phongo_bulkwritecommand_t* intern) +{ + mongoc_bulkwriteopts_t* opts = mongoc_bulkwriteopts_new(); + + if (intern->bypass != PHONGO_BULKWRITECOMMAND_BYPASS_UNSET) { + mongoc_bulkwriteopts_set_bypassdocumentvalidation(opts, intern->bypass); + } + + if (intern->comment) { + mongoc_bulkwriteopts_set_comment(opts, intern->comment); + } + + if (intern->let) { + mongoc_bulkwriteopts_set_let(opts, intern->let); + } + + mongoc_bulkwriteopts_set_ordered(opts, intern->ordered); + mongoc_bulkwriteopts_set_verboseresults(opts, intern->verbose); + + if (intern->write_concern) { + mongoc_bulkwriteopts_set_writeconcern(opts, intern->write_concern); + } + + return opts; +} + // TODO: Make this a common utility function to share with BulkWrite.c /* Extracts the "_id" field of a BSON document into a return value. */ static void phongo_bwc_extract_id(bson_t* doc, zval** return_value) @@ -381,17 +409,6 @@ static PHP_METHOD(MongoDB_Driver_BulkWriteCommand, deleteOne) mongoc_bulkwrite_deleteoneopts_destroy(opts); } -static PHP_METHOD(MongoDB_Driver_BulkWriteCommand, execute) -{ - php_phongo_bulkwritecommand_t* intern; - - intern = Z_BULKWRITECOMMAND_OBJ_P(getThis()); - - PHONGO_PARSE_PARAMETERS_NONE(); - - RETURN_LONG(intern->num_ops); -} - static PHP_METHOD(MongoDB_Driver_BulkWriteCommand, insertOne) { php_phongo_bulkwritecommand_t* intern; diff --git a/src/MongoDB/BulkWriteCommand.h b/src/MongoDB/BulkWriteCommand.h new file mode 100644 index 000000000..d8c2cabfa --- /dev/null +++ b/src/MongoDB/BulkWriteCommand.h @@ -0,0 +1,26 @@ +/* + * Copyright 2024-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PHONGO_BULKWRITECOMMAND_H +#define PHONGO_BULKWRITECOMMAND_H + +#include "mongoc/mongoc.h" + +#include "phongo_structs.h" + +mongoc_bulkwriteopts_t* phongo_bwc_assemble_opts(php_phongo_bulkwritecommand_t* intern); + +#endif /* PHONGO_BULKWRITECOMMAND_H */ diff --git a/src/MongoDB/BulkWriteCommand.stub.php b/src/MongoDB/BulkWriteCommand.stub.php index d26f9f2ca..c8554917f 100644 --- a/src/MongoDB/BulkWriteCommand.stub.php +++ b/src/MongoDB/BulkWriteCommand.stub.php @@ -25,6 +25,4 @@ final public function replaceOne(string $namespace, array|object $filter, array| final public function updateOne(string $namespace, array|object $filter, array|object $update, ?array $options = null): void {} final public function updateMany(string $namespace, array|object $filter, array|object $update, ?array $options = null): void {} - - final public function execute(Manager|Server $managerOrServer): mixed {} } diff --git a/src/MongoDB/BulkWriteCommand_arginfo.h b/src/MongoDB/BulkWriteCommand_arginfo.h index 6663b5f34..1dde4d410 100644 --- a/src/MongoDB/BulkWriteCommand_arginfo.h +++ b/src/MongoDB/BulkWriteCommand_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: e2198c12dd76c9056a16ee6cb86b8af059fdcd6f */ + * Stub hash: 2af15e87d1d8095137d315218e688221ac2b5983 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_Driver_BulkWriteCommand___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") @@ -37,10 +37,6 @@ ZEND_END_ARG_INFO() #define arginfo_class_MongoDB_Driver_BulkWriteCommand_updateMany arginfo_class_MongoDB_Driver_BulkWriteCommand_updateOne -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_Driver_BulkWriteCommand_execute, 0, 1, IS_MIXED, 0) - ZEND_ARG_OBJ_TYPE_MASK(0, managerOrServer, MongoDB\\Driver\\Manager|MongoDB\\Driver\\Server, 0, NULL) -ZEND_END_ARG_INFO() - static ZEND_METHOD(MongoDB_Driver_BulkWriteCommand, __construct); static ZEND_METHOD(MongoDB_Driver_BulkWriteCommand, count); @@ -50,7 +46,6 @@ static ZEND_METHOD(MongoDB_Driver_BulkWriteCommand, insertOne); static ZEND_METHOD(MongoDB_Driver_BulkWriteCommand, replaceOne); static ZEND_METHOD(MongoDB_Driver_BulkWriteCommand, updateOne); static ZEND_METHOD(MongoDB_Driver_BulkWriteCommand, updateMany); -static ZEND_METHOD(MongoDB_Driver_BulkWriteCommand, execute); static const zend_function_entry class_MongoDB_Driver_BulkWriteCommand_methods[] = { @@ -62,7 +57,6 @@ static const zend_function_entry class_MongoDB_Driver_BulkWriteCommand_methods[] ZEND_ME(MongoDB_Driver_BulkWriteCommand, replaceOne, arginfo_class_MongoDB_Driver_BulkWriteCommand_replaceOne, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_BulkWriteCommand, updateOne, arginfo_class_MongoDB_Driver_BulkWriteCommand_updateOne, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_BulkWriteCommand, updateMany, arginfo_class_MongoDB_Driver_BulkWriteCommand_updateMany, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) - ZEND_ME(MongoDB_Driver_BulkWriteCommand, execute, arginfo_class_MongoDB_Driver_BulkWriteCommand_execute, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_FE_END }; diff --git a/src/MongoDB/Manager.c b/src/MongoDB/Manager.c index c61a68b9a..5d78e27bf 100644 --- a/src/MongoDB/Manager.c +++ b/src/MongoDB/Manager.c @@ -595,6 +595,42 @@ static PHP_METHOD(MongoDB_Driver_Manager, executeBulkWrite) } } +/* Executes a BulkWriteCommand (i.e. bulkWrite command for MongoDB 8.0+) */ +static PHP_METHOD(MongoDB_Driver_Manager, executeBulkWriteCommand) +{ + php_phongo_manager_t* intern; + zval* zbwc; + php_phongo_bulkwritecommand_t* bwc; + zval* zoptions = NULL; + uint32_t server_id = 0; + zval* zsession = NULL; + + PHONGO_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_OBJECT_OF_CLASS(zbwc, php_phongo_bulkwritecommand_ce) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL_OR_NULL(zoptions) + PHONGO_PARSE_PARAMETERS_END(); + + intern = Z_MANAGER_OBJ_P(getThis()); + bwc = Z_BULKWRITECOMMAND_OBJ_P(zbwc); + + if (!phongo_parse_session(zoptions, intern->client, NULL, &zsession)) { + /* Exception should already have been thrown */ + return; + } + + if (!php_phongo_manager_select_server(true, false, NULL, zsession, intern->client, &server_id)) { + /* Exception should already have been thrown */ + return; + } + + /* If the Server was created in a different process, reset the client so + * that its session pool is cleared. */ + PHONGO_RESET_CLIENT_IF_PID_DIFFERS(intern, intern); + + phongo_execute_bulkwritecommand(getThis(), bwc, zoptions, server_id, return_value); +} + /* Returns the autoEncryption.encryptedFieldsMap driver option */ static PHP_METHOD(MongoDB_Driver_Manager, getEncryptedFieldsMap) { diff --git a/src/MongoDB/Manager.stub.php b/src/MongoDB/Manager.stub.php index 7ebcd656a..f5e0388aa 100644 --- a/src/MongoDB/Manager.stub.php +++ b/src/MongoDB/Manager.stub.php @@ -18,6 +18,8 @@ final public function createClientEncryption(array $options): ClientEncryption { final public function executeBulkWrite(string $namespace, BulkWrite $bulk, array|WriteConcern|null $options = null): WriteResult {} + final public function executeBulkWriteCommand(BulkWriteCommand $bulkWriteCommand, ?array $options = null): ?BulkWriteCommandResult {} + final public function executeCommand(string $db, Command $command, array|ReadPreference|null $options = null): Cursor {} final public function executeQuery(string $namespace, Query $query, array|ReadPreference|null $options = null): Cursor {} diff --git a/src/MongoDB/Manager_arginfo.h b/src/MongoDB/Manager_arginfo.h index 714ac016e..eb4e54834 100644 --- a/src/MongoDB/Manager_arginfo.h +++ b/src/MongoDB/Manager_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: ce2904be644a230dc05dc1427f74c3717035eb06 */ + * Stub hash: 337181a092093d2fef515051672481636f676f3c */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_Driver_Manager___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, uri, IS_STRING, 1, "null") @@ -21,6 +21,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_Manager_exec ZEND_ARG_OBJ_TYPE_MASK(0, options, MongoDB\\Driver\\WriteConcern, MAY_BE_ARRAY|MAY_BE_NULL, "null") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_Manager_executeBulkWriteCommand, 0, 1, MongoDB\\Driver\\BulkWriteCommandResult, 1) + ZEND_ARG_OBJ_INFO(0, bulkWriteCommand, MongoDB\\Driver\\BulkWriteCommand, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_Manager_executeCommand, 0, 2, MongoDB\\Driver\\Cursor, 0) ZEND_ARG_TYPE_INFO(0, db, IS_STRING, 0) ZEND_ARG_OBJ_INFO(0, command, MongoDB\\Driver\\Command, 0) @@ -73,6 +78,7 @@ static ZEND_METHOD(MongoDB_Driver_Manager, __construct); static ZEND_METHOD(MongoDB_Driver_Manager, addSubscriber); static ZEND_METHOD(MongoDB_Driver_Manager, createClientEncryption); static ZEND_METHOD(MongoDB_Driver_Manager, executeBulkWrite); +static ZEND_METHOD(MongoDB_Driver_Manager, executeBulkWriteCommand); static ZEND_METHOD(MongoDB_Driver_Manager, executeCommand); static ZEND_METHOD(MongoDB_Driver_Manager, executeQuery); static ZEND_METHOD(MongoDB_Driver_Manager, executeReadCommand); @@ -93,6 +99,7 @@ static const zend_function_entry class_MongoDB_Driver_Manager_methods[] = { ZEND_ME(MongoDB_Driver_Manager, addSubscriber, arginfo_class_MongoDB_Driver_Manager_addSubscriber, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_Manager, createClientEncryption, arginfo_class_MongoDB_Driver_Manager_createClientEncryption, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_Manager, executeBulkWrite, arginfo_class_MongoDB_Driver_Manager_executeBulkWrite, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) + ZEND_ME(MongoDB_Driver_Manager, executeBulkWriteCommand, arginfo_class_MongoDB_Driver_Manager_executeBulkWriteCommand, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_Manager, executeCommand, arginfo_class_MongoDB_Driver_Manager_executeCommand, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_Manager, executeQuery, arginfo_class_MongoDB_Driver_Manager_executeQuery, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_Manager, executeReadCommand, arginfo_class_MongoDB_Driver_Manager_executeReadCommand, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) diff --git a/src/MongoDB/Server.c b/src/MongoDB/Server.c index e500c7b16..9244e7bb2 100644 --- a/src/MongoDB/Server.c +++ b/src/MongoDB/Server.c @@ -215,6 +215,30 @@ static PHP_METHOD(MongoDB_Driver_Server, executeBulkWrite) } } +/* Executes a BulkWriteCommand (i.e. bulkWrite command for MongoDB 8.0+) */ +static PHP_METHOD(MongoDB_Driver_Server, executeBulkWriteCommand) +{ + php_phongo_server_t* intern; + zval* zbwc; + php_phongo_bulkwritecommand_t* bwc; + zval* zoptions = NULL; + + PHONGO_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_OBJECT_OF_CLASS(zbwc, php_phongo_bulkwritecommand_ce) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL_OR_NULL(zoptions) + PHONGO_PARSE_PARAMETERS_END(); + + intern = Z_SERVER_OBJ_P(getThis()); + bwc = Z_BULKWRITECOMMAND_OBJ_P(zbwc); + + /* If the Server was created in a different process, reset the client so + * that its session pool is cleared. */ + PHONGO_RESET_CLIENT_IF_PID_DIFFERS(intern, Z_MANAGER_OBJ_P(&intern->manager)); + + phongo_execute_bulkwritecommand(&intern->manager, bwc, zoptions, intern->server_id, return_value); +} + /* Returns the hostname for this Server */ static PHP_METHOD(MongoDB_Driver_Server, getHost) { diff --git a/src/MongoDB/Server.stub.php b/src/MongoDB/Server.stub.php index 549fe4ef8..b3bc6e5dc 100644 --- a/src/MongoDB/Server.stub.php +++ b/src/MongoDB/Server.stub.php @@ -74,6 +74,8 @@ final private function __construct() {} final public function executeBulkWrite(string $namespace, BulkWrite $bulkWrite, array|WriteConcern|null $options = null): WriteResult {} + final public function executeBulkWriteCommand(BulkWriteCommand $bulkWriteCommand, ?array $options = null): ?BulkWriteCommandResult {} + final public function executeCommand(string $db, Command $command, array|ReadPreference|null $options = null): Cursor {} final public function executeQuery(string $namespace, Query $query, array|ReadPreference|null $options = null): Cursor {} diff --git a/src/MongoDB/Server_arginfo.h b/src/MongoDB/Server_arginfo.h index 701186aad..e5e79b13c 100644 --- a/src/MongoDB/Server_arginfo.h +++ b/src/MongoDB/Server_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 5edd7242100364c5e4beddc89b4d92dda70c09f6 */ + * Stub hash: f7941440babc2f1adfe3cd8bb811abfa033c1b37 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_Driver_Server___construct, 0, 0, 0) ZEND_END_ARG_INFO() @@ -10,6 +10,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_Server_execu ZEND_ARG_OBJ_TYPE_MASK(0, options, MongoDB\\Driver\\WriteConcern, MAY_BE_ARRAY|MAY_BE_NULL, "null") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_Server_executeBulkWriteCommand, 0, 1, MongoDB\\Driver\\BulkWriteCommandResult, 1) + ZEND_ARG_OBJ_INFO(0, bulkWriteCommand, MongoDB\\Driver\\BulkWriteCommand, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_Server_executeCommand, 0, 2, MongoDB\\Driver\\Cursor, 0) ZEND_ARG_TYPE_INFO(0, db, IS_STRING, 0) ZEND_ARG_OBJ_INFO(0, command, MongoDB\\Driver\\Command, 0) @@ -65,6 +70,7 @@ ZEND_END_ARG_INFO() static ZEND_METHOD(MongoDB_Driver_Server, __construct); static ZEND_METHOD(MongoDB_Driver_Server, executeBulkWrite); +static ZEND_METHOD(MongoDB_Driver_Server, executeBulkWriteCommand); static ZEND_METHOD(MongoDB_Driver_Server, executeCommand); static ZEND_METHOD(MongoDB_Driver_Server, executeQuery); static ZEND_METHOD(MongoDB_Driver_Server, executeReadCommand); @@ -87,6 +93,7 @@ static ZEND_METHOD(MongoDB_Driver_Server, isSecondary); static const zend_function_entry class_MongoDB_Driver_Server_methods[] = { ZEND_ME(MongoDB_Driver_Server, __construct, arginfo_class_MongoDB_Driver_Server___construct, ZEND_ACC_PRIVATE|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_Server, executeBulkWrite, arginfo_class_MongoDB_Driver_Server_executeBulkWrite, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) + ZEND_ME(MongoDB_Driver_Server, executeBulkWriteCommand, arginfo_class_MongoDB_Driver_Server_executeBulkWriteCommand, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_Server, executeCommand, arginfo_class_MongoDB_Driver_Server_executeCommand, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_Server, executeQuery, arginfo_class_MongoDB_Driver_Server_executeQuery, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_Server, executeReadCommand, arginfo_class_MongoDB_Driver_Server_executeReadCommand, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) diff --git a/src/phongo_execute.c b/src/phongo_execute.c index 848b69edd..1850d769f 100644 --- a/src/phongo_execute.c +++ b/src/phongo_execute.c @@ -27,6 +27,8 @@ #include "phongo_execute.h" #include "phongo_util.h" +#include "MongoDB/BulkWriteCommand.h" +#include "MongoDB/BulkWriteCommandResult.h" #include "MongoDB/Cursor.h" #include "MongoDB/ReadPreference.h" #include "MongoDB/Session.h" @@ -333,6 +335,83 @@ bool phongo_execute_bulk_write(zval* manager, const char* namespace, php_phongo_ return success; } +bool phongo_execute_bulkwritecommand(zval* manager, php_phongo_bulkwritecommand_t* bwc, zval* zoptions, uint32_t server_id, zval* return_value) +{ + mongoc_client_t* client = NULL; + mongoc_bulkwrite_t* bw = bwc->bw; + mongoc_bulkwriteopts_t* bw_opts = NULL; + mongoc_bulkwritereturn_t bw_ret = { 0 }; + php_phongo_bulkwritecommandresult_t* bwcr; + zval* zsession = NULL; + bool success = true; + + client = Z_MANAGER_OBJ_P(manager)->client; + + if (!phongo_parse_session(zoptions, client, NULL, &zsession)) { + /* Exception should already have been thrown */ + return false; + } + + mongoc_bulkwrite_set_client(bw, client); + + bw_opts = phongo_bwc_assemble_opts(bwc); + mongoc_bulkwriteopts_set_serverid(bw_opts, server_id); + + if (zsession) { + mongoc_bulkwrite_set_session(bw, Z_SESSION_OBJ_P(zsession)->client_session); + /* Save a reference to the session on the class struct to avoid leaving + * a dangling pointer within mongoc_bulkwrite_t. */ + ZVAL_ZVAL(&bwc->session, zsession, 1, 0); + } + + bw_ret = mongoc_bulkwrite_execute(bw, bw_opts); + + bwcr = phongo_bulkwritecommandresult_init(return_value, &bw_ret, manager); + + if (bw_ret.exc) { + success = false; + bson_error_t error = { 0 }; + + // Check if there is a top-level error + if (mongoc_bulkwriteexception_error(bw_ret.exc, &error)) { + phongo_throw_exception_from_bson_error_t_and_reply(&error, mongoc_bulkwriteexception_errorreply(bw_ret.exc)); + } + + /* Unlike mongoc_bulk_operation_execute, mongoc_bulkwrite_execute may + * report COMMAND_INVALID_ARG alongside a partial result (CDRIVER-5842). + * If there is no result, we can throw InvalidArgumentException without + * proxying it behind a BulkWriteException. */ + if (!bw_ret.res && error.domain == MONGOC_ERROR_COMMAND && error.code == MONGOC_ERROR_COMMAND_INVALID_ARG) { + // TODO: Do we care about other mongoc_bulkwriteexception_t fields? + goto cleanup; + } + + if (EG(exception)) { + char* message; + + (void) spprintf(&message, 0, "Bulk write failed due to previous %s: %s", PHONGO_ZVAL_EXCEPTION_NAME(EG(exception)), error.message); + zend_throw_exception(php_phongo_bulkwritecommandexception_ce, message, 0); + efree(message); + } else { + // TODO: Determine appropriate message w/o top-level error + zend_throw_exception(php_phongo_bulkwritecommandexception_ce, "Bulk write failed", 0); + } + + /* Ensure error labels are added to the final BulkWriteCommandException. If a + * previous exception was also thrown, error labels will already have + * been added by phongo_throw_exception_from_bson_error_t_and_reply. */ + phongo_exception_add_error_labels(mongoc_bulkwriteexception_errorreply(bw_ret.exc)); + phongo_add_exception_prop(ZEND_STRL("bulkWriteCommandResult"), return_value); + } + +cleanup: + mongoc_bulkwriteopts_destroy(bw_opts); + mongoc_bulkwriteresult_destroy(bw_ret.res); + mongoc_bulkwriteexception_destroy(bw_ret.exc); + + return success; +} + bool phongo_execute_command(zval* manager, php_phongo_command_type_t type, const char* db, zval* zcommand, zval* options, uint32_t server_id, zval* return_value) { mongoc_client_t* client; diff --git a/src/phongo_execute.h b/src/phongo_execute.h index 47312b9e7..b7307828b 100644 --- a/src/phongo_execute.h +++ b/src/phongo_execute.h @@ -22,6 +22,8 @@ #include +#include "phongo_structs.h" + /* This enum is used for processing options and selecting a libmongoc function * to use in phongo_execute_command. The values are important, as READ and WRITE * are also used as a bit field to determine whether readPreference, @@ -37,6 +39,7 @@ typedef enum { } php_phongo_command_type_t; bool phongo_execute_bulk_write(zval* manager, const char* namespace, php_phongo_bulkwrite_t* bulk_write, zval* zwriteConcern, uint32_t server_id, zval* return_value); +bool phongo_execute_bulkwritecommand(zval* manager, php_phongo_bulkwritecommand_t* bwc, zval* zoptions, uint32_t server_id, zval* return_value); bool phongo_execute_command(zval* manager, php_phongo_command_type_t type, const char* db, zval* zcommand, zval* zreadPreference, uint32_t server_id, zval* return_value); bool phongo_execute_query(zval* manager, const char* namespace, zval* zquery, zval* zreadPreference, uint32_t server_id, zval* return_value); From 95fa6ccd674e89673dcb2b48ab750769cd767409 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Thu, 16 Jan 2025 13:50:30 -0500 Subject: [PATCH 06/13] PHPC-2494: Handle null error fields in BulkWriteCommandResult Always return arrays for writeErrors and writeConcernErrors --- src/MongoDB/BulkWriteCommandResult.c | 42 +++++++++++--------- src/MongoDB/BulkWriteCommandResult.stub.php | 4 +- src/MongoDB/BulkWriteCommandResult_arginfo.h | 8 ++-- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/MongoDB/BulkWriteCommandResult.c b/src/MongoDB/BulkWriteCommandResult.c index 9c61197f8..e060e3b5d 100644 --- a/src/MongoDB/BulkWriteCommandResult.c +++ b/src/MongoDB/BulkWriteCommandResult.c @@ -45,6 +45,10 @@ static bool php_phongo_bulkwritecommandresult_get_writeconcernerrors(php_phongo_ array_init(return_value); + if (!intern->write_concern_errors) { + return true; + } + for (bson_iter_init(&iter, intern->write_concern_errors); bson_iter_next(&iter);) { bson_t bson; uint32_t len; @@ -78,6 +82,10 @@ static bool php_phongo_bulkwritecommandresult_get_writeerrors(php_phongo_bulkwri array_init(return_value); + if (!intern->write_errors) { + return true; + } + for (bson_iter_init(&iter, intern->write_errors); bson_iter_next(&iter);) { bson_t bson; uint32_t len; @@ -246,7 +254,6 @@ static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, getWriteConcernErrors) PHONGO_PARSE_PARAMETERS_NONE(); - // TODO: null handling php_phongo_bulkwritecommandresult_get_writeconcernerrors(intern, return_value); } @@ -259,7 +266,6 @@ static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, getWriteErrors) PHONGO_PARSE_PARAMETERS_NONE(); - // TODO: null handling php_phongo_bulkwritecommandresult_get_writeerrors(intern, return_value); } @@ -271,8 +277,9 @@ static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, getErrorReply) PHONGO_PARSE_PARAMETERS_NONE(); - // TODO: null handling - phongo_document_new(return_value, intern->error_reply, true); + if (intern->error_reply) { + phongo_document_new(return_value, intern->error_reply, true); + } } /* Returns whether the write operation was acknowledged (based on the write @@ -364,22 +371,18 @@ static HashTable* php_phongo_bulkwritecommandresult_get_debug_info(zend_object* ADD_ASSOC_NULL_EX(&retval, "deleteResults"); } - if (intern->write_errors) { + { zval writeerrors; php_phongo_bulkwritecommandresult_get_writeerrors(intern, &writeerrors); ADD_ASSOC_ZVAL_EX(&retval, "writeErrors", &writeerrors); - } else { - ADD_ASSOC_NULL_EX(&retval, "writeErrors"); } - if (intern->write_concern_errors) { + { zval writeconcernerrors; php_phongo_bulkwritecommandresult_get_writeconcernerrors(intern, &writeconcernerrors); ADD_ASSOC_ZVAL_EX(&retval, "writeConcernErrors", &writeconcernerrors); - } else { - ADD_ASSOC_NULL_EX(&retval, "writeConcernErrors"); } if (intern->error_reply) { @@ -416,6 +419,11 @@ void php_phongo_bulkwritecommandresult_init_ce(INIT_FUNC_ARGS) php_phongo_handler_bulkwritecommandresult.offset = XtOffsetOf(php_phongo_bulkwritecommandresult_t, std); } +static inline bson_t* _bson_copy_or_null (const bson_t* bson) +{ + return bson_empty0(bson) ? NULL : bson_copy(bson); +} + php_phongo_bulkwritecommandresult_t* phongo_bulkwritecommandresult_init(zval* return_value, mongoc_bulkwritereturn_t* bw_ret, zval* manager) { php_phongo_bulkwritecommandresult_t* bwcr; @@ -433,20 +441,18 @@ php_phongo_bulkwritecommandresult_t* phongo_bulkwritecommandresult_init(zval* re bwcr->modified_count = mongoc_bulkwriteresult_modifiedcount(bw_ret->res); bwcr->deleted_count = mongoc_bulkwriteresult_deletedcount(bw_ret->res); -#define BSON_COPY_OR_NULL(bson) ((bson) ? bson_copy(bson) : NULL) - bwcr->insert_results = BSON_COPY_OR_NULL(mongoc_bulkwriteresult_insertresults(bw_ret->res)); - bwcr->update_results = BSON_COPY_OR_NULL(mongoc_bulkwriteresult_updateresults(bw_ret->res)); - bwcr->delete_results = BSON_COPY_OR_NULL(mongoc_bulkwriteresult_deleteresults(bw_ret->res)); -#undef BSON_COPY_OR_NULL + bwcr->insert_results = _bson_copy_or_null(mongoc_bulkwriteresult_insertresults(bw_ret->res)); + bwcr->update_results = _bson_copy_or_null(mongoc_bulkwriteresult_updateresults(bw_ret->res)); + bwcr->delete_results = _bson_copy_or_null(mongoc_bulkwriteresult_deleteresults(bw_ret->res)); bwcr->server_id = mongoc_bulkwriteresult_serverid(bw_ret->res); } // Copy mongoc_bulkwriteexception_t fields if (bw_ret->exc) { - bwcr->error_reply = bson_copy(mongoc_bulkwriteexception_errorreply(bw_ret->exc)); - bwcr->write_errors = bson_copy(mongoc_bulkwriteexception_writeerrors(bw_ret->exc)); - bwcr->write_concern_errors = bson_copy(mongoc_bulkwriteexception_writeconcernerrors(bw_ret->exc)); + bwcr->error_reply = _bson_copy_or_null(mongoc_bulkwriteexception_errorreply(bw_ret->exc)); + bwcr->write_errors = _bson_copy_or_null(mongoc_bulkwriteexception_writeerrors(bw_ret->exc)); + bwcr->write_concern_errors = _bson_copy_or_null(mongoc_bulkwriteexception_writeconcernerrors(bw_ret->exc)); } ZVAL_ZVAL(&bwcr->manager, manager, 1, 0); diff --git a/src/MongoDB/BulkWriteCommandResult.stub.php b/src/MongoDB/BulkWriteCommandResult.stub.php index e316ee8b1..ca5a82f67 100644 --- a/src/MongoDB/BulkWriteCommandResult.stub.php +++ b/src/MongoDB/BulkWriteCommandResult.stub.php @@ -30,9 +30,9 @@ final public function getUpdateResults(): ?\MongoDB\BSON\Document {} final public function getDeleteResults(): ?\MongoDB\BSON\Document {} - final public function getWriteErrors(): ?\MongoDB\BSON\Document {} + final public function getWriteErrors(): array {} - final public function getWriteConcernErrors(): ?\MongoDB\BSON\PackedArray {} + final public function getWriteConcernErrors(): array {} final public function getErrorReply(): ?\MongoDB\BSON\Document {} diff --git a/src/MongoDB/BulkWriteCommandResult_arginfo.h b/src/MongoDB/BulkWriteCommandResult_arginfo.h index 6fa406da2..1856afbe8 100644 --- a/src/MongoDB/BulkWriteCommandResult_arginfo.h +++ b/src/MongoDB/BulkWriteCommandResult_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d69e0d25638cc7f95b04d3cf0e4749838c6ddca8 */ + * Stub hash: d14704cc61cbe0ed62906e92a9036fbbd5e228d8 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_Driver_BulkWriteCommandResult___construct, 0, 0, 0) ZEND_END_ARG_INFO() @@ -25,11 +25,11 @@ ZEND_END_ARG_INFO() #define arginfo_class_MongoDB_Driver_BulkWriteCommandResult_getDeleteResults arginfo_class_MongoDB_Driver_BulkWriteCommandResult_getInsertResults -#define arginfo_class_MongoDB_Driver_BulkWriteCommandResult_getWriteErrors arginfo_class_MongoDB_Driver_BulkWriteCommandResult_getInsertResults - -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_BulkWriteCommandResult_getWriteConcernErrors, 0, 0, MongoDB\\BSON\\PackedArray, 1) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_Driver_BulkWriteCommandResult_getWriteErrors, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() +#define arginfo_class_MongoDB_Driver_BulkWriteCommandResult_getWriteConcernErrors arginfo_class_MongoDB_Driver_BulkWriteCommandResult_getWriteErrors + #define arginfo_class_MongoDB_Driver_BulkWriteCommandResult_getErrorReply arginfo_class_MongoDB_Driver_BulkWriteCommandResult_getInsertResults ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_Driver_BulkWriteCommandResult_isAcknowledged, 0, 0, _IS_BOOL, 0) From a430e1f81e43c48bfb8e9176c165dc43db64bd47 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Thu, 16 Jan 2025 13:51:33 -0500 Subject: [PATCH 07/13] PHPC-2494: Return writeErrors as an associative array --- src/MongoDB/BulkWriteCommandResult.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/MongoDB/BulkWriteCommandResult.c b/src/MongoDB/BulkWriteCommandResult.c index e060e3b5d..9b42eaf91 100644 --- a/src/MongoDB/BulkWriteCommandResult.c +++ b/src/MongoDB/BulkWriteCommandResult.c @@ -19,6 +19,7 @@ #include #include +#include #include "php_array_api.h" @@ -91,6 +92,7 @@ static bool php_phongo_bulkwritecommandresult_get_writeerrors(php_phongo_bulkwri uint32_t len; const uint8_t* data; zval write_error; + zend_ulong index; if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) { continue; @@ -102,12 +104,14 @@ static bool php_phongo_bulkwritecommandresult_get_writeerrors(php_phongo_bulkwri continue; } - if (!phongo_writeerror_init_ex(&write_error, &bson, atoi(bson_iter_key(&iter)))) { + index = (zend_ulong) ZEND_STRTOUL(bson_iter_key(&iter), NULL, 10); + + if (!phongo_writeerror_init_ex(&write_error, &bson, (int32_t) index)) { zval_ptr_dtor(&write_error); continue; } - add_next_index_zval(return_value, &write_error); + add_index_zval(return_value, index, &write_error); } return true; From 3c7a65a31882a16533dcd938c008c71e7a989834 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Thu, 16 Jan 2025 14:34:27 -0500 Subject: [PATCH 08/13] PHPC-2493: Revise error handling for mongoc_bulkwrite_execute --- src/phongo_execute.c | 45 ++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/src/phongo_execute.c b/src/phongo_execute.c index 1850d769f..f5cb54533 100644 --- a/src/phongo_execute.c +++ b/src/phongo_execute.c @@ -368,21 +368,42 @@ bool phongo_execute_bulkwritecommand(zval* manager, php_phongo_bulkwritecommand_ bwcr = phongo_bulkwritecommandresult_init(return_value, &bw_ret, manager); + /* Error handling for mongoc_bulkwrite_execute differs significantly from + * mongoc_bulk_operation_execute. + * + * - There may or may not be a top-level error. Top-level errors include + * both logical errors (invalid arguments) and runtime errors (e.g. server + * selection failure). A bulk write fails due to write or write concern + * errors will typically not have a top-level error. + * + * - There may or may not be an error reply document. This document could be + * the response of a failed bulkWrite command, but it may also originate + * from libmongoc (e.g. server selection, appending a session, iterating + * BSON). This function only uses it to extrapolate error labels and it is + * otherwise accessible to the user through BulkWriteCommandResult. + * + * - InvalidArgumentException may be thrown directly for a basic top-level + * error (assuming BulkWriteCommandResult would also be irrelevant). + * Otherwise, BulkWriteCommandException is thrown with an attached + * BulkWriteCommandResult that collects any error reply, write errors, and + * write concern errors, along with a possible partial write result. + */ if (bw_ret.exc) { success = false; bson_error_t error = { 0 }; + const bson_t *error_reply = mongoc_bulkwriteexception_errorreply(bw_ret.exc); - // Check if there is a top-level error + // Consult any top-level error to throw the first exception if (mongoc_bulkwriteexception_error(bw_ret.exc, &error)) { - phongo_throw_exception_from_bson_error_t_and_reply(&error, mongoc_bulkwriteexception_errorreply(bw_ret.exc)); + phongo_throw_exception_from_bson_error_t_and_reply(&error, error_reply); } /* Unlike mongoc_bulk_operation_execute, mongoc_bulkwrite_execute may - * report COMMAND_INVALID_ARG alongside a partial result (CDRIVER-5842). - * If there is no result, we can throw InvalidArgumentException without - * proxying it behind a BulkWriteException. */ - if (!bw_ret.res && error.domain == MONGOC_ERROR_COMMAND && error.code == MONGOC_ERROR_COMMAND_INVALID_ARG) { - // TODO: Do we care about other mongoc_bulkwriteexception_t fields? + * report MONGOC_ERROR_COMMAND_INVALID_ARG alongside a partial result + * (CDRIVER-5842). Throw InvalidArgumentException directly iff there is + * neither a partial write result nor an error reply (we can assume + * there are no write or write concern errors for this case). */ + if (EG(exception) && EG(exception)->ce == php_phongo_invalidargumentexception_ce && !bw_ret.res && !error_reply) { goto cleanup; } @@ -393,14 +414,14 @@ bool phongo_execute_bulkwritecommand(zval* manager, php_phongo_bulkwritecommand_ zend_throw_exception(php_phongo_bulkwritecommandexception_ce, message, 0); efree(message); } else { - // TODO: Determine appropriate message w/o top-level error zend_throw_exception(php_phongo_bulkwritecommandexception_ce, "Bulk write failed", 0); } - /* Ensure error labels are added to the final BulkWriteCommandException. If a - * previous exception was also thrown, error labels will already have - * been added by phongo_throw_exception_from_bson_error_t_and_reply. */ - phongo_exception_add_error_labels(mongoc_bulkwriteexception_errorreply(bw_ret.exc)); + /* Ensure error labels are added to the final BulkWriteCommandException. + * If RuntimeException was previously thrown, labels may also have been + * added to it by phongo_throw_exception_from_bson_error_t_and_reply. */ + phongo_exception_add_error_labels(error_reply); + phongo_add_exception_prop(ZEND_STRL("bulkWriteCommandResult"), return_value); } From 61912eda06522436e2e6e2355b85ec7aeb60b516 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Fri, 17 Jan 2025 15:50:04 -0500 Subject: [PATCH 09/13] Consistent WriteError and WriteConcernError parsing for bulk write results --- src/MongoDB/BulkWriteCommandResult.c | 99 ++++++++++++++-------------- src/MongoDB/WriteConcernError.c | 14 ++-- src/MongoDB/WriteConcernError.h | 2 +- src/MongoDB/WriteError.c | 14 +++- src/MongoDB/WriteError.h | 4 +- src/MongoDB/WriteResult.c | 21 ++++-- 6 files changed, 89 insertions(+), 65 deletions(-) diff --git a/src/MongoDB/BulkWriteCommandResult.c b/src/MongoDB/BulkWriteCommandResult.c index 9b42eaf91..a3e534cf1 100644 --- a/src/MongoDB/BulkWriteCommandResult.c +++ b/src/MongoDB/BulkWriteCommandResult.c @@ -40,78 +40,81 @@ zend_class_entry* php_phongo_bulkwritecommandresult_ce; -static bool php_phongo_bulkwritecommandresult_get_writeconcernerrors(php_phongo_bulkwritecommandresult_t* intern, zval* return_value) +/* Populates return_value with a list of WriteConcernError objects. Returns true + * on success; otherwise, false is returned and an exception is thrown. */ +static bool phongo_bulkwritecommandresult_get_writeconcernerrors(php_phongo_bulkwritecommandresult_t* intern, zval* return_value) { bson_iter_t iter; array_init(return_value); - if (!intern->write_concern_errors) { - return true; - } + if (intern->write_concern_errors && bson_iter_init(&iter, intern->write_concern_errors)) { + while (bson_iter_next(&iter)) { + bson_t bson; + uint32_t len; + const uint8_t* data; + zval write_concern_error; - for (bson_iter_init(&iter, intern->write_concern_errors); bson_iter_next(&iter);) { - bson_t bson; - uint32_t len; - const uint8_t* data; - zval write_concern_error; + if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) { + continue; + } - if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) { - continue; - } + bson_iter_document(&iter, &len, &data); - bson_iter_document(&iter, &len, &data); + if (!bson_init_static(&bson, data, len)) { + continue; + } - if (!bson_init_static(&bson, data, len)) { - continue; - } + if (!phongo_writeconcernerror_init(&write_concern_error, &bson)) { + /* Exception already thrown */ + zval_ptr_dtor(&write_concern_error); + return false; + } - if (!phongo_writeconcernerror_init(&write_concern_error, &bson)) { - zval_ptr_dtor(&write_concern_error); - continue; + add_next_index_zval(return_value, &write_concern_error); } - - add_next_index_zval(return_value, &write_concern_error); } return true; } -static bool php_phongo_bulkwritecommandresult_get_writeerrors(php_phongo_bulkwritecommandresult_t* intern, zval* return_value) +/* Populates return_value with a map of WriteError objects indexed by the offset + * of the corresponding operation. Returns true on success; otherwise, false is + * returned and an exception is thrown. */ +static bool phongo_bulkwritecommandresult_get_writeerrors(php_phongo_bulkwritecommandresult_t* intern, zval* return_value) { bson_iter_t iter; array_init(return_value); - if (!intern->write_errors) { - return true; - } + if (intern->write_errors && bson_iter_init(&iter, intern->write_errors)) { + while (bson_iter_next(&iter)) { + bson_t bson; + uint32_t len; + const uint8_t* data; + zval write_error; + zend_ulong index; - for (bson_iter_init(&iter, intern->write_errors); bson_iter_next(&iter);) { - bson_t bson; - uint32_t len; - const uint8_t* data; - zval write_error; - zend_ulong index; + if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) { + continue; + } - if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) { - continue; - } + bson_iter_document(&iter, &len, &data); - bson_iter_document(&iter, &len, &data); + if (!bson_init_static(&bson, data, len)) { + continue; + } - if (!bson_init_static(&bson, data, len)) { - continue; - } + index = (zend_ulong) ZEND_STRTOUL(bson_iter_key(&iter), NULL, 10); - index = (zend_ulong) ZEND_STRTOUL(bson_iter_key(&iter), NULL, 10); + if (!phongo_writeerror_init_ex(&write_error, &bson, (int32_t) index)) { + /* Exception already thrown */ + zval_ptr_dtor(&write_error); + return false; + } - if (!phongo_writeerror_init_ex(&write_error, &bson, (int32_t) index)) { - zval_ptr_dtor(&write_error); - continue; + add_index_zval(return_value, index, &write_error); } - - add_index_zval(return_value, index, &write_error); } return true; @@ -258,7 +261,7 @@ static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, getWriteConcernErrors) PHONGO_PARSE_PARAMETERS_NONE(); - php_phongo_bulkwritecommandresult_get_writeconcernerrors(intern, return_value); + phongo_bulkwritecommandresult_get_writeconcernerrors(intern, return_value); } /* Returns any write errors that occurred */ @@ -270,7 +273,7 @@ static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, getWriteErrors) PHONGO_PARSE_PARAMETERS_NONE(); - php_phongo_bulkwritecommandresult_get_writeerrors(intern, return_value); + phongo_bulkwritecommandresult_get_writeerrors(intern, return_value); } static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, getErrorReply) @@ -378,14 +381,14 @@ static HashTable* php_phongo_bulkwritecommandresult_get_debug_info(zend_object* { zval writeerrors; - php_phongo_bulkwritecommandresult_get_writeerrors(intern, &writeerrors); + phongo_bulkwritecommandresult_get_writeerrors(intern, &writeerrors); ADD_ASSOC_ZVAL_EX(&retval, "writeErrors", &writeerrors); } { zval writeconcernerrors; - php_phongo_bulkwritecommandresult_get_writeconcernerrors(intern, &writeconcernerrors); + phongo_bulkwritecommandresult_get_writeconcernerrors(intern, &writeconcernerrors); ADD_ASSOC_ZVAL_EX(&retval, "writeConcernErrors", &writeconcernerrors); } diff --git a/src/MongoDB/WriteConcernError.c b/src/MongoDB/WriteConcernError.c index 9624d5b0e..76f395b87 100644 --- a/src/MongoDB/WriteConcernError.c +++ b/src/MongoDB/WriteConcernError.c @@ -133,7 +133,13 @@ void php_phongo_writeconcernerror_init_ce(INIT_FUNC_ARGS) php_phongo_handler_writeconcernerror.offset = XtOffsetOf(php_phongo_writeconcernerror_t, std); } -zend_bool phongo_writeconcernerror_init(zval* return_value, bson_t* bson) +/* Initializes a new WriteConcernError in return_value using the BSON document. + * Returns true on success; otherwise, false is returned and an exception is + * thrown. + * + * This function supports documents from both mongoc_bulk_operation_execute and + * mongoc_bulkwriteexception_t (returned by mongoc_bulkwrite_execute). */ +bool phongo_writeconcernerror_init(zval* return_value, const bson_t* bson) { bson_iter_t iter; php_phongo_writeconcernerror_t* intern; @@ -150,10 +156,10 @@ zend_bool phongo_writeconcernerror_init(zval* return_value, bson_t* bson) // Additionally check for field name used by mongoc_bulkwriteexception_t if ((bson_iter_init_find(&iter, bson, "errmsg") && BSON_ITER_HOLDS_UTF8(&iter)) || (bson_iter_init_find(&iter, bson, "message") && BSON_ITER_HOLDS_UTF8(&iter))) { - uint32_t errmsg_len; - const char* err_msg = bson_iter_utf8(&iter, &errmsg_len); + uint32_t len; + const char* message = bson_iter_utf8(&iter, &len); - intern->message = estrndup(err_msg, errmsg_len); + intern->message = estrndup(message, len); } // Additionally check for field name used by mongoc_bulkwriteexception_t diff --git a/src/MongoDB/WriteConcernError.h b/src/MongoDB/WriteConcernError.h index f5f632bfb..0801543de 100644 --- a/src/MongoDB/WriteConcernError.h +++ b/src/MongoDB/WriteConcernError.h @@ -21,6 +21,6 @@ #include -zend_bool phongo_writeconcernerror_init(zval* return_value, bson_t* bson); +bool phongo_writeconcernerror_init(zval* return_value, const bson_t* bson); #endif /* PHONGO_WRITECONCERNERROR_H */ diff --git a/src/MongoDB/WriteError.c b/src/MongoDB/WriteError.c index 5befa1d2e..46d2e580a 100644 --- a/src/MongoDB/WriteError.c +++ b/src/MongoDB/WriteError.c @@ -143,12 +143,19 @@ void php_phongo_writeerror_init_ce(INIT_FUNC_ARGS) php_phongo_handler_writeerror.offset = XtOffsetOf(php_phongo_writeerror_t, std); } -zend_bool phongo_writeerror_init(zval* return_value, bson_t* bson) +bool phongo_writeerror_init(zval* return_value, const bson_t* bson) { return phongo_writeerror_init_ex(return_value, bson, 0); } -zend_bool phongo_writeerror_init_ex(zval* return_value, bson_t* bson, int32_t index) +/* Initializes a new WriteError in return_value using the BSON document. Returns + * true on success; otherwise, false is returned and an exception is thrown. + * + * This function supports documents from both mongoc_bulk_operation_execute and + * mongoc_bulkwriteexception_t (returned by mongoc_bulkwrite_execute). When + * initializing from mongoc_bulkwriteexception_t, an index should be explicitly + * provided since the BSON document will not have an "index" field. */ +bool phongo_writeerror_init_ex(zval* return_value, const bson_t* bson, int32_t index) { bson_iter_t iter; php_phongo_writeerror_t* intern; @@ -181,6 +188,7 @@ zend_bool phongo_writeerror_init_ex(zval* return_value, bson_t* bson, int32_t in bson_iter_document(&iter, &len, &data); if (!php_phongo_bson_data_to_zval(data, len, &intern->info)) { + /* Exception already thrown */ zval_ptr_dtor(&intern->info); ZVAL_UNDEF(&intern->info); @@ -189,7 +197,7 @@ zend_bool phongo_writeerror_init_ex(zval* return_value, bson_t* bson, int32_t in } /* If the WriteError is initialized from mongoc_bulkwriteexception_t, an - * index will already have been specified. */ + * index should already have been specified. */ if (!intern->index && bson_iter_init_find(&iter, bson, "index") && BSON_ITER_HOLDS_INT32(&iter)) { intern->index = bson_iter_int32(&iter); } diff --git a/src/MongoDB/WriteError.h b/src/MongoDB/WriteError.h index 3cb641bf5..69347a1e5 100644 --- a/src/MongoDB/WriteError.h +++ b/src/MongoDB/WriteError.h @@ -21,7 +21,7 @@ #include -zend_bool phongo_writeerror_init(zval* return_value, bson_t* bson); -zend_bool phongo_writeerror_init_ex(zval* return_value, bson_t* bson, int32_t index); +bool phongo_writeerror_init(zval* return_value, const bson_t* bson); +bool phongo_writeerror_init_ex(zval* return_value, const bson_t* bson, int32_t index); #endif /* PHONGO_WRITEERROR_H */ diff --git a/src/MongoDB/WriteResult.c b/src/MongoDB/WriteResult.c index 3b6324b31..bdffe1369 100644 --- a/src/MongoDB/WriteResult.c +++ b/src/MongoDB/WriteResult.c @@ -45,7 +45,10 @@ zend_class_entry* php_phongo_writeresult_ce; -static bool php_phongo_writeresult_get_writeconcernerror(php_phongo_writeresult_t* intern, zval* return_value) +/* Populates return_value with a WriteConcernError object (if available). + * Returns true on success; otherwise, false is returned and an exception is + * thrown. */ +static bool phongo_writeresult_get_writeconcernerror(php_phongo_writeresult_t* intern, zval* return_value) { bson_iter_t iter, child; zval writeconcernerror; @@ -69,6 +72,7 @@ static bool php_phongo_writeresult_get_writeconcernerror(php_phongo_writeresult_ } if (!phongo_writeconcernerror_init(&writeconcernerror, &cbson)) { + /* Exception already thrown */ zval_ptr_dtor(&writeconcernerror); return false; } @@ -82,7 +86,9 @@ static bool php_phongo_writeresult_get_writeconcernerror(php_phongo_writeresult_ return true; } -static bool php_phongo_writeresult_get_writeerrors(php_phongo_writeresult_t* intern, zval* return_value) +/* Populates return_value with a list of WriteError objects. Returns true on + * success; otherwise, false is returned and an exception is thrown. */ +static bool phongo_writeresult_get_writeerrors(php_phongo_writeresult_t* intern, zval* return_value) { bson_iter_t iter, child; @@ -106,8 +112,9 @@ static bool php_phongo_writeresult_get_writeerrors(php_phongo_writeresult_t* int } if (!phongo_writeerror_init(&writeerror, &cbson)) { + /* Exception already thrown */ zval_ptr_dtor(&writeerror); - continue; + return false; } add_next_index_zval(return_value, &writeerror); @@ -283,7 +290,7 @@ static PHP_METHOD(MongoDB_Driver_WriteResult, getWriteConcernError) PHONGO_PARSE_PARAMETERS_NONE(); - php_phongo_writeresult_get_writeconcernerror(intern, return_value); + phongo_writeresult_get_writeconcernerror(intern, return_value); } /* Returns any write errors that occurred */ @@ -295,7 +302,7 @@ static PHP_METHOD(MongoDB_Driver_WriteResult, getWriteErrors) PHONGO_PARSE_PARAMETERS_NONE(); - php_phongo_writeresult_get_writeerrors(intern, return_value); + phongo_writeresult_get_writeerrors(intern, return_value); } static PHP_METHOD(MongoDB_Driver_WriteResult, getErrorReplies) @@ -402,14 +409,14 @@ static HashTable* php_phongo_writeresult_get_debug_info(zend_object* object, int { zval writeerrors; - php_phongo_writeresult_get_writeerrors(intern, &writeerrors); + phongo_writeresult_get_writeerrors(intern, &writeerrors); ADD_ASSOC_ZVAL_EX(&retval, "writeErrors", &writeerrors); } { zval writeconcernerror; - php_phongo_writeresult_get_writeconcernerror(intern, &writeconcernerror); + phongo_writeresult_get_writeconcernerror(intern, &writeconcernerror); ADD_ASSOC_ZVAL_EX(&retval, "writeConcernError", &writeconcernerror); } From 42fb48197bb74babf76d92b73363666b3ff78a04 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 14 Jan 2025 09:21:10 -0500 Subject: [PATCH 10/13] BulkWriteCommand tests --- ...and-ctor-bypassDocumentValidation-001.phpt | 67 ++++++ ...and-ctor-bypassDocumentValidation-002.phpt | 114 ++++++++++ .../bulkwritecommand-ctor-comment-001.phpt | 61 ++++++ ...lkwritecommand-ctor-comment_error-001.phpt | 27 +++ .../bulkwritecommand-ctor-let-001.phpt | 71 ++++++ .../bulkwritecommand-ctor-let_error-001.phpt | 34 +++ .../bulkwritecommand-ctor-ordered-001.phpt | 75 +++++++ .../bulkwritecommand-ctor-ordered-002.phpt | 75 +++++++ ...kwritecommand-ctor-verboseresults-001.phpt | 109 ++++++++++ ...kwritecommand-ctor-verboseresults-002.phpt | 59 +++++ .../manager-executeBulkWriteCommand-001.phpt | 203 ++++++++++++++++++ 11 files changed, 895 insertions(+) create mode 100644 tests/bulkwritecommand/bulkwritecommand-ctor-bypassDocumentValidation-001.phpt create mode 100644 tests/bulkwritecommand/bulkwritecommand-ctor-bypassDocumentValidation-002.phpt create mode 100644 tests/bulkwritecommand/bulkwritecommand-ctor-comment-001.phpt create mode 100644 tests/bulkwritecommand/bulkwritecommand-ctor-comment_error-001.phpt create mode 100644 tests/bulkwritecommand/bulkwritecommand-ctor-let-001.phpt create mode 100644 tests/bulkwritecommand/bulkwritecommand-ctor-let_error-001.phpt create mode 100644 tests/bulkwritecommand/bulkwritecommand-ctor-ordered-001.phpt create mode 100644 tests/bulkwritecommand/bulkwritecommand-ctor-ordered-002.phpt create mode 100644 tests/bulkwritecommand/bulkwritecommand-ctor-verboseresults-001.phpt create mode 100644 tests/bulkwritecommand/bulkwritecommand-ctor-verboseresults-002.phpt create mode 100644 tests/bulkwritecommand/manager-executeBulkWriteCommand-001.phpt diff --git a/tests/bulkwritecommand/bulkwritecommand-ctor-bypassDocumentValidation-001.phpt b/tests/bulkwritecommand/bulkwritecommand-ctor-bypassDocumentValidation-001.phpt new file mode 100644 index 000000000..7aa4204da --- /dev/null +++ b/tests/bulkwritecommand/bulkwritecommand-ctor-bypassDocumentValidation-001.phpt @@ -0,0 +1,67 @@ +--TEST-- +MongoDB\Driver\BulkWriteCommand::__construct() bypassDocumentValidation=true +--SKIPIF-- + + + + +--FILE-- +executeWriteCommand(DATABASE_NAME, new MongoDB\Driver\Command([ + 'create' => COLLECTION_NAME, + 'validator' => [ + '$jsonSchema' => [ + 'bsonType' => 'object', + 'required' => ['x'], + ], + ], +])); + +$bulk = new MongoDB\Driver\BulkWriteCommand(['bypassDocumentValidation' => true]); +$bulk->insertOne(NS, ['_id' => 1]); + +$result = $manager->executeBulkWriteCommand($bulk); + +var_dump($result); + +?> +===DONE=== + +--EXPECTF-- +object(MongoDB\Driver\BulkWriteCommandResult)#%d (%d) { + ["isAcknowledged"]=> + bool(true) + ["insertedCount"]=> + int(1) + ["matchedCount"]=> + int(0) + ["modifiedCount"]=> + int(0) + ["upsertedCount"]=> + int(0) + ["deletedCount"]=> + int(0) + ["insertResults"]=> + NULL + ["updateResults"]=> + NULL + ["deleteResults"]=> + NULL + ["writeErrors"]=> + array(0) { + } + ["writeConcernErrors"]=> + array(0) { + } + ["errorReply"]=> + NULL + ["server"]=> + object(MongoDB\Driver\Server)#%d (%d) {%A + } +} +===DONE=== \ No newline at end of file diff --git a/tests/bulkwritecommand/bulkwritecommand-ctor-bypassDocumentValidation-002.phpt b/tests/bulkwritecommand/bulkwritecommand-ctor-bypassDocumentValidation-002.phpt new file mode 100644 index 000000000..8f231839d --- /dev/null +++ b/tests/bulkwritecommand/bulkwritecommand-ctor-bypassDocumentValidation-002.phpt @@ -0,0 +1,114 @@ +--TEST-- +MongoDB\Driver\BulkWriteCommand::__construct() bypassDocumentValidation=false +--SKIPIF-- + + + + +--FILE-- +executeWriteCommand(DATABASE_NAME, new MongoDB\Driver\Command([ + 'create' => COLLECTION_NAME, + 'validator' => [ + '$jsonSchema' => [ + 'bsonType' => 'object', + 'required' => ['x'], + ], + ], +])); + +$bulk = new MongoDB\Driver\BulkWriteCommand(['bypassDocumentValidation' => false]); +/* Include a successful write operation to ensure that mongoc_bulkwriteresult_t + * is populated (CDRIVER-5856). */ +$bulk->insertOne(NS, ['_id' => 1, 'x' => 1]); +$bulk->insertOne(NS, ['_id' => 2]); + +try { + $manager->executeBulkWriteCommand($bulk); +} catch (MongoDB\Driver\Exception\BulkWriteCommandException $e) { + printf("%s(%d): %s\n", get_class($e), $e->getCode(), $e->getMessage()); + var_dump($e->getBulkWriteCommandResult()); +} + +?> +===DONE=== + +--EXPECTF-- +MongoDB\Driver\Exception\BulkWriteCommandException(0): Bulk write failed +object(MongoDB\Driver\BulkWriteCommandResult)#%d (%d) { + ["isAcknowledged"]=> + bool(true) + ["insertedCount"]=> + int(1) + ["matchedCount"]=> + int(0) + ["modifiedCount"]=> + int(0) + ["upsertedCount"]=> + int(0) + ["deletedCount"]=> + int(0) + ["insertResults"]=> + NULL + ["updateResults"]=> + NULL + ["deleteResults"]=> + NULL + ["writeErrors"]=> + array(1) { + [1]=> + object(MongoDB\Driver\WriteError)#%d (%d) { + ["message"]=> + string(26) "Document failed validation" + ["code"]=> + int(121) + ["index"]=> + int(1) + ["info"]=> + object(stdClass)#%d (%d) { + ["failingDocumentId"]=> + int(2) + ["details"]=> + object(stdClass)#%d (%d) { + ["operatorName"]=> + string(11) "$jsonSchema" + ["schemaRulesNotSatisfied"]=> + array(1) { + [0]=> + object(stdClass)#%d (%d) { + ["operatorName"]=> + string(8) "required" + ["specifiedAs"]=> + object(stdClass)#%d (%d) { + ["required"]=> + array(1) { + [0]=> + string(1) "x" + } + } + ["missingProperties"]=> + array(1) { + [0]=> + string(1) "x" + } + } + } + } + } + } + } + ["writeConcernErrors"]=> + array(0) { + } + ["errorReply"]=> + NULL + ["server"]=> + object(MongoDB\Driver\Server)#%d (%d) {%A + } +} +===DONE=== diff --git a/tests/bulkwritecommand/bulkwritecommand-ctor-comment-001.phpt b/tests/bulkwritecommand/bulkwritecommand-ctor-comment-001.phpt new file mode 100644 index 000000000..2b978ea92 --- /dev/null +++ b/tests/bulkwritecommand/bulkwritecommand-ctor-comment-001.phpt @@ -0,0 +1,61 @@ +--TEST-- +MongoDB\Driver\BulkWriteCommand::__construct() comment +--SKIPIF-- + + + + +--FILE-- +getCommand(); + + if (!isset($command->comment)) { + printf("%s does not include comment option\n", $event->getCommandName()); + + return; + } + + printf("%s included comment: %s\n", $event->getCommandName(), json_encode($command->comment)); + } + + public function commandSucceeded(MongoDB\Driver\Monitoring\CommandSucceededEvent $event): void + { + } + + public function commandFailed(MongoDB\Driver\Monitoring\CommandFailedEvent $event): void + { + } +} + +$manager = create_test_manager(); + +$bulk = new MongoDB\Driver\BulkWriteCommand(['comment' => ['foo' => 1]]); +$bulk->insertOne(NS, ['_id' => 1]); + +$manager->addSubscriber(new CommandLogger); +$manager->executeBulkWriteCommand($bulk); + +$cursor = $manager->executeQuery(NS, new MongoDB\Driver\Query([])); +var_dump($cursor->toArray()); + +?> +===DONE=== + +--EXPECTF-- +bulkWrite included comment: {"foo":1} +find does not include comment option +array(1) { + [0]=> + object(stdClass)#%d (%d) { + ["_id"]=> + int(1) + } +} +===DONE=== diff --git a/tests/bulkwritecommand/bulkwritecommand-ctor-comment_error-001.phpt b/tests/bulkwritecommand/bulkwritecommand-ctor-comment_error-001.phpt new file mode 100644 index 000000000..7d59c56b6 --- /dev/null +++ b/tests/bulkwritecommand/bulkwritecommand-ctor-comment_error-001.phpt @@ -0,0 +1,27 @@ +--TEST-- +MongoDB\Driver\BulkWriteCommand::__construct() comment option bsonSerialize() exception +--FILE-- + new Comment()]); +}, Exception::class), "\n"; + +?> +===DONE=== + +--EXPECT-- +OK: Got Exception +phongo_zval_to_bson_value fails +===DONE=== \ No newline at end of file diff --git a/tests/bulkwritecommand/bulkwritecommand-ctor-let-001.phpt b/tests/bulkwritecommand/bulkwritecommand-ctor-let-001.phpt new file mode 100644 index 000000000..ed4367173 --- /dev/null +++ b/tests/bulkwritecommand/bulkwritecommand-ctor-let-001.phpt @@ -0,0 +1,71 @@ +--TEST-- +MongoDB\Driver\BulkWriteCommand::__construct() let +--SKIPIF-- + + + + +--FILE-- + 'cherry', + 'newFlavor' => 'orange', +]; + +$bulk = new MongoDB\Driver\BulkWriteCommand(['let' => $let]); + +$bulk->insertOne(NS, ['_id' => 1, 'flavor' => 'chocolate']); +$bulk->insertOne(NS, ['_id' => 2, 'flavor' => 'strawberry']); +$bulk->insertOne(NS, ['_id' => 3, 'flavor' => 'cherry']); + +$bulk->updateMany( + NS, + ['$expr' => ['$eq' => ['$flavor', '$$targetFlavor']]], + ['$set' => ['flavor' => '$$newFlavor']], +); + +$result = $manager->executeBulkWriteCommand($bulk); + +var_dump($result); + +?> +===DONE=== + +--EXPECTF-- +object(MongoDB\Driver\BulkWriteCommandResult)#%d (%d) { + ["isAcknowledged"]=> + bool(true) + ["insertedCount"]=> + int(3) + ["matchedCount"]=> + int(1) + ["modifiedCount"]=> + int(1) + ["upsertedCount"]=> + int(0) + ["deletedCount"]=> + int(0) + ["insertResults"]=> + NULL + ["updateResults"]=> + NULL + ["deleteResults"]=> + NULL + ["writeErrors"]=> + array(0) { + } + ["writeConcernErrors"]=> + array(0) { + } + ["errorReply"]=> + NULL + ["server"]=> + object(MongoDB\Driver\Server)#%d (%d) {%A + } +} +===DONE=== diff --git a/tests/bulkwritecommand/bulkwritecommand-ctor-let_error-001.phpt b/tests/bulkwritecommand/bulkwritecommand-ctor-let_error-001.phpt new file mode 100644 index 000000000..10a514b26 --- /dev/null +++ b/tests/bulkwritecommand/bulkwritecommand-ctor-let_error-001.phpt @@ -0,0 +1,34 @@ +--TEST-- +MongoDB\Driver\BulkWriteCommand::__construct() let option invalid type +--FILE-- + $invalidValue]); + }, MongoDB\Driver\Exception\InvalidArgumentException::class), "\n"; +} + +echo throws(function() { + new MongoDB\Driver\BulkWriteCommand(['let' => MongoDB\BSON\PackedArray::fromPHP([])]); +}, MongoDB\Driver\Exception\UnexpectedValueException::class), "\n"; + +?> +===DONE=== + +--EXPECT-- +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected "let" option to be array or object, bool given +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected "let" option to be array or object, int given +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected "let" option to be array or object, string given +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected "let" option to be array or object, null given +OK: Got MongoDB\Driver\Exception\UnexpectedValueException +MongoDB\BSON\PackedArray cannot be serialized as a root document +===DONE=== diff --git a/tests/bulkwritecommand/bulkwritecommand-ctor-ordered-001.phpt b/tests/bulkwritecommand/bulkwritecommand-ctor-ordered-001.phpt new file mode 100644 index 000000000..137a2a3db --- /dev/null +++ b/tests/bulkwritecommand/bulkwritecommand-ctor-ordered-001.phpt @@ -0,0 +1,75 @@ +--TEST-- +MongoDB\Driver\BulkWriteCommand::__construct() ordered=true +--SKIPIF-- + + + + +--FILE-- + true]); +$bulk->insertOne(NS, ['_id' => 1]); +$bulk->insertOne(NS, ['_id' => 1]); +$bulk->insertOne(NS, ['_id' => 2]); + +try { + $manager->executeBulkWriteCommand($bulk); +} catch (MongoDB\Driver\Exception\BulkWriteCommandException $e) { + printf("%s(%d): %s\n", get_class($e), $e->getCode(), $e->getMessage()); + var_dump($e->getBulkWriteCommandResult()); +} + +?> +===DONE=== + +--EXPECTF-- +MongoDB\Driver\Exception\BulkWriteCommandException(0): Bulk write failed +object(MongoDB\Driver\BulkWriteCommandResult)#%d (%d) { + ["isAcknowledged"]=> + bool(true) + ["insertedCount"]=> + int(1) + ["matchedCount"]=> + int(0) + ["modifiedCount"]=> + int(0) + ["upsertedCount"]=> + int(0) + ["deletedCount"]=> + int(0) + ["insertResults"]=> + NULL + ["updateResults"]=> + NULL + ["deleteResults"]=> + NULL + ["writeErrors"]=> + array(1) { + [1]=> + object(MongoDB\Driver\WriteError)#%d (%d) { + ["message"]=> + string(128) "E11000 duplicate key error collection: phongo.bulkwritecommand_bulkwritecommand_ctor_ordered_001 index: _id_ dup key: { _id: 1 }" + ["code"]=> + int(11000) + ["index"]=> + int(1) + ["info"]=> + object(stdClass)#%d (%d) { + } + } + } + ["writeConcernErrors"]=> + array(0) { + } + ["errorReply"]=> + NULL + ["server"]=> + object(MongoDB\Driver\Server)#%d (%d) {%A + } +} +===DONE=== diff --git a/tests/bulkwritecommand/bulkwritecommand-ctor-ordered-002.phpt b/tests/bulkwritecommand/bulkwritecommand-ctor-ordered-002.phpt new file mode 100644 index 000000000..a2e4f30bd --- /dev/null +++ b/tests/bulkwritecommand/bulkwritecommand-ctor-ordered-002.phpt @@ -0,0 +1,75 @@ +--TEST-- +MongoDB\Driver\BulkWriteCommand::__construct() ordered=false +--SKIPIF-- + + + + +--FILE-- + false]); +$bulk->insertOne(NS, ['_id' => 1]); +$bulk->insertOne(NS, ['_id' => 1]); +$bulk->insertOne(NS, ['_id' => 2]); + +try { + $manager->executeBulkWriteCommand($bulk); +} catch (MongoDB\Driver\Exception\BulkWriteCommandException $e) { + printf("%s(%d): %s\n", get_class($e), $e->getCode(), $e->getMessage()); + var_dump($e->getBulkWriteCommandResult()); +} + +?> +===DONE=== + +--EXPECTF-- +MongoDB\Driver\Exception\BulkWriteCommandException(0): Bulk write failed +object(MongoDB\Driver\BulkWriteCommandResult)#%d (%d) { + ["isAcknowledged"]=> + bool(true) + ["insertedCount"]=> + int(2) + ["matchedCount"]=> + int(0) + ["modifiedCount"]=> + int(0) + ["upsertedCount"]=> + int(0) + ["deletedCount"]=> + int(0) + ["insertResults"]=> + NULL + ["updateResults"]=> + NULL + ["deleteResults"]=> + NULL + ["writeErrors"]=> + array(1) { + [1]=> + object(MongoDB\Driver\WriteError)#%d (%d) { + ["message"]=> + string(128) "E11000 duplicate key error collection: phongo.bulkwritecommand_bulkwritecommand_ctor_ordered_002 index: _id_ dup key: { _id: 1 }" + ["code"]=> + int(11000) + ["index"]=> + int(1) + ["info"]=> + object(stdClass)#%d (%d) { + } + } + } + ["writeConcernErrors"]=> + array(0) { + } + ["errorReply"]=> + NULL + ["server"]=> + object(MongoDB\Driver\Server)#%d (%d) {%A + } +} +===DONE=== diff --git a/tests/bulkwritecommand/bulkwritecommand-ctor-verboseresults-001.phpt b/tests/bulkwritecommand/bulkwritecommand-ctor-verboseresults-001.phpt new file mode 100644 index 000000000..b4f178cc9 --- /dev/null +++ b/tests/bulkwritecommand/bulkwritecommand-ctor-verboseresults-001.phpt @@ -0,0 +1,109 @@ +--TEST-- +MongoDB\Driver\BulkWriteCommand::__construct() verboseResults=true +--SKIPIF-- + + + + +--FILE-- + true]); +$bulk->insertOne(NS, ['_id' => 1]); +$bulk->updateOne(NS, ['_id' => 1], ['$set' => ['x' => 1]]); +$bulk->deleteOne(NS, ['_id' => 1]); + +$result = $manager->executeBulkWriteCommand($bulk); + +var_dump($result); + +?> +===DONE=== + +--EXPECTF-- +object(MongoDB\Driver\BulkWriteCommandResult)#%d (%d) { + ["isAcknowledged"]=> + bool(true) + ["insertedCount"]=> + int(1) + ["matchedCount"]=> + int(1) + ["modifiedCount"]=> + int(1) + ["upsertedCount"]=> + int(0) + ["deletedCount"]=> + int(1) + ["insertResults"]=> + object(MongoDB\BSON\Document)#%d (%d) { + ["data"]=> + string(40) "HQAAAAMwABUAAAAQaW5zZXJ0ZWRJZAABAAAAAAA=" + ["value"]=> + object(stdClass)#%d (%d) { + ["0"]=> + object(MongoDB\BSON\Document)#%d (%d) { + ["data"]=> + string(28) "FQAAABBpbnNlcnRlZElkAAEAAAAA" + ["value"]=> + object(stdClass)#%d (%d) { + ["insertedId"]=> + int(1) + } + } + } + } + ["updateResults"]=> + object(MongoDB\BSON\Document)#%d (%d) { + ["data"]=> + string(80) "OgAAAAMxADIAAAASbWF0Y2hlZENvdW50AAEAAAAAAAAAEm1vZGlmaWVkQ291bnQAAQAAAAAAAAAAAA==" + ["value"]=> + object(stdClass)#%d (%d) { + ["1"]=> + object(MongoDB\BSON\Document)#%d (%d) { + ["data"]=> + string(68) "MgAAABJtYXRjaGVkQ291bnQAAQAAAAAAAAASbW9kaWZpZWRDb3VudAABAAAAAAAAAAA=" + ["value"]=> + object(stdClass)#%d (%d) { + ["matchedCount"]=> + int(1) + ["modifiedCount"]=> + int(1) + } + } + } + } + ["deleteResults"]=> + object(MongoDB\BSON\Document)#%d (%d) { + ["data"]=> + string(48) "IwAAAAMyABsAAAASZGVsZXRlZENvdW50AAEAAAAAAAAAAAA=" + ["value"]=> + object(stdClass)#%d (%d) { + ["2"]=> + object(MongoDB\BSON\Document)#%d (%d) { + ["data"]=> + string(36) "GwAAABJkZWxldGVkQ291bnQAAQAAAAAAAAAA" + ["value"]=> + object(stdClass)#%d (%d) { + ["deletedCount"]=> + int(1) + } + } + } + } + ["writeErrors"]=> + array(0) { + } + ["writeConcernErrors"]=> + array(0) { + } + ["errorReply"]=> + NULL + ["server"]=> + object(MongoDB\Driver\Server)#%d (%d) {%A + } +} +===DONE=== diff --git a/tests/bulkwritecommand/bulkwritecommand-ctor-verboseresults-002.phpt b/tests/bulkwritecommand/bulkwritecommand-ctor-verboseresults-002.phpt new file mode 100644 index 000000000..09acc3371 --- /dev/null +++ b/tests/bulkwritecommand/bulkwritecommand-ctor-verboseresults-002.phpt @@ -0,0 +1,59 @@ +--TEST-- +MongoDB\Driver\BulkWriteCommand::__construct() verboseResults=false +--SKIPIF-- + + + + +--FILE-- + false]); +$bulk->insertOne(NS, ['_id' => 1]); +$bulk->updateOne(NS, ['_id' => 1], ['$set' => ['x' => 1]]); +$bulk->deleteOne(NS, ['_id' => 1]); + +$result = $manager->executeBulkWriteCommand($bulk); + +var_dump($result); + +?> +===DONE=== + +--EXPECTF-- +object(MongoDB\Driver\BulkWriteCommandResult)#%d (%d) { + ["isAcknowledged"]=> + bool(true) + ["insertedCount"]=> + int(1) + ["matchedCount"]=> + int(1) + ["modifiedCount"]=> + int(1) + ["upsertedCount"]=> + int(0) + ["deletedCount"]=> + int(1) + ["insertResults"]=> + NULL + ["updateResults"]=> + NULL + ["deleteResults"]=> + NULL + ["writeErrors"]=> + array(0) { + } + ["writeConcernErrors"]=> + array(0) { + } + ["errorReply"]=> + NULL + ["server"]=> + object(MongoDB\Driver\Server)#%d (%d) {%A + } +} +===DONE=== diff --git a/tests/bulkwritecommand/manager-executeBulkWriteCommand-001.phpt b/tests/bulkwritecommand/manager-executeBulkWriteCommand-001.phpt new file mode 100644 index 000000000..114a7cebc --- /dev/null +++ b/tests/bulkwritecommand/manager-executeBulkWriteCommand-001.phpt @@ -0,0 +1,203 @@ +--TEST-- +MongoDB\Driver\BulkWriteCommand::__construct() +--SKIPIF-- + + + + +--FILE-- + true]); +$bulk->insertOne(NS, ['_id' => 1]); +$bulk->insertOne(NS, ['_id' => 2]); +$bulk->insertOne(NS, ['_id' => 3]); +$bulk->insertOne(NS, ['_id' => 4]); +$bulk->insertOne(NS, ['_id' => 5]); +$bulk->replaceOne(NS, ['_id' => 1], ['x' => 1]); +$bulk->updateOne(NS, ['_id' => 2], ['$set' => ['x' => 1]]); +$bulk->updateMany(NS, ['x' => ['$exists' => true]], ['$inc' => ['x' => 1]]); +$bulk->deleteOne(NS, ['_id' => ['$gt' => 4]]); +$bulk->deleteMany(NS, ['_id' => ['$gt' => 2]]); + +$result = $manager->executeBulkWriteCommand($bulk); + +var_dump($bulk); +var_dump($result); + +?> +===DONE=== + +--EXPECTF-- +object(MongoDB\Driver\BulkWriteCommand)#%d (%d) { + ["bypassDocumentValidation"]=> + NULL + ["ordered"]=> + bool(true) + ["verboseResults"]=> + bool(true) + ["session"]=> + NULL + ["write_concern"]=> + NULL +} +object(MongoDB\Driver\BulkWriteCommandResult)#%d (%d) { + ["isAcknowledged"]=> + bool(true) + ["insertedCount"]=> + int(5) + ["matchedCount"]=> + int(4) + ["modifiedCount"]=> + int(4) + ["upsertedCount"]=> + int(0) + ["deletedCount"]=> + int(3) + ["insertResults"]=> + object(MongoDB\BSON\Document)#%d (%d) { + ["data"]=> + string(168) "%s" + ["value"]=> + object(stdClass)#%d (%d) { + ["0"]=> + object(MongoDB\BSON\Document)#%d (%d) { + ["data"]=> + string(28) "%s" + ["value"]=> + object(stdClass)#%d (%d) { + ["insertedId"]=> + int(1) + } + } + ["1"]=> + object(MongoDB\BSON\Document)#%d (%d) { + ["data"]=> + string(28) "%s" + ["value"]=> + object(stdClass)#%d (%d) { + ["insertedId"]=> + int(2) + } + } + ["2"]=> + object(MongoDB\BSON\Document)#%d (%d) { + ["data"]=> + string(28) "%s" + ["value"]=> + object(stdClass)#%d (%d) { + ["insertedId"]=> + int(3) + } + } + ["3"]=> + object(MongoDB\BSON\Document)#%d (%d) { + ["data"]=> + string(28) "%s" + ["value"]=> + object(stdClass)#%d (%d) { + ["insertedId"]=> + int(4) + } + } + ["4"]=> + object(MongoDB\BSON\Document)#%d (%d) { + ["data"]=> + string(28) "%s" + ["value"]=> + object(stdClass)#%d (%d) { + ["insertedId"]=> + int(5) + } + } + } + } + ["updateResults"]=> + object(MongoDB\BSON\Document)#%d (%d) { + ["data"]=> + string(220) "%s" + ["value"]=> + object(stdClass)#%d (%d) { + ["5"]=> + object(MongoDB\BSON\Document)#%d (%d) { + ["data"]=> + string(68) "%s" + ["value"]=> + object(stdClass)#%d (%d) { + ["matchedCount"]=> + int(1) + ["modifiedCount"]=> + int(1) + } + } + ["6"]=> + object(MongoDB\BSON\Document)#%d (%d) { + ["data"]=> + string(68) "%s" + ["value"]=> + object(stdClass)#%d (%d) { + ["matchedCount"]=> + int(1) + ["modifiedCount"]=> + int(1) + } + } + ["7"]=> + object(MongoDB\BSON\Document)#%d (%d) { + ["data"]=> + string(68) "%s" + ["value"]=> + object(stdClass)#%d (%d) { + ["matchedCount"]=> + int(2) + ["modifiedCount"]=> + int(2) + } + } + } + } + ["deleteResults"]=> + object(MongoDB\BSON\Document)#%d (%d) { + ["data"]=> + string(88) "%s" + ["value"]=> + object(stdClass)#%d (%d) { + ["8"]=> + object(MongoDB\BSON\Document)#%d (%d) { + ["data"]=> + string(36) "%s" + ["value"]=> + object(stdClass)#%d (%d) { + ["deletedCount"]=> + int(1) + } + } + ["9"]=> + object(MongoDB\BSON\Document)#%d (%d) { + ["data"]=> + string(36) "%s" + ["value"]=> + object(stdClass)#%d (%d) { + ["deletedCount"]=> + int(2) + } + } + } + } + ["writeErrors"]=> + array(0) { + } + ["writeConcernErrors"]=> + array(0) { + } + ["errorReply"]=> + NULL + ["server"]=> + object(MongoDB\Driver\Server)#%d (%d) {%A + } +} +===DONE=== From 0c0d80d8495e55785967b271fb3beee2aeaecaac Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Wed, 5 Feb 2025 16:51:03 -0500 Subject: [PATCH 11/13] clang-format --- src/MongoDB/BulkWriteCommand.c | 14 +++++++------- src/MongoDB/BulkWriteCommandResult.c | 20 ++++++++++---------- src/MongoDB/Manager.c | 12 ++++++------ src/MongoDB/Server.c | 8 ++++---- src/MongoDB/WriteConcernError.c | 4 ++-- src/MongoDB/WriteError.c | 4 ++-- src/phongo_execute.c | 14 +++++++------- 7 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/MongoDB/BulkWriteCommand.c b/src/MongoDB/BulkWriteCommand.c index 022149534..48b8889d2 100644 --- a/src/MongoDB/BulkWriteCommand.c +++ b/src/MongoDB/BulkWriteCommand.c @@ -132,13 +132,13 @@ static PHP_METHOD(MongoDB_Driver_BulkWriteCommand, __construct) PHONGO_PARSE_PARAMETERS_END(); // TODO: Consider removing initialization for zero values - intern->bw = mongoc_bulkwrite_new(); - intern->bypass = PHONGO_BULKWRITECOMMAND_BYPASS_UNSET; - intern->comment = NULL; - intern->let = NULL; - intern->num_ops = 0; - intern->ordered = true; - intern->verbose = false; + intern->bw = mongoc_bulkwrite_new(); + intern->bypass = PHONGO_BULKWRITECOMMAND_BYPASS_UNSET; + intern->comment = NULL; + intern->let = NULL; + intern->num_ops = 0; + intern->ordered = true; + intern->verbose = false; intern->write_concern = NULL; if (!zoptions) { diff --git a/src/MongoDB/BulkWriteCommandResult.c b/src/MongoDB/BulkWriteCommandResult.c index a3e534cf1..7a4685c18 100644 --- a/src/MongoDB/BulkWriteCommandResult.c +++ b/src/MongoDB/BulkWriteCommandResult.c @@ -32,10 +32,10 @@ #include "MongoDB/WriteError.h" #include "BulkWriteCommandResult_arginfo.h" -#define PHONGO_BULKWRITECOMMANDRESULT_CHECK_ACKNOWLEDGED(method) \ - if (!intern->is_acknowledged) { \ +#define PHONGO_BULKWRITECOMMANDRESULT_CHECK_ACKNOWLEDGED(method) \ + if (!intern->is_acknowledged) { \ phongo_throw_exception(PHONGO_ERROR_LOGIC, "MongoDB\\Driver\\BulkWriteCommandResult::" method "() should not be called for an unacknowledged write result"); \ - return; \ + return; \ } zend_class_entry* php_phongo_bulkwritecommandresult_ce; @@ -338,7 +338,7 @@ static zend_object* php_phongo_bulkwritecommandresult_create_object(zend_class_e static HashTable* php_phongo_bulkwritecommandresult_get_debug_info(zend_object* object, int* is_temp) { php_phongo_bulkwritecommandresult_t* intern; - zval retval = ZVAL_STATIC_INIT; + zval retval = ZVAL_STATIC_INIT; intern = Z_OBJ_BULKWRITECOMMANDRESULT(object); *is_temp = 1; @@ -426,7 +426,7 @@ void php_phongo_bulkwritecommandresult_init_ce(INIT_FUNC_ARGS) php_phongo_handler_bulkwritecommandresult.offset = XtOffsetOf(php_phongo_bulkwritecommandresult_t, std); } -static inline bson_t* _bson_copy_or_null (const bson_t* bson) +static inline bson_t* _bson_copy_or_null(const bson_t* bson) { return bson_empty0(bson) ? NULL : bson_copy(bson); } @@ -437,16 +437,16 @@ php_phongo_bulkwritecommandresult_t* phongo_bulkwritecommandresult_init(zval* re object_init_ex(return_value, php_phongo_bulkwritecommandresult_ce); - bwcr = Z_BULKWRITECOMMANDRESULT_OBJ_P(return_value); + bwcr = Z_BULKWRITECOMMANDRESULT_OBJ_P(return_value); bwcr->is_acknowledged = !!bw_ret->res; // Copy mongoc_bulkwriteresult_t fields if (bw_ret->res) { bwcr->inserted_count = mongoc_bulkwriteresult_insertedcount(bw_ret->res); bwcr->upserted_count = mongoc_bulkwriteresult_upsertedcount(bw_ret->res); - bwcr->matched_count = mongoc_bulkwriteresult_matchedcount(bw_ret->res); + bwcr->matched_count = mongoc_bulkwriteresult_matchedcount(bw_ret->res); bwcr->modified_count = mongoc_bulkwriteresult_modifiedcount(bw_ret->res); - bwcr->deleted_count = mongoc_bulkwriteresult_deletedcount(bw_ret->res); + bwcr->deleted_count = mongoc_bulkwriteresult_deletedcount(bw_ret->res); bwcr->insert_results = _bson_copy_or_null(mongoc_bulkwriteresult_insertresults(bw_ret->res)); bwcr->update_results = _bson_copy_or_null(mongoc_bulkwriteresult_updateresults(bw_ret->res)); @@ -457,8 +457,8 @@ php_phongo_bulkwritecommandresult_t* phongo_bulkwritecommandresult_init(zval* re // Copy mongoc_bulkwriteexception_t fields if (bw_ret->exc) { - bwcr->error_reply = _bson_copy_or_null(mongoc_bulkwriteexception_errorreply(bw_ret->exc)); - bwcr->write_errors = _bson_copy_or_null(mongoc_bulkwriteexception_writeerrors(bw_ret->exc)); + bwcr->error_reply = _bson_copy_or_null(mongoc_bulkwriteexception_errorreply(bw_ret->exc)); + bwcr->write_errors = _bson_copy_or_null(mongoc_bulkwriteexception_writeerrors(bw_ret->exc)); bwcr->write_concern_errors = _bson_copy_or_null(mongoc_bulkwriteexception_writeconcernerrors(bw_ret->exc)); } diff --git a/src/MongoDB/Manager.c b/src/MongoDB/Manager.c index 5d78e27bf..ff7bf5037 100644 --- a/src/MongoDB/Manager.c +++ b/src/MongoDB/Manager.c @@ -598,12 +598,12 @@ static PHP_METHOD(MongoDB_Driver_Manager, executeBulkWrite) /* Executes a BulkWriteCommand (i.e. bulkWrite command for MongoDB 8.0+) */ static PHP_METHOD(MongoDB_Driver_Manager, executeBulkWriteCommand) { - php_phongo_manager_t* intern; - zval* zbwc; + php_phongo_manager_t* intern; + zval* zbwc; php_phongo_bulkwritecommand_t* bwc; - zval* zoptions = NULL; - uint32_t server_id = 0; - zval* zsession = NULL; + zval* zoptions = NULL; + uint32_t server_id = 0; + zval* zsession = NULL; PHONGO_PARSE_PARAMETERS_START(1, 2) Z_PARAM_OBJECT_OF_CLASS(zbwc, php_phongo_bulkwritecommand_ce) @@ -612,7 +612,7 @@ static PHP_METHOD(MongoDB_Driver_Manager, executeBulkWriteCommand) PHONGO_PARSE_PARAMETERS_END(); intern = Z_MANAGER_OBJ_P(getThis()); - bwc = Z_BULKWRITECOMMAND_OBJ_P(zbwc); + bwc = Z_BULKWRITECOMMAND_OBJ_P(zbwc); if (!phongo_parse_session(zoptions, intern->client, NULL, &zsession)) { /* Exception should already have been thrown */ diff --git a/src/MongoDB/Server.c b/src/MongoDB/Server.c index 9244e7bb2..7b2dbf8f3 100644 --- a/src/MongoDB/Server.c +++ b/src/MongoDB/Server.c @@ -218,10 +218,10 @@ static PHP_METHOD(MongoDB_Driver_Server, executeBulkWrite) /* Executes a BulkWriteCommand (i.e. bulkWrite command for MongoDB 8.0+) */ static PHP_METHOD(MongoDB_Driver_Server, executeBulkWriteCommand) { - php_phongo_server_t* intern; - zval* zbwc; + php_phongo_server_t* intern; + zval* zbwc; php_phongo_bulkwritecommand_t* bwc; - zval* zoptions = NULL; + zval* zoptions = NULL; PHONGO_PARSE_PARAMETERS_START(1, 2) Z_PARAM_OBJECT_OF_CLASS(zbwc, php_phongo_bulkwritecommand_ce) @@ -230,7 +230,7 @@ static PHP_METHOD(MongoDB_Driver_Server, executeBulkWriteCommand) PHONGO_PARSE_PARAMETERS_END(); intern = Z_SERVER_OBJ_P(getThis()); - bwc = Z_BULKWRITECOMMAND_OBJ_P(zbwc); + bwc = Z_BULKWRITECOMMAND_OBJ_P(zbwc); /* If the Server was created in a different process, reset the client so * that its session pool is cleared. */ diff --git a/src/MongoDB/WriteConcernError.c b/src/MongoDB/WriteConcernError.c index 76f395b87..edf81dd45 100644 --- a/src/MongoDB/WriteConcernError.c +++ b/src/MongoDB/WriteConcernError.c @@ -155,7 +155,7 @@ bool phongo_writeconcernerror_init(zval* return_value, const bson_t* bson) // Additionally check for field name used by mongoc_bulkwriteexception_t if ((bson_iter_init_find(&iter, bson, "errmsg") && BSON_ITER_HOLDS_UTF8(&iter)) || - (bson_iter_init_find(&iter, bson, "message") && BSON_ITER_HOLDS_UTF8(&iter))) { + (bson_iter_init_find(&iter, bson, "message") && BSON_ITER_HOLDS_UTF8(&iter))) { uint32_t len; const char* message = bson_iter_utf8(&iter, &len); @@ -164,7 +164,7 @@ bool phongo_writeconcernerror_init(zval* return_value, const bson_t* bson) // Additionally check for field name used by mongoc_bulkwriteexception_t if ((bson_iter_init_find(&iter, bson, "errInfo") && BSON_ITER_HOLDS_DOCUMENT(&iter)) || - (bson_iter_init_find(&iter, bson, "details") && BSON_ITER_HOLDS_DOCUMENT(&iter))) { + (bson_iter_init_find(&iter, bson, "details") && BSON_ITER_HOLDS_DOCUMENT(&iter))) { uint32_t len; const uint8_t* data = NULL; diff --git a/src/MongoDB/WriteError.c b/src/MongoDB/WriteError.c index 46d2e580a..148a90c0d 100644 --- a/src/MongoDB/WriteError.c +++ b/src/MongoDB/WriteError.c @@ -172,7 +172,7 @@ bool phongo_writeerror_init_ex(zval* return_value, const bson_t* bson, int32_t i // Additionally check for field name used by mongoc_bulkwriteexception_t if ((bson_iter_init_find(&iter, bson, "errmsg") && BSON_ITER_HOLDS_UTF8(&iter)) || - (bson_iter_init_find(&iter, bson, "message") && BSON_ITER_HOLDS_UTF8(&iter))) { + (bson_iter_init_find(&iter, bson, "message") && BSON_ITER_HOLDS_UTF8(&iter))) { uint32_t errmsg_len; const char* err_msg = bson_iter_utf8(&iter, &errmsg_len); @@ -181,7 +181,7 @@ bool phongo_writeerror_init_ex(zval* return_value, const bson_t* bson, int32_t i // Additionally check for field name used by mongoc_bulkwriteexception_t if ((bson_iter_init_find(&iter, bson, "errInfo") && BSON_ITER_HOLDS_DOCUMENT(&iter)) || - (bson_iter_init_find(&iter, bson, "details") && BSON_ITER_HOLDS_DOCUMENT(&iter))) { + (bson_iter_init_find(&iter, bson, "details") && BSON_ITER_HOLDS_DOCUMENT(&iter))) { uint32_t len; const uint8_t* data = NULL; diff --git a/src/phongo_execute.c b/src/phongo_execute.c index f5cb54533..77f138f59 100644 --- a/src/phongo_execute.c +++ b/src/phongo_execute.c @@ -337,13 +337,13 @@ bool phongo_execute_bulk_write(zval* manager, const char* namespace, php_phongo_ bool phongo_execute_bulkwritecommand(zval* manager, php_phongo_bulkwritecommand_t* bwc, zval* zoptions, uint32_t server_id, zval* return_value) { - mongoc_client_t* client = NULL; - mongoc_bulkwrite_t* bw = bwc->bw; + mongoc_client_t* client = NULL; + mongoc_bulkwrite_t* bw = bwc->bw; mongoc_bulkwriteopts_t* bw_opts = NULL; - mongoc_bulkwritereturn_t bw_ret = { 0 }; + mongoc_bulkwritereturn_t bw_ret = { 0 }; php_phongo_bulkwritecommandresult_t* bwcr; zval* zsession = NULL; - bool success = true; + bool success = true; client = Z_MANAGER_OBJ_P(manager)->client; @@ -389,9 +389,9 @@ bool phongo_execute_bulkwritecommand(zval* manager, php_phongo_bulkwritecommand_ * write concern errors, along with a possible partial write result. */ if (bw_ret.exc) { - success = false; - bson_error_t error = { 0 }; - const bson_t *error_reply = mongoc_bulkwriteexception_errorreply(bw_ret.exc); + success = false; + bson_error_t error = { 0 }; + const bson_t* error_reply = mongoc_bulkwriteexception_errorreply(bw_ret.exc); // Consult any top-level error to throw the first exception if (mongoc_bulkwriteexception_error(bw_ret.exc, &error)) { From 7d092b768586fbba004d3f58d6c724040513387d Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Mon, 10 Feb 2025 16:47:21 -0500 Subject: [PATCH 12/13] Remove BulkWriteCommandResult::getServer() Per CDRIVER-5843, libmongoc does not consistently populate this field. It also isn't required by the spec, so omit it for now. --- src/MongoDB/BulkWriteCommandResult.c | 29 ------------------- src/MongoDB/BulkWriteCommandResult.stub.php | 2 -- src/MongoDB/BulkWriteCommandResult_arginfo.h | 7 +---- src/phongo_structs.h | 1 - ...and-ctor-bypassDocumentValidation-001.phpt | 3 -- ...and-ctor-bypassDocumentValidation-002.phpt | 3 -- .../bulkwritecommand-ctor-let-001.phpt | 3 -- .../bulkwritecommand-ctor-ordered-001.phpt | 3 -- .../bulkwritecommand-ctor-ordered-002.phpt | 3 -- ...kwritecommand-ctor-verboseresults-001.phpt | 3 -- ...kwritecommand-ctor-verboseresults-002.phpt | 3 -- .../manager-executeBulkWriteCommand-001.phpt | 3 -- 12 files changed, 1 insertion(+), 62 deletions(-) diff --git a/src/MongoDB/BulkWriteCommandResult.c b/src/MongoDB/BulkWriteCommandResult.c index 7a4685c18..b8ffdaf48 100644 --- a/src/MongoDB/BulkWriteCommandResult.c +++ b/src/MongoDB/BulkWriteCommandResult.c @@ -27,7 +27,6 @@ #include "phongo_error.h" #include "BSON/Document.h" -#include "MongoDB/Server.h" #include "MongoDB/WriteConcernError.h" #include "MongoDB/WriteError.h" #include "BulkWriteCommandResult_arginfo.h" @@ -192,21 +191,6 @@ static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, getUpsertedCount) RETURN_LONG(intern->upserted_count); } -/* Returns the last Server used to execute a command for the bulk write */ -static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, getServer) -{ - php_phongo_bulkwritecommandresult_t* intern; - - intern = Z_BULKWRITECOMMANDRESULT_OBJ_P(getThis()); - - PHONGO_PARSE_PARAMETERS_NONE(); - - PHONGO_BULKWRITECOMMANDRESULT_CHECK_ACKNOWLEDGED("getServer"); - - // TODO: null handling - phongo_server_init(return_value, &intern->manager, intern->server_id); -} - static PHP_METHOD(MongoDB_Driver_BulkWriteCommandResult, getInsertResults) { php_phongo_bulkwritecommandresult_t* intern; @@ -401,17 +385,6 @@ static HashTable* php_phongo_bulkwritecommandresult_get_debug_info(zend_object* ADD_ASSOC_NULL_EX(&retval, "errorReply"); } - if (intern->server_id) { - zval server; - - phongo_server_init(&server, &intern->manager, intern->server_id); - ADD_ASSOC_ZVAL_EX(&retval, "server", &server); - } else { - /* TODO: Determine if this path is only reached when a partial result is - * attached to a BulkWriteCommandException on an unacknowledged write. */ - ADD_ASSOC_NULL_EX(&retval, "server"); - } - return Z_ARRVAL(retval); } @@ -451,8 +424,6 @@ php_phongo_bulkwritecommandresult_t* phongo_bulkwritecommandresult_init(zval* re bwcr->insert_results = _bson_copy_or_null(mongoc_bulkwriteresult_insertresults(bw_ret->res)); bwcr->update_results = _bson_copy_or_null(mongoc_bulkwriteresult_updateresults(bw_ret->res)); bwcr->delete_results = _bson_copy_or_null(mongoc_bulkwriteresult_deleteresults(bw_ret->res)); - - bwcr->server_id = mongoc_bulkwriteresult_serverid(bw_ret->res); } // Copy mongoc_bulkwriteexception_t fields diff --git a/src/MongoDB/BulkWriteCommandResult.stub.php b/src/MongoDB/BulkWriteCommandResult.stub.php index ca5a82f67..bcca4816f 100644 --- a/src/MongoDB/BulkWriteCommandResult.stub.php +++ b/src/MongoDB/BulkWriteCommandResult.stub.php @@ -22,8 +22,6 @@ final public function getUpsertedCount(): int {} final public function getDeletedCount(): int {} - final public function getServer(): Server {} - final public function getInsertResults(): ?\MongoDB\BSON\Document {} final public function getUpdateResults(): ?\MongoDB\BSON\Document {} diff --git a/src/MongoDB/BulkWriteCommandResult_arginfo.h b/src/MongoDB/BulkWriteCommandResult_arginfo.h index 1856afbe8..0e80889d5 100644 --- a/src/MongoDB/BulkWriteCommandResult_arginfo.h +++ b/src/MongoDB/BulkWriteCommandResult_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d14704cc61cbe0ed62906e92a9036fbbd5e228d8 */ + * Stub hash: a4cbc5665d7b1d99f5f4c6425d6d2eb3829bb001 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_Driver_BulkWriteCommandResult___construct, 0, 0, 0) ZEND_END_ARG_INFO() @@ -15,9 +15,6 @@ ZEND_END_ARG_INFO() #define arginfo_class_MongoDB_Driver_BulkWriteCommandResult_getDeletedCount arginfo_class_MongoDB_Driver_BulkWriteCommandResult_getInsertedCount -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_BulkWriteCommandResult_getServer, 0, 0, MongoDB\\Driver\\Server, 0) -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_BulkWriteCommandResult_getInsertResults, 0, 0, MongoDB\\BSON\\Document, 1) ZEND_END_ARG_INFO() @@ -42,7 +39,6 @@ static ZEND_METHOD(MongoDB_Driver_BulkWriteCommandResult, getMatchedCount); static ZEND_METHOD(MongoDB_Driver_BulkWriteCommandResult, getModifiedCount); static ZEND_METHOD(MongoDB_Driver_BulkWriteCommandResult, getUpsertedCount); static ZEND_METHOD(MongoDB_Driver_BulkWriteCommandResult, getDeletedCount); -static ZEND_METHOD(MongoDB_Driver_BulkWriteCommandResult, getServer); static ZEND_METHOD(MongoDB_Driver_BulkWriteCommandResult, getInsertResults); static ZEND_METHOD(MongoDB_Driver_BulkWriteCommandResult, getUpdateResults); static ZEND_METHOD(MongoDB_Driver_BulkWriteCommandResult, getDeleteResults); @@ -59,7 +55,6 @@ static const zend_function_entry class_MongoDB_Driver_BulkWriteCommandResult_met ZEND_ME(MongoDB_Driver_BulkWriteCommandResult, getModifiedCount, arginfo_class_MongoDB_Driver_BulkWriteCommandResult_getModifiedCount, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_BulkWriteCommandResult, getUpsertedCount, arginfo_class_MongoDB_Driver_BulkWriteCommandResult_getUpsertedCount, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_BulkWriteCommandResult, getDeletedCount, arginfo_class_MongoDB_Driver_BulkWriteCommandResult_getDeletedCount, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) - ZEND_ME(MongoDB_Driver_BulkWriteCommandResult, getServer, arginfo_class_MongoDB_Driver_BulkWriteCommandResult_getServer, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_BulkWriteCommandResult, getInsertResults, arginfo_class_MongoDB_Driver_BulkWriteCommandResult_getInsertResults, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_BulkWriteCommandResult, getUpdateResults, arginfo_class_MongoDB_Driver_BulkWriteCommandResult_getUpdateResults, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_BulkWriteCommandResult, getDeleteResults, arginfo_class_MongoDB_Driver_BulkWriteCommandResult_getDeleteResults, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) diff --git a/src/phongo_structs.h b/src/phongo_structs.h index 534e07f63..ba3f46cbc 100644 --- a/src/phongo_structs.h +++ b/src/phongo_structs.h @@ -65,7 +65,6 @@ typedef struct { bson_t* write_concern_errors; bson_t* error_reply; zval manager; - uint32_t server_id; zend_object std; } php_phongo_bulkwritecommandresult_t; diff --git a/tests/bulkwritecommand/bulkwritecommand-ctor-bypassDocumentValidation-001.phpt b/tests/bulkwritecommand/bulkwritecommand-ctor-bypassDocumentValidation-001.phpt index 7aa4204da..e1280e162 100644 --- a/tests/bulkwritecommand/bulkwritecommand-ctor-bypassDocumentValidation-001.phpt +++ b/tests/bulkwritecommand/bulkwritecommand-ctor-bypassDocumentValidation-001.phpt @@ -60,8 +60,5 @@ object(MongoDB\Driver\BulkWriteCommandResult)#%d (%d) { } ["errorReply"]=> NULL - ["server"]=> - object(MongoDB\Driver\Server)#%d (%d) {%A - } } ===DONE=== \ No newline at end of file diff --git a/tests/bulkwritecommand/bulkwritecommand-ctor-bypassDocumentValidation-002.phpt b/tests/bulkwritecommand/bulkwritecommand-ctor-bypassDocumentValidation-002.phpt index 8f231839d..abcad96f5 100644 --- a/tests/bulkwritecommand/bulkwritecommand-ctor-bypassDocumentValidation-002.phpt +++ b/tests/bulkwritecommand/bulkwritecommand-ctor-bypassDocumentValidation-002.phpt @@ -107,8 +107,5 @@ object(MongoDB\Driver\BulkWriteCommandResult)#%d (%d) { } ["errorReply"]=> NULL - ["server"]=> - object(MongoDB\Driver\Server)#%d (%d) {%A - } } ===DONE=== diff --git a/tests/bulkwritecommand/bulkwritecommand-ctor-let-001.phpt b/tests/bulkwritecommand/bulkwritecommand-ctor-let-001.phpt index ed4367173..1c3565097 100644 --- a/tests/bulkwritecommand/bulkwritecommand-ctor-let-001.phpt +++ b/tests/bulkwritecommand/bulkwritecommand-ctor-let-001.phpt @@ -64,8 +64,5 @@ object(MongoDB\Driver\BulkWriteCommandResult)#%d (%d) { } ["errorReply"]=> NULL - ["server"]=> - object(MongoDB\Driver\Server)#%d (%d) {%A - } } ===DONE=== diff --git a/tests/bulkwritecommand/bulkwritecommand-ctor-ordered-001.phpt b/tests/bulkwritecommand/bulkwritecommand-ctor-ordered-001.phpt index 137a2a3db..43dfb8c8a 100644 --- a/tests/bulkwritecommand/bulkwritecommand-ctor-ordered-001.phpt +++ b/tests/bulkwritecommand/bulkwritecommand-ctor-ordered-001.phpt @@ -68,8 +68,5 @@ object(MongoDB\Driver\BulkWriteCommandResult)#%d (%d) { } ["errorReply"]=> NULL - ["server"]=> - object(MongoDB\Driver\Server)#%d (%d) {%A - } } ===DONE=== diff --git a/tests/bulkwritecommand/bulkwritecommand-ctor-ordered-002.phpt b/tests/bulkwritecommand/bulkwritecommand-ctor-ordered-002.phpt index a2e4f30bd..49b084f7c 100644 --- a/tests/bulkwritecommand/bulkwritecommand-ctor-ordered-002.phpt +++ b/tests/bulkwritecommand/bulkwritecommand-ctor-ordered-002.phpt @@ -68,8 +68,5 @@ object(MongoDB\Driver\BulkWriteCommandResult)#%d (%d) { } ["errorReply"]=> NULL - ["server"]=> - object(MongoDB\Driver\Server)#%d (%d) {%A - } } ===DONE=== diff --git a/tests/bulkwritecommand/bulkwritecommand-ctor-verboseresults-001.phpt b/tests/bulkwritecommand/bulkwritecommand-ctor-verboseresults-001.phpt index b4f178cc9..df6f45649 100644 --- a/tests/bulkwritecommand/bulkwritecommand-ctor-verboseresults-001.phpt +++ b/tests/bulkwritecommand/bulkwritecommand-ctor-verboseresults-001.phpt @@ -102,8 +102,5 @@ object(MongoDB\Driver\BulkWriteCommandResult)#%d (%d) { } ["errorReply"]=> NULL - ["server"]=> - object(MongoDB\Driver\Server)#%d (%d) {%A - } } ===DONE=== diff --git a/tests/bulkwritecommand/bulkwritecommand-ctor-verboseresults-002.phpt b/tests/bulkwritecommand/bulkwritecommand-ctor-verboseresults-002.phpt index 09acc3371..2b48ae4cc 100644 --- a/tests/bulkwritecommand/bulkwritecommand-ctor-verboseresults-002.phpt +++ b/tests/bulkwritecommand/bulkwritecommand-ctor-verboseresults-002.phpt @@ -52,8 +52,5 @@ object(MongoDB\Driver\BulkWriteCommandResult)#%d (%d) { } ["errorReply"]=> NULL - ["server"]=> - object(MongoDB\Driver\Server)#%d (%d) {%A - } } ===DONE=== diff --git a/tests/bulkwritecommand/manager-executeBulkWriteCommand-001.phpt b/tests/bulkwritecommand/manager-executeBulkWriteCommand-001.phpt index 114a7cebc..98cb480b4 100644 --- a/tests/bulkwritecommand/manager-executeBulkWriteCommand-001.phpt +++ b/tests/bulkwritecommand/manager-executeBulkWriteCommand-001.phpt @@ -196,8 +196,5 @@ object(MongoDB\Driver\BulkWriteCommandResult)#%d (%d) { } ["errorReply"]=> NULL - ["server"]=> - object(MongoDB\Driver\Server)#%d (%d) {%A - } } ===DONE=== From cd1622ea1bff823ddeb190f28beb8f352e734f7a Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 18 Feb 2025 10:30:41 -0500 Subject: [PATCH 13/13] Check for _id extraction before appending insert --- src/MongoDB/BulkWriteCommand.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/MongoDB/BulkWriteCommand.c b/src/MongoDB/BulkWriteCommand.c index 48b8889d2..23ee668ed 100644 --- a/src/MongoDB/BulkWriteCommand.c +++ b/src/MongoDB/BulkWriteCommand.c @@ -437,6 +437,11 @@ static PHP_METHOD(MongoDB_Driver_BulkWriteCommand, insertOne) goto cleanup; } + if (!bson_out) { + phongo_throw_exception(PHONGO_ERROR_LOGIC, "php_phongo_zval_to_bson() did not return an _id. Please file a bug report."); + goto cleanup; + } + if (!mongoc_bulkwrite_append_insertone(intern->bw, ns, &bdocument, NULL, &error)) { phongo_throw_exception_from_bson_error_t(&error); goto cleanup; @@ -444,11 +449,6 @@ static PHP_METHOD(MongoDB_Driver_BulkWriteCommand, insertOne) intern->num_ops++; - if (!bson_out) { - phongo_throw_exception(PHONGO_ERROR_LOGIC, "php_phongo_zval_to_bson() did not return document identifier. Please file a bug report."); - goto cleanup; - } - phongo_bwc_extract_id(bson_out, &return_value); cleanup: