Files
mongo-php-driver/src/phongo_error.c
T
Andreas Braun b0b1fd1b94 PHPC-2146: Refactor typemap struct and BSON encoding/decoding of zvals (#1369)
* Avoid re-parsing bson when converting to zval

A lot of occurrences start out with a bson_t*, then call bson_get_data on it to pass it to php_phongo_bson_to_zval_ex. This however creates a new bson reader and creates a new bson_t* from the given data. This is unnecessary and we should pass the original bson_t* in these instances.

* Refactor typemap struct

* Extract object initialisation from document visitor

* Refactor document and array visitors

* Fix wrong signature in array visitor declaration

* Rename ce member for consistency

* Add exception thrown comment

* Fix clang-format
2022-10-24 12:22:18 +02:00

219 lines
6.7 KiB
C

/*
* Copyright 2022-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 <php.h>
#include <Zend/zend_exceptions.h>
#include "php_phongo.h"
#include "phongo_error.h"
/* This constant is used for determining if a server error for an exceeded query
* or command should select ExecutionTimeoutException. */
#define PHONGO_SERVER_ERROR_EXCEEDED_TIME_LIMIT 50
void phongo_add_exception_prop(const char* prop, int prop_len, zval* value)
{
if (EG(exception)) {
zval ex;
ZVAL_OBJ(&ex, EG(exception));
zend_update_property(Z_OBJCE(ex), PHONGO_COMPAT_OBJ_P(&ex), prop, prop_len, value);
}
}
zend_class_entry* phongo_exception_from_phongo_domain(php_phongo_error_domain_t domain)
{
switch (domain) {
case PHONGO_ERROR_INVALID_ARGUMENT:
return php_phongo_invalidargumentexception_ce;
case PHONGO_ERROR_LOGIC:
return php_phongo_logicexception_ce;
case PHONGO_ERROR_RUNTIME:
return php_phongo_runtimeexception_ce;
case PHONGO_ERROR_UNEXPECTED_VALUE:
return php_phongo_unexpectedvalueexception_ce;
case PHONGO_ERROR_MONGOC_FAILED:
return php_phongo_runtimeexception_ce;
case PHONGO_ERROR_CONNECTION_FAILED:
return php_phongo_connectionexception_ce;
}
MONGOC_ERROR("Resolving unknown phongo error domain: %d", domain);
return php_phongo_runtimeexception_ce;
}
zend_class_entry* phongo_exception_from_mongoc_domain(mongoc_error_domain_t domain, mongoc_error_code_t code)
{
if (domain == MONGOC_ERROR_CLIENT) {
if (code == MONGOC_ERROR_CLIENT_AUTHENTICATE) {
return php_phongo_authenticationexception_ce;
}
if (code == MONGOC_ERROR_CLIENT_INVALID_ENCRYPTION_ARG) {
return php_phongo_invalidargumentexception_ce;
}
}
if (domain == MONGOC_ERROR_COMMAND && code == MONGOC_ERROR_COMMAND_INVALID_ARG) {
return php_phongo_invalidargumentexception_ce;
}
if (domain == MONGOC_ERROR_SERVER) {
if (code == PHONGO_SERVER_ERROR_EXCEEDED_TIME_LIMIT) {
return php_phongo_executiontimeoutexception_ce;
}
return php_phongo_serverexception_ce;
}
if (domain == MONGOC_ERROR_SERVER_SELECTION && code == MONGOC_ERROR_SERVER_SELECTION_FAILURE) {
return php_phongo_connectiontimeoutexception_ce;
}
if (domain == MONGOC_ERROR_STREAM) {
if (code == MONGOC_ERROR_STREAM_SOCKET) {
return php_phongo_connectiontimeoutexception_ce;
}
return php_phongo_connectionexception_ce;
}
if (domain == MONGOC_ERROR_WRITE_CONCERN) {
return php_phongo_serverexception_ce;
}
if (domain == MONGOC_ERROR_PROTOCOL && code == MONGOC_ERROR_PROTOCOL_BAD_WIRE_VERSION) {
return php_phongo_connectionexception_ce;
}
if (domain == MONGOC_ERROR_CLIENT_SIDE_ENCRYPTION) {
return php_phongo_encryptionexception_ce;
}
return php_phongo_runtimeexception_ce;
}
void phongo_throw_exception(php_phongo_error_domain_t domain, const char* format, ...)
{
va_list args;
char* message;
int message_len;
va_start(args, format);
message_len = vspprintf(&message, 0, format, args);
zend_throw_exception(phongo_exception_from_phongo_domain(domain), message, 0);
efree(message);
va_end(args);
}
static int phongo_exception_append_error_labels(zval* labels, const bson_iter_t* iter)
{
bson_iter_t error_labels;
uint32_t label_count = 0;
if (!BSON_ITER_HOLDS_ARRAY(iter) || !bson_iter_recurse(iter, &error_labels)) {
return label_count;
}
while (bson_iter_next(&error_labels)) {
if (BSON_ITER_HOLDS_UTF8(&error_labels)) {
const char* error_label;
uint32_t error_label_len;
error_label = bson_iter_utf8(&error_labels, &error_label_len);
ADD_NEXT_INDEX_STRINGL(labels, error_label, error_label_len);
label_count++;
}
}
return label_count;
}
void phongo_exception_add_error_labels(const bson_t* reply)
{
bson_iter_t iter, child;
zval labels;
uint32_t label_count = 0;
if (!reply) {
return;
}
array_init(&labels);
if (bson_iter_init_find(&iter, reply, "errorLabels")) {
label_count += phongo_exception_append_error_labels(&labels, &iter);
}
if (bson_iter_init_find(&iter, reply, "writeConcernError") && BSON_ITER_HOLDS_DOCUMENT(&iter) &&
bson_iter_recurse(&iter, &child) && bson_iter_find(&child, "errorLabels")) {
label_count += phongo_exception_append_error_labels(&labels, &child);
}
/* mongoc_write_result_t always reports writeConcernErrors in an array, so
* we must iterate this to collect WCE labels for BulkWrite replies. */
if (bson_iter_init_find(&iter, reply, "writeConcernErrors") && BSON_ITER_HOLDS_ARRAY(&iter) && bson_iter_recurse(&iter, &child)) {
bson_iter_t wce;
while (bson_iter_next(&child)) {
if (BSON_ITER_HOLDS_DOCUMENT(&child) && bson_iter_recurse(&child, &wce) && bson_iter_find(&wce, "errorLabels")) {
label_count += phongo_exception_append_error_labels(&labels, &wce);
}
}
}
if (label_count > 0) {
phongo_add_exception_prop(ZEND_STRL("errorLabels"), &labels);
}
zval_ptr_dtor(&labels);
}
void phongo_throw_exception_from_bson_error_t_and_reply(bson_error_t* error, const bson_t* reply)
{
/* Server errors (other than ExceededTimeLimit) and write concern errors
* may use CommandException and report the result document for the
* failed command. For BC, ExceededTimeLimit errors will continue to use
* ExcecutionTimeoutException and omit the result document. */
if (reply && ((error->domain == MONGOC_ERROR_SERVER && error->code != PHONGO_SERVER_ERROR_EXCEEDED_TIME_LIMIT) || error->domain == MONGOC_ERROR_WRITE_CONCERN)) {
zval zv;
zend_throw_exception(php_phongo_commandexception_ce, error->message, error->code);
if (php_phongo_bson_to_zval(reply, &zv)) {
phongo_add_exception_prop(ZEND_STRL("resultDocument"), &zv);
}
zval_ptr_dtor(&zv);
} else {
zend_throw_exception(phongo_exception_from_mongoc_domain(error->domain, error->code), error->message, error->code);
}
phongo_exception_add_error_labels(reply);
}
void phongo_throw_exception_from_bson_error_t(bson_error_t* error)
{
phongo_throw_exception_from_bson_error_t_and_reply(error, NULL);
}
#ifndef MONGOC_ENABLE_CLIENT_SIDE_ENCRYPTION
void phongo_throw_exception_no_cse(php_phongo_error_domain_t domain, const char* message)
{
phongo_throw_exception(domain, "%s Please recompile with support for libmongocrypt using the with-mongodb-client-side-encryption configure switch.", message);
}
#endif /* MONGOC_ENABLE_CLIENT_SIDE_ENCRYPTION */