From: Ulf Wendel Date: Thu, 15 Oct 2009 15:49:40 +0000 (+0000) Subject: Fixing problems when calling connect (again and again) on a valid connection handle... X-Git-Tag: php-5.4.0alpha1~191^2~2507 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=355a88f676ccac3ca80a4748890d91482164a546;p=php Fixing problems when calling connect (again and again) on a valid connection handle. Most of the patch comes from Andrey. --- diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c index 76266d2996..1b550cf283 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -229,22 +229,7 @@ static void mysqli_link_free_storage(void *object TSRMLS_DC) if (my_res && my_res->ptr) { MY_MYSQL *mysql = (MY_MYSQL *)my_res->ptr; if (mysql->mysql) { - if (!mysql->persistent) { - mysqli_close(mysql->mysql, MYSQLI_CLOSE_IMPLICIT); - } 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; - - zend_ptr_stack_push(&plist->free_links, mysql->mysql); - - MyG(num_links)--; - MyG(num_active_persistent)--; - MyG(num_inactive_persistent)++; - } - } - } + php_mysqli_close(mysql, MYSQLI_CLOSE_EXPLICIT TSRMLS_CC); } php_clear_mysql(mysql); efree(mysql); diff --git a/ext/mysqli/mysqli_api.c b/ext/mysqli/mysqli_api.c index 19ab150b2c..9f4ec568ff 100644 --- a/ext/mysqli/mysqli_api.c +++ b/ext/mysqli/mysqli_api.c @@ -557,6 +557,32 @@ PHP_FUNCTION(mysqli_character_set_name) } /* }}} */ +/* {{{ php_mysqli_close */ +void php_mysqli_close(MY_MYSQL * mysql, int close_type TSRMLS_DC) +{ + if (!mysql->persistent) { + mysqli_close(mysql->mysql, MYSQLI_CLOSE_EXPLICIT); + } 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; + zend_ptr_stack_push(&plist->free_links, mysql->mysql); + + MyG(num_links)--; + MyG(num_active_persistent)--; + MyG(num_inactive_persistent)++; + } + } + mysql->persistent = FALSE; + } + mysql->mysql = NULL; + + php_clear_mysql(mysql); +} +/* }}} */ + + /* {{{ proto bool mysqli_close(object link) U Close connection */ PHP_FUNCTION(mysqli_close) @@ -570,24 +596,7 @@ PHP_FUNCTION(mysqli_close) MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_INITIALIZED); - 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; - zend_ptr_stack_push(&plist->free_links, mysql->mysql); - - MyG(num_links)--; - MyG(num_active_persistent)--; - MyG(num_inactive_persistent)++; - } - } - } - - php_clear_mysql(mysql); + php_mysqli_close(mysql, MYSQLI_CLOSE_EXPLICIT TSRMLS_CC); MYSQLI_CLEAR_RESOURCE(&mysql_link); efree(mysql); diff --git a/ext/mysqli/mysqli_libmysql.h b/ext/mysqli/mysqli_libmysql.h index 1ea1fc3bf4..be82c4b982 100644 --- a/ext/mysqli/mysqli_libmysql.h +++ b/ext/mysqli/mysqli_libmysql.h @@ -20,9 +20,9 @@ */ /* These are unused */ -#define MYSQLI_CLOSE_EXPLICIT -#define MYSQLI_CLOSE_IMPLICIT -#define MYSQLI_CLOSE_DISCONNECTED +#define MYSQLI_CLOSE_EXPLICIT 0 +#define MYSQLI_CLOSE_IMPLICIT 1 +#define MYSQLI_CLOSE_DISCONNECTED 2 #define MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE 200 #define MYSQLND_OPT_INT_AND_YEAR_AS_INT 201 diff --git a/ext/mysqli/mysqli_nonapi.c b/ext/mysqli/mysqli_nonapi.c index 2ed19ae2d4..7acd7a3159 100644 --- a/ext/mysqli/mysqli_nonapi.c +++ b/ext/mysqli/mysqli_nonapi.c @@ -85,14 +85,7 @@ void mysqli_common_connect(INTERNAL_FUNCTION_PARAMETERS, zend_bool is_real_conne 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) { - mysql = (MY_MYSQL*) mysqli_resource->ptr; - if (mysqli_resource->status > MYSQLI_STATUS_INITIALIZED) { - php_clear_mysql(mysql); - if (mysql->mysql) { - mysqli_close(mysql->mysql, MYSQLI_CLOSE_EXPLICIT); - mysql->mysql = NULL; - } - } + mysql = (MY_MYSQL*) mysqli_resource->ptr; } } if (!mysql) { @@ -118,7 +111,10 @@ void mysqli_common_connect(INTERNAL_FUNCTION_PARAMETERS, zend_bool is_real_conne flags &= ~CLIENT_LOCAL_FILES; } } - + if (mysql->mysql && mysqli_resource && mysqli_resource->status > MYSQLI_STATUS_INITIALIZED) { + /* already connected, we should close the connection */ + php_mysqli_close(mysql, MYSQLI_CLOSE_IMPLICIT TSRMLS_CC); + } if (!socket_len || !socket) { socket = MyG(default_socket); @@ -157,15 +153,6 @@ void mysqli_common_connect(INTERNAL_FUNCTION_PARAMETERS, zend_bool is_real_conne do { if (zend_ptr_stack_num_elements(&plist->free_links)) { - if (is_real_connect) { - /* - Gotcha! If there are some options set on the handle with mysqli_options() - they will be lost. We will fetch other handle with other options. This could - be a source of bug reports of people complaining but...nothing else could be - done, if they want PCONN! - */ - mysqli_close(mysql->mysql, MYSQLI_CLOSE_IMPLICIT); - } mysql->mysql = zend_ptr_stack_pop(&plist->free_links); MyG(num_inactive_persistent)--; @@ -302,6 +289,7 @@ err: if (mysql->hash_key) { efree(mysql->hash_key); mysql->hash_key = NULL; + mysql->persistent = FALSE; } if (!is_real_connect) { efree(mysql); diff --git a/ext/mysqli/php_mysqli_structs.h b/ext/mysqli/php_mysqli_structs.h index 0865430067..984dda87d8 100644 --- a/ext/mysqli/php_mysqli_structs.h +++ b/ext/mysqli/php_mysqli_structs.h @@ -231,6 +231,9 @@ extern zend_class_entry *mysqli_exception_class_entry; extern int php_le_pmysqli(void); extern void php_mysqli_dtor_p_elements(void *data); +extern void php_mysqli_close(MY_MYSQL * mysql, int close_type TSRMLS_DC); + + #ifdef HAVE_SPL extern PHPAPI zend_class_entry *spl_ce_RuntimeException; #endif diff --git a/ext/mysqli/tests/mysqli_pconn_conn_multiple.phpt b/ext/mysqli/tests/mysqli_pconn_conn_multiple.phpt new file mode 100644 index 0000000000..2c6aae0d3b --- /dev/null +++ b/ext/mysqli/tests/mysqli_pconn_conn_multiple.phpt @@ -0,0 +1,164 @@ +--TEST-- +Calling connect() on an open persistent connection to create a new persistent connection +--SKIPIF-- + +--INI-- +mysqli.allow_persistent=1 +mysqli.max_persistent=-1 +mysqli.max_links=-1 +--FILE-- +thread_id) + printf("[002] Cannot determine thread id, test will fail, [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (true !== ($tmp = my_mysqli_real_connect($link, $host, $user, $passwd, $db, $port, $socket))) + printf("[003] Expecting boolean/true got %s/%s\n", gettype($tmp), $tmp); + + if (!is_int($new_thread_id = mysqli_thread_id($link)) || ($new_thread_id < 0)) + printf("[004] Expecting int/any got %s/%s\n", gettype($tmp), $tmp); + + if ($thread_id == $new_thread_id) + printf("[005] Expecting new connection and new thread id. Old thread id %d, new thread id %d\n", $thread_id, $new_thread_id); + + if (!($res = mysqli_query($link, "SELECT 'ok' AS it_works")) || + !($row = mysqli_fetch_assoc($res))) + printf("[006] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + var_dump($row); + mysqli_free_result($res); + + mysqli_close($link); + + if (!$link = new my_mysqli($phost, $user, $passwd, $db, $port, $socket)) + printf("[007] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $phost, $user, $db, $port, $socket); + + if (!$thread_id = $link->thread_id) + printf("[008] Cannot determine thread id, test will fail, [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (true !== ($tmp = $link->real_connect($host, $user, $passwd, $db, $port, $socket))) + printf("[009] Expecting boolean/true got %s/%s\n", gettype($tmp), $tmp); + + if (!is_int($new_thread_id = $link->thread_id) || ($new_thread_id < 0)) + printf("[010] Expecting int/any got %s/%s\n", gettype($tmp), $tmp); + + if ($thread_id == $new_thread_id) + printf("[011] Expecting new connection and new thread id. Old thread id %d, new thread id %d\n", $thread_id, $new_thread_id); + + if (!($res = $link->query("SELECT 'works also with oo' AS syntax")) || + !($row = $res->fetch_assoc())) + printf("[012] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + var_dump($row); + mysqli_free_result($res); + + mysqli_close($link); + + if (NULL !== ($tmp = $link->connect($phost, $user, $passwd, $db, $port, $socket))) + printf("[013] Expecting NULL got %s/%s\n", gettype($tmp), $tmp); + + if (!$link = mysqli_connect($phost, $user, $passwd, $db, $port, $socket)) + printf("[014] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $phost, $user, $db, $port, $socket); + + if (NULL !== ($tmp = $link->connect($host, $user, $passwd, $db, $port, $socket))) + printf("[015] Expecting NULL got %s/%s\n", gettype($tmp), $tmp); + + printf("Flipping phost/host order\n"); + + if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + printf("[016] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $host, $user, $db, $port, $socket); + + if (!$thread_id = mysqli_thread_id($link)) + printf("[017] Cannot determine thread id, test will fail, [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (true !== ($tmp = my_mysqli_real_connect($link, $phost, $user, $passwd, $db, $port, $socket))) + printf("[018] Expecting boolean/true got %s/%s\n", gettype($tmp), $tmp); + + if (!is_int($new_thread_id = mysqli_thread_id($link)) || ($new_thread_id < 0)) + printf("[019] Expecting int/any got %s/%s\n", gettype($tmp), $tmp); + + if ($thread_id == $new_thread_id) + printf("[020] Expecting new connection and new thread id. Old thread id %d, new thread id %d\n", $thread_id, $new_thread_id); + + if (!($res = mysqli_query($link, "SELECT 'ok' AS it_works")) || + !($row = mysqli_fetch_assoc($res))) + printf("[021] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + var_dump($row); + mysqli_free_result($res); + + mysqli_close($link); + + if (!$link = new my_mysqli($host, $user, $passwd, $db, $port, $socket)) + printf("[022] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $host, $user, $db, $port, $socket); + + if (!$thread_id = $link->thread_id) + printf("[023] Cannot determine thread id, test will fail, [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (true !== ($tmp = $link->real_connect($phost, $user, $passwd, $db, $port, $socket))) + printf("[024] Expecting boolean/true got %s/%s\n", gettype($tmp), $tmp); + + if (!is_int($new_thread_id = $link->thread_id) || ($new_thread_id < 0)) + printf("[025] Expecting int/any got %s/%s\n", gettype($tmp), $tmp); + + if ($thread_id == $new_thread_id) + printf("[026] Expecting new connection and new thread id. Old thread id %d, new thread id %d\n", $thread_id, $new_thread_id); + + if (!($res = $link->query("SELECT 'works also with oo' AS syntax")) || + !($row = $res->fetch_assoc())) + printf("[027] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + var_dump($row); + mysqli_free_result($res); + + mysqli_close($link); + + if (NULL !== ($tmp = $link->connect($host, $user, $passwd, $db, $port, $socket))) + printf("[028] Expecting NULL got %s/%s\n", gettype($tmp), $tmp); + + if (!$link = mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + printf("[029] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $host, $user, $db, $port, $socket); + + if (NULL !== ($tmp = $link->connect($phost, $user, $passwd, $db, $port, $socket))) + printf("[030] Expecting NULL got %s/%s\n", gettype($tmp), $tmp); + + print "done!"; +?> +--EXPECTF-- +array(1) { + [%u|b%"it_works"]=> + %unicode|string%(2) "ok" +} +array(1) { + [%u|b%"syntax"]=> + %unicode|string%(18) "works also with oo" +} +Flipping phost/host order +array(1) { + [%u|b%"it_works"]=> + %unicode|string%(2) "ok" +} +array(1) { + [%u|b%"syntax"]=> + %unicode|string%(18) "works also with oo" +} +done! \ No newline at end of file diff --git a/ext/mysqli/tests/mysqli_pconn_twice.phpt b/ext/mysqli/tests/mysqli_pconn_twice.phpt new file mode 100644 index 0000000000..ddb95e732d --- /dev/null +++ b/ext/mysqli/tests/mysqli_pconn_twice.phpt @@ -0,0 +1,79 @@ +--TEST-- +Calling connect() on an open persistent connection to create a new persistent connection +--SKIPIF-- + +--INI-- +mysqli.allow_persistent=1 +mysqli.max_persistent=-1 +mysqli.max_links=-1 +--FILE-- +real_connect($host, $user, $passwd, $db, $port, $socket))) + printf("[009] Expecting boolean/true got %s/%s\n", gettype($tmp), $tmp); + + /* it is undefined which pooled connection we get - thread ids may differ */ + + if (!($res = $link->query("SELECT 'works also with oo' AS syntax")) || + !($row = $res->fetch_assoc())) + printf("[012] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + var_dump($row); + mysqli_free_result($res); + + mysqli_close($link); + + if (NULL !== ($tmp = $link->connect($host, $user, $passwd, $db, $port, $socket))) + printf("[013] Expecting NULL got %s/%s\n", gettype($tmp), $tmp); + + if (!$link = mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + printf("[014] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $host, $user, $db, $port, $socket); + + if (NULL !== ($tmp = $link->connect($host, $user, $passwd, $db, $port, $socket))) + printf("[015] Expecting NULL got %s/%s\n", gettype($tmp), $tmp); + + print "done!"; +?> +--EXPECTF-- +array(1) { + [%u|b%"it_works"]=> + %unicode|string%(2) "ok" +} +array(1) { + [%u|b%"syntax"]=> + %unicode|string%(18) "works also with oo" +} +done! \ No newline at end of file diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index a3f0abbe22..815247d96f 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -664,8 +664,7 @@ PHPAPI MYSQLND *mysqlnd_connect(MYSQLND *conn, conn->thread_id = greet_packet.thread_id; conn->protocol_version = greet_packet.protocol_version; - conn->server_version = greet_packet.server_version; - greet_packet.server_version = NULL; /* The string will be freed otherwise */ + conn->server_version = pestrdup(greet_packet.server_version, conn->persistent); conn->greet_charset = mysqlnd_find_charset_nr(greet_packet.charset_no); /* we allow load data local infile by default */ @@ -1945,10 +1944,12 @@ MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn, break; #endif case MYSQLND_OPT_NET_CMD_BUFFER_SIZE: + DBG_INF("MYSQLND_OPT_NET_CMD_BUFFER_SIZE"); if (*(unsigned int*) value < MYSQLND_NET_CMD_BUFFER_MIN_SIZE) { DBG_RETURN(FAIL); } conn->net.cmd_buffer.length = *(unsigned int*) value; + DBG_INF_FMT("new_length=%u", conn->net.cmd_buffer.length); if (!conn->net.cmd_buffer.buffer) { conn->net.cmd_buffer.buffer = mnd_pemalloc(conn->net.cmd_buffer.length, conn->persistent); } else { @@ -1958,25 +1959,32 @@ MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn, } break; case MYSQLND_OPT_NET_READ_BUFFER_SIZE: + DBG_INF("MYSQLND_OPT_NET_READ_BUFFER_SIZE"); conn->options.net_read_buffer_size = *(unsigned int*) value; + DBG_INF_FMT("new_length=%u", conn->options.net_read_buffer_size); break; #ifdef MYSQLND_STRING_TO_INT_CONVERSION case MYSQLND_OPT_INT_AND_FLOAT_NATIVE: + DBG_INF("MYSQLND_OPT_INT_AND_FLOAT_NATIVE"); conn->options.int_and_float_native = *(unsigned int*) value; break; #endif case MYSQL_OPT_CONNECT_TIMEOUT: + DBG_INF("MYSQL_OPT_CONNECT_TIMEOUT"); conn->options.timeout_connect = *(unsigned int*) value; break; #ifdef WHEN_SUPPORTED_BY_MYSQLI case MYSQL_OPT_READ_TIMEOUT: + DBG_INF("MYSQL_OPT_READ_TIMEOUT"); conn->options.timeout_read = *(unsigned int*) value; break; case MYSQL_OPT_WRITE_TIMEOUT: + DBG_INF("MYSQL_OPT_WRITE_TIMEOUT"); conn->options.timeout_write = *(unsigned int*) value; break; #endif case MYSQL_OPT_LOCAL_INFILE: + DBG_INF("MYSQL_OPT_LOCAL_INFILE"); if (!value || (*(unsigned int*) value) ? 1 : 0) { conn->options.flags |= CLIENT_LOCAL_FILES; } else { @@ -1984,6 +1992,8 @@ MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn, } break; case MYSQL_INIT_COMMAND: + DBG_INF("MYSQL_INIT_COMMAND"); + DBG_INF_FMT("command=%s", value); /* when num_commands is 0, then realloc will be effectively a malloc call, internally */ conn->options.init_commands = mnd_perealloc(conn->options.init_commands, sizeof(char *) * (conn->options.num_commands + 1), conn->persistent); @@ -2003,7 +2013,9 @@ MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn, /* currently not supported. Todo!! */ break; case MYSQL_SET_CHARSET_NAME: + DBG_INF("MYSQL_SET_CHARSET_NAME"); conn->options.charset_name = pestrdup(value, conn->persistent); + DBG_INF_FMT("charset=%s", conn->options.charset_name); break; #ifdef WHEN_SUPPORTED_BY_MYSQLI case MYSQL_SET_CHARSET_DIR: diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c index cc0697cd0b..ea77bdc0bf 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.c +++ b/ext/mysqlnd/mysqlnd_wireprotocol.c @@ -589,7 +589,7 @@ php_mysqlnd_greet_read(void *_packet, MYSQLND *conn TSRMLS_DC) DBG_RETURN(PASS); } - packet->server_version = pestrdup((char *)p, conn->persistent); + packet->server_version = estrdup((char *)p); p+= strlen(packet->server_version) + 1; /* eat the '\0' */ packet->thread_id = uint4korr(p);