diff options
35 files changed, 4159 insertions, 1556 deletions
diff --git a/ext/mysql/package.xml b/ext/mysql/package.xml index 455349a970..25506b4585 100644 --- a/ext/mysql/package.xml +++ b/ext/mysql/package.xml @@ -47,6 +47,7 @@ package.xml added to support installation using pear installer <file role="src" name="mysql.mak"/> <file role="src" name="php_mysql.c"/> <file role="src" name="php_mysql.h"/> + <file role="src" name="mysql_mysqlnd.h"/> <file role="test" name="tests/001.phpt"/> <file role="test" name="tests/002.phpt"/> <file role="test" name="tests/003.phpt"/> diff --git a/ext/mysql/php_mysql.c b/ext/mysql/php_mysql.c index b483c76e0a..accc9783bc 100644 --- a/ext/mysql/php_mysql.c +++ b/ext/mysql/php_mysql.c @@ -1,6 +1,6 @@ /* +----------------------------------------------------------------------+ - | PHP Version 5 | + | PHP Version 6 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2007 The PHP Group | +----------------------------------------------------------------------+ @@ -132,7 +132,7 @@ static MYSQLND_QCACHE *mysql_mysqlnd_qcache; /* {{{ mysql_functions[] */ -const zend_function_entry mysql_functions[] = { +static const zend_function_entry mysql_functions[] = { PHP_FE(mysql_connect, NULL) PHP_FE(mysql_pconnect, NULL) PHP_FE(mysql_close, NULL) @@ -469,8 +469,11 @@ PHP_MSHUTDOWN_FUNCTION(mysql) #if MYSQL_VERSION_ID >= 40000 #ifdef PHP_WIN32 unsigned long client_ver = mysql_get_client_version(); - /* Can't call mysql_server_end() multiple times prior to 5.0.42 on Windows */ - if ((client_ver > 50042 && client_ver < 50100) || client_ver > 50122) { + /* + Can't call mysql_server_end() multiple times prior to 5.0.42 on Windows. + PHP bug#41350 MySQL bug#25621 + */ + if ((client_ver >= 50042 && client_ver < 50100) || client_ver > 50122) { mysql_server_end(); } #else @@ -1764,6 +1767,9 @@ PHP_FUNCTION(mysql_escape_string) /* Now we kind of realloc() by returning a zval pointing to a buffer with enough size */ RETVAL_UTF8_STRINGL(new_str, new_str_len, ZSTR_DUPLICATE); efree(new_str); + if (MySG(trace_mode)){ + php_error_docref("function.mysql-real-escape-string" TSRMLS_CC, E_WARNING, "This function is deprecated; use mysql_real_escape_string() instead."); + } } /* }}} */ diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c index c68ded2d7c..5a0b3ec427 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -72,15 +72,53 @@ typedef struct _mysqli_prop_handler { static int le_pmysqli; -/* Destructor for Persistent Connections */ -ZEND_RSRC_DTOR_FUNC(php_mysqli_dtor) + +static int php_mysqli_persistent_on_rshut(zend_rsrc_list_entry *le TSRMLS_DC) { - if (rsrc->ptr) { - MYSQL *mysql = (MYSQL *)rsrc->ptr; + if (le->type == le_pmysqli) { + mysqli_plist_entry *plist = (mysqli_plist_entry *) le->ptr; + HashPosition pos; + MYSQL **mysql; + ulong idx; + dtor_func_t pDestructor = plist->used_links.pDestructor; + plist->used_links.pDestructor = NULL; /* Don't call pDestructor now */ + + zend_hash_internal_pointer_reset_ex(&plist->used_links, &pos); + while (SUCCESS == zend_hash_get_current_data_ex(&plist->used_links, (void **)&mysql, &pos)) { + zend_hash_get_current_key_ex(&plist->used_links, NULL, NULL, &idx, FALSE, &pos); + /* Make it free */ + zend_hash_next_index_insert(&plist->free_links, mysql, sizeof(MYSQL *), NULL); + /* First move forward */ + zend_hash_move_forward_ex(&plist->used_links, &pos); + /* The delete, because del will free memory, but we need it's ->nextItem */ + zend_hash_index_del(&plist->used_links, idx); + } + + /* restore pDestructor, which should be php_mysqli_dtor_p_elements() */ + plist->used_links.pDestructor = pDestructor; + } + return ZEND_HASH_APPLY_KEEP; +} + +/* Destructor for mysqli entries in free_links/used_links */ +void php_mysqli_dtor_p_elements(void *data) +{ + MYSQL **mysql = (MYSQL **) data; + TSRMLS_FETCH(); #if defined(HAVE_MYSQLND) - mysqlnd_end_psession(mysql); + mysqlnd_end_psession(*mysql); #endif - mysqli_close(mysql, MYSQLI_CLOSE_IMPLICIT); + mysqli_close(*mysql, MYSQLI_CLOSE_IMPLICIT); +} + + +ZEND_RSRC_DTOR_FUNC(php_mysqli_dtor) +{ + if (rsrc->ptr) { + mysqli_plist_entry *plist = (mysqli_plist_entry *) rsrc->ptr; + zend_hash_destroy(&plist->free_links); + zend_hash_destroy(&plist->used_links); + free(plist); } } @@ -162,6 +200,10 @@ void php_clear_stmt_bind(MY_STMT *stmt TSRMLS_DC) /* {{{ php_clear_mysql */ void php_clear_mysql(MY_MYSQL *mysql) { + if (mysql->hash_key) { + efree(mysql->hash_key); + mysql->hash_key = NULL; + } if (mysql->li_read) { efree(Z_STRVAL_P(mysql->li_read)); FREE_ZVAL(mysql->li_read); @@ -508,6 +550,8 @@ ZEND_GET_MODULE(mysqli) */ PHP_INI_BEGIN() STD_PHP_INI_ENTRY_EX("mysqli.max_links", "-1", PHP_INI_SYSTEM, OnUpdateLong, max_links, zend_mysqli_globals, mysqli_globals, display_link_numbers) + STD_PHP_INI_ENTRY_EX("mysqli.max_persistent", "-1", PHP_INI_SYSTEM, OnUpdateLong, max_persistent, zend_mysqli_globals, mysqli_globals, display_link_numbers) + STD_PHP_INI_BOOLEAN("mysqli.allow_persistent", "1", PHP_INI_SYSTEM, OnUpdateLong, allow_persistent, zend_mysqli_globals, mysqli_globals) STD_PHP_INI_ENTRY("mysqli.default_host", NULL, PHP_INI_ALL, OnUpdateString, default_host, zend_mysqli_globals, mysqli_globals) STD_PHP_INI_ENTRY("mysqli.default_user", NULL, PHP_INI_ALL, OnUpdateString, default_user, zend_mysqli_globals, mysqli_globals) STD_PHP_INI_ENTRY("mysqli.default_pw", NULL, PHP_INI_ALL, OnUpdateString, default_pw, zend_mysqli_globals, mysqli_globals) @@ -527,7 +571,11 @@ PHP_INI_END() static PHP_GINIT_FUNCTION(mysqli) { mysqli_globals->num_links = 0; + mysqli_globals->num_active_persistent = 0; + mysqli_globals->num_inactive_persistent = 0; mysqli_globals->max_links = -1; + mysqli_globals->max_persistent = -1; + mysqli_globals->allow_persistent = 1; mysqli_globals->default_port = 0; mysqli_globals->default_host = NULL; mysqli_globals->default_user = NULL; @@ -559,9 +607,11 @@ PHP_MINIT_FUNCTION(mysqli) REGISTER_INI_ENTRIES(); #ifndef HAVE_MYSQLND +#if MYSQL_VERSION_ID >= 40000 if (mysql_server_init(0, NULL, NULL)) { return FAILURE; } +#endif #else mysqli_mysqlnd_zval_cache = mysqlnd_palloc_init_cache(MyG(cache_size)); mysqli_mysqlnd_qcache = mysqlnd_qcache_init_cache(); @@ -631,6 +681,8 @@ PHP_MINIT_FUNCTION(mysqli) REGISTER_LONG_CONSTANT("MYSQLI_INIT_COMMAND", MYSQL_INIT_COMMAND, CONST_CS | CONST_PERSISTENT); #if defined(HAVE_MYSQLND) REGISTER_LONG_CONSTANT("MYSQLI_OPT_NUMERIC_AND_DATETIME_AS_UNICODE", MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("MYSQLI_OPT_NET_CMD_BUFFER_SIZE", MYSQLND_OPT_NET_CMD_BUFFER_SIZE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("MYSQLI_OPT_NET_READ_BUFFER_SIZE", MYSQLND_OPT_NET_READ_BUFFER_SIZE, CONST_CS | CONST_PERSISTENT); #endif #ifdef MYSQLND_STRING_TO_INT_CONVERSION REGISTER_LONG_CONSTANT("MYSQLI_OPT_INT_AND_YEAR_AS_INT", MYSQLND_OPT_INT_AND_YEAR_AS_INT, CONST_CS | CONST_PERSISTENT); @@ -746,15 +798,20 @@ PHP_MINIT_FUNCTION(mysqli) PHP_MSHUTDOWN_FUNCTION(mysqli) { #ifndef HAVE_MYSQLND +#if MYSQL_VERSION_ID >= 40000 #ifdef PHP_WIN32 - unsigned long client_ver = mysql_get_client_version; - /* Can't call mysql_server_end() multiple times prior to 5.0.42 on Windows */ - if ((client_ver > 50042 && client_ver < 50100) || client_ver > 50122) { + unsigned long client_ver = mysql_get_client_version(); + /* + Can't call mysql_server_end() multiple times prior to 5.0.42 on Windows. + PHP bug#41350 MySQL bug#25621 + */ + if ((client_ver >= 50042 && client_ver < 50100) || client_ver > 50122) { mysql_server_end(); } #else mysql_server_end(); #endif +#endif #else mysqlnd_palloc_free_cache(mysqli_mysqlnd_zval_cache); mysqlnd_qcache_free_cache_reference(&mysqli_mysqlnd_qcache); @@ -776,7 +833,7 @@ PHP_MSHUTDOWN_FUNCTION(mysqli) */ PHP_RINIT_FUNCTION(mysqli) { -#if !defined(HAVE_MYSQLND) && defined(ZTS) +#if !defined(HAVE_MYSQLND) && defined(ZTS) && MYSQL_VERSION_ID >= 40000 if (mysql_thread_init()) { return FAILURE; } @@ -795,7 +852,10 @@ PHP_RINIT_FUNCTION(mysqli) */ PHP_RSHUTDOWN_FUNCTION(mysqli) { -#if !defined(HAVE_MYSQLND) && defined(ZTS) + /* check persistent connections, move used to free */ + zend_hash_apply(&EG(persistent_list), (apply_func_t) php_mysqli_persistent_on_rshut TSRMLS_CC); + +#if !defined(HAVE_MYSQLND) && defined(ZTS) && MYSQL_VERSION_ID >= 40000 mysql_thread_end(); #endif if (MyG(error_msg)) { @@ -813,6 +873,10 @@ PHP_RSHUTDOWN_FUNCTION(mysqli) */ PHP_MINFO_FUNCTION(mysqli) { +#if defined(HAVE_MYSQLND) + char buf[32]; +#endif + php_info_print_table_start(); php_info_print_table_header(2, "MysqlI Support", "enabled"); php_info_print_table_row(2, "Client API library version", mysql_get_client_info()); @@ -820,6 +884,12 @@ PHP_MINFO_FUNCTION(mysqli) php_info_print_table_row(2, "Client API header version", MYSQL_SERVER_VERSION); php_info_print_table_row(2, "MYSQLI_SOCKET", MYSQL_UNIX_ADDR); #else + snprintf(buf, sizeof(buf), "%ld", MyG(num_active_persistent)); + php_info_print_table_row(2, "Active Persistent Links", buf); + snprintf(buf, sizeof(buf), "%ld", MyG(num_inactive_persistent)); + php_info_print_table_row(2, "Inactive Persistent Links", buf); + snprintf(buf, sizeof(buf), "%ld", MyG(num_links)); + php_info_print_table_row(2, "Active Links", buf); { zval values; @@ -1151,6 +1221,9 @@ PHP_MYSQLI_API void php_mysqli_set_error(long mysql_errno, char *mysql_err TSRML } /* }}} */ +#if !defined(HAVE_MYSQLND) + + #define ALLOC_CALLBACK_ARGS(a, b, c)\ if (c) {\ a = (zval ***)safe_emalloc(c, sizeof(zval **), 0);\ @@ -1171,27 +1244,23 @@ if (a) {\ #define LOCAL_INFILE_ERROR_MSG(source,dest)\ memset(source, 0, LOCAL_INFILE_ERROR_LEN);\ -memcpy(source, dest, LOCAL_INFILE_ERROR_LEN-1);\ +memcpy(source, dest, MIN(strlen(dest), LOCAL_INFILE_ERROR_LEN-1));\ php_error_docref(NULL TSRMLS_CC, E_WARNING, dest); + /* {{{ void php_set_local_infile_handler_default */ void php_set_local_infile_handler_default(MY_MYSQL *mysql) { /* register internal callback functions */ -#if !defined(HAVE_MYSQLND) mysql_set_local_infile_handler(mysql->mysql, &php_local_infile_init, &php_local_infile_read, &php_local_infile_end, &php_local_infile_error, (void *)mysql); if (mysql->li_read) { zval_ptr_dtor(&mysql->li_read); mysql->li_read = NULL; } -#else - mysqlnd_local_infile_default(mysql->mysql, TRUE); -#endif } /* }}} */ -#if !defined(HAVE_MYSQLND) /* {{{ php_local_infile_init */ int php_local_infile_init(void **ptr, const char *filename, void *userdata) @@ -1304,6 +1373,16 @@ int php_local_infile_read(void *ptr, char *buf, uint buf_len) LOCAL_INFILE_ERROR_MSG(data->error_msg, "Can't execute load data local init callback function"); rc = -1; } + /* + If the (ab)user has closed the file handle we should + not try to use it anymore or even close it + */ + if (!zend_rsrc_list_get_rsrc_type(Z_LVAL_P(fp) TSRMLS_CC)) { + LOCAL_INFILE_ERROR_MSG(data->error_msg, "File handle closed"); + rc = -1; + /* Thus the end handler won't try to free already freed memory */ + mysql->li_stream = NULL; + } FREE_CALLBACK_ARGS(callback_args, 1, argc); efree(fp); @@ -1343,8 +1422,9 @@ void php_local_infile_end(void *ptr) } return; } - - php_stream_close(mysql->li_stream); + if (mysql->li_stream) { + php_stream_close(mysql->li_stream); + } free(data); return; } diff --git a/ext/mysqli/mysqli_api.c b/ext/mysqli/mysqli_api.c index 4d806018e6..19195d2719 100644 --- a/ext/mysqli/mysqli_api.c +++ b/ext/mysqli/mysqli_api.c @@ -28,6 +28,7 @@ #include "php.h" #include "php_ini.h" +#include "php_globals.h" #include "ext/standard/info.h" #include "php_mysqli_structs.h" @@ -518,6 +519,11 @@ PHP_FUNCTION(mysqli_change_user) RETURN_FALSE; } + /* Change user resets the charset in the server, change it back */ + if (UG(unicode)) { + mysql_set_character_set(mysql->mysql, "utf8"); + } + RETURN_TRUE; } /* }}} */ @@ -552,12 +558,31 @@ PHP_FUNCTION(mysqli_close) MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_INITIALIZED); - php_clear_mysql(mysql); if (!mysql->persistent) { mysqli_close(mysql->mysql, MYSQLI_CLOSE_EXPLICIT); + mysql->mysql = NULL; + } else { + zend_rsrc_list_entry *le; + if (zend_hash_find(&EG(persistent_list), mysql->hash_key, strlen(mysql->hash_key) + 1, (void **)&le) == SUCCESS) { + if (Z_TYPE_P(le) == php_le_pmysqli()) { + mysqli_plist_entry *plist = (mysqli_plist_entry *) le->ptr; + dtor_func_t pDestructor = plist->used_links.pDestructor; + + plist->used_links.pDestructor = NULL; /* Don't call pDestructor now */ + zend_hash_index_del(&plist->used_links, mysql->hash_index); + plist->used_links.pDestructor = pDestructor; /* Restore the destructor */ + + zend_hash_next_index_insert(&plist->free_links, &mysql->mysql, sizeof(MYSQL *), NULL); + MyG(num_links)--; + MyG(num_active_persistent)--; + MyG(num_inactive_persistent)++; + } + } } + php_clear_mysql(mysql); + MYSQLI_CLEAR_RESOURCE(&mysql_link); efree(mysql); RETURN_TRUE; @@ -1365,6 +1390,7 @@ PHP_FUNCTION(mysqli_kill) /* {{{ proto void mysqli_set_local_infile_default(object link) U unsets user defined handler for load local infile command */ +#if !defined(HAVE_MYSQLND) PHP_FUNCTION(mysqli_set_local_infile_default) { MY_MYSQL *mysql; @@ -1376,15 +1402,10 @@ PHP_FUNCTION(mysqli_set_local_infile_default) MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID); -#if !defined(HAVE_MYSQLND) if (mysql->li_read) { - efree(Z_STRVAL_P(mysql->li_read)); zval_dtor(mysql->li_read); mysql->li_read = NULL; } -#else - mysqlnd_local_infile_default(mysql->mysql, TRUE); -#endif } /* }}} */ @@ -1417,19 +1438,16 @@ PHP_FUNCTION(mysqli_set_local_infile_handler) zval_dtor(&callback_name); /* save callback function */ -#if !defined(HAVE_MYSQLND) if (!mysql->li_read) { MAKE_STD_ZVAL(mysql->li_read); } else { zval_dtor(mysql->li_read); } ZVAL_STRINGL(mysql->li_read, Z_STRVAL_P(callback_func), Z_STRLEN_P(callback_func), 1); -#else - mysqlnd_set_local_infile_handler(mysql->mysql, callback_func->value.str.val); -#endif RETURN_TRUE; } +#endif /* }}} */ /* {{{ proto bool mysqli_more_results(object link) U @@ -1778,7 +1796,7 @@ PHP_FUNCTION(mysqli_real_escape_string) { newstr_len = mysql_real_escape_string(mysql->mysql, newstr, escapestr, escapestr_len); newstr = erealloc(newstr, newstr_len + 1); - RETURN_UTF8_STRING(newstr, ZSTR_AUTOFREE); + RETURN_UTF8_STRINGL(newstr, newstr_len, ZSTR_AUTOFREE); } /* }}} */ @@ -2290,7 +2308,10 @@ PHP_FUNCTION(mysqli_stmt_store_result) int i = 0; for (i = mysql_stmt_field_count(stmt->stmt) - 1; i >=0; --i) { - if (stmt->stmt->fields && stmt->stmt->fields[i].type == MYSQL_TYPE_BLOB) { + if (stmt->stmt->fields && (stmt->stmt->fields[i].type == MYSQL_TYPE_BLOB || + stmt->stmt->fields[i].type == MYSQL_TYPE_MEDIUM_BLOB || + stmt->stmt->fields[i].type == MYSQL_TYPE_LONG_BLOB)) + { my_bool tmp=1; mysql_stmt_attr_set(stmt->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &tmp); break; diff --git a/ext/mysqli/mysqli_driver.c b/ext/mysqli/mysqli_driver.c index 2418bbc0b4..e1d3880a89 100644 --- a/ext/mysqli/mysqli_driver.c +++ b/ext/mysqli/mysqli_driver.c @@ -142,7 +142,7 @@ ZEND_FUNCTION(mysqli_driver_construct) ((mysqli_object *) zend_object_store_get_object(getThis() TSRMLS_CC))->ptr = mysqli_resource; } -mysqli_property_entry mysqli_driver_property_entries[] = { +const mysqli_property_entry mysqli_driver_property_entries[] = { {"client_info", driver_client_info_read, NULL}, {"client_version", driver_client_version_read, NULL}, {"driver_version", driver_driver_version_read, NULL}, diff --git a/ext/mysqli/mysqli_fe.c b/ext/mysqli/mysqli_fe.c index cae2188a0a..19a0412505 100644 --- a/ext/mysqli/mysqli_fe.c +++ b/ext/mysqli/mysqli_fe.c @@ -119,8 +119,10 @@ const zend_function_entry mysqli_functions[] = { PHP_FE(mysqli_info, NULL) PHP_FE(mysqli_insert_id, NULL) PHP_FE(mysqli_kill, NULL) +#if !defined(HAVE_MYSQLND) PHP_FE(mysqli_set_local_infile_default, NULL) PHP_FE(mysqli_set_local_infile_handler, NULL) +#endif PHP_FE(mysqli_more_results, NULL) PHP_FE(mysqli_multi_query, NULL) PHP_FE(mysqli_next_result, NULL) @@ -227,8 +229,10 @@ const zend_function_entry mysqli_link_methods[] = { PHP_FALIAS(get_warnings, mysqli_get_warnings, NULL) PHP_FALIAS(init,mysqli_init,NULL) PHP_FALIAS(kill,mysqli_kill,NULL) +#if !defined(HAVE_MYSQLND) PHP_FALIAS(set_local_infile_default,mysqli_set_local_infile_default,NULL) PHP_FALIAS(set_local_infile_handler,mysqli_set_local_infile_handler,NULL) +#endif PHP_FALIAS(multi_query,mysqli_multi_query,NULL) PHP_FALIAS(mysqli,mysqli_connect,NULL) PHP_FALIAS(more_results,mysqli_more_results, NULL) diff --git a/ext/mysqli/mysqli_mysqlnd.h b/ext/mysqli/mysqli_mysqlnd.h index e5cd4a14ea..2aaa61d0d8 100644 --- a/ext/mysqli/mysqli_mysqlnd.h +++ b/ext/mysqli/mysqli_mysqlnd.h @@ -19,8 +19,8 @@ */ -#ifndef MYSQL_MYSQLND_H -#define MYSQL_MYSQLND_H +#ifndef MYSQLI_MYSQLND_H +#define MYSQLI_MYSQLND_H #include "ext/mysqlnd/mysqlnd_libmysql_compat.h" diff --git a/ext/mysqli/mysqli_nonapi.c b/ext/mysqli/mysqli_nonapi.c index d2559f8e01..f8407c4117 100644 --- a/ext/mysqli/mysqli_nonapi.c +++ b/ext/mysqli/mysqli_nonapi.c @@ -38,17 +38,18 @@ Open a connection to a mysql server */ PHP_FUNCTION(mysqli_connect) { - MY_MYSQL *mysql; - MYSQLI_RESOURCE *mysqli_resource; - zval *object = getThis(); - char *hostname = NULL, *username=NULL, *passwd=NULL, *dbname=NULL, *socket=NULL; - unsigned int hostname_len = 0, username_len = 0, passwd_len = 0, dbname_len = 0, socket_len = 0; + MY_MYSQL *mysql = NULL; + MYSQLI_RESOURCE *mysqli_resource = NULL; + zval *object = getThis(); + char *hostname = NULL, *username=NULL, *passwd=NULL, *dbname=NULL, *socket=NULL; + unsigned int hostname_len = 0, username_len = 0, passwd_len = 0, dbname_len = 0, socket_len = 0; zend_bool persistent = FALSE; - long port=0; + long port = 0; uint hash_len; char *hash_key = NULL; zend_bool new_connection = FALSE; zend_rsrc_list_entry *le; + mysqli_plist_entry *plist = NULL; if (getThis() && !ZEND_NUM_ARGS()) { RETURN_NULL(); @@ -77,60 +78,123 @@ PHP_FUNCTION(mysqli_connect) hostname = MyG(default_host); } - mysql = (MY_MYSQL *) ecalloc(1, sizeof(MY_MYSQL)); - if (strlen(SAFE_STR(hostname)) > 2 && !strncasecmp(hostname, "p:", 2)) { - mysql->persistent = persistent = TRUE; - hostname += 2; - - if (!strlen(hostname)) { - hostname = MyG(default_host); + if (object && instanceof_function(Z_OBJCE_P(object), mysqli_link_class_entry TSRMLS_CC)) { + mysqli_resource = ((mysqli_object *) zend_object_store_get_object(object TSRMLS_CC))->ptr; + if (mysqli_resource && mysqli_resource->ptr && + mysqli_resource->status >= MYSQLI_STATUS_INITIALIZED) + { + mysql = (MY_MYSQL*)mysqli_resource->ptr; + php_clear_mysql(mysql); + if (mysql->mysql) { + mysqli_close(mysql->mysql, MYSQLI_CLOSE_EXPLICIT); + mysql->mysql = NULL; + } } + } + if (!mysql) { + mysql = (MY_MYSQL *) ecalloc(1, sizeof(MY_MYSQL)); + } - /* caclulate hash length: mysqli_ + Hostname + 5 (Port) + username + dbname + pw */ - hash_len = 7 + strlen(SAFE_STR(hostname)) + 5 + strlen(SAFE_STR(username)) - + strlen(SAFE_STR(dbname)) + strlen(SAFE_STR(passwd)) + 1; - - hash_key = emalloc(hash_len); - hash_len = snprintf(hash_key, hash_len, "mysqli_%s%ld%s%s%s", SAFE_STR(hostname), - port, SAFE_STR(username), SAFE_STR(dbname), - SAFE_STR(passwd)); - - /* check if we can reuse exisiting connection ... */ - if (zend_hash_find(&EG(persistent_list), hash_key, hash_len + 1, (void **)&le) == SUCCESS) { - if (Z_TYPE_P(le) == php_le_pmysqli()) { - mysql->mysql = (MYSQL *)le->ptr; - - /* reset variables */ - /* todo: option for ping or change_user */ + if (strlen(SAFE_STR(hostname)) > 2 && !strncasecmp(hostname, "p:", 2)) { + hostname += 2; + if (!MyG(allow_persistent)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Persistent connections are disabled. Downgrading to normal"); + } else { + mysql->persistent = persistent = TRUE; + + if (!strlen(hostname)) { + hostname = MyG(default_host); + } + + hash_len = spprintf(&hash_key, 0, "mysqli_%s%ld%s%s%s", SAFE_STR(hostname), + port, SAFE_STR(username), SAFE_STR(dbname), + SAFE_STR(passwd)); + + /* check if we can reuse exisiting connection ... */ + if (zend_hash_find(&EG(persistent_list), hash_key, hash_len + 1, (void **)&le) == SUCCESS) { + if (Z_TYPE_P(le) == php_le_pmysqli()) { + plist = (mysqli_plist_entry *) le->ptr; + + do { + if (zend_hash_num_elements(&plist->free_links)) { + HashPosition pos; + MYSQL **free_mysql; + ulong idx; + dtor_func_t pDestructor = plist->free_links.pDestructor; + + zend_hash_internal_pointer_reset_ex(&plist->free_links, &pos); + if (SUCCESS != zend_hash_get_current_data_ex(&plist->free_links, + (void **)&free_mysql, &pos)) { + break; + } + if (HASH_KEY_IS_LONG != zend_hash_get_current_key_ex(&plist->free_links, NULL, + NULL, &idx, FALSE, &pos)) { + break; + } + mysql->mysql = *free_mysql; + plist->free_links.pDestructor = NULL; /* Don't call pDestructor now */ + if (SUCCESS != zend_hash_index_del(&plist->free_links, idx)) { + plist->used_links.pDestructor = pDestructor; /* Restore the destructor */ + break; + } + plist->used_links.pDestructor = pDestructor; /* Restore the destructor */ + MyG(num_inactive_persistent)--; + MyG(num_active_persistent)++; + + /* reset variables */ + /* todo: option for ping or change_user */ #if G0 - if (!mysql_change_user(mysql->mysql, username, passwd, dbname)) { + if (!mysql_change_user(mysql->mysql, username, passwd, dbname)) { +#else + if (!mysql_ping(mysql->mysql)) { #endif - if (!mysql_ping(mysql->mysql)) { #ifdef HAVE_MYSQLND - mysqlnd_restart_psession(mysql->mysql); + mysqlnd_restart_psession(mysql->mysql); #endif - goto end; + idx = zend_hash_next_free_element(&plist->used_links); + if (SUCCESS != zend_hash_next_index_insert(&plist->used_links, &free_mysql, + sizeof(MYSQL *), NULL)) { + php_mysqli_dtor_p_elements(free_mysql); + break; + } + mysql->hash_index = idx; + mysql->hash_key = hash_key; + goto end; + } + } + } while (0); } - /* - Here we fall if the connection is not ok. - When we update EG(persistent_list) with a new connection, this one will - get destructed. No need to do it explicitly. - */ - } + } else { + zend_rsrc_list_entry le; + le.type = php_le_pmysqli(); + le.ptr = plist = calloc(1, sizeof(mysqli_plist_entry)); + + zend_hash_init(&plist->free_links, MAX(10, MyG(max_persistent)), NULL, php_mysqli_dtor_p_elements, 1); + zend_hash_init(&plist->used_links, MAX(10, MyG(max_persistent)), NULL, php_mysqli_dtor_p_elements, 1); + zend_hash_update(&EG(persistent_list), hash_key, hash_len + 1, (void *)&le, sizeof(le), NULL); + } } } + if (MyG(max_links) != -1 && MyG(num_links) >= MyG(max_links)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Too many open links (%ld)", MyG(num_links)); + goto err; + } + if (persistent && MyG(max_persistent) != -1 && + (MyG(num_active_persistent) + MyG(num_inactive_persistent))>= MyG(max_persistent)) + { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Too many open persistent links (%ld)", + MyG(num_active_persistent) + MyG(num_inactive_persistent)); + goto err; + } + #if !defined(HAVE_MYSQLND) if (!(mysql->mysql = mysql_init(NULL))) { #else if (!(mysql->mysql = mysqlnd_init(persistent))) { #endif - efree(mysql); - if (persistent) { - efree(hash_key); - } - RETURN_FALSE; + goto err; } new_connection = TRUE; @@ -138,7 +202,6 @@ PHP_FUNCTION(mysqli_connect) mysql_options(mysql->mysql, MYSQL_SET_CHARSET_NAME, "utf8"); } - #ifdef HAVE_EMBEDDED_MYSQLI if (hostname_len) { unsigned int external=1; @@ -162,11 +225,7 @@ PHP_FUNCTION(mysqli_connect) /* free mysql structure */ mysqli_close(mysql->mysql, MYSQLI_CLOSE_DISCONNECTED); - efree(mysql); - if (persistent) { - efree(hash_key); - } - RETURN_FALSE; + goto err; } /* when PHP runs in unicode, set default character set to utf8 */ @@ -187,38 +246,48 @@ PHP_FUNCTION(mysqli_connect) mysql_options(mysql->mysql, MYSQL_OPT_LOCAL_INFILE, (char *)&MyG(allow_local_infile)); end: - mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE)); - mysqli_resource->ptr = (void *)mysql; + if (!mysqli_resource) { + mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE)); + mysqli_resource->ptr = (void *)mysql; + } mysqli_resource->status = MYSQLI_STATUS_VALID; /* store persistent connection */ if (persistent && new_connection) { - zend_rsrc_list_entry le; - - le.type = php_le_pmysqli(); - le.ptr = mysql->mysql; - + ulong hash_index = zend_hash_next_free_element(&plist->used_links); /* save persistent connection */ - if (zend_hash_update(&EG(persistent_list), hash_key, hash_len + 1, (void *)&le, - sizeof(le), NULL) == FAILURE) { + if (SUCCESS != zend_hash_next_index_insert(&plist->used_links, &mysql->mysql, + sizeof(MYSQL *), NULL)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't store persistent connection"); - } - } - if (persistent) { - efree(hash_key); + } else { + mysql->hash_index = hash_index; + } + MyG(num_active_persistent)++; } + mysql->hash_key = hash_key; + MyG(num_links)++; + #if !defined(HAVE_MYSQLND) mysql->multi_query = 0; #else mysql->multi_query = 1; #endif + if (!object || !instanceof_function(Z_OBJCE_P(object), mysqli_link_class_entry TSRMLS_CC)) { MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_link_class_entry); } else { ((mysqli_object *) zend_object_store_get_object(object TSRMLS_CC))->ptr = mysqli_resource; } + return; + +err: + efree(mysql); + if (persistent) { + efree(hash_key); + } + RETVAL_FALSE; } /* }}} */ diff --git a/ext/mysqli/mysqli_prop.c b/ext/mysqli/mysqli_prop.c index a6ea40eca5..7ac113bb59 100644 --- a/ext/mysqli/mysqli_prop.c +++ b/ext/mysqli/mysqli_prop.c @@ -313,7 +313,7 @@ MYSQLI_MAP_PROPERTY_FUNC_STRING(stmt_error_read, mysql_stmt_error, MYSQLI_GET_ST MYSQLI_MAP_PROPERTY_FUNC_STRING(stmt_sqlstate_read, mysql_stmt_sqlstate, MYSQLI_GET_STMT(MYSQLI_STATUS_INITIALIZED)); /* }}} */ -mysqli_property_entry mysqli_link_property_entries[] = { +const mysqli_property_entry mysqli_link_property_entries[] = { {"affected_rows", link_affected_rows_read, NULL}, {"client_info", link_client_info_read, NULL}, {"client_version", link_client_version_read, NULL}, @@ -334,7 +334,7 @@ mysqli_property_entry mysqli_link_property_entries[] = { {NULL, NULL, NULL} }; -mysqli_property_entry mysqli_result_property_entries[] = { +const mysqli_property_entry mysqli_result_property_entries[] = { {"current_field", result_current_field_read, NULL}, {"field_count", result_field_count_read, NULL}, {"lengths", result_lengths_read, NULL}, @@ -343,7 +343,7 @@ mysqli_property_entry mysqli_result_property_entries[] = { {NULL, NULL, NULL} }; -mysqli_property_entry mysqli_stmt_property_entries[] = { +const mysqli_property_entry mysqli_stmt_property_entries[] = { {"affected_rows", stmt_affected_rows_read, NULL}, {"insert_id", stmt_insert_id_read, NULL}, {"num_rows", stmt_num_rows_read, NULL}, diff --git a/ext/mysqli/mysqli_warning.c b/ext/mysqli/mysqli_warning.c index 685995e20b..4ad4fcfd13 100644 --- a/ext/mysqli/mysqli_warning.c +++ b/ext/mysqli/mysqli_warning.c @@ -321,7 +321,7 @@ const zend_function_entry mysqli_warning_methods[] = { /* }}} */ /* {{{ mysqli_warning_property_entries */ -mysqli_property_entry mysqli_warning_property_entries[] = { +const mysqli_property_entry mysqli_warning_property_entries[] = { {"message", mysqli_warning_message, NULL}, {"sqlstate", mysqli_warning_sqlstate, NULL}, {"errno", mysqli_warning_errno, NULL}, diff --git a/ext/mysqli/php_mysqli_structs.h b/ext/mysqli/php_mysqli_structs.h index 0bb998ce0f..17997053eb 100644 --- a/ext/mysqli/php_mysqli_structs.h +++ b/ext/mysqli/php_mysqli_structs.h @@ -1,6 +1,6 @@ /* +----------------------------------------------------------------------+ - | PHP Version 5 | + | PHP Version 6 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2007 The PHP Group | +----------------------------------------------------------------------+ @@ -98,9 +98,11 @@ typedef struct { typedef struct { MYSQL *mysql; + char *hash_key; zval *li_read; php_stream *li_stream; zend_bool persistent; + unsigned long hash_index; /* Used when persistent, hold the index in plist->used_links */ unsigned int multi_query; UConverter *conv; } MY_MYSQL; @@ -145,6 +147,11 @@ typedef struct { } mysqli_local_infile; #endif +typedef struct { + HashTable free_links; + HashTable used_links; +} mysqli_plist_entry; + #ifdef PHP_WIN32 #define PHP_MYSQLI_API __declspec(dllexport) #define MYSQLI_LLU_SPEC "%I64u" @@ -173,11 +180,11 @@ extern const zend_function_entry mysqli_driver_methods[]; extern const zend_function_entry mysqli_warning_methods[]; extern const zend_function_entry mysqli_exception_methods[]; -extern mysqli_property_entry mysqli_link_property_entries[]; -extern mysqli_property_entry mysqli_result_property_entries[]; -extern mysqli_property_entry mysqli_stmt_property_entries[]; -extern mysqli_property_entry mysqli_driver_property_entries[]; -extern mysqli_property_entry mysqli_warning_property_entries[]; +extern const mysqli_property_entry mysqli_link_property_entries[]; +extern const mysqli_property_entry mysqli_result_property_entries[]; +extern const mysqli_property_entry mysqli_stmt_property_entries[]; +extern const mysqli_property_entry mysqli_driver_property_entries[]; +extern const mysqli_property_entry mysqli_warning_property_entries[]; #ifdef HAVE_MYSQLND extern MYSQLND_ZVAL_PCACHE *mysqli_mysqlnd_zval_cache; @@ -205,6 +212,7 @@ extern zend_class_entry *mysqli_driver_class_entry; extern zend_class_entry *mysqli_warning_class_entry; extern zend_class_entry *mysqli_exception_class_entry; extern int php_le_pmysqli(void); +extern void php_mysqli_dtor_p_elements(void *data); #ifdef HAVE_SPL extern PHPAPI zend_class_entry *spl_ce_RuntimeException; @@ -279,9 +287,10 @@ PHP_MYSQLI_EXPORT(zend_object_value) mysqli_objects_new(zend_class_entry * TSRML #define MYSQLI_RETURN_LONG_LONG(__val) \ { \ if ((__val) < LONG_MAX) { \ - RETURN_LONG((__val)); \ + RETURN_LONG((long) (__val)); \ } else { \ char *ret; \ + /* always used with my_ulonglong -> %llu */ \ int l = spprintf(&ret, 0, "%llu", (__val)); \ RETURN_STRINGL(ret, l, 0); \ } \ @@ -347,6 +356,10 @@ ZEND_BEGIN_MODULE_GLOBALS(mysqli) long default_link; long num_links; long max_links; + long num_active_persistent; + long num_inactive_persistent; + long max_persistent; + long allow_persistent; long cache_size; unsigned long default_port; char *default_host; diff --git a/ext/mysqlnd/config.w32 b/ext/mysqlnd/config.w32 index 876eb9ca54..a23a9d6061 100644 --- a/ext/mysqlnd/config.w32 +++ b/ext/mysqlnd/config.w32 @@ -41,6 +41,7 @@ if (PHP_MYSQLI != "no") { mysqlnd_source = "mysqlnd.c " + // "mysqlnd_alloc.c " + + "mysqlnd_debug.c " + "mysqlnd_charset.c " + "mysqlnd_loaddata.c " + "mysqlnd_palloc.c " + @@ -51,14 +52,15 @@ if (PHP_MYSQLI != "no") { "mysqlnd_result_meta.c " + "mysqlnd_statistics.c " + "mysqlnd_wireprotocol.c"; - - EXTENSION("mysqli", mysqli_source); - // Specify that add "mysqlnd" sources, but use same object file - // directory as the "mysqli" sources - // FIXME the hard coded "ext/mysqli/mysqlnd" prevents pointing - // out sources in another directory? Like above: PHP_MYSQLI + "\\include;" - ADD_SOURCES("ext/mysqli/mysqlnd", mysqlnd_source, "mysqli"); - AC_DEFINE('HAVE_MYSQLILIB', 1, 'Have MySQLi library'); - AC_DEFINE('HAVE_MYSQLND' , 1, 'MySQL native driver support enabled'); + if (CHECK_LIB("ws2_32.lib", "mysqlnd")) { + EXTENSION("mysqli", mysqli_source); + // Specify that add "mysqlnd" sources, but use same object file + // directory as the "mysqli" sources + // FIXME the hard coded "ext/mysqli/mysqlnd" prevents pointing + // out sources in another directory? Like above: PHP_MYSQLI + "\\include;" + ADD_SOURCES("ext/mysqli/mysqlnd", mysqlnd_source, "mysqli"); + AC_DEFINE('HAVE_MYSQLILIB', 1, 'Have MySQLi library'); + AC_DEFINE('HAVE_MYSQLND' , 1, 'MySQL native driver support enabled'); + } } } diff --git a/ext/mysqlnd/config9.m4 b/ext/mysqlnd/config9.m4 index 67c482400b..9d09ba71da 100644 --- a/ext/mysqlnd/config9.m4 +++ b/ext/mysqlnd/config9.m4 @@ -7,7 +7,7 @@ if test "$PHP_MYSQLND_ENABLED" = "yes"; then mysqlnd_sources="mysqlnd.c mysqlnd_charset.c mysqlnd_wireprotocol.c \ mysqlnd_ps.c mysqlnd_loaddata.c mysqlnd_palloc.c \ mysqlnd_ps_codec.c mysqlnd_statistics.c mysqlnd_qcache.c\ - mysqlnd_result.c mysqlnd_result_meta.c" + mysqlnd_result.c mysqlnd_result_meta.c mysqlnd_debug.c" PHP_NEW_EXTENSION(mysqlnd, $mysqlnd_sources, no) PHP_ADD_BUILD_DIR([ext/mysqlnd], 1) diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index 78ac992ebb..98b3ab7dc9 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -19,7 +19,6 @@ */ /* $Id$ */ - #include "php.h" #include "mysqlnd.h" #include "mysqlnd_wireprotocol.h" @@ -27,13 +26,12 @@ #include "mysqlnd_result.h" #include "mysqlnd_statistics.h" #include "mysqlnd_charset.h" +#include "mysqlnd_debug.h" #include "php_ini.h" #include "ext/standard/basic_functions.h" #include "ext/standard/php_lcg.h" #include "ext/standard/info.h" -#define MYSQLND_SILENT - /* the server doesn't support 4byte utf8, but let's make it forward compatible */ #define MYSQLND_MAX_ALLOWED_USER_LEN 256 /* 64 char * 4byte */ #define MYSQLND_MAX_ALLOWED_DB_LEN 256 /* 64 char * 4byte */ @@ -62,26 +60,27 @@ extern MYSQLND_CHARSET *mysqlnd_charsets; -static const char * mysqlnd_server_gone = "MySQL server has gone away"; +const char * mysqlnd_server_gone = "MySQL server has gone away"; const char * mysqlnd_out_of_sync = "Commands out of sync; you can't run this command now"; MYSQLND_STATS *mysqlnd_global_stats = NULL; static zend_bool mysqlnd_library_initted = FALSE; + +enum_func_status mysqlnd_send_close(MYSQLND * conn TSRMLS_DC); + /* {{{ mysqlnd_library_init */ static -void mysqlnd_library_init(zend_bool collect_statistics) +void mysqlnd_library_init() { if (mysqlnd_library_initted == FALSE) { mysqlnd_library_initted = TRUE; _mysqlnd_init_ps_subsystem(); - if (collect_statistics) { - mysqlnd_global_stats = calloc(1, sizeof(MYSQLND_STATS)); - + /* Should be calloc, as mnd_calloc will reference LOCK_access*/ + mysqlnd_global_stats = calloc(1, sizeof(MYSQLND_STATS)); #ifdef ZTS - mysqlnd_global_stats->LOCK_access = tsrm_mutex_alloc(); + mysqlnd_global_stats->LOCK_access = tsrm_mutex_alloc(); #endif - } } } /* }}} */ @@ -92,13 +91,12 @@ static void mysqlnd_library_end() { if (mysqlnd_library_initted == TRUE) { - if (mysqlnd_global_stats) { #ifdef ZTS - tsrm_mutex_free(mysqlnd_global_stats->LOCK_access); + tsrm_mutex_free(mysqlnd_global_stats->LOCK_access); #endif - free(mysqlnd_global_stats); - mysqlnd_global_stats = NULL; - } + /* mnd_free will reference LOCK_access and crash...*/ + free(mysqlnd_global_stats); + mysqlnd_global_stats = NULL; mysqlnd_library_initted = FALSE; } } @@ -111,106 +109,130 @@ MYSQLND_METHOD(mysqlnd_conn, free_contents)(MYSQLND *conn TSRMLS_DC) { zend_bool pers = conn->persistent; - mysqlnd_local_infile_default(conn, TRUE); + DBG_ENTER("mysqlnd_conn::free_contents"); + + mysqlnd_local_infile_default(conn); if (conn->current_result) { conn->current_result->m.free_result_contents(conn->current_result TSRMLS_CC); - efree(conn->current_result); + mnd_efree(conn->current_result); conn->current_result = NULL; } if (conn->net.stream) { - php_stream_free(conn->net.stream, (pers) ? PHP_STREAM_FREE_RSRC_DTOR : - PHP_STREAM_FREE_CLOSE); + DBG_INF_FMT("Freeing stream. abstract=%p", conn->net.stream->abstract); + if (pers) { + php_stream_free(conn->net.stream, PHP_STREAM_FREE_CLOSE_PERSISTENT | PHP_STREAM_FREE_RSRC_DTOR); + } else { + php_stream_free(conn->net.stream, PHP_STREAM_FREE_CLOSE); + + } conn->net.stream = NULL; } + + DBG_INF("Freeing memory of members"); if (conn->host) { - pefree(conn->host, pers); + DBG_INF("Freeing host"); + mnd_pefree(conn->host, pers); conn->host = NULL; } if (conn->user) { - pefree(conn->user, pers); + DBG_INF("Freeing user"); + mnd_pefree(conn->user, pers); conn->user = NULL; } if (conn->passwd) { - pefree(conn->passwd, pers); + DBG_INF("Freeing passwd"); + mnd_pefree(conn->passwd, pers); conn->passwd = NULL; } if (conn->unix_socket) { - pefree(conn->unix_socket, pers); + DBG_INF("Freeing unix_socket"); + mnd_pefree(conn->unix_socket, pers); conn->unix_socket = NULL; } if (conn->scheme) { - pefree(conn->scheme, pers); + DBG_INF("Freeing scheme"); + mnd_pefree(conn->scheme, pers); conn->scheme = NULL; } if (conn->server_version) { - pefree(conn->server_version, pers); + DBG_INF("Freeing server_version"); + mnd_pefree(conn->server_version, pers); conn->server_version = NULL; } if (conn->host_info) { - pefree(conn->host_info, pers); + DBG_INF("Freeing host_info"); + mnd_pefree(conn->host_info, pers); conn->host_info = NULL; } if (conn->scramble) { - pefree(conn->scramble, pers); + DBG_INF("Freeing scramble"); + mnd_pefree(conn->scramble, pers); conn->scramble = NULL; } if (conn->last_message) { - pefree(conn->last_message, pers); + mnd_pefree(conn->last_message, pers); conn->last_message = NULL; } if (conn->options.charset_name) { - pefree(conn->options.charset_name, pers); + mnd_pefree(conn->options.charset_name, pers); conn->options.charset_name = NULL; } if (conn->options.num_commands) { unsigned int i; for (i=0; i < conn->options.num_commands; i++) { - pefree(conn->options.init_commands[i], pers); + mnd_pefree(conn->options.init_commands[i], pers); } - pefree(conn->options.init_commands, pers); + mnd_pefree(conn->options.init_commands, pers); conn->options.init_commands = NULL; } if (conn->options.cfg_file) { - pefree(conn->options.cfg_file, pers); + mnd_pefree(conn->options.cfg_file, pers); conn->options.cfg_file = NULL; } if (conn->options.cfg_section) { - pefree(conn->options.cfg_section, pers); + mnd_pefree(conn->options.cfg_section, pers); conn->options.cfg_section = NULL; } if (conn->options.ssl_key) { - pefree(conn->options.ssl_key, pers); + mnd_pefree(conn->options.ssl_key, pers); conn->options.ssl_key = NULL; } if (conn->options.ssl_cert) { - pefree(conn->options.ssl_cert, pers); + mnd_pefree(conn->options.ssl_cert, pers); conn->options.ssl_cert = NULL; } if (conn->options.ssl_ca) { - pefree(conn->options.ssl_ca, pers); + mnd_pefree(conn->options.ssl_ca, pers); conn->options.ssl_ca = NULL; } if (conn->options.ssl_capath) { - pefree(conn->options.ssl_capath, pers); + mnd_pefree(conn->options.ssl_capath, pers); conn->options.ssl_capath = NULL; } if (conn->options.ssl_cipher) { - pefree(conn->options.ssl_cipher, pers); + mnd_pefree(conn->options.ssl_cipher, pers); conn->options.ssl_cipher = NULL; } if (conn->zval_cache) { + DBG_INF("Freeing zval cache reference"); mysqlnd_palloc_free_thd_cache_reference(&conn->zval_cache); conn->zval_cache = NULL; } if (conn->qcache) { + DBG_INF("Freeing qcache reference"); mysqlnd_qcache_free_cache_reference(&conn->qcache); conn->qcache = NULL; } if (conn->net.cmd_buffer.buffer) { - pefree(conn->net.cmd_buffer.buffer, pers); + DBG_INF("Freeing cmd buffer"); + mnd_pefree(conn->net.cmd_buffer.buffer, pers); conn->net.cmd_buffer.buffer = NULL; } + conn->charset = NULL; + conn->greet_charset = NULL; + + DBG_VOID_RETURN; } /* }}} */ @@ -219,9 +241,14 @@ MYSQLND_METHOD(mysqlnd_conn, free_contents)(MYSQLND *conn TSRMLS_DC) static void MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND *conn TSRMLS_DC) { + DBG_ENTER("mysqlnd_conn::dtor"); + DBG_INF_FMT("conn=%llu", conn->thread_id); + conn->m->free_contents(conn TSRMLS_CC); - pefree(conn, conn->persistent); + mnd_pefree(conn, conn->persistent); + + DBG_VOID_RETURN; } /* }}} */ @@ -233,19 +260,22 @@ mysqlnd_simple_command_handle_response(MYSQLND *conn, enum php_mysql_packet_type TSRMLS_DC) { enum_func_status ret; + + DBG_ENTER("mysqlnd_simple_command_handle_response"); + DBG_INF_FMT("silent=%d packet=%d command=%s", silent, ok_packet, mysqlnd_command_to_text[command]); + switch (ok_packet) { case PROT_OK_PACKET:{ php_mysql_packet_ok ok_response; PACKET_INIT_ALLOCA(ok_response, PROT_OK_PACKET); if (FAIL == (ret = PACKET_READ_ALLOCA(ok_response, conn))) { if (!silent) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's OK packet", - mysqlnd_command_to_text[command]); + DBG_ERR_FMT("Error while reading %s's OK packet", mysqlnd_command_to_text[command]); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's OK packet. PID=%d", + mysqlnd_command_to_text[command], getpid()); } } else { -#ifndef MYSQLND_SILENT - php_printf("\tOK from server\n"); -#endif + DBG_INF_FMT("OK from server"); if (0xFF == ok_response.field_count) { /* The server signalled error. Set the error */ SET_CLIENT_ERROR(conn->error_info, ok_response.error_no, @@ -283,8 +313,9 @@ mysqlnd_simple_command_handle_response(MYSQLND *conn, enum php_mysql_packet_type SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet"); if (!silent) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's EOF packet", - mysqlnd_command_to_text[command]); + DBG_ERR_FMT("Error while reading %s's EOF packet", mysqlnd_command_to_text[command]); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's EOF packet. PID=%d", + mysqlnd_command_to_text[command], getpid()); } } else if (0xFF == ok_response.field_count) { /* The server signalled error. Set the error */ @@ -295,14 +326,13 @@ mysqlnd_simple_command_handle_response(MYSQLND *conn, enum php_mysql_packet_type SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet"); if (!silent) { + DBG_ERR_FMT("EOF packet expected, field count wasn't 0xFE but 0x%2X", ok_response.field_count); php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet expected, field count wasn't 0xFE but 0x%2X", ok_response.field_count); } } else { -#ifndef MYSQLND_SILENT - php_printf("\tOK from server\n"); -#endif + DBG_INF_FMT("OK from server"); } PACKET_FREE_ALLOCA(ok_response); break; @@ -315,7 +345,8 @@ mysqlnd_simple_command_handle_response(MYSQLND *conn, enum php_mysql_packet_type ok_packet); break; } - return ret; + DBG_INF(ret == PASS ? "PASS":"FAIL"); + DBG_RETURN(ret); } /* }}} */ @@ -329,16 +360,21 @@ mysqlnd_simple_command(MYSQLND *conn, enum php_mysqlnd_server_command command, enum_func_status ret = PASS; php_mysql_packet_command cmd_packet; + DBG_ENTER("mysqlnd_simple_command"); + DBG_INF_FMT("command=%s ok_packet=%d silent=%d", mysqlnd_command_to_text[command], ok_packet, silent); + switch (conn->state) { case CONN_READY: break; case CONN_QUIT_SENT: SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone); - return FAIL; + DBG_ERR("Server is gone"); + DBG_RETURN(FAIL); default: SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); - return FAIL; + DBG_ERR("Command out of sync"); + DBG_RETURN(FAIL); } /* clean UPSERT info */ @@ -355,9 +391,10 @@ mysqlnd_simple_command(MYSQLND *conn, enum php_mysqlnd_server_command command, if (! PACKET_WRITE_ALLOCA(cmd_packet, conn)) { if (!silent) { - php_error(E_WARNING, "Error while sending %s packet", mysqlnd_command_to_text[command]); + DBG_ERR_FMT("Error while sending %s packet", mysqlnd_command_to_text[command]); + php_error(E_WARNING, "Error while sending %s packet. PID=%d", mysqlnd_command_to_text[command], getpid()); } - SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone); + DBG_ERR("Server is gone"); ret = FAIL; } else if (ok_packet != PROT_LAST) { ret = mysqlnd_simple_command_handle_response(conn, ok_packet, silent, command TSRMLS_CC); @@ -369,7 +406,8 @@ mysqlnd_simple_command(MYSQLND *conn, enum php_mysqlnd_server_command command, to us. We should not free it. */ - return ret; + DBG_INF(ret == PASS ? "PASS":"FAIL"); + DBG_RETURN(ret); } /* }}} */ @@ -379,23 +417,29 @@ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, set_server_option)(MYSQLND * const conn, enum_mysqlnd_server_option option TSRMLS_DC) { + enum_func_status ret; char buffer[2]; + DBG_ENTER("mysqlnd_conn::set_server_option"); + int2store(buffer, (uint) option); - return mysqlnd_simple_command(conn, COM_SET_OPTION, buffer, sizeof(buffer), - PROT_EOF_PACKET, FALSE TSRMLS_CC); + ret = mysqlnd_simple_command(conn, COM_SET_OPTION, buffer, sizeof(buffer), + PROT_EOF_PACKET, FALSE TSRMLS_CC); + DBG_RETURN(ret); } /* }}} */ -/* {{{ mysqlnd_start_psession */ -PHPAPI void mysqlnd_restart_psession(MYSQLND *conn) +/* {{{ _mysqlnd_restart_psession */ +PHPAPI void _mysqlnd_restart_psession(MYSQLND *conn TSRMLS_DC) { + DBG_ENTER("_mysqlnd_restart_psession"); MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CONNECT_REUSED); /* Free here what should not be seen by the next script */ if (conn->last_message) { - pefree(conn->last_message, conn->persistent); + mnd_pefree(conn->last_message, conn->persistent); conn->last_message = NULL; } + DBG_VOID_RETURN; } /* }}} */ @@ -421,22 +465,40 @@ PHPAPI MYSQLND *mysqlnd_connect(MYSQLND *conn, { char *transport = NULL, *errstr = NULL; char *hashed_details = NULL; - int transport_len, errcode = 0; + int transport_len, hashed_details_len, errcode = 0; unsigned int streams_options = ENFORCE_SAFE_MODE; unsigned int streams_flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT; zend_bool self_alloced = FALSE; struct timeval tv; zend_bool unix_socket = FALSE; const MYSQLND_CHARSET * charset; + zend_bool reconnect = FALSE; php_mysql_packet_greet greet_packet; php_mysql_packet_auth *auth_packet; php_mysql_packet_ok ok_packet; - if (conn && conn->state != CONN_ALLOCED) { - SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, - mysqlnd_out_of_sync); - return NULL; + DBG_ENTER("mysqlnd_connect"); + DBG_INF_FMT("host=%s user=%s db=%s port=%d flags=%d persistent=%d state=%d", + host?host:"", user?user:"", db?db:"", port, mysql_flags, + conn? conn->persistent:0, conn? conn->state:-1); + + DBG_INF_FMT("state=%d", conn->state); + if (conn && conn->state > CONN_ALLOCED && conn->state ) { + DBG_INF("Connecting on a connected handle."); + + if (conn->state < CONN_QUIT_SENT) { + MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CLOSE_IMPLICIT); + reconnect = TRUE; + mysqlnd_send_close(conn TSRMLS_CC); + } + + conn->m->free_contents(conn TSRMLS_CC); + MYSQLND_DEC_CONN_STATISTIC(&conn->stats, STAT_OPENED_CONNECTIONS); + if (conn->persistent) { + MYSQLND_DEC_CONN_STATISTIC(&conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS); + } + /* Now reconnect using the same handle */ } if (!host || !host[0]) { @@ -468,14 +530,16 @@ PHPAPI MYSQLND *mysqlnd_connect(MYSQLND *conn, { transport_len = spprintf(&transport, 0, "tcp://%s:%d", host, port); } + DBG_INF_FMT("transport=%p", transport); if (conn->persistent) { struct timeval tv; gettimeofday(&tv, NULL); /* We should generate something unique */ - spprintf(&hashed_details, 0, "%s@%s@%s@%ld@%ld@%0.8F", - transport, user, db, tv.tv_sec, (long int)tv.tv_usec, - php_combined_lcg(TSRMLS_C) * 10); + hashed_details_len = spprintf(&hashed_details, 0, "%s@%s@%s@%ld@%ld@%0.8F", + transport, user, db, tv.tv_sec, (long int)tv.tv_usec, + php_combined_lcg(TSRMLS_C) * 10); + DBG_INF_FMT("hashed_details=%s", hashed_details); } PACKET_INIT_ALLOCA(greet_packet, PROT_GREET_PACKET); @@ -496,17 +560,43 @@ PHPAPI MYSQLND *mysqlnd_connect(MYSQLND *conn, } if (conn->persistent) { conn->scheme = pestrndup(transport, transport_len, 1); - efree(transport); + mnd_efree(transport); } else { conn->scheme = transport; } + DBG_INF(conn->scheme); conn->net.stream = php_stream_xport_create(conn->scheme, transport_len, streams_options, streams_flags, hashed_details, (conn->options.timeout_connect) ? &tv : NULL, NULL /*ctx*/, &errstr, &errcode); + DBG_INF_FMT("stream=%p", conn->net.stream); + if (hashed_details) { - efree(hashed_details); + /* + If persistent, the streams register it in EG(persistent_list). + This is unwanted. ext/mysql or ext/mysqli are responsible to clean, + whatever they have to. + */ + zend_rsrc_list_entry *le; + + if (zend_hash_find(&EG(persistent_list), hashed_details, hashed_details_len + 1, + (void*) &le) == SUCCESS) { + /* + in_free will let streams code skip destructing - big HACK, + but STREAMS suck big time regarding persistent streams. + Just not compatible for extensions that need persistency. + */ + conn->net.stream->in_free = 1; + zend_hash_del(&EG(persistent_list), hashed_details, hashed_details_len + 1); + conn->net.stream->in_free = 0; + } +#if ZEND_DEBUG + /* Shut-up the streams, they don't know what they are doing */ + conn->net.stream->__exposed = 1; +#endif + mnd_efree(hashed_details); } + if (errstr || !conn->net.stream) { goto err; } @@ -524,15 +614,17 @@ PHPAPI MYSQLND *mysqlnd_connect(MYSQLND *conn, } if (FAIL == PACKET_READ_ALLOCA(greet_packet, conn)) { -#ifndef MYSQLND_SILENT - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading greeting packet"); -#endif + DBG_ERR("Error while reading greeting packet"); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading greeting packet. PID=%d", getpid()); goto err; } else if (greet_packet.error_no) { + DBG_ERR_FMT("errorno=%d error=%s", greet_packet.error_no, greet_packet.error); SET_CLIENT_ERROR(conn->error_info, greet_packet.error_no, greet_packet.sqlstate, greet_packet.error); goto err; } else if (greet_packet.pre41) { + DBG_ERR_FMT("Connecting to 3.22, 3.23 & 4.0 is not supported. Server is %-.32s", + greet_packet.server_version); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connecting to 3.22, 3.23 & 4.0 " " is not supported. Server is %-.32s", greet_packet.server_version); SET_CLIENT_ERROR(conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, @@ -545,6 +637,7 @@ PHPAPI MYSQLND *mysqlnd_connect(MYSQLND *conn, conn->server_version = greet_packet.server_version; greet_packet.server_version = NULL; /* The string will be freed otherwise */ + conn->greet_charset = mysqlnd_find_charset_nr(greet_packet.charset_no); /* we allow load data local infile by default */ mysql_flags |= CLIENT_LOCAL_FILES; @@ -567,23 +660,26 @@ PHPAPI MYSQLND *mysqlnd_connect(MYSQLND *conn, auth_packet->max_packet_size= 3UL*1024UL*1024UL*1024UL; auth_packet->client_flags= mysql_flags; - conn->scramble = auth_packet->server_scramble_buf = pemalloc(SCRAMBLE_LENGTH, conn->persistent); + conn->scramble = auth_packet->server_scramble_buf = mnd_pemalloc(SCRAMBLE_LENGTH, conn->persistent); memcpy(auth_packet->server_scramble_buf, greet_packet.scramble_buf, SCRAMBLE_LENGTH); - PACKET_WRITE(auth_packet, conn); + if (!PACKET_WRITE(auth_packet, conn)) { + conn->state = CONN_QUIT_SENT; + SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone); + goto err; + } if (FAIL == PACKET_READ_ALLOCA(ok_packet, conn) || ok_packet.field_count >= 0xFE) { if (ok_packet.field_count == 0xFE) { /* old authentication with new server !*/ + DBG_ERR("mysqlnd cannot connect to MySQL 4.1+ using old authentication"); php_error_docref(NULL TSRMLS_CC, E_WARNING, "mysqlnd cannot connect to MySQL 4.1+ using old authentication"); } else if (ok_packet.field_count == 0xFF) { if (ok_packet.sqlstate[0]) { if (!self_alloced) { strncpy(conn->error_info.sqlstate, ok_packet.sqlstate, sizeof(conn->error_info.sqlstate)); } -#ifndef MYSQLND_SILENT - php_error_docref(NULL TSRMLS_CC, E_WARNING, "ERROR:%d [SQLSTATE:%s] %s", - ok_packet.error_no, ok_packet.sqlstate, ok_packet.error); -#endif + DBG_ERR_FMT("ERROR:%d [SQLSTATE:%s] %s", + ok_packet.error_no, ok_packet.sqlstate, ok_packet.error); } if (!self_alloced) { conn->error_info.error_no = ok_packet.error_no; @@ -603,7 +699,7 @@ PHPAPI MYSQLND *mysqlnd_connect(MYSQLND *conn, spprintf(&p, 0, "MySQL host info: %s via TCP/IP", conn->host); if (conn->persistent) { conn->host_info = pestrdup(p, 1); - efree(p); + mnd_efree(p); } else { conn->host_info = p; } @@ -631,17 +727,39 @@ PHPAPI MYSQLND *mysqlnd_connect(MYSQLND *conn, conn->zval_cache = mysqlnd_palloc_get_thd_cache_reference(zval_cache); conn->net.cmd_buffer.length = 128L*1024L; - conn->net.cmd_buffer.buffer = pemalloc(conn->net.cmd_buffer.length, conn->persistent); + conn->net.cmd_buffer.buffer = mnd_pemalloc(conn->net.cmd_buffer.length, conn->persistent); + + mysqlnd_local_infile_default(conn); + { + uint buf_size; + buf_size = MYSQLND_G(net_read_buffer_size); /* this is long, cast to uint*/ + conn->m->set_client_option(conn, MYSQLND_OPT_NET_READ_BUFFER_SIZE, + (char *)&buf_size TSRMLS_CC); + + buf_size = MYSQLND_G(net_cmd_buffer_size); /* this is long, cast to uint*/ + conn->m->set_client_option(conn, MYSQLND_OPT_NET_CMD_BUFFER_SIZE, + (char *)&buf_size TSRMLS_CC); + } MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CONNECT_SUCCESS); + if (reconnect) { + MYSQLND_INC_GLOBAL_STATISTIC(STAT_RECONNECT); + } + MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_OPENED_CONNECTIONS); + if (conn->persistent) { + MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS); + } + DBG_INF_FMT("connection_id=%llu", conn->thread_id); +#if PHP_MAJOR_VERSION >= 6 { uint as_unicode = 1; conn->m->set_client_option(conn, MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE, - (char *)&as_unicode); + (char *)&as_unicode TSRMLS_CC); + DBG_INF("unicode set"); } - - return conn; +#endif + DBG_RETURN(conn); } err: PACKET_FREE_ALLOCA(greet_packet); @@ -649,14 +767,15 @@ err: PACKET_FREE_ALLOCA(ok_packet); if (errstr) { + DBG_ERR_FMT("[%d] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme); SET_CLIENT_ERROR(conn->error_info, errcode, UNKNOWN_SQLSTATE, errstr); php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%d] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme); - efree(errstr); + mnd_efree(errstr); } if (conn->scheme) { - pefree(conn->scheme, conn->persistent); + mnd_pefree(conn->scheme, conn->persistent); conn->scheme = NULL; } @@ -673,7 +792,7 @@ err: } else { MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CONNECT_FAILURE); } - return NULL; + DBG_RETURN(NULL); } /* }}} */ @@ -687,11 +806,13 @@ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC) { enum_func_status ret; + DBG_ENTER("mysqlnd_conn::query"); + DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query); if (PASS != mysqlnd_simple_command(conn, COM_QUERY, query, query_len, PROT_LAST /* we will handle the OK packet*/, FALSE TSRMLS_CC)) { - return FAIL; + DBG_RETURN(FAIL); } /* @@ -699,21 +820,82 @@ MYSQLND_METHOD(mysqlnd_conn, query)(MYSQLND *conn, const char *query, unsigned i information from the ok packet. We will fetch it ourselves. */ ret = mysqlnd_query_read_result_set_header(conn, NULL TSRMLS_CC); - - return ret; + DBG_RETURN(ret); } /* }}} */ +/* + COM_FIELD_LIST is special, different from a SHOW FIELDS FROM : + - There is no result set header - status from the command, which + impacts us to allocate big chunk of memory for reading the metadata. + - The EOF packet is consumed by the metadata packet reader. +*/ + +/* {{{ mysqlnd_conn::list_fields */ +MYSQLND_RES * +MYSQLND_METHOD(mysqlnd_conn, list_fields)(MYSQLND *conn, const char *table, const char *achtung_wild TSRMLS_DC) +{ + /* db + \0 + wild + \0 (for wild) */ + char buff[MYSQLND_MAX_ALLOWED_DB_LEN * 4 * 2 + 1 + 1], *p; + size_t table_len, wild_len; + MYSQLND_RES *result = NULL; + DBG_ENTER("mysqlnd_conn::list_fields"); + DBG_INF_FMT("conn=%llu table=%s wild=%s", conn->thread_id, table? table:"",achtung_wild? achtung_wild:""); + + p = buff; + if (table && (table_len = strlen(table))) { + memcpy(p, table, MIN(table_len, MYSQLND_MAX_ALLOWED_DB_LEN * 4)); + p += table_len; + *p++ = '\0'; + } + + if (achtung_wild && (wild_len = strlen(achtung_wild))) { + memcpy(p, achtung_wild, MIN(wild_len, MYSQLND_MAX_ALLOWED_DB_LEN * 4)); + p += wild_len; + *p++ = '\0'; + } + + if (PASS != mysqlnd_simple_command(conn, COM_FIELD_LIST, buff, p - buff, + PROT_LAST /* we will handle the OK packet*/, + FALSE TSRMLS_CC)) { + DBG_RETURN(NULL); + } + /* + Prepare for the worst case. + MyISAM goes to 2500 BIT columns, double it for safety. + */ + result = mysqlnd_result_init(5000, mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache) TSRMLS_CC); + + + if (FAIL == result->m.read_result_metadata(result, conn TSRMLS_CC)) { + DBG_ERR("Error ocurred while reading metadata"); + result->m.free_result(result, TRUE TSRMLS_CC); + DBG_RETURN(NULL); + } + + result->type = MYSQLND_RES_NORMAL; + result->m.fetch_row = result->m.fetch_row_normal_unbuffered; + result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); + result->unbuf->eof_reached = TRUE; + + DBG_RETURN(result); +} +/* }}} */ + /* {{{ mysqlnd_conn::list_method */ MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_conn, list_method)(MYSQLND *conn, const char *query, char *achtung_wild, char *par1 TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, list_method)(MYSQLND *conn, const char *query, + const char *achtung_wild, char *par1 TSRMLS_DC) { char *show_query = NULL; size_t show_query_len; MYSQLND_RES *result = NULL; + DBG_ENTER("mysqlnd_conn::list_method"); + DBG_INF_FMT("conn=%llu query=%s wild=%d", conn->thread_id, query, achtung_wild); + if (par1) { if (achtung_wild) { show_query_len = spprintf(&show_query, 0, query, par1, achtung_wild); @@ -732,12 +914,13 @@ MYSQLND_METHOD(mysqlnd_conn, list_method)(MYSQLND *conn, const char *query, char result = conn->m->store_result(conn TSRMLS_CC); } if (show_query != query) { - efree(show_query); + mnd_efree(show_query); } - return result; + DBG_RETURN(result); } /* }}} */ + /* {{{ mysqlnd_conn::errno */ static unsigned int MYSQLND_METHOD(mysqlnd_conn, errno)(const MYSQLND * const conn) @@ -766,22 +949,26 @@ MYSQLND_METHOD(mysqlnd_conn, sqlstate)(const MYSQLND * const conn) /* {{{ mysqlnd_old_escape_string */ -PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, int escapestr_len) +PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, int escapestr_len TSRMLS_DC) { - return mysqlnd_cset_escape_slashes(mysqlnd_find_charset_name("latin1"), - newstr, escapestr, escapestr_len); + DBG_ENTER("mysqlnd_old_escape_string"); + DBG_RETURN(mysqlnd_cset_escape_slashes(mysqlnd_find_charset_name("latin1"), + newstr, escapestr, escapestr_len TSRMLS_CC)); } /* }}} */ /* {{{ mysqlnd_conn::escape_string */ static ulong -MYSQLND_METHOD(mysqlnd_conn, escape_string)(const MYSQLND * const conn, char *newstr, const char *escapestr, int escapestr_len) +MYSQLND_METHOD(mysqlnd_conn, escape_string)(const MYSQLND * const conn, char *newstr, + const char *escapestr, int escapestr_len TSRMLS_DC) { + DBG_ENTER("mysqlnd_conn::escape_string"); + DBG_INF_FMT("conn=%llu", conn->thread_id); if (conn->upsert_status.server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) { - return mysqlnd_cset_escape_quotes(conn->charset, newstr, escapestr, escapestr_len); + DBG_RETURN(mysqlnd_cset_escape_quotes(conn->charset, newstr, escapestr, escapestr_len TSRMLS_CC)); } - return mysqlnd_cset_escape_slashes(conn->charset, newstr, escapestr, escapestr_len); + DBG_RETURN(mysqlnd_cset_escape_slashes(conn->charset, newstr, escapestr, escapestr_len TSRMLS_CC)); } /* }}} */ @@ -790,7 +977,9 @@ MYSQLND_METHOD(mysqlnd_conn, escape_string)(const MYSQLND * const conn, char *ne static enum_func_status MYSQLND_METHOD(mysqlnd_conn, dump_debug_info)(MYSQLND * const conn TSRMLS_DC) { - return mysqlnd_simple_command(conn, COM_DEBUG, NULL, 0, PROT_EOF_PACKET, FALSE TSRMLS_CC); + DBG_ENTER("mysqlnd_conn::dump_debug_info"); + DBG_INF_FMT("conn=%llu", conn->thread_id); + DBG_RETURN(mysqlnd_simple_command(conn, COM_DEBUG, NULL, 0, PROT_EOF_PACKET, FALSE TSRMLS_CC)); } /* }}} */ @@ -802,6 +991,10 @@ MYSQLND_METHOD(mysqlnd_conn, select_db)(MYSQLND * const conn, unsigned int db_len TSRMLS_DC) { enum_func_status ret; + + DBG_ENTER("mysqlnd_conn::select_db"); + DBG_INF_FMT("conn=%llu db=%s", conn->thread_id, db); + ret = mysqlnd_simple_command(conn, COM_INIT_DB, db, db_len, PROT_OK_PACKET, FALSE TSRMLS_CC); /* The server sends 0 but libmysql doesn't read it and has established @@ -809,7 +1002,7 @@ MYSQLND_METHOD(mysqlnd_conn, select_db)(MYSQLND * const conn, */ SET_ERROR_AFF_ROWS(conn); - return ret; + DBG_RETURN(ret); } /* }}} */ @@ -819,14 +1012,19 @@ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, ping)(MYSQLND * const conn TSRMLS_DC) { enum_func_status ret; - ret = mysqlnd_simple_command(conn, COM_PING, NULL, 0, PROT_OK_PACKET, FALSE TSRMLS_CC); + + DBG_ENTER("mysqlnd_conn::ping"); + DBG_INF_FMT("conn=%llu", conn->thread_id); + + ret = mysqlnd_simple_command(conn, COM_PING, NULL, 0, PROT_OK_PACKET, TRUE TSRMLS_CC); /* The server sends 0 but libmysql doesn't read it and has established a protocol of giving back -1. Thus we have to follow it :( */ SET_ERROR_AFF_ROWS(conn); - return ret; + DBG_INF_FMT("ret=%d", ret); + DBG_RETURN(ret); } /* }}} */ @@ -838,13 +1036,16 @@ MYSQLND_METHOD(mysqlnd_conn, stat)(MYSQLND *conn, char **message, unsigned int * enum_func_status ret; php_mysql_packet_stats stats_header; + DBG_ENTER("mysqlnd_conn::stat"); + DBG_INF_FMT("conn=%llu", conn->thread_id); + ret = mysqlnd_simple_command(conn, COM_STATISTICS, NULL, 0, PROT_LAST, FALSE TSRMLS_CC); if (FAIL == ret) { - return FAIL; + DBG_RETURN(FAIL); } PACKET_INIT_ALLOCA(stats_header, PROT_STATS_PACKET); if (FAIL == (ret = PACKET_READ_ALLOCA(stats_header, conn))) { - return FAIL; + DBG_RETURN(FAIL); } *message = stats_header.message; *message_len = stats_header.message_len; @@ -852,18 +1053,22 @@ MYSQLND_METHOD(mysqlnd_conn, stat)(MYSQLND *conn, char **message, unsigned int * stats_header.message = NULL; PACKET_FREE_ALLOCA(stats_header); - return PASS; + DBG_INF(*message); + DBG_RETURN(PASS); } /* }}} */ /* {{{ mysqlnd_conn::kill */ static enum_func_status -MYSQLND_METHOD(mysqlnd_conn, kill)(MYSQLND *conn, unsigned long pid TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, kill)(MYSQLND *conn, unsigned int pid TSRMLS_DC) { enum_func_status ret; char buff[4]; + DBG_ENTER("mysqlnd_conn::kill"); + DBG_INF_FMT("conn=%llu pid=%lu", conn->thread_id, pid); + int4store(buff, pid); /* If we kill ourselves don't expect OK packet, PROT_LAST will skip it */ @@ -878,7 +1083,7 @@ MYSQLND_METHOD(mysqlnd_conn, kill)(MYSQLND *conn, unsigned long pid TSRMLS_DC) 4, PROT_LAST, FALSE TSRMLS_CC))) { conn->state = CONN_QUIT_SENT; } - return ret; + DBG_RETURN(ret); } /* }}} */ @@ -892,10 +1097,13 @@ MYSQLND_METHOD(mysqlnd_conn, set_charset)(MYSQLND * const conn, const char * con size_t query_len; const MYSQLND_CHARSET * const charset = mysqlnd_find_charset_name(csname); + DBG_ENTER("mysqlnd_conn::set_charset"); + DBG_INF_FMT("conn=%llu cs=%s", conn->thread_id, csname); + if (!charset) { SET_CLIENT_ERROR(conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE, "Invalid characterset or character set not supported"); - return FAIL; + DBG_RETURN(FAIL); } query_len = spprintf(&query, 0, "SET NAMES %s", csname); @@ -907,8 +1115,10 @@ MYSQLND_METHOD(mysqlnd_conn, set_charset)(MYSQLND * const conn, const char * con } else { conn->charset = charset; } - efree(query); - return ret; + mnd_efree(query); + + DBG_INF(ret == PASS? "PASS":"FAIL"); + DBG_RETURN(ret); } /* }}} */ @@ -918,9 +1128,12 @@ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, refresh)(MYSQLND * const conn, unsigned long options TSRMLS_DC) { zend_uchar bits[1]; + DBG_ENTER("mysqlnd_conn::refresh"); + DBG_INF_FMT("conn=%llu options=%lu", conn->thread_id, options); + int1store(bits, options); - return mysqlnd_simple_command(conn, COM_REFRESH, (char *)bits, 1, PROT_OK_PACKET, FALSE TSRMLS_CC); + DBG_RETURN(mysqlnd_simple_command(conn, COM_REFRESH, (char *)bits, 1, PROT_OK_PACKET, FALSE TSRMLS_CC)); } /* }}} */ @@ -930,22 +1143,31 @@ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, shutdown)(MYSQLND * const conn, unsigned long level TSRMLS_DC) { zend_uchar bits[1]; + DBG_ENTER("mysqlnd_conn::shutdown"); + DBG_INF_FMT("conn=%llu level=%lu", conn->thread_id, level); + int1store(bits, level); - return mysqlnd_simple_command(conn, COM_SHUTDOWN, (char *)bits, 1, PROT_OK_PACKET, FALSE TSRMLS_CC); + DBG_RETURN(mysqlnd_simple_command(conn, COM_SHUTDOWN, (char *)bits, 1, PROT_OK_PACKET, FALSE TSRMLS_CC)); } /* }}} */ /* {{{ mysqlnd_send_close */ -static enum_func_status +enum_func_status mysqlnd_send_close(MYSQLND * conn TSRMLS_DC) { enum_func_status ret = PASS; + + DBG_ENTER("mysqlnd_send_close"); + DBG_INF_FMT("conn=%llu conn->net.stream->abstract=%p", + conn->thread_id, conn->net.stream? conn->net.stream->abstract:NULL); + switch (conn->state) { case CONN_READY: + DBG_INF("Connection clean, sending COM_QUIT"); ret = mysqlnd_simple_command(conn, COM_QUIT, NULL, 0, PROT_LAST, - conn->tmp_int? TRUE : FALSE TSRMLS_CC); + TRUE TSRMLS_CC); /* Do nothing */ break; case CONN_SENDING_LOAD_DATA: @@ -956,10 +1178,8 @@ mysqlnd_send_close(MYSQLND * conn TSRMLS_DC) case CONN_NEXT_RESULT_PENDING: case CONN_QUERY_SENT: case CONN_FETCHING_DATA: - MYSQLND_INC_CONN_STATISTIC(NULL, STAT_CLOSE_IN_MIDDLE); -#ifndef MYSQLND_SILENT - php_printf("Brutally closing connection [%p][%s]\n", conn, conn->scheme); -#endif + MYSQLND_INC_GLOBAL_STATISTIC(STAT_CLOSE_IN_MIDDLE); + DBG_ERR_FMT("Brutally closing connection [%p][%s]", conn, conn->scheme); /* Do nothing, the connection will be brutally closed and the server will catch it and free close from its side. @@ -981,7 +1201,7 @@ mysqlnd_send_close(MYSQLND * conn TSRMLS_DC) */ conn->state = CONN_QUIT_SENT; - return ret; + DBG_RETURN(ret); } /* }}} */ @@ -999,13 +1219,25 @@ MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn, enum_connection_close_type c }; enum_mysqlnd_collected_stats stat = close_type_to_stat_map[close_type]; - MYSQLND_INC_CONN_STATISTIC(NULL, stat); + DBG_ENTER("mysqlnd_conn::close"); + DBG_INF_FMT("conn=%llu", conn->thread_id); - mysqlnd_send_close(conn TSRMLS_CC); + MYSQLND_INC_CONN_STATISTIC(&conn->stats, stat); + MYSQLND_DEC_CONN_STATISTIC(&conn->stats, STAT_OPENED_CONNECTIONS); + if (conn->persistent) { + MYSQLND_DEC_CONN_STATISTIC(&conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS); + } - conn->m->free_reference(conn TSRMLS_CC); + /* + Close now, free_reference will try, + if we are last, but that's not a problem. + */ + ret = mysqlnd_send_close(conn TSRMLS_CC); + + ret = conn->m->free_reference(conn TSRMLS_CC); - return ret; + + DBG_RETURN(ret); } /* }}} */ @@ -1021,18 +1253,22 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_reference)(MYSQLND * const conn) /* {{{ mysqlnd_conn::free_reference */ -static void +static enum_func_status MYSQLND_METHOD_PRIVATE(mysqlnd_conn, free_reference)(MYSQLND * const conn TSRMLS_DC) { + enum_func_status ret = PASS; + DBG_ENTER("mysqlnd_conn::free_reference"); + DBG_INF_FMT("conn=%llu conn->refcount=%u", conn->thread_id, conn->refcount); if (!(--conn->refcount)) { /* No multithreading issues as we don't share the connection :) This will free the object too, of course because references has reached zero. */ - mysqlnd_send_close(conn TSRMLS_CC); + ret = mysqlnd_send_close(conn TSRMLS_CC); conn->m->dtor(conn TSRMLS_CC); } + DBG_RETURN(ret); } /* }}} */ @@ -1181,8 +1417,11 @@ MYSQLND_METHOD(mysqlnd_conn, next_result)(MYSQLND * const conn TSRMLS_DC) { enum_func_status ret; + DBG_ENTER("mysqlnd_conn::next_result"); + DBG_INF_FMT("conn=%llu", conn->thread_id); + if (conn->state != CONN_NEXT_RESULT_PENDING) { - return FAIL; + DBG_RETURN(FAIL); } SET_EMPTY_ERROR(conn->error_info); @@ -1192,13 +1431,12 @@ MYSQLND_METHOD(mysqlnd_conn, next_result)(MYSQLND * const conn TSRMLS_DC) in mysqlnd_store_result() or mysqlnd_fetch_row_unbuffered() */ if (FAIL == (ret = mysqlnd_query_read_result_set_header(conn, NULL TSRMLS_CC))) { -#ifndef MYSQLND_SILENT - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Serious error"); -#endif + DBG_ERR_FMT("Serious error. %s::%d", __FILE__, __LINE__); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Serious error. PID=%d", getpid()); conn->state = CONN_QUIT_SENT; } - return ret; + DBG_RETURN(ret); } /* }}} */ @@ -1272,6 +1510,10 @@ MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn, char buffer[MYSQLND_MAX_ALLOWED_USER_LEN + 1 + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1]; char *p = buffer; + DBG_ENTER("mysqlnd_conn::change_user"); + DBG_INF_FMT("conn=%llu user=%s passwd=%s db=%s", + conn->thread_id, user?user:"", passwd?"***":"null", db?db:""); + if (!user) { user = ""; } @@ -1283,8 +1525,8 @@ MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn, } /* 1. user ASCIIZ */ - user_len = strlen(user); - memcpy(p, user, MIN(user_len, MYSQLND_MAX_ALLOWED_DB_LEN)); + user_len = MIN(strlen(user), MYSQLND_MAX_ALLOWED_DB_LEN); + memcpy(p, user, user_len); p += user_len; *p++ = '\0'; @@ -1308,7 +1550,7 @@ MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn, if (PASS != mysqlnd_simple_command(conn, COM_CHANGE_USER, buffer, p - buffer, PROT_LAST /* we will handle the OK packet*/, FALSE TSRMLS_CC)) { - return FAIL; + DBG_RETURN(FAIL); } PACKET_INIT_ALLOCA(chg_user_resp, PROT_CHG_USER_PACKET); @@ -1330,15 +1572,30 @@ MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn, PACKET_INIT_ALLOCA(redundant_error_packet, PROT_OK_PACKET); PACKET_READ_ALLOCA(redundant_error_packet, conn); PACKET_FREE_ALLOCA(redundant_error_packet); + DBG_INF_FMT("Server is %d, buggy, sends two ERR messages", mysqlnd_get_server_version(conn)); } } + if (ret == PASS) { + mnd_pefree(conn->user, conn->persistent); + conn->user = pestrndup(user, user_len, conn->persistent); + mnd_pefree(conn->passwd, conn->persistent); + conn->passwd = pestrdup(passwd, conn->persistent); + if (conn->last_message) { + mnd_pefree(conn->last_message, conn->persistent); + conn->last_message = NULL; + } + conn->charset = conn->greet_charset; + memset(&conn->upsert_status, 0, sizeof(conn->upsert_status)); + } + + SET_ERROR_AFF_ROWS(conn); /* Here we should close all statements. Unbuffered queries should not be a problem as we won't allow sending COM_CHANGE_USER. */ - - return ret; + DBG_INF(ret == PASS? "PASS":"FAIL"); + DBG_RETURN(ret); } /* }}} */ @@ -1347,12 +1604,30 @@ MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn, static enum_func_status MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn, enum mysqlnd_option option, - const char * const value) + const char * const value + TSRMLS_DC) { + DBG_ENTER("mysqlnd_conn::set_client_option"); + DBG_INF_FMT("conn=%llu option=%d", conn->thread_id, option); switch (option) { +#if PHP_MAJOR_VERSION >= 6 case MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE: conn->options.numeric_and_datetime_as_unicode = *(uint*) value; break; +#endif + case MYSQLND_OPT_NET_CMD_BUFFER_SIZE: + conn->net.cmd_buffer.length = *(uint*) value; + if (!conn->net.cmd_buffer.buffer) { + conn->net.cmd_buffer.buffer = mnd_pemalloc(conn->net.cmd_buffer.length, conn->persistent); + } else { + conn->net.cmd_buffer.buffer = mnd_perealloc(conn->net.cmd_buffer.buffer, + conn->net.cmd_buffer.length, + conn->persistent); + } + break; + case MYSQLND_OPT_NET_READ_BUFFER_SIZE: + conn->options.net_read_buffer_size = *(uint*) value; + break; #ifdef MYSQLND_STRING_TO_INT_CONVERSION case MYSQLND_OPT_INT_AND_YEAR_AS_INT: conn->options.int_and_year_as_int = *(uint*) value; @@ -1414,9 +1689,9 @@ MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn, /* not sure, todo ? */ #endif default: - return FAIL; + DBG_RETURN(FAIL); } - return PASS; + DBG_RETURN(PASS); } /* }}} */ @@ -1427,15 +1702,19 @@ MYSQLND_METHOD(mysqlnd_conn, use_result)(MYSQLND * const conn TSRMLS_DC) { MYSQLND_RES *result; + DBG_ENTER("mysqlnd_conn::use_result"); + DBG_INF_FMT("conn=%llu", conn->thread_id); + if (!conn->current_result) { - return NULL; + DBG_RETURN(NULL); } /* Nothing to store for UPSERT/LOAD DATA */ if (conn->last_query_type != QUERY_SELECT || conn->state != CONN_FETCHING_DATA) { SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, - mysqlnd_out_of_sync); - return NULL; + mysqlnd_out_of_sync); + DBG_ERR("Command out of sync"); + DBG_RETURN(NULL); } MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_UNBUFFERED_SETS); @@ -1444,7 +1723,8 @@ MYSQLND_METHOD(mysqlnd_conn, use_result)(MYSQLND * const conn TSRMLS_DC) conn->current_result = NULL; result->conn = conn->m->get_reference(conn); - return result->m.use_result(result, FALSE TSRMLS_CC); + result = result->m.use_result(result, FALSE TSRMLS_CC); + DBG_RETURN(result); } /* }}} */ @@ -1455,15 +1735,19 @@ MYSQLND_METHOD(mysqlnd_conn, store_result)(MYSQLND * const conn TSRMLS_DC) { MYSQLND_RES *result; + DBG_ENTER("mysqlnd_conn::store_result"); + DBG_INF_FMT("conn=%llu", conn->thread_id); + if (!conn->current_result) { - return NULL; + DBG_RETURN(NULL); } /* Nothing to store for UPSERT/LOAD DATA*/ if (conn->last_query_type != QUERY_SELECT || conn->state != CONN_FETCHING_DATA) { SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); - return NULL; + DBG_ERR("Command out of sync"); + DBG_RETURN(NULL); } MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_BUFFERED_SETS); @@ -1471,7 +1755,8 @@ MYSQLND_METHOD(mysqlnd_conn, store_result)(MYSQLND * const conn TSRMLS_DC) result = conn->current_result; conn->current_result = NULL; - return result->m.store_result(result, conn, FALSE TSRMLS_CC); + result = result->m.store_result(result, conn, FALSE TSRMLS_CC); + DBG_RETURN(result); } /* }}} */ @@ -1482,12 +1767,15 @@ MYSQLND_METHOD(mysqlnd_conn, get_connection_stats)(const MYSQLND * const conn, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC) { + DBG_ENTER("mysqlnd_conn::get_connection_stats"); + DBG_INF_FMT("conn=%llu", conn->thread_id); mysqlnd_fill_stats_hash(&(conn->stats), return_value TSRMLS_CC ZEND_FILE_LINE_CC); + DBG_VOID_RETURN; } /* }}} */ -MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn); +MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC); MYSQLND_CLASS_METHODS_START(mysqlnd_conn) @@ -1524,6 +1812,7 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn) MYSQLND_METHOD(mysqlnd_conn, get_proto_info), MYSQLND_METHOD(mysqlnd_conn, info), MYSQLND_METHOD(mysqlnd_conn, charset_name), + MYSQLND_METHOD(mysqlnd_conn, list_fields), MYSQLND_METHOD(mysqlnd_conn, list_method), MYSQLND_METHOD(mysqlnd_conn, insert_id), @@ -1544,16 +1833,20 @@ MYSQLND_CLASS_METHODS_END; /* {{{ mysqlnd_init */ -PHPAPI MYSQLND *mysqlnd_init(zend_bool persistent) +PHPAPI MYSQLND *_mysqlnd_init(zend_bool persistent TSRMLS_DC) { - MYSQLND *ret = pecalloc(1, sizeof(MYSQLND), persistent); + MYSQLND *ret = mnd_pecalloc(1, sizeof(MYSQLND), persistent); + + DBG_ENTER("mysqlnd_init"); + DBG_INF_FMT("persistent=%d", persistent); + SET_ERROR_AFF_ROWS(ret); ret->persistent = persistent; ret->m = & mysqlnd_mysqlnd_conn_methods; ret->m->get_reference(ret); - return ret; + DBG_RETURN(ret); } /* }}} */ @@ -1562,7 +1855,7 @@ PHPAPI MYSQLND *mysqlnd_init(zend_bool persistent) * * Every user visible function must have an entry in mysqlnd_functions[]. */ -static const zend_function_entry mysqlnd_functions[] = { +static zend_function_entry mysqlnd_functions[] = { {NULL, NULL, NULL} /* Must be the last line in mysqlnd_functions[] */ }; /* }}} */ @@ -1595,7 +1888,7 @@ PHPAPI void mysqlnd_minfo_print_hash(zval *values) php_info_print_table_row(2, s, Z_STRVAL_PP(values_entry)); } if (s) { - efree(s); + mnd_efree(s); } } else { php_info_print_table_row(2, string_key.s, Z_STRVAL_PP(values_entry)); @@ -1632,6 +1925,7 @@ void mysqlnd_minfo_print_hash(zval *values) */ PHP_MINFO_FUNCTION(mysqlnd) { + char buf[32]; zval values; php_info_print_table_start(); @@ -1642,19 +1936,33 @@ PHP_MINFO_FUNCTION(mysqlnd) php_info_print_table_header(2, "Client statistics", ""); mysqlnd_get_client_stats(&values); mysqlnd_minfo_print_hash(&values); + php_info_print_table_row(2, "Collecting statistics", MYSQLND_G(collect_statistics)? "Yes":"No"); + php_info_print_table_row(2, "Collecting memory statistics", MYSQLND_G(collect_memory_statistics)? "Yes":"No"); + + snprintf(buf, sizeof(buf), "%ld", MYSQLND_G(net_cmd_buffer_size)); + php_info_print_table_row(2, "Command buffer size", buf); + snprintf(buf, sizeof(buf), "%ld", MYSQLND_G(net_read_buffer_size)); + php_info_print_table_row(2, "Read buffer size", buf); + zval_dtor(&values); php_info_print_table_end(); } /* }}} */ -ZEND_DECLARE_MODULE_GLOBALS(mysqlnd) +ZEND_DECLARE_MODULE_GLOBALS(mysqlnd); + /* {{{ PHP_GINIT_FUNCTION */ static PHP_GINIT_FUNCTION(mysqlnd) { - mysqlnd_globals->collect_statistics = FALSE; + mysqlnd_globals->collect_statistics = TRUE; + mysqlnd_globals->collect_memory_statistics = FALSE; + mysqlnd_globals->debug = NULL; /* The actual string */ + mysqlnd_globals->dbg = NULL; /* The DBG object*/ + mysqlnd_globals->net_cmd_buffer_size = 2048; + mysqlnd_globals->net_read_buffer_size = 32768; } /* }}} */ @@ -1662,7 +1970,11 @@ static PHP_GINIT_FUNCTION(mysqlnd) /* {{{ PHP_INI_BEGIN */ PHP_INI_BEGIN() - STD_PHP_INI_BOOLEAN("mysqlnd.collect_statistics", "1", PHP_INI_SYSTEM, OnUpdateBool, collect_statistics, zend_mysqlnd_globals, mysqlnd_globals) + STD_PHP_INI_BOOLEAN("mysqlnd.collect_statistics", "1", PHP_INI_ALL, OnUpdateBool, collect_statistics, zend_mysqlnd_globals, mysqlnd_globals) + STD_PHP_INI_BOOLEAN("mysqlnd.collect_memory_statistics", "0", PHP_INI_SYSTEM, OnUpdateBool, collect_memory_statistics, zend_mysqlnd_globals, mysqlnd_globals) + STD_PHP_INI_ENTRY("mysqlnd.debug", NULL, PHP_INI_SYSTEM, OnUpdateString, debug, zend_mysqlnd_globals, mysqlnd_globals) + STD_PHP_INI_ENTRY("mysqlnd.net_cmd_buffer_size", "2048", PHP_INI_ALL, OnUpdateLong, net_cmd_buffer_size, zend_mysqlnd_globals, mysqlnd_globals) + STD_PHP_INI_ENTRY("mysqlnd.net_read_buffer_size", "32768",PHP_INI_ALL, OnUpdateLong, net_read_buffer_size, zend_mysqlnd_globals, mysqlnd_globals) PHP_INI_END() /* }}} */ @@ -1673,7 +1985,7 @@ static PHP_MINIT_FUNCTION(mysqlnd) { REGISTER_INI_ENTRIES(); - mysqlnd_library_init(MYSQLND_G(collect_statistics)); + mysqlnd_library_init(); return SUCCESS; } /* }}} */ @@ -1691,6 +2003,45 @@ static PHP_MSHUTDOWN_FUNCTION(mysqlnd) /* }}} */ +#if PHP_DEBUG +/* {{{ PHP_RINIT_FUNCTION + */ +static PHP_RINIT_FUNCTION(mysqlnd) +{ +#ifdef PHP_DEBUG + if (MYSQLND_G(debug)) { + MYSQLND_DEBUG *dbg = mysqlnd_debug_init(TSRMLS_C); + if (!dbg) { + return FAILURE; + } + dbg->m->set_mode(dbg, MYSQLND_G(debug)); + MYSQLND_G(dbg) = dbg; + } +#endif + return SUCCESS; +} +/* }}} */ + + +/* {{{ PHP_RSHUTDOWN_FUNCTION + */ +static PHP_RSHUTDOWN_FUNCTION(mysqlnd) +{ +#ifdef PHP_DEBUG + MYSQLND_DEBUG *dbg = MYSQLND_G(dbg); + DBG_ENTER("RSHUTDOWN"); + if (dbg) { + dbg->m->close(dbg); + dbg->m->free(dbg); + MYSQLND_G(dbg) = NULL; + } +#endif + return SUCCESS; +} +/* }}} */ +#endif + + /* {{{ mysqlnd_module_entry */ zend_module_entry mysqlnd_module_entry = { @@ -1699,8 +2050,13 @@ zend_module_entry mysqlnd_module_entry = { mysqlnd_functions, PHP_MINIT(mysqlnd), PHP_MSHUTDOWN(mysqlnd), +#if PHP_DEBUG + PHP_RINIT(mysqlnd), + PHP_RSHUTDOWN(mysqlnd), +#else NULL, NULL, +#endif PHP_MINFO(mysqlnd), MYSQLND_VERSION, PHP_MODULE_GLOBALS(mysqlnd), diff --git a/ext/mysqlnd/mysqlnd.h b/ext/mysqlnd/mysqlnd.h index b6ca05454b..f648207fdc 100644 --- a/ext/mysqlnd/mysqlnd.h +++ b/ext/mysqlnd/mysqlnd.h @@ -23,13 +23,9 @@ #ifndef MYSQLND_H #define MYSQLND_H -#define MYSQLND_VERSION "mysqlnd 5.0.2-dev - 070702 - $Revision$" +#define MYSQLND_VERSION "mysqlnd 5.0.2-dev - 070928 - $Revision$" #define MYSQLND_VERSION_ID 50002 -#define phpext_mysqlnd_ptr &mysqlnd_module_entry -extern zend_module_entry mysqlnd_module_entry; - - /* This forces inlining of some accessor functions */ #define MYSQLND_USE_OPTIMISATIONS 0 @@ -49,607 +45,18 @@ extern zend_module_entry mysqlnd_module_entry; #define MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND 1 #endif -#include "mysqlnd_portability.h" - #ifdef ZTS #include "TSRM.h" #endif +#include "mysqlnd_portability.h" #include "mysqlnd_enum_n_def.h" - -/* - /-----> CONN_CLOSE <---------------\ - | ^ \ - | | \ - CONN_READY -> CONN_QUERY_SENT -> CONN_FETCHING_DATA - ^ | - \-------------------------------------/ -*/ -typedef enum mysqlnd_connection_state -{ - CONN_ALLOCED = 0, - CONN_READY, - CONN_QUERY_SENT, - CONN_SENDING_LOAD_DATA, - CONN_FETCHING_DATA, - CONN_NEXT_RESULT_PENDING, - CONN_QUIT_SENT, /* object is "destroyed" at this stage */ -} enum_mysqlnd_connection_state; - - -typedef enum mysqlnd_stmt_state { - MYSQLND_STMT_INITTED = 0, - MYSQLND_STMT_PREPARED, - MYSQLND_STMT_EXECUTED, - MYSQLND_STMT_WAITING_USE_OR_STORE, - MYSQLND_STMT_USE_OR_STORE_CALLED, - MYSQLND_STMT_USER_FETCHING, /* fetch_row_buff or fetch_row_unbuf */ -} enum_mysqlnd_stmt_state; - - -typedef enum param_bind_flags { - MYSQLND_PARAM_BIND_BLOB_USED = 1 -} enum_param_bind_flags; - - -/* PS */ -enum mysqlnd_stmt_attr -{ - STMT_ATTR_UPDATE_MAX_LENGTH, - STMT_ATTR_CURSOR_TYPE, - STMT_ATTR_PREFETCH_ROWS -}; - -enum myslqnd_cursor_type -{ - CURSOR_TYPE_NO_CURSOR= 0, - CURSOR_TYPE_READ_ONLY= 1, - CURSOR_TYPE_FOR_UPDATE= 2, - CURSOR_TYPE_SCROLLABLE= 4 -}; - -typedef enum mysqlnd_connection_close_type -{ - MYSQLND_CLOSE_EXPLICIT = 0, - MYSQLND_CLOSE_IMPLICIT, - MYSQLND_CLOSE_DISCONNECTED, - MYSQLND_CLOSE_LAST /* for checking, should always be last */ -} enum_connection_close_type; - -typedef enum mysqlnd_collected_stats { - STAT_BYTES_SENT, - STAT_BYTES_RECEIVED, - STAT_PACKETS_SENT, - STAT_PACKETS_RECEIVED, - STAT_PROTOCOL_OVERHEAD_IN, - STAT_PROTOCOL_OVERHEAD_OUT, - STAT_RSET_QUERY, - STAT_NON_RSET_QUERY, - STAT_NO_INDEX_USED, - STAT_BAD_INDEX_USED, - STAT_BUFFERED_SETS, - STAT_UNBUFFERED_SETS, - STAT_PS_BUFFERED_SETS, - STAT_PS_UNBUFFERED_SETS, - STAT_FLUSHED_NORMAL_SETS, - STAT_FLUSHED_PS_SETS, - STAT_ROWS_FETCHED_FROM_SERVER, - STAT_ROWS_FETCHED_FROM_CLIENT, - STAT_ROWS_SKIPPED, - STAT_COPY_ON_WRITE_SAVED, - STAT_COPY_ON_WRITE_PERFORMED, - STAT_CMD_BUFFER_TOO_SMALL, - STAT_CONNECT_SUCCESS, - STAT_CONNECT_FAILURE, - STAT_CONNECT_REUSED, - STAT_CLOSE_EXPLICIT, - STAT_CLOSE_IMPLICIT, - STAT_CLOSE_DISCONNECT, - STAT_CLOSE_IN_MIDDLE, - STAT_FREE_RESULT_EXPLICIT, - STAT_FREE_RESULT_IMPLICIT, - STAT_STMT_CLOSE_EXPLICIT, - STAT_STMT_CLOSE_IMPLICIT, - STAT_LAST /* Should be always the last */ -} enum_mysqlnd_collected_stats; - - -typedef struct st_mysqlnd_cmd_buffer { - zend_uchar *buffer; - size_t length; -} MYSQLND_CMD_BUFFER; - - -typedef struct st_mysqlnd_field { - char *name; /* Name of column */ - char *org_name; /* Original column name, if an alias */ - char *table; /* Table of column if column was a field */ - char *org_table; /* Org table name, if table was an alias */ - char *db; /* Database for table */ - char *catalog; /* Catalog for table */ - char *def; /* Default value (set by mysql_list_fields) */ - unsigned long length; /* Width of column (create length) */ - unsigned long max_length; /* Max width for selected set */ - unsigned int name_length; - unsigned int org_name_length; - unsigned int table_length; - unsigned int org_table_length; - unsigned int db_length; - unsigned int catalog_length; - unsigned int def_length; - unsigned int flags; /* Diverse flags */ - unsigned int decimals; /* Number of decimals in field */ - unsigned int charsetnr; /* Character set */ - enum mysqlnd_field_types type; /* Type of field. See mysql_com.h for types */ - char *root; - size_t root_len; -} MYSQLND_FIELD; - - - -typedef struct st_mysqlnd_upsert_result { - unsigned int warning_count; - unsigned int server_status; - unsigned long long affected_rows; - unsigned long long last_insert_id; -} mysqlnd_upsert_status; - - -typedef struct st_mysqlnd_error_info { - char error[MYSQLND_ERRMSG_SIZE+1]; - char sqlstate[MYSQLND_SQLSTATE_LENGTH + 1]; - unsigned int error_no; -} mysqlnd_error_info; - - -typedef struct st_mysqlnd_zval_pcache MYSQLND_ZVAL_PCACHE; -typedef struct st_mysqlnd_thread_zval_pcache MYSQLND_THD_ZVAL_PCACHE; -typedef struct st_mysqlnd_qcache MYSQLND_QCACHE; - - -typedef struct st_mysqlnd_infile_info { - php_stream *fd; - int error_no; - char error_msg[MYSQLND_ERRMSG_SIZE + 1]; - const char *filename; - zval *callback; -} MYSQLND_INFILE_INFO; - - -/* character set information */ -typedef struct st_mysqlnd_charset -{ - uint nr; - char *name; - char *collation; - uint char_minlen; - uint char_maxlen; - uint dangerous_for_escape_backslash; - uint (*mb_charlen)(uint c); - uint (*mb_valid)(const char *start, const char *end); -} MYSQLND_CHARSET; - - -/* local infile handler */ -typedef struct st_mysqlnd_infile -{ - int (*local_infile_init)(void **ptr, char *filename, void **userdata TSRMLS_DC); - int (*local_infile_read)(void *ptr, char *buf, uint buf_len TSRMLS_DC); - int (*local_infile_error)(void *ptr, char *error_msg, uint error_msg_len TSRMLS_DC); - void (*local_infile_end)(void *ptr TSRMLS_DC); - zval *callback; - void *userdata; -} MYSQLND_INFILE; - -typedef struct st_mysqlnd_option { - /* timeouts */ - uint timeout_connect; - uint timeout_read; - uint timeout_write; - - ulong flags; - - /* init commands - we need to send them to server directly after connect */ - uint num_commands; - char **init_commands; - - /* configuration file information */ - char *cfg_file; - char *cfg_section; - - /* SSL information */ - char *ssl_key; - char *ssl_cert; - char *ssl_ca; - char *ssl_capath; - char *ssl_cipher; - zend_bool use_ssl; - - char *charset_name; - /* maximum allowed packet size for communication */ - ulong max_allowed_packet; - - zend_bool numeric_and_datetime_as_unicode; -#ifdef MYSQLND_STRING_TO_INT_CONVERSION - zend_bool int_and_year_as_int; -#endif -} MYSQLND_OPTION; - - -typedef struct st_mysqlnd_connection MYSQLND; -typedef struct st_mysqlnd_res MYSQLND_RES; -typedef char** MYSQLND_ROW; /* return data as array of strings */ -typedef struct st_mysqlnd_stmt MYSQLND_STMT; -typedef unsigned int MYSQLND_FIELD_OFFSET; - -typedef struct st_mysqlnd_param_bind MYSQLND_PARAM_BIND; - -typedef struct st_mysqlnd_result_bind MYSQLND_RESULT_BIND; - -typedef struct st_mysqlnd_result_metadata MYSQLND_RES_METADATA; -typedef struct st_mysqlnd_buffered_result MYSQLND_RES_BUFFERED; -typedef struct st_mysqlnd_unbuffered_result MYSQLND_RES_UNBUFFERED; - - -typedef MYSQLND_RES* (*mysqlnd_stmt_use_or_store_func)(MYSQLND_STMT * const TSRMLS_DC); -typedef enum_func_status (*mysqlnd_fetch_row_func)(MYSQLND_RES *result, - void *param, - unsigned int flags, - zend_bool *fetched_anything - TSRMLS_DC); - -typedef struct st_mysqlnd_stats { - my_uint64 values[STAT_LAST]; -#ifdef ZTS - MUTEX_T LOCK_access; -#endif -} MYSQLND_STATS; - - -typedef struct st_mysqlnd_net { - php_stream *stream; - /* sequence for simple checking of correct packets */ - zend_uchar packet_no; - -#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND - zend_uchar last_command; -#endif - - /* cmd buffer */ - MYSQLND_CMD_BUFFER cmd_buffer; -} MYSQLND_NET; - - -struct st_mysqlnd_conn_methods { - ulong (*escape_string)(const MYSQLND * const conn, char *newstr, const char *escapestr, int escapestr_len); - enum_func_status (*set_charset)(MYSQLND * const conn, const char * const charset TSRMLS_DC); - enum_func_status (*query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC); - MYSQLND_RES * (*use_result)(MYSQLND * const conn TSRMLS_DC); - MYSQLND_RES * (*store_result)(MYSQLND * const conn TSRMLS_DC); - enum_func_status (*next_result)(MYSQLND * const conn TSRMLS_DC); - zend_bool (*more_results)(const MYSQLND * const conn); - - MYSQLND_STMT * (*stmt_init)(MYSQLND * const conn); - - enum_func_status (*shutdown_server)(MYSQLND * const conn, unsigned long level TSRMLS_DC); - enum_func_status (*refresh_server)(MYSQLND * const conn, unsigned long options TSRMLS_DC); - - enum_func_status (*ping)(MYSQLND * const conn TSRMLS_DC); - enum_func_status (*kill_connection)(MYSQLND *conn, unsigned long pid TSRMLS_DC); - enum_func_status (*select_db)(MYSQLND * const conn, const char * const db, unsigned int db_len TSRMLS_DC); - enum_func_status (*server_dump_debug_information)(MYSQLND * const conn TSRMLS_DC); - enum_func_status (*change_user)(MYSQLND * const conn, const char * user, const char * passwd, const char * db TSRMLS_DC); - - unsigned int (*get_error_no)(const MYSQLND * const conn); - const char * (*get_error_str)(const MYSQLND * const conn); - const char * (*get_sqlstate)(const MYSQLND * const conn); - mynd_ulonglong (*get_thread_id)(const MYSQLND * const conn); - void (*get_statistics)(const MYSQLND * const conn, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); - - unsigned long (*get_server_version)(const MYSQLND * const conn); - const char * (*get_server_information)(const MYSQLND * const conn); - enum_func_status (*get_server_statistics)(MYSQLND *conn, char **message, unsigned int * message_len TSRMLS_DC); - const char * (*get_host_information)(const MYSQLND * const conn); - unsigned int (*get_protocol_information)(const MYSQLND * const conn); - const char * (*get_last_message)(const MYSQLND * const conn); - const char * (*charset_name)(const MYSQLND * const conn); - MYSQLND_RES * (*list_method)(MYSQLND *conn, const char *query, char *achtung_wild, char *par1 TSRMLS_DC); - - mynd_ulonglong (*get_last_insert_id)(const MYSQLND * const conn); - mynd_ulonglong (*get_affected_rows)(const MYSQLND * const conn); - unsigned int (*get_warning_count)(const MYSQLND * const conn); - - unsigned int (*get_field_count)(const MYSQLND * const conn); - - enum_func_status (*set_server_option)(MYSQLND * const conn, enum_mysqlnd_server_option option TSRMLS_DC); - enum_func_status (*set_client_option)(MYSQLND * const conn, enum_mysqlnd_option option, const char * const value); - void (*free_contents)(MYSQLND *conn TSRMLS_DC); /* private */ - enum_func_status (*close)(MYSQLND *conn, enum_connection_close_type close_type TSRMLS_DC); - void (*dtor)(MYSQLND *conn TSRMLS_DC); /* private */ - - MYSQLND * (*get_reference)(MYSQLND * const conn); - void (*free_reference)(MYSQLND * const conn TSRMLS_DC); -}; - - -struct st_mysqlnd_res_methods { - mysqlnd_fetch_row_func fetch_row; - mysqlnd_fetch_row_func fetch_row_normal_buffered; /* private */ - mysqlnd_fetch_row_func fetch_row_normal_unbuffered; /* private */ - - MYSQLND_RES * (*use_result)(MYSQLND_RES * const result, zend_bool ps_protocol TSRMLS_DC); - MYSQLND_RES * (*store_result)(MYSQLND_RES * result, MYSQLND * const conn, zend_bool ps TSRMLS_DC); - void (*fetch_into)(MYSQLND_RES *result, unsigned int flags, zval *return_value, enum_mysqlnd_extension ext TSRMLS_DC ZEND_FILE_LINE_DC); - void (*fetch_all)(MYSQLND_RES *result, unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); - void (*fetch_field_data)(MYSQLND_RES *result, unsigned int offset, zval *return_value TSRMLS_DC); - mynd_ulonglong (*num_rows)(const MYSQLND_RES * const result); - unsigned int (*num_fields)(const MYSQLND_RES * const result); - enum_func_status (*skip_result)(MYSQLND_RES * const result TSRMLS_DC); - enum_func_status (*seek_data)(MYSQLND_RES * result, mynd_ulonglong row); - MYSQLND_FIELD_OFFSET (*seek_field)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET field_offset); - MYSQLND_FIELD_OFFSET (*field_tell)(const MYSQLND_RES * const result); - MYSQLND_FIELD * (*fetch_field)(MYSQLND_RES * const result); - MYSQLND_FIELD * (*fetch_field_direct)(const MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET fieldnr); - - enum_func_status (*read_result_metadata)(MYSQLND_RES *result, MYSQLND *conn TSRMLS_DC); - unsigned long * (*fetch_lengths)(MYSQLND_RES * const result); - void (*free_result_buffers)(MYSQLND_RES * result TSRMLS_DC); /* private */ - enum_func_status (*free_result)(MYSQLND_RES * result, zend_bool implicit TSRMLS_DC); - void (*free_result_internal)(MYSQLND_RES *result TSRMLS_DC); - void (*free_result_contents)(MYSQLND_RES *result TSRMLS_DC); -}; - - -struct st_mysqlnd_res_meta_methods { - MYSQLND_FIELD * (*fetch_field)(MYSQLND_RES_METADATA * const meta); - MYSQLND_FIELD * (*fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, MYSQLND_FIELD_OFFSET fieldnr); - MYSQLND_FIELD_OFFSET (*field_tell)(const MYSQLND_RES_METADATA * const meta); - enum_func_status (*read_metadata)(MYSQLND_RES_METADATA * const meta, MYSQLND *conn TSRMLS_DC); - MYSQLND_RES_METADATA * (*clone_metadata)(const MYSQLND_RES_METADATA * const meta, zend_bool persistent); - void (*free_metadata)(MYSQLND_RES_METADATA *meta, zend_bool persistent TSRMLS_DC); -}; - - -struct st_mysqlnd_stmt_methods { - enum_func_status (*prepare)(MYSQLND_STMT * const stmt, const char * const query, unsigned int query_len TSRMLS_DC); - enum_func_status (*execute)(MYSQLND_STMT * const stmt TSRMLS_DC); - MYSQLND_RES * (*use_result)(MYSQLND_STMT * const stmt TSRMLS_DC); - MYSQLND_RES * (*store_result)(MYSQLND_STMT * const stmt TSRMLS_DC); - MYSQLND_RES * (*get_result)(MYSQLND_STMT * const stmt TSRMLS_DC); - enum_func_status (*free_result)(MYSQLND_STMT * const stmt TSRMLS_DC); - enum_func_status (*seek_data)(const MYSQLND_STMT * const stmt, mynd_ulonglong row); - enum_func_status (*reset)(MYSQLND_STMT * const stmt TSRMLS_DC); - enum_func_status (*close)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC); /* private */ - enum_func_status (*dtor)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC); /* use this for mysqlnd_stmt_close */ - - enum_func_status (*fetch)(MYSQLND_STMT * const stmt, zend_bool * const fetched_anything TSRMLS_DC); - - enum_func_status (*bind_param)(MYSQLND_STMT * const stmt, MYSQLND_PARAM_BIND * const param_bind); - enum_func_status (*bind_result)(MYSQLND_STMT * const stmt, MYSQLND_RESULT_BIND * const result_bind); - enum_func_status (*send_long_data)(MYSQLND_STMT * const stmt, unsigned int param_num, - const char * const data, unsigned long length TSRMLS_DC); - MYSQLND_RES * (*get_parameter_metadata)(MYSQLND_STMT * const stmt); - MYSQLND_RES * (*get_result_metadata)(MYSQLND_STMT * const stmt); - - mynd_ulonglong (*get_last_insert_id)(const MYSQLND_STMT * const stmt); - mynd_ulonglong (*get_affected_rows)(const MYSQLND_STMT * const stmt); - mynd_ulonglong (*get_num_rows)(const MYSQLND_STMT * const stmt); - - unsigned int (*get_param_count)(const MYSQLND_STMT * const stmt); - unsigned int (*get_field_count)(const MYSQLND_STMT * const stmt); - unsigned int (*get_warning_count)(const MYSQLND_STMT * const stmt); - - unsigned int (*get_error_no)(const MYSQLND_STMT * const stmt); - const char * (*get_error_str)(const MYSQLND_STMT * const stmt); - const char * (*get_sqlstate)(const MYSQLND_STMT * const stmt); - - enum_func_status (*get_attribute)(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, void * const value); - enum_func_status (*set_attribute)(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, const void * const value); -}; - - -struct st_mysqlnd_connection { -/* Operation related */ - MYSQLND_NET net; - -/* Information related */ - char *host; - char *unix_socket; - char *user; - char *passwd; - unsigned int *passwd_len; - char *scheme; - unsigned long long thread_id; - char *server_version; - char *host_info; - unsigned char *scramble; - const MYSQLND_CHARSET *charset; - MYSQLND_INFILE infile; - unsigned int protocol_version; - unsigned long max_packet_size; - unsigned int port; - unsigned long client_flag; - unsigned long server_capabilities; - - int tmp_int; - - - /* For UPSERT queries */ - mysqlnd_upsert_status upsert_status; - char *last_message; - unsigned int last_message_len; - - /* If error packet, we use these */ - mysqlnd_error_info error_info; - - /* - To prevent queries during unbuffered fetches. Also to - mark the connection as destroyed for garbage collection. - */ - enum mysqlnd_connection_state state; - enum_mysqlnd_query_type last_query_type; - /* Temporary storage between query and (use|store)_result() call */ - MYSQLND_RES *current_result; - - /* - How many result sets reference this connection. - It won't be freed until this number reaches 0. - The last one, please close the door! :-) - The result set objects can determine by inspecting - 'quit_sent' whether the connection is still valid. - */ - unsigned int refcount; - - /* Temporal storage for mysql_query */ - unsigned int field_count; - - /* persistent connection */ - zend_bool persistent; - - /* options */ - MYSQLND_OPTION options; - - /* zval cache */ - MYSQLND_THD_ZVAL_PCACHE *zval_cache; - - /* qcache */ - MYSQLND_QCACHE *qcache; - - /* stats */ - MYSQLND_STATS stats; - - struct st_mysqlnd_conn_methods *m; -}; - -typedef struct st_php_mysql_packet_row php_mysql_packet_row; - - -struct mysqlnd_field_hash_key { - zend_bool is_numeric; - unsigned long key; -#if PHP_MAJOR_VERSION >= 6 - zstr ustr; - unsigned int ulen; -#endif -}; - - -struct st_mysqlnd_result_metadata { - MYSQLND_FIELD *fields; - struct mysqlnd_field_hash_key *zend_hash_keys; - unsigned int current_field; - unsigned int field_count; - /* We need this to make fast allocs in rowp_read */ - unsigned int bit_fields_count; - size_t bit_fields_total_len; /* trailing \0 not counted */ - - struct st_mysqlnd_res_meta_methods *m; -}; - - -struct st_mysqlnd_buffered_result { - zval ***data; - zval ***data_cursor; - zend_uchar **row_buffers; - mynd_ulonglong row_count; - zend_bool persistent; - - MYSQLND_QCACHE *qcache; - unsigned int references; - - zend_bool async_invalid; - mysqlnd_error_info error_info; -}; - - -struct st_mysqlnd_unbuffered_result { - /* For unbuffered (both normal and PS) */ - zval **last_row_data; - zend_uchar *last_row_buffer; - - mynd_ulonglong row_count; - zend_bool eof_reached; -}; - - -struct st_mysqlnd_res { - struct st_mysqlnd_res_methods m; - - MYSQLND *conn; - enum_mysqlnd_res_type type; - unsigned int field_count; - - /* For metadata functions */ - MYSQLND_RES_METADATA *meta; - - /* To be used with store_result() - both normal and PS */ - MYSQLND_RES_BUFFERED *data; - - MYSQLND_RES_UNBUFFERED *unbuf; - - /* - Column lengths of current row - both buffered and unbuffered. - For buffered results it duplicates the data found in **data - */ - unsigned long *lengths; - - php_mysql_packet_row *row_packet; /* Unused for PS */ - - /* zval cache */ - MYSQLND_THD_ZVAL_PCACHE *zval_cache; -}; - - - - -#define MYSQLND_DEFAULT_PREFETCH_ROWS (ulong) 1 - -struct st_mysqlnd_param_bind { - zval *zv; - zend_uchar type; - enum_param_bind_flags flags; -}; - -struct st_mysqlnd_result_bind { - zval *zv; - zend_uchar original_type; - zend_bool bound; -}; - - -struct st_mysqlnd_stmt { - MYSQLND *conn; - unsigned long stmt_id; - unsigned long flags;/* cursor is set here */ - enum_mysqlnd_stmt_state state; - unsigned int warning_count; - MYSQLND_RES *result; - unsigned int field_count; - unsigned int param_count; - unsigned char send_types_to_server; - MYSQLND_PARAM_BIND *param_bind; - MYSQLND_RESULT_BIND *result_bind; - zend_bool result_zvals_separated_once; - - mysqlnd_upsert_status upsert_status; - - mysqlnd_error_info error_info; - - zend_bool update_max_length; - unsigned long prefetch_rows; - - zend_bool cursor_exists; - mysqlnd_stmt_use_or_store_func default_rset_handler; - - MYSQLND_CMD_BUFFER cmd_buffer; - - struct st_mysqlnd_stmt_methods *m; -}; - +#include "mysqlnd_structs.h" /* Library related */ -PHPAPI void mysqlnd_restart_psession(MYSQLND *conn); +#define mysqlnd_restart_psession(conn) _mysqlnd_restart_psession((conn) TSRMLS_CC) +PHPAPI void _mysqlnd_restart_psession(MYSQLND *conn TSRMLS_DC); PHPAPI void mysqlnd_end_psession(MYSQLND *conn); PHPAPI void mysqlnd_minfo_print_hash(zval *values); #define mysqlnd_thread_safe() TRUE @@ -659,7 +66,8 @@ PHPAPI const MYSQLND_CHARSET * mysqlnd_find_charset_name(const char * const char /* Connect */ -PHPAPI MYSQLND * mysqlnd_init(zend_bool persistent); +#define mysqlnd_init(persistent) _mysqlnd_init((persistent) TSRMLS_CC) +PHPAPI MYSQLND * _mysqlnd_init(zend_bool persistent TSRMLS_DC); PHPAPI MYSQLND * mysqlnd_connect(MYSQLND *conn, char *host, char *user, char *passwd, unsigned int passwd_len, @@ -672,7 +80,8 @@ PHPAPI MYSQLND * mysqlnd_connect(MYSQLND *conn, #define mysqlnd_change_user(conn, user, passwd, db) (conn)->m->change_user((conn), (user), (passwd), (db) TSRMLS_CC) -#define mysqlnd_debug(x) +#define mysqlnd_debug(x) _mysqlnd_debug((x) TSRMLS_CC) +void _mysqlnd_debug(const char *mode TSRMLS_DC); /* Query */ #define mysqlnd_fetch_into(result, flags, ret_val, ext) (result)->m.fetch_into((result), (flags), (ret_val), (ext) TSRMLS_CC ZEND_FILE_LINE_CC) @@ -691,7 +100,7 @@ PHPAPI MYSQLND * mysqlnd_connect(MYSQLND *conn, #define mysqlnd_next_result(conn) (conn)->m->next_result((conn) TSRMLS_CC) #define mysqlnd_more_results(conn) (conn)->m->more_results((conn)) #define mysqlnd_free_result(r,e_or_i) ((MYSQLND_RES*)r)->m.free_result(((MYSQLND_RES*)(r)), (e_or_i) TSRMLS_CC) -#define mysqlnd_data_seek(result, row) (result)->m.seek_data((result), (row)) +#define mysqlnd_data_seek(result, row) (result)->m.seek_data((result), (row) TSRMLS_CC) /*****************************************************************************************************/ #if defined(MYSQLND_USE_OPTIMISATIONS) && MYSQLND_USE_OPTIMISATIONS == 1 @@ -722,7 +131,7 @@ PHPAPI MYSQLND * mysqlnd_connect(MYSQLND *conn, #define mysqlnd_field_seek(result, ofs) (result)->m.seek_field((result), (ofs)) #define mysqlnd_field_tell(result) (result)->meta? (result)->meta->current_field:0) -#define mysqlnd_fetch_field(result) (result)->m.fetch_field((result)) +#define mysqlnd_fetch_field(result) (result)->m.fetch_field((result) TSRMLS_CC) #define mysqlnd_fetch_field_direct(result,fnr) ((result)->meta? &((result)->meta->fields[(fnr)]):NULL) /* mysqlnd metadata */ @@ -772,8 +181,8 @@ PHPAPI unsigned long * mysqlnd_fetch_lengths(MYSQLND_RES * const result); #define mysqlnd_field_seek(result, ofs) (result)->m.seek_field((result), (ofs)) #define mysqlnd_field_tell(result) (result)->m.field_tell((result)) -#define mysqlnd_fetch_field(result) (result)->m.fetch_field((result)) -#define mysqlnd_fetch_field_direct(result,fnr) (result)->m.fetch_field_direct((result), (fnr)) +#define mysqlnd_fetch_field(result) (result)->m.fetch_field((result) TSRMLS_CC) +#define mysqlnd_fetch_field_direct(result,fnr) (result)->m.fetch_field_direct((result), (fnr) TSRMLS_CC) /* mysqlnd metadata */ PHPAPI const char * mysqlnd_get_client_info(); @@ -797,7 +206,7 @@ PHPAPI unsigned int mysqlnd_get_client_version(); PHPAPI const char * mysqlnd_field_type_name(enum mysqlnd_field_types field_type); /* LOAD DATA LOCAL */ -PHPAPI void mysqlnd_local_infile_default(MYSQLND *conn, zend_bool free_callback); +PHPAPI void mysqlnd_local_infile_default(MYSQLND *conn); PHPAPI void mysqlnd_set_local_infile_handler(MYSQLND * const conn, const char * const funcname); /* Simple commands */ @@ -805,7 +214,7 @@ PHPAPI void mysqlnd_set_local_infile_handler(MYSQLND * const conn, const char * #define mysqlnd_commit(conn) (conn)->m->query((conn), "COMMIT", sizeof("COMMIT")-1 TSRMLS_CC) #define mysqlnd_rollback(conn) (conn)->m->query((conn), "ROLLBACK", sizeof("ROLLBACK")-1 TSRMLS_CC) #define mysqlnd_list_dbs(conn, wild) (conn)->m->list_method((conn), wild? "SHOW DATABASES LIKE %s":"SHOW DATABASES", (wild), NULL TSRMLS_CC) -#define mysqlnd_list_fields(conn, tab,wild) (conn)->m->list_method((conn), wild? "SHOW FIELDS FROM %s LIKE %s":"SHOW FIELDS FROM %s", wild, tab TSRMLS_CC) +#define mysqlnd_list_fields(conn, tab,wild) (conn)->m->list_fields((conn), (tab), (wild) TSRMLS_CC) #define mysqlnd_list_processes(conn) (conn)->m->list_method((conn), "SHOW PROCESSLIST", NULL, NULL TSRMLS_CC) #define mysqlnd_list_tables(conn, wild) (conn)->m->list_method((conn), wild? "SHOW TABLES LIKE %s":"SHOW TABLES", (wild), NULL TSRMLS_CC) #define mysqlnd_dump_debug_info(conn) (conn)->m->server_dump_debug_information((conn) TSRMLS_CC) @@ -817,38 +226,38 @@ PHPAPI void mysqlnd_set_local_infile_handler(MYSQLND * const conn, const char * #define mysqlnd_get_server_version(conn) (conn)->m->get_server_version((conn)) #define mysqlnd_set_character_set(conn, cs) (conn)->m->set_charset((conn), (cs) TSRMLS_CC) #define mysqlnd_stat(conn, msg, msg_len) (conn)->m->get_server_statistics((conn), (msg), (msg_len) TSRMLS_CC) -#define mysqlnd_options(conn, opt, value) (conn)->m->set_client_option((conn), (opt), (value)) +#define mysqlnd_options(conn, opt, value) (conn)->m->set_client_option((conn), (opt), (value) TSRMLS_CC) #define mysqlnd_set_server_option(conn, op) (conn)->m->set_server_option((conn), (op) TSRMLS_CC) /* Escaping */ #define mysqlnd_real_escape_string(conn, newstr, escapestr, escapestr_len) \ - (conn)->m->escape_string((conn), (newstr), (escapestr), (escapestr_len)) + (conn)->m->escape_string((conn), (newstr), (escapestr), (escapestr_len) TSRMLS_CC) #define mysqlnd_escape_string(newstr, escapestr, escapestr_len) \ - mysqlnd_old_escape_string((newstr), (escapestr), (escapestr_len)) + mysqlnd_old_escape_string((newstr), (escapestr), (escapestr_len) TSRMLS_CC) -PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, int escapestr_len); +PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, int escapestr_len TSRMLS_DC); /* PS */ -#define mysqlnd_stmt_init(conn) (conn)->m->stmt_init((conn)) +#define mysqlnd_stmt_init(conn) (conn)->m->stmt_init((conn) TSRMLS_CC) #define mysqlnd_stmt_store_result(stmt) (!mysqlnd_stmt_field_count((stmt)) ? PASS:((stmt)->m->store_result((stmt) TSRMLS_CC)? PASS:FAIL)) #define mysqlnd_stmt_get_result(stmt) (stmt)->m->get_result((stmt) TSRMLS_CC) -#define mysqlnd_stmt_data_seek(stmt, row) (stmt)->m->seek_data((stmt), (row)) +#define mysqlnd_stmt_data_seek(stmt, row) (stmt)->m->seek_data((stmt), (row) TSRMLS_CC) #define mysqlnd_stmt_prepare(stmt, q, qlen) (stmt)->m->prepare((stmt), (q), (qlen) TSRMLS_CC) #define mysqlnd_stmt_execute(stmt) (stmt)->m->execute((stmt) TSRMLS_CC) #define mysqlnd_stmt_send_long_data(s,p,d,l) (s)->m->send_long_data((s), (p), (d), (l) TSRMLS_CC) -#define mysqlnd_stmt_bind_param(stmt,bind) (stmt)->m->bind_param((stmt), (bind)) -#define mysqlnd_stmt_bind_result(stmt,bind) (stmt)->m->bind_result((stmt), (bind)) +#define mysqlnd_stmt_bind_param(stmt,bind) (stmt)->m->bind_param((stmt), (bind) TSRMLS_CC) +#define mysqlnd_stmt_bind_result(stmt,bind) (stmt)->m->bind_result((stmt), (bind) TSRMLS_CC) #define mysqlnd_stmt_param_metadata(stmt) (stmt)->m->get_parameter_metadata((stmt)) -#define mysqlnd_stmt_result_metadata(stmt) (stmt)->m->get_result_metadata((stmt)) +#define mysqlnd_stmt_result_metadata(stmt) (stmt)->m->get_result_metadata((stmt) TSRMLS_CC) #define mysqlnd_stmt_free_result(stmt) (stmt)->m->free_result((stmt) TSRMLS_CC) #define mysqlnd_stmt_close(stmt, implicit) (stmt)->m->dtor((stmt), (implicit) TSRMLS_CC) #define mysqlnd_stmt_reset(stmt) (stmt)->m->reset((stmt) TSRMLS_CC) -#define mysqlnd_stmt_attr_get(stmt, attr, value) (stmt)->m->get_attribute((stmt), (attr), (value)) -#define mysqlnd_stmt_attr_set(stmt, attr, value) (stmt)->m->set_attribute((stmt), (attr), (value)) +#define mysqlnd_stmt_attr_get(stmt, attr, value) (stmt)->m->get_attribute((stmt), (attr), (value) TSRMLS_CC) +#define mysqlnd_stmt_attr_set(stmt, attr, value) (stmt)->m->set_attribute((stmt), (attr), (value) TSRMLS_CC) #define mysqlnd_stmt_fetch(stmt, fetched) (stmt)->m->fetch((stmt), (fetched) TSRMLS_CC) @@ -857,21 +266,29 @@ PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, int PHPAPI void _mysqlnd_get_client_stats(zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); /* Persistent caching zval allocator */ -PHPAPI MYSQLND_ZVAL_PCACHE* mysqlnd_palloc_init_cache(unsigned int cache_size); -PHPAPI void mysqlnd_palloc_free_cache(MYSQLND_ZVAL_PCACHE *cache); -PHPAPI void mysqlnd_palloc_stats(const MYSQLND_ZVAL_PCACHE * const cache, zval *return_value); +#define mysqlnd_palloc_init_cache(size) _mysqlnd_palloc_init_cache((size) TSRMLS_CC) +#define mysqlnd_palloc_free_cache(cache) _mysqlnd_palloc_free_cache((cache) TSRMLS_CC) +PHPAPI MYSQLND_ZVAL_PCACHE* _mysqlnd_palloc_init_cache(unsigned int cache_size TSRMLS_DC); +PHPAPI void _mysqlnd_palloc_free_cache(MYSQLND_ZVAL_PCACHE *cache TSRMLS_DC); +PHPAPI void mysqlnd_palloc_stats(const MYSQLND_ZVAL_PCACHE * const cache, + zval *return_value); + +#define mysqlnd_palloc_rinit(cache) _mysqlnd_palloc_rinit((cache) TSRMLS_CC) +#define mysqlnd_palloc_rshutdown(cache) _mysqlnd_palloc_rshutdown((cache) TSRMLS_CC) +PHPAPI MYSQLND_THD_ZVAL_PCACHE * _mysqlnd_palloc_rinit(MYSQLND_ZVAL_PCACHE * cache TSRMLS_DC); +PHPAPI void _mysqlnd_palloc_rshutdown(MYSQLND_THD_ZVAL_PCACHE * cache TSRMLS_DC); -PHPAPI MYSQLND_THD_ZVAL_PCACHE * mysqlnd_palloc_rinit(MYSQLND_ZVAL_PCACHE * cache); -PHPAPI void mysqlnd_palloc_rshutdown(MYSQLND_THD_ZVAL_PCACHE * cache); +#define mysqlnd_palloc_init_thd_cache(cache) _mysqlnd_palloc_init_thd_cache((cache) TSRMLS_CC) +#define mysqlnd_palloc_free_thd_cache_reference(cache) _mysqlnd_palloc_free_thd_cache_reference((cache) TSRMLS_CC) -PHPAPI MYSQLND_THD_ZVAL_PCACHE* mysqlnd_palloc_init_thd_cache(MYSQLND_ZVAL_PCACHE * const cache); -MYSQLND_THD_ZVAL_PCACHE* mysqlnd_palloc_get_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE * const cache); -PHPAPI void mysqlnd_palloc_free_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE **cache); +PHPAPI MYSQLND_THD_ZVAL_PCACHE* _mysqlnd_palloc_init_thd_cache(MYSQLND_ZVAL_PCACHE * const cache TSRMLS_DC); +MYSQLND_THD_ZVAL_PCACHE* mysqlnd_palloc_get_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE * const cache); +PHPAPI void _mysqlnd_palloc_free_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE **cache TSRMLS_DC); /* There two should not be used from outside */ -void * mysqlnd_palloc_get_zval(MYSQLND_THD_ZVAL_PCACHE * const cache, zend_bool *allocated); +void * mysqlnd_palloc_get_zval(MYSQLND_THD_ZVAL_PCACHE * const cache, zend_bool *allocated TSRMLS_DC); void mysqlnd_palloc_zval_ptr_dtor(zval **zv, MYSQLND_THD_ZVAL_PCACHE * const cache, enum_mysqlnd_res_type type, zend_bool *copy_ctor_called TSRMLS_DC); @@ -908,8 +325,15 @@ void mysqlnd_qcache_put(MYSQLND_QCACHE * const cache, char * query, size_t qu ZEND_BEGIN_MODULE_GLOBALS(mysqlnd) zend_bool collect_statistics; + zend_bool collect_memory_statistics; + char* debug; /* The actual string */ + MYSQLND_DEBUG *dbg; /* The DBG object */ + long net_cmd_buffer_size; + long net_read_buffer_size; ZEND_END_MODULE_GLOBALS(mysqlnd) +ZEND_EXTERN_MODULE_GLOBALS(mysqlnd); + #ifdef ZTS #define MYSQLND_G(v) TSRMG(mysqlnd_globals_id, zend_mysqlnd_globals *, v) #else diff --git a/ext/mysqlnd/mysqlnd_charset.c b/ext/mysqlnd/mysqlnd_charset.c index 9208e7f256..cd6112af9f 100644 --- a/ext/mysqlnd/mysqlnd_charset.c +++ b/ext/mysqlnd/mysqlnd_charset.c @@ -21,10 +21,11 @@ #include "php_globals.h" #include "mysqlnd.h" #include "mysqlnd_priv.h" +#include "mysqlnd_debug.h" /* {{{ utf8 functions */ -static uint check_mb_utf8(const char *start, const char *end) +static uint check_mb_utf8_sequence(const char *start, const char *end) { zend_uchar c; @@ -62,6 +63,11 @@ static uint check_mb_utf8(const char *start, const char *end) return 0; } +static uint check_mb_utf8_valid(const char *start, const char *end) +{ + uint len = check_mb_utf8_sequence(start, end); + return (len > 1)? len:0; +} static uint mysqlnd_mbcharlen_utf8(uint utf8) { @@ -93,7 +99,7 @@ static uint mysqlnd_mbcharlen_utf8(uint utf8) static uint check_mb_big5(const char *start, const char *end) { - return ((end - start) > 1 && valid_big5head(*(start)) && valid_big5tail(*(start + 1)) ? 2 : 0); + return (valid_big5head(*(start)) && (end - start) > 1 && valid_big5tail(*(start + 1)) ? 2 : 0); } @@ -111,14 +117,14 @@ static uint mysqlnd_mbcharlen_big5(uint big5) static uint check_mb_cp932(const char *start, const char *end) { - return (((end > start) + 1) && valid_cp932head((uint)start[0]) && - valid_cp932tail((uint)start[1])) ? 2 : 0; + return (valid_cp932head((zend_uchar)start[0]) && (end - start > 1) && + valid_cp932tail((zend_uchar)start[1])) ? 2 : 0; } static uint mysqlnd_mbcharlen_cp932(uint cp932) { - return (valid_cp932head(cp932)) ? 2 : 1; + return (valid_cp932head((zend_uchar)cp932)) ? 2 : 1; } /* }}} */ @@ -156,7 +162,7 @@ static uint mysqlnd_mbcharlen_euckr(uint kr) static uint check_mb_eucjpms(const char *start, const char *end) { - if (*((uint *)start) < 0x80) { + if (*((zend_uchar *)start) < 0x80) { return 0; /* invalid eucjpms character */ } if (valid_eucjpms(start[0]) && (end - start) > 1 && valid_eucjpms(start[1])) { @@ -187,13 +193,13 @@ static uint mysqlnd_mbcharlen_eucjpms(uint jpms) /* {{{ gb2312 functions */ -#define valid_gb2312_head(c) (0xA1 <= (c) && (c) <= 0xF7) -#define valid_gb2312_tail(c) (0xA1 <= (c) && (c) <= 0xFE) +#define valid_gb2312_head(c) (0xA1 <= (zend_uchar)(c) && (zend_uchar)(c) <= 0xF7) +#define valid_gb2312_tail(c) (0xA1 <= (zend_uchar)(c) && (zend_uchar)(c) <= 0xFE) static uint check_mb_gb2312(const char *start, const char *end) { - return (end - start > 1 || valid_gb2312_head((uint)start[0]) || + return (valid_gb2312_head((uint)start[0]) && end - start > 1 && valid_gb2312_tail((uint)start[1])) ? 2 : 0; } @@ -206,15 +212,12 @@ static uint mysqlnd_mbcharlen_gb2312(uint gb) /* {{{ gbk functions */ -#define valid_gbk_head(c) ((uint)(c) >> 8) -#define valid_gbk_tail(c) ((uint)(c) & 0xFF) +#define valid_gbk_head(c) (0x81<=(zend_uchar)(c) && (zend_uchar)(c)<=0xFE) +#define valid_gbk_tail(c) ((0x40<=(zend_uchar)(c) && (zend_uchar)(c)<=0x7E) || (0x80<=(zend_uchar)(c) && (zend_uchar)(c)<=0xFE)) static uint check_mb_gbk(const char *start, const char *end) { - if (end - start <= 1) { - return 0; /* invalid length */ - } - return (valid_gbk_head(start[0]) && valid_gbk_tail(start[1])) ? 2 : 0; + return (valid_gbk_head(start[0]) && (end) - (start) > 1 && valid_gbk_tail(start[1])) ? 2 : 0; } static uint mysqlnd_mbcharlen_gbk(uint gbk) @@ -223,6 +226,7 @@ static uint mysqlnd_mbcharlen_gbk(uint gbk) } /* }}} */ + /* {{{ sjis functions */ #define valid_sjis_head(c) ((0x81 <= (c) && (c) <= 0x9F) && \ (0xE0 <= (c) && (c) <= 0xFC)) @@ -232,16 +236,13 @@ static uint mysqlnd_mbcharlen_gbk(uint gbk) static uint check_mb_sjis(const char *start, const char *end) { - if (end - start <= 1) { - return 0; - } - return (valid_sjis_head((uint)start[0]) && valid_sjis_tail((uint)start[1])) ? 2 : 0; + return (valid_sjis_head((zend_uchar)start[0]) && (end - start) > 1 && valid_sjis_tail((zend_uchar)start[1])) ? 2 : 0; } static uint mysqlnd_mbcharlen_sjis(uint sjis) { - return (valid_sjis_head((uint)sjis)) ? 2 : 1; + return (valid_sjis_head((zend_uchar)sjis)) ? 2 : 1; } /* }}} */ @@ -267,7 +268,7 @@ static uint mysqlnd_mbcharlen_ucs2(uint ucs2 __attribute((unused))) static uint check_mb_ujis(const char *start, const char *end) { - if ((uint)start[0] < 0x80) { + if (*(uchar*)start < 0x80) { return 0; /* invalid ujis character */ } if (valid_ujis(*(start)) && valid_ujis(*((start)+1))) { @@ -276,8 +277,7 @@ static uint check_mb_ujis(const char *start, const char *end) if (valid_ujis_ss2(*(start)) && valid_ujis_kata(*((start)+1))) { return 2; } - if (valid_ujis_ss3(*(start)) && (end-start) > 2 && valid_ujis(*((start)+1)) - && valid_ujis(*((start)+2))) { + if (valid_ujis_ss3(*(start)) && (end-start) > 2 && valid_ujis(*((start)+1)) && valid_ujis(*((start)+2))) { return 3; } return 0; @@ -286,14 +286,7 @@ static uint check_mb_ujis(const char *start, const char *end) static uint mysqlnd_mbcharlen_ujis(uint ujis) { - if (((ujis & 0xFF) >= 0xA1 && (ujis & 0xFF) <= 0xFE) || - ((ujis & 0xFF) == 0x8E)) { - return 2; - } - if ((ujis & 0xFF) == 0x8F) { - return 3; - } - return 1; + return (valid_ujis(ujis)? 2: valid_ujis_ss2(ujis)? 2: valid_ujis_ss3(ujis)? 3: 1); } /* }}} */ @@ -315,7 +308,7 @@ const MYSQLND_CHARSET mysqlnd_charsets[] = { 13, "sjis", "sjis_japanese_ci", 1, 2, 0, mysqlnd_mbcharlen_sjis, check_mb_sjis}, { 16, "hebrew", "hebrew_general_ci", 1, 1, 0, NULL, NULL}, { 18, "tis620", "tis620_thai_ci", 1, 1, 0, NULL, NULL}, - { 19, "euckr", "euckr_korean_ci", 1, 2, 0, mysqlnd_mbcharlen_euckr, check_mb_eucjpms}, + { 19, "euckr", "euckr_korean_ci", 1, 2, 0, mysqlnd_mbcharlen_euckr, check_mb_euckr}, { 22, "koi8u", "koi8u_general_ci", 1, 1, 0, NULL, NULL}, { 24, "gb2312", "gb2312_chinese_ci", 1, 2, 0, mysqlnd_mbcharlen_gb2312, check_mb_gb2312}, { 25, "greek", "greek_general_ci", 1, 1, 0, NULL, NULL}, @@ -323,7 +316,7 @@ const MYSQLND_CHARSET mysqlnd_charsets[] = { 28, "gbk", "gbk_chinese_ci", 1, 2, 0, mysqlnd_mbcharlen_gbk, check_mb_gbk}, { 30, "latin5", "latin5_turkish_ci", 1, 1, 0, NULL, NULL}, { 32, "armscii8", "armscii8_general_ci", 1, 1, 0, NULL, NULL}, - { 33, "utf8", "utf8_general_ci", 1, 2, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, + { 33, "utf8", "utf8_general_ci", 1, 2, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, { 35, "ucs2", "ucs2_general_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 36, "cp866", "cp866_general_ci", 1, 1, 0, NULL, NULL}, { 37, "keybcs2", "keybcs2_general_ci", 1, 1, 0, NULL, NULL}, @@ -337,7 +330,7 @@ const MYSQLND_CHARSET mysqlnd_charsets[] = { 63, "binary", "binary", 1, 1, 0, NULL, NULL}, { 92, "geostd8", "geostd8_general_ci", 1, 1, 0, NULL, NULL}, { 95, "cp932", "cp932_japanese_ci", 1, 2, 1, mysqlnd_mbcharlen_cp932, check_mb_cp932}, - { 97, "eucjpms", "eucjpms_japanese_ci", 1, 3, 0, mysqlnd_mbcharlen_eucjpms, }, + { 97, "eucjpms", "eucjpms_japanese_ci", 1, 3, 0, mysqlnd_mbcharlen_eucjpms, check_mb_eucjpms}, { 2, "latin2", "latin2_czech_cs", 1, 1, 0, NULL, NULL}, { 5, "latin1", "latin1_german_ci", 1, 1, 0, NULL, NULL}, { 14, "cp1251", "cp1251_bulgarian_ci", 1, 1, 0, NULL, NULL}, @@ -379,7 +372,7 @@ const MYSQLND_CHARSET mysqlnd_charsets[] = { 81, "cp852", "cp852_bin", 1, 1, 0, NULL, NULL}, { 82, "swe7", "swe7_bin", 1, 1, 0, NULL, NULL}, { 93, "geostd8", "geostd8_bin", 1, 1, 0, NULL, NULL}, - { 83, "utf8", "utf8_bin", 1, 2, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, + { 83, "utf8", "utf8_bin", 1, 2, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, { 84, "big5", "big5_bin", 1, 2, 0, mysqlnd_mbcharlen_big5, check_mb_big5}, { 85, "euckr", "euckr_bin", 1, 2, 0, mysqlnd_mbcharlen_euckr, check_mb_euckr}, { 86, "gb2312", "gb2312_bin", 1, 2, 0, mysqlnd_mbcharlen_gb2312, check_mb_gb2312}, @@ -411,26 +404,26 @@ const MYSQLND_CHARSET mysqlnd_charsets[] = { 144, "ucs2", "ucs2_persian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 145, "ucs2", "ucs2_esperanto_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 146, "ucs2", "ucs2_hungarian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, - { 192, "utf8", "utf8_general_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, - { 193, "utf8", "utf8_icelandic_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, - { 194, "utf8", "utf8_latvian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, - { 195, "utf8", "utf8_romanian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, - { 196, "utf8", "utf8_slovenian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, - { 197, "utf8", "utf8_polish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, - { 198, "utf8", "utf8_estonian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, - { 119, "utf8", "utf8_spanish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, - { 200, "utf8", "utf8_swedish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, - { 201, "utf8", "utf8_turkish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, - { 202, "utf8", "utf8_czech_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, - { 203, "utf8", "utf8_danish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8 }, - { 204, "utf8", "utf8_lithunian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8 }, - { 205, "utf8", "utf8_slovak_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, - { 206, "utf8", "utf8_spanish2_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, - { 207, "utf8", "utf8_roman_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, - { 208, "utf8", "utf8_persian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, - { 209, "utf8", "utf8_esperanto_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, - { 210, "utf8", "utf8_hungarian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8}, - { 254, "utf8", "utf8_general_cs", 1, 2, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8 }, + { 192, "utf8", "utf8_general_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, + { 193, "utf8", "utf8_icelandic_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, + { 194, "utf8", "utf8_latvian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, + { 195, "utf8", "utf8_romanian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, + { 196, "utf8", "utf8_slovenian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, + { 197, "utf8", "utf8_polish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, + { 198, "utf8", "utf8_estonian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, + { 119, "utf8", "utf8_spanish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, + { 200, "utf8", "utf8_swedish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, + { 201, "utf8", "utf8_turkish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, + { 202, "utf8", "utf8_czech_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, + { 203, "utf8", "utf8_danish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid }, + { 204, "utf8", "utf8_lithunian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid }, + { 205, "utf8", "utf8_slovak_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, + { 206, "utf8", "utf8_spanish2_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, + { 207, "utf8", "utf8_roman_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, + { 208, "utf8", "utf8_persian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, + { 209, "utf8", "utf8_esperanto_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, + { 210, "utf8", "utf8_hungarian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid}, + { 254, "utf8", "utf8_general_cs", 1, 2, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid }, { 0, NULL, NULL, 0, 0, 0, NULL, NULL} }; /* }}} */ @@ -469,19 +462,21 @@ PHPAPI const MYSQLND_CHARSET * mysqlnd_find_charset_name(const char * const name /* {{{ mysqlnd_cset_escape_quotes */ -PHPAPI ulong mysqlnd_cset_escape_quotes(const MYSQLND_CHARSET * const cset, char *newstr, const char *escapestr, int escapestr_len) +PHPAPI ulong mysqlnd_cset_escape_quotes(const MYSQLND_CHARSET * const cset, char *newstr, + const char *escapestr, int escapestr_len TSRMLS_DC) { const char *newstr_s = newstr; const char *newstr_e = newstr + 2 * escapestr_len; const char *end = escapestr + escapestr_len; zend_bool escape_overflow = FALSE; + DBG_ENTER("mysqlnd_cset_escape_quotes"); + for (;escapestr < end; escapestr++) { uint len = 0; /* check unicode characters */ - if (cset->char_maxlen > 1 && cset->mb_charlen(*escapestr) > 1 && - (len = cset->mb_valid(escapestr, end))) { + if (cset->char_maxlen > 1 && (len = cset->mb_valid(escapestr, end))) { /* check possible overflow */ if ((newstr + len) > newstr_e) { @@ -495,7 +490,7 @@ PHPAPI ulong mysqlnd_cset_escape_quotes(const MYSQLND_CHARSET * const cset, char escapestr--; continue; } - if (*newstr == '\'') { + if (*escapestr == '\'') { if (newstr + 2 > newstr_e) { escape_overflow = TRUE; break; @@ -507,35 +502,36 @@ PHPAPI ulong mysqlnd_cset_escape_quotes(const MYSQLND_CHARSET * const cset, char escape_overflow = TRUE; break; } - *newstr++= *escapestr; + *newstr++ = *escapestr; } } *newstr = '\0'; if (escape_overflow) { - return (ulong)~0; + DBG_RETURN((ulong)~0); } - return (ulong)(newstr - newstr_s); + DBG_RETURN((ulong)(newstr - newstr_s)); } /* }}} */ /* {{{ mysqlnd_cset_escape_slashes */ -PHPAPI ulong mysqlnd_cset_escape_slashes(const MYSQLND_CHARSET * const cset, char *newstr, const char *escapestr, int escapestr_len) +PHPAPI ulong mysqlnd_cset_escape_slashes(const MYSQLND_CHARSET * const cset, char *newstr, + const char *escapestr, int escapestr_len TSRMLS_DC) { const char *newstr_s = newstr; const char *newstr_e = newstr + 2 * escapestr_len; const char *end = escapestr + escapestr_len; zend_bool escape_overflow = FALSE; + + DBG_ENTER("mysqlnd_cset_escape_slashes"); for (;escapestr < end; escapestr++) { char esc = '\0'; uint len = 0; /* check unicode characters */ - if (cset->char_maxlen > 1 && cset->mb_charlen(*escapestr) > 1 && - (len = cset->mb_valid(escapestr, end))) { - + if (cset->char_maxlen > 1 && (len = cset->mb_valid(escapestr, end))) { /* check possible overflow */ if ((newstr + len) > newstr_e) { escape_overflow = TRUE; @@ -548,8 +544,7 @@ PHPAPI ulong mysqlnd_cset_escape_slashes(const MYSQLND_CHARSET * const cset, cha escapestr--; continue; } - if (cset->char_maxlen > 1 && cset->mb_charlen(*escapestr) > 1 && - (len = cset->mb_valid(escapestr, end))) { + if (cset->char_maxlen > 1 && cset->mb_charlen(*escapestr) > 1) { esc = *escapestr; } else { switch (*escapestr) { @@ -592,9 +587,9 @@ PHPAPI ulong mysqlnd_cset_escape_slashes(const MYSQLND_CHARSET * const cset, cha *newstr = '\0'; if (escape_overflow) { - return (ulong)~0; + DBG_RETURN((ulong)~0); } - return (ulong)(newstr - newstr_s); + DBG_RETURN((ulong)(newstr - newstr_s)); } /* }}} */ diff --git a/ext/mysqlnd/mysqlnd_charset.h b/ext/mysqlnd/mysqlnd_charset.h index 85b0cd51a8..d324d1ff0b 100644 --- a/ext/mysqlnd/mysqlnd_charset.h +++ b/ext/mysqlnd/mysqlnd_charset.h @@ -19,10 +19,10 @@ */ PHPAPI ulong mysqlnd_cset_escape_quotes(const MYSQLND_CHARSET * const charset, char *newstr, - const char *escapestr, int escapestr_len); + const char *escapestr, int escapestr_len TSRMLS_DC); PHPAPI ulong mysqlnd_cset_escape_slashes(const MYSQLND_CHARSET * const cset, char *newstr, - const char *escapestr, int escapestr_len); + const char *escapestr, int escapestr_len TSRMLS_DC); /* diff --git a/ext/mysqlnd/mysqlnd_debug.c b/ext/mysqlnd/mysqlnd_debug.c new file mode 100644 index 0000000000..fedfccf651 --- /dev/null +++ b/ext/mysqlnd/mysqlnd_debug.c @@ -0,0 +1,1338 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 6 | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2007 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Georg Richter <georg@mysql.com> | + | Andrey Hristov <andrey@mysql.com> | + | Ulf Wendel <uwendel@mysql.com> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "php.h" +#include "mysqlnd.h" +#include "mysqlnd_priv.h" +#include "mysqlnd_debug.h" +#include "mysqlnd_wireprotocol.h" +#include "mysqlnd_palloc.h" +#include "mysqlnd_statistics.h" +#include "zend_builtin_functions.h" + + +static const char * const mysqlnd_debug_default_trace_file = "/tmp/mysqlnd.trace"; + +#ifdef ZTS +#define MYSQLND_ZTS(self) TSRMLS_D = (self)->TSRMLS_C +#else +#define MYSQLND_ZTS(self) +#endif + +#define MYSQLND_DEBUG_DUMP_TIME 1 +#define MYSQLND_DEBUG_DUMP_TRACE 2 +#define MYSQLND_DEBUG_DUMP_PID 4 +#define MYSQLND_DEBUG_DUMP_LINE 8 +#define MYSQLND_DEBUG_DUMP_FILE 16 +#define MYSQLND_DEBUG_DUMP_LEVEL 32 +#define MYSQLND_DEBUG_APPEND 64 +#define MYSQLND_DEBUG_FLUSH 128 +#define MYSQLND_DEBUG_TRACE_MEMORY_CALLS 256 + +static char * mysqlnd_emalloc_name = "_mysqlnd_emalloc"; +static char * mysqlnd_pemalloc_name = "_mysqlnd_pemalloc"; +static char * mysqlnd_ecalloc_name = "_mysqlnd_ecalloc"; +static char * mysqlnd_pecalloc_name = "_mysqlnd_pecalloc"; +static char * mysqlnd_erealloc_name = "_mysqlnd_erealloc"; +static char * mysqlnd_perealloc_name= "_mysqlnd_perealloc"; +static char * mysqlnd_efree_name = "_mysqlnd_efree"; +static char * mysqlnd_pefree_name = "_mysqlnd_pefree"; +static char * mysqlnd_malloc_name = "_mysqlnd_malloc"; +static char * mysqlnd_calloc_name = "_mysqlnd_calloc"; +static char * mysqlnd_realloc_name = "_mysqlnd_realloc"; +static char * mysqlnd_free_name = "_mysqlnd_free"; + +/* {{{ mysqlnd_debug::open */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_debug, open)(MYSQLND_DEBUG * self, zend_bool reopen) +{ + MYSQLND_ZTS(self); + + if (!self->file_name) { + return FAIL; + } + + self->stream = php_stream_open_wrapper(self->file_name, + reopen == TRUE || self->flags & MYSQLND_DEBUG_APPEND? "ab":"wb", + REPORT_ERRORS, NULL); + return self->stream? PASS:FAIL; +} +/* }}} */ + + +/* {{{ mysqlnd_debug::log */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_debug, log)(MYSQLND_DEBUG * self, + unsigned int line, const char * const file, + unsigned int level, const char * type, const char * message) +{ + char pipe_buffer[512]; + MYSQLND_ZTS(self); + enum_func_status ret; + int i; + char * message_line; + size_t message_line_len; + unsigned int flags = self->flags; + char pid_buffer[10], time_buffer[30], file_buffer[200], + line_buffer[6], level_buffer[7]; + + if (!self->stream) { + if (FAIL == self->m->open(self, FALSE)) { + return FAIL; + } + } + + if (level == -1) { + level = zend_stack_count(&self->call_stack); + } + i = MIN(level, sizeof(pipe_buffer) / 2 - 1); + pipe_buffer[i*2] = '\0'; + for (;i > 0;i--) { + pipe_buffer[i*2 - 1] = ' '; + pipe_buffer[i*2 - 2] = '|'; + } + + + if (flags & MYSQLND_DEBUG_DUMP_PID) { + snprintf(pid_buffer, sizeof(pid_buffer) - 1, "%5u: ", self->pid); + pid_buffer[sizeof(pid_buffer) - 1 ] = '\0'; + } + if (flags & MYSQLND_DEBUG_DUMP_TIME) { + /* The following from FF's DBUG library, which is in the public domain */ +#if defined(PHP_WIN32) + /* FIXME This doesn't give microseconds as in Unix case, and the resolution is + in system ticks, 10 ms intervals. See my_getsystime.c for high res */ + SYSTEMTIME loc_t; + GetLocalTime(&loc_t); + snprintf(time_buffer, sizeof(time_buffer) - 1, + /* "%04d-%02d-%02d " */ + "%02d:%02d:%02d.%06d ", + /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/ + loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds); + time_buffer[sizeof(time_buffer) - 1 ] = '\0'; +#else + struct timeval tv; + struct tm *tm_p; + if (gettimeofday(&tv, NULL) != -1) { + if ((tm_p= localtime((const time_t *)&tv.tv_sec))) { + snprintf(time_buffer, sizeof(time_buffer) - 1, + /* "%04d-%02d-%02d " */ + "%02d:%02d:%02d.%06d ", + /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/ + tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec, + (int) (tv.tv_usec)); + time_buffer[sizeof(time_buffer) - 1 ] = '\0'; + } + } +#endif + } + if (flags & MYSQLND_DEBUG_DUMP_FILE) { + snprintf(file_buffer, sizeof(file_buffer) - 1, "%14s: ", file); + file_buffer[sizeof(file_buffer) - 1 ] = '\0'; + } + if (flags & MYSQLND_DEBUG_DUMP_LINE) { + snprintf(line_buffer, sizeof(line_buffer) - 1, "%5u: ", line); + line_buffer[sizeof(line_buffer) - 1 ] = '\0'; + } + if (flags & MYSQLND_DEBUG_DUMP_LEVEL) { + snprintf(level_buffer, sizeof(level_buffer) - 1, "%4u: ", level); + level_buffer[sizeof(level_buffer) - 1 ] = '\0'; + } + + message_line_len = spprintf(&message_line, 0, "%s%s%s%s%s%s%s%s\n", + flags & MYSQLND_DEBUG_DUMP_PID? pid_buffer:"", + flags & MYSQLND_DEBUG_DUMP_TIME? time_buffer:"", + flags & MYSQLND_DEBUG_DUMP_FILE? file_buffer:"", + flags & MYSQLND_DEBUG_DUMP_LINE? line_buffer:"", + flags & MYSQLND_DEBUG_DUMP_LEVEL? level_buffer:"", + pipe_buffer, type? type:"", message); + + ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL; + efree(message_line); + if (flags & MYSQLND_DEBUG_FLUSH) { + self->m->close(self); + self->m->open(self, TRUE); + } + return ret; +} +/* }}} */ + + +/* {{{ mysqlnd_debug::log_va */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_debug, log_va)(MYSQLND_DEBUG *self, + unsigned int line, const char * const file, + unsigned int level, const char * type, + const char *format, ...) +{ + char pipe_buffer[512]; + MYSQLND_ZTS(self); + int i; + enum_func_status ret; + char * message_line, *buffer; + size_t message_line_len; + va_list args; + unsigned int flags = self->flags; + char pid_buffer[10], time_buffer[30], file_buffer[200], + line_buffer[6], level_buffer[7]; + + if (!self->stream) { + if (FAIL == self->m->open(self, FALSE)) { + return FAIL; + } + } + + if (level == -1) { + level = zend_stack_count(&self->call_stack); + } + i = MIN(level, sizeof(pipe_buffer) / 2 - 1); + pipe_buffer[i*2] = '\0'; + for (;i > 0;i--) { + pipe_buffer[i*2 - 1] = ' '; + pipe_buffer[i*2 - 2] = '|'; + } + + + if (flags & MYSQLND_DEBUG_DUMP_PID) { + snprintf(pid_buffer, sizeof(pid_buffer) - 1, "%5u: ", self->pid); + pid_buffer[sizeof(pid_buffer) - 1 ] = '\0'; + } + if (flags & MYSQLND_DEBUG_DUMP_TIME) { + /* The following from FF's DBUG library, which is in the public domain */ +#if defined(PHP_WIN32) + /* FIXME This doesn't give microseconds as in Unix case, and the resolution is + in system ticks, 10 ms intervals. See my_getsystime.c for high res */ + SYSTEMTIME loc_t; + GetLocalTime(&loc_t); + snprintf(time_buffer, sizeof(time_buffer) - 1, + /* "%04d-%02d-%02d " */ + "%02d:%02d:%02d.%06d ", + /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/ + loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds); + time_buffer[sizeof(time_buffer) - 1 ] = '\0'; +#else + struct timeval tv; + struct tm *tm_p; + if (gettimeofday(&tv, NULL) != -1) { + if ((tm_p= localtime((const time_t *)&tv.tv_sec))) { + snprintf(time_buffer, sizeof(time_buffer) - 1, + /* "%04d-%02d-%02d " */ + "%02d:%02d:%02d.%06d ", + /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/ + tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec, + (int) (tv.tv_usec)); + time_buffer[sizeof(time_buffer) - 1 ] = '\0'; + } + } +#endif + } + if (flags & MYSQLND_DEBUG_DUMP_FILE) { + snprintf(file_buffer, sizeof(file_buffer) - 1, "%14s: ", file); + file_buffer[sizeof(file_buffer) - 1 ] = '\0'; + } + if (flags & MYSQLND_DEBUG_DUMP_LINE) { + snprintf(line_buffer, sizeof(line_buffer) - 1, "%5u: ", line); + line_buffer[sizeof(line_buffer) - 1 ] = '\0'; + } + if (flags & MYSQLND_DEBUG_DUMP_LEVEL) { + snprintf(level_buffer, sizeof(level_buffer) - 1, "%4u: ", level); + level_buffer[sizeof(level_buffer) - 1 ] = '\0'; + } + + + va_start(args, format); + vspprintf(&buffer, 0, format, args); + va_end(args); + + message_line_len = spprintf(&message_line, 0, "%s%s%s%s%s%s%s%s\n", + flags & MYSQLND_DEBUG_DUMP_PID? pid_buffer:"", + flags & MYSQLND_DEBUG_DUMP_TIME? time_buffer:"", + flags & MYSQLND_DEBUG_DUMP_FILE? file_buffer:"", + flags & MYSQLND_DEBUG_DUMP_LINE? line_buffer:"", + flags & MYSQLND_DEBUG_DUMP_LEVEL? level_buffer:"", + pipe_buffer, type? type:"", buffer); + efree(buffer); + + ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL; + efree(message_line); + + if (flags & MYSQLND_DEBUG_FLUSH) { + self->m->close(self); + self->m->open(self, TRUE); + } + return ret; +} +/* }}} */ + + +/* FALSE - The DBG_ calls won't be traced, TRUE - will be traced */ +/* {{{ mysqlnd_res_meta::func_enter */ +static zend_bool +MYSQLND_METHOD(mysqlnd_debug, func_enter)(MYSQLND_DEBUG * self, + unsigned int line, const char * const file, + char * func_name, size_t func_name_len) +{ + if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) { + return FALSE; + } + if (zend_stack_count(&self->call_stack) >= self->nest_level_limit) { + return FALSE; + } + + if ((self->flags & MYSQLND_DEBUG_TRACE_MEMORY_CALLS) == 0 && + (func_name == mysqlnd_emalloc_name || func_name == mysqlnd_pemalloc_name || + func_name == mysqlnd_ecalloc_name || func_name == mysqlnd_pecalloc_name || + func_name == mysqlnd_erealloc_name || func_name == mysqlnd_perealloc_name || + func_name == mysqlnd_efree_name || func_name == mysqlnd_pefree_name || + func_name == mysqlnd_efree_name || func_name == mysqlnd_pefree_name || + func_name == mysqlnd_malloc_name || func_name == mysqlnd_calloc_name || + func_name == mysqlnd_realloc_name || func_name == mysqlnd_free_name || + func_name == mysqlnd_palloc_zval_ptr_dtor_name || func_name == mysqlnd_palloc_get_zval_name || + func_name == mysqlnd_read_header_name || func_name == mysqlnd_read_body_name)) { + zend_stack_push(&self->call_stack, "", sizeof("")); + return FALSE; + } + + zend_stack_push(&self->call_stack, func_name, func_name_len + 1); + + if (zend_hash_num_elements(&self->not_filtered_functions) && + 0 == zend_hash_exists(&self->not_filtered_functions, func_name, strlen(func_name) + 1)) + { + return FALSE; + } + + self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, ">%s", func_name); + return TRUE; +} +/* }}} */ + + +/* {{{ mysqlnd_res_meta::func_leave */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_debug, func_leave)(MYSQLND_DEBUG * self, unsigned int line, + const char * const file) +{ + char *func_name; + + if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) { + return PASS; + } + if (zend_stack_count(&self->call_stack) >= self->nest_level_limit) { + return PASS; + } + + zend_stack_top(&self->call_stack, (void **)&func_name); + + if (func_name[0] == '\0') { + ; /* don't log that function */ + } else if (!zend_hash_num_elements(&self->not_filtered_functions) || + 1 == zend_hash_exists(&self->not_filtered_functions, func_name, strlen(func_name) + 1)) + { + self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, "<%s", func_name); + } + + return zend_stack_del_top(&self->call_stack) == SUCCESS? PASS:FAIL; +} +/* }}} */ + + +/* {{{ mysqlnd_res_meta::close */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_debug, close)(MYSQLND_DEBUG * self) +{ + MYSQLND_ZTS(self); + if (self->stream) { + php_stream_free(self->stream, PHP_STREAM_FREE_CLOSE); + self->stream = NULL; + } + return PASS; +} +/* }}} */ + + +/* {{{ mysqlnd_res_meta::free */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_debug, free)(MYSQLND_DEBUG * self) +{ + if (self->file_name && self->file_name != mysqlnd_debug_default_trace_file) { + efree(self->file_name); + self->file_name = NULL; + } + zend_stack_destroy(&self->call_stack); + zend_hash_destroy(&self->not_filtered_functions); + efree(self); + return PASS; +} +/* }}} */ + +enum mysqlnd_debug_parser_state +{ + PARSER_WAIT_MODIFIER, + PARSER_WAIT_COLON, + PARSER_WAIT_VALUE +}; + + +/* {{{ mysqlnd_res_meta::set_mode */ +static void +MYSQLND_METHOD(mysqlnd_debug, set_mode)(MYSQLND_DEBUG * self, const char * const mode) +{ + size_t mode_len = strlen(mode), i; + enum mysqlnd_debug_parser_state state = PARSER_WAIT_MODIFIER; + + self->flags = 0; + self->nest_level_limit = 0; + if (self->file_name && self->file_name != mysqlnd_debug_default_trace_file) { + efree(self->file_name); + self->file_name = NULL; + } + if (zend_hash_num_elements(&self->not_filtered_functions)) { + zend_hash_destroy(&self->not_filtered_functions); + zend_hash_init(&self->not_filtered_functions, 0, NULL, NULL, 0); + } + + for (i = 0; i < mode_len; i++) { + switch (mode[i]) { + case 'O': + case 'A': + self->flags |= MYSQLND_DEBUG_FLUSH; + case 'a': + case 'o': + if (mode[i] == 'a' || mode[i] == 'A') { + self->flags |= MYSQLND_DEBUG_APPEND; + } + if (i + 1 < mode_len && mode[i+1] == ',') { + unsigned int j = i + 2; + while (j < mode_len) { + if (mode[j] == ':') { + break; + } + j++; + } + if (j > i + 2) { + self->file_name = estrndup(mode + i + 2, j - i - 2); + } + i = j; + } else { + self->file_name = (char *) mysqlnd_debug_default_trace_file; + } + state = PARSER_WAIT_COLON; + break; + case ':': +#if 0 + if (state != PARSER_WAIT_COLON) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Consecutive semicolons at position %u", i); + } +#endif + state = PARSER_WAIT_MODIFIER; + break; + case 'f': /* limit output to these functions */ + if (i + 1 < mode_len && mode[i+1] == ',') { + unsigned int j = i + 2; + i++; + while (j < mode_len) { + if (mode[j] == ':') { + /* function names with :: */ + if ((j + 1 < mode_len) && mode[j+1] == ':') { + j += 2; + continue; + } + } + if (mode[j] == ',' || mode[j] == ':') { + if (j > i + 2) { + char func_name[1024]; + size_t func_name_len = MIN(sizeof(func_name) - 1, j - i - 1); + memcpy(func_name, mode + i + 1, func_name_len); + func_name[func_name_len] = '\0'; + + zend_hash_add_empty_element(&self->not_filtered_functions, + func_name, func_name_len + 1); + i = j; + } + if (mode[j] == ':') { + break; + } + } + j++; + } + i = j; + } else { +#if 0 + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Expected list of functions for '%c' found none", mode[i]); +#endif + } + state = PARSER_WAIT_COLON; + break; + case 'D': + case 'd': + case 'g': + case 'p': + /* unsupported */ + if ((i + 1) < mode_len && mode[i+1] == ',') { + i+= 2; + while (i < mode_len) { + if (mode[i++] == ':') { + break; + } + } + } + state = PARSER_WAIT_COLON; + break; + case 'F': + self->flags |= MYSQLND_DEBUG_DUMP_FILE; + state = PARSER_WAIT_COLON; + break; + case 'i': + self->flags |= MYSQLND_DEBUG_DUMP_PID; + state = PARSER_WAIT_COLON; + break; + case 'L': + self->flags |= MYSQLND_DEBUG_DUMP_LINE; + state = PARSER_WAIT_COLON; + break; + case 'n': + self->flags |= MYSQLND_DEBUG_DUMP_LEVEL; + state = PARSER_WAIT_COLON; + break; + case 't': + if (mode[i+1] == ',') { + unsigned int j = i + 2; + while (j < mode_len) { + if (mode[j] == ':') { + break; + } + j++; + } + if (j > i + 2) { + char *value_str = estrndup(mode + i + 2, j - i - 2); + self->nest_level_limit = atoi(value_str); + efree(value_str); + } + i = j; + } else { + self->nest_level_limit = 200; /* default value for FF DBUG */ + } + self->flags |= MYSQLND_DEBUG_DUMP_TRACE; + state = PARSER_WAIT_COLON; + break; + case 'T': + self->flags |= MYSQLND_DEBUG_DUMP_TIME; + state = PARSER_WAIT_COLON; + break; + case 'N': + case 'P': + case 'r': + case 'S': + state = PARSER_WAIT_COLON; + break; + case 'm': /* mysqlnd extension - trace memory functions */ + self->flags |= MYSQLND_DEBUG_TRACE_MEMORY_CALLS; + state = PARSER_WAIT_COLON; + break; + default: + if (state == PARSER_WAIT_MODIFIER) { +#if 0 + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unrecognized format '%c'", mode[i]); +#endif + if (i+1 < mode_len && mode[i+1] == ',') { + i+= 2; + while (i < mode_len) { + if (mode[i] == ':') { + break; + } + i++; + } + } + state = PARSER_WAIT_COLON; + } else if (state == PARSER_WAIT_COLON) { +#if 0 + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Colon expected, '%c' found", mode[i]); +#endif + } + break; + } + } +} +/* }}} */ + + +MYSQLND_CLASS_METHODS_START(mysqlnd_debug) + MYSQLND_METHOD(mysqlnd_debug, open), + MYSQLND_METHOD(mysqlnd_debug, set_mode), + MYSQLND_METHOD(mysqlnd_debug, log), + MYSQLND_METHOD(mysqlnd_debug, log_va), + MYSQLND_METHOD(mysqlnd_debug, func_enter), + MYSQLND_METHOD(mysqlnd_debug, func_leave), + MYSQLND_METHOD(mysqlnd_debug, close), + MYSQLND_METHOD(mysqlnd_debug, free), +MYSQLND_CLASS_METHODS_END; + + + +/* {{{ mysqlnd_debug_init */ +MYSQLND_DEBUG *mysqlnd_debug_init(TSRMLS_D) +{ + MYSQLND_DEBUG *ret = ecalloc(1, sizeof(MYSQLND_DEBUG)); +#ifdef ZTS + ret->TSRMLS_C = TSRMLS_C; +#endif + ret->nest_level_limit = 0; + ret->pid = getpid(); + zend_stack_init(&ret->call_stack); + zend_hash_init(&ret->not_filtered_functions, 0, NULL, NULL, 0); + + ret->m = & mysqlnd_mysqlnd_debug_methods; + + return ret; +} +/* }}} */ + + +/* {{{ _mysqlnd_debug */ +void _mysqlnd_debug(const char *mode TSRMLS_DC) +{ +#ifdef PHP_DEBUG + MYSQLND_DEBUG *dbg = MYSQLND_G(dbg); + if (!dbg) { + MYSQLND_G(dbg) = dbg = mysqlnd_debug_init(TSRMLS_C); + if (!dbg) { + return; + } + } + + dbg->m->close(dbg); + dbg->m->set_mode(dbg, mode); + while (zend_stack_count(&dbg->call_stack)) { + zend_stack_del_top(&dbg->call_stack); + } +#endif +} +/* }}} */ + + +/* {{{ _mysqlnd_emalloc */ +void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D) +{ + void *ret; + DBG_ENTER(mysqlnd_emalloc_name); + DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); + DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC)); + ret = emalloc(size); + DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC)); + DBG_INF_FMT("size=%lu ptr=%p", size, ret); + + if (MYSQLND_G(collect_memory_statistics)) { + MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_EMALLOC_COUNT, 1, STAT_MEM_EMALLOC_AMMOUNT, size); + } + DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_pemalloc */ +void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D) +{ + void *ret; + DBG_ENTER(mysqlnd_pemalloc_name); + DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); + if (persistent == FALSE) { + DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC)); + } + + ret = pemalloc(size, persistent); + DBG_INF_FMT("size=%lu ptr=%p persistent=%d", size, ret, persistent); + + if (persistent == FALSE) { + DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC)); + } + + if (MYSQLND_G(collect_memory_statistics)) { + enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_MALLOC_COUNT:STAT_MEM_EMALLOC_COUNT; + enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_MALLOC_AMMOUNT:STAT_MEM_EMALLOC_AMMOUNT; + MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(s1, 1, s2, size); + } + + DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_ecalloc */ +void * _mysqlnd_ecalloc(uint nmemb, size_t size MYSQLND_MEM_D) +{ + void *ret; + DBG_ENTER(mysqlnd_ecalloc_name); + DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); + DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC)); + + ret = ecalloc(nmemb, size); + + DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC)); + DBG_INF_FMT("size=%lu ptr=%p", size, ret); + if (MYSQLND_G(collect_memory_statistics)) { + MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_ECALLOC_COUNT, 1, STAT_MEM_ECALLOC_AMMOUNT, size); + } + DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_pecalloc */ +void * _mysqlnd_pecalloc(uint nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D) +{ + void *ret; + DBG_ENTER(mysqlnd_pecalloc_name); + DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); + if (persistent == FALSE) { + DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC)); + } + + ret = pecalloc(nmemb, size, persistent); + DBG_INF_FMT("size=%lu ptr=%p", size, ret); + + if (persistent == FALSE) { + DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC)); + } + + if (MYSQLND_G(collect_memory_statistics)) { + enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_CALLOC_COUNT:STAT_MEM_ECALLOC_COUNT; + enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_CALLOC_AMMOUNT:STAT_MEM_ECALLOC_AMMOUNT; + MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(s1, 1, s2, size); + } + + DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_erealloc */ +void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D) +{ + void *ret; + DBG_ENTER(mysqlnd_erealloc_name); + DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); + DBG_INF_FMT("ptr=%p new_size=%lu", ptr, new_size); + DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC)); + + ret = erealloc(ptr, new_size); + + DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC)); + DBG_INF_FMT("new_ptr=%p", ret); + if (MYSQLND_G(collect_memory_statistics)) { + MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_EREALLOC_COUNT, 1, STAT_MEM_EREALLOC_AMMOUNT, new_size); + } + DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_perealloc */ +void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D) +{ + void *ret; + DBG_ENTER(mysqlnd_perealloc_name); + DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); + DBG_INF_FMT("ptr=%p new_size=%lu persist=%d", ptr, new_size, persistent); + if (persistent == FALSE) { + DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC)); + } + + ret = perealloc(ptr, new_size, persistent); + + DBG_INF_FMT("new_ptr=%p", ret); + + if (persistent == FALSE) { + DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC)); + } + MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_EREALLOC_COUNT:STAT_MEM_REALLOC_COUNT); + if (MYSQLND_G(collect_memory_statistics)) { + enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_REALLOC_COUNT:STAT_MEM_EREALLOC_COUNT; + enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_REALLOC_AMMOUNT:STAT_MEM_EREALLOC_AMMOUNT; + MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(s1, 1, s2, new_size); + } + DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_efree */ +void _mysqlnd_efree(void *ptr MYSQLND_MEM_D) +{ + DBG_ENTER(mysqlnd_efree_name); + DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); + DBG_INF_FMT("ptr=%p", ptr); + DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC)); + MYSQLND_INC_GLOBAL_STATISTIC(STAT_MEM_EFREE_COUNT); + + efree(ptr); + + DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC)); + DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ _mysqlnd_pefree */ +void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D) +{ + DBG_ENTER(mysqlnd_pefree_name); + DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); + DBG_INF_FMT("ptr=%p persistent=%d", ptr, persistent); + if (persistent == FALSE) { + DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC)); + } + + pefree(ptr, persistent); + + if (persistent == FALSE) { + DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC)); + } + if (MYSQLND_G(collect_memory_statistics)) { + MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_FREE_COUNT:STAT_MEM_EFREE_COUNT); + } + DBG_VOID_RETURN; +} + + +/* {{{ _mysqlnd_malloc */ +void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D) +{ + void *ret; + DBG_ENTER(mysqlnd_malloc_name); + DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); + + ret = malloc(size); + + DBG_INF_FMT("size=%lu ptr=%p", size, ret); + if (MYSQLND_G(collect_memory_statistics)) { + MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_MALLOC_COUNT, 1, STAT_MEM_MALLOC_AMMOUNT, size); + } + DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_calloc */ +void * _mysqlnd_calloc(uint nmemb, size_t size MYSQLND_MEM_D) +{ + void *ret; + DBG_ENTER(mysqlnd_calloc_name); + DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); + + ret = calloc(nmemb, size); + + DBG_INF_FMT("size=%lu ptr=%p", size, ret); + if (MYSQLND_G(collect_memory_statistics)) { + MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_CALLOC_COUNT, 1, STAT_MEM_CALLOC_AMMOUNT, size); + } + DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_realloc */ +void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D) +{ + void *ret; + DBG_ENTER(mysqlnd_realloc_name); + DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); + DBG_INF_FMT("ptr=%p new_size=%lu ", new_size, ptr); + DBG_INF_FMT("before: %lu", zend_memory_usage(TRUE TSRMLS_CC)); + + ret = realloc(ptr, new_size); + + DBG_INF_FMT("new_ptr=%p", ret); + + if (MYSQLND_G(collect_memory_statistics)) { + MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_REALLOC_COUNT, 1, STAT_MEM_REALLOC_AMMOUNT, new_size); + } + DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_free */ +void _mysqlnd_free(void *ptr MYSQLND_MEM_D) +{ + DBG_ENTER(mysqlnd_free_name); + DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); + DBG_INF_FMT("ptr=%p", ptr); + + free(ptr); + + if (MYSQLND_G(collect_memory_statistics)) { + MYSQLND_INC_GLOBAL_STATISTIC(STAT_MEM_FREE_COUNT); + } + DBG_VOID_RETURN; +} +/* }}} */ + + + + +/* Follows code borrowed from zend_builtin_functions.c because the functions there are static */ + +#if PHP_MAJOR_VERSION >= 6 +/* {{{ gettraceasstring() macros */ +#define TRACE_APPEND_CHR(chr) \ + *str = (char*)erealloc(*str, *len + 1 + 1); \ + (*str)[(*len)++] = chr + +#define TRACE_APPEND_STRL(val, vallen) \ + { \ + int l = vallen; \ + *str = (char*)erealloc(*str, *len + l + 1); \ + memcpy((*str) + *len, val, l); \ + *len += l; \ + } + +#define TRACE_APPEND_USTRL(val, vallen) \ + { \ + zval tmp, copy; \ + int use_copy; \ + ZVAL_UNICODEL(&tmp, val, vallen, 1); \ + zend_make_printable_zval(&tmp, ©, &use_copy); \ + TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \ + zval_dtor(©); \ + zval_dtor(&tmp); \ + } + +#define TRACE_APPEND_ZVAL(zv) \ + if (Z_TYPE_P((zv)) == IS_UNICODE) { \ + zval copy; \ + int use_copy; \ + zend_make_printable_zval((zv), ©, &use_copy); \ + TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \ + zval_dtor(©); \ + } else { \ + TRACE_APPEND_STRL(Z_STRVAL_P((zv)), Z_STRLEN_P((zv))); \ + } + +#define TRACE_APPEND_STR(val) \ + TRACE_APPEND_STRL(val, sizeof(val)-1) + +#define TRACE_APPEND_KEY(key) \ + if (zend_ascii_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \ + if (Z_TYPE_PP(tmp) == IS_UNICODE) { \ + zval copy; \ + int use_copy; \ + zend_make_printable_zval(*tmp, ©, &use_copy); \ + TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \ + zval_dtor(©); \ + } else { \ + TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); \ + } \ + } +/* }}} */ + +/* {{{ mysqlnd_build_trace_args */ +static int mysqlnd_build_trace_args(zval **arg, int num_args, va_list args, zend_hash_key *hash_key) +{ + char **str; + int *len; + + str = va_arg(args, char**); + len = va_arg(args, int*); + + /* the trivial way would be to do: + * conver_to_string_ex(arg); + * append it and kill the now tmp arg. + * but that could cause some E_NOTICE and also damn long lines. + */ + + switch (Z_TYPE_PP(arg)) { + case IS_NULL: + TRACE_APPEND_STR("NULL, "); + break; + case IS_STRING: { + int l_added; + TRACE_APPEND_CHR('\''); + if (Z_STRLEN_PP(arg) > 15) { + TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15); + TRACE_APPEND_STR("...', "); + l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */ + } else { + l_added = Z_STRLEN_PP(arg); + TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added); + TRACE_APPEND_STR("', "); + l_added += 3 + 1; + } + while (--l_added) { + if ((unsigned char)(*str)[*len - l_added] < 32) { + (*str)[*len - l_added] = '?'; + } + } + break; + } + case IS_UNICODE: { + int l_added; + TSRMLS_FETCH(); + + /* + * We do not want to apply current error mode here, since + * zend_make_printable_zval() uses output encoding converter. + * Temporarily set output encoding converter to escape offending + * chars with \uXXXX notation. + */ + zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, ZEND_CONV_ERROR_ESCAPE_JAVA); + TRACE_APPEND_CHR('\''); + if (Z_USTRLEN_PP(arg) > 15) { + TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), 15); + TRACE_APPEND_STR("...', "); + l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */ + } else { + l_added = Z_USTRLEN_PP(arg); + TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), l_added); + TRACE_APPEND_STR("', "); + l_added += 3 + 1; + } + /* + * Reset output encoding converter error mode. + */ + zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, UG(from_error_mode)); + while (--l_added) { + if ((unsigned char)(*str)[*len - l_added] < 32) { + (*str)[*len - l_added] = '?'; + } + } + break; + } + case IS_BOOL: + if (Z_LVAL_PP(arg)) { + TRACE_APPEND_STR("true, "); + } else { + TRACE_APPEND_STR("false, "); + } + break; + case IS_RESOURCE: + TRACE_APPEND_STR("Resource id #"); + /* break; */ + case IS_LONG: { + long lval = Z_LVAL_PP(arg); + char s_tmp[MAX_LENGTH_OF_LONG + 1]; + int l_tmp = zend_sprintf(s_tmp, "%ld", lval); /* SAFE */ + TRACE_APPEND_STRL(s_tmp, l_tmp); + TRACE_APPEND_STR(", "); + break; + } + case IS_DOUBLE: { + double dval = Z_DVAL_PP(arg); + char *s_tmp; + int l_tmp; + TSRMLS_FETCH(); + + s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1); + l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval); /* SAFE */ + TRACE_APPEND_STRL(s_tmp, l_tmp); + /* %G already handles removing trailing zeros from the fractional part, yay */ + efree(s_tmp); + TRACE_APPEND_STR(", "); + break; + } + case IS_ARRAY: + TRACE_APPEND_STR("Array, "); + break; + case IS_OBJECT: { + zstr class_name; + zend_uint class_name_len; + int dup; + TSRMLS_FETCH(); + + TRACE_APPEND_STR("Object("); + + dup = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC); + + if (UG(unicode)) { + zval tmp; + + ZVAL_UNICODEL(&tmp, class_name.u, class_name_len, 1); + convert_to_string_with_converter(&tmp, ZEND_U_CONVERTER(UG(output_encoding_conv))); + TRACE_APPEND_STRL(Z_STRVAL(tmp), Z_STRLEN(tmp)); + zval_dtor(&tmp); + } else { + TRACE_APPEND_STRL(class_name.s, class_name_len); + } + if(!dup) { + efree(class_name.v); + } + + TRACE_APPEND_STR("), "); + break; + } + default: + break; + } + return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ + + +static int mysqlnd_build_trace_string(zval **frame, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ +{ + char *s_tmp, **str; + int *len, *num; + long line; + HashTable *ht = Z_ARRVAL_PP(frame); + zval **file, **tmp; + + str = va_arg(args, char**); + len = va_arg(args, int*); + num = va_arg(args, int*); + + s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1); + sprintf(s_tmp, "#%d ", (*num)++); + TRACE_APPEND_STRL(s_tmp, strlen(s_tmp)); + efree(s_tmp); + if (zend_ascii_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) { + if (zend_ascii_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) { + line = Z_LVAL_PP(tmp); + } else { + line = 0; + } + TRACE_APPEND_ZVAL(*file); + s_tmp = emalloc(MAX_LENGTH_OF_LONG + 2 + 1); + sprintf(s_tmp, "(%ld): ", line); + TRACE_APPEND_STRL(s_tmp, strlen(s_tmp)); + efree(s_tmp); + } else { + TRACE_APPEND_STR("[internal function]: "); + } + TRACE_APPEND_KEY("class"); + TRACE_APPEND_KEY("type"); + TRACE_APPEND_KEY("function"); + TRACE_APPEND_CHR('('); + if (zend_ascii_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) { + int last_len = *len; + zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp), (apply_func_args_t)mysqlnd_build_trace_args, 2, str, len); + if (last_len != *len) { + *len -= 2; /* remove last ', ' */ + } + } + TRACE_APPEND_STR(")\n"); + return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ + + +#else /* PHP 5*/ + + +/* {{{ gettraceasstring() macros */ +#define TRACE_APPEND_CHR(chr) \ + *str = (char*)erealloc(*str, *len + 1 + 1); \ + (*str)[(*len)++] = chr + +#define TRACE_APPEND_STRL(val, vallen) \ + { \ + int l = vallen; \ + *str = (char*)erealloc(*str, *len + l + 1); \ + memcpy((*str) + *len, val, l); \ + *len += l; \ + } + +#define TRACE_APPEND_STR(val) \ + TRACE_APPEND_STRL(val, sizeof(val)-1) + +#define TRACE_APPEND_KEY(key) \ + if (zend_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \ + TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); \ + } + +/* }}} */ + + +static int mysqlnd_build_trace_args(zval **arg, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ +{ + char **str; + int *len; + + str = va_arg(args, char**); + len = va_arg(args, int*); + + /* the trivial way would be to do: + * conver_to_string_ex(arg); + * append it and kill the now tmp arg. + * but that could cause some E_NOTICE and also damn long lines. + */ + + switch (Z_TYPE_PP(arg)) { + case IS_NULL: + TRACE_APPEND_STR("NULL, "); + break; + case IS_STRING: { + int l_added; + TRACE_APPEND_CHR('\''); + if (Z_STRLEN_PP(arg) > 15) { + TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15); + TRACE_APPEND_STR("...', "); + l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */ + } else { + l_added = Z_STRLEN_PP(arg); + TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added); + TRACE_APPEND_STR("', "); + l_added += 3 + 1; + } + while (--l_added) { + if ((*str)[*len - l_added] < 32) { + (*str)[*len - l_added] = '?'; + } + } + break; + } + case IS_BOOL: + if (Z_LVAL_PP(arg)) { + TRACE_APPEND_STR("true, "); + } else { + TRACE_APPEND_STR("false, "); + } + break; + case IS_RESOURCE: + TRACE_APPEND_STR("Resource id #"); + /* break; */ + case IS_LONG: { + long lval = Z_LVAL_PP(arg); + char s_tmp[MAX_LENGTH_OF_LONG + 1]; + int l_tmp = zend_sprintf(s_tmp, "%ld", lval); /* SAFE */ + TRACE_APPEND_STRL(s_tmp, l_tmp); + TRACE_APPEND_STR(", "); + break; + } + case IS_DOUBLE: { + double dval = Z_DVAL_PP(arg); + char *s_tmp; + int l_tmp; + TSRMLS_FETCH(); + + s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1); + l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval); /* SAFE */ + TRACE_APPEND_STRL(s_tmp, l_tmp); + /* %G already handles removing trailing zeros from the fractional part, yay */ + efree(s_tmp); + TRACE_APPEND_STR(", "); + break; + } + case IS_ARRAY: + TRACE_APPEND_STR("Array, "); + break; + case IS_OBJECT: { + char *class_name; + zend_uint class_name_len; + int dup; + TSRMLS_FETCH(); + + TRACE_APPEND_STR("Object("); + + dup = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC); + + TRACE_APPEND_STRL(class_name, class_name_len); + if(!dup) { + efree(class_name); + } + + TRACE_APPEND_STR("), "); + break; + } + default: + break; + } + return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ + +static int mysqlnd_build_trace_string(zval **frame, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ +{ + char *s_tmp, **str; + int *len, *num; + long line; + HashTable *ht = Z_ARRVAL_PP(frame); + zval **file, **tmp; + + str = va_arg(args, char**); + len = va_arg(args, int*); + num = va_arg(args, int*); + + s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1); + sprintf(s_tmp, "#%d ", (*num)++); + TRACE_APPEND_STRL(s_tmp, strlen(s_tmp)); + efree(s_tmp); + if (zend_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) { + if (zend_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) { + line = Z_LVAL_PP(tmp); + } else { + line = 0; + } + s_tmp = emalloc(Z_STRLEN_PP(file) + MAX_LENGTH_OF_LONG + 4 + 1); + sprintf(s_tmp, "%s(%ld): ", Z_STRVAL_PP(file), line); + TRACE_APPEND_STRL(s_tmp, strlen(s_tmp)); + efree(s_tmp); + } else { + TRACE_APPEND_STR("[internal function]: "); + } + TRACE_APPEND_KEY("class"); + TRACE_APPEND_KEY("type"); + TRACE_APPEND_KEY("function"); + TRACE_APPEND_CHR('('); + if (zend_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) { + int last_len = *len; + zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp), (apply_func_args_t)mysqlnd_build_trace_args, 2, str, len); + if (last_len != *len) { + *len -= 2; /* remove last ', ' */ + } + } + TRACE_APPEND_STR(")\n"); + return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ +#endif + + +char * mysqlnd_get_backtrace(TSRMLS_D) +{ + zval *trace; + char *res = estrdup(""), **str = &res, *s_tmp; + int res_len = 0, *len = &res_len, num = 0; + + MAKE_STD_ZVAL(trace); + zend_fetch_debug_backtrace(trace, 0, 0 TSRMLS_CC); + + zend_hash_apply_with_arguments(Z_ARRVAL_P(trace), (apply_func_args_t)mysqlnd_build_trace_string, 3, str, len, &num); + zval_ptr_dtor(&trace); + + s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 7 + 1); + sprintf(s_tmp, "#%d {main}", num); + TRACE_APPEND_STRL(s_tmp, strlen(s_tmp)); + efree(s_tmp); + + res[res_len] = '\0'; + + return res; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_debug.h b/ext/mysqlnd/mysqlnd_debug.h new file mode 100644 index 0000000000..e60a6d912d --- /dev/null +++ b/ext/mysqlnd/mysqlnd_debug.h @@ -0,0 +1,147 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 6 | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2007 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Georg Richter <georg@mysql.com> | + | Andrey Hristov <andrey@mysql.com> | + | Ulf Wendel <uwendel@mysql.com> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef MYSQLND_DEBUG_H +#define MYSQLND_DEBUG_H + +#include "zend_stack.h" + +#define MYSQLND_DEBUG_MEMORY 1 + +struct st_mysqlnd_debug_methods +{ + enum_func_status (*open)(MYSQLND_DEBUG *self, zend_bool reopen); + void (*set_mode)(MYSQLND_DEBUG *self, const char * const mode); + enum_func_status (*log)(MYSQLND_DEBUG *self, unsigned int line, const char * const file, + unsigned int level, const char * type, const char *message); + enum_func_status (*log_va)(MYSQLND_DEBUG *self, unsigned int line, const char * const file, + unsigned int level, const char * type, const char *format, ...); + zend_bool (*func_enter)(MYSQLND_DEBUG *self, unsigned int line, const char * const file, + char * func_name, size_t func_name_len); + enum_func_status (*func_leave)(MYSQLND_DEBUG *self, unsigned int line, const char * const file); + enum_func_status (*close)(MYSQLND_DEBUG *self); + enum_func_status (*free)(MYSQLND_DEBUG *self); +}; + +struct st_mysqlnd_debug +{ + php_stream *stream; +#ifdef ZTS + TSRMLS_D; +#endif + unsigned int flags; + unsigned int nest_level_limit; + int pid; + char * file_name; + zend_stack call_stack; + HashTable not_filtered_functions; + struct st_mysqlnd_debug_methods *m; +}; + + +MYSQLND_DEBUG *mysqlnd_debug_init(TSRMLS_D); + +#define MYSQLND_MEM_D TSRMLS_DC ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC + + +void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D); +void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D); +void * _mysqlnd_ecalloc(uint nmemb, size_t size MYSQLND_MEM_D); +void * _mysqlnd_pecalloc(uint nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D); +void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D); +void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D); +void _mysqlnd_efree(void *ptr MYSQLND_MEM_D); +void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D); +void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D); +void * _mysqlnd_calloc(uint nmemb, size_t size MYSQLND_MEM_D); +void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D); +void _mysqlnd_free(void *ptr MYSQLND_MEM_D); + +char * mysqlnd_get_backtrace(TSRMLS_D); + +#if PHP_DEBUG && !defined(PHP_WIN32) +#define DBG_INF(msg) do { if (dbg_skip_trace == FALSE) MYSQLND_G(dbg)->m->log(MYSQLND_G(dbg), __LINE__, __FILE__, -1, "info : ", (msg)); } while (0) +#define DBG_ERR(msg) do { if (dbg_skip_trace == FALSE) MYSQLND_G(dbg)->m->log(MYSQLND_G(dbg), __LINE__, __FILE__, -1, "error: ", (msg)); } while (0) +#define DBG_INF_FMT(...) do { if (dbg_skip_trace == FALSE) MYSQLND_G(dbg)->m->log_va(MYSQLND_G(dbg), __LINE__, __FILE__, -1, "info : ", __VA_ARGS__); } while (0) +#define DBG_ERR_FMT(...) do { if (dbg_skip_trace == FALSE) MYSQLND_G(dbg)->m->log_va(MYSQLND_G(dbg), __LINE__, __FILE__, -1, "error: ", __VA_ARGS__); } while (0) + +#define DBG_ENTER(func_name) zend_bool dbg_skip_trace = TRUE; if (MYSQLND_G(dbg)) dbg_skip_trace = !MYSQLND_G(dbg)->m->func_enter(MYSQLND_G(dbg), __LINE__, __FILE__, func_name, strlen(func_name)); +#define DBG_RETURN(value) do { if (MYSQLND_G(dbg)) MYSQLND_G(dbg)->m->func_leave(MYSQLND_G(dbg), __LINE__, __FILE__); return (value); } while (0) +#define DBG_VOID_RETURN do { if (MYSQLND_G(dbg)) MYSQLND_G(dbg)->m->func_leave(MYSQLND_G(dbg), __LINE__, __FILE__); return; } while (0) + + + +#else +#define DBG_INF(msg) +#define DBG_ERR(msg) +#define DBG_INF_FMT(...) +#define DBG_ERR_FMT(...) + +#define DBG_ENTER(func_name) +#define DBG_RETURN(value) return (value) +#define DBG_VOID_RETURN return; + +#endif + + +#if MYSQLND_DEBUG_MEMORY + +#define mnd_emalloc(size) _mysqlnd_emalloc((size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_pemalloc(size, pers) _mysqlnd_pemalloc((size), (pers) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_ecalloc(nmemb, size) _mysqlnd_ecalloc((nmemb), (size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_pecalloc(nmemb, size, p) _mysqlnd_pecalloc((nmemb), (size), (p) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_erealloc(ptr, new_size) _mysqlnd_erealloc((ptr), (new_size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_perealloc(ptr, new_size, p) _mysqlnd_perealloc((ptr), (new_size), (p) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_efree(ptr) _mysqlnd_efree((ptr) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_pefree(ptr, pers) _mysqlnd_pefree((ptr), (pers) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_malloc(size) _mysqlnd_malloc((size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_calloc(nmemb, size) _mysqlnd_calloc((nmemb), (size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_realloc(ptr, new_size) _mysqlnd_realloc((ptr), (new_size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define mnd_free(ptr) _mysqlnd_free((ptr) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) + +#else + +#define mnd_emalloc(size) emalloc((size)) +#define mnd_pemalloc(size, pers) pemalloc((size), (pers)) +#define mnd_ecalloc(nmemb, size) ecalloc((nmemb), (size)) +#define mnd_pecalloc(nmemb, size, p) pecalloc((nmemb), (size), (p)) +#define mnd_erealloc(ptr, new_size) erealloc((ptr), (new_size)) +#define mnd_perealloc(ptr, new_size, p) perealloc((ptr), (new_size), (p)) +#define mnd_efree(ptr) efree((ptr)) +#define mnd_pefree(ptr, pers) pefree((ptr), (pers)) +#define mnd_malloc(size) malloc((size)) +#define mnd_calloc(nmemb, size) calloc((nmemb), (size)) +#define mnd_realloc(ptr, new_size) realloc((ptr), (new_size)) +#define mnd_free(ptr) free((ptr)) + +#endif + +#endif /* MYSQLND_DEBUG_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/mysqlnd/mysqlnd_enum_n_def.h b/ext/mysqlnd/mysqlnd_enum_n_def.h index 53d5f7d8d7..f8a3a8b57d 100644 --- a/ext/mysqlnd/mysqlnd_enum_n_def.h +++ b/ext/mysqlnd/mysqlnd_enum_n_def.h @@ -114,10 +114,14 @@ typedef enum mysqlnd_option MYSQL_REPORT_DATA_TRUNCATION, MYSQL_OPT_RECONNECT, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, +#if PHP_MAJOR_VERSION >= 6 MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE = 200, +#endif #ifdef MYSQLND_STRING_TO_INT_CONVERSION MYSQLND_OPT_INT_AND_YEAR_AS_INT = 201, #endif + MYSQLND_OPT_NET_CMD_BUFFER_SIZE = 202, + MYSQLND_OPT_NET_READ_BUFFER_SIZE = 203, } enum_mysqlnd_option; @@ -218,6 +222,138 @@ typedef enum mysqlnd_server_option /* see mysqlnd_charset.c for more information */ #define MYSQLND_BINARY_CHARSET_NR 63 + +/* + /-----> CONN_CLOSE <---------------\ + | ^ \ + | | \ + CONN_READY -> CONN_QUERY_SENT -> CONN_FETCHING_DATA + ^ | + \-------------------------------------/ +*/ +typedef enum mysqlnd_connection_state +{ + CONN_ALLOCED = 0, + CONN_READY, + CONN_QUERY_SENT, + CONN_SENDING_LOAD_DATA, + CONN_FETCHING_DATA, + CONN_NEXT_RESULT_PENDING, + CONN_QUIT_SENT, /* object is "destroyed" at this stage */ +} enum_mysqlnd_connection_state; + + +typedef enum mysqlnd_stmt_state +{ + MYSQLND_STMT_INITTED = 0, + MYSQLND_STMT_PREPARED, + MYSQLND_STMT_EXECUTED, + MYSQLND_STMT_WAITING_USE_OR_STORE, + MYSQLND_STMT_USE_OR_STORE_CALLED, + MYSQLND_STMT_USER_FETCHING, /* fetch_row_buff or fetch_row_unbuf */ +} enum_mysqlnd_stmt_state; + + +typedef enum param_bind_flags +{ + MYSQLND_PARAM_BIND_BLOB_USED = 1 +} enum_param_bind_flags; + + +/* PS */ +enum mysqlnd_stmt_attr +{ + STMT_ATTR_UPDATE_MAX_LENGTH, + STMT_ATTR_CURSOR_TYPE, + STMT_ATTR_PREFETCH_ROWS +}; + +enum myslqnd_cursor_type +{ + CURSOR_TYPE_NO_CURSOR= 0, + CURSOR_TYPE_READ_ONLY= 1, + CURSOR_TYPE_FOR_UPDATE= 2, + CURSOR_TYPE_SCROLLABLE= 4 +}; + +typedef enum mysqlnd_connection_close_type +{ + MYSQLND_CLOSE_EXPLICIT = 0, + MYSQLND_CLOSE_IMPLICIT, + MYSQLND_CLOSE_DISCONNECTED, + MYSQLND_CLOSE_LAST /* for checking, should always be last */ +} enum_connection_close_type; + +typedef enum mysqlnd_collected_stats +{ + STAT_BYTES_SENT, + STAT_BYTES_RECEIVED, + STAT_PACKETS_SENT, + STAT_PACKETS_RECEIVED, + STAT_PROTOCOL_OVERHEAD_IN, + STAT_PROTOCOL_OVERHEAD_OUT, + STAT_RSET_QUERY, + STAT_NON_RSET_QUERY, + STAT_NO_INDEX_USED, + STAT_BAD_INDEX_USED, + STAT_BUFFERED_SETS, + STAT_UNBUFFERED_SETS, + STAT_PS_BUFFERED_SETS, + STAT_PS_UNBUFFERED_SETS, + STAT_FLUSHED_NORMAL_SETS, + STAT_FLUSHED_PS_SETS, + STAT_PS_PREPARED_NEVER_EXECUTED, + STAT_PS_PREPARED_ONCE_USED, + STAT_ROWS_FETCHED_FROM_SERVER_NORMAL, + STAT_ROWS_FETCHED_FROM_SERVER_PS, + STAT_ROWS_BUFFERED_FROM_CLIENT_NORMAL, + STAT_ROWS_BUFFERED_FROM_CLIENT_PS, + STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF, + STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF, + STAT_ROWS_FETCHED_FROM_CLIENT_PS_BUF, + STAT_ROWS_FETCHED_FROM_CLIENT_PS_UNBUF, + STAT_ROWS_FETCHED_FROM_CLIENT_PS_CURSOR, + STAT_ROWS_SKIPPED_NORMAL, + STAT_ROWS_SKIPPED_PS, + STAT_COPY_ON_WRITE_SAVED, + STAT_COPY_ON_WRITE_PERFORMED, + STAT_CMD_BUFFER_TOO_SMALL, + STAT_CONNECT_SUCCESS, + STAT_CONNECT_FAILURE, + STAT_CONNECT_REUSED, + STAT_RECONNECT, + STAT_PCONNECT_SUCCESS, + STAT_OPENED_CONNECTIONS, + STAT_OPENED_PERSISTENT_CONNECTIONS, + STAT_CLOSE_EXPLICIT, + STAT_CLOSE_IMPLICIT, + STAT_CLOSE_DISCONNECT, + STAT_CLOSE_IN_MIDDLE, + STAT_FREE_RESULT_EXPLICIT, + STAT_FREE_RESULT_IMPLICIT, + STAT_STMT_CLOSE_EXPLICIT, + STAT_STMT_CLOSE_IMPLICIT, + STAT_MEM_EMALLOC_COUNT, + STAT_MEM_EMALLOC_AMMOUNT, + STAT_MEM_ECALLOC_COUNT, + STAT_MEM_ECALLOC_AMMOUNT, + STAT_MEM_EREALLOC_COUNT, + STAT_MEM_EREALLOC_AMMOUNT, + STAT_MEM_EFREE_COUNT, + STAT_MEM_MALLOC_COUNT, + STAT_MEM_MALLOC_AMMOUNT, + STAT_MEM_CALLOC_COUNT, + STAT_MEM_CALLOC_AMMOUNT, + STAT_MEM_REALLOC_COUNT, + STAT_MEM_REALLOC_AMMOUNT, + STAT_MEM_FREE_COUNT, + STAT_LAST /* Should be always the last */ +} enum_mysqlnd_collected_stats; + + +#define MYSQLND_DEFAULT_PREFETCH_ROWS (ulong) 1 + + #endif /* MYSQLND_ENUM_N_DEF_H */ diff --git a/ext/mysqlnd/mysqlnd_loaddata.c b/ext/mysqlnd/mysqlnd_loaddata.c index ce7a27bcf4..2e5466aa07 100644 --- a/ext/mysqlnd/mysqlnd_loaddata.c +++ b/ext/mysqlnd/mysqlnd_loaddata.c @@ -23,6 +23,7 @@ #include "mysqlnd.h" #include "mysqlnd_wireprotocol.h" #include "mysqlnd_priv.h" +#include "mysqlnd_debug.h" enum_func_status mysqlnd_simple_command_handle_response(MYSQLND *conn, enum php_mysql_packet_type ok_packet, @@ -34,7 +35,7 @@ enum_func_status mysqlnd_simple_command_handle_response(MYSQLND *conn, if (c) {\ a = (zval ***)safe_emalloc(c, sizeof(zval **), 0);\ for (i = b; i < c; i++) {\ - a[i] = emalloc(sizeof(zval *));\ + a[i] = mnd_emalloc(sizeof(zval *));\ MAKE_STD_ZVAL(*a[i]);\ }\ } @@ -43,9 +44,9 @@ if (c) {\ if (a) {\ for (i=b; i < c; i++) {\ zval_ptr_dtor(a[i]);\ - efree(a[i]);\ + mnd_efree(a[i]);\ }\ - efree(a);\ + mnd_efree(a);\ } /* {{{ mysqlnd_local_infile_init */ @@ -55,14 +56,16 @@ int mysqlnd_local_infile_init(void **ptr, char *filename, void **userdata TSRMLS MYSQLND_INFILE_INFO *info; php_stream_context *context = NULL; - *ptr= info= ((MYSQLND_INFILE_INFO *)ecalloc(1, sizeof(MYSQLND_INFILE_INFO))); + DBG_ENTER("mysqlnd_local_infile_init"); + + *ptr= info= ((MYSQLND_INFILE_INFO *)mnd_ecalloc(1, sizeof(MYSQLND_INFILE_INFO))); /* check open_basedir */ if (PG(open_basedir)) { if (php_check_open_basedir_ex(filename, 0 TSRMLS_CC) == -1) { strcpy(info->error_msg, "open_basedir restriction in effect. Unable to open file"); info->error_no = CR_UNKNOWN_ERROR; - return 1; + DBG_RETURN(1); } } @@ -72,10 +75,10 @@ int mysqlnd_local_infile_init(void **ptr, char *filename, void **userdata TSRMLS if (info->fd == NULL) { snprintf((char *)info->error_msg, sizeof(info->error_msg), "Can't find file '%-.64s'.", filename); info->error_no = MYSQLND_EE_FILENOTFOUND; - return 1; + DBG_RETURN(1); } - return 0; + DBG_RETURN(0); } /* }}} */ @@ -87,81 +90,16 @@ int mysqlnd_local_infile_read(void *ptr, char *buf, uint buf_len TSRMLS_DC) MYSQLND_INFILE_INFO *info = (MYSQLND_INFILE_INFO *)ptr; int count; - /* default processing */ - if (!info->callback) { - count = (int)php_stream_read(info->fd, buf, buf_len); + DBG_ENTER("mysqlnd_local_infile_read"); - if (count < 0) { - strcpy(info->error_msg, "Error reading file"); - info->error_no = MYSQLND_EE_READ; - } - - return count; - } else { - zval ***callback_args; - zval *retval; - zval *fp; - int argc = 4; - int i; - long rc; - - ALLOC_CALLBACK_ARGS(callback_args, 1, argc); - - /* set parameters: filepointer, buffer, buffer_len, errormsg */ - - MAKE_STD_ZVAL(fp); - php_stream_to_zval(info->fd, fp); - callback_args[0] = &fp; - ZVAL_STRING(*callback_args[1], "", 1); - ZVAL_LONG(*callback_args[2], buf_len); - ZVAL_STRING(*callback_args[3], "", 1); - - if (call_user_function_ex(EG(function_table), - NULL, - info->callback, - &retval, - argc, - callback_args, - 0, - NULL TSRMLS_CC) == SUCCESS) { - - rc = Z_LVAL_P(retval); - zval_ptr_dtor(&retval); - - if (rc > 0) { - const char * msg = NULL; - if (rc >= 0 && rc != Z_STRLEN_P(*callback_args[1])) { - msg = "Mismatch between the return value of the callback and the content " - "length of the buffer."; - php_error_docref(NULL TSRMLS_CC, E_WARNING, msg); - rc = -1; - } else if (Z_STRLEN_P(*callback_args[1]) > buf_len) { - /* check buffer overflow */ - msg = "Too much data returned"; - rc = -1; - } else { - memcpy(buf, Z_STRVAL_P(*callback_args[1]), MIN(rc, Z_STRLEN_P(*callback_args[1]))); - } - if (rc == -1) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, msg); - strcpy(info->error_msg, msg); - info->error_no = MYSQLND_EE_READ; - } - } else if (rc < 0) { - strncpy(info->error_msg, Z_STRVAL_P(*callback_args[3]), MYSQLND_ERRMSG_SIZE); - info->error_no = MYSQLND_EE_READ; - } - } else { - strcpy(info->error_msg, "Can't execute load data local init callback function"); - info->error_no = MYSQLND_EE_READ; - rc = -1; - } - - efree(fp); - FREE_CALLBACK_ARGS(callback_args, 1, argc); - return rc; + count = (int)php_stream_read(info->fd, buf, buf_len); + if (count < 0) { + strcpy(info->error_msg, "Error reading file"); + info->error_no = CR_UNKNOWN_ERROR; } + + DBG_RETURN(count); } /* }}} */ @@ -172,14 +110,17 @@ int mysqlnd_local_infile_error(void *ptr, char *error_buf, uint error_buf_len TS { MYSQLND_INFILE_INFO *info = (MYSQLND_INFILE_INFO *)ptr; + DBG_ENTER("mysqlnd_local_infile_error"); + if (info) { strncpy(error_buf, info->error_msg, error_buf_len); - - return info->error_no; + DBG_INF_FMT("have info, %d", info->error_no); + DBG_RETURN(info->error_no); } strncpy(error_buf, "Unknown error", error_buf_len); - return CR_UNKNOWN_ERROR; + DBG_INF_FMT("no info, %d", CR_UNKNOWN_ERROR); + DBG_RETURN(CR_UNKNOWN_ERROR); } /* }}} */ @@ -194,25 +135,21 @@ void mysqlnd_local_infile_end(void *ptr TSRMLS_DC) /* php_stream_close segfaults on NULL */ if (info->fd) { php_stream_close(info->fd); + info->fd = NULL; } - efree(info); + mnd_efree(info); } } /* }}} */ /* {{{ mysqlnd_local_infile_default */ -PHPAPI void mysqlnd_local_infile_default(MYSQLND *conn, zend_bool free_callback) +PHPAPI void mysqlnd_local_infile_default(MYSQLND *conn) { conn->infile.local_infile_init = mysqlnd_local_infile_init; conn->infile.local_infile_read = mysqlnd_local_infile_read; conn->infile.local_infile_error = mysqlnd_local_infile_error; conn->infile.local_infile_end = mysqlnd_local_infile_end; - conn->infile.userdata = NULL; - if (free_callback == TRUE && conn->infile.callback) { - zval_ptr_dtor(&conn->infile.callback); - conn->infile.callback = NULL; - } } /* }}} */ @@ -245,6 +182,8 @@ mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename, zend_bool *is_w size_t ret; MYSQLND_INFILE infile; + DBG_ENTER("mysqlnd_handle_local_infile"); + if (!(conn->options.flags & CLIENT_LOCAL_FILES)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "LOAD DATA LOCAL INFILE forbidden"); /* write empty packet to server */ @@ -253,15 +192,9 @@ mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename, zend_bool *is_w goto infile_error; } - /* check if we have valid functions */ - if (!conn->infile.local_infile_init || !conn->infile.local_infile_read || - !conn->infile.local_infile_error || !conn->infile.local_infile_end) { - mysqlnd_local_infile_default(conn, FALSE); - } - infile = conn->infile; /* allocate buffer for reading data */ - buf = (char *)ecalloc(1, buflen); + buf = (char *)mnd_ecalloc(1, buflen); *is_warning = FALSE; @@ -278,17 +211,11 @@ mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename, zend_bool *is_w goto infile_error; } - /* pass callback handler */ - if (infile.callback) { - MYSQLND_INFILE_INFO *ptr = (MYSQLND_INFILE_INFO *)info; - ptr->callback = infile.callback; - } - - /* read data */ while ((bufsize = infile.local_infile_read (info, buf + MYSQLND_HEADER_SIZE, buflen - MYSQLND_HEADER_SIZE TSRMLS_CC)) > 0) { if ((ret = mysqlnd_stream_write_w_header(conn, buf, bufsize TSRMLS_CC)) < 0) { + DBG_ERR_FMT("Error during read : %d %s %s", CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn); SET_CLIENT_ERROR(conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn); goto infile_error; } @@ -303,6 +230,7 @@ mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename, zend_bool *is_w /* error during read occured */ if (bufsize < 0) { *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); @@ -319,8 +247,9 @@ infile_error: } (*conn->infile.local_infile_end)(info TSRMLS_CC); - efree(buf); - return result; + mnd_efree(buf); + DBG_INF_FMT("%s", result == PASS? "PASS":"FAIL"); + DBG_RETURN(result); } /* }}} */ diff --git a/ext/mysqlnd/mysqlnd_palloc.c b/ext/mysqlnd/mysqlnd_palloc.c index a5df45c82e..fba769038a 100644 --- a/ext/mysqlnd/mysqlnd_palloc.c +++ b/ext/mysqlnd/mysqlnd_palloc.c @@ -23,8 +23,11 @@ #include "mysqlnd.h" #include "mysqlnd_priv.h" #include "mysqlnd_palloc.h" +#include "mysqlnd_debug.h" -#define MYSQLND_SILENT +/* Used in mysqlnd_debug.c */ +char * mysqlnd_palloc_zval_ptr_dtor_name = "mysqlnd_palloc_zval_ptr_dtor"; +char * mysqlnd_palloc_get_zval_name = "mysqlnd_palloc_get_zval"; #ifdef ZTS @@ -43,14 +46,14 @@ #endif -/* {{{ mysqlnd_palloc_init_cache */ -PHPAPI MYSQLND_ZVAL_PCACHE* mysqlnd_palloc_init_cache(unsigned int cache_size) +/* {{{ _mysqlnd_palloc_init_cache */ +PHPAPI MYSQLND_ZVAL_PCACHE* _mysqlnd_palloc_init_cache(unsigned int cache_size TSRMLS_DC) { MYSQLND_ZVAL_PCACHE *ret = calloc(1, sizeof(MYSQLND_ZVAL_PCACHE)); unsigned int i; -#ifndef MYSQLND_SILENT - php_printf("[mysqlnd_palloc_init_cache %p]\n", ret); -#endif + + DBG_ENTER("_mysqlnd_palloc_init_cache"); + DBG_INF_FMT("cache=%p size=%u", ret, cache_size); #ifdef ZTS ret->LOCK_access = tsrm_mutex_alloc(); @@ -77,9 +80,8 @@ PHPAPI MYSQLND_ZVAL_PCACHE* mysqlnd_palloc_init_cache(unsigned int cache_size) /* 2. Add to the free list */ *(--ret->free_list.last_added) = &(ret->block[i]); } - - return ret; + DBG_RETURN(ret); } /* }}} */ @@ -105,31 +107,32 @@ MYSQLND_ZVAL_PCACHE* mysqlnd_palloc_get_cache_reference(MYSQLND_ZVAL_PCACHE * co to the free list after usage. We ZVAL_NULL() them when we allocate them in the constructor of the cache. */ -void mysqlnd_palloc_free_cache(MYSQLND_ZVAL_PCACHE *cache) +void _mysqlnd_palloc_free_cache(MYSQLND_ZVAL_PCACHE *cache TSRMLS_DC) { -#ifndef MYSQLND_SILENT - php_printf("[mysqlnd_palloc_free_cache %p]\n", cache); -#endif + DBG_ENTER("_mysqlnd_palloc_free_cache"); + DBG_INF_FMT("cache=%p", cache); #ifdef ZTS tsrm_mutex_free(cache->LOCK_access); #endif /* Data in pointed by 'block' was cleaned in RSHUTDOWN */ - free(cache->block); - free(cache->free_list.ptr_line); - free(cache); + mnd_free(cache->block); + mnd_free(cache->free_list.ptr_line); + mnd_free(cache); + + DBG_VOID_RETURN; } /* }}} */ -/* {{{ mysqlnd_palloc_init_thd_cache */ -PHPAPI MYSQLND_THD_ZVAL_PCACHE* mysqlnd_palloc_init_thd_cache(MYSQLND_ZVAL_PCACHE * const cache) +/* {{{ _mysqlnd_palloc_init_thd_cache */ +PHPAPI MYSQLND_THD_ZVAL_PCACHE* _mysqlnd_palloc_init_thd_cache(MYSQLND_ZVAL_PCACHE * const cache TSRMLS_DC) { MYSQLND_THD_ZVAL_PCACHE *ret = calloc(1, sizeof(MYSQLND_THD_ZVAL_PCACHE)); -#ifndef MYSQLND_SILENT - php_printf("[mysqlnd_palloc_init_thd_cache %p]\n", ret); -#endif + DBG_ENTER("_mysqlnd_palloc_init_thd_cache"); + DBG_INF_FMT("ret = %p", ret); + ret->parent = mysqlnd_palloc_get_cache_reference(cache); #ifdef ZTS @@ -143,7 +146,7 @@ PHPAPI MYSQLND_THD_ZVAL_PCACHE* mysqlnd_palloc_init_thd_cache(MYSQLND_ZVAL_PCACH /* Backward and forward looping is possible */ ret->gc_list.last_added = ret->gc_list.ptr_line; - return ret; + DBG_RETURN(ret); } /* }}} */ @@ -169,32 +172,33 @@ MYSQLND_THD_ZVAL_PCACHE* mysqlnd_palloc_get_thd_cache_reference(MYSQLND_THD_ZVAL constructor of the cache. */ static -void mysqlnd_palloc_free_thd_cache(MYSQLND_THD_ZVAL_PCACHE *cache) +void mysqlnd_palloc_free_thd_cache(MYSQLND_THD_ZVAL_PCACHE *cache TSRMLS_DC) { -#ifndef MYSQLND_SILENT - php_printf("[mysqlnd_palloc_free_thd_cache %p]\n", cache); -#endif + DBG_ENTER("mysqlnd_palloc_free_thd_cache"); + DBG_INF_FMT("cache=%p", cache); - free(cache->gc_list.ptr_line); - free(cache); + mnd_free(cache->gc_list.ptr_line); + mnd_free(cache); + + DBG_VOID_RETURN; } /* }}} */ -/* {{{ mysqlnd_palloc_free_thd_cache_reference */ -PHPAPI void mysqlnd_palloc_free_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE **cache) +/* {{{ _mysqlnd_palloc_free_thd_cache_reference */ +PHPAPI void _mysqlnd_palloc_free_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE **cache TSRMLS_DC) { + DBG_ENTER("_mysqlnd_palloc_free_thd_cache_reference"); if (*cache) { -#ifndef MYSQLND_SILENT - php_printf("[mysqlnd_palloc_free_thd_cache_reference %p] refs=%d\n", *cache, (*cache)->references); -#endif + DBG_INF_FMT("cache=%p refs=%d", *cache, (*cache)->references); --(*cache)->parent->references; if (--(*cache)->references == 0) { - mysqlnd_palloc_free_thd_cache(*cache); + mysqlnd_palloc_free_thd_cache(*cache TSRMLS_CC); } *cache = NULL; } + DBG_VOID_RETURN; } /* }}} */ @@ -285,15 +289,14 @@ PHPAPI void mysqlnd_palloc_free_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE **ca /* {{{ mysqlnd_palloc_get_zval */ -void *mysqlnd_palloc_get_zval(MYSQLND_THD_ZVAL_PCACHE * const thd_cache, zend_bool *allocated) +void *mysqlnd_palloc_get_zval(MYSQLND_THD_ZVAL_PCACHE * const thd_cache, zend_bool *allocated TSRMLS_DC) { void *ret = NULL; -#ifndef MYSQLND_SILENT - php_printf("[mysqlnd_palloc_get_zval %p] *last_added=%p free_items=%d\n", + DBG_ENTER("mysqlnd_palloc_get_zval"); + DBG_INF_FMT("cache=%p *last_added=%p free_items=%d", thd_cache, thd_cache? thd_cache->parent->free_list.last_added:NULL, thd_cache->parent->free_items); -#endif if (thd_cache) { MYSQLND_ZVAL_PCACHE *cache = thd_cache->parent; @@ -328,7 +331,8 @@ void *mysqlnd_palloc_get_zval(MYSQLND_THD_ZVAL_PCACHE * const thd_cache, zend_bo ZVAL_ADDREF(&(((mysqlnd_zval *)ret)->zv)); } - return ret; + DBG_INF_FMT("allocated=%d ret=%p", *allocated, ret); + DBG_RETURN(ret); } /* }}} */ @@ -338,13 +342,12 @@ void mysqlnd_palloc_zval_ptr_dtor(zval **zv, MYSQLND_THD_ZVAL_PCACHE * const thd enum_mysqlnd_res_type type, zend_bool *copy_ctor_called TSRMLS_DC) { MYSQLND_ZVAL_PCACHE *cache; -#ifndef MYSQLND_SILENT - php_printf("[mysqlnd_palloc_zval_ptr_dtor %p] parent_block=%p last_in_block=%p *zv=%p type=%d refc=%d\n", + DBG_ENTER("mysqlnd_palloc_zval_ptr_dtor"); + DBG_INF_FMT("cache=%p parent_block=%p last_in_block=%p *zv=%p refc=%d type=%d ", thd_cache, thd_cache->parent? thd_cache->parent->block:NULL, thd_cache->parent? thd_cache->parent->last_in_block:NULL, - *zv, type, ZVAL_REFCOUNT(*zv)); -#endif + *zv, ZVAL_REFCOUNT(*zv), type); *copy_ctor_called = FALSE; /* Check whether cache is used and the zval is from the cache */ if (!thd_cache || !(cache = thd_cache->parent) || ((char *)*zv < (char *)thd_cache->parent->block || @@ -381,7 +384,7 @@ void mysqlnd_palloc_zval_ptr_dtor(zval **zv, MYSQLND_THD_ZVAL_PCACHE * const thd } } zval_ptr_dtor(zv); - return; + DBG_VOID_RETURN; } /* The zval is from our cache */ @@ -441,26 +444,28 @@ void mysqlnd_palloc_zval_ptr_dtor(zval **zv, MYSQLND_THD_ZVAL_PCACHE * const thd UNLOCK_PCACHE(cache); } + DBG_VOID_RETURN; } /* }}} */ -/* {{{ mysqlnd_palloc_rinit */ -PHPAPI MYSQLND_THD_ZVAL_PCACHE * mysqlnd_palloc_rinit(MYSQLND_ZVAL_PCACHE * cache) +/* {{{ _mysqlnd_palloc_rinit */ +PHPAPI MYSQLND_THD_ZVAL_PCACHE * _mysqlnd_palloc_rinit(MYSQLND_ZVAL_PCACHE * cache TSRMLS_DC) { return mysqlnd_palloc_init_thd_cache(cache); } /* }}} */ -/* {{{ mysqlnd_palloc_rshutdown */ -PHPAPI void mysqlnd_palloc_rshutdown(MYSQLND_THD_ZVAL_PCACHE * thd_cache) +/* {{{ _mysqlnd_palloc_rshutdown */ +PHPAPI void _mysqlnd_palloc_rshutdown(MYSQLND_THD_ZVAL_PCACHE * thd_cache TSRMLS_DC) { MYSQLND_ZVAL_PCACHE *cache; mysqlnd_zval **p; -#ifndef MYSQLND_SILENT - php_printf("[mysqlnd_palloc_rshutdown %p]\n", thd_cache); -#endif + + DBG_ENTER("_mysqlnd_palloc_rshutdown"); + DBG_INF_FMT("cache=%p", thd_cache); + if (!thd_cache || !(cache = thd_cache->parent)) { return; } @@ -490,6 +495,8 @@ PHPAPI void mysqlnd_palloc_rshutdown(MYSQLND_THD_ZVAL_PCACHE * thd_cache) UNLOCK_PCACHE(cache); mysqlnd_palloc_free_thd_cache_reference(&thd_cache); + + DBG_VOID_RETURN; } /* }}} */ diff --git a/ext/mysqlnd/mysqlnd_palloc.h b/ext/mysqlnd/mysqlnd_palloc.h index 434df01730..8ef0eaab87 100644 --- a/ext/mysqlnd/mysqlnd_palloc.h +++ b/ext/mysqlnd/mysqlnd_palloc.h @@ -22,6 +22,11 @@ #ifndef MYSQLND_PALLOC_H #define MYSQLND_PALLOC_H +/* Used in mysqlnd_debug.c */ +extern char * mysqlnd_palloc_zval_ptr_dtor_name; +extern char * mysqlnd_palloc_get_zval_name; + + /* Session caching allocator */ struct st_mysqlnd_zval_list { zval **ptr_line; diff --git a/ext/mysqlnd/mysqlnd_priv.h b/ext/mysqlnd/mysqlnd_priv.h index 9702c480f5..83bc0665cb 100644 --- a/ext/mysqlnd/mysqlnd_priv.h +++ b/ext/mysqlnd/mysqlnd_priv.h @@ -98,8 +98,7 @@ #define CR_INVALID_PARAMETER_NO 2034 #define CR_INVALID_BUFFER_USE 2035 -#define MYSQLND_EE_READ 2 -#define MYSQLND_EE_FILENOTFOUND 29 +#define MYSQLND_EE_FILENOTFOUND 7890 #define UNKNOWN_SQLSTATE "HY000" @@ -164,6 +163,7 @@ struct st_mysqlnd_perm_bind { extern struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + 1]; extern const char * mysqlnd_out_of_sync; +extern const char * mysqlnd_server_gone; enum_func_status mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename, zend_bool *is_warning TSRMLS_DC); @@ -171,9 +171,6 @@ enum_func_status mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename void _mysqlnd_init_ps_subsystem();/* This one is private, mysqlnd_library_init() will call it */ -void mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uchar **p, - size_t *buf_len, unsigned int null_byte_offset); - void ps_fetch_from_1_to_8_bytes(zval *zv, const MYSQLND_FIELD * const field, uint pack_len, zend_uchar **row, zend_bool as_unicode, unsigned int byte_count TSRMLS_DC); diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c index fad3f9fefe..00dc46761f 100644 --- a/ext/mysqlnd/mysqlnd_ps.c +++ b/ext/mysqlnd/mysqlnd_ps.c @@ -26,6 +26,7 @@ #include "mysqlnd_result.h" #include "mysqlnd_result_meta.h" #include "mysqlnd_statistics.h" +#include "mysqlnd_debug.h" #define MYSQLND_SILENT @@ -42,7 +43,7 @@ enum_func_status mysqlnd_simple_command(MYSQLND *conn, enum php_mysqlnd_server_c /* Exported by mysqlnd_ps_codec.c */ zend_uchar* mysqlnd_stmt_execute_generate_request(MYSQLND_STMT *stmt, size_t *request_len, - zend_bool *free_buffer); + zend_bool *free_buffer TSRMLS_DC); MYSQLND_RES * _mysqlnd_stmt_use_result(MYSQLND_STMT *stmt TSRMLS_DC); @@ -55,7 +56,7 @@ enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC); -void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt); +void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC); /* {{{ mysqlnd_stmt::store_result */ @@ -67,24 +68,27 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC) MYSQLND_RES *result; zend_bool to_cache = FALSE; + DBG_ENTER("mysqlnd_stmt::store_result"); + DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + /* be compliant with libmysql - NULL will turn */ if (!stmt->field_count) { - return NULL; + DBG_RETURN(NULL); } if (stmt->cursor_exists) { /* Silently convert buffered to unbuffered, for now */ - return stmt->m->use_result(stmt TSRMLS_CC); + MYSQLND_RES * res = stmt->m->use_result(stmt TSRMLS_CC); + DBG_RETURN(res); } - /* Nothing to store for UPSERT/LOAD DATA*/ if (conn->state != CONN_FETCHING_DATA || stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE) { SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); - return NULL; + DBG_RETURN(NULL); } stmt->default_rset_handler = stmt->m->store_result; @@ -119,12 +123,12 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC) } else { conn->error_info = result->data->error_info; stmt->result->m.free_result_contents(stmt->result TSRMLS_CC); - efree(stmt->result); + mnd_efree(stmt->result); stmt->result = NULL; stmt->state = MYSQLND_STMT_PREPARED; } - return result; + DBG_RETURN(result); } /* }}} */ @@ -136,23 +140,25 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const stmt TSRMLS_DC) MYSQLND *conn = stmt->conn; MYSQLND_RES *result; + DBG_ENTER("mysqlnd_stmt::get_result"); + DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + /* be compliant with libmysql - NULL will turn */ if (!stmt->field_count) { - return NULL; + DBG_RETURN(NULL); } if (stmt->cursor_exists) { /* Silently convert buffered to unbuffered, for now */ - return stmt->m->use_result(stmt TSRMLS_CC); + MYSQLND_RES * res = stmt->m->use_result(stmt TSRMLS_CC); + DBG_RETURN(res); } /* Nothing to store for UPSERT/LOAD DATA*/ - if (conn->state != CONN_FETCHING_DATA || - stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE) - { + if (conn->state != CONN_FETCHING_DATA || stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE) { SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); - return NULL; + DBG_RETURN(NULL); } SET_EMPTY_ERROR(stmt->error_info); @@ -160,9 +166,9 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const stmt TSRMLS_DC) MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_BUFFERED_SETS); result = mysqlnd_result_init(stmt->result->field_count, - mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache)); + mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache) TSRMLS_CC); - result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE); + result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC); /* Not set for SHOW statements at PREPARE stage */ if (stmt->result->conn) { @@ -179,7 +185,7 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const stmt TSRMLS_DC) stmt->state = MYSQLND_STMT_PREPARED; } - return result; + DBG_RETURN(result); } /* }}} */ @@ -192,16 +198,20 @@ mysqlnd_stmt_skip_metadata(MYSQLND_STMT *stmt TSRMLS_DC) unsigned int i = 0; php_mysql_packet_res_field field_packet; + DBG_ENTER("mysqlnd_stmt_skip_metadata"); + DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + PACKET_INIT_ALLOCA(field_packet, PROT_RSET_FLD_PACKET); field_packet.skip_parsing = TRUE; for (;i < stmt->param_count; i++) { if (FAIL == PACKET_READ_ALLOCA(field_packet, stmt->conn)) { PACKET_FREE_ALLOCA(field_packet); - return FAIL; + DBG_RETURN(FAIL); } } PACKET_FREE_ALLOCA(field_packet); - return PASS; + + DBG_RETURN(PASS); } /* }}} */ @@ -212,6 +222,9 @@ mysqlnd_stmt_read_prepare_response(MYSQLND_STMT *stmt TSRMLS_DC) { php_mysql_packet_prepare_response prepare_resp; + DBG_ENTER("mysqlnd_stmt_read_prepare_response"); + DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + PACKET_INIT_ALLOCA(prepare_resp, PROT_PREPARE_RESP_PACKET); if (FAIL == PACKET_READ_ALLOCA(prepare_resp, stmt->conn)) { PACKET_FREE_ALLOCA(prepare_resp); @@ -229,7 +242,7 @@ mysqlnd_stmt_read_prepare_response(MYSQLND_STMT *stmt TSRMLS_DC) stmt->param_count = prepare_resp.param_count; PACKET_FREE_ALLOCA(prepare_resp); - return PASS; + DBG_RETURN(PASS); } /* }}} */ @@ -241,11 +254,14 @@ mysqlnd_stmt_prepare_read_eof(MYSQLND_STMT *stmt TSRMLS_DC) php_mysql_packet_eof fields_eof; enum_func_status ret; + DBG_ENTER("mysqlnd_stmt_prepare_read_eof"); + DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + PACKET_INIT_ALLOCA(fields_eof, PROT_EOF_PACKET); if (FAIL == (ret = PACKET_READ_ALLOCA(fields_eof, stmt->conn))) { if (stmt->result) { stmt->result->m.free_result_contents(stmt->result TSRMLS_CC); - efree(stmt->result); + mnd_efree(stmt->result); memset(stmt, 0, sizeof(MYSQLND_STMT)); stmt->state = MYSQLND_STMT_INITTED; } @@ -256,7 +272,7 @@ mysqlnd_stmt_prepare_read_eof(MYSQLND_STMT *stmt TSRMLS_DC) } PACKET_FREE_ALLOCA(fields_eof); - return ret; + DBG_RETURN(ret); } /* }}} */ @@ -268,6 +284,9 @@ MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const stmt, const char * co { MYSQLND_STMT *stmt_to_prepare = stmt; + DBG_ENTER("mysqlnd_stmt::prepare"); + DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + SET_ERROR_AFF_ROWS(stmt); SET_ERROR_AFF_ROWS(stmt->conn); @@ -312,7 +331,7 @@ MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const stmt, const char * co no metadata at prepare. */ if (stmt_to_prepare->field_count) { - MYSQLND_RES *result = mysqlnd_result_init(stmt_to_prepare->field_count, NULL); + MYSQLND_RES *result = mysqlnd_result_init(stmt_to_prepare->field_count, NULL TSRMLS_CC); /* Allocate the result now as it is needed for the reading of metadata */ stmt_to_prepare->result = result; @@ -333,10 +352,11 @@ MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const stmt, const char * co memcpy(stmt, stmt_to_prepare, sizeof(MYSQLND_STMT)); /* Now we will have a clean new statement object */ - efree(stmt_to_prepare); + mnd_efree(stmt_to_prepare); } stmt->state = MYSQLND_STMT_PREPARED; - return PASS; + DBG_INF("PASS"); + DBG_RETURN(PASS); fail: if (stmt_to_prepare != stmt) { @@ -344,7 +364,8 @@ fail: } stmt->state = MYSQLND_STMT_INITTED; - return FAIL; + DBG_INF("FAIL"); + DBG_RETURN(FAIL); } /* }}} */ @@ -359,6 +380,9 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) size_t request_len; zend_bool free_request; + DBG_ENTER("mysqlnd_stmt::execute"); + DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + SET_ERROR_AFF_ROWS(stmt); SET_ERROR_AFF_ROWS(stmt->conn); @@ -398,16 +422,18 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); - return FAIL; + DBG_INF("FAIL"); + DBG_RETURN(FAIL); } if (stmt->param_count && !stmt->param_bind) { SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE, "No data supplied for parameters in prepared statement"); - return FAIL; + DBG_INF("FAIL"); + DBG_RETURN(FAIL); } - request = mysqlnd_stmt_execute_generate_request(stmt, &request_len, &free_request); + request = mysqlnd_stmt_execute_generate_request(stmt, &request_len, &free_request TSRMLS_CC); /* support for buffer types should be added here ! */ @@ -416,13 +442,15 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) FALSE TSRMLS_CC); if (free_request) { - efree(request); + mnd_efree(request); } if (ret == FAIL) { stmt->error_info = conn->error_info; - return FAIL; + DBG_INF("FAIL"); + DBG_RETURN(FAIL); } + stmt->execute_count++; ret = mysqlnd_query_read_result_set_header(stmt->conn, stmt TSRMLS_CC); if (ret == FAIL) { @@ -439,9 +467,11 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) stmt->state = MYSQLND_STMT_EXECUTED; if (conn->last_query_type == QUERY_UPSERT) { stmt->upsert_status = conn->upsert_status; - return PASS; + DBG_INF("PASS"); + DBG_RETURN(PASS); } else if (conn->last_query_type == QUERY_LOAD_LOCAL) { - return PASS; + DBG_INF("PASS"); + DBG_RETURN(PASS); } stmt->result->type = MYSQLND_RES_PS_BUF; @@ -463,14 +493,15 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) use_result() or store_result() and we should be able to scrap the data on the line, if he just decides to close the statement. */ -#ifndef MYSQLND_SILENT - php_printf("server_status=%d cursor=%d\n", stmt->upsert_status.server_status, stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS); -#endif + DBG_INF_FMT("server_status=%d cursor=%d\n", stmt->upsert_status.server_status, + stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS); + if (stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS) { stmt->cursor_exists = TRUE; conn->state = CONN_READY; /* Only cursor read */ stmt->default_rset_handler = stmt->m->use_result; + DBG_INF("use_result"); } else if (stmt->flags & CURSOR_TYPE_READ_ONLY) { /* We have asked for CURSOR but got no cursor, because the condition @@ -484,14 +515,17 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) */ /* preferred is buffered read */ stmt->default_rset_handler = stmt->m->store_result; + DBG_INF("store_result"); } else { /* preferred is unbuffered read */ stmt->default_rset_handler = stmt->m->use_result; + DBG_INF("use_result"); } } } - return ret; + DBG_INF(ret == PASS? "PASS":"FAIL"); + DBG_RETURN(ret); } /* }}} */ @@ -504,6 +538,9 @@ mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int f unsigned int i; MYSQLND_STMT *stmt = (MYSQLND_STMT *) param; + DBG_ENTER("mysqlnd_fetch_stmt_row_buffered"); + DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + /* If we haven't read everything */ if (result->data->data_cursor && (result->data->data_cursor - result->data->data) < result->data->row_count) @@ -518,6 +555,7 @@ mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int f #endif /* copy the type */ if (stmt->result_bind[i].bound == TRUE) { + DBG_INF_FMT("i=%d type=%d", i, Z_TYPE_P(current_row[i])); if (Z_TYPE_P(current_row[i]) != IS_NULL) { /* Copy the value. @@ -540,20 +578,22 @@ mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int f } result->data->data_cursor++; *fetched_anything = TRUE; + /* buffered result sets don't have a connection */ + MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_PS_BUF); + DBG_INF("row fetched"); } else { result->data->data_cursor = NULL; *fetched_anything = FALSE; -#ifndef MYSQLND_SILENT - php_printf("NO MORE DATA\n "); -#endif + DBG_INF("no more data"); } - return PASS; + DBG_INF("PASS"); + DBG_RETURN(PASS); } /* }}} */ /* {{{ mysqlnd_stmt_fetch_row_unbuffered */ -enum_func_status +static enum_func_status mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) { @@ -562,15 +602,19 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int unsigned int i, field_count = result->field_count; php_mysql_packet_row *row_packet = result->row_packet; + DBG_ENTER("mysqlnd_stmt_fetch_row_unbuffered"); + if (result->unbuf->eof_reached) { /* No more rows obviously */ *fetched_anything = FALSE; - return PASS; + DBG_INF("eof reached"); + DBG_RETURN(PASS); } if (result->conn->state != CONN_FETCHING_DATA) { SET_CLIENT_ERROR(result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, - UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); - return FAIL; + UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); + DBG_ERR("command out of sync"); + DBG_RETURN(FAIL); } /* Let the row packet fill our buffer and skip additional malloc + memcpy */ row_packet->skip_extraction = stmt && stmt->result_bind? FALSE:TRUE; @@ -586,6 +630,7 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int if (!row_packet->skip_extraction) { mysqlnd_unbuffered_free_last_data(result TSRMLS_CC); + DBG_INF("extracting data"); result->unbuf->last_row_data = row_packet->fields; result->unbuf->last_row_buffer = row_packet->row_buffer; row_packet->fields = NULL; @@ -620,14 +665,16 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int } } } + MYSQLND_INC_CONN_STATISTIC(&stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_UNBUF); } else { + DBG_INF("skipping extraction"); /* Data has been allocated and usually mysqlnd_unbuffered_free_last_data() frees it but we can't call this function as it will cause problems with the bound variables. Thus we need to do part of what it does or Zend will report leaks. */ - efree(row_packet->row_buffer); + mnd_efree(row_packet->row_buffer); row_packet->row_buffer = NULL; } } else if (ret == FAIL) { @@ -639,6 +686,7 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int result->conn->state = CONN_READY; result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */ } else if (row_packet->eof) { + DBG_INF("EOF"); /* Mark the connection as usable again */ result->unbuf->eof_reached = TRUE; result->conn->upsert_status.warning_count = row_packet->warning_count; @@ -655,26 +703,31 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int *fetched_anything = FALSE; } - return ret; + DBG_INF_FMT("ret=%s fetched_anything=%d", ret == PASS? "PASS":"FAIL", *fetched_anything); + DBG_RETURN(ret); } /* }}} */ /* {{{ mysqlnd_stmt::use_result */ -MYSQLND_RES * +static MYSQLND_RES * MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT *stmt TSRMLS_DC) { MYSQLND_RES *result; MYSQLND *conn = stmt->conn; + DBG_ENTER("mysqlnd_stmt::use_result"); + DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + if (!stmt->field_count || (!stmt->cursor_exists && conn->state != CONN_FETCHING_DATA) || (stmt->cursor_exists && conn->state != CONN_READY) || (stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE)) { SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, - UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); - return NULL; + UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); + DBG_ERR("command out of sync"); + DBG_RETURN(NULL); } SET_EMPTY_ERROR(stmt->error_info); @@ -683,14 +736,15 @@ MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT *stmt TSRMLS_DC) MYSQLND_INC_CONN_STATISTIC(&stmt->conn->stats, STAT_PS_UNBUFFERED_SETS); result = stmt->result; - result->type = MYSQLND_RES_PS_UNBUF; + result->type = MYSQLND_RES_PS_UNBUF; result->m.fetch_row = stmt->cursor_exists? mysqlnd_fetch_stmt_row_cursor: mysqlnd_stmt_fetch_row_unbuffered; result->m.fetch_lengths = NULL; /* makes no sense */ result->zval_cache = mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache); - result->unbuf = ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); + result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); + DBG_INF_FMT("cursor=%d zval_cache=%p", stmt->cursor_exists, result->zval_cache); /* Will be freed in the mysqlnd_internal_free_result_contents() called by the resource destructor. mysqlnd_fetch_row_unbuffered() expects @@ -708,7 +762,8 @@ MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT *stmt TSRMLS_DC) /* No multithreading issues as we don't share the connection :) */ - return result; + DBG_INF_FMT("%p", result); + DBG_RETURN(result); } /* }}} */ @@ -725,15 +780,20 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla zend_uchar buf[STMT_ID_LENGTH /* statement id */ + 4 /* number of rows to fetch */]; php_mysql_packet_row *row_packet = result->row_packet; + DBG_ENTER("mysqlnd_fetch_stmt_row_cursor"); + DBG_INF_FMT("stmt=%lu flags=%u", stmt->stmt_id, flags); + if (!stmt) { - return FAIL; + DBG_ERR("no statement"); + DBG_RETURN(FAIL); } if (stmt->state < MYSQLND_STMT_USER_FETCHING) { /* Only initted - error */ SET_CLIENT_ERROR(stmt->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); - return FAIL; + DBG_ERR("command out of sync"); + DBG_RETURN(FAIL); } SET_EMPTY_ERROR(stmt->error_info); @@ -746,7 +806,7 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla PROT_LAST /* we will handle the response packet*/, FALSE TSRMLS_CC)) { stmt->error_info = stmt->conn->error_info; - return FAIL; + DBG_RETURN(FAIL); } row_packet->skip_extraction = stmt->result_bind? FALSE:TRUE; @@ -768,7 +828,7 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla stmt->result_bind[i].zv has been already destructed in mysqlnd_unbuffered_free_last_data() */ - + DBG_INF_FMT("i=%d type=%d", i, Z_TYPE_P(stmt->result_bind[i].zv)); if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data)) ) { stmt->result_bind[i].zv->value = data->value; #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF @@ -792,9 +852,10 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla /* We asked for one row, the next one should be EOF, eat it */ ret = PACKET_READ(row_packet, result->conn); if (row_packet->row_buffer) { - efree(row_packet->row_buffer); + mnd_efree(row_packet->row_buffer); row_packet->row_buffer = NULL; } + MYSQLND_INC_CONN_STATISTIC(&stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_CURSOR); } else { *fetched_anything = FALSE; @@ -815,21 +876,30 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla stmt->conn->upsert_status.server_status = row_packet->server_status; - return ret; + DBG_INF_FMT("ret=%s fetched=%d s_status=%d warns=%d eof=%d", + ret == PASS? "PASS":"FAIL", *fetched_anything, + row_packet->server_status, row_packet->warning_count, + result->unbuf->eof_reached); + DBG_RETURN(ret); } /* }}} */ -/* {{{ mysqlnd_stmt_fetch */ +/* {{{ mysqlnd_stmt::fetch */ PHPAPI enum_func_status MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const stmt, zend_bool * const fetched_anything TSRMLS_DC) { + enum_func_status ret; + DBG_ENTER("mysqlnd_stmt::fetch"); + DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + if (!stmt->result || stmt->state < MYSQLND_STMT_WAITING_USE_OR_STORE) { SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); - - return FAIL; + + DBG_ERR("command out of sync"); + DBG_RETURN(FAIL); } else if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { /* Execute only once. We have to free the previous contents of user's bound vars */ @@ -840,6 +910,7 @@ MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const stmt, SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); + DBG_INF_FMT("result_bind=%p separated_once=%d", stmt->result_bind, stmt->result_zvals_separated_once); /* The user might have not bound any variables for result. Do the binding once she does it. @@ -859,9 +930,8 @@ MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const stmt, stmt->result_zvals_separated_once = TRUE; } - MYSQLND_INC_CONN_STATISTIC(&stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT); - - return stmt->result->m.fetch_row(stmt->result, (void*)stmt, 0, fetched_anything TSRMLS_CC); + ret = stmt->result->m.fetch_row(stmt->result, (void*)stmt, 0, fetched_anything TSRMLS_CC); + DBG_RETURN(ret); } /* }}} */ @@ -874,12 +944,16 @@ MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const stmt TSRMLS_DC) MYSQLND * conn = stmt->conn; zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */]; + DBG_ENTER("mysqlnd_stmt::reset"); + DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); if (stmt->stmt_id) { if (stmt->param_bind) { unsigned int i; + DBG_INF("resetting long data"); /* Reset Long Data */ for (i = 0; i < stmt->param_count; i++) { if (stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED) { @@ -894,11 +968,13 @@ MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const stmt TSRMLS_DC) clean. */ if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { + DBG_INF("fetching result set header"); stmt->default_rset_handler(stmt TSRMLS_CC); stmt->state = MYSQLND_STMT_USER_FETCHING; } if (stmt->result) { + DBG_INF("skipping result"); stmt->result->m.skip_result(stmt->result TSRMLS_CC); } /* Now the line should be free, if it wasn't */ @@ -914,7 +990,8 @@ MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const stmt TSRMLS_DC) stmt->state = MYSQLND_STMT_PREPARED; } - return ret; + DBG_INF(ret == PASS? "PASS":"FAIL"); + DBG_RETURN(ret); } /* }}} */ @@ -930,25 +1007,32 @@ MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const stmt, unsigned size_t packet_len; enum php_mysqlnd_server_command cmd = COM_STMT_SEND_LONG_DATA; + DBG_ENTER("mysqlnd_stmt::send_long_data"); + DBG_INF_FMT("stmt=%lu param_no=%d data_len=%lu", stmt->stmt_id, param_no, length); + SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); if (stmt->state < MYSQLND_STMT_PREPARED) { SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared); - return FAIL; + DBG_ERR("not prepared"); + DBG_RETURN(FAIL); } if (!stmt->param_bind) { SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); - return FAIL; + DBG_ERR("command out of sync"); + DBG_RETURN(FAIL); } if (param_no >= stmt->param_count) { SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number"); - return FAIL; + DBG_ERR("invalid param_no"); + DBG_RETURN(FAIL); } if (stmt->param_bind[param_no].type != MYSQL_TYPE_LONG_BLOB) { SET_STMT_ERROR(stmt, CR_INVALID_BUFFER_USE, UNKNOWN_SQLSTATE, mysqlnd_not_bound_as_blob); - return FAIL; + DBG_ERR("param_no is not of a blob type"); + DBG_RETURN(FAIL); } /* @@ -963,7 +1047,7 @@ MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const stmt, unsigned if (conn->state == CONN_READY) { stmt->param_bind[param_no].flags |= MYSQLND_PARAM_BIND_BLOB_USED; - cmd_buf = emalloc(packet_len = STMT_ID_LENGTH + 2 + length); + cmd_buf = mnd_emalloc(packet_len = STMT_ID_LENGTH + 2 + length); int4store(cmd_buf, stmt->stmt_id); int2store(cmd_buf + STMT_ID_LENGTH, param_no); @@ -972,7 +1056,7 @@ MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const stmt, unsigned /* COM_STMT_SEND_LONG_DATA doesn't send an OK packet*/ ret = mysqlnd_simple_command(conn, cmd, (char *)cmd_buf, packet_len, PROT_LAST , FALSE TSRMLS_CC); - efree(cmd_buf); + mnd_efree(cmd_buf); if (FAIL == ret) { stmt->error_info = conn->error_info; } @@ -1008,7 +1092,8 @@ MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const stmt, unsigned #endif } - return ret; + DBG_INF(ret == PASS? "PASS":"FAIL"); + DBG_RETURN(ret); } /* }}} */ @@ -1016,13 +1101,17 @@ MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const stmt, unsigned /* {{{ _mysqlnd_stmt_bind_param */ static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, bind_param)(MYSQLND_STMT * const stmt, - MYSQLND_PARAM_BIND * const param_bind) + MYSQLND_PARAM_BIND * const param_bind TSRMLS_DC) { unsigned int i = 0; + DBG_ENTER("mysqlnd_stmt::bind_param"); + DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count); + if (stmt->state < MYSQLND_STMT_PREPARED) { SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared); - return FAIL; + DBG_ERR("not prepared"); + DBG_RETURN(FAIL); } SET_EMPTY_ERROR(stmt->error_info); @@ -1032,8 +1121,10 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_param)(MYSQLND_STMT * const stmt, if (!param_bind) { SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, "Re-binding (still) not supported"); - return FAIL; + DBG_ERR("Re-binding (still) not supported"); + DBG_RETURN(FAIL); } else if (stmt->param_bind) { + DBG_INF("Binding"); /* There is already result bound. Forbid for now re-binding!! @@ -1049,12 +1140,13 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_param)(MYSQLND_STMT * const stmt, stmt->param_bind[i].zv = NULL; } } - efree(stmt->param_bind); + mnd_efree(stmt->param_bind); } stmt->param_bind = param_bind; for (i = 0; i < stmt->param_count; i++) { /* The client will use stmt_send_long_data */ + DBG_INF_FMT("%d is of type %d", i, stmt->param_bind[i].type); if (stmt->param_bind[i].type != MYSQL_TYPE_LONG_BLOB) { /* Prevent from freeing */ ZVAL_ADDREF(stmt->param_bind[i].zv); @@ -1066,7 +1158,8 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_param)(MYSQLND_STMT * const stmt, } stmt->send_types_to_server = 1; } - return PASS; + DBG_INF("PASS"); + DBG_RETURN(PASS); } /* }}} */ @@ -1074,27 +1167,32 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_param)(MYSQLND_STMT * const stmt, /* {{{ mysqlnd_stmt::bind_result */ static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt, - MYSQLND_RESULT_BIND * const result_bind) + MYSQLND_RESULT_BIND * const result_bind TSRMLS_DC) { uint i = 0; + DBG_ENTER("mysqlnd_stmt::bind_result"); + DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count); + SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); if (stmt->state < MYSQLND_STMT_PREPARED) { SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared); if (result_bind) { - efree(result_bind); + mnd_efree(result_bind); } - return FAIL; + DBG_ERR("not prepared"); + DBG_RETURN(FAIL); } if (stmt->field_count) { if (!result_bind) { - return FAIL; + DBG_ERR("no result bind passed"); + DBG_RETURN(FAIL); } - mysqlnd_stmt_separate_result_bind(stmt); + mysqlnd_stmt_separate_result_bind(stmt TSRMLS_CC); stmt->result_bind = result_bind; for (i = 0; i < stmt->field_count; i++) { @@ -1108,9 +1206,10 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt, stmt->result_bind[i].bound = TRUE; } } else if (result_bind) { - efree(result_bind); + mnd_efree(result_bind); } - return PASS; + DBG_INF("PASS"); + DBG_RETURN(PASS); } /* }}} */ @@ -1198,9 +1297,9 @@ MYSQLND_METHOD(mysqlnd_stmt, sqlstate)(const MYSQLND_STMT * const stmt) /* {{{ mysqlnd_stmt::data_seek */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, data_seek)(const MYSQLND_STMT * const stmt, mynd_ulonglong row) +MYSQLND_METHOD(mysqlnd_stmt, data_seek)(const MYSQLND_STMT * const stmt, mynd_ulonglong row TSRMLS_DC) { - return stmt->result? stmt->result->m.seek_data(stmt->result, row) : FAIL; + return stmt->result? stmt->result->m.seek_data(stmt->result, row TSRMLS_CC) : FAIL; } /* }}} */ @@ -1220,14 +1319,18 @@ MYSQLND_METHOD(mysqlnd_stmt, param_metadata)(MYSQLND_STMT * const stmt) /* {{{ mysqlnd_stmt::result_metadata */ static MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const stmt) +MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const stmt TSRMLS_DC) { MYSQLND_RES *result; + DBG_ENTER("mysqlnd_stmt::result_metadata"); + DBG_INF_FMT("stmt=%u field_count=%u", stmt->stmt_id, stmt->field_count); + if (!stmt->field_count || !stmt->conn || !stmt->result || !stmt->result->meta) { - return NULL; + DBG_INF("NULL"); + DBG_RETURN(NULL); } /* @@ -1239,14 +1342,15 @@ MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const stmt) In the meantime we don't need a zval cache reference for this fake result set, so we don't get one. */ - result = mysqlnd_result_init(stmt->field_count, NULL); + result = mysqlnd_result_init(stmt->field_count, NULL TSRMLS_CC); result->type = MYSQLND_RES_NORMAL; result->m.fetch_row = result->m.fetch_row_normal_unbuffered; - result->unbuf = ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); + result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); result->unbuf->eof_reached = TRUE; - result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE); + result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC); - return result; + DBG_INF_FMT("result=%p", result); + DBG_RETURN(result); } /* }}} */ @@ -1255,9 +1359,12 @@ MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const stmt) static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, - const void * const value) + const void * const value TSRMLS_DC) { unsigned long val = *(unsigned long *) value; + DBG_ENTER("mysqlnd_stmt::attr_set"); + DBG_INF_FMT("stmt=%lu attr_type=%u value=%lu", stmt->stmt_id, attr_type, val); + switch (attr_type) { case STMT_ATTR_UPDATE_MAX_LENGTH: /* @@ -1286,9 +1393,10 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const stmt, } default: SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented"); - return FAIL; + DBG_RETURN(FAIL); } - return PASS; + DBG_INF("PASS"); + DBG_RETURN(PASS); } /* }}} */ @@ -1297,8 +1405,11 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const stmt, static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, attr_get)(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, - void * const value) + void * const value TSRMLS_DC) { + DBG_ENTER("mysqlnd_stmt::attr_set"); + DBG_INF_FMT("stmt=%lu attr_type=%u", stmt->stmt_id, attr_type); + switch (attr_type) { case STMT_ATTR_UPDATE_MAX_LENGTH: *(zend_bool *) value= stmt->update_max_length; @@ -1310,9 +1421,10 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_get)(MYSQLND_STMT * const stmt, *(unsigned long *) value= stmt->prefetch_rows; break; default: - return FAIL; + DBG_RETURN(FAIL); } - return PASS; + DBG_INF_FMT("value=%lu", value); + DBG_RETURN(PASS); } /* }}} */ @@ -1321,24 +1433,30 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_get)(MYSQLND_STMT * const stmt, static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC) { + DBG_ENTER("mysqlnd_stmt::free_result"); + DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + if (!stmt->result) { - return PASS; + DBG_INF("no result"); + DBG_RETURN(PASS); } if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { + DBG_INF("fetching result set header"); /* Do implicit use_result and then flush the result */ stmt->default_rset_handler = stmt->m->use_result; stmt->default_rset_handler(stmt TSRMLS_CC); } if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) { + DBG_INF("skipping result"); /* Flush if anything is left and unbuffered set */ stmt->result->m.skip_result(stmt->result TSRMLS_CC); /* Separate the bound variables, which point to the result set, then destroy the set. */ - mysqlnd_stmt_separate_result_bind(stmt); + mysqlnd_stmt_separate_result_bind(stmt TSRMLS_CC); /* Now we can destroy the result set */ stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC); @@ -1350,18 +1468,22 @@ MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC) /* Line is free! */ stmt->conn->state = CONN_READY; - return PASS; + DBG_RETURN(PASS); } /* }}} */ /* {{{ mysqlnd_stmt_separate_result_bind */ -void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt) +void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC) { - int i; + unsigned int i; + + DBG_ENTER("mysqlnd_stmt_separate_result_bind"); + DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u", + stmt->stmt_id, stmt->result_bind, stmt->field_count); if (!stmt->result_bind) { - return; + DBG_VOID_RETURN; } /* @@ -1372,6 +1494,7 @@ void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt) for (i = 0; i < stmt->field_count; i++) { /* Let's try with no cache */ if (stmt->result_bind[i].bound == TRUE) { + DBG_INF_FMT("%d has refcount=%u", i, ZVAL_REFCOUNT(stmt->result_bind[i].zv)); /* We have to separate the actual zval value of the bound variable from our allocated zvals or we will face double-free @@ -1394,8 +1517,10 @@ void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt) } } } - efree(stmt->result_bind); + mnd_efree(stmt->result_bind); stmt->result_bind = NULL; + + DBG_VOID_RETURN; } /* }}} */ @@ -1404,6 +1529,10 @@ void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt) static void mysqlnd_internal_free_stmt_content(MYSQLND_STMT *stmt TSRMLS_DC) { + DBG_ENTER("mysqlnd_internal_free_stmt_content"); + DBG_INF_FMT("stmt=%lu param_bind=%p param_count=%u", + stmt->stmt_id, stmt->param_bind, stmt->param_count); + /* Destroy the input bind */ if (stmt->param_bind) { int i; @@ -1419,7 +1548,7 @@ void mysqlnd_internal_free_stmt_content(MYSQLND_STMT *stmt TSRMLS_DC) } } - efree(stmt->param_bind); + mnd_efree(stmt->param_bind); stmt->param_bind = NULL; } @@ -1427,14 +1556,14 @@ void mysqlnd_internal_free_stmt_content(MYSQLND_STMT *stmt TSRMLS_DC) First separate the bound variables, which point to the result set, then destroy the set. */ - mysqlnd_stmt_separate_result_bind(stmt); + mysqlnd_stmt_separate_result_bind(stmt TSRMLS_CC); /* Not every statement has a result set attached */ if (stmt->result) { stmt->result->m.free_result_internal(stmt->result TSRMLS_CC); stmt->result = NULL; } if (stmt->cmd_buffer.buffer) { - efree(stmt->cmd_buffer.buffer); + mnd_efree(stmt->cmd_buffer.buffer); stmt->cmd_buffer.buffer = NULL; } @@ -1442,6 +1571,8 @@ void mysqlnd_internal_free_stmt_content(MYSQLND_STMT *stmt TSRMLS_DC) stmt->conn->m->free_reference(stmt->conn TSRMLS_CC); stmt->conn = NULL; } + + DBG_VOID_RETURN; } /* }}} */ @@ -1452,6 +1583,10 @@ MYSQLND_METHOD(mysqlnd_stmt, close)(MYSQLND_STMT * const stmt, zend_bool implici { MYSQLND * conn = stmt->conn; zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */]; + enum_mysqlnd_collected_stats stat = STAT_LAST; + + DBG_ENTER("mysqlnd_stmt::close"); + DBG_INF_FMT("stmt=%lu", stmt->stmt_id); SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); @@ -1462,21 +1597,23 @@ MYSQLND_METHOD(mysqlnd_stmt, close)(MYSQLND_STMT * const stmt, zend_bool implici clean. */ if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { + DBG_INF("fetching result set header"); stmt->default_rset_handler(stmt TSRMLS_CC); stmt->state = MYSQLND_STMT_USER_FETCHING; } /* unbuffered set not fetched to the end ? Clean the line */ if (stmt->result) { + DBG_INF("skipping result"); stmt->result->m.skip_result(stmt->result TSRMLS_CC); } /* After this point we are allowed to free the result set, as we have cleaned the line */ - if (stmt->stmt_id) { - MYSQLND_INC_CONN_STATISTIC(NULL, implicit == TRUE? STAT_FREE_RESULT_IMPLICIT: - STAT_FREE_RESULT_EXPLICIT); + if (stmt->stmt_id) { + MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE? STAT_FREE_RESULT_IMPLICIT: + STAT_FREE_RESULT_EXPLICIT); int4store(cmd_buf, stmt->stmt_id); if (conn->state == CONN_READY && @@ -1484,13 +1621,26 @@ MYSQLND_METHOD(mysqlnd_stmt, close)(MYSQLND_STMT * const stmt, zend_bool implici PROT_LAST /* COM_STMT_CLOSE doesn't send an OK packet*/, FALSE TSRMLS_CC)) { stmt->error_info = conn->error_info; - return FAIL; + DBG_RETURN(FAIL); } } + switch (stmt->execute_count) { + case 0: + stat = STAT_PS_PREPARED_NEVER_EXECUTED; + break; + case 1: + stat = STAT_PS_PREPARED_ONCE_USED; + break; + default: + break; + } + if (stat != STAT_LAST) { + MYSQLND_INC_CONN_STATISTIC(&conn->stats, stat); + } mysqlnd_internal_free_stmt_content(stmt TSRMLS_CC); - return PASS; + DBG_RETURN(PASS); } /* }}} */ @@ -1501,14 +1651,18 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, dtor)(MYSQLND_STMT * const stmt, zend_bool { enum_func_status ret; - MYSQLND_INC_CONN_STATISTIC(NULL, implicit == TRUE? STAT_STMT_CLOSE_IMPLICIT: - STAT_STMT_CLOSE_EXPLICIT); + DBG_ENTER("mysqlnd_stmt::close"); + DBG_INF_FMT("stmt=%p", stmt); + + MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE? STAT_STMT_CLOSE_IMPLICIT: + STAT_STMT_CLOSE_EXPLICIT); if (PASS == (ret = stmt->m->close(stmt, implicit TSRMLS_CC))) { - efree(stmt); + mnd_efree(stmt); } - return ret; + DBG_INF(ret == PASS? "PASS":"FAIL"); + DBG_RETURN(ret); } /* }}} */ @@ -1552,14 +1706,17 @@ struct st_mysqlnd_stmt_methods mysqlnd_stmt_methods = { /* {{{ _mysqlnd_stmt_init */ -MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn) +MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC) { - MYSQLND_STMT *stmt = ecalloc(1, sizeof(MYSQLND_STMT)); + MYSQLND_STMT *stmt = mnd_ecalloc(1, sizeof(MYSQLND_STMT)); + + DBG_ENTER("_mysqlnd_stmt_init"); + DBG_INF_FMT("stmt=%p", stmt); stmt->m = &mysqlnd_stmt_methods; stmt->state = MYSQLND_STMT_INITTED; stmt->cmd_buffer.length = 4096; - stmt->cmd_buffer.buffer = emalloc(stmt->cmd_buffer.length); + stmt->cmd_buffer.buffer = mnd_emalloc(stmt->cmd_buffer.length); stmt->prefetch_rows = MYSQLND_DEFAULT_PREFETCH_ROWS; /* @@ -1569,7 +1726,7 @@ MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn) */ stmt->conn = conn->m->get_reference(conn); - return stmt; + DBG_RETURN(stmt); } /* }}} */ diff --git a/ext/mysqlnd/mysqlnd_ps_codec.c b/ext/mysqlnd/mysqlnd_ps_codec.c index 993873f4b2..ea04cfb5fa 100644 --- a/ext/mysqlnd/mysqlnd_ps_codec.c +++ b/ext/mysqlnd/mysqlnd_ps_codec.c @@ -23,7 +23,7 @@ #include "mysqlnd.h" #include "mysqlnd_wireprotocol.h" #include "mysqlnd_priv.h" - +#include "mysqlnd_debug.h" #define MYSQLND_SILENT @@ -205,7 +205,7 @@ void ps_fetch_int32(zval *zv, const MYSQLND_FIELD * const field, if (uval > INT_MAX) { char *tmp, *p; int j=10; - tmp= emalloc(11); + tmp= mnd_emalloc(11); p= &tmp[9]; do { *p-- = (uval % 10) + 48; @@ -354,7 +354,7 @@ void ps_fetch_time(zval *zv, const MYSQLND_FIELD * const field, if (!as_unicode) { #endif ZVAL_STRINGL(zv, to, length, 1); - efree(to); + mnd_efree(to); #if PHP_MAJOR_VERSION >= 6 } else { ZVAL_UTF8_STRINGL(zv, to, length, ZSTR_AUTOFREE); @@ -402,7 +402,7 @@ void ps_fetch_date(zval *zv, const MYSQLND_FIELD * const field, if (!as_unicode) { #endif ZVAL_STRINGL(zv, to, length, 1); - efree(to); + mnd_efree(to); #if PHP_MAJOR_VERSION >= 6 } else { ZVAL_UTF8_STRINGL(zv, to, length, ZSTR_AUTOFREE); @@ -458,7 +458,7 @@ void ps_fetch_datetime(zval *zv, const MYSQLND_FIELD * const field, if (!as_unicode) { #endif ZVAL_STRINGL(zv, to, length, 1); - efree(to); + mnd_efree(to); #if PHP_MAJOR_VERSION >= 6 } else { ZVAL_UTF8_STRINGL(zv, to, length, ZSTR_AUTOFREE); @@ -653,7 +653,7 @@ void _mysqlnd_init_ps_subsystem() /* {{{ mysqlnd_stmt_execute_store_params */ void mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uchar **p, - size_t *buf_len, unsigned int null_byte_offset) + size_t *buf_len, unsigned int null_byte_offset TSRMLS_DC) { unsigned int i = 0; unsigned left = (*buf_len - (*p - *buf)); @@ -667,7 +667,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uch unsigned int offset = *p - *buf; zend_uchar *tmp_buf; *buf_len = offset + stmt->param_count * 2 + 20; - tmp_buf = emalloc(*buf_len); + tmp_buf = mnd_emalloc(*buf_len); memcpy(tmp_buf, *buf, offset); *buf = tmp_buf; @@ -734,7 +734,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uch unsigned int offset = *p - *buf; zend_uchar *tmp_buf; *buf_len = offset + data_size + 10; /* Allocate + 10 for safety */ - tmp_buf = emalloc(*buf_len); + tmp_buf = mnd_emalloc(*buf_len); memcpy(tmp_buf, *buf, offset); *buf = tmp_buf; /* Update our pos pointer */ @@ -805,7 +805,8 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uch /* {{{ mysqlnd_stmt_execute_generate_request */ -zend_uchar* mysqlnd_stmt_execute_generate_request(MYSQLND_STMT *stmt, size_t *request_len, zend_bool *free_buffer) +zend_uchar* mysqlnd_stmt_execute_generate_request(MYSQLND_STMT *stmt, size_t *request_len, + zend_bool *free_buffer TSRMLS_DC) { zend_uchar *p = stmt->cmd_buffer.buffer, *cmd_buffer = stmt->cmd_buffer.buffer; @@ -835,7 +836,7 @@ zend_uchar* mysqlnd_stmt_execute_generate_request(MYSQLND_STMT *stmt, size_t *re int1store(p, stmt->send_types_to_server); p++; - mysqlnd_stmt_execute_store_params(stmt, &cmd_buffer, &p, &cmd_buffer_length, null_byte_offset); + mysqlnd_stmt_execute_store_params(stmt, &cmd_buffer, &p, &cmd_buffer_length, null_byte_offset TSRMLS_CC); *free_buffer = (cmd_buffer != stmt->cmd_buffer.buffer); *request_len = (p - cmd_buffer); diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c index 1eaed84180..2d42d65469 100644 --- a/ext/mysqlnd/mysqlnd_result.c +++ b/ext/mysqlnd/mysqlnd_result.c @@ -27,6 +27,7 @@ #include "mysqlnd_result_meta.h" #include "mysqlnd_statistics.h" #include "mysqlnd_charset.h" +#include "mysqlnd_debug.h" #include "ext/standard/basic_functions.h" #define MYSQLND_SILENT @@ -36,8 +37,11 @@ void mysqlnd_unbuffered_free_last_data(MYSQLND_RES *result TSRMLS_DC) { MYSQLND_RES_UNBUFFERED *unbuf = result->unbuf; + + DBG_ENTER("mysqlnd_unbuffered_free_last_data"); + if (!unbuf) { - return; + DBG_VOID_RETURN; } if (unbuf->last_row_data) { @@ -69,6 +73,8 @@ void mysqlnd_unbuffered_free_last_data(MYSQLND_RES *result TSRMLS_DC) efree(unbuf->last_row_buffer); unbuf->last_row_buffer = NULL; } + + DBG_VOID_RETURN; } /* }}} */ @@ -80,6 +86,10 @@ void mysqlnd_free_buffered_data(MYSQLND_RES *result TSRMLS_DC) unsigned int field_count = result->field_count; unsigned int row; + DBG_ENTER("mysqlnd_free_buffered_data"); + DBG_INF_FMT("Freeing "MYSQLND_LLU_SPEC" row(s)", result->data->row_count); + + DBG_INF_FMT("before: real_usage=%lu usage=%lu", zend_memory_usage(TRUE TSRMLS_CC), zend_memory_usage(FALSE TSRMLS_CC)); for (row = 0; row < result->data->row_count; row++) { unsigned int col; zval **current_row = current_row = set->data[row]; @@ -89,12 +99,19 @@ void mysqlnd_free_buffered_data(MYSQLND_RES *result TSRMLS_DC) zend_bool copy_ctor_called; mysqlnd_palloc_zval_ptr_dtor(&(current_row[col]), zval_cache, result->type, ©_ctor_called TSRMLS_CC); - MYSQLND_INC_CONN_STATISTIC(NULL, copy_ctor_called? STAT_COPY_ON_WRITE_PERFORMED: - STAT_COPY_ON_WRITE_SAVED); +#if MYSQLND_DEBUG_MEMORY + DBG_INF_FMT("Copy_ctor_called=%d", copy_ctor_called); +#endif + MYSQLND_INC_GLOBAL_STATISTIC(copy_ctor_called? STAT_COPY_ON_WRITE_PERFORMED: + STAT_COPY_ON_WRITE_SAVED); } +#if MYSQLND_DEBUG_MEMORY + DBG_INF("Freeing current_row & current_buffer"); +#endif pefree(current_row, set->persistent); pefree(current_buffer, set->persistent); } + DBG_INF("Freeing data & row_buffer"); pefree(set->data, set->persistent); pefree(set->row_buffers, set->persistent); set->data = NULL; @@ -104,7 +121,11 @@ void mysqlnd_free_buffered_data(MYSQLND_RES *result TSRMLS_DC) if (set->qcache) { mysqlnd_qcache_free_cache_reference(&set->qcache); } + DBG_INF("Freeing set"); pefree(set, set->persistent); + + DBG_INF_FMT("after: real_usage=%lu usage=%lu", zend_memory_usage(TRUE TSRMLS_CC), zend_memory_usage(FALSE TSRMLS_CC)); + DBG_VOID_RETURN; } /* }}} */ @@ -113,6 +134,8 @@ void mysqlnd_free_buffered_data(MYSQLND_RES *result TSRMLS_DC) void MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES *result TSRMLS_DC) { + DBG_ENTER("mysqlnd_res::free_result_buffers"); + DBG_INF_FMT("%s", result->unbuf? "unbuffered":(result->data? "buffered":"unknown")); if (result->unbuf) { mysqlnd_unbuffered_free_last_data(result TSRMLS_CC); @@ -127,6 +150,8 @@ MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES *result TSRMLS_DC) efree(result->lengths); result->lengths = NULL; } + + DBG_VOID_RETURN; } /* }}} */ @@ -135,9 +160,12 @@ MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES *result TSRMLS_DC) static void mysqlnd_internal_free_result_contents(MYSQLND_RES *result TSRMLS_DC) { + DBG_ENTER("mysqlnd_internal_free_result_contents"); + result->m.free_result_buffers(result TSRMLS_CC); if (result->row_packet) { + DBG_INF("Freeing packet"); PACKET_FREE(result->row_packet); result->row_packet = NULL; } @@ -150,9 +178,12 @@ void mysqlnd_internal_free_result_contents(MYSQLND_RES *result TSRMLS_DC) } if (result->zval_cache) { + DBG_INF("Freeing zval cache reference"); mysqlnd_palloc_free_thd_cache_reference(&result->zval_cache); result->zval_cache = NULL; } + + DBG_VOID_RETURN; } /* }}} */ @@ -161,6 +192,7 @@ void mysqlnd_internal_free_result_contents(MYSQLND_RES *result TSRMLS_DC) static void mysqlnd_internal_free_result(MYSQLND_RES *result TSRMLS_DC) { + DBG_ENTER("mysqlnd_internal_free_result"); /* result->conn is an address if this is an unbuffered query. In this case, decrement the reference counter in the connection @@ -173,6 +205,8 @@ void mysqlnd_internal_free_result(MYSQLND_RES *result TSRMLS_DC) result->m.free_result_contents(result TSRMLS_CC); efree(result); + + DBG_VOID_RETURN; } /* }}} */ @@ -181,6 +215,8 @@ void mysqlnd_internal_free_result(MYSQLND_RES *result TSRMLS_DC) static enum_func_status MYSQLND_METHOD(mysqlnd_res, read_result_metadata)(MYSQLND_RES *result, MYSQLND *conn TSRMLS_DC) { + DBG_ENTER("mysqlnd_res::read_result_metadata"); + /* Make it safe to call it repeatedly for PS - better free and allocate a new because the number of field might change @@ -192,14 +228,14 @@ MYSQLND_METHOD(mysqlnd_res, read_result_metadata)(MYSQLND_RES *result, MYSQLND * result->meta = NULL; } - result->meta = mysqlnd_result_meta_init(result->field_count); + result->meta = mysqlnd_result_meta_init(result->field_count TSRMLS_CC); /* 1. Read all fields metadata */ /* It's safe to reread without freeing */ if (FAIL == result->meta->m->read_metadata(result->meta, conn TSRMLS_CC)) { result->m.free_result_contents(result TSRMLS_CC); - return FAIL; + DBG_RETURN(FAIL); } /* @@ -209,7 +245,7 @@ MYSQLND_METHOD(mysqlnd_res, read_result_metadata)(MYSQLND_RES *result, MYSQLND * If PS, then no result set follows. */ - return PASS; + DBG_RETURN(PASS); } /* }}} */ @@ -221,6 +257,9 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC enum_func_status ret; php_mysql_packet_rset_header rset_header; + DBG_ENTER("mysqlnd_query_read_result_set_header"); + DBG_INF_FMT("stmt=%d", stmt? stmt->stmt_id:0); + ret = FAIL; PACKET_INIT_ALLOCA(rset_header, PROT_RSET_HEADER_PACKET); do { @@ -254,6 +293,7 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC switch (rset_header.field_count) { case MYSQLND_NULL_LENGTH: { /* LOAD DATA LOCAL INFILE */ zend_bool is_warning; + DBG_INF("LOAD DATA"); conn->last_query_type = QUERY_LOAD_LOCAL; conn->state = CONN_SENDING_LOAD_DATA; ret = mysqlnd_handle_local_infile(conn, rset_header.info_or_local_file, &is_warning TSRMLS_CC); @@ -262,6 +302,7 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC break; } case 0: /* UPSERT */ + DBG_INF("UPSERT"); conn->last_query_type = QUERY_UPSERT; conn->field_count = rset_header.field_count; conn->upsert_status.warning_count = rset_header.warning_count; @@ -283,8 +324,9 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC default:{ /* Result set */ php_mysql_packet_eof fields_eof; MYSQLND_RES *result; - uint stat = -1; + enum_mysqlnd_collected_stats stat = STAT_LAST; + DBG_INF("Result set pending"); SET_EMPTY_MESSAGE(conn->last_message, conn->last_message_len, conn->persistent); MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_RSET_QUERY); @@ -297,9 +339,11 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC result = conn->current_result= mysqlnd_result_init(rset_header.field_count, - mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache)); + mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache) + TSRMLS_CC); } else { if (!stmt->result) { + DBG_INF("This is 'SHOW'/'EXPLAIN'-like query."); /* This is 'SHOW'/'EXPLAIN'-like query. Current implementation of prepared statements can't send result set metadata for these queries @@ -309,7 +353,8 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC result = stmt->result = mysqlnd_result_init(rset_header.field_count, - mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache)); + mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache) + TSRMLS_CC); } else { /* Update result set metadata if it for some reason changed between @@ -333,12 +378,14 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC efree(conn->current_result); conn->current_result = NULL; } + DBG_ERR("Error ocurred while reading metadata"); break; } /* Check for SERVER_STATUS_MORE_RESULTS if needed */ PACKET_INIT_ALLOCA(fields_eof, PROT_EOF_PACKET); if (FAIL == (ret = PACKET_READ_ALLOCA(fields_eof, conn))) { + DBG_ERR("Error ocurred while reading the EOF packet"); result->m.free_result_contents(result TSRMLS_CC); efree(result); if (!stmt) { @@ -349,6 +396,7 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC stmt->state = MYSQLND_STMT_INITTED; } } else { + DBG_INF_FMT("warns=%u status=%u", fields_eof.warning_count, fields_eof.server_status); conn->upsert_status.warning_count = fields_eof.warning_count; conn->upsert_status.server_status = fields_eof.server_status; if (fields_eof.server_status & MYSQLND_SERVER_QUERY_NO_GOOD_INDEX_USED) { @@ -356,7 +404,12 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC } else if (fields_eof.server_status & MYSQLND_SERVER_QUERY_NO_INDEX_USED) { stat = STAT_NO_INDEX_USED; } - if (stat != -1) { + if (stat != STAT_LAST) { + char *backtrace = mysqlnd_get_backtrace(TSRMLS_C); +#if A0 + php_log_err(backtrace TSRMLS_CC); +#endif + efree(backtrace); MYSQLND_INC_CONN_STATISTIC(&conn->stats, stat); } } @@ -368,7 +421,9 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC } } while (0); PACKET_FREE_ALLOCA(rset_header); - return ret; + + DBG_INF(ret == PASS? "PASS":"FAIL"); + DBG_RETURN(ret); } /* }}} */ @@ -438,17 +493,20 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flag php_mysql_packet_row *row_packet = result->row_packet; unsigned long *lengths = result->lengths; + DBG_ENTER("mysqlnd_fetch_row_unbuffered"); + DBG_INF_FMT("flags=%d", flags); + if (result->unbuf->eof_reached) { /* No more rows obviously */ *fetched_anything = FALSE; - return PASS; + DBG_RETURN(PASS); } if (result->conn->state != CONN_FETCHING_DATA) { SET_CLIENT_ERROR(result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); - return FAIL; + DBG_RETURN(FAIL); } - /* Let the row packet fill our buffer and skip additional malloc + memcpy */ + /* Let the row packet fill our buffer and skip additional mnd_malloc + memcpy */ row_packet->skip_extraction = row? FALSE:TRUE; /* @@ -466,7 +524,7 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flag row_packet->fields = NULL; row_packet->row_buffer = NULL; - MYSQLND_INC_CONN_STATISTIC(&result->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT); + MYSQLND_INC_CONN_STATISTIC(&result->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF); if (!row_packet->skip_extraction) { HashTable *row_ht = Z_ARRVAL_P(row); @@ -527,14 +585,15 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flag } } else if (ret == FAIL) { if (row_packet->error_info.error_no) { - result->conn->error_info = row_packet->error_info; + result->conn->error_info = row_packet->error_info; + DBG_ERR_FMT("errorno=%d error=%s", row_packet->error_info.error_no, row_packet->error_info.error); } *fetched_anything = FALSE; result->conn->state = CONN_READY; result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */ } else if (row_packet->eof) { /* Mark the connection as usable again */ - + DBG_INF_FMT("warns=%u status=%u", row_packet->warning_count, row_packet->server_status); result->unbuf->eof_reached = TRUE; result->conn->upsert_status.warning_count = row_packet->warning_count; result->conn->upsert_status.server_status = row_packet->server_status; @@ -551,7 +610,8 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flag *fetched_anything = FALSE; } - return PASS; + DBG_INF_FMT("ret=%s fetched=%d", ret == PASS? "PASS":"FAIL", *fetched_anything); + DBG_RETURN(PASS); } /* }}} */ @@ -560,10 +620,13 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flag MYSQLND_RES * MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps TSRMLS_DC) { + DBG_ENTER("mysqlnd_res::use_result"); + DBG_INF_FMT("ps=%d", ps); + result->type = MYSQLND_RES_NORMAL; result->m.fetch_row = result->m.fetch_row_normal_unbuffered; result->m.fetch_lengths = mysqlnd_fetch_lengths_unbuffered; - result->unbuf = ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); + result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); /* Will be freed in the mysqlnd_internal_free_result_contents() called @@ -576,11 +639,11 @@ MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps result->row_packet->fields_metadata = result->meta->fields; result->row_packet->bit_fields_count = result->meta->bit_fields_count; result->row_packet->bit_fields_total_len = result->meta->bit_fields_total_len; - result->lengths = ecalloc(result->field_count, sizeof(unsigned long)); + result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long)); /* No multithreading issues as we don't share the connection :) */ - return result; + DBG_RETURN(result); } /* }}} */ @@ -593,6 +656,9 @@ mysqlnd_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, unsigned int i; zval *row = (zval *) param; + DBG_ENTER("mysqlnd_fetch_row_buffered"); + DBG_INF_FMT("flags=%u row=%p", flags, row); + /* If we haven't read everything */ if (result->data->data_cursor && (result->data->data_cursor - result->data->data) < result->data->row_count) @@ -647,14 +713,14 @@ mysqlnd_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, } result->data->data_cursor++; *fetched_anything = TRUE; + MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF); } else { result->data->data_cursor = NULL; *fetched_anything = FALSE; -#ifndef MYSQLND_SILENT - php_printf("NO MORE DATA\n "); -#endif + DBG_INF("EOF reached"); } - return PASS; + DBG_INF_FMT("ret=PASS fetched=%d", *fetched_anything); + DBG_RETURN(PASS); } /* }}} */ @@ -674,11 +740,15 @@ mysqlnd_store_result_fetch_data(MYSQLND * const conn, MYSQLND_RES *result, unsigned int next_extend = STORE_RESULT_PREALLOCATED_SET, free_rows; MYSQLND_RES_BUFFERED *set; + DBG_ENTER("mysqlnd_store_result_fetch_data"); + DBG_INF_FMT("conn=%llu binary_proto=%d update_max_len=%d to_cache=%d", + conn->thread_id, binary_protocol, update_max_length, to_cache); + free_rows = next_extend; - result->data = set = pecalloc(1, sizeof(MYSQLND_RES_BUFFERED), to_cache); - set->data = pemalloc(STORE_RESULT_PREALLOCATED_SET * sizeof(zval **), to_cache); - set->row_buffers= pemalloc(STORE_RESULT_PREALLOCATED_SET * sizeof(zend_uchar *), to_cache); + result->data = set = mnd_pecalloc(1, sizeof(MYSQLND_RES_BUFFERED), to_cache); + set->data = mnd_pemalloc(STORE_RESULT_PREALLOCATED_SET * sizeof(zval **), to_cache); + set->row_buffers= mnd_pemalloc(STORE_RESULT_PREALLOCATED_SET * sizeof(zend_uchar *), to_cache); set->persistent = to_cache; set->qcache = to_cache? mysqlnd_qcache_get_cache_reference(conn->qcache):NULL; set->references = 1; @@ -697,9 +767,9 @@ mysqlnd_store_result_fetch_data(MYSQLND * const conn, MYSQLND_RES *result, if (!free_rows) { mynd_ulonglong total_rows = free_rows = next_extend = next_extend * 5 / 3; /* extend with 33% */ total_rows += set->row_count; - set->data = perealloc(set->data, total_rows * sizeof(zval **), set->persistent); + set->data = mnd_perealloc(set->data, total_rows * sizeof(zval **), set->persistent); - set->row_buffers = perealloc(set->row_buffers, + set->row_buffers = mnd_perealloc(set->row_buffers, total_rows * sizeof(zend_uchar *), set->persistent); } free_rows--; @@ -734,7 +804,9 @@ mysqlnd_store_result_fetch_data(MYSQLND * const conn, MYSQLND_RES *result, transfered above. */ } - MYSQLND_INC_CONN_STATISTIC_W_VALUE(&conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT, + MYSQLND_INC_CONN_STATISTIC_W_VALUE(&conn->stats, + binary_protocol? STAT_ROWS_BUFFERED_FROM_CLIENT_PS: + STAT_ROWS_BUFFERED_FROM_CLIENT_NORMAL, set->row_count); /* Finally clean */ @@ -744,10 +816,10 @@ mysqlnd_store_result_fetch_data(MYSQLND * const conn, MYSQLND_RES *result, } /* save some memory */ if (free_rows) { - set->data = perealloc(set->data, + set->data = mnd_perealloc(set->data, (size_t) set->row_count * sizeof(zval **), set->persistent); - set->row_buffers = perealloc(set->row_buffers, + set->row_buffers = mnd_perealloc(set->row_buffers, (size_t) set->row_count * sizeof(zend_uchar *), set->persistent); } @@ -769,7 +841,9 @@ mysqlnd_store_result_fetch_data(MYSQLND * const conn, MYSQLND_RES *result, } PACKET_FREE_ALLOCA(row_packet); - return ret; + DBG_INF_FMT("ret=%s row_count=%u warns=%u status=%u", ret == PASS? "PASS":"FAIL", + set->row_count, conn->upsert_status.warning_count, conn->upsert_status.server_status); + DBG_RETURN(ret); } /* }}} */ @@ -783,6 +857,9 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result, enum_func_status ret; zend_bool to_cache = FALSE; + DBG_ENTER("mysqlnd_res::store_result"); + DBG_INF_FMT("conn=%d ps_protocol=%d", conn->thread_id, ps_protocol); + result->conn = NULL; /* store result does not reference the connection */ result->type = MYSQLND_RES_NORMAL; result->m.fetch_row = result->m.fetch_row_normal_buffered; @@ -790,7 +867,7 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result, conn->state = CONN_FETCHING_DATA; - result->lengths = ecalloc(result->field_count, sizeof(unsigned long)); + result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long)); ret = mysqlnd_store_result_fetch_data(conn, result, result->meta, ps_protocol, TRUE, to_cache TSRMLS_CC); @@ -803,7 +880,7 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result, result = NULL; } - return result; + DBG_RETURN(result); } /* }}} */ @@ -814,6 +891,7 @@ MYSQLND_METHOD(mysqlnd_res, skip_result)(MYSQLND_RES * const result TSRMLS_DC) { zend_bool fetched_anything; + DBG_ENTER("mysqlnd_res::skip_result"); /* Unbuffered sets A PS could be prepared - there is metadata and thus a stmt->result but the @@ -822,6 +900,7 @@ MYSQLND_METHOD(mysqlnd_res, skip_result)(MYSQLND_RES * const result TSRMLS_DC) if (!result->data && result->conn && result->unbuf && !result->unbuf->eof_reached && result->m.fetch_row) { + DBG_INF("skipping result"); /* We have to fetch all data to clean the line */ MYSQLND_INC_CONN_STATISTIC(&result->conn->stats, result->type == MYSQLND_RES_NORMAL? STAT_FLUSHED_NORMAL_SETS: @@ -833,7 +912,7 @@ MYSQLND_METHOD(mysqlnd_res, skip_result)(MYSQLND_RES * const result TSRMLS_DC) /* do nothing */; } } - return PASS; + DBG_RETURN(PASS); } /* }}} */ @@ -842,21 +921,27 @@ MYSQLND_METHOD(mysqlnd_res, skip_result)(MYSQLND_RES * const result TSRMLS_DC) static enum_func_status MYSQLND_METHOD(mysqlnd_res, free_result)(MYSQLND_RES *result, zend_bool implicit TSRMLS_DC) { + DBG_ENTER("mysqlnd_res::free_result"); + DBG_INF_FMT("implicit=%d", implicit); + result->m.skip_result(result TSRMLS_CC); MYSQLND_INC_CONN_STATISTIC(result->conn? &result->conn->stats : NULL, - implicit == TRUE? STAT_FREE_RESULT_EXPLICIT: - STAT_FREE_RESULT_IMPLICIT); + implicit == TRUE? STAT_FREE_RESULT_IMPLICIT: + STAT_FREE_RESULT_EXPLICIT); result->m.free_result_internal(result TSRMLS_CC); - return PASS; + DBG_RETURN(PASS); } /* }}} */ -/* {{{ _mysqlnd_data_seek */ +/* {{{ mysqlnd_res::data_seek */ static enum_func_status -MYSQLND_METHOD(mysqlnd_res, data_seek)(MYSQLND_RES *result, mynd_ulonglong row) +MYSQLND_METHOD(mysqlnd_res, data_seek)(MYSQLND_RES *result, mynd_ulonglong row TSRMLS_DC) { + DBG_ENTER("mysqlnd_res::data_seek"); + DBG_INF_FMT("row=%lu", row); + if (!result->data) { return FAIL; } @@ -868,7 +953,7 @@ MYSQLND_METHOD(mysqlnd_res, data_seek)(MYSQLND_RES *result, mynd_ulonglong row) result->data->data_cursor = result->data->data + row; } - return PASS; + DBG_RETURN(PASS); } /* }}} */ @@ -894,9 +979,10 @@ MYSQLND_METHOD(mysqlnd_res, num_fields)(const MYSQLND_RES * const res) /* {{{ mysqlnd_res::fetch_field */ static MYSQLND_FIELD * -MYSQLND_METHOD(mysqlnd_res, fetch_field)(MYSQLND_RES * const result) +MYSQLND_METHOD(mysqlnd_res, fetch_field)(MYSQLND_RES * const result TSRMLS_DC) { - return result->meta? result->meta->m->fetch_field(result->meta):NULL; + DBG_ENTER("mysqlnd_res::fetch_field"); + DBG_RETURN(result->meta? result->meta->m->fetch_field(result->meta TSRMLS_CC):NULL); } /* }}} */ @@ -904,9 +990,10 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field)(MYSQLND_RES * const result) /* {{{ mysqlnd_res::fetch_field_direct */ static MYSQLND_FIELD * MYSQLND_METHOD(mysqlnd_res, fetch_field_direct)(const MYSQLND_RES * const result, - MYSQLND_FIELD_OFFSET fieldnr) + MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC) { - return result->meta? result->meta->m->fetch_field_direct(result->meta, fieldnr):NULL; + DBG_ENTER("mysqlnd_res::fetch_field_direct"); + DBG_RETURN(result->meta? result->meta->m->fetch_field_direct(result->meta, fieldnr TSRMLS_CC):NULL); } /* }}} */ @@ -943,8 +1030,12 @@ MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES *result, unsigned int flags, { zend_bool fetched_anything; + DBG_ENTER("mysqlnd_res::fetch_into"); + DBG_INF_FMT("flags=%u mysqlnd_extension=%d", flags, extension); + if (!result->m.fetch_row) { - RETURN_NULL(); + RETVAL_NULL(); + DBG_VOID_RETURN; } /* Hint Zend how many elements we will have in the hash. Thus it won't @@ -953,15 +1044,15 @@ MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES *result, unsigned int flags, mysqlnd_array_init(return_value, mysqlnd_num_fields(result) * 2); if (FAIL == result->m.fetch_row(result, (void *)return_value, flags, &fetched_anything TSRMLS_CC)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading a row"); - RETURN_FALSE; + RETVAL_FALSE; } else if (fetched_anything == FALSE) { zval_dtor(return_value); switch (extension) { case MYSQLND_MYSQLI: - RETURN_NULL(); + RETVAL_NULL(); break; case MYSQLND_MYSQL: - RETURN_FALSE; + RETVAL_FALSE; break; default:exit(0); } @@ -970,6 +1061,7 @@ MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES *result, unsigned int flags, return_value is IS_NULL for no more data and an array for data. Thus it's ok to return here. */ + DBG_VOID_RETURN; } /* }}} */ @@ -982,12 +1074,16 @@ MYSQLND_METHOD(mysqlnd_res, fetch_all)(MYSQLND_RES *result, unsigned int flags, zval *row; ulong i = 0; + DBG_ENTER("mysqlnd_res::fetch_all"); + DBG_INF_FMT("flags=%u", flags); + /* mysqlnd_res::fetch_all works with buffered resultsets only */ if (result->conn || !result->data || !result->data->row_count || !result->data->data_cursor || result->data->data_cursor >= result->data->data + result->data->row_count) { - RETURN_NULL(); + RETVAL_NULL(); + DBG_VOID_RETURN; } mysqlnd_array_init(return_value, (uint) result->data->row_count); @@ -999,6 +1095,8 @@ MYSQLND_METHOD(mysqlnd_res, fetch_all)(MYSQLND_RES *result, unsigned int flags, mysqlnd_fetch_into(result, flags, row, MYSQLND_MYSQLI); add_index_zval(return_value, i++, row); } + + DBG_VOID_RETURN; } /* }}} */ @@ -1012,8 +1110,12 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field_data)(MYSQLND_RES *result, unsigned int zval **entry; uint i = 0; + DBG_ENTER("mysqlnd_res::fetch_field_data"); + DBG_INF_FMT("offset=%u", offset); + if (!result->m.fetch_row) { - RETURN_NULL(); + RETVAL_NULL(); + DBG_VOID_RETURN; } /* Hint Zend how many elements we will have in the hash. Thus it won't @@ -1023,7 +1125,8 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field_data)(MYSQLND_RES *result, unsigned int mysqlnd_fetch_into(result, MYSQLND_FETCH_NUM, &row, MYSQLND_MYSQL); if (Z_TYPE(row) != IS_ARRAY) { zval_dtor(&row); - RETURN_NULL(); + RETVAL_NULL(); + DBG_VOID_RETURN; } zend_hash_internal_pointer_reset(Z_ARRVAL(row)); while (i++ < offset) { @@ -1037,14 +1140,19 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field_data)(MYSQLND_RES *result, unsigned int zval_copy_ctor(return_value); ZVAL_REFCOUNT(return_value) = 1; zval_dtor(&row); + + DBG_VOID_RETURN; } /* }}} */ /* {{{ mysqlnd_result_init */ -MYSQLND_RES *mysqlnd_result_init(unsigned int field_count, MYSQLND_THD_ZVAL_PCACHE *cache) +MYSQLND_RES *mysqlnd_result_init(unsigned int field_count, MYSQLND_THD_ZVAL_PCACHE *cache TSRMLS_DC) { - MYSQLND_RES *ret = ecalloc(1, sizeof(MYSQLND_RES)); + MYSQLND_RES *ret = mnd_ecalloc(1, sizeof(MYSQLND_RES)); + + DBG_ENTER("mysqlnd_result_init"); + DBG_INF_FMT("field_count=%u cache=%p", field_count, cache); ret->field_count = field_count; ret->zval_cache = cache; @@ -1072,7 +1180,7 @@ MYSQLND_RES *mysqlnd_result_init(unsigned int field_count, MYSQLND_THD_ZVAL_PCAC ret->m.fetch_row_normal_buffered = mysqlnd_fetch_row_buffered; ret->m.fetch_row_normal_unbuffered = mysqlnd_fetch_row_unbuffered; - return ret; + DBG_RETURN(ret); } /* }}} */ diff --git a/ext/mysqlnd/mysqlnd_result.h b/ext/mysqlnd/mysqlnd_result.h index dd07097b52..eec92becff 100644 --- a/ext/mysqlnd/mysqlnd_result.h +++ b/ext/mysqlnd/mysqlnd_result.h @@ -23,7 +23,7 @@ #ifndef MYSQLND_RESULT_H #define MYSQLND_RESULT_H -MYSQLND_RES *mysqlnd_result_init(unsigned int field_count, MYSQLND_THD_ZVAL_PCACHE *cache); +MYSQLND_RES *mysqlnd_result_init(unsigned int field_count, MYSQLND_THD_ZVAL_PCACHE *cache TSRMLS_DC); void mysqlnd_unbuffered_free_last_data(MYSQLND_RES *result TSRMLS_DC); diff --git a/ext/mysqlnd/mysqlnd_result_meta.c b/ext/mysqlnd/mysqlnd_result_meta.c index e434d143ca..bd80ae24b2 100644 --- a/ext/mysqlnd/mysqlnd_result_meta.c +++ b/ext/mysqlnd/mysqlnd_result_meta.c @@ -24,20 +24,21 @@ #include "mysqlnd_priv.h" #include "mysqlnd_result.h" #include "mysqlnd_wireprotocol.h" +#include "mysqlnd_debug.h" #include "ext/standard/basic_functions.h" /* {{{ php_mysqlnd_free_field_metadata */ static -void php_mysqlnd_free_field_metadata(MYSQLND_FIELD *meta, zend_bool persistent) +void php_mysqlnd_free_field_metadata(MYSQLND_FIELD *meta, zend_bool persistent TSRMLS_DC) { if (meta) { if (meta->root) { - pefree(meta->root, persistent); + mnd_pefree(meta->root, persistent); meta->root = NULL; } if (meta->def) { - pefree(meta->def, persistent); + mnd_pefree(meta->def, persistent); meta->def = NULL; } } @@ -143,32 +144,40 @@ MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const met int i = 0; php_mysql_packet_res_field field_packet; + DBG_ENTER("mysqlnd_res_meta::read_metadata"); + PACKET_INIT_ALLOCA(field_packet, PROT_RSET_FLD_PACKET); for (;i < meta->field_count; i++) { long idx; if (meta->fields[i].root) { /* We re-read metadata for PS */ - efree(meta->fields[i].root); + mnd_efree(meta->fields[i].root); meta->fields[i].root = NULL; } field_packet.metadata = &(meta->fields[i]); if (FAIL == PACKET_READ_ALLOCA(field_packet, conn)) { PACKET_FREE_ALLOCA(field_packet); - return FAIL; + DBG_RETURN(FAIL); + } + if (field_packet.stupid_list_fields_eof == TRUE) { + break; } if (mysqlnd_ps_fetch_functions[meta->fields[i].type].func == NULL) { + DBG_ERR_FMT("Unknown type %d sent by the server. Please send a report to the developers", + meta->fields[i].type); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown type %d sent by the server. " "Please send a report to the developers", meta->fields[i].type); PACKET_FREE_ALLOCA(field_packet); - return FAIL; + DBG_RETURN(FAIL); } if (meta->fields[i].type == MYSQL_TYPE_BIT) { size_t field_len; + DBG_INF("BIT"); ++meta->bit_fields_count; /* .length is in bits */ field_len = meta->fields[i].length / 8; @@ -213,7 +222,7 @@ MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const met mysqlnd_unicode_is_key_numeric(ustr, ulen + 1, &idx))) { meta->zend_hash_keys[i].key = idx; - efree(ustr); + mnd_efree(ustr); } else { meta->zend_hash_keys[i].ustr.u = ustr; meta->zend_hash_keys[i].ulen = ulen; @@ -239,7 +248,7 @@ MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const met } PACKET_FREE_ALLOCA(field_packet); - return PASS; + DBG_RETURN(PASS); } /* }}} */ @@ -251,29 +260,37 @@ MYSQLND_METHOD(mysqlnd_res_meta, free)(MYSQLND_RES_METADATA *meta, zend_bool per int i; MYSQLND_FIELD *fields; + DBG_ENTER("mysqlnd_res_meta::free"); + DBG_INF_FMT("persistent=%d", persistent); + if ((fields = meta->fields)) { + DBG_INF("Freeing fields metadata"); i = meta->field_count; while (i--) { - php_mysqlnd_free_field_metadata(fields++, persistent); + php_mysqlnd_free_field_metadata(fields++, persistent TSRMLS_CC); } - pefree(meta->fields, persistent); + mnd_pefree(meta->fields, persistent); meta->fields = NULL; } if (meta->zend_hash_keys) { + DBG_INF("Freeing zend_hash_keys"); #if PHP_MAJOR_VERSION >= 6 if (UG(unicode)) { for (i = 0; i < meta->field_count; i++) { if (meta->zend_hash_keys[i].ustr.v) { - pefree(meta->zend_hash_keys[i].ustr.v, persistent); + mnd_pefree(meta->zend_hash_keys[i].ustr.v, persistent); } } } #endif - pefree(meta->zend_hash_keys, persistent); + mnd_pefree(meta->zend_hash_keys, persistent); meta->zend_hash_keys = NULL; } - pefree(meta, persistent); + DBG_INF("Freeing metadata structure"); + mnd_pefree(meta, persistent); + + DBG_VOID_RETURN; } /* }}} */ @@ -281,16 +298,19 @@ MYSQLND_METHOD(mysqlnd_res_meta, free)(MYSQLND_RES_METADATA *meta, zend_bool per /* {{{ mysqlnd_res::clone_metadata */ static MYSQLND_RES_METADATA * MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata)(const MYSQLND_RES_METADATA * const meta, - zend_bool persistent) + zend_bool persistent TSRMLS_DC) { unsigned int i; /* +1 is to have empty marker at the end */ - MYSQLND_RES_METADATA *new_meta = pemalloc(sizeof(MYSQLND_RES_METADATA), persistent); - MYSQLND_FIELD *new_fields = pecalloc(meta->field_count + 1, sizeof(MYSQLND_FIELD), persistent); + MYSQLND_RES_METADATA *new_meta = mnd_pemalloc(sizeof(MYSQLND_RES_METADATA), persistent); + MYSQLND_FIELD *new_fields = mnd_pecalloc(meta->field_count + 1, sizeof(MYSQLND_FIELD), persistent); MYSQLND_FIELD *orig_fields = meta->fields; size_t len = meta->field_count * sizeof(struct mysqlnd_field_hash_key); - new_meta->zend_hash_keys = pemalloc(len, persistent); + DBG_ENTER("mysqlnd_res_meta::clone_metadata"); + DBG_INF_FMT("persistent=%d", persistent); + + new_meta->zend_hash_keys = mnd_pemalloc(len, persistent); memcpy(new_meta->zend_hash_keys, meta->zend_hash_keys, len); new_meta->m = meta->m; @@ -301,7 +321,7 @@ MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata)(const MYSQLND_RES_METADATA * co memcpy(new_fields, orig_fields, (meta->field_count) * sizeof(MYSQLND_FIELD)); for (i = 0; i < meta->field_count; i++) { /* First copy the root, then field by field adjust the pointers */ - new_fields[i].root = pemalloc(orig_fields[i].root_len, persistent); + new_fields[i].root = mnd_pemalloc(orig_fields[i].root_len, persistent); memcpy(new_fields[i].root, orig_fields[i].root, new_fields[i].root_len); if (orig_fields[i].name && orig_fields[i].name != mysqlnd_empty_string) { @@ -328,7 +348,7 @@ MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata)(const MYSQLND_RES_METADATA * co } /* def is not on the root, if allocated at all */ if (orig_fields[i].def) { - new_fields[i].def = pemalloc(orig_fields[i].def_length + 1, persistent); + new_fields[i].def = mnd_pemalloc(orig_fields[i].def_length + 1, persistent); /* copy the trailing \0 too */ memcpy(new_fields[i].def, orig_fields[i].def, orig_fields[i].def_length + 1); } @@ -344,17 +364,19 @@ MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata)(const MYSQLND_RES_METADATA * co new_meta->fields = new_fields; - return new_meta; + DBG_RETURN(new_meta); } /* }}} */ /* {{{ mysqlnd_res_meta::fetch_field */ static MYSQLND_FIELD * -MYSQLND_METHOD(mysqlnd_res_meta, fetch_field)(MYSQLND_RES_METADATA * const meta) +MYSQLND_METHOD(mysqlnd_res_meta, fetch_field)(MYSQLND_RES_METADATA * const meta TSRMLS_DC) { - if (meta->current_field >= meta->field_count) - return NULL; - return &meta->fields[meta->current_field++]; + DBG_ENTER("mysqlnd_res_meta::fetch_field"); + if (meta->current_field >= meta->field_count) { + DBG_RETURN(NULL); + } + DBG_RETURN(&meta->fields[meta->current_field++]); } /* }}} */ @@ -362,9 +384,11 @@ MYSQLND_METHOD(mysqlnd_res_meta, fetch_field)(MYSQLND_RES_METADATA * const meta) /* {{{ mysqlnd_res_meta::fetch_field_direct */ static MYSQLND_FIELD * MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, - MYSQLND_FIELD_OFFSET fieldnr) + MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC) { - return &meta->fields[fieldnr]; + DBG_ENTER("mysqlnd_res_meta::fetch_field_direct"); + DBG_INF_FMT("fieldnr=%d", fieldnr); + DBG_RETURN(&meta->fields[fieldnr]); } /* }}} */ @@ -389,17 +413,20 @@ MYSQLND_CLASS_METHODS_END; /* {{{ mysqlnd_result_meta_init */ -MYSQLND_RES_METADATA *mysqlnd_result_meta_init(unsigned int field_count) +MYSQLND_RES_METADATA *mysqlnd_result_meta_init(unsigned int field_count TSRMLS_DC) { MYSQLND_RES_METADATA *ret; + DBG_ENTER("mysqlnd_result_meta_init"); + /* +1 is to have empty marker at the end */ - ret = ecalloc(1, sizeof(MYSQLND_RES_METADATA)); + ret = mnd_ecalloc(1, sizeof(MYSQLND_RES_METADATA)); ret->field_count = field_count; ret->fields = ecalloc(field_count + 1, sizeof(MYSQLND_FIELD)); ret->zend_hash_keys = ecalloc(field_count, sizeof(struct mysqlnd_field_hash_key)); ret->m = & mysqlnd_mysqlnd_res_meta_methods; - return ret; + DBG_INF_FMT("meta=%p", ret); + DBG_RETURN(ret); } diff --git a/ext/mysqlnd/mysqlnd_result_meta.h b/ext/mysqlnd/mysqlnd_result_meta.h index 1a7c308b29..f1bfaee681 100644 --- a/ext/mysqlnd/mysqlnd_result_meta.h +++ b/ext/mysqlnd/mysqlnd_result_meta.h @@ -24,7 +24,7 @@ #define MYSQLND_RESULT_META_H -MYSQLND_RES_METADATA *mysqlnd_result_meta_init(unsigned int field_count); +MYSQLND_RES_METADATA *mysqlnd_result_meta_init(unsigned int field_count TSRMLS_DC); diff --git a/ext/mysqlnd/mysqlnd_statistics.c b/ext/mysqlnd/mysqlnd_statistics.c index 4c93e0f79e..66d9bacd44 100644 --- a/ext/mysqlnd/mysqlnd_statistics.c +++ b/ext/mysqlnd/mysqlnd_statistics.c @@ -23,20 +23,13 @@ #include "mysqlnd.h" #include "mysqlnd_priv.h" #include "mysqlnd_statistics.h" - - -typedef struct st_mysqlnd_string -{ - char *s; - size_t l; -} MYSQLND_STRING; +#include "mysqlnd_debug.h" #define STR_W_LEN(str) str, (sizeof(str) - 1) /* {{{ mysqlnd_stats_values_names */ -static const MYSQLND_STRING mysqlnd_stats_values_names[STAT_LAST] = { { STR_W_LEN("bytes_sent") }, @@ -55,15 +48,29 @@ const MYSQLND_STRING mysqlnd_stats_values_names[STAT_LAST] = { STR_W_LEN("ps_unbuffered_sets") }, { STR_W_LEN("flushed_normal_sets") }, { STR_W_LEN("flushed_ps_sets") }, - { STR_W_LEN("rows_fetched_from_server") }, - { STR_W_LEN("rows_fetched_from_client") }, - { STR_W_LEN("rows_skipped") }, + { STR_W_LEN("ps_prepared_never_executed") }, + { STR_W_LEN("ps_prepared_once_executed") }, + { STR_W_LEN("rows_fetched_from_server_normal") }, + { STR_W_LEN("rows_fetched_from_server_ps") }, + { STR_W_LEN("rows_buffered_from_client_normal") }, + { STR_W_LEN("rows_buffered_from_client_ps") }, + { STR_W_LEN("rows_fetched_from_client_normal_buffered") }, + { STR_W_LEN("rows_fetched_from_client_normal_unbuffered") }, + { STR_W_LEN("rows_fetched_from_client_ps_buffered") }, + { STR_W_LEN("rows_fetched_from_client_ps_unbuffered") }, + { STR_W_LEN("rows_fetched_from_client_ps_cursor") }, + { STR_W_LEN("rows_skipped_normal") }, + { STR_W_LEN("rows_skipped_ps") }, { STR_W_LEN("copy_on_write_saved") }, { STR_W_LEN("copy_on_write_performed") }, { STR_W_LEN("command_buffer_too_small") }, { STR_W_LEN("connect_success") }, { STR_W_LEN("connect_failure") }, { STR_W_LEN("connection_reused") }, + { STR_W_LEN("reconnect") }, + { STR_W_LEN("pconnect_success") }, + { STR_W_LEN("active_connections") }, + { STR_W_LEN("active_persistent_connections") }, { STR_W_LEN("explicit_close") }, { STR_W_LEN("implicit_close") }, { STR_W_LEN("disconnect_close") }, @@ -72,10 +79,25 @@ const MYSQLND_STRING mysqlnd_stats_values_names[STAT_LAST] = { STR_W_LEN("implicit_free_result") }, { STR_W_LEN("explicit_stmt_close") }, { STR_W_LEN("implicit_stmt_close") }, + { STR_W_LEN("mem_emalloc_count") }, + { STR_W_LEN("mem_emalloc_ammount") }, + { STR_W_LEN("mem_ecalloc_count") }, + { STR_W_LEN("mem_ecalloc_ammount") }, + { STR_W_LEN("mem_erealloc_count") }, + { STR_W_LEN("mem_erealloc_ammount") }, + { STR_W_LEN("mem_efree_count") }, + { STR_W_LEN("mem_malloc_count") }, + { STR_W_LEN("mem_malloc_ammount") }, + { STR_W_LEN("mem_calloc_count") }, + { STR_W_LEN("mem_calloc_ammount") }, + { STR_W_LEN("mem_realloc_calloc") }, + { STR_W_LEN("mem_realloc_ammount") }, + { STR_W_LEN("mem_free_count") } }; /* }}} */ +/* {{{ mysqlnd_fill_stats_hash */ void mysqlnd_fill_stats_hash(const MYSQLND_STATS * const stats, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC) { @@ -105,19 +127,22 @@ mysqlnd_fill_stats_hash(const MYSQLND_STATS * const stats, zval *return_value TS } } } +/* }}} */ /* {{{ _mysqlnd_get_client_stats */ PHPAPI void _mysqlnd_get_client_stats(zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC) { MYSQLND_STATS stats, *stats_ptr = mysqlnd_global_stats; - + DBG_ENTER("_mysqlnd_get_client_stats"); if (!stats_ptr) { memset(&stats, 0, sizeof(stats)); stats_ptr = &stats; } mysqlnd_fill_stats_hash(stats_ptr, return_value TSRMLS_CC ZEND_FILE_LINE_CC); + DBG_VOID_RETURN; } +/* }}} */ /* diff --git a/ext/mysqlnd/mysqlnd_statistics.h b/ext/mysqlnd/mysqlnd_statistics.h index e4d761d728..2122ac2411 100644 --- a/ext/mysqlnd/mysqlnd_statistics.h +++ b/ext/mysqlnd/mysqlnd_statistics.h @@ -26,50 +26,93 @@ extern MYSQLND_STATS *mysqlnd_global_stats; +typedef struct st_mysqlnd_string +{ + char *s; + size_t l; +} MYSQLND_STRING; + +extern const MYSQLND_STRING mysqlnd_stats_values_names[]; + #ifdef ZTS +#define MYSQLND_INC_GLOBAL_STATISTIC(statistic) \ + { \ + if (MYSQLND_G(collect_statistics)) { \ + DBG_INF_FMT("Global stat increase [%s]", mysqlnd_stats_values_names[statistic]); \ + tsrm_mutex_lock(mysqlnd_global_stats->LOCK_access); \ + mysqlnd_global_stats->values[(statistic)]++; \ + tsrm_mutex_unlock(mysqlnd_global_stats->LOCK_access); \ + }\ + } + +#define MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(statistic1, value1, statistic2, value2) \ + { \ + if (MYSQLND_G(collect_statistics)) { \ + DBG_INF_FMT("Global stats increase w value [%s] [%s]", mysqlnd_stats_values_names[statistic1], mysqlnd_stats_values_names[statistic2]); \ + tsrm_mutex_lock(mysqlnd_global_stats->LOCK_access); \ + mysqlnd_global_stats->values[(statistic1)] += (value1); \ + mysqlnd_global_stats->values[(statistic2)] += (value2); \ + tsrm_mutex_unlock(mysqlnd_global_stats->LOCK_access); \ + }\ + } + +#define MYSQLND_DEC_CONN_STATISTIC(conn_stats, statistic) \ + { \ + if (MYSQLND_G(collect_statistics)) { \ + DBG_INF_FMT("Global&conn stat decrease [%s]", mysqlnd_stats_values_names[statistic]); \ + tsrm_mutex_lock(mysqlnd_global_stats->LOCK_access); \ + mysqlnd_global_stats->values[(statistic)]--; \ + tsrm_mutex_unlock(mysqlnd_global_stats->LOCK_access); \ + if ((conn_stats)) { \ + ((MYSQLND_STATS *) conn_stats)->values[(statistic)]--; \ + } \ + }\ + } + #define MYSQLND_INC_CONN_STATISTIC(conn_stats, statistic) \ { \ - if (mysqlnd_global_stats) { \ + if (MYSQLND_G(collect_statistics)) { \ + DBG_INF_FMT("Global&Conn stat increase [%s]", mysqlnd_stats_values_names[statistic]); \ tsrm_mutex_lock(mysqlnd_global_stats->LOCK_access); \ - mysqlnd_global_stats->values[statistic]++; \ + mysqlnd_global_stats->values[(statistic)]++; \ tsrm_mutex_unlock(mysqlnd_global_stats->LOCK_access); \ if ((conn_stats)) { \ - ((MYSQLND_STATS *) conn_stats)->values[statistic]++; \ + ((MYSQLND_STATS *) conn_stats)->values[(statistic)]++; \ } \ }\ } #define MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn_stats, statistic, value) \ { \ - if (mysqlnd_global_stats) {\ - my_uint64 v = (value); \ - \ + if (MYSQLND_G(collect_statistics)) { \ + my_uint64 v = (my_uint64) (value); \ + DBG_INF_FMT("Global&Conn stat increase w value [%s]", mysqlnd_stats_values_names[statistic]); \ tsrm_mutex_lock(mysqlnd_global_stats->LOCK_access); \ - mysqlnd_global_stats->values[statistic] += v; \ + mysqlnd_global_stats->values[(statistic)] += v; \ tsrm_mutex_unlock(mysqlnd_global_stats->LOCK_access); \ if ((conn_stats)) { \ - ((MYSQLND_STATS *) conn_stats)->values[statistic]+= v; \ + ((MYSQLND_STATS *) conn_stats)->values[(statistic)]+= v; \ } \ }\ } #define MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, statistic1, value1, statistic2, value2, statistic3, value3) \ { \ - if (mysqlnd_global_stats) { \ - my_uint64 v1 = (value1); \ - my_uint64 v2 = (value2); \ - my_uint64 v3 = (value3); \ + if (MYSQLND_G(collect_statistics)) { \ + my_uint64 v1 = (my_uint64) (value1); \ + my_uint64 v2 = (my_uint64) (value2); \ + my_uint64 v3 = (my_uint64) (value3); \ \ tsrm_mutex_lock(mysqlnd_global_stats->LOCK_access); \ - mysqlnd_global_stats->values[statistic1]+= v1; \ - mysqlnd_global_stats->values[statistic2]+= v2; \ - mysqlnd_global_stats->values[statistic3]+= v3; \ + mysqlnd_global_stats->values[(statistic1)]+= v1; \ + mysqlnd_global_stats->values[(statistic2)]+= v2; \ + mysqlnd_global_stats->values[(statistic3)]+= v3; \ tsrm_mutex_unlock(mysqlnd_global_stats->LOCK_access); \ if ((conn_stats)) { \ - ((MYSQLND_STATS *) conn_stats)->values[statistic1]+= v1; \ - ((MYSQLND_STATS *) conn_stats)->values[statistic2]+= v2; \ - ((MYSQLND_STATS *) conn_stats)->values[statistic3]+= v3; \ + ((MYSQLND_STATS *) conn_stats)->values[(statistic1)]+= v1; \ + ((MYSQLND_STATS *) conn_stats)->values[(statistic2)]+= v2; \ + ((MYSQLND_STATS *) conn_stats)->values[(statistic3)]+= v3; \ } \ } \ } @@ -77,42 +120,73 @@ extern MYSQLND_STATS *mysqlnd_global_stats; #else /* NON-ZTS */ +#define MYSQLND_INC_GLOBAL_STATISTIC(statistic) \ + { \ + if (MYSQLND_G(collect_statistics)) { \ + DBG_INF_FMT("Global stat increase [%s]", mysqlnd_stats_values_names[statistic]); \ + mysqlnd_global_stats->values[(statistic)]++; \ + } \ + } + +#define MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(statistic1, value1, statistic2, value2) \ + { \ + if (MYSQLND_G(collect_statistics)) { \ + DBG_INF_FMT("Global stats increase w value [%s] [%s]", \ + mysqlnd_stats_values_names[statistic1], mysqlnd_stats_values_names[statistic2]); \ + mysqlnd_global_stats->values[(statistic1)] += (value1); \ + mysqlnd_global_stats->values[(statistic2)] += (value2); \ + }\ + } + + +#define MYSQLND_DEC_CONN_STATISTIC(conn_stats, statistic) \ + { \ + if (MYSQLND_G(collect_statistics)) { \ + DBG_INF_FMT("Global&Conn stat decrease [%s]", mysqlnd_stats_values_names[statistic]); \ + mysqlnd_global_stats->values[(statistic)]--; \ + if ((conn_stats)) { \ + ((MYSQLND_STATS *) conn_stats)->values[(statistic)]--; \ + } \ + } \ + } + #define MYSQLND_INC_CONN_STATISTIC(conn_stats, statistic) \ { \ - if (mysqlnd_global_stats) { \ - mysqlnd_global_stats->values[statistic]++; \ + if (MYSQLND_G(collect_statistics)) { \ + DBG_INF_FMT("Global&Conn stat increase [%s]", mysqlnd_stats_values_names[statistic]); \ + mysqlnd_global_stats->values[(statistic)]++; \ if ((conn_stats)) { \ - ((MYSQLND_STATS *) conn_stats)->values[statistic]++; \ + ((MYSQLND_STATS *) conn_stats)->values[(statistic)]++; \ } \ } \ } #define MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn_stats, statistic, value) \ { \ - my_uint64 v = (value); \ - \ - if (mysqlnd_global_stats) { \ - mysqlnd_global_stats->values[statistic]+= v; \ + my_uint64 v = (my_uint64) (value); \ + DBG_INF_FMT("Global&Conn stats increase w value [%s]", mysqlnd_stats_values_names[statistic]); \ + if (MYSQLND_G(collect_statistics)) { \ + mysqlnd_global_stats->values[(statistic)] += v; \ if ((conn_stats)) { \ - ((MYSQLND_STATS *) conn_stats)->values[statistic]+= v; \ + ((MYSQLND_STATS *) conn_stats)->values[(statistic)] += v; \ } \ } \ } #define MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, statistic1, value1, statistic2, value2, statistic3, value3) \ { \ - if (mysqlnd_global_stats) { \ - my_uint64 v1 = (value1); \ - my_uint64 v2 = (value2); \ - my_uint64 v3 = (value3); \ + if (MYSQLND_G(collect_statistics)) { \ + my_uint64 v1 = (my_uint64) (value1); \ + my_uint64 v2 = (my_uint64) (value2); \ + my_uint64 v3 = (my_uint64) (value3); \ \ - mysqlnd_global_stats->values[statistic1]+= v1; \ - mysqlnd_global_stats->values[statistic2]+= v2; \ - mysqlnd_global_stats->values[statistic3]+= v3; \ + mysqlnd_global_stats->values[(statistic1)]+= v1; \ + mysqlnd_global_stats->values[(statistic2)]+= v2; \ + mysqlnd_global_stats->values[(statistic3)]+= v3; \ if ((conn_stats)) { \ - ((MYSQLND_STATS *) conn_stats)->values[statistic1]+= v1; \ - ((MYSQLND_STATS *) conn_stats)->values[statistic2]+= v2; \ - ((MYSQLND_STATS *) conn_stats)->values[statistic3]+= v3; \ + ((MYSQLND_STATS *) conn_stats)->values[(statistic1)]+= v1; \ + ((MYSQLND_STATS *) conn_stats)->values[(statistic2)]+= v2; \ + ((MYSQLND_STATS *) conn_stats)->values[(statistic3)]+= v3; \ } \ } \ } diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h new file mode 100644 index 0000000000..0542b271ca --- /dev/null +++ b/ext/mysqlnd/mysqlnd_structs.h @@ -0,0 +1,540 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 6 | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2007 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Georg Richter <georg@mysql.com> | + | Andrey Hristov <andrey@mysql.com> | + | Ulf Wendel <uwendel@mysql.com> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef MYSQLND_STRUCTS_H +#define MYSQLND_STRUCTS_H + +typedef struct st_mysqlnd_cmd_buffer +{ + zend_uchar *buffer; + size_t length; +} MYSQLND_CMD_BUFFER; + + +typedef struct st_mysqlnd_field +{ + char *name; /* Name of column */ + char *org_name; /* Original column name, if an alias */ + char *table; /* Table of column if column was a field */ + char *org_table; /* Org table name, if table was an alias */ + char *db; /* Database for table */ + char *catalog; /* Catalog for table */ + char *def; /* Default value (set by mysql_list_fields) */ + unsigned long length; /* Width of column (create length) */ + unsigned long max_length; /* Max width for selected set */ + unsigned int name_length; + unsigned int org_name_length; + unsigned int table_length; + unsigned int org_table_length; + unsigned int db_length; + unsigned int catalog_length; + unsigned int def_length; + unsigned int flags; /* Diverse flags */ + unsigned int decimals; /* Number of decimals in field */ + unsigned int charsetnr; /* Character set */ + enum mysqlnd_field_types type; /* Type of field. See mysql_com.h for types */ + char *root; + size_t root_len; +} MYSQLND_FIELD; + + + +typedef struct st_mysqlnd_upsert_result +{ + unsigned int warning_count; + unsigned int server_status; + unsigned long long affected_rows; + unsigned long long last_insert_id; +} mysqlnd_upsert_status; + + +typedef struct st_mysqlnd_error_info +{ + char error[MYSQLND_ERRMSG_SIZE+1]; + char sqlstate[MYSQLND_SQLSTATE_LENGTH + 1]; + unsigned int error_no; +} mysqlnd_error_info; + + +typedef struct st_mysqlnd_zval_pcache MYSQLND_ZVAL_PCACHE; +typedef struct st_mysqlnd_thread_zval_pcache MYSQLND_THD_ZVAL_PCACHE; +typedef struct st_mysqlnd_qcache MYSQLND_QCACHE; + + +typedef struct st_mysqlnd_infile_info +{ + php_stream *fd; + int error_no; + char error_msg[MYSQLND_ERRMSG_SIZE + 1]; + const char *filename; +} MYSQLND_INFILE_INFO; + + +/* character set information */ +typedef struct st_mysqlnd_charset +{ + uint nr; + char *name; + char *collation; + uint char_minlen; + uint char_maxlen; + uint dangerous_for_escape_backslash; + uint (*mb_charlen)(uint c); + uint (*mb_valid)(const char *start, const char *end); +} MYSQLND_CHARSET; + + +/* local infile handler */ +typedef struct st_mysqlnd_infile +{ + int (*local_infile_init)(void **ptr, char *filename, void **userdata TSRMLS_DC); + int (*local_infile_read)(void *ptr, char *buf, uint buf_len TSRMLS_DC); + int (*local_infile_error)(void *ptr, char *error_msg, uint error_msg_len TSRMLS_DC); + void (*local_infile_end)(void *ptr TSRMLS_DC); + zval *callback; + void *userdata; +} MYSQLND_INFILE; + +typedef struct st_mysqlnd_option +{ + /* timeouts */ + uint timeout_connect; + uint timeout_read; + uint timeout_write; + + ulong flags; + + /* init commands - we need to send them to server directly after connect */ + uint num_commands; + char **init_commands; + + /* configuration file information */ + char *cfg_file; + char *cfg_section; + + /* SSL information */ + char *ssl_key; + char *ssl_cert; + char *ssl_ca; + char *ssl_capath; + char *ssl_cipher; + zend_bool use_ssl; + + char *charset_name; + /* maximum allowed packet size for communication */ + ulong max_allowed_packet; + + zend_bool numeric_and_datetime_as_unicode; +#ifdef MYSQLND_STRING_TO_INT_CONVERSION + zend_bool int_and_year_as_int; +#endif + unsigned int net_read_buffer_size; +} MYSQLND_OPTION; + + +typedef struct st_mysqlnd_connection MYSQLND; +typedef struct st_mysqlnd_res MYSQLND_RES; +typedef char** MYSQLND_ROW; /* return data as array of strings */ +typedef struct st_mysqlnd_stmt MYSQLND_STMT; +typedef unsigned int MYSQLND_FIELD_OFFSET; + +typedef struct st_mysqlnd_param_bind MYSQLND_PARAM_BIND; + +typedef struct st_mysqlnd_result_bind MYSQLND_RESULT_BIND; + +typedef struct st_mysqlnd_result_metadata MYSQLND_RES_METADATA; +typedef struct st_mysqlnd_buffered_result MYSQLND_RES_BUFFERED; +typedef struct st_mysqlnd_unbuffered_result MYSQLND_RES_UNBUFFERED; + +typedef struct st_mysqlnd_debug MYSQLND_DEBUG; + + +typedef MYSQLND_RES* (*mysqlnd_stmt_use_or_store_func)(MYSQLND_STMT * const TSRMLS_DC); +typedef enum_func_status (*mysqlnd_fetch_row_func)(MYSQLND_RES *result, + void *param, + unsigned int flags, + zend_bool *fetched_anything + TSRMLS_DC); + +typedef struct st_mysqlnd_stats +{ + my_uint64 values[STAT_LAST]; +#ifdef ZTS + MUTEX_T LOCK_access; +#endif +} MYSQLND_STATS; + + +typedef struct st_mysqlnd_net +{ + php_stream *stream; + /* sequence for simple checking of correct packets */ + zend_uchar packet_no; + +#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND + zend_uchar last_command; +#endif + + /* cmd buffer */ + MYSQLND_CMD_BUFFER cmd_buffer; +} MYSQLND_NET; + + +struct st_mysqlnd_conn_methods +{ + ulong (*escape_string)(const MYSQLND * const conn, char *newstr, const char *escapestr, int escapestr_len TSRMLS_DC); + enum_func_status (*set_charset)(MYSQLND * const conn, const char * const charset TSRMLS_DC); + enum_func_status (*query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC); + MYSQLND_RES * (*use_result)(MYSQLND * const conn TSRMLS_DC); + MYSQLND_RES * (*store_result)(MYSQLND * const conn TSRMLS_DC); + enum_func_status (*next_result)(MYSQLND * const conn TSRMLS_DC); + zend_bool (*more_results)(const MYSQLND * const conn); + + MYSQLND_STMT * (*stmt_init)(MYSQLND * const conn TSRMLS_DC); + + enum_func_status (*shutdown_server)(MYSQLND * const conn, unsigned long level TSRMLS_DC); + enum_func_status (*refresh_server)(MYSQLND * const conn, unsigned long options TSRMLS_DC); + + enum_func_status (*ping)(MYSQLND * const conn TSRMLS_DC); + enum_func_status (*kill_connection)(MYSQLND *conn, unsigned int pid TSRMLS_DC); + enum_func_status (*select_db)(MYSQLND * const conn, const char * const db, unsigned int db_len TSRMLS_DC); + enum_func_status (*server_dump_debug_information)(MYSQLND * const conn TSRMLS_DC); + enum_func_status (*change_user)(MYSQLND * const conn, const char * user, const char * passwd, const char * db TSRMLS_DC); + + unsigned int (*get_error_no)(const MYSQLND * const conn); + const char * (*get_error_str)(const MYSQLND * const conn); + const char * (*get_sqlstate)(const MYSQLND * const conn); + mynd_ulonglong (*get_thread_id)(const MYSQLND * const conn); + void (*get_statistics)(const MYSQLND * const conn, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); + + unsigned long (*get_server_version)(const MYSQLND * const conn); + const char * (*get_server_information)(const MYSQLND * const conn); + enum_func_status (*get_server_statistics)(MYSQLND *conn, char **message, unsigned int * message_len TSRMLS_DC); + const char * (*get_host_information)(const MYSQLND * const conn); + unsigned int (*get_protocol_information)(const MYSQLND * const conn); + const char * (*get_last_message)(const MYSQLND * const conn); + const char * (*charset_name)(const MYSQLND * const conn); + MYSQLND_RES * (*list_fields)(MYSQLND *conn, const char *table, const char *achtung_wild TSRMLS_DC); + MYSQLND_RES * (*list_method)(MYSQLND *conn, const char *query, const char *achtung_wild, char *par1 TSRMLS_DC); + + mynd_ulonglong (*get_last_insert_id)(const MYSQLND * const conn); + mynd_ulonglong (*get_affected_rows)(const MYSQLND * const conn); + unsigned int (*get_warning_count)(const MYSQLND * const conn); + + unsigned int (*get_field_count)(const MYSQLND * const conn); + + enum_func_status (*set_server_option)(MYSQLND * const conn, enum_mysqlnd_server_option option TSRMLS_DC); + enum_func_status (*set_client_option)(MYSQLND * const conn, enum_mysqlnd_option option, const char * const value TSRMLS_DC); + void (*free_contents)(MYSQLND *conn TSRMLS_DC); /* private */ + enum_func_status (*close)(MYSQLND *conn, enum_connection_close_type close_type TSRMLS_DC); + void (*dtor)(MYSQLND *conn TSRMLS_DC); /* private */ + + MYSQLND * (*get_reference)(MYSQLND * const conn); + enum_func_status (*free_reference)(MYSQLND * const conn TSRMLS_DC); +}; + + +struct st_mysqlnd_res_methods +{ + mysqlnd_fetch_row_func fetch_row; + mysqlnd_fetch_row_func fetch_row_normal_buffered; /* private */ + mysqlnd_fetch_row_func fetch_row_normal_unbuffered; /* private */ + + MYSQLND_RES * (*use_result)(MYSQLND_RES * const result, zend_bool ps_protocol TSRMLS_DC); + MYSQLND_RES * (*store_result)(MYSQLND_RES * result, MYSQLND * const conn, zend_bool ps TSRMLS_DC); + void (*fetch_into)(MYSQLND_RES *result, unsigned int flags, zval *return_value, enum_mysqlnd_extension ext TSRMLS_DC ZEND_FILE_LINE_DC); + void (*fetch_all)(MYSQLND_RES *result, unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); + void (*fetch_field_data)(MYSQLND_RES *result, unsigned int offset, zval *return_value TSRMLS_DC); + mynd_ulonglong (*num_rows)(const MYSQLND_RES * const result); + unsigned int (*num_fields)(const MYSQLND_RES * const result); + enum_func_status (*skip_result)(MYSQLND_RES * const result TSRMLS_DC); + enum_func_status (*seek_data)(MYSQLND_RES * result, mynd_ulonglong row TSRMLS_DC); + MYSQLND_FIELD_OFFSET (*seek_field)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET field_offset); + MYSQLND_FIELD_OFFSET (*field_tell)(const MYSQLND_RES * const result); + MYSQLND_FIELD * (*fetch_field)(MYSQLND_RES * const result TSRMLS_DC); + MYSQLND_FIELD * (*fetch_field_direct)(const MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC); + + enum_func_status (*read_result_metadata)(MYSQLND_RES *result, MYSQLND *conn TSRMLS_DC); + unsigned long * (*fetch_lengths)(MYSQLND_RES * const result); + void (*free_result_buffers)(MYSQLND_RES * result TSRMLS_DC); /* private */ + enum_func_status (*free_result)(MYSQLND_RES * result, zend_bool implicit TSRMLS_DC); + void (*free_result_internal)(MYSQLND_RES *result TSRMLS_DC); + void (*free_result_contents)(MYSQLND_RES *result TSRMLS_DC); +}; + + +struct st_mysqlnd_res_meta_methods +{ + MYSQLND_FIELD * (*fetch_field)(MYSQLND_RES_METADATA * const meta TSRMLS_DC); + MYSQLND_FIELD * (*fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC); + MYSQLND_FIELD_OFFSET (*field_tell)(const MYSQLND_RES_METADATA * const meta); + enum_func_status (*read_metadata)(MYSQLND_RES_METADATA * const meta, MYSQLND *conn TSRMLS_DC); + MYSQLND_RES_METADATA * (*clone_metadata)(const MYSQLND_RES_METADATA * const meta, zend_bool persistent TSRMLS_DC); + void (*free_metadata)(MYSQLND_RES_METADATA *meta, zend_bool persistent TSRMLS_DC); +}; + + +struct st_mysqlnd_stmt_methods +{ + enum_func_status (*prepare)(MYSQLND_STMT * const stmt, const char * const query, unsigned int query_len TSRMLS_DC); + enum_func_status (*execute)(MYSQLND_STMT * const stmt TSRMLS_DC); + MYSQLND_RES * (*use_result)(MYSQLND_STMT * const stmt TSRMLS_DC); + MYSQLND_RES * (*store_result)(MYSQLND_STMT * const stmt TSRMLS_DC); + MYSQLND_RES * (*get_result)(MYSQLND_STMT * const stmt TSRMLS_DC); + enum_func_status (*free_result)(MYSQLND_STMT * const stmt TSRMLS_DC); + enum_func_status (*seek_data)(const MYSQLND_STMT * const stmt, mynd_ulonglong row TSRMLS_DC); + enum_func_status (*reset)(MYSQLND_STMT * const stmt TSRMLS_DC); + enum_func_status (*close)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC); /* private */ + enum_func_status (*dtor)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC); /* use this for mysqlnd_stmt_close */ + + enum_func_status (*fetch)(MYSQLND_STMT * const stmt, zend_bool * const fetched_anything TSRMLS_DC); + + enum_func_status (*bind_param)(MYSQLND_STMT * const stmt, MYSQLND_PARAM_BIND * const param_bind TSRMLS_DC); + enum_func_status (*bind_result)(MYSQLND_STMT * const stmt, MYSQLND_RESULT_BIND * const result_bind TSRMLS_DC); + enum_func_status (*send_long_data)(MYSQLND_STMT * const stmt, unsigned int param_num, + const char * const data, unsigned long length TSRMLS_DC); + MYSQLND_RES * (*get_parameter_metadata)(MYSQLND_STMT * const stmt); + MYSQLND_RES * (*get_result_metadata)(MYSQLND_STMT * const stmt TSRMLS_DC); + + mynd_ulonglong (*get_last_insert_id)(const MYSQLND_STMT * const stmt); + mynd_ulonglong (*get_affected_rows)(const MYSQLND_STMT * const stmt); + mynd_ulonglong (*get_num_rows)(const MYSQLND_STMT * const stmt); + + unsigned int (*get_param_count)(const MYSQLND_STMT * const stmt); + unsigned int (*get_field_count)(const MYSQLND_STMT * const stmt); + unsigned int (*get_warning_count)(const MYSQLND_STMT * const stmt); + + unsigned int (*get_error_no)(const MYSQLND_STMT * const stmt); + const char * (*get_error_str)(const MYSQLND_STMT * const stmt); + const char * (*get_sqlstate)(const MYSQLND_STMT * const stmt); + + enum_func_status (*get_attribute)(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, void * const value TSRMLS_DC); + enum_func_status (*set_attribute)(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, const void * const value TSRMLS_DC); +}; + + +struct st_mysqlnd_connection +{ +/* Operation related */ + MYSQLND_NET net; + +/* Information related */ + char *host; + char *unix_socket; + char *user; + char *passwd; + unsigned int *passwd_len; + char *scheme; + unsigned long long thread_id; + char *server_version; + char *host_info; + unsigned char *scramble; + const MYSQLND_CHARSET *charset; + const MYSQLND_CHARSET *greet_charset; + MYSQLND_INFILE infile; + unsigned int protocol_version; + unsigned long max_packet_size; + unsigned int port; + unsigned long client_flag; + unsigned long server_capabilities; + + int tmp_int; + + + /* For UPSERT queries */ + mysqlnd_upsert_status upsert_status; + char *last_message; + unsigned int last_message_len; + + /* If error packet, we use these */ + mysqlnd_error_info error_info; + + /* + To prevent queries during unbuffered fetches. Also to + mark the connection as destroyed for garbage collection. + */ + enum mysqlnd_connection_state state; + enum_mysqlnd_query_type last_query_type; + /* Temporary storage between query and (use|store)_result() call */ + MYSQLND_RES *current_result; + + /* + How many result sets reference this connection. + It won't be freed until this number reaches 0. + The last one, please close the door! :-) + The result set objects can determine by inspecting + 'quit_sent' whether the connection is still valid. + */ + unsigned int refcount; + + /* Temporal storage for mysql_query */ + unsigned int field_count; + + /* persistent connection */ + zend_bool persistent; + + /* options */ + MYSQLND_OPTION options; + + /* zval cache */ + MYSQLND_THD_ZVAL_PCACHE *zval_cache; + + /* qcache */ + MYSQLND_QCACHE *qcache; + + /* stats */ + MYSQLND_STATS stats; + + struct st_mysqlnd_conn_methods *m; +}; + +typedef struct st_php_mysql_packet_row php_mysql_packet_row; + + +struct mysqlnd_field_hash_key +{ + zend_bool is_numeric; + unsigned long key; +#if PHP_MAJOR_VERSION >= 6 + zstr ustr; + unsigned int ulen; +#endif +}; + + +struct st_mysqlnd_result_metadata +{ + MYSQLND_FIELD *fields; + struct mysqlnd_field_hash_key *zend_hash_keys; + unsigned int current_field; + unsigned int field_count; + /* We need this to make fast allocs in rowp_read */ + unsigned int bit_fields_count; + size_t bit_fields_total_len; /* trailing \0 not counted */ + + struct st_mysqlnd_res_meta_methods *m; +}; + + +struct st_mysqlnd_buffered_result +{ + zval ***data; + zval ***data_cursor; + zend_uchar **row_buffers; + mynd_ulonglong row_count; + zend_bool persistent; + + MYSQLND_QCACHE *qcache; + unsigned int references; + + zend_bool async_invalid; + mysqlnd_error_info error_info; +}; + + +struct st_mysqlnd_unbuffered_result +{ + /* For unbuffered (both normal and PS) */ + zval **last_row_data; + zend_uchar *last_row_buffer; + + mynd_ulonglong row_count; + zend_bool eof_reached; +}; + + +struct st_mysqlnd_res +{ + struct st_mysqlnd_res_methods m; + + MYSQLND *conn; + enum_mysqlnd_res_type type; + unsigned int field_count; + + /* For metadata functions */ + MYSQLND_RES_METADATA *meta; + + /* To be used with store_result() - both normal and PS */ + MYSQLND_RES_BUFFERED *data; + + MYSQLND_RES_UNBUFFERED *unbuf; + + /* + Column lengths of current row - both buffered and unbuffered. + For buffered results it duplicates the data found in **data + */ + unsigned long *lengths; + + php_mysql_packet_row *row_packet; /* Unused for PS */ + + /* zval cache */ + MYSQLND_THD_ZVAL_PCACHE *zval_cache; +}; + + +struct st_mysqlnd_param_bind +{ + zval *zv; + zend_uchar type; + enum_param_bind_flags flags; +}; + +struct st_mysqlnd_result_bind +{ + zval *zv; + zend_uchar original_type; + zend_bool bound; +}; + + +struct st_mysqlnd_stmt +{ + MYSQLND *conn; + unsigned long stmt_id; + unsigned long flags;/* cursor is set here */ + enum_mysqlnd_stmt_state state; + unsigned int warning_count; + MYSQLND_RES *result; + unsigned int field_count; + unsigned int param_count; + unsigned char send_types_to_server; + MYSQLND_PARAM_BIND *param_bind; + MYSQLND_RESULT_BIND *result_bind; + zend_bool result_zvals_separated_once; + + mysqlnd_upsert_status upsert_status; + + mysqlnd_error_info error_info; + + zend_bool update_max_length; + unsigned long prefetch_rows; + + zend_bool cursor_exists; + mysqlnd_stmt_use_or_store_func default_rset_handler; + + MYSQLND_CMD_BUFFER cmd_buffer; + unsigned int execute_count;/* count how many times the stmt was executed */ + + struct st_mysqlnd_stmt_methods *m; +}; + +#endif /* MYSQLND_STRUCTS_H */ diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c index b82b351887..73179281fa 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.c +++ b/ext/mysqlnd/mysqlnd_wireprotocol.c @@ -24,17 +24,22 @@ #include "mysqlnd_wireprotocol.h" #include "mysqlnd_statistics.h" #include "mysqlnd_palloc.h" +#include "mysqlnd_debug.h" #include "ext/standard/sha1.h" #include "php_network.h" +#include "zend_ini.h" + #ifndef PHP_WIN32 #include <netinet/tcp.h> #else - +#include <winsock.h> #endif + #define USE_CORK 0 -#define MYSQLND_SILENT +#define MYSQLND_SILENT 1 + #define MYSQLND_DUMP_HEADER_N_BODY2 #define MYSQLND_DUMP_HEADER_N_BODY_FULL2 @@ -43,16 +48,23 @@ #define PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_size, packet_type) \ { \ if (FAIL == mysqlnd_read_header((conn), &((packet)->header) TSRMLS_CC)) {\ - return FAIL;\ + conn->state = CONN_QUIT_SENT; \ + SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysqlnd_server_gone); \ + DBG_ERR_FMT("Can't read %s's header", (packet_type)); \ + DBG_RETURN(FAIL);\ }\ if ((buf_size) < (packet)->header.size) { \ - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Packet buffer wasn't big enough" \ - "%u bytes will be unread", (packet)->header.size - (buf_size));\ + DBG_ERR_FMT("Packet buffer wasn't big enough %u bytes will be unread", \ + (packet)->header.size - (buf_size)); \ }\ if (!mysqlnd_read_body((conn), (buf), \ MIN((buf_size), (packet)->header.size) TSRMLS_CC)) { \ - php_error(E_WARNING, "Empty %s packet body", (packet_type));\ - return FAIL; \ + conn->state = CONN_QUIT_SENT; \ + SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysqlnd_server_gone); \ + DBG_ERR_FMT("Empty %s packet body", (packet_type)); \ + DBG_RETURN(FAIL);\ } \ } @@ -63,6 +75,10 @@ static const char *unknown_sqlstate= "HY000"; char * const mysqlnd_empty_string = ""; +/* Used in mysqlnd_debug.c */ +char * mysqlnd_read_header_name = "mysqlnd_read_header"; +char * mysqlnd_read_body_name = "mysqlnd_read_body"; + /* {{{ mysqlnd_command_to_text */ @@ -181,6 +197,8 @@ size_t php_mysqlnd_consume_uneaten_data(MYSQLND * const conn, enum php_mysqlnd_s int opt = PHP_STREAM_OPTION_BLOCKING; int was_blocked = net->stream->ops->set_option(net->stream, opt, 0, NULL TSRMLS_CC); + DBG_ENTER("php_mysqlnd_consume_uneaten_data"); + if (PHP_STREAM_OPTION_RETURN_ERR != was_blocked) { /* Do a read of 1 byte */ int bytes_consumed; @@ -194,26 +212,32 @@ size_t php_mysqlnd_consume_uneaten_data(MYSQLND * const conn, enum php_mysqlnd_s } if (bytes_consumed) { + DBG_ERR_FMT("Skipped %u bytes. Last command %s hasn't consumed all the output from the server", + bytes_consumed, mysqlnd_command_to_text[net->last_command]); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Skipped %u bytes. Last command %s hasn't " - "consumed all the output from the server", - bytes_consumed, mysqlnd_command_to_text[net->last_command]); + "consumed all the output from the server. PID=%d", + bytes_consumed, mysqlnd_command_to_text[net->last_command], getpid()); } } net->last_command = cmd; - return skipped_bytes; + DBG_RETURN(skipped_bytes); } #endif /* }}} */ + /* {{{ php_mysqlnd_read_error_from_line */ static enum_func_status php_mysqlnd_read_error_from_line(zend_uchar *buf, size_t buf_len, char *error, int error_buf_len, - unsigned int *error_no, char *sqlstate) + unsigned int *error_no, char *sqlstate TSRMLS_DC) { zend_uchar *p = buf; int error_msg_len= 0; + + DBG_ENTER("php_mysqlnd_read_error_from_line"); + if (buf_len > 2) { *error_no = uint2korr(p); p+= 2; @@ -231,7 +255,8 @@ enum_func_status php_mysqlnd_read_error_from_line(zend_uchar *buf, size_t buf_le } sqlstate[MYSQLND_SQLSTATE_LENGTH] = '\0'; error[error_msg_len]= '\0'; - return FAIL; + + DBG_RETURN(FAIL); } /* }}} */ @@ -239,15 +264,20 @@ enum_func_status php_mysqlnd_read_error_from_line(zend_uchar *buf, size_t buf_le /* {{{ mysqlnd_set_sock_no_delay */ int mysqlnd_set_sock_no_delay(php_stream *stream) { + int socketd = ((php_netstream_data_t*)stream->abstract)->socket; int ret = SUCCESS; int flag = 1; int result = setsockopt(socketd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); + TSRMLS_FETCH(); + + DBG_ENTER("mysqlnd_set_sock_no_delay"); + if (result == -1) { ret = FAILURE; } - return ret; + DBG_RETURN(ret); } /* }}} */ @@ -270,12 +300,15 @@ int mysqlnd_set_sock_no_delay(php_stream *stream) size_t mysqlnd_stream_write_w_header(MYSQLND * const conn, char * const buf, size_t count TSRMLS_DC) { zend_uchar safe_buf[((MYSQLND_HEADER_SIZE) + (sizeof(zend_uchar)) - 1) / (sizeof(zend_uchar))]; - zend_uchar *safe_storage = (char *) &safe_buf; + zend_uchar *safe_storage = safe_buf; MYSQLND_NET *net = &conn->net; size_t old_chunk_size = net->stream->chunk_size; size_t ret, left = count, packets_sent = 1; zend_uchar *p = (zend_uchar *) buf; + DBG_ENTER("mysqlnd_stream_write_w_header"); + DBG_INF_FMT("conn=%llu count=%lu", conn->thread_id, count); + net->stream->chunk_size = MYSQLND_MAX_PACKET_SIZE; while (left > MYSQLND_MAX_PACKET_SIZE) { @@ -299,6 +332,12 @@ size_t mysqlnd_stream_write_w_header(MYSQLND * const conn, char * const buf, siz ret = php_stream_write(net->stream, (char *)p, left + MYSQLND_HEADER_SIZE); RESTORE_HEADER_SIZE(p, safe_storage); + if (!ret) { + DBG_ERR_FMT("Can't %u send bytes", count); + conn->state = CONN_QUIT_SENT; + SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone); + } + MYSQLND_INC_CONN_STATISTIC_W_VALUE3(&conn->stats, STAT_BYTES_SENT, count + packets_sent * MYSQLND_HEADER_SIZE, STAT_PROTOCOL_OVERHEAD_OUT, packets_sent * MYSQLND_HEADER_SIZE, @@ -306,7 +345,7 @@ size_t mysqlnd_stream_write_w_header(MYSQLND * const conn, char * const buf, siz net->stream->chunk_size = old_chunk_size; - return ret; + DBG_RETURN(ret); } /* }}} */ @@ -326,6 +365,8 @@ size_t mysqlnd_stream_write_w_command(MYSQLND * const conn, enum php_mysqlnd_ser zend_bool command_sent = FALSE; int corked = 1; + DBG_ENTER("mysqlnd_stream_write_w_command"); + net->stream->chunk_size = MYSQLND_MAX_PACKET_SIZE; setsockopt(((php_netstream_data_t*)net->stream->abstract)->socket, @@ -375,7 +416,7 @@ size_t mysqlnd_stream_write_w_command(MYSQLND * const conn, enum php_mysqlnd_ser net->stream->chunk_size = old_chunk_size; - return ret; + DBG_RETURN(ret); } #endif /* }}} */ @@ -390,9 +431,11 @@ mysqlnd_read_header(MYSQLND *conn, mysqlnd_packet_header *header TSRMLS_DC) char *p = buffer; int to_read = MYSQLND_HEADER_SIZE, ret; + DBG_ENTER(mysqlnd_read_header_name); + do { if (!(ret= php_stream_read(net->stream, p, to_read))) { - php_error(E_WARNING, "Error while reading header from socket"); + DBG_ERR_FMT("Error while reading header from socket"); return FAIL; } p += ret; @@ -415,14 +458,18 @@ mysqlnd_read_header(MYSQLND *conn, mysqlnd_packet_header *header TSRMLS_DC) */ net->packet_no++; #ifdef MYSQLND_DUMP_HEADER_N_BODY - php_printf("\nHEADER: packet_no=%d size=%3d\n", header->packet_no, header->size); + DBG_ERR_FMT("HEADER: packet_no=%d size=%3d", header->packet_no, header->size); #endif - return PASS; + DBG_RETURN(PASS); } - php_error(E_WARNING, "Packets out of order. Expected %d received %d. Packet size=%d", - net->packet_no, header->packet_no, header->size); - return FAIL; +#if !MYSQLND_SILENT + DBG_ERR_FMT("Packets out of order. Expected %d received %d. Packet size=%d", + net->packet_no, header->packet_no, header->size); +#endif + php_error(E_WARNING, "Packets out of order. Expected %d received %d. Packet size=%d. PID=%d", + net->packet_no, header->packet_no, header->size, getpid()); + DBG_RETURN(FAIL); } /* }}} */ @@ -433,34 +480,47 @@ size_t mysqlnd_read_body(MYSQLND *conn, zend_uchar *buf, size_t size TSRMLS_DC) { size_t ret; char *p = (char *)buf; -#ifdef MYSQLND_DUMP_HEADER_N_BODY int iter = 0; -#endif MYSQLND_NET *net = &conn->net; + size_t old_chunk_size = net->stream->chunk_size; + DBG_ENTER(mysqlnd_read_body_name); + DBG_INF_FMT("chunk_size=%d", net->stream->chunk_size); + + net->stream->chunk_size = MIN(size, conn->options.net_read_buffer_size); do { size -= (ret = php_stream_read(net->stream, p, size)); -#ifdef MYSQLND_DUMP_HEADER_N_BODY if (size || iter++) { - php_printf("read=%d buf=%p p=%p chunk_size=%d left=%d\n", + DBG_INF_FMT("read=%d buf=%p p=%p chunk_size=%d left=%d", ret, buf, p , net->stream->chunk_size, size); } -#endif p += ret; } while (size > 0); MYSQLND_INC_CONN_STATISTIC_W_VALUE(&conn->stats, STAT_BYTES_RECEIVED, p - (char*)buf); + net->stream->chunk_size = old_chunk_size; #ifdef MYSQLND_DUMP_HEADER_N_BODY_FULL { int i; - php_printf("\tBODY: requested=%d last_read=%3d\n\t", p - (char*)buf, ret); - for (i = 0 ; i < p - (char*)buf; i++) printf("[%c]", *(char *)(&(buf[i]))); php_printf("\n\t"); - for (i = 0 ; i < p - (char*)buf; i++) printf("%.2X ", (int)*((char*)&(buf[i])));php_printf("\n-=-=-=-=-\n"); + DBG_INF_FMT("BODY: requested=%d last_read=%3d", p - (char*)buf, ret); + for (i = 0 ; i < p - (char*)buf; i++) { + if (i && (i % 30 == 0)) { + printf("\n\t\t"); + } + printf("[%c] ", *(char *)(&(buf[i]))); + } + for (i = 0 ; i < p - (char*)buf; i++) { + if (i && (i % 30 == 0)) { + printf("\n\t\t"); + } + printf("%.2X ", (int)*((char*)&(buf[i]))); + } + php_printf("\n\t\t\t-=-=-=-=-\n"); } #endif - return p - (char*)buf; + DBG_RETURN(p - (char*)buf); } /* }}} */ @@ -474,6 +534,8 @@ php_mysqlnd_greet_read(void *_packet, MYSQLND *conn TSRMLS_DC) zend_uchar *begin = buf; php_mysql_packet_greet *packet= (php_mysql_packet_greet *) _packet; + DBG_ENTER("php_mysqlnd_greet_read"); + PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "greeting"); packet->protocol_version = uint1korr(p); @@ -482,7 +544,8 @@ php_mysqlnd_greet_read(void *_packet, MYSQLND *conn TSRMLS_DC) if (packet->protocol_version == 0xFF) { php_mysqlnd_read_error_from_line(p, packet->header.size - 1, packet->error, sizeof(packet->error), - &packet->error_no, packet->sqlstate); + &packet->error_no, packet->sqlstate + TSRMLS_CC); /* The server doesn't send sqlstate in the greet packet. It's a bug#26426 , so we have to set it correctly ourselves. @@ -491,8 +554,7 @@ php_mysqlnd_greet_read(void *_packet, MYSQLND *conn TSRMLS_DC) if (packet->error_no == 1040) { memcpy(packet->sqlstate, "08004", MYSQLND_SQLSTATE_LENGTH); } - return PASS; - + DBG_RETURN(PASS); } packet->server_version = pestrdup((char *)p, conn->persistent); @@ -527,26 +589,27 @@ php_mysqlnd_greet_read(void *_packet, MYSQLND *conn TSRMLS_DC) packet->pre41 = TRUE; } if (p - begin > packet->header.size) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet %d bytes shorter than expected", - p - begin - packet->header.size); + DBG_ERR_FMT("GREET packet %d bytes shorter than expected", p - begin - packet->header.size); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet %d bytes shorter than expected. PID=%d", + p - begin - packet->header.size, getpid()); } - return PASS; + DBG_RETURN(PASS); } /* }}} */ /* {{{ php_mysqlnd_greet_free_mem */ static -void php_mysqlnd_greet_free_mem(void *_packet, zend_bool alloca) +void php_mysqlnd_greet_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) { php_mysql_packet_greet *p= (php_mysql_packet_greet *) _packet; if (p->server_version) { - efree(p->server_version); + mnd_efree(p->server_version); p->server_version = NULL; } if (!alloca) { - efree(p); + mnd_efree(p); } } /* }}} */ @@ -608,6 +671,8 @@ size_t php_mysqlnd_auth_write(void *_packet, MYSQLND *conn TSRMLS_DC) int len; register php_mysql_packet_auth *packet= (php_mysql_packet_auth *) _packet; + DBG_ENTER("php_mysqlnd_auth_write"); + packet->client_flags |= MYSQLND_CAPABILITIES; if (packet->db) { @@ -659,16 +724,16 @@ size_t php_mysqlnd_auth_write(void *_packet, MYSQLND *conn TSRMLS_DC) /* Handle CLIENT_CONNECT_WITH_DB */ /* no \0 for no DB */ - return mysqlnd_stream_write_w_header(conn, buffer, p - buffer - MYSQLND_HEADER_SIZE TSRMLS_CC); + DBG_RETURN(mysqlnd_stream_write_w_header(conn, buffer, p - buffer - MYSQLND_HEADER_SIZE TSRMLS_CC)); } /* }}} */ /* {{{ php_mysqlnd_auth_free_mem */ static -void php_mysqlnd_auth_free_mem(void *_packet, zend_bool alloca) +void php_mysqlnd_auth_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) { if (!alloca) { - efree((php_mysql_packet_auth *) _packet); + mnd_efree((php_mysql_packet_auth *) _packet); } } /* }}} */ @@ -684,6 +749,8 @@ php_mysqlnd_ok_read(void *_packet, MYSQLND *conn TSRMLS_DC) int i; register php_mysql_packet_ok *packet= (php_mysql_packet_ok *) _packet; + DBG_ENTER("php_mysqlnd_ok_read"); + PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "OK"); /* Should be always 0x0 or 0xFF for error */ @@ -693,8 +760,9 @@ php_mysqlnd_ok_read(void *_packet, MYSQLND *conn TSRMLS_DC) if (0xFF == packet->field_count) { php_mysqlnd_read_error_from_line(p, packet->header.size - 1, packet->error, sizeof(packet->error), - &packet->error_no, packet->sqlstate); - return PASS; + &packet->error_no, packet->sqlstate + TSRMLS_CC); + DBG_RETURN(PASS); } /* Everything was fine! */ packet->affected_rows = php_mysqlnd_net_field_length_ll(&p); @@ -714,32 +782,32 @@ php_mysqlnd_ok_read(void *_packet, MYSQLND *conn TSRMLS_DC) packet->message = NULL; } -#ifndef MYSQLND_SILENT - php_printf("OK packet: aff_rows=%lld last_ins_id=%ld server_status=%d warnings=%d\n", - packet->affected_rows, packet->last_insert_id, packet->server_status, - packet->warning_count); -#endif + DBG_INF_FMT("OK packet: aff_rows=%lld last_ins_id=%ld server_status=%d warnings=%d", + packet->affected_rows, packet->last_insert_id, packet->server_status, + packet->warning_count); + if (p - begin > packet->header.size) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "OK packet %d bytes shorter than expected", - p - begin - packet->header.size); + DBG_ERR_FMT("OK packet %d bytes shorter than expected", p - begin - packet->header.size); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "OK packet %d bytes shorter than expected. PID=%d", + p - begin - packet->header.size, getpid()); } - return PASS; + DBG_RETURN(PASS); } /* }}} */ /* {{{ php_mysqlnd_ok_free_mem */ static -void php_mysqlnd_ok_free_mem(void *_packet, zend_bool alloca) +void php_mysqlnd_ok_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) { php_mysql_packet_ok *p= (php_mysql_packet_ok *) _packet; if (p->message) { - efree(p->message); + mnd_efree(p->message); p->message = NULL; } if (!alloca) { - efree(p); + mnd_efree(p); } } /* }}} */ @@ -760,6 +828,8 @@ php_mysqlnd_eof_read(void *_packet, MYSQLND *conn TSRMLS_DC) zend_uchar *p= buf; zend_uchar *begin = buf; + DBG_ENTER("php_mysqlnd_eof_read"); + PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "EOF"); /* Should be always 0xFE */ @@ -769,8 +839,9 @@ php_mysqlnd_eof_read(void *_packet, MYSQLND *conn TSRMLS_DC) if (0xFF == packet->field_count) { php_mysqlnd_read_error_from_line(p, packet->header.size - 1, packet->error, sizeof(packet->error), - &packet->error_no, packet->sqlstate); - return PASS; + &packet->error_no, packet->sqlstate + TSRMLS_CC); + DBG_RETURN(PASS); } /* @@ -789,26 +860,24 @@ php_mysqlnd_eof_read(void *_packet, MYSQLND *conn TSRMLS_DC) } if (p - begin > packet->header.size) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet %d bytes shorter than expected", - p - begin - packet->header.size); + DBG_ERR_FMT("EOF packet %d bytes shorter than expected", p - begin - packet->header.size); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet %d bytes shorter than expected. PID=%d", + p - begin - packet->header.size, getpid()); } -#ifndef MYSQLND_SILENT - php_printf("EOF packet: server_status=%d warnings=%d\n", - packet->server_status, packet->warning_count); -#endif + DBG_INF_FMT("EOF packet: status=%d warnings=%d", packet->server_status, packet->warning_count); - return PASS; + DBG_RETURN(PASS); } /* }}} */ /* {{{ php_mysqlnd_eof_free_mem */ static -void php_mysqlnd_eof_free_mem(void *_packet, zend_bool alloca) +void php_mysqlnd_eof_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) { if (!alloca) { - efree(_packet); + mnd_efree(_packet); } } /* }}} */ @@ -820,13 +889,20 @@ size_t php_mysqlnd_cmd_write(void *_packet, MYSQLND *conn TSRMLS_DC) /* Let's have some space, which we can use, if not enough, we will allocate new buffer */ php_mysql_packet_command *packet= (php_mysql_packet_command *) _packet; MYSQLND_NET *net = &conn->net; + unsigned int error_reporting = EG(error_reporting); + size_t written; + DBG_ENTER("php_mysqlnd_cmd_write"); /* Reset packet_no, or we will get bad handshake! Every command starts a new TX and packet numbers are reset to 0. */ net->packet_no = 0; + if (error_reporting) { + EG(error_reporting) = 0; + } + #ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND php_mysqlnd_consume_uneaten_data(conn, packet->command TSRMLS_CC); #endif @@ -835,14 +911,15 @@ size_t php_mysqlnd_cmd_write(void *_packet, MYSQLND *conn TSRMLS_DC) char buffer[MYSQLND_HEADER_SIZE + 1]; int1store(buffer + MYSQLND_HEADER_SIZE, packet->command); - return mysqlnd_stream_write_w_header(conn, buffer, 1 TSRMLS_CC); + written = mysqlnd_stream_write_w_header(conn, buffer, 1 TSRMLS_CC); } else { #if USE_CORK && defined(TCP_CORK) - return mysqlnd_stream_write_w_command(conn, packet->command, packet->argument, packet->arg_len TSRMLS_CC); + written = mysqlnd_stream_write_w_command(conn, packet->command, packet->argument, + packet->arg_len TSRMLS_CC)); #else size_t tmp_len = packet->arg_len + 1 + MYSQLND_HEADER_SIZE, ret; zend_uchar *tmp, *p; - tmp = (tmp_len > net->cmd_buffer.length)? emalloc(tmp_len):net->cmd_buffer.buffer; + tmp = (tmp_len > net->cmd_buffer.length)? mnd_emalloc(tmp_len):net->cmd_buffer.buffer; p = tmp + MYSQLND_HEADER_SIZE; /* skip the header */ int1store(p, packet->command); @@ -853,21 +930,26 @@ size_t php_mysqlnd_cmd_write(void *_packet, MYSQLND *conn TSRMLS_DC) ret = mysqlnd_stream_write_w_header(conn, (char *)tmp, tmp_len - MYSQLND_HEADER_SIZE TSRMLS_CC); if (tmp != net->cmd_buffer.buffer) { MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CMD_BUFFER_TOO_SMALL); - efree(tmp); + mnd_efree(tmp); } - return ret; + written = ret; #endif } + if (error_reporting) { + /* restore error reporting */ + EG(error_reporting) = error_reporting; + } + DBG_RETURN(written); } /* }}} */ /* {{{ php_mysqlnd_cmd_free_mem */ static -void php_mysqlnd_cmd_free_mem(void *_packet, zend_bool alloca) +void php_mysqlnd_cmd_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) { if (!alloca) { - efree((php_mysql_packet_command *) _packet); + mnd_efree((php_mysql_packet_command *) _packet); } } /* }}} */ @@ -883,6 +965,8 @@ php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC) size_t len; php_mysql_packet_rset_header *packet= (php_mysql_packet_rset_header *) _packet; + DBG_ENTER("php_mysqlnd_rset_header_read"); + PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "resultset header"); /* @@ -894,8 +978,9 @@ php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC) p++; php_mysqlnd_read_error_from_line(p, packet->header.size - 1, packet->error_info.error, sizeof(packet->error_info.error), - &packet->error_info.error_no, packet->error_info.sqlstate); - return PASS; + &packet->error_info.error_no, packet->error_info.sqlstate + TSRMLS_CC); + DBG_RETURN(PASS); } packet->field_count= php_mysqlnd_net_field_length(&p); @@ -906,7 +991,7 @@ php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC) Thus, the name is size - 1. And we add 1 for a trailing \0. */ len = packet->header.size - 1; - packet->info_or_local_file = pemalloc(len + 1, conn->persistent); + packet->info_or_local_file = mnd_pemalloc(len + 1, conn->persistent); memcpy(packet->info_or_local_file, p, len); packet->info_or_local_file[len] = '\0'; packet->info_or_local_file_len = len; @@ -920,7 +1005,7 @@ php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC) p+=2; /* Check for additional textual data */ if (packet->header.size > (p - buf) && (len = php_mysqlnd_net_field_length(&p))) { - packet->info_or_local_file = pemalloc(len + 1, conn->persistent); + packet->info_or_local_file = mnd_pemalloc(len + 1, conn->persistent); memcpy(packet->info_or_local_file, p, len); packet->info_or_local_file[len] = '\0'; packet->info_or_local_file_len = len; @@ -931,26 +1016,27 @@ php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC) break; } if (p - begin > packet->header.size) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet %d bytes shorter than expected", - p - begin - packet->header.size); + DBG_ERR_FMT("GREET packet %d bytes shorter than expected", p - begin - packet->header.size); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet %d bytes shorter than expected. PID=%d", + p - begin - packet->header.size, getpid()); } - return PASS; + DBG_RETURN(PASS); } /* }}} */ /* {{{ php_mysqlnd_rset_header_free_mem */ static -void php_mysqlnd_rset_header_free_mem(void *_packet, zend_bool alloca) +void php_mysqlnd_rset_header_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) { php_mysql_packet_rset_header *p= (php_mysql_packet_rset_header *) _packet; if (p->info_or_local_file) { - efree(p->info_or_local_file); + mnd_efree(p->info_or_local_file); p->info_or_local_file = NULL; } if (!alloca) { - efree(p); + mnd_efree(p); } } /* }}} */ @@ -986,16 +1072,25 @@ php_mysqlnd_rset_field_read(void *_packet, MYSQLND *conn TSRMLS_DC) MYSQLND_FIELD *meta; unsigned int i, field_count = sizeof(rset_field_offsets)/sizeof(size_t); + DBG_ENTER("php_mysqlnd_rset_field_read"); + PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "field"); if (packet->skip_parsing) { - return PASS; + DBG_RETURN(PASS); + } + if (*p == 0xFE && packet->header.size < 8) { + /* Premature EOF. That should be COM_FIELD_LIST */ + DBG_INF("Premature EOF. That should be COM_FIELD_LIST"); + packet->stupid_list_fields_eof = TRUE; + DBG_RETURN(PASS); } meta = packet->metadata; for (i = 0; i < field_count; i += 2) { - switch ((len = php_mysqlnd_net_field_length(&p))) { + len = php_mysqlnd_net_field_length(&p); + switch ((len)) { case 0: *(char **)(((char*)meta) + rset_field_offsets[i]) = mysqlnd_empty_string; *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = 0; @@ -1003,7 +1098,7 @@ php_mysqlnd_rset_field_read(void *_packet, MYSQLND *conn TSRMLS_DC) case MYSQLND_NULL_LENGTH: goto faulty_fake; default: - *(char **)(((char *)meta) + rset_field_offsets[i]) = p; + *(char **)(((char *)meta) + rset_field_offsets[i]) = (char *)p; *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = len; p += len; total_len += len + 1; @@ -1042,9 +1137,17 @@ php_mysqlnd_rset_field_read(void *_packet, MYSQLND *conn TSRMLS_DC) } - /* def could be empty, thus don't allocate on the root */ - if (packet->header.size > (p - buf) && (len = php_mysqlnd_net_field_length(&p))) { - meta->def = emalloc(len + 1); + /* + def could be empty, thus don't allocate on the root. + NULL_LENGTH (0xFB) comes from COM_FIELD_LIST when the default value is NULL. + Otherwise the string is length encoded. + */ + if (packet->header.size > (p - buf) && + (len = php_mysqlnd_net_field_length(&p)) && + len != MYSQLND_NULL_LENGTH) + { + DBG_INF_FMT("Def found, length %lu", len); + meta->def = mnd_emalloc(len + 1); memcpy(meta->def, p, len); meta->def[len] = '\0'; meta->def_length = len; @@ -1052,11 +1155,12 @@ php_mysqlnd_rset_field_read(void *_packet, MYSQLND *conn TSRMLS_DC) } if (p - begin > packet->header.size) { + DBG_ERR_FMT("Result set field packet %d bytes shorter than expected", p - begin - packet->header.size); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Result set field packet %d bytes " - "shorter than expected", p - begin - packet->header.size); + "shorter than expected. PID=%d", p - begin - packet->header.size, getpid()); } - root_ptr = meta->root = emalloc(total_len); + root_ptr = meta->root = mnd_emalloc(total_len); meta->root_len = total_len; /* Now do allocs */ if (meta->catalog && meta->catalog != mysqlnd_empty_string) { @@ -1099,32 +1203,30 @@ php_mysqlnd_rset_field_read(void *_packet, MYSQLND *conn TSRMLS_DC) *(root_ptr +=len) = '\0'; root_ptr++; } - -#ifndef MYSQLND_SILENT - php_printf("\tFIELD=[%s.%s.%s]\n", meta->db? meta->db:"*NA*", - meta->table? meta->table:"*NA*", - meta->name? meta->name:"*NA*"); -#endif - - return PASS; +/* + DBG_INF_FMT("FIELD=[%s.%s.%s]", meta->db? meta->db:"*NA*", meta->table? meta->table:"*NA*", + meta->name? meta->name:"*NA*"); +*/ + DBG_RETURN(PASS); faulty_fake: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Protocol error. Server sent NULL_LENGTH.", + DBG_ERR_FMT("Protocol error. Server sent NULL_LENGTH. The server is faulty"); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Protocol error. Server sent NULL_LENGTH." " The server is faulty"); - return FAIL; + DBG_RETURN(FAIL); } /* }}} */ /* {{{ php_mysqlnd_rset_field_free_mem */ static -void php_mysqlnd_rset_field_free_mem(void *_packet, zend_bool alloca) +void php_mysqlnd_rset_field_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) { php_mysql_packet_res_field *p= (php_mysql_packet_res_field *) _packet; /* p->metadata was passed to us as temporal buffer */ if (!alloca) { - efree(p); + mnd_efree(p); } } /* }}} */ @@ -1139,7 +1241,8 @@ php_mysqlnd_read_row_ex(MYSQLND *conn, zend_uchar **buf, int buf_size, mysqlnd_packet_header header; zend_uchar *new_buf = NULL, *p = *buf; zend_bool first_iteration = TRUE; - MYSQLND_NET *net = &conn->net; + + DBG_ENTER("php_mysqlnd_read_row_ex"); /* To ease the process the server splits everything in packets up to 2^24 - 1. @@ -1164,8 +1267,7 @@ php_mysqlnd_read_row_ex(MYSQLND *conn, zend_uchar **buf, int buf_size, We need a trailing \0 for the last string, in case of text-mode, to be able to implement read-only variables. Thus, we add + 1. */ - p = new_buf = pemalloc(*data_size + 1, persistent_alloc); - net->stream->chunk_size = header.size; + p = new_buf = mnd_pemalloc(*data_size + 1, persistent_alloc); } else if (!first_iteration) { /* Empty packet after MYSQLND_MAX_PACKET_SIZE packet. That's ok, break */ if (!header.size) { @@ -1178,13 +1280,14 @@ php_mysqlnd_read_row_ex(MYSQLND *conn, zend_uchar **buf, int buf_size, We need a trailing \0 for the last string, in case of text-mode, to be able to implement read-only variables. */ - new_buf = perealloc(new_buf, *data_size + 1, persistent_alloc); + new_buf = mnd_perealloc(new_buf, *data_size + 1, persistent_alloc); /* The position could have changed, recalculate */ p = new_buf + (*data_size - header.size); } if (!mysqlnd_read_body(conn, p, header.size TSRMLS_CC)) { - php_error(E_WARNING, "Empty row packet body"); + DBG_ERR("Empty row packet body"); + php_error(E_WARNING, "Empty row packet body. PID=%d", getpid()); ret = FAIL; break; } @@ -1197,7 +1300,7 @@ php_mysqlnd_read_row_ex(MYSQLND *conn, zend_uchar **buf, int buf_size, *buf = new_buf; } *data_size -= prealloc_more_bytes; - return ret; + DBG_RETURN(ret); } @@ -1213,6 +1316,8 @@ void php_mysqlnd_rowp_read_binary_protocol(php_mysql_packet_row *packet, MYSQLND zend_bool allocated; void *obj; + DBG_ENTER("php_mysqlnd_rowp_read_binary_protocol"); + end_field = (current_field = start_field = packet->fields) + packet->field_count; @@ -1224,7 +1329,7 @@ void php_mysqlnd_rowp_read_binary_protocol(php_mysql_packet_row *packet, MYSQLND for (i = 0; current_field < end_field; current_field++, i++) { #if 1 - obj = mysqlnd_palloc_get_zval(conn->zval_cache, &allocated); + obj = mysqlnd_palloc_get_zval(conn->zval_cache, &allocated TSRMLS_CC); if (allocated) { *current_field = (zval *) obj; } else { @@ -1249,6 +1354,8 @@ void php_mysqlnd_rowp_read_binary_protocol(php_mysql_packet_row *packet, MYSQLND } /* Normal queries: The buffer has one more byte at the end, because we need it */ packet->row_buffer[data_size] = '\0'; + + DBG_VOID_RETURN; } /* }}} */ @@ -1267,6 +1374,8 @@ void php_mysqlnd_rowp_read_text_protocol(php_mysql_packet_row *packet, MYSQLND * zend_bool as_int = conn->options.int_and_year_as_int; #endif + DBG_ENTER("php_mysqlnd_rowp_read_text_protocol"); + end_field = (current_field = start_field = packet->fields) + packet->field_count; for (i = 0; current_field < end_field; current_field++, i++) { /* Don't reverse the order. It is significant!*/ @@ -1276,7 +1385,7 @@ void php_mysqlnd_rowp_read_text_protocol(php_mysql_packet_row *packet, MYSQLND * /* php_mysqlnd_net_field_length() call should be after *this_field_len_pos = p; */ unsigned long len = php_mysqlnd_net_field_length(&p); - obj = mysqlnd_palloc_get_zval(conn->zval_cache, &allocated); + obj = mysqlnd_palloc_get_zval(conn->zval_cache, &allocated TSRMLS_CC); if (allocated) { *current_field = (zval *) obj; } else { @@ -1306,8 +1415,10 @@ void php_mysqlnd_rowp_read_text_protocol(php_mysql_packet_row *packet, MYSQLND * ZVAL_NULL(*current_field); last_field_was_string = FALSE; } else { +#if PHP_MAJOR_VERSION >= 6 || defined(MYSQLND_STRING_TO_INT_CONVERSION) struct st_mysqlnd_perm_bind perm_bind = mysqlnd_ps_fetch_functions[packet->fields_metadata[i].type]; +#endif #ifdef MYSQLND_STRING_TO_INT_CONVERSION if (as_int && perm_bind.php_type == IS_LONG && @@ -1368,7 +1479,10 @@ void php_mysqlnd_rowp_read_text_protocol(php_mysql_packet_row *packet, MYSQLND * } else #endif { - ZVAL_STRINGL(*current_field, start, bit_area - start - 1, 0); + ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0); + } + if (allocated == FALSE) { + ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_INT_BUFFER; } } else if (Z_TYPE_PP(current_field) == IS_STRING){ memcpy(bit_area, Z_STRVAL_PP(current_field), Z_STRLEN_PP(current_field)); @@ -1381,12 +1495,15 @@ void php_mysqlnd_rowp_read_text_protocol(php_mysql_packet_row *packet, MYSQLND * } else #endif { - ZVAL_STRINGL(*current_field, start, bit_area - start - 1, 0); + ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0); + } + if (allocated == FALSE) { + ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_INT_BUFFER; } } /* IS_UNICODE should not be specially handled. In unicode mode - the buffers are not pointed - everything is copied. + the buffers are not referenced - everything is copied. */ } else #if PHP_MAJOR_VERSION < 6 @@ -1445,6 +1562,8 @@ void php_mysqlnd_rowp_read_text_protocol(php_mysql_packet_row *packet, MYSQLND * /* Normal queries: The buffer has one more byte at the end, because we need it */ packet->row_buffer[data_size] = '\0'; } + + DBG_VOID_RETURN; } /* }}} */ @@ -1465,6 +1584,8 @@ php_mysqlnd_rowp_read(void *_packet, MYSQLND *conn TSRMLS_DC) php_mysql_packet_row *packet= (php_mysql_packet_row *) _packet; size_t post_alloc_for_bit_fields = 0; + DBG_ENTER("php_mysqlnd_rowp_read"); + if (!packet->binary_protocol && packet->bit_fields_count) { /* For every field we need terminating \0 */ post_alloc_for_bit_fields = @@ -1492,7 +1613,8 @@ php_mysqlnd_rowp_read(void *_packet, MYSQLND *conn TSRMLS_DC) packet->error_info.error, sizeof(packet->error_info.error), &packet->error_info.error_no, - packet->error_info.sqlstate); + packet->error_info.sqlstate + TSRMLS_CC); } else if (*p == 0xFE && data_size < 8) { /* EOF */ packet->eof = TRUE; p++; @@ -1503,13 +1625,16 @@ php_mysqlnd_rowp_read(void *_packet, MYSQLND *conn TSRMLS_DC) /* Seems we have 3 bytes reserved for future use */ } } else { - MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_ROWS_FETCHED_FROM_SERVER); + MYSQLND_INC_CONN_STATISTIC(&conn->stats, + packet->binary_protocol? STAT_ROWS_FETCHED_FROM_SERVER_PS: + STAT_ROWS_FETCHED_FROM_SERVER_NORMAL); packet->eof = FALSE; /* packet->field_count is set by the user of the packet */ if (!packet->skip_extraction) { if (!packet->fields) { + DBG_INF("Allocating packet->fields"); /* old-API will probably set packet->fields to NULL every time, though for unbuffered sets it makes not much sense as the zvals in this buffer matter, @@ -1520,7 +1645,7 @@ php_mysqlnd_rowp_read(void *_packet, MYSQLND *conn TSRMLS_DC) but mostly like old-API unbuffered and thus will populate this array with value. */ - packet->fields = (zval **) pemalloc(packet->field_count * sizeof(zval *), + packet->fields = (zval **) mnd_pemalloc(packet->field_count * sizeof(zval *), packet->persistent_alloc); } @@ -1530,24 +1655,26 @@ php_mysqlnd_rowp_read(void *_packet, MYSQLND *conn TSRMLS_DC) php_mysqlnd_rowp_read_text_protocol(packet, conn, p, data_size TSRMLS_CC); } } else { - MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_ROWS_SKIPPED); + MYSQLND_INC_CONN_STATISTIC(&conn->stats, + packet->binary_protocol? STAT_ROWS_SKIPPED_PS: + STAT_ROWS_SKIPPED_NORMAL); } } end: net->stream->chunk_size = old_chunk_size; - return ret; + DBG_RETURN(ret); } /* }}} */ /* {{{ php_mysqlnd_rowp_free_mem */ static -void php_mysqlnd_rowp_free_mem(void *_packet, zend_bool alloca) +void php_mysqlnd_rowp_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) { php_mysql_packet_row *p= (php_mysql_packet_row *) _packet; if (p->row_buffer) { - pefree(p->row_buffer, p->persistent_alloc); + mnd_pefree(p->row_buffer, p->persistent_alloc); p->row_buffer = NULL; } /* @@ -1558,7 +1685,7 @@ void php_mysqlnd_rowp_free_mem(void *_packet, zend_bool alloca) not free the array. As it is passed to us, we should not clean it ourselves. */ if (!alloca) { - efree(p); + mnd_efree(p); } } /* }}} */ @@ -1572,29 +1699,31 @@ php_mysqlnd_stats_read(void *_packet, MYSQLND *conn TSRMLS_DC) zend_uchar buf[1024]; php_mysql_packet_stats *packet= (php_mysql_packet_stats *) _packet; + DBG_ENTER("php_mysqlnd_stats_read"); + PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "statistics"); - packet->message = pemalloc(packet->header.size + 1, conn->persistent); + packet->message = mnd_pemalloc(packet->header.size + 1, conn->persistent); memcpy(packet->message, buf, packet->header.size); packet->message[packet->header.size] = '\0'; packet->message_len = packet->header.size; - return PASS; + DBG_RETURN(PASS); } /* }}} */ /* {{{ php_mysqlnd_stats_free_mem */ static -void php_mysqlnd_stats_free_mem(void *_packet, zend_bool alloca) +void php_mysqlnd_stats_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) { php_mysql_packet_stats *p= (php_mysql_packet_stats *) _packet; if (p->message) { - efree(p->message); + mnd_efree(p->message); p->message = NULL; } if (!alloca) { - efree(p); + mnd_efree(p); } } /* }}} */ @@ -1615,6 +1744,8 @@ php_mysqlnd_prepare_read(void *_packet, MYSQLND *conn TSRMLS_DC) unsigned int data_size; php_mysql_packet_prepare_response *packet= (php_mysql_packet_prepare_response *) _packet; + DBG_ENTER("php_mysqlnd_prepare_read"); + PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "prepare"); data_size = packet->header.size; @@ -1626,15 +1757,17 @@ php_mysqlnd_prepare_read(void *_packet, MYSQLND *conn TSRMLS_DC) packet->error_info.error, sizeof(packet->error_info.error), &packet->error_info.error_no, - packet->error_info.sqlstate); - return PASS; + packet->error_info.sqlstate + TSRMLS_CC); + DBG_RETURN(PASS); } if (data_size != PREPARE_RESPONSE_SIZE_41 && data_size != PREPARE_RESPONSE_SIZE_50 && !(data_size > PREPARE_RESPONSE_SIZE_50)) { - php_error(E_WARNING, "Wrong COM_STMT_PREPARE response size. Received %d", data_size); - return FAIL; + DBG_ERR_FMT("Wrong COM_STMT_PREPARE response size. Received %d", data_size); + php_error(E_WARNING, "Wrong COM_STMT_PREPARE response size. Received %d. PID=%d", data_size, getpid()); + DBG_RETURN(FAIL); } packet->stmt_id = uint4korr(p); @@ -1651,31 +1784,30 @@ php_mysqlnd_prepare_read(void *_packet, MYSQLND *conn TSRMLS_DC) /* 0x0 filler sent by the server for 5.0+ clients */ p++; - packet->warning_count= uint2korr(p); + packet->warning_count = uint2korr(p); } -#ifndef MYSQLND_SILENT - php_printf("\tPrepare packet read: stmt_id=%d fields=%d params=%d\n", + DBG_INF_FMT("Prepare packet read: stmt_id=%d fields=%d params=%d", packet->stmt_id, packet->field_count, packet->param_count); -#endif if (p - begin > packet->header.size) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "PREPARE packet %d bytes shorter than expected", - p - begin - packet->header.size); + DBG_ERR_FMT("PREPARE packet %d bytes shorter than expected", p - begin - packet->header.size); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "PREPARE packet %d bytes shorter than expected. PID=%d", + p - begin - packet->header.size, getpid()); } - return PASS; + DBG_RETURN(PASS); } /* }}} */ /* {{{ php_mysqlnd_prepare_free_mem */ static -void php_mysqlnd_prepare_free_mem(void *_packet, zend_bool alloca) +void php_mysqlnd_prepare_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) { php_mysql_packet_prepare_response *p= (php_mysql_packet_prepare_response *) _packet; if (!alloca) { - efree(p); + mnd_efree(p); } } /* }}} */ @@ -1691,6 +1823,8 @@ php_mysqlnd_chg_user_read(void *_packet, MYSQLND *conn TSRMLS_DC) zend_uchar *begin = buf; php_mysql_packet_chg_user_resp *packet= (php_mysql_packet_chg_user_resp *) _packet; + DBG_ENTER("php_mysqlnd_chg_user_read"); + PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "change user response "); /* @@ -1705,7 +1839,7 @@ php_mysqlnd_chg_user_read(void *_packet, MYSQLND *conn TSRMLS_DC) if (packet->header.size == 1 && buf[0] == 0xFE && packet->server_capabilities & CLIENT_SECURE_CONNECTION) { /* We don't handle 3.23 authentication */ - return FAIL; + DBG_RETURN(FAIL); } if (0xFF == packet->field_count) { @@ -1713,24 +1847,26 @@ php_mysqlnd_chg_user_read(void *_packet, MYSQLND *conn TSRMLS_DC) packet->error_info.error, sizeof(packet->error_info.error), &packet->error_info.error_no, - packet->error_info.sqlstate); + packet->error_info.sqlstate + TSRMLS_CC); } if (p - begin > packet->header.size) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "CHANGE_USER packet %d bytes shorter than expected", - p - begin - packet->header.size); + DBG_ERR_FMT("CHANGE_USER packet %d bytes shorter than expected", p - begin - packet->header.size); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "CHANGE_USER packet %d bytes shorter than expected. PID=%d", + p - begin - packet->header.size, getpid()); } - return PASS; + DBG_RETURN(PASS); } /* }}} */ /* {{{ php_mysqlnd_chg_user_free_mem */ static -void php_mysqlnd_chg_user_free_mem(void *_packet, zend_bool alloca) +void php_mysqlnd_chg_user_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) { if (!alloca) { - efree(_packet); + mnd_efree(_packet); } } /* }}} */ diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.h b/ext/mysqlnd/mysqlnd_wireprotocol.h index 83411246c9..f48df1cba8 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.h +++ b/ext/mysqlnd/mysqlnd_wireprotocol.h @@ -31,6 +31,10 @@ typedef zend_uchar mysqlnd_1b; typedef zend_ushort mysqlnd_2b; typedef zend_uint mysqlnd_4b; +/* Used in mysqlnd_debug.c */ +extern char * mysqlnd_read_header_name; +extern char * mysqlnd_read_body_name; + /* Packet handling */ #define PACKET_INIT(packet, enum_type, c_type) \ @@ -42,7 +46,7 @@ typedef zend_uint mysqlnd_4b; #define PACKET_READ(packet, conn) ((packet)->header.m->read_from_net((packet), (conn) TSRMLS_CC)) #define PACKET_FREE(packet) \ do { \ - ((packet)->header.m->free_mem((packet), FALSE)); \ + ((packet)->header.m->free_mem((packet), FALSE TSRMLS_CC)); \ } while (0); #define PACKET_INIT_ALLOCA(packet, enum_type) \ @@ -52,7 +56,7 @@ typedef zend_uint mysqlnd_4b; } #define PACKET_WRITE_ALLOCA(packet, conn) PACKET_WRITE(&(packet), (conn)) #define PACKET_READ_ALLOCA(packet, conn) PACKET_READ(&(packet), (conn)) -#define PACKET_FREE_ALLOCA(packet) (packet.header.m->free_mem(&(packet), TRUE)) +#define PACKET_FREE_ALLOCA(packet) (packet.header.m->free_mem(&(packet), TRUE TSRMLS_CC)) /* Enums */ enum php_mysql_packet_type @@ -114,7 +118,7 @@ typedef struct st_mysqlnd_packet_methods { size_t struct_size; enum_func_status (*read_from_net)(void *packet, MYSQLND *conn TSRMLS_DC); size_t (*write_to_net)(void *packet, MYSQLND *conn TSRMLS_DC); - void (*free_mem)(void *packet, zend_bool alloca); + void (*free_mem)(void *packet, zend_bool alloca TSRMLS_DC); } mysqlnd_packet_methods; extern mysqlnd_packet_methods packet_methods[]; @@ -237,6 +241,7 @@ typedef struct st_php_mysql_packet_res_field { MYSQLND_FIELD *metadata; /* For table definitions, empty for result sets */ zend_bool skip_parsing; + zend_bool stupid_list_fields_eof; } php_mysql_packet_res_field; |