From: Andrey Hristov Date: Thu, 4 Aug 2011 09:51:26 +0000 (+0000) Subject: Add mysqli_error_list() that returns an array with errors. Typically only X-Git-Tag: php-5.4.0beta1~532 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c9e7716cfd137b7c9ec2980273b3bbd4bc743e1f;p=php Add mysqli_error_list() that returns an array with errors. Typically only one and just one for libmysql. mysqlnd can return generate more than one error during its work and with mysqli_error() only the last error is being reported. In the array returned by mysqli_error_list() / $mysqli->error_list, all errors will be found. The list is reset when the next command is executed --- diff --git a/ext/mysqli/mysqli_fe.c b/ext/mysqli/mysqli_fe.c index eb133b1a80..a126148b29 100644 --- a/ext/mysqli/mysqli_fe.c +++ b/ext/mysqli/mysqli_fe.c @@ -352,6 +352,7 @@ const zend_function_entry mysqli_functions[] = { #endif PHP_FE(mysqli_errno, arginfo_mysqli_only_link) PHP_FE(mysqli_error, arginfo_mysqli_only_link) + PHP_FE(mysqli_error_list, arginfo_mysqli_only_link) PHP_FE(mysqli_stmt_execute, arginfo_mysqli_only_statement) PHP_FALIAS(mysqli_execute, mysqli_stmt_execute, arginfo_mysqli_only_statement) PHP_FE(mysqli_fetch_field, arginfo_mysqli_only_result) @@ -424,6 +425,7 @@ const zend_function_entry mysqli_functions[] = { PHP_FE(mysqli_stmt_data_seek, arginfo_mysqli_stmt_data_seek) PHP_FE(mysqli_stmt_errno, arginfo_mysqli_only_statement) PHP_FE(mysqli_stmt_error, arginfo_mysqli_only_statement) + PHP_FE(mysqli_stmt_error_list, arginfo_mysqli_only_statement) PHP_FE(mysqli_stmt_fetch, arginfo_mysqli_only_statement) PHP_FE(mysqli_stmt_field_count, arginfo_mysqli_only_statement) PHP_FE(mysqli_stmt_free_result, arginfo_mysqli_only_statement) diff --git a/ext/mysqli/mysqli_fe.h b/ext/mysqli/mysqli_fe.h index bf2a70ab56..9dc7843110 100644 --- a/ext/mysqli/mysqli_fe.h +++ b/ext/mysqli/mysqli_fe.h @@ -38,6 +38,7 @@ PHP_FUNCTION(mysqli_debug); PHP_FUNCTION(mysqli_dump_debug_info); PHP_FUNCTION(mysqli_errno); PHP_FUNCTION(mysqli_error); +PHP_FUNCTION(mysqli_error_list); PHP_FUNCTION(mysqli_fetch_all); PHP_FUNCTION(mysqli_fetch_array); PHP_FUNCTION(mysqli_fetch_assoc); @@ -111,6 +112,7 @@ PHP_FUNCTION(mysqli_stmt_close); PHP_FUNCTION(mysqli_stmt_data_seek); PHP_FUNCTION(mysqli_stmt_errno); PHP_FUNCTION(mysqli_stmt_error); +PHP_FUNCTION(mysqli_stmt_error_list); PHP_FUNCTION(mysqli_stmt_free_result); PHP_FUNCTION(mysqli_stmt_get_result); PHP_FUNCTION(mysqli_stmt_get_warnings); diff --git a/ext/mysqli/mysqli_nonapi.c b/ext/mysqli/mysqli_nonapi.c index 11c0618c49..8dbaed2bff 100644 --- a/ext/mysqli/mysqli_nonapi.c +++ b/ext/mysqli/mysqli_nonapi.c @@ -385,6 +385,7 @@ PHP_FUNCTION(mysqli_fetch_all) /* }}} */ + /* {{{ proto array mysqli_get_client_stats(void) Returns statistics about the zval cache */ PHP_FUNCTION(mysqli_get_client_stats) @@ -415,6 +416,93 @@ PHP_FUNCTION(mysqli_get_connection_stats) #endif /* }}} */ +/* {{{ proto mixed mysqli_error_list (object connection) + Fetches all client errors */ +PHP_FUNCTION(mysqli_error_list) +{ + MY_MYSQL *mysql; + zval *mysql_link; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) { + return; + } + MYSQLI_FETCH_RESOURCE_CONN(mysql, &mysql_link, MYSQLI_STATUS_VALID); + array_init(return_value); +#if defined(MYSQLI_USE_MYSQLND) + if (mysql->mysql->error_info.error_list) { + MYSQLND_ERROR_LIST_ELEMENT * message; + zend_llist_position pos; + for (message = (MYSQLND_ERROR_LIST_ELEMENT *) zend_llist_get_first_ex(mysql->mysql->error_info.error_list, &pos); + message; + message = (MYSQLND_ERROR_LIST_ELEMENT *) zend_llist_get_next_ex(mysql->mysql->error_info.error_list, &pos)) + { + zval * single_error; + MAKE_STD_ZVAL(single_error); + array_init(single_error); + add_assoc_long_ex(single_error, "errno", sizeof("errno"), message->error_no); + add_assoc_string_ex(single_error, "sqlstate", sizeof("sqlstate"), message->sqlstate, 1); + add_assoc_string_ex(single_error, "error", sizeof("error"), message->error, 1); + add_next_index_zval(return_value, single_error); + } + } +#else + if (mysql_errno(mysql->mysql)) { + zval * single_error; + MAKE_STD_ZVAL(single_error); + array_init(single_error); + add_assoc_long_ex(single_error, "errno", sizeof("errno"), mysql_errno(mysql->mysql)); + add_assoc_string_ex(single_error, "sqlstate", sizeof("sqlstate"), mysql_sqlstate(mysql->mysql), 1); + add_assoc_string_ex(single_error, "error", sizeof("error"), mysql_error(mysql->mysql), 1); + add_next_index_zval(return_value, single_error); + } +#endif +} +/* }}} */ + + +/* {{{ proto string mysqli_stmt_error_list(object stmt) +*/ +PHP_FUNCTION(mysqli_stmt_error_list) +{ + MY_STMT *stmt; + zval *mysql_stmt; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) { + return; + } + MYSQLI_FETCH_RESOURCE_STMT(stmt, &mysql_stmt, MYSQLI_STATUS_INITIALIZED); + array_init(return_value); +#if defined(MYSQLI_USE_MYSQLND) + if (stmt->stmt && stmt->stmt->data && stmt->stmt->data->error_info.error_list) { + MYSQLND_ERROR_LIST_ELEMENT * message; + zend_llist_position pos; + for (message = (MYSQLND_ERROR_LIST_ELEMENT *) zend_llist_get_first_ex(stmt->stmt->data->error_info.error_list, &pos); + message; + message = (MYSQLND_ERROR_LIST_ELEMENT *) zend_llist_get_next_ex(stmt->stmt->data->error_info.error_list, &pos)) + { + zval * single_error; + MAKE_STD_ZVAL(single_error); + array_init(single_error); + add_assoc_long_ex(single_error, "errno", sizeof("errno"), message->error_no); + add_assoc_string_ex(single_error, "sqlstate", sizeof("sqlstate"), message->sqlstate, 1); + add_assoc_string_ex(single_error, "error", sizeof("error"), message->error, 1); + add_next_index_zval(return_value, single_error); + } + } +#else + if (mysql_stmt_errno(stmt->stmt)) { + zval * single_error; + MAKE_STD_ZVAL(single_error); + array_init(single_error); + add_assoc_long_ex(single_error, "errno", sizeof("errno"), mysql_stmt_errno(stmt->stmt)); + add_assoc_string_ex(single_error, "sqlstate", sizeof("sqlstate"), mysql_stmt_sqlstate(stmt->stmt), 1); + add_assoc_string_ex(single_error, "error", sizeof("error"), mysql_stmt_error(stmt->stmt), 1); + add_next_index_zval(return_value, single_error); + } +#endif +} +/* }}} */ + /* {{{ proto mixed mysqli_fetch_object (object result [, string class_name [, NULL|array ctor_params]]) Fetch a result row as an object */ diff --git a/ext/mysqli/mysqli_prop.c b/ext/mysqli/mysqli_prop.c index 8a5eef2dcd..9e233022c0 100644 --- a/ext/mysqli/mysqli_prop.c +++ b/ext/mysqli/mysqli_prop.c @@ -189,6 +189,54 @@ static int link_affected_rows_read(mysqli_object *obj, zval **retval TSRMLS_DC) } /* }}} */ + +/* {{{ property link_error_list_read */ +static int link_error_list_read(mysqli_object *obj, zval **retval TSRMLS_DC) +{ + MY_MYSQL *mysql; + + MAKE_STD_ZVAL(*retval); + + CHECK_STATUS(MYSQLI_STATUS_VALID); + + mysql = (MY_MYSQL *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr; + + array_init(*retval); + if (mysql) { +#if defined(MYSQLI_USE_MYSQLND) + if (mysql->mysql->error_info.error_list) { + MYSQLND_ERROR_LIST_ELEMENT * message; + zend_llist_position pos; + for (message = (MYSQLND_ERROR_LIST_ELEMENT *) zend_llist_get_first_ex(mysql->mysql->error_info.error_list, &pos); + message; + message = (MYSQLND_ERROR_LIST_ELEMENT *) zend_llist_get_next_ex(mysql->mysql->error_info.error_list, &pos)) + { + zval * single_error; + MAKE_STD_ZVAL(single_error); + array_init(single_error); + add_assoc_long_ex(single_error, "errno", sizeof("errno"), message->error_no); + add_assoc_string_ex(single_error, "sqlstate", sizeof("sqlstate"), message->sqlstate, 1); + add_assoc_string_ex(single_error, "error", sizeof("error"), message->error, 1); + add_next_index_zval(*retval, single_error); + } + } +#else + if (mysql_errno(mysql->mysql)) { + zval * single_error; + MAKE_STD_ZVAL(single_error); + array_init(single_error); + add_assoc_long_ex(single_error, "errno", sizeof("errno"), mysql_errno(mysql->mysql)); + add_assoc_string_ex(single_error, "sqlstate", sizeof("sqlstate"), mysql_sqlstate(mysql->mysql), 1); + add_assoc_string_ex(single_error, "error", sizeof("error"), mysql_error(mysql->mysql), 1); + add_next_index_zval(*retval, single_error); + } +#endif + } + return SUCCESS; +} +/* }}} */ + + /* link properties */ MYSQLI_MAP_PROPERTY_FUNC_LONG(link_errno_read, mysql_errno, MYSQLI_GET_MYSQL(MYSQLI_STATUS_INITIALIZED), ulong, "%lu") MYSQLI_MAP_PROPERTY_FUNC_STRING(link_error_read, mysql_error, MYSQLI_GET_MYSQL(MYSQLI_STATUS_INITIALIZED)) @@ -202,6 +250,7 @@ MYSQLI_MAP_PROPERTY_FUNC_LONG(link_server_version_read, mysql_get_server_version MYSQLI_MAP_PROPERTY_FUNC_STRING(link_sqlstate_read, mysql_sqlstate, MYSQLI_GET_MYSQL(MYSQLI_STATUS_VALID)) MYSQLI_MAP_PROPERTY_FUNC_LONG(link_thread_id_read, mysql_thread_id, MYSQLI_GET_MYSQL(MYSQLI_STATUS_VALID), ulong, "%lu") MYSQLI_MAP_PROPERTY_FUNC_LONG(link_warning_count_read, mysql_warning_count, MYSQLI_GET_MYSQL(MYSQLI_STATUS_VALID), ulong, "%lu") + /* result properties */ /* {{{ property result_type_read */ @@ -306,6 +355,51 @@ static int stmt_affected_rows_read(mysqli_object *obj, zval **retval TSRMLS_DC) } /* }}} */ +/* {{{ property stmt_error_list_read */ +static int stmt_error_list_read(mysqli_object *obj, zval **retval TSRMLS_DC) +{ + MY_STMT * stmt; + + MAKE_STD_ZVAL(*retval); + CHECK_STATUS(MYSQLI_STATUS_INITIALIZED); + + stmt = (MY_STMT *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr; + array_init(*retval); + if (stmt && stmt->stmt) { +#if defined(MYSQLI_USE_MYSQLND) + if (stmt->stmt->data && stmt->stmt->data->error_info.error_list) { + MYSQLND_ERROR_LIST_ELEMENT * message; + zend_llist_position pos; + for (message = (MYSQLND_ERROR_LIST_ELEMENT *) zend_llist_get_first_ex(stmt->stmt->data->error_info.error_list, &pos); + message; + message = (MYSQLND_ERROR_LIST_ELEMENT *) zend_llist_get_next_ex(stmt->stmt->data->error_info.error_list, &pos)) + { + zval * single_error; + MAKE_STD_ZVAL(single_error); + array_init(single_error); + add_assoc_long_ex(single_error, "errno", sizeof("errno"), message->error_no); + add_assoc_string_ex(single_error, "sqlstate", sizeof("sqlstate"), message->sqlstate, 1); + add_assoc_string_ex(single_error, "error", sizeof("error"), message->error, 1); + add_next_index_zval(*retval, single_error); + } + } +#else + if (mysql_stmt_errno(stmt->stmt)) { + zval * single_error; + MAKE_STD_ZVAL(single_error); + array_init(single_error); + add_assoc_long_ex(single_error, "errno", sizeof("errno"), mysql_stmt_errno(stmt->stmt)); + add_assoc_string_ex(single_error, "sqlstate", sizeof("sqlstate"), mysql_stmt_sqlstate(stmt->stmt), 1); + add_assoc_string_ex(single_error, "error", sizeof("error"), mysql_stmt_error(stmt->stmt), 1); + add_next_index_zval(*retval, single_error); + } +#endif + } + return SUCCESS; +} +/* }}} */ + + MYSQLI_MAP_PROPERTY_FUNC_LONG(stmt_insert_id_read, mysql_stmt_insert_id, MYSQLI_GET_STMT(MYSQLI_STATUS_VALID), my_ulonglong, MYSQLI_LLU_SPEC) MYSQLI_MAP_PROPERTY_FUNC_LONG(stmt_num_rows_read, mysql_stmt_num_rows, MYSQLI_GET_STMT(MYSQLI_STATUS_VALID), my_ulonglong, MYSQLI_LLU_SPEC) MYSQLI_MAP_PROPERTY_FUNC_LONG(stmt_param_count_read, mysql_stmt_param_count, MYSQLI_GET_STMT(MYSQLI_STATUS_VALID), ulong, "%lu") @@ -323,6 +417,7 @@ const mysqli_property_entry mysqli_link_property_entries[] = { {"connect_error", sizeof("connect_error") - 1, link_connect_error_read, NULL}, {"errno", sizeof("errno") - 1, link_errno_read, NULL}, {"error", sizeof("error") - 1, link_error_read, NULL}, + {"error_list", sizeof("error_list") - 1, link_error_list_read, NULL}, {"field_count", sizeof("field_count") - 1, link_field_count_read, NULL}, {"host_info", sizeof("host_info") - 1, link_host_info_read, NULL}, {"info", sizeof("info") - 1, link_info_read, NULL}, @@ -345,6 +440,7 @@ const zend_property_info mysqli_link_property_info_entries[] = { {ZEND_ACC_PUBLIC, "connect_error", sizeof("connect_error") - 1, -1, 0, NULL, 0, NULL}, {ZEND_ACC_PUBLIC, "errno", sizeof("errno") - 1, -1, 0, NULL, 0, NULL}, {ZEND_ACC_PUBLIC, "error", sizeof("error") - 1, -1, 0, NULL, 0, NULL}, + {ZEND_ACC_PUBLIC, "error_list", sizeof("error_list") - 1, -1, 0, NULL, 0, NULL}, {ZEND_ACC_PUBLIC, "field_count", sizeof("field_count") - 1, -1, 0, NULL, 0, NULL}, {ZEND_ACC_PUBLIC, "host_info", sizeof("host_info") - 1, -1, 0, NULL, 0, NULL}, {ZEND_ACC_PUBLIC, "info", sizeof("info") - 1, -1, 0, NULL, 0, NULL}, @@ -385,6 +481,7 @@ const mysqli_property_entry mysqli_stmt_property_entries[] = { {"field_count", sizeof("field_count") - 1, stmt_field_count_read, NULL}, {"errno", sizeof("errno") - 1, stmt_errno_read, NULL}, {"error", sizeof("error") - 1, stmt_error_read, NULL}, + {"error_list", sizeof("error_list") - 1, stmt_error_list_read, NULL}, {"sqlstate", sizeof("sqlstate") - 1, stmt_sqlstate_read, NULL}, {"id", sizeof("id") - 1, stmt_id_read, NULL}, {NULL, 0, NULL, NULL} @@ -399,6 +496,7 @@ const zend_property_info mysqli_stmt_property_info_entries[] = { {ZEND_ACC_PUBLIC, "field_count",sizeof("field_count") - 1, -1, 0, NULL, 0, NULL}, {ZEND_ACC_PUBLIC, "errno", sizeof("errno") - 1, -1, 0, NULL, 0, NULL}, {ZEND_ACC_PUBLIC, "error", sizeof("error") - 1, -1, 0, NULL, 0, NULL}, + {ZEND_ACC_PUBLIC, "error_list", sizeof("error_list") - 1, -1, 0, NULL, 0, NULL}, {ZEND_ACC_PUBLIC, "sqlstate", sizeof("sqlstate") - 1, -1, 0, NULL, 0, NULL}, {ZEND_ACC_PUBLIC, "id", sizeof("id") - 1, -1, 0, NULL, 0, NULL}, {0, NULL, 0, -1, 0, NULL, 0, NULL} diff --git a/ext/mysqli/tests/057.phpt b/ext/mysqli/tests/057.phpt index 8c8b3eeed7..2cb887506a 100644 --- a/ext/mysqli/tests/057.phpt +++ b/ext/mysqli/tests/057.phpt @@ -91,6 +91,9 @@ object(mysqli_stmt)#%d (%d) { int(0) [%u|b%"error"]=> %unicode|string%(0) "" + [%u|b%"error_list"]=> + array(0) { + } [%u|b%"sqlstate"]=> %unicode|string%(5) "00000" [%u|b%"id"]=> diff --git a/ext/mysqli/tests/bug34810.phpt b/ext/mysqli/tests/bug34810.phpt index 35eb717534..c6158b9913 100644 --- a/ext/mysqli/tests/bug34810.phpt +++ b/ext/mysqli/tests/bug34810.phpt @@ -77,6 +77,9 @@ object(mysqli)#%d (%d) { int(0) [%u|b%"error"]=> %unicode|string%(0) "" + [%u|b%"error_list"]=> + array(0) { + } [%u|b%"field_count"]=> int(0) [%u|b%"host_info"]=> @@ -113,6 +116,8 @@ object(mysqli)#%d (%d) { int(0) [%u|b%"error"]=> %unicode|string%(0) "" + [%u|b%"error_list"]=> + NULL [%u|b%"field_count"]=> NULL [%u|b%"host_info"]=> diff --git a/ext/mysqli/tests/mysqli_class_mysqli_interface.phpt b/ext/mysqli/tests/mysqli_class_mysqli_interface.phpt index a3bd92fcfc..9eff426911 100644 --- a/ext/mysqli/tests/mysqli_class_mysqli_interface.phpt +++ b/ext/mysqli/tests/mysqli_class_mysqli_interface.phpt @@ -131,6 +131,11 @@ require_once('skipifconnectfailure.inc'); $mysqli->error, gettype($mysqli->error), mysqli_error($link), gettype(mysqli_error($link))); + assert(mysqli_error_list($link) === $mysqli->error_list); + printf("mysqli->error_list = '%s'/%s ('%s'/%s)\n", + $mysqli->error_list, gettype($mysqli->error_list), + mysqli_error_list($link), gettype(mysqli_error_list($link))); + assert(mysqli_field_count($link) === $mysqli->field_count); printf("mysqli->field_count = '%s'/%s ('%s'/%s)\n", $mysqli->field_count, gettype($mysqli->field_count), @@ -222,6 +227,7 @@ connect_errno connect_error errno error +error_list field_count host_info info @@ -241,6 +247,7 @@ connect_errno connect_error errno error +error_list field_count host_info info @@ -258,6 +265,7 @@ mysqli->client_info = '%s'/%unicode|string% ('%s'/%unicode|string%) mysqli->client_version = '%d'/integer ('%d'/integer) mysqli->errno = '0'/integer ('0'/integer) mysqli->error = ''/%unicode|string% (''/%unicode|string%) +mysqli->error_list = 'Array'/array ('Array'/array) mysqli->field_count = '0'/integer ('0'/integer) mysqli->insert_id = '0'/integer ('0'/integer) mysqli->sqlstate = '00000'/%unicode|string% ('00000'/%unicode|string%) diff --git a/ext/mysqli/tests/mysqli_class_mysqli_properties_no_conn.phpt b/ext/mysqli/tests/mysqli_class_mysqli_properties_no_conn.phpt index d0940a7561..61630b0961 100644 --- a/ext/mysqli/tests/mysqli_class_mysqli_properties_no_conn.phpt +++ b/ext/mysqli/tests/mysqli_class_mysqli_properties_no_conn.phpt @@ -151,6 +151,7 @@ connect_errno = '%s' connect_error = ''%s' errno = 'NULL' error = 'NULL' +error_list = 'NULL' field_count = 'NULL' host_info = 'NULL' info = 'NULL' @@ -170,6 +171,7 @@ connect_errno = '%s' connect_error = '%s' errno = 'NULL' error = 'NULL' +error_list = 'NULL' field_count = 'NULL' host_info = 'NULL' info = 'NULL' @@ -220,6 +222,7 @@ connect_errno = '%s' connect_error = '%s' errno = 'NULL' error = 'NULL' +error_list = 'NULL' field_count = 'NULL' host_info = 'NULL' info = 'NULL' @@ -239,6 +242,7 @@ connect_errno = '%s' connect_error = '%s' errno = 'NULL' error = 'NULL' +error_list = 'NULL' field_count = 'NULL' host_info = 'NULL' info = 'NULL' diff --git a/ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt b/ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt index 3ce2b588f8..c7f348323e 100644 --- a/ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt +++ b/ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt @@ -1129,6 +1129,14 @@ isStatic: no isDefault: yes Modifiers: 256 +Inspecting property 'error_list' +isPublic: yes +isPrivate: no +isProtected: no +isStatic: no +isDefault: yes +Modifiers: 256 + Inspecting property 'field_count' isPublic: yes isPrivate: no @@ -1215,6 +1223,7 @@ Default property 'connect_errno' Default property 'connect_error' Default property 'errno' Default property 'error' +Default property 'error_list' Default property 'field_count' Default property 'host_info' Default property 'info' diff --git a/ext/mysqli/tests/mysqli_class_mysqli_stmt_interface.phpt b/ext/mysqli/tests/mysqli_class_mysqli_stmt_interface.phpt index 7cbb127212..acf7379198 100644 --- a/ext/mysqli/tests/mysqli_class_mysqli_stmt_interface.phpt +++ b/ext/mysqli/tests/mysqli_class_mysqli_stmt_interface.phpt @@ -97,6 +97,9 @@ printf("stmt->errno = '%s'\n", $stmt->errno); assert(mysqli_stmt_error($stmt) === $stmt->error); printf("stmt->error = '%s'\n", $stmt->error); +assert(mysqli_stmt_error_list($stmt) === $stmt->error_list); +var_dump("stmt->error = ", $stmt->error_list); + assert(mysqli_stmt_field_count($stmt) === $stmt->field_count); printf("stmt->field_count = '%s'\n", $stmt->field_count); @@ -143,6 +146,7 @@ Class variables: affected_rows errno error +error_list field_count id insert_id @@ -158,6 +162,7 @@ param_count field_count errno error +error_list sqlstate id @@ -173,6 +178,9 @@ stmt->affected_rows = '' stmt->affected_rows = '1' stmt->errno = '0' stmt->error = '' +string(14) "stmt->error = " +array(0) { +} stmt->field_count = '0' stmt->id = '%d' stmt->insert_id = '0' diff --git a/ext/mysqli/tests/mysqli_connect.phpt b/ext/mysqli/tests/mysqli_connect.phpt index cf7c7bede8..e9d6ed543f 100644 --- a/ext/mysqli/tests/mysqli_connect.phpt +++ b/ext/mysqli/tests/mysqli_connect.phpt @@ -146,7 +146,7 @@ mysqli_connect() print "done!"; ?> --EXPECTF-- -Warning: mysqli_connect(): (%d/%d): Access denied for user '%s'@'%s' (using password: YES) in %s on line %d +Warning: mysqli_connect(): (%s/%d): Access denied for user '%s'@'%s' (using password: YES) in %s on line %d array(1) { [%u|b%"testing"]=> %unicode|string%(21) "mysqli.default_socket" diff --git a/ext/mysqli/tests/mysqli_connect_oo.phpt b/ext/mysqli/tests/mysqli_connect_oo.phpt index d406e24845..af257ca6df 100644 --- a/ext/mysqli/tests/mysqli_connect_oo.phpt +++ b/ext/mysqli/tests/mysqli_connect_oo.phpt @@ -144,7 +144,7 @@ new mysqli() print "done!"; ?> --EXPECTF-- -Warning: mysqli::mysqli(): (%d/%d): Access denied for user '%sunknown%s'@'%s' (using password: %s) in %s on line %d +Warning: mysqli::mysqli(): (%s/%d): Access denied for user '%sunknown%s'@'%s' (using password: %s) in %s on line %d ... and now Exceptions Access denied for user '%s'@'%s' (using password: %s) done! \ No newline at end of file diff --git a/ext/mysqli/tests/mysqli_kill.phpt b/ext/mysqli/tests/mysqli_kill.phpt index 5706adf46d..bb83dd6c05 100644 --- a/ext/mysqli/tests/mysqli_kill.phpt +++ b/ext/mysqli/tests/mysqli_kill.phpt @@ -96,6 +96,18 @@ object(mysqli)#%d (%d) { int(2006) [%u|b%"error"]=> %unicode|string%(%d) "%s" + [%u|b%"error_list"]=> + array(1) { + [0]=> + array(3) { + [%u|b%"errno"]=> + int(2006) + [%u|b%"sqlstate"]=> + %unicode|string%(5) "%s" + [%u|b%"error"]=> + %unicode|string%(%d) "%s" + } + } [%u|b%"field_count"]=> int(0) [%u|b%"host_info"]=> diff --git a/ext/mysqli/tests/mysqli_real_connect.phpt b/ext/mysqli/tests/mysqli_real_connect.phpt index 2c371328b9..350c30092e 100644 --- a/ext/mysqli/tests/mysqli_real_connect.phpt +++ b/ext/mysqli/tests/mysqli_real_connect.phpt @@ -175,7 +175,7 @@ require_once('skipifconnectfailure.inc'); require_once("clean_table.inc"); ?> --EXPECTF-- -Warning: mysqli_real_connect(): (%d/%d): Access denied for user '%s'@'%s' (using password: YES) in %s on line %d +Warning: mysqli_real_connect(): (%s/%d): Access denied for user '%s'@'%s' (using password: YES) in %s on line %d object(mysqli)#%d (%d) { [%u|b%"affected_rows"]=> NULL @@ -191,6 +191,8 @@ object(mysqli)#%d (%d) { %s [%u|b%"error"]=> %s + [%u|b%"error_list"]=> + NULL [%u|b%"field_count"]=> NULL [%u|b%"host_info"]=> diff --git a/ext/mysqli/tests/mysqli_real_connect_pconn.phpt b/ext/mysqli/tests/mysqli_real_connect_pconn.phpt index 5dedd47c96..4cc18198c6 100644 --- a/ext/mysqli/tests/mysqli_real_connect_pconn.phpt +++ b/ext/mysqli/tests/mysqli_real_connect_pconn.phpt @@ -151,5 +151,5 @@ mysqli.max_persistent=10 require_once("clean_table.inc"); ?> --EXPECTF-- -Warning: mysqli_real_connect(): (%d/%d): Access denied for user '%s'@'%s' (using password: YES) in %s on line %d +Warning: mysqli_real_connect(): (%s/%d): Access denied for user '%s'@'%s' (using password: YES) in %s on line %d done! diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index e02fd79099..e850862c88 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -69,6 +69,21 @@ static struct st_mysqlnd_conn_methods *mysqlnd_conn_methods; static struct st_mysqlnd_plugin_core mysqlnd_plugin_core; +/* {{{ mysqlnd_error_list_pdtor */ +static void +mysqlnd_error_list_pdtor(void * pDest) +{ + MYSQLND_ERROR_LIST_ELEMENT * element = (MYSQLND_ERROR_LIST_ELEMENT *) pDest; + TSRMLS_FETCH(); + DBG_ENTER("mysqlnd_error_list_pdtor"); + if (element->error) { + mnd_pefree(element->error, TRUE); + } + DBG_VOID_RETURN; +} +/* }}} */ + + /* {{{ mysqlnd_library_end */ PHPAPI void mysqlnd_library_end(TSRMLS_D) { @@ -178,6 +193,11 @@ MYSQLND_METHOD(mysqlnd_conn, free_contents)(MYSQLND * conn TSRMLS_DC) mnd_pefree(conn->last_message, pers); conn->last_message = NULL; } + if (conn->error_info.error_list) { + zend_llist_clean(conn->error_info.error_list); + mnd_pefree(conn->error_info.error_list, pers); + conn->error_info.error_list = NULL; + } conn->charset = NULL; conn->greet_charset = NULL; @@ -2492,6 +2512,14 @@ PHPAPI MYSQLND * _mysqlnd_init(zend_bool persistent TSRMLS_DC) ret = NULL; } + ret->error_info.error_list = mnd_pecalloc(1, sizeof(zend_llist), persistent); + if (!ret->error_info.error_list) { + ret->m->dtor(ret TSRMLS_CC); + ret = NULL; + } else { + zend_llist_init(ret->error_info.error_list, sizeof(MYSQLND_ERROR_LIST_ELEMENT), (llist_dtor_func_t)mysqlnd_error_list_pdtor, persistent); + } + DBG_RETURN(ret); } /* }}} */ diff --git a/ext/mysqlnd/mysqlnd_auth.c b/ext/mysqlnd/mysqlnd_auth.c index 1bb08013c2..1a56e8ef0a 100644 --- a/ext/mysqlnd/mysqlnd_auth.c +++ b/ext/mysqlnd/mysqlnd_auth.c @@ -137,8 +137,7 @@ mysqlnd_auth_handshake(MYSQLND * conn, strlcpy(conn->error_info.sqlstate, auth_resp_packet->sqlstate, sizeof(conn->error_info.sqlstate)); DBG_ERR_FMT("ERROR:%u [SQLSTATE:%s] %s", auth_resp_packet->error_no, auth_resp_packet->sqlstate, auth_resp_packet->error); } - conn->error_info.error_no = auth_resp_packet->error_no; - strlcpy(conn->error_info.error, auth_resp_packet->error, sizeof(conn->error_info.error)); + SET_CLIENT_ERROR(conn->error_info, auth_resp_packet->error_no, UNKNOWN_SQLSTATE, auth_resp_packet->error); } goto end; } @@ -235,7 +234,7 @@ mysqlnd_auth_change_user(MYSQLND * const conn, } ret = PACKET_READ(chg_user_resp, conn); - conn->error_info = chg_user_resp->error_info; + COPY_CLIENT_ERROR(conn->error_info, chg_user_resp->error_info); if (0xFE == chg_user_resp->response_code) { ret = FAIL; diff --git a/ext/mysqlnd/mysqlnd_loaddata.c b/ext/mysqlnd/mysqlnd_loaddata.c index e7803c8169..db7a9e7201 100644 --- a/ext/mysqlnd/mysqlnd_loaddata.c +++ b/ext/mysqlnd/mysqlnd_loaddata.c @@ -181,11 +181,12 @@ mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename, zend_bool *is_w /* init handler: allocate read buffer and open file */ if (infile.local_infile_init(&info, (char *)filename, conn->infile.userdata TSRMLS_CC)) { + char tmp_buf[sizeof(conn->error_info.error)]; + int tmp_error_no; *is_warning = TRUE; /* error occured */ - strcpy(conn->error_info.sqlstate, UNKNOWN_SQLSTATE); - conn->error_info.error_no = - infile.local_infile_error(info, conn->error_info.error, sizeof(conn->error_info.error) TSRMLS_CC); + tmp_error_no = infile.local_infile_error(info, tmp_buf, sizeof(tmp_buf) TSRMLS_CC); + SET_CLIENT_ERROR(conn->error_info, tmp_error_no, UNKNOWN_SQLSTATE, tmp_buf); /* write empty packet to server */ ret = conn->net->m.send(conn, empty_packet, 0 TSRMLS_CC); goto infile_error; @@ -208,11 +209,12 @@ mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename, zend_bool *is_w /* error during read occured */ if (bufsize < 0) { + char tmp_buf[sizeof(conn->error_info.error)]; + int tmp_error_no; *is_warning = TRUE; DBG_ERR_FMT("Bufsize < 0, warning, %d %s %s", CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn); - strcpy(conn->error_info.sqlstate, UNKNOWN_SQLSTATE); - conn->error_info.error_no = - infile.local_infile_error(info, conn->error_info.error, sizeof(conn->error_info.error) TSRMLS_CC); + tmp_error_no = infile.local_infile_error(info, tmp_buf, sizeof(tmp_buf) TSRMLS_CC); + SET_CLIENT_ERROR(conn->error_info, tmp_error_no, UNKNOWN_SQLSTATE, tmp_buf); goto infile_error; } diff --git a/ext/mysqlnd/mysqlnd_priv.h b/ext/mysqlnd/mysqlnd_priv.h index 49ec127800..5bd688a285 100644 --- a/ext/mysqlnd/mysqlnd_priv.h +++ b/ext/mysqlnd/mysqlnd_priv.h @@ -134,15 +134,41 @@ (error_info).error_no = 0; \ (error_info).error[0] = '\0'; \ strlcpy((error_info).sqlstate, "00000", sizeof((error_info).sqlstate)); \ + if ((error_info).error_list) { \ + zend_llist_clean((error_info).error_list); \ + } \ } + #define SET_CLIENT_ERROR(error_info, a, b, c) \ - { \ +{ \ + if (0 == (a)) { \ + SET_EMPTY_ERROR((error_info)); \ + } else { \ (error_info).error_no = (a); \ strlcpy((error_info).sqlstate, (b), sizeof((error_info).sqlstate)); \ strlcpy((error_info).error, (c), sizeof((error_info).error)); \ + if ((error_info).error_list) {\ + MYSQLND_ERROR_LIST_ELEMENT error_for_the_list = {0}; \ + \ + error_for_the_list.error_no = (a); \ + strlcpy(error_for_the_list.sqlstate, (b), sizeof(error_for_the_list.sqlstate)); \ + error_for_the_list.error = mnd_pestrdup((c), TRUE); \ + if (error_for_the_list.error) { \ + DBG_INF_FMT("adding error [%s] to the list", error_for_the_list.error); \ + zend_llist_add_element((error_info).error_list, &error_for_the_list); \ + } \ + } \ + } \ +} + + +#define COPY_CLIENT_ERROR(error_info_to, error_info_from) \ + { \ + SET_CLIENT_ERROR((error_info_to), (error_info_from).error_no, (error_info_from).sqlstate, (error_info_from).error); \ } + #define SET_OOM_ERROR(error_info) SET_CLIENT_ERROR((error_info), CR_OUT_OF_MEMORY, UNKNOWN_SQLSTATE, mysqlnd_out_of_memory) diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c index 0930987094..e4688c1564 100644 --- a/ext/mysqlnd/mysqlnd_ps.c +++ b/ext/mysqlnd/mysqlnd_ps.c @@ -52,6 +52,22 @@ enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, static void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC); static void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, unsigned int param_no TSRMLS_DC); + +/* {{{ mysqlnd_ps_error_list_pdtor */ +static void +mysqlnd_ps_error_list_pdtor(void * pDest) +{ + MYSQLND_ERROR_LIST_ELEMENT * element = (MYSQLND_ERROR_LIST_ELEMENT *) pDest; + TSRMLS_FETCH(); + DBG_ENTER("mysqlnd_ps_error_list_pdtor"); + if (element->error) { + mnd_pefree(element->error, TRUE); + } + DBG_VOID_RETURN; +} +/* }}} */ + + /* {{{ mysqlnd_stmt::store_result */ static MYSQLND_RES * MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s TSRMLS_DC) @@ -110,7 +126,7 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s TSRMLS_DC) stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED; } else { - conn->error_info = result->stored_data->error_info; + COPY_CLIENT_ERROR(conn->error_info, result->stored_data->error_info); stmt->result->m.free_result_contents(stmt->result TSRMLS_CC); mnd_efree(stmt->result); stmt->result = NULL; @@ -177,7 +193,7 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s TSRMLS_DC) stmt->state = MYSQLND_STMT_PREPARED; result->type = MYSQLND_RES_PS_BUF; } else { - stmt->error_info = conn->error_info; + COPY_CLIENT_ERROR(stmt->error_info, conn->error_info); stmt->state = MYSQLND_STMT_PREPARED; break; } @@ -300,7 +316,8 @@ mysqlnd_stmt_read_prepare_response(MYSQLND_STMT * s TSRMLS_DC) } if (0xFF == prepare_resp->error_code) { - stmt->error_info = stmt->conn->error_info = prepare_resp->error_info; + COPY_CLIENT_ERROR(stmt->error_info, prepare_resp->error_info); + COPY_CLIENT_ERROR(stmt->conn->error_info, prepare_resp->error_info); goto done; } ret = PASS; @@ -484,7 +501,7 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s TSRMLS_DC) ret = mysqlnd_query_read_result_set_header(stmt->conn, s TSRMLS_CC); if (ret == FAIL) { - stmt->error_info = conn->error_info; + COPY_CLIENT_ERROR(stmt->error_info, conn->error_info); stmt->upsert_status.affected_rows = conn->upsert_status.affected_rows; if (CONN_GET_STATE(conn) == CONN_QUIT_SENT) { /* close the statement here, the connection has been closed */ @@ -685,7 +702,7 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const s TSRMLS_DC) } if (ret == FAIL) { - stmt->error_info = conn->error_info; + COPY_CLIENT_ERROR(stmt->error_info, conn->error_info); DBG_INF("FAIL"); DBG_RETURN(FAIL); } @@ -902,8 +919,8 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int *fetched_anything = TRUE; } else if (ret == FAIL) { if (row_packet->error_info.error_no) { - stmt->conn->error_info = row_packet->error_info; - stmt->error_info = row_packet->error_info; + COPY_CLIENT_ERROR(stmt->conn->error_info, row_packet->error_info); + COPY_CLIENT_ERROR(stmt->error_info, row_packet->error_info); } CONN_SET_STATE(result->conn, CONN_READY); result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */ @@ -1014,7 +1031,7 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla if (FAIL == stmt->conn->m->simple_command(stmt->conn, COM_STMT_FETCH, buf, sizeof(buf), PROT_LAST /* we will handle the response packet*/, FALSE, TRUE TSRMLS_CC)) { - stmt->error_info = stmt->conn->error_info; + COPY_CLIENT_ERROR(stmt->error_info, stmt->conn->error_info); DBG_RETURN(FAIL); } @@ -1218,7 +1235,7 @@ MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const s TSRMLS_DC) FAIL == (ret = conn->m->simple_command(conn, COM_STMT_RESET, cmd_buf, sizeof(cmd_buf), PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC))) { - stmt->error_info = conn->error_info; + COPY_CLIENT_ERROR(stmt->error_info, conn->error_info); } stmt->upsert_status = conn->upsert_status; @@ -1337,7 +1354,7 @@ MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const s, unsigned in ret = conn->m->simple_command(conn, cmd, cmd_buf, packet_len, PROT_LAST , FALSE, TRUE TSRMLS_CC); mnd_efree(cmd_buf); if (FAIL == ret) { - stmt->error_info = conn->error_info; + COPY_CLIENT_ERROR(stmt->error_info, conn->error_info); } } else { ret = FAIL; @@ -2117,6 +2134,11 @@ MYSQLND_METHOD(mysqlnd_stmt, free_stmt_content)(MYSQLND_STMT * const s TSRMLS_DC stmt->result->m.free_result_internal(stmt->result TSRMLS_CC); stmt->result = NULL; } + if (stmt->error_info.error_list) { + zend_llist_clean(stmt->error_info.error_list); + mnd_pefree(stmt->error_info.error_list, s->persistent); + stmt->error_info.error_list = NULL; + } DBG_VOID_RETURN; } @@ -2174,7 +2196,7 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const s, zend_boo FAIL == conn->m->simple_command(conn, COM_STMT_CLOSE, cmd_buf, sizeof(cmd_buf), PROT_LAST /* COM_STMT_CLOSE doesn't send an OK packet*/, FALSE, TRUE TSRMLS_CC)) { - stmt->error_info = conn->error_info; + COPY_CLIENT_ERROR(stmt->error_info, conn->error_info); DBG_RETURN(FAIL); } } @@ -2375,6 +2397,11 @@ MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC) or normal query result will close it then. */ stmt->conn = conn->m->get_reference(conn TSRMLS_CC); + stmt->error_info.error_list = mnd_pecalloc(1, sizeof(zend_llist), ret->persistent); + if (!stmt->error_info.error_list) { + break; + } + zend_llist_init(stmt->error_info.error_list, sizeof(MYSQLND_ERROR_LIST_ELEMENT), (llist_dtor_func_t) mysqlnd_ps_error_list_pdtor, conn->persistent); DBG_RETURN(ret); } while (0); diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c index f1c6332788..6262793803 100644 --- a/ext/mysqlnd/mysqlnd_result.c +++ b/ext/mysqlnd/mysqlnd_result.c @@ -400,7 +400,7 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT * s TSRMLS_DC) This will copy the error code and the messages, as they are buffers in the struct */ - conn->error_info = rset_header->error_info; + COPY_CLIENT_ERROR(conn->error_info, rset_header->error_info); ret = FAIL; DBG_ERR_FMT("error=%s", rset_header->error_info.error); /* Return back from CONN_QUERY_SENT */ @@ -705,7 +705,7 @@ mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES * result TSRMLS_DC) } } else if (ret == FAIL) { if (row_packet->error_info.error_no) { - result->conn->error_info = row_packet->error_info; + COPY_CLIENT_ERROR(result->conn->error_info, row_packet->error_info); DBG_ERR_FMT("errorno=%u error=%s", row_packet->error_info.error_no, row_packet->error_info.error); } CONN_SET_STATE(result->conn, CONN_READY); @@ -840,7 +840,7 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES * result, void *param, unsigned int fla result->unbuf->row_count++; } else if (ret == FAIL) { if (row_packet->error_info.error_no) { - result->conn->error_info = row_packet->error_info; + COPY_CLIENT_ERROR(result->conn->error_info, row_packet->error_info); DBG_ERR_FMT("errorno=%u error=%s", row_packet->error_info.error_no, row_packet->error_info.error); } CONN_SET_STATE(result->conn, CONN_READY); @@ -1228,7 +1228,7 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND * const conn, MYSQL } if (ret == FAIL) { - set->error_info = row_packet->error_info; + COPY_CLIENT_ERROR(set->error_info, row_packet->error_info); } else { /* Position at the first row */ set->data_cursor = set->data; @@ -1277,7 +1277,7 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result, ret = result->m.store_result_fetch_data(conn, result, result->meta, ps_protocol TSRMLS_CC); if (FAIL == ret) { if (result->stored_data) { - conn->error_info = result->stored_data->error_info; + COPY_CLIENT_ERROR(conn->error_info, result->stored_data->error_info); } else { SET_OOM_ERROR(conn->error_info); } diff --git a/ext/mysqlnd/mysqlnd_result_meta.c b/ext/mysqlnd/mysqlnd_result_meta.c index fbef4daf8c..51588016a4 100644 --- a/ext/mysqlnd/mysqlnd_result_meta.c +++ b/ext/mysqlnd/mysqlnd_result_meta.c @@ -170,7 +170,7 @@ MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const met DBG_RETURN(FAIL); } if (field_packet->error_info.error_no) { - conn->error_info = field_packet->error_info; + COPY_CLIENT_ERROR(conn->error_info, field_packet->error_info); /* Return back from CONN_QUERY_SENT */ PACKET_FREE(field_packet); DBG_RETURN(FAIL); diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h index edea2e895c..94af6ced24 100644 --- a/ext/mysqlnd/mysqlnd_structs.h +++ b/ext/mysqlnd/mysqlnd_structs.h @@ -102,9 +102,18 @@ typedef struct st_mysqlnd_error_info char error[MYSQLND_ERRMSG_SIZE+1]; char sqlstate[MYSQLND_SQLSTATE_LENGTH + 1]; unsigned int error_no; + zend_llist * error_list; } MYSQLND_ERROR_INFO; +typedef struct st_mysqlnd_error_list_element +{ + char * error; + char sqlstate[MYSQLND_SQLSTATE_LENGTH + 1]; + unsigned int error_no; +} MYSQLND_ERROR_LIST_ELEMENT; + + typedef struct st_mysqlnd_infile_info { php_stream *fd;