From: Andrey Hristov Date: Thu, 24 Apr 2008 14:22:19 +0000 (+0000) Subject: Update ext/mysql's and ext/mysqli's tests X-Git-Tag: BEFORE_NEW_PARAMETER_PARSE~333 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f4e659d2af2ba68eb6d9092560eb0247a33fb827;p=php Update ext/mysql's and ext/mysqli's tests Add mysqli_stmt_more_result()/mysqli_stmt_next_result(), but only in mysqlnd builds as libmysql doesn't support this feature. --- diff --git a/ext/mysql/tests/mysql_fetch_array.phpt b/ext/mysql/tests/mysql_fetch_array.phpt index f197ae9494..c3a70ea55c 100644 --- a/ext/mysql/tests/mysql_fetch_array.phpt +++ b/ext/mysql/tests/mysql_fetch_array.phpt @@ -184,9 +184,9 @@ func_mysql_fetch_array($link, $engine, "BIGINT", NULL, NULL, 260); // func_mysql_fetch_array($link, $engine, "BIGINT UNSIGNED", 18446744073709551615, "1.84467e+019", 270, "/1\.84467e\+[0]?19/iu"); func_mysql_fetch_array($link, $engine, "BIGINT UNSIGNED", NULL, NULL, 280); -func_mysql_fetch_array($link, $engine, "FLOAT", -9223372036854775808 - 1.1, "-9.22337e+18", 290, "/-9\.22337e\+[0]?18/iu"); +func_mysql_fetch_array($link, $engine, "FLOAT", -9223372036854775808 - 1.1, "-9.22337e+18", 290, "/-9\.22337e\+?[0]?18/iu"); func_mysql_fetch_array($link, $engine, "FLOAT", NULL, NULL, 300); -func_mysql_fetch_array($link, $engine, "FLOAT UNSIGNED", 18446744073709551615 + 1.1, "1.84467e+19", 310, "/1\.84467e\+[0]?19/iu"); +func_mysql_fetch_array($link, $engine, "FLOAT UNSIGNED", 18446744073709551615 + 1.1, "1.84467e+19", 310, "/1\.84467e\+?[0]?19/iu"); func_mysql_fetch_array($link, $engine, "FLOAT UNSIGNED ", NULL, NULL, 320); func_mysql_fetch_array($link, $engine, "DOUBLE(10,2)", -99999999.99, "-99999999.99", 330); diff --git a/ext/mysql/tests/mysql_phpinfo.phpt b/ext/mysql/tests/mysql_phpinfo.phpt index af75992e8c..42e5b1806a 100644 --- a/ext/mysql/tests/mysql_phpinfo.phpt +++ b/ext/mysql/tests/mysql_phpinfo.phpt @@ -19,6 +19,9 @@ ob_end_clean(); if (!stristr($phpinfo, "mysql support")) printf("[001] ext/mysql should have exposed itself.\n"); +if (!stristr($phpinfo, "client api library version")) + printf("[002] ext/mysql should have exposed the library version.\n"); + if (!stristr($phpinfo, "mysql.default_host")) printf("[003] php.ini setting mysql.default_host not shown.\n"); @@ -70,4 +73,4 @@ if ($IS_MYSQLND) { print "done!"; ?> --EXPECTF-- -done! +done! \ No newline at end of file diff --git a/ext/mysqli/mysqli_api.c b/ext/mysqli/mysqli_api.c index 6e1ff659a5..a8c376cf46 100644 --- a/ext/mysqli/mysqli_api.c +++ b/ext/mysqli/mysqli_api.c @@ -1471,6 +1471,48 @@ PHP_FUNCTION(mysqli_next_result) { } /* }}} */ + +#ifdef MYSQLI_USE_MYSQLND +/* {{{ proto bool mysqli_stmt_next_result(object link) + check if there any more query results from a multi query */ +PHP_FUNCTION(mysqli_stmt_more_results) +{ + MY_STMT *stmt; + zval *mysql_stmt; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) { + return; + } + MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID); + + RETURN_BOOL(mysqlnd_stmt_more_results(stmt->stmt)); +} +/* }}} */ + + +/* {{{ proto bool mysqli_stmt_next_result(object link) + read next result from multi_query */ +PHP_FUNCTION(mysqli_stmt_next_result) { + MY_STMT *stmt; + zval *mysql_stmt; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) { + return; + } + MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID); + + if (!mysqlnd_stmt_more_results(stmt->stmt)) { + php_error_docref(NULL TSRMLS_CC, E_STRICT, "There is no next result set. " + "Please, call mysqli_stmt_more_results()/mysqli_stmt::more_results() to check " + "whether to call this function/method"); + } + + RETURN_BOOL(!mysqlnd_stmt_next_result(stmt->stmt)); +} +/* }}} */ +#endif + + /* {{{ proto int mysqli_num_fields(object result) Get number of fields in result */ PHP_FUNCTION(mysqli_num_fields) diff --git a/ext/mysqli/mysqli_fe.c b/ext/mysqli/mysqli_fe.c index 5b2cd7b4f1..6184a902fb 100644 --- a/ext/mysqli/mysqli_fe.c +++ b/ext/mysqli/mysqli_fe.c @@ -155,6 +155,10 @@ const zend_function_entry mysqli_functions[] = { PHP_FE(mysqli_stmt_data_seek, NULL) PHP_FE(mysqli_stmt_errno, NULL) PHP_FE(mysqli_stmt_error, NULL) +#if defined(MYSQLI_USE_MYSQLND) + PHP_FE(mysqli_stmt_more_results, NULL) + PHP_FE(mysqli_stmt_next_result, NULL) +#endif PHP_FE(mysqli_stmt_num_rows, NULL) PHP_FE(mysqli_stmt_sqlstate, NULL) PHP_FE(mysqli_stmt_store_result, NULL) @@ -280,6 +284,10 @@ const zend_function_entry mysqli_stmt_methods[] = { PHP_FALIAS(get_warnings, mysqli_stmt_get_warnings, NULL) PHP_FALIAS(result_metadata, mysqli_stmt_result_metadata,NULL) PHP_FALIAS(num_rows, mysqli_stmt_num_rows,NULL) +#if defined(MYSQLI_USE_MYSQLND) + PHP_FALIAS(more_results, mysqli_stmt_more_results, NULL) + PHP_FALIAS(next_result, mysqli_stmt_next_result, NULL) +#endif PHP_FALIAS(send_long_data,mysqli_stmt_send_long_data,NULL) PHP_FALIAS(stmt,mysqli_prepare,NULL) PHP_FALIAS(free_result,mysqli_stmt_free_result,NULL) diff --git a/ext/mysqli/mysqli_nonapi.c b/ext/mysqli/mysqli_nonapi.c index db96817d3e..9b18e8c383 100644 --- a/ext/mysqli/mysqli_nonapi.c +++ b/ext/mysqli/mysqli_nonapi.c @@ -409,8 +409,8 @@ PHP_FUNCTION(mysqli_fetch_all) Returns statistics about the zval cache */ PHP_FUNCTION(mysqli_get_cache_stats) { - if (ZEND_NUM_ARGS()) { - WRONG_PARAM_COUNT; + if (zend_parse_parameters_none() == FAILURE) { + return; } mysqlnd_palloc_stats(mysqli_mysqlnd_zval_cache, return_value); } @@ -421,8 +421,8 @@ PHP_FUNCTION(mysqli_get_cache_stats) Returns statistics about the zval cache */ PHP_FUNCTION(mysqli_get_client_stats) { - if (ZEND_NUM_ARGS()) { - WRONG_PARAM_COUNT; + if (zend_parse_parameters_none() == FAILURE) { + return; } mysqlnd_get_client_stats(return_value); } @@ -665,7 +665,7 @@ PHP_FUNCTION(mysqli_set_charset) MY_MYSQL *mysql; zval *mysql_link; char *cs_name; - int csname_len; + int csname_len; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &mysql_link, mysqli_link_class_entry, &cs_name, &csname_len) == FAILURE) { return; diff --git a/ext/mysqli/php_mysqli_structs.h b/ext/mysqli/php_mysqli_structs.h index 5cb16b3c3c..d763806a85 100644 --- a/ext/mysqli/php_mysqli_structs.h +++ b/ext/mysqli/php_mysqli_structs.h @@ -463,6 +463,8 @@ PHP_FUNCTION(mysqli_stmt_get_result); PHP_FUNCTION(mysqli_stmt_get_warnings); PHP_FUNCTION(mysqli_stmt_reset); PHP_FUNCTION(mysqli_stmt_insert_id); +PHP_FUNCTION(mysqli_stmt_more_results); +PHP_FUNCTION(mysqli_stmt_next_result); PHP_FUNCTION(mysqli_stmt_num_rows); PHP_FUNCTION(mysqli_stmt_sqlstate); PHP_FUNCTION(mysqli_stmt_store_result); diff --git a/ext/mysqli/tests/mysqli_class_mysqli_stmt_interface.phpt b/ext/mysqli/tests/mysqli_class_mysqli_stmt_interface.phpt index cc604aaef0..dce75af358 100644 --- a/ext/mysqli/tests/mysqli_class_mysqli_stmt_interface.phpt +++ b/ext/mysqli/tests/mysqli_class_mysqli_stmt_interface.phpt @@ -41,8 +41,11 @@ Interface of the class mysqli_stmt 'store_result' => true, ); - if ($IS_MYSQLND) + if ($IS_MYSQLND) { $expected_methods['get_result'] = true; + $expected_methods['more_results'] = true; + $expected_methods['next_result'] = true; + } foreach ($methods as $k => $method) { if (isset($expected_methods[$method])) { diff --git a/ext/mysqli/tests/mysqli_fetch_all.phpt b/ext/mysqli/tests/mysqli_fetch_all.phpt index f93d204a67..7559b05034 100644 --- a/ext/mysqli/tests/mysqli_fetch_all.phpt +++ b/ext/mysqli/tests/mysqli_fetch_all.phpt @@ -198,9 +198,9 @@ if (!function_exists('mysqli_fetch_all')) func_mysqli_fetch_all($link, $engine, "BIGINT UNSIGNED", 18446744073709551615, "18446744073709551615", 270); func_mysqli_fetch_all($link, $engine, "BIGINT UNSIGNED", NULL, NULL, 280); - func_mysqli_fetch_all($link, $engine, "FLOAT", -9223372036854775808 - 1.1, "-9.22337e+18", 290, "/-9\.22337e\+[0]?18/iu"); + func_mysqli_fetch_all($link, $engine, "FLOAT", -9223372036854775808 - 1.1, "-9.22337e+18", 290, "/-9\.22337e\+?[0]?18/iu"); func_mysqli_fetch_all($link, $engine, "FLOAT", NULL, NULL, 300); - func_mysqli_fetch_all($link, $engine, "FLOAT UNSIGNED", 18446744073709551615 + 1.1, "1.84467e+19", 310, "/1\.84467e\+[0]?19/iu"); + func_mysqli_fetch_all($link, $engine, "FLOAT UNSIGNED", 18446744073709551615 + 1.1, "1.84467e+19", 310, "/1\.84467e\+?[0]?19/iu"); func_mysqli_fetch_all($link, $engine, "FLOAT UNSIGNED ", NULL, NULL, 320); func_mysqli_fetch_all($link, $engine, "DOUBLE(10,2)", -99999999.99, "-99999999.99", 330); diff --git a/ext/mysqli/tests/mysqli_fetch_all_oo.phpt b/ext/mysqli/tests/mysqli_fetch_all_oo.phpt index fcbc52e782..b0901e01a9 100644 --- a/ext/mysqli/tests/mysqli_fetch_all_oo.phpt +++ b/ext/mysqli/tests/mysqli_fetch_all_oo.phpt @@ -198,9 +198,9 @@ if (!function_exists('mysqli_fetch_all')) func_mysqli_fetch_all_oo($link, $engine, "BIGINT UNSIGNED", 18446744073709551615, "18446744073709551615", 270); func_mysqli_fetch_all_oo($link, $engine, "BIGINT UNSIGNED", NULL, NULL, 280); - func_mysqli_fetch_all_oo($link, $engine, "FLOAT", -9223372036854775808 - 1.1, "-9.22337e+18", 290, "/-9\.22337e\+[0]?18/iu"); + func_mysqli_fetch_all_oo($link, $engine, "FLOAT", -9223372036854775808 - 1.1, "-9.22337e+18", 290, "/-9\.22337e\+?[0]?18/iu"); func_mysqli_fetch_all_oo($link, $engine, "FLOAT", NULL, NULL, 300); - func_mysqli_fetch_all_oo($link, $engine, "FLOAT UNSIGNED", 18446744073709551615 + 1.1, "1.84467e+19", 310, "/1\.84467e\+[0]?19/iu"); + func_mysqli_fetch_all_oo($link, $engine, "FLOAT UNSIGNED", 18446744073709551615 + 1.1, "1.84467e+?19", 310, "/1\.84467e\+?[0]?19/iu"); func_mysqli_fetch_all_oo($link, $engine, "FLOAT UNSIGNED ", NULL, NULL, 320); func_mysqli_fetch_all_oo($link, $engine, "DOUBLE(10,2)", -99999999.99, "-99999999.99", 330); diff --git a/ext/mysqli/tests/mysqli_fetch_array.phpt b/ext/mysqli/tests/mysqli_fetch_array.phpt index 798d75096a..1b46d0d356 100644 --- a/ext/mysqli/tests/mysqli_fetch_array.phpt +++ b/ext/mysqli/tests/mysqli_fetch_array.phpt @@ -191,9 +191,9 @@ require_once('skipifconnectfailure.inc'); func_mysqli_fetch_array($link, $engine, "BIGINT UNSIGNED", NULL, NULL, 280); } - func_mysqli_fetch_array($link, $engine, "FLOAT", -9223372036854775808 - 1.1, "-9.22337e+18", 290, "/-9\.22337e\+[0]?18/iu"); + func_mysqli_fetch_array($link, $engine, "FLOAT", -9223372036854775808 - 1.1, "-9.22337e+18", 290, "/-9\.22337e\+?[0]?18/iu"); func_mysqli_fetch_array($link, $engine, "FLOAT", NULL, NULL, 300); - func_mysqli_fetch_array($link, $engine, "FLOAT UNSIGNED", 18446744073709551615 + 1.1, "1.84467e+19", 310, "/1\.84467e\+[0]?19/iu"); + func_mysqli_fetch_array($link, $engine, "FLOAT UNSIGNED", 18446744073709551615 + 1.1, "1.84467e+?19", 310, "/1\.84467e\+?[0]?19/iu"); func_mysqli_fetch_array($link, $engine, "FLOAT UNSIGNED ", NULL, NULL, 320); func_mysqli_fetch_array($link, $engine, "DOUBLE(10,2)", -99999999.99, "-99999999.99", 330); diff --git a/ext/mysqli/tests/mysqli_fetch_array_oo.phpt b/ext/mysqli/tests/mysqli_fetch_array_oo.phpt index a13b131e5f..816ea9fe19 100644 --- a/ext/mysqli/tests/mysqli_fetch_array_oo.phpt +++ b/ext/mysqli/tests/mysqli_fetch_array_oo.phpt @@ -175,9 +175,9 @@ require_once('skipifconnectfailure.inc'); func_mysqli_fetch_array($mysqli, $engine, "BIGINT UNSIGNED", NULL, NULL, 280); } - func_mysqli_fetch_array($mysqli, $engine, "FLOAT", -9223372036854775808 - 1.1, "-9.22337e+18", 290, "/-9\.22337e\+[0]?18/iu"); + func_mysqli_fetch_array($mysqli, $engine, "FLOAT", -9223372036854775808 - 1.1, "-9.22337e+18", 290, "/-9\.22337e\+?[0]?18/iu"); func_mysqli_fetch_array($mysqli, $engine, "FLOAT", NULL, NULL, 300); - func_mysqli_fetch_array($mysqli, $engine, "FLOAT UNSIGNED", 18446744073709551615 + 1.1, "1.84467e+19", 310, "/1\.84467e\+[0]?19/iu"); + func_mysqli_fetch_array($mysqli, $engine, "FLOAT UNSIGNED", 18446744073709551615 + 1.1, "1.84467e+?19", 310, "/1\.84467e\+?[0]?19/iu"); func_mysqli_fetch_array($mysqli, $engine, "FLOAT UNSIGNED ", NULL, NULL, 320); func_mysqli_fetch_array($mysqli, $engine, "DOUBLE(10,2)", -99999999.99, "-99999999.99", 330); diff --git a/ext/mysqli/tests/mysqli_fetch_field_flags.phpt b/ext/mysqli/tests/mysqli_fetch_field_flags.phpt index ebaab10cff..9c74408c20 100644 --- a/ext/mysqli/tests/mysqli_fetch_field_flags.phpt +++ b/ext/mysqli/tests/mysqli_fetch_field_flags.phpt @@ -5,11 +5,22 @@ mysqli_fetch_field() - flags/field->flags require_once('skipif.inc'); require_once('skipifemb.inc'); require_once('skipifconnectfailure.inc'); + +require_once('connect.inc'); +if (!$link = mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + die(printf("skip: [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error())); + +if (mysqli_get_server_version($link) < 50041) + die("skip: Due to many MySQL Server differences, the test requires 5.0.41+"); + +mysqli_close($link); ?> --FILE-- 'NOT_NULL', MYSQLI_PRI_KEY_FLAG => 'PRI_KEY', @@ -23,12 +34,19 @@ require_once('skipifconnectfailure.inc'); MYSQLI_SET_FLAG => 'SET', MYSQLI_NUM_FLAG => 'NUM', MYSQLI_PART_KEY_FLAG => 'PART_KEY', - MYSQLI_GROUP_FLAG => 'MYSQLI_GROUP_FLAG' - // MYSQLI_NO_DEFAULT_VALUE_FLAG - // MYSQLI_BINARY_FLAG - // MYSQLI_ENUM_FLAG + // MYSQLI_GROUP_FLAG => 'MYSQLI_GROUP_FLAG' - internal usage only + (defined('MYSQLI_NO_DEFAULT_VALUE_FLAG') ? MYSQLI_NO_DEFAULT_VALUE_FLAG : 4096) => 'NO_DEFAULT_VALUE', + (defined('MYSQLI_BINARY_FLAG') ? MYSQLI_BINARY_FLAG : 128) => 'BINARY', + (defined('MYSQLI_ENUM_FLAG') ? MYSQLI_ENUM_FLAG : 256) => 'ENUM', // MYSQLI_BINCMP_FLAG ); + + // 5.1.24 / 6.0.4+ + if (defined('MYSQLI_ON_UPDATE_NOW')) + $flags[MYSQLI_ON_UPDATE_NOW] = 'ON_UPDATE_NOW'; + else + $flags[8192] = 'ON_UPDATE_NOW'; + krsort($flags); $columns = array( @@ -37,13 +55,13 @@ require_once('skipifconnectfailure.inc'); 'INT NOT NULL DEFAULT 1' => 'NOT_NULL NUM', 'INT UNSIGNED DEFAULT NULL' => 'UNSIGNED NUM', 'INT UNSIGNED NOT NULL' => 'NOT_NULL UNSIGNED NO_DEFAULT_VALUE NUM', - 'INT UNSIGNED NOT NULL DEFAULT 1' => 'NOT_NULL UNSIGNED NULL', + 'INT UNSIGNED NOT NULL DEFAULT 1' => 'NOT_NULL UNSIGNED NUM', 'INT UNSIGNED ZEROFILL DEFAULT NULL' => 'UNSIGNED ZEROFILL NUM', 'INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY' => 'NOT_NULL PRI_KEY UNSIGNED AUTO_INCREMENT NUM PART_KEY', 'CHAR(1) DEFAULT NULL' => '', 'CHAR(1) NOT NULL' => 'NOT_NULL NO_DEFAULT_VALUE', 'TIMESTAMP NOT NULL' => 'NOT_NULL UNSIGNED ZEROFILL BINARY TIMESTAMP', - 'VARBINARY(127) DEFAULT NULL' => 'NOT_NULL BINARY', + 'VARBINARY(127) DEFAULT NULL' => 'BINARY', 'BLOB' => 'BLOB BINARY', 'TINYBLOB' => 'BLOB BINARY', 'MEDIUMBLOB' => 'BLOB BINARY', @@ -67,10 +85,9 @@ require_once('skipifconnectfailure.inc'); ); function checkFlags($reported_flags, $expected_flags, $flags) { - $found_flags = $unexpected_flags = ''; foreach ($flags as $code => $name) { - if ($code >= $reported_flags) { + if ($reported_flags >= $code) { $reported_flags -= $code; $found_flags .= $name . ' '; if (stristr($expected_flags, $name)) { @@ -118,13 +135,57 @@ require_once('skipifconnectfailure.inc'); continue; } + /* + TODO + Unfortunately different server versions give you slightly different + results.The test does not yet fully reflect all server changes/bugs etc. + */ + switch ($column_def) { + case 'TIMESTAMP NOT NULL': + // http://bugs.mysql.com/bug.php?id=30081 - new flag introduced in 5.1.24/6.0.4 + $version = mysqli_get_server_version($link); + if ((($version > 50122) && ($version < 60000)) || + ($version >= 60004)) { + // new flag ON_UPDATE_NOW_FLAG (8192) + $expected_flags .= ' ON_UPDATE_NOW'; + } + break; + + case 'INT UNSIGNED NOT NULL': + case 'INT NOT NULL': + case 'CHAR(1) NOT NULL': + case 'SET("one", "two") NOT NULL': + case 'ENUM("one", "two") NOT NULL': + $version = mysqli_get_server_version($link); + if ($version < 50000) { + // TODO - check exact version! + $expected_flags = trim(str_replace('NO_DEFAULT_VALUE', '', $expected_flags)); + } + break; + + case 'BIT': + $version = mysqli_get_server_version($link); + if ($version <= 50105) { + // TODO - check exact version! + $expected_flags = trim(str_replace('UNSIGNED', '', $expected_flags)); + } + + default: + break; + } + list($missing_flags, $unexpected_flags, $flags_found) = checkFlags($field->flags, $expected_flags, $flags); - if ($unexpected_flags) + if ($unexpected_flags) { printf("[006] Found unexpected flags '%s' for %s, found '%s'\n", $unexpected_flags, $column_def, $flags_found); - if ($missing_flags) + } + if ($missing_flags) { printf("[007] The flags '%s' have not been reported for %s, found '%s'\n", $missing_flags, $column_def, $flags_found); + var_dump($create); + var_dump(mysqli_get_server_version($link)); + die($missing_flags); + } mysqli_free_result($res); } diff --git a/ext/mysqli/tests/mysqli_stmt_execute.phpt b/ext/mysqli/tests/mysqli_stmt_execute.phpt index 3106e8e38f..6f3df35d6b 100644 --- a/ext/mysqli/tests/mysqli_stmt_execute.phpt +++ b/ext/mysqli/tests/mysqli_stmt_execute.phpt @@ -57,15 +57,61 @@ require_once('skipifconnectfailure.inc'); if (true !== ($tmp = mysqli_stmt_execute($stmt))) printf("[012] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + // calling reset between executions + mysqli_stmt_close($stmt); + if (!$stmt = mysqli_stmt_init($link)) + printf("[013] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (!mysqli_stmt_prepare($stmt, "SELECT id FROM test ORDER BY id LIMIT 1")) + printf("[014] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + if (true !== ($tmp = mysqli_stmt_execute($stmt))) + printf("[015] Expecting boolean/true, got %s/%s. [%d] %s\n", + gettype($tmp), $tmp, mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + $id = null; + if (!mysqli_stmt_bind_result($stmt, $id) || !mysqli_stmt_fetch($stmt)) + printf("[016] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + if ($id !== 1) + printf("[017] Expecting int/1 got %s/%s\n", gettype($id), $id); + + if (true !== ($tmp = mysqli_stmt_reset($stmt))) + printf("[018] Expecting boolean/true, got %s/%s. [%d] %s\n", + gettype($tmp), $tmp, mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + printf("Don't know what we should expect\n"); + var_dump(mysqli_stmt_execute($stmt)); + var_dump(mysqli_stmt_fetch($stmt)); + + mysqli_stmt_close($stmt); + if (!$stmt = mysqli_stmt_init($link)) + printf("[019] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (!mysqli_stmt_prepare($stmt, "SELECT id FROM test ORDER BY id LIMIT 1")) + printf("[020] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + if (true !== ($tmp = mysqli_stmt_execute($stmt))) + printf("[021] Expecting boolean/true, got %s/%s. [%d] %s\n", + gettype($tmp), $tmp, mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + if (true !== ($tmp = mysqli_stmt_reset($stmt))) + printf("[022] Expecting boolean/true, got %s/%s. [%d] %s\n", + gettype($tmp), $tmp, mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + printf("Don't know what we should expect\n"); + var_dump(mysqli_stmt_execute($stmt)); + var_dump(mysqli_stmt_fetch($stmt)); + mysqli_kill($link, mysqli_thread_id($link)); if (false !== ($tmp = mysqli_stmt_execute($stmt))) - printf("[014] Expecting boolean/false, got %s/%s\n", gettype($tmp), $tmp); + printf("[023] Expecting boolean/false, got %s/%s\n", gettype($tmp), $tmp); mysqli_stmt_close($stmt); if (NULL !== ($tmp = mysqli_stmt_execute($stmt))) - printf("[015] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + printf("[024] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); mysqli_close($link); print "done!"; diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index d7d6b95d68..988b5e0745 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -1274,10 +1274,12 @@ MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn, enum_connection_close_type c /* {{{ mysqlnd_conn::get_reference */ static MYSQLND * -MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_reference)(MYSQLND * const conn) +MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_reference)(MYSQLND * const conn TSRMLS_DC) { + DBG_ENTER("mysqlnd_conn::get_reference"); ++conn->refcount; - return conn; + DBG_INF_FMT("conn=%llu new_refcount=%u", conn->thread_id, conn->refcount); + DBG_RETURN(conn); } /* }}} */ @@ -1288,7 +1290,7 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_conn, free_reference)(MYSQLND * const conn TSRMLS { enum_func_status ret = PASS; DBG_ENTER("mysqlnd_conn::free_reference"); - DBG_INF_FMT("conn=%llu conn->refcount=%u", conn->thread_id, conn->refcount); + DBG_INF_FMT("conn=%llu old_refcount=%u", conn->thread_id, conn->refcount); if (!(--conn->refcount)) { /* No multithreading issues as we don't share the connection :) @@ -1474,10 +1476,11 @@ MYSQLND_METHOD(mysqlnd_conn, get_server_version)(const MYSQLND * const conn) /* {{{ mysqlnd_conn::more_results */ static zend_bool -MYSQLND_METHOD(mysqlnd_conn,more_results)(const MYSQLND * const conn) +MYSQLND_METHOD(mysqlnd_conn, more_results)(const MYSQLND * const conn TSRMLS_DC) { + DBG_ENTER("mysqlnd_conn::more_results"); /* (conn->state == CONN_NEXT_RESULT_PENDING) too */ - return conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS? TRUE:FALSE; + DBG_RETURN(conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS? TRUE:FALSE); } /* }}} */ @@ -1792,7 +1795,7 @@ MYSQLND_METHOD(mysqlnd_conn, use_result)(MYSQLND * const conn TSRMLS_DC) result = conn->current_result; conn->current_result = NULL; - result->conn = conn->m->get_reference(conn); + result->conn = conn->m->get_reference(conn TSRMLS_CC); result = result->m.use_result(result, FALSE TSRMLS_CC); DBG_RETURN(result); @@ -1956,7 +1959,7 @@ PHPAPI MYSQLND *_mysqlnd_init(zend_bool persistent TSRMLS_DC) ret->persistent = persistent; ret->m = & mysqlnd_mysqlnd_conn_methods; - ret->m->get_reference(ret); + ret->m->get_reference(ret TSRMLS_CC); #ifdef MYSQLND_THREADED ret->LOCK_state = tsrm_mutex_alloc(); diff --git a/ext/mysqlnd/mysqlnd.h b/ext/mysqlnd/mysqlnd.h index 86c11cfe53..d41a66fca8 100644 --- a/ext/mysqlnd/mysqlnd.h +++ b/ext/mysqlnd/mysqlnd.h @@ -112,7 +112,7 @@ PHPAPI void _mysqlnd_debug(const char *mode TSRMLS_DC); #define mysqlnd_store_result(conn) (conn)->m->store_result((conn) TSRMLS_CC) #define mysqlnd_bg_store_result(conn) (conn)->m->background_store_result((conn) TSRMLS_CC) #define mysqlnd_next_result(conn) (conn)->m->next_result((conn) TSRMLS_CC) -#define mysqlnd_more_results(conn) (conn)->m->more_results((conn)) +#define mysqlnd_more_results(conn) (conn)->m->more_results((conn) TSRMLS_CC) #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) TSRMLS_CC) @@ -262,6 +262,8 @@ PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, size #define mysqlnd_stmt_store_result(stmt) (!mysqlnd_stmt_field_count((stmt)) ? PASS:((stmt)->m->store_result((stmt) TSRMLS_CC)? PASS:FAIL)) #define mysqlnd_stmt_bg_store_result(stmt) (!mysqlnd_stmt_field_count((stmt)) ? PASS:((stmt)->m->background_store_result((stmt) TSRMLS_CC)? PASS:FAIL)) #define mysqlnd_stmt_get_result(stmt) (stmt)->m->get_result((stmt) TSRMLS_CC) +#define mysqlnd_stmt_more_results(stmt) (stmt)->m->more_results((stmt) TSRMLS_CC) +#define mysqlnd_stmt_next_result(stmt) (stmt)->m->next_result((stmt) TSRMLS_CC) #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) @@ -271,6 +273,7 @@ PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, size #define mysqlnd_stmt_refresh_bind_param(s) (s)->m->refresh_bind_param((s) TSRMLS_CC) #define mysqlnd_stmt_set_param_bind_dtor(s,d) (s)->m->set_param_bind_dtor((s), (d) TSRMLS_CC) #define mysqlnd_stmt_bind_result(stmt,bind) (stmt)->m->bind_result((stmt), (bind) TSRMLS_CC) +#define mysqlnd_stmt_bind_one_result(s,no) (s)->m->bind_one_result((s), (no) TSRMLS_CC) #define mysqlnd_stmt_set_result_bind_dtor(s,d) (s)->m->set_result_bind_dtor((s), (d) 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) TSRMLS_CC) @@ -305,9 +308,10 @@ PHPAPI void _mysqlnd_palloc_rshutdown(MYSQLND_THD_ZVAL_PCACHE * cache TSRM #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) +#define mysqlnd_palloc_get_thd_cache_reference(cache) _mysqlnd_palloc_get_thd_cache_reference((cache) TSRMLS_CC) 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); +MYSQLND_THD_ZVAL_PCACHE* _mysqlnd_palloc_get_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE * const cache TSRMLS_DC); PHPAPI void _mysqlnd_palloc_free_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE **cache TSRMLS_DC); diff --git a/ext/mysqlnd/mysqlnd_enum_n_def.h b/ext/mysqlnd/mysqlnd_enum_n_def.h index 940a6f7872..9c97cf7796 100644 --- a/ext/mysqlnd/mysqlnd_enum_n_def.h +++ b/ext/mysqlnd/mysqlnd_enum_n_def.h @@ -28,8 +28,26 @@ #define MYSQLND_SQLSTATE_LENGTH 5 #define MYSQLND_SQLSTATE_NULL "00000" +#define SERVER_STATUS_IN_TRANS 1 /* Transaction has started */ +#define SERVER_STATUS_AUTOCOMMIT 2 /* Server in auto_commit mode */ +#define SERVER_MORE_RESULTS_EXISTS 8 /* Multi query - next query exists */ #define MYSQLND_SERVER_QUERY_NO_GOOD_INDEX_USED 16 #define MYSQLND_SERVER_QUERY_NO_INDEX_USED 32 +/* + The server was able to fulfill the clients request and opened a + read-only non-scrollable cursor for a query. This flag comes + in reply to COM_STMT_EXECUTE and COM_STMT_FETCH commands. +*/ +#define SERVER_STATUS_CURSOR_EXISTS 64 +/* + This flag is sent when a read-only cursor is exhausted, in reply to + COM_STMT_FETCH command. +*/ +#define SERVER_STATUS_LAST_ROW_SENT 128 +#define SERVER_STATUS_DB_DROPPED 256 /* A database was dropped */ +#define SERVER_STATUS_NO_BACKSLASH_ESCAPES 512 +#define SERVER_QUERY_WAS_SLOW 1024 + #define MYSQLND_NO_DATA 100 #define MYSQLND_DATA_TRUNCATED 101 diff --git a/ext/mysqlnd/mysqlnd_palloc.c b/ext/mysqlnd/mysqlnd_palloc.c index 976e473733..f64500b5bf 100644 --- a/ext/mysqlnd/mysqlnd_palloc.c +++ b/ext/mysqlnd/mysqlnd_palloc.c @@ -151,14 +151,16 @@ PHPAPI MYSQLND_THD_ZVAL_PCACHE* _mysqlnd_palloc_init_thd_cache(MYSQLND_ZVAL_PCAC /* }}} */ -/* {{{ mysqlnd_palloc_get_thd_cache_reference */ -MYSQLND_THD_ZVAL_PCACHE* mysqlnd_palloc_get_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE * const cache) +/* {{{ _mysqlnd_palloc_get_thd_cache_reference */ +MYSQLND_THD_ZVAL_PCACHE* _mysqlnd_palloc_get_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE * const cache TSRMLS_DC) { + DBG_ENTER("_mysqlnd_palloc_get_thd_cache_reference"); if (cache) { ++cache->references; + DBG_INF_FMT("cache=%p new_refc=%d", *cache, cache->references); mysqlnd_palloc_get_cache_reference(cache->parent); } - return cache; + DBG_RETURN(cache); } /* }}} */ @@ -190,8 +192,8 @@ PHPAPI void _mysqlnd_palloc_free_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE **c { DBG_ENTER("_mysqlnd_palloc_free_thd_cache_reference"); if (*cache) { - DBG_INF_FMT("cache=%p refs=%d", *cache, (*cache)->references); --(*cache)->parent->references; + DBG_INF_FMT("cache=%p references_left=%d", *cache, (*cache)->references); if (--(*cache)->references == 0) { mysqlnd_palloc_free_thd_cache(*cache TSRMLS_CC); @@ -493,7 +495,7 @@ PHPAPI void _mysqlnd_palloc_rshutdown(MYSQLND_THD_ZVAL_PCACHE * thd_cache TSRMLS ++cache->free_items; #ifdef ZTS memset(&((*p)->thread_id), 0, sizeof(THREAD_T)); -#endif +#endif p++; } UNLOCK_PCACHE(cache); diff --git a/ext/mysqlnd/mysqlnd_priv.h b/ext/mysqlnd/mysqlnd_priv.h index 7ffbc67eaf..229dd903b7 100644 --- a/ext/mysqlnd/mysqlnd_priv.h +++ b/ext/mysqlnd/mysqlnd_priv.h @@ -74,23 +74,6 @@ -#define SERVER_STATUS_IN_TRANS 1 /* Transaction has started */ -#define SERVER_STATUS_AUTOCOMMIT 2 /* Server in auto_commit mode */ -#define SERVER_MORE_RESULTS_EXISTS 8 /* Multi query - next query exists */ -/* - The server was able to fulfill the clients request and opened a - read-only non-scrollable cursor for a query. This flag comes - in reply to COM_STMT_EXECUTE and COM_STMT_FETCH commands. -*/ -#define SERVER_STATUS_CURSOR_EXISTS 64 -/* - This flag is sent when a read-only cursor is exhausted, in reply to - COM_STMT_FETCH command. -*/ -#define SERVER_STATUS_LAST_ROW_SENT 128 -#define SERVER_STATUS_DB_DROPPED 256 /* A database was dropped */ -#define SERVER_STATUS_NO_BACKSLASH_ESCAPES 512 -#define SERVER_QUERY_WAS_SLOW 1024 /* Client Error codes */ diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c index 59e9a96f63..acacee22ef 100644 --- a/ext/mysqlnd/mysqlnd_ps.c +++ b/ext/mysqlnd/mysqlnd_ps.c @@ -57,7 +57,10 @@ enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, zend_bool *fetched_anything TSRMLS_DC); static void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC); +static void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, uint param_no TSRMLS_DC); +static void mysqlnd_internal_free_stmt_content(MYSQLND_STMT * const stmt TSRMLS_DC); +static enum_func_status mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const stmt TSRMLS_DC); /* {{{ mysqlnd_stmt::store_result */ static MYSQLND_RES * @@ -100,7 +103,9 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC) result->type = MYSQLND_RES_PS_BUF; result->m.fetch_row = mysqlnd_fetch_stmt_row_buffered; result->m.fetch_lengths = NULL;/* makes no sense */ - result->zval_cache = mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache); + if (!result->zval_cache) { + result->zval_cache = mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache); + } /* Create room for 'next_extend' rows */ @@ -167,7 +172,9 @@ MYSQLND_METHOD(mysqlnd_stmt, background_store_result)(MYSQLND_STMT * const stmt result->type = MYSQLND_RES_PS_BUF; result->m.fetch_row = mysqlnd_fetch_stmt_row_buffered; result->m.fetch_lengths = NULL;/* makes no sense */ - result->zval_cache = mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache); + if (!result->zval_cache) { + result->zval_cache = mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache); + } /* Create room for 'next_extend' rows */ @@ -248,6 +255,45 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const stmt TSRMLS_DC) /* }}} */ +/* {{{ mysqlnd_stmt::more_results */ +static zend_bool +MYSQLND_METHOD(mysqlnd_stmt, more_results)(const MYSQLND_STMT * stmt TSRMLS_DC) +{ + DBG_ENTER("mysqlnd_stmt::more_results"); + /* (conn->state == CONN_NEXT_RESULT_PENDING) too */ + DBG_RETURN((stmt->conn && (stmt->conn->upsert_status.server_status & + SERVER_MORE_RESULTS_EXISTS))? + TRUE: + FALSE); +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::next_result */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_stmt, next_result)(MYSQLND_STMT * stmt TSRMLS_DC) +{ + MYSQLND *conn = stmt->conn; + + DBG_ENTER("mysqlnd_stmt::next_result"); + DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + + if (!conn || + CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING || + !(conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) || + !stmt->result) + { + DBG_RETURN(FAIL); + } + + /* Free space for next result */ + mysqlnd_internal_free_stmt_content(stmt TSRMLS_CC); + + DBG_RETURN(mysqlnd_stmt_execute_parse_response(stmt TSRMLS_CC)); +} +/* }}} */ + + /* {{{ mysqlnd_stmt_skip_metadata */ static enum_func_status mysqlnd_stmt_skip_metadata(MYSQLND_STMT *stmt TSRMLS_DC) @@ -399,7 +445,7 @@ MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const stmt, const char * co /* Allocate the result now as it is needed for the reading of metadata */ stmt_to_prepare->result = result; - result->conn = stmt_to_prepare->conn->m->get_reference(stmt_to_prepare->conn); + result->conn = stmt_to_prepare->conn->m->get_reference(stmt_to_prepare->conn TSRMLS_CC); result->type = MYSQLND_RES_PS_BUF; @@ -411,7 +457,7 @@ MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const stmt, const char * co if (stmt_to_prepare != stmt) { /* Free old buffers, binding and resources on server */ - stmt->m->close(stmt, TRUE TSRMLS_CC); + stmt->m->net_close(stmt, TRUE TSRMLS_CC); memcpy(stmt, stmt_to_prepare, sizeof(MYSQLND_STMT)); @@ -434,6 +480,92 @@ fail: /* }}} */ +/* {{{ mysqlnd_stmt_execute_parse_response */ +static enum_func_status +mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const stmt TSRMLS_DC) +{ + enum_func_status ret; + MYSQLND *conn = stmt->conn; + + DBG_ENTER("mysqlnd_stmt_execute_parse_response"); + + CONN_SET_STATE(conn, CONN_QUERY_SENT); + + ret = mysqlnd_query_read_result_set_header(stmt->conn, stmt TSRMLS_CC); + if (ret == FAIL) { + stmt->error_info = conn->error_info; + stmt->upsert_status.affected_rows = conn->upsert_status.affected_rows; + if (CONN_GET_STATE(conn) == CONN_QUIT_SENT) { + /* close the statement here, the connection has been closed */ + } + stmt->state = MYSQLND_STMT_PREPARED; + } else { + SET_EMPTY_ERROR(stmt->error_info); + SET_EMPTY_ERROR(stmt->conn->error_info); + stmt->send_types_to_server = 0; + stmt->upsert_status = conn->upsert_status; + stmt->state = MYSQLND_STMT_EXECUTED; + if (conn->last_query_type == QUERY_UPSERT || conn->last_query_type == QUERY_LOAD_LOCAL) { + DBG_INF("PASS"); + DBG_RETURN(PASS); + } + + stmt->result->type = MYSQLND_RES_PS_BUF; + if (!stmt->result->conn) { + /* + For SHOW we don't create (bypasses PS in server) + a result set at prepare and thus a connection was missing + */ + stmt->result->conn = stmt->conn->m->get_reference(stmt->conn TSRMLS_CC); + } + + /* Update stmt->field_count as SHOW sets it to 0 at prepare */ + stmt->field_count = stmt->result->field_count = conn->field_count; + stmt->result->lengths = NULL; + if (stmt->field_count) { + stmt->state = MYSQLND_STMT_WAITING_USE_OR_STORE; + /* + We need to set this because the user might not call + 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. + */ + DBG_INF_FMT("server_status=%d cursor=%d", 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_SET_STATE(conn, 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 + above is not fulfilled. Then... + + This is a single-row result set, a result set with no rows, EXPLAIN, + SHOW VARIABLES, or some other command which either a) bypasses the + cursors framework in the server and writes rows directly to the + network or b) is more efficient if all (few) result set rows are + precached on client and server's resources are freed. + */ + /* 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"); + } + } + } + + DBG_INF(ret == PASS? "PASS":"FAIL"); + DBG_RETURN(ret); +} +/* }}} */ + + /* {{{ mysqlnd_stmt::execute */ static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) @@ -451,6 +583,12 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) SET_ERROR_AFF_ROWS(stmt->conn); if (stmt->state > MYSQLND_STMT_PREPARED && stmt->field_count) { + /* + We don need to copy the data from the buffers which we will clean. + Because it has already been copied. See + #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF + */ +#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF if (stmt->result_bind && stmt->result_zvals_separated_once == TRUE && stmt->state >= MYSQLND_STMT_USER_FETCHING) @@ -474,6 +612,29 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) } } } +#endif + + /* + If right after execute() we have to call the appropriate + use_result() or store_result() and clean. + */ + 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); + } + + if (stmt->state > MYSQLND_STMT_PREPARED) { + /* As the buffers have been freed, we should go back to PREPARED */ + stmt->state = MYSQLND_STMT_PREPARED; + } /* Executed, but the user hasn't started to fetch @@ -534,79 +695,7 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) } stmt->execute_count++; - CONN_SET_STATE(conn, CONN_QUERY_SENT); - - ret = mysqlnd_query_read_result_set_header(stmt->conn, stmt TSRMLS_CC); - if (ret == FAIL) { - stmt->error_info = conn->error_info; - stmt->upsert_status.affected_rows = conn->upsert_status.affected_rows; - if (CONN_GET_STATE(conn) == CONN_QUIT_SENT) { - /* close the statement here, the connection has been closed */ - } - stmt->state = MYSQLND_STMT_PREPARED; - } else { - SET_EMPTY_ERROR(stmt->error_info); - SET_EMPTY_ERROR(stmt->conn->error_info); - stmt->send_types_to_server = 0; - stmt->upsert_status = conn->upsert_status; - stmt->state = MYSQLND_STMT_EXECUTED; - if (conn->last_query_type == QUERY_UPSERT || conn->last_query_type == QUERY_LOAD_LOCAL) { - DBG_INF("PASS"); - DBG_RETURN(PASS); - } - - stmt->result->type = MYSQLND_RES_PS_BUF; - if (!stmt->result->conn) { - /* - For SHOW we don't create (bypasses PS in server) - a result set at prepare and thus a connection was missing - */ - stmt->result->conn = stmt->conn->m->get_reference(stmt->conn); - } - - /* Update stmt->field_count as SHOW sets it to 0 at prepare */ - stmt->field_count = stmt->result->field_count = conn->field_count; - stmt->result->lengths = NULL; - if (stmt->field_count) { - stmt->state = MYSQLND_STMT_WAITING_USE_OR_STORE; - /* - We need to set this because the user might not call - 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. - */ - DBG_INF_FMT("server_status=%d cursor=%d", 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_SET_STATE(conn, 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 - above is not fulfilled. Then... - - This is a single-row result set, a result set with no rows, EXPLAIN, - SHOW VARIABLES, or some other command which either a) bypasses the - cursors framework in the server and writes rows directly to the - network or b) is more efficient if all (few) result set rows are - precached on client and server's resources are freed. - */ - /* 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"); - } - } - } - - DBG_INF(ret == PASS? "PASS":"FAIL"); - DBG_RETURN(ret); + DBG_RETURN(mysqlnd_stmt_execute_parse_response(stmt TSRMLS_CC)); } /* }}} */ @@ -661,7 +750,9 @@ mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int f for (i = 0; i < result->field_count; i++) { /* Clean what we copied last time */ #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF - zval_dtor(stmt->result_bind[i].zv); + if (stmt->result_bind[i].zv) { + zval_dtor(stmt->result_bind[i].zv); + } #endif /* copy the type */ if (stmt->result_bind[i].bound == TRUE) { @@ -978,7 +1069,7 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla stmt->conn->upsert_status.server_status = row_packet->server_status; - DBG_INF_FMT("ret=%s fetched=%d s_status=%d warns=%d eof=%d", + DBG_INF_FMT("ret=%s fetched=%d server_status=%d warnings=%d eof=%d", ret == PASS? "PASS":"FAIL", *fetched_anything, row_packet->server_status, row_packet->warning_count, result->unbuf->eof_reached); @@ -1079,8 +1170,13 @@ MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const stmt TSRMLS_DC) DBG_INF("skipping result"); stmt->result->m.skip_result(stmt->result TSRMLS_CC); } + /* Now the line should be free, if it wasn't */ + DBG_INF("freeing result"); + /* free_result() doesn't actually free stmt->result but only the buffers */ + stmt->m->free_result(stmt TSRMLS_CC); + int4store(cmd_buf, stmt->stmt_id); if (CONN_GET_STATE(conn) == CONN_READY && FAIL == (ret = mysqlnd_simple_command(conn, COM_STMT_RESET, (char *)cmd_buf, @@ -1365,8 +1461,6 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt, 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); @@ -1377,6 +1471,9 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt, DBG_RETURN(FAIL); } + SET_EMPTY_ERROR(stmt->error_info); + SET_EMPTY_ERROR(stmt->conn->error_info); + if (stmt->field_count) { uint i = 0; @@ -1386,7 +1483,7 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt, } mysqlnd_stmt_separate_result_bind(stmt TSRMLS_CC); - + stmt->result_zvals_separated_once = FALSE; stmt->result_bind = result_bind; for (i = 0; i < stmt->field_count; i++) { /* Prevent from freeing */ @@ -1407,6 +1504,50 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt, /* }}} */ +/* {{{ mysqlnd_stmt::bind_result */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const stmt, uint param_no TSRMLS_DC) +{ + DBG_ENTER("mysqlnd_stmt::bind_result"); + DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count); + + if (stmt->state < MYSQLND_STMT_PREPARED) { + SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared); + DBG_ERR("not prepared"); + DBG_RETURN(FAIL); + } + + if (param_no < 0 || param_no >= stmt->field_count) { + SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number"); + DBG_ERR("invalid param_no"); + DBG_RETURN(FAIL); + } + + SET_EMPTY_ERROR(stmt->error_info); + SET_EMPTY_ERROR(stmt->conn->error_info); + + if (stmt->field_count) { + mysqlnd_stmt_separate_one_result_bind(stmt, param_no TSRMLS_CC); + /* Guaranteed is that stmt->result_bind is NULL */ + if (!stmt->result_bind) { + stmt->result_bind = ecalloc(stmt->field_count, sizeof(MYSQLND_RESULT_BIND)); + } else { + stmt->result_bind = erealloc(stmt->result_bind, stmt->field_count * sizeof(MYSQLND_RESULT_BIND)); + } + ALLOC_INIT_ZVAL(stmt->result_bind[param_no].zv); + /* + Don't update is_ref !!! it's not our job + Otherwise either 009.phpt or mysqli_stmt_bind_result.phpt + will fail. + */ + stmt->result_bind[param_no].bound = TRUE; + } + DBG_INF("PASS"); + DBG_RETURN(PASS); +} +/* }}} */ + + /* {{{ mysqlnd_stmt::set_bind_result_dtor */ static void MYSQLND_METHOD(mysqlnd_stmt, set_result_bind_dtor)(MYSQLND_STMT * const stmt, @@ -1613,7 +1754,7 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const stmt, /* {{{ mysqlnd_stmt::attr_get */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, attr_get)(MYSQLND_STMT * const stmt, +MYSQLND_METHOD(mysqlnd_stmt, attr_get)(const MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, void * const value TSRMLS_DC) { @@ -1638,7 +1779,7 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_get)(MYSQLND_STMT * const stmt, } /* }}} */ - +/* free_result() doesn't actually free stmt->result but only the buffers */ /* {{{ mysqlnd_stmt::free_result */ static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC) @@ -1651,6 +1792,10 @@ MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC) DBG_RETURN(PASS); } + /* + If right after execute() we have to call the appropriate + use_result() or store_result() and clean. + */ if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { DBG_INF("fetching result set header"); /* Do implicit use_result and then flush the result */ @@ -1672,8 +1817,10 @@ MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC) stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC); } - /* As the buffers have been freed, we should go back to PREPARED */ - stmt->state = MYSQLND_STMT_PREPARED; + if (stmt->state > MYSQLND_STMT_PREPARED) { + /* As the buffers have been freed, we should go back to PREPARED */ + stmt->state = MYSQLND_STMT_PREPARED; + } /* Line is free! */ CONN_SET_STATE(stmt->conn, CONN_READY); @@ -1737,9 +1884,55 @@ void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC) /* }}} */ +/* {{{ mysqlnd_stmt_separate_one_result_bind */ +void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, uint param_no TSRMLS_DC) +{ + DBG_ENTER("mysqlnd_stmt_separate_one_result_bind"); + DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u param_no=%d", + stmt->stmt_id, stmt->result_bind, stmt->field_count, param_no); + + if (!stmt->result_bind) { + DBG_VOID_RETURN; + } + + /* + Because only the bound variables can point to our internal buffers, then + separate or free only them. Free is possible because the user could have + lost reference. + */ + /* Let's try with no cache */ + if (stmt->result_bind[param_no].bound == TRUE) { + DBG_INF_FMT("%d has refcount=%u", param_no, Z_REFCOUNT_P(stmt->result_bind[param_no].zv)); + /* + We have to separate the actual zval value of the bound + variable from our allocated zvals or we will face double-free + */ + if (Z_REFCOUNT_P(stmt->result_bind[param_no].zv) > 1) { +#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF + zval_copy_ctor(stmt->result_bind[param_no].zv); +#endif + zval_ptr_dtor(&stmt->result_bind[param_no].zv); + } else { + /* + If it is a string, what is pointed will be freed + later in free_result(). We need to remove the variable to + which the user has lost reference. + */ +#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF + ZVAL_NULL(stmt->result_bind[param_no].zv); +#endif + zval_ptr_dtor(&stmt->result_bind[param_no].zv); + } + } + + DBG_VOID_RETURN; +} +/* }}} */ + + /* {{{ mysqlnd_internal_free_stmt_content */ static -void mysqlnd_internal_free_stmt_content(MYSQLND_STMT *stmt TSRMLS_DC) +void mysqlnd_internal_free_stmt_content(MYSQLND_STMT * const stmt TSRMLS_DC) { DBG_ENTER("mysqlnd_internal_free_stmt_content"); DBG_INF_FMT("stmt=%lu param_bind=%p param_count=%u", @@ -1778,24 +1971,15 @@ void mysqlnd_internal_free_stmt_content(MYSQLND_STMT *stmt TSRMLS_DC) stmt->result->m.free_result_internal(stmt->result TSRMLS_CC); stmt->result = NULL; } - if (stmt->cmd_buffer.buffer) { - mnd_efree(stmt->cmd_buffer.buffer); - stmt->cmd_buffer.buffer = NULL; - } - - if (stmt->conn) { - stmt->conn->m->free_reference(stmt->conn TSRMLS_CC); - stmt->conn = NULL; - } DBG_VOID_RETURN; } /* }}} */ -/* {{{ mysqlnd_stmt::close */ +/* {{{ mysqlnd_stmt::net_close */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, close)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC) +MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC) { MYSQLND * conn = stmt->conn; zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */]; @@ -1854,16 +2038,25 @@ MYSQLND_METHOD(mysqlnd_stmt, close)(MYSQLND_STMT * const stmt, zend_bool implici MYSQLND_INC_CONN_STATISTIC(&conn->stats, stat); } + if (stmt->execute_cmd_buffer.buffer) { + mnd_efree(stmt->execute_cmd_buffer.buffer); + stmt->execute_cmd_buffer.buffer = NULL; + } + mysqlnd_internal_free_stmt_content(stmt TSRMLS_CC); + if (stmt->conn) { + stmt->conn->m->free_reference(stmt->conn TSRMLS_CC); + stmt->conn = NULL; + } + DBG_RETURN(PASS); } /* }}} */ - /* {{{ mysqlnd_stmt::dtor */ static enum_func_status -MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, dtor)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, dtor)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC) { enum_func_status ret; @@ -1873,9 +2066,8 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, dtor)(MYSQLND_STMT * const stmt, zend_bool MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE? STAT_STMT_CLOSE_IMPLICIT: STAT_STMT_CLOSE_EXPLICIT); - if (PASS == (ret = stmt->m->close(stmt, implicit TSRMLS_CC))) { - mnd_efree(stmt); - } + ret = stmt->m->net_close(stmt, implicit TSRMLS_CC); + mnd_efree(stmt); DBG_INF(ret == PASS? "PASS":"FAIL"); DBG_RETURN(ret); @@ -1891,11 +2083,13 @@ struct st_mysqlnd_stmt_methods mysqlnd_stmt_methods = { MYSQLND_METHOD(mysqlnd_stmt, store_result), MYSQLND_METHOD(mysqlnd_stmt, background_store_result), MYSQLND_METHOD(mysqlnd_stmt, get_result), + MYSQLND_METHOD(mysqlnd_stmt, more_results), + MYSQLND_METHOD(mysqlnd_stmt, next_result), MYSQLND_METHOD(mysqlnd_stmt, free_result), MYSQLND_METHOD(mysqlnd_stmt, data_seek), MYSQLND_METHOD(mysqlnd_stmt, reset), - MYSQLND_METHOD(mysqlnd_stmt, close), - MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, dtor), + MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close), + MYSQLND_METHOD(mysqlnd_stmt, dtor), MYSQLND_METHOD(mysqlnd_stmt, fetch), @@ -1904,6 +2098,7 @@ struct st_mysqlnd_stmt_methods mysqlnd_stmt_methods = { MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param), MYSQLND_METHOD(mysqlnd_stmt, set_param_bind_dtor), MYSQLND_METHOD(mysqlnd_stmt, bind_result), + MYSQLND_METHOD(mysqlnd_stmt, bind_one_result), MYSQLND_METHOD(mysqlnd_stmt, set_result_bind_dtor), MYSQLND_METHOD(mysqlnd_stmt, send_long_data), MYSQLND_METHOD(mysqlnd_stmt, param_metadata), @@ -1936,8 +2131,8 @@ MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC) stmt->m = &mysqlnd_stmt_methods; stmt->state = MYSQLND_STMT_INITTED; - stmt->cmd_buffer.length = 4096; - stmt->cmd_buffer.buffer = mnd_emalloc(stmt->cmd_buffer.length); + stmt->execute_cmd_buffer.length = 4096; + stmt->execute_cmd_buffer.buffer = mnd_emalloc(stmt->execute_cmd_buffer.length); stmt->prefetch_rows = MYSQLND_DEFAULT_PREFETCH_ROWS; /* @@ -1945,7 +2140,7 @@ MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC) be destructed till there is open statements. The last statement or normal query result will close it then. */ - stmt->conn = conn->m->get_reference(conn); + stmt->conn = conn->m->get_reference(conn TSRMLS_CC); stmt->m->set_param_bind_dtor(stmt, mysqlnd_efree_param_bind_dtor TSRMLS_CC); stmt->m->set_result_bind_dtor(stmt, mysqlnd_efree_result_bind_dtor TSRMLS_CC); diff --git a/ext/mysqlnd/mysqlnd_ps_codec.c b/ext/mysqlnd/mysqlnd_ps_codec.c index c832c6b463..0e2c4345db 100644 --- a/ext/mysqlnd/mysqlnd_ps_codec.c +++ b/ext/mysqlnd/mysqlnd_ps_codec.c @@ -771,9 +771,9 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uch 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; - size_t cmd_buffer_length = stmt->cmd_buffer.length; + zend_uchar *p = stmt->execute_cmd_buffer.buffer, + *cmd_buffer = stmt->execute_cmd_buffer.buffer; + size_t cmd_buffer_length = stmt->execute_cmd_buffer.length; unsigned int null_byte_offset, null_count= (stmt->param_count + 7) / 8; @@ -801,7 +801,7 @@ zend_uchar* mysqlnd_stmt_execute_generate_request(MYSQLND_STMT *stmt, size_t *re 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); + *free_buffer = (cmd_buffer != stmt->execute_cmd_buffer.buffer); *request_len = (p - cmd_buffer); return cmd_buffer; } diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c index fac01c1047..b30f90409f 100644 --- a/ext/mysqlnd/mysqlnd_result.c +++ b/ext/mysqlnd/mysqlnd_result.c @@ -177,6 +177,7 @@ void mysqlnd_unbuffered_free_last_data(MYSQLND_RES *result TSRMLS_DC) } /* }}} */ + /* {{{ mysqlnd_free_buffered_data */ void mysqlnd_free_buffered_data(MYSQLND_RES *result TSRMLS_DC) { @@ -231,6 +232,7 @@ void mysqlnd_free_buffered_data(MYSQLND_RES *result TSRMLS_DC) } /* }}} */ + #ifdef MYSQLND_THREADED /* {{{ mysqlnd_free_background_buffered_data */ void mysqlnd_free_background_buffered_data(MYSQLND_RES *result TSRMLS_DC) @@ -309,8 +311,9 @@ void mysqlnd_free_background_buffered_data(MYSQLND_RES *result TSRMLS_DC) /* }}} */ #endif /* MYSQL_THREADING */ + /* {{{ mysqlnd_res::free_result_buffers */ -void +static void MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES *result TSRMLS_DC) { DBG_ENTER("mysqlnd_res::free_result_buffers"); @@ -580,8 +583,15 @@ 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); + DBG_INF_FMT("warnings=%u server_status=%u", fields_eof.warning_count, fields_eof.server_status); conn->upsert_status.warning_count = fields_eof.warning_count; + /* + If SERVER_MORE_RESULTS_EXISTS is set then this is either MULTI_QUERY or a CALL() + The first packet after sending the query/com_execute has the bit set only + in this cases. Not sure why it's a needed but it marks that the whole stream + will include many result sets. What actually matters are the bits set at the end + of every result set (the EOF packet). + */ conn->upsert_status.server_status = fields_eof.server_status; if (fields_eof.server_status & MYSQLND_SERVER_QUERY_NO_GOOD_INDEX_USED) { stat = STAT_BAD_INDEX_USED; @@ -789,7 +799,7 @@ mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES *result TSRMLS_DC) 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); + DBG_INF_FMT("warningss=%u server_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; @@ -929,7 +939,7 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flag 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); + DBG_INF_FMT("warnings=%u server_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; @@ -1261,7 +1271,7 @@ mysqlnd_store_result_fetch_data(MYSQLND * const conn, MYSQLND_RES *result, } PACKET_FREE(row_packet); - DBG_INF_FMT("ret=%s row_count=%u warns=%u status=%u", ret == PASS? "PASS":"FAIL", + DBG_INF_FMT("ret=%s row_count=%u warnings=%u server_status=%u", ret == PASS? "PASS":"FAIL", set->row_count, conn->upsert_status.warning_count, conn->upsert_status.server_status); DBG_RETURN(ret); } @@ -1281,7 +1291,7 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result, DBG_INF_FMT("conn=%d ps_protocol=%d", conn->thread_id, ps_protocol); /* We need the conn because we are doing lazy zval initialization in buffered_fetch_row */ - result->conn = conn->m->get_reference(conn); + result->conn = conn->m->get_reference(conn TSRMLS_CC); result->type = MYSQLND_RES_NORMAL; result->m.fetch_row = result->m.fetch_row_normal_buffered; result->m.fetch_lengths = mysqlnd_fetch_lengths_buffered; @@ -1305,6 +1315,7 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result, } /* }}} */ + #ifdef MYSQLND_THREADED /* {{{ mysqlnd_fetch_row_async_buffered */ static enum_func_status @@ -1432,6 +1443,7 @@ mysqlnd_fetch_row_async_buffered(MYSQLND_RES *result, void *param, unsigned int } /* }}} */ + /* {{{ mysqlnd_background_store_result_fetch_data */ enum_func_status mysqlnd_background_store_result_fetch_data(MYSQLND_RES *result TSRMLS_DC) @@ -1465,7 +1477,9 @@ mysqlnd_background_store_result_fetch_data(MYSQLND_RES *result TSRMLS_DC) old_size = set->data_size; set->data_size = total_rows; set->data = mnd_perealloc(set->data, set->data_size * sizeof(zval **), set->persistent); -// memset(set->data + old_size, 0, (set->data_size - old_size) * sizeof(zval **)); +#if 0 + memset(set->data + old_size, 0, (set->data_size - old_size) * sizeof(zval **)); +#endif set->row_buffers = mnd_perealloc(set->row_buffers, total_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *), set->persistent); @@ -1511,11 +1525,12 @@ mysqlnd_background_store_result_fetch_data(MYSQLND_RES *result TSRMLS_DC) transfered above. */ } -// 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); - +#if 0 + 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); +#endif tsrm_mutex_lock(set->LOCK); /* Finally clean */ if (row_packet->eof) { @@ -1551,7 +1566,7 @@ mysqlnd_background_store_result_fetch_data(MYSQLND_RES *result TSRMLS_DC) } else { CONN_SET_STATE(conn, CONN_READY); } - DBG_INF_FMT("ret=%s row_count=%u warns=%u status=%u", ret == PASS? "PASS":"FAIL", + DBG_INF_FMT("ret=%s row_count=%u warnings=%u server_status=%u", ret == PASS? "PASS":"FAIL", set->row_count, conn->upsert_status.warning_count, conn->upsert_status.server_status); DBG_RETURN(ret); } @@ -1560,7 +1575,7 @@ mysqlnd_background_store_result_fetch_data(MYSQLND_RES *result TSRMLS_DC) /* {{{ mysqlnd_res::background_store_result */ -MYSQLND_RES * +static MYSQLND_RES * MYSQLND_METHOD(mysqlnd_res, background_store_result)(MYSQLND_RES * result, MYSQLND * const conn, zend_bool ps TSRMLS_DC) { #ifndef MYSQLND_THREADED @@ -1573,7 +1588,7 @@ MYSQLND_METHOD(mysqlnd_res, background_store_result)(MYSQLND_RES * result, MYSQL DBG_INF_FMT("conn=%d ps_protocol=%d", conn->thread_id, ps); /* We need the conn because we are doing lazy zval initialization in buffered_fetch_row */ - result->conn = conn->m->get_reference(conn); + result->conn = conn->m->get_reference(conn TSRMLS_CC); result->type = MYSQLND_RES_NORMAL; result->m.fetch_row = mysqlnd_fetch_row_async_buffered; result->m.fetch_lengths = mysqlnd_fetch_lengths_async_buffered; diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h index d9cc7351f1..2f6dcfa2e1 100644 --- a/ext/mysqlnd/mysqlnd_structs.h +++ b/ext/mysqlnd/mysqlnd_structs.h @@ -241,7 +241,7 @@ struct st_mysqlnd_conn_methods MYSQLND_RES * (*store_result)(MYSQLND * const conn TSRMLS_DC); MYSQLND_RES * (*background_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); + zend_bool (*more_results)(const MYSQLND * const conn TSRMLS_DC); MYSQLND_STMT * (*stmt_init)(MYSQLND * const conn TSRMLS_DC); @@ -282,7 +282,7 @@ struct st_mysqlnd_conn_methods 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); + MYSQLND * (*get_reference)(MYSQLND * const conn TSRMLS_DC); enum_func_status (*free_reference)(MYSQLND * const conn TSRMLS_DC); enum mysqlnd_connection_state (*get_state)(MYSQLND * const conn TSRMLS_DC); void (*set_state)(MYSQLND * const conn, enum mysqlnd_connection_state new_state TSRMLS_DC); @@ -344,10 +344,12 @@ struct st_mysqlnd_stmt_methods MYSQLND_RES * (*store_result)(MYSQLND_STMT * const stmt TSRMLS_DC); MYSQLND_RES * (*background_store_result)(MYSQLND_STMT * const stmt TSRMLS_DC); MYSQLND_RES * (*get_result)(MYSQLND_STMT * const stmt TSRMLS_DC); + zend_bool (*more_results)(const MYSQLND_STMT * const stmt TSRMLS_DC); + enum_func_status (*next_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, uint64 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 (*net_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); @@ -357,6 +359,7 @@ struct st_mysqlnd_stmt_methods enum_func_status (*refresh_bind_param)(MYSQLND_STMT * const stmt TSRMLS_DC); void (*set_param_bind_dtor)(MYSQLND_STMT * const stmt, void (*param_bind_dtor)(MYSQLND_PARAM_BIND *) TSRMLS_DC); enum_func_status (*bind_result)(MYSQLND_STMT * const stmt, MYSQLND_RESULT_BIND * const result_bind TSRMLS_DC); + enum_func_status (*bind_one_result)(MYSQLND_STMT * const stmt, uint param_no TSRMLS_DC); void (*set_result_bind_dtor)(MYSQLND_STMT * const stmt, void (*result_bind_dtor)(MYSQLND_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); @@ -375,7 +378,7 @@ struct st_mysqlnd_stmt_methods 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 (*get_attribute)(const 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); }; @@ -587,7 +590,6 @@ struct st_mysqlnd_param_bind struct st_mysqlnd_result_bind { zval *zv; - zend_uchar original_type; zend_bool bound; }; @@ -617,7 +619,7 @@ struct st_mysqlnd_stmt zend_bool cursor_exists; mysqlnd_stmt_use_or_store_func default_rset_handler; - MYSQLND_CMD_BUFFER cmd_buffer; + MYSQLND_CMD_BUFFER execute_cmd_buffer; unsigned int execute_count;/* count how many times the stmt was executed */ void (*param_bind_dtor)(MYSQLND_PARAM_BIND *); diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c index f4eaeefd22..4816a4fb37 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.c +++ b/ext/mysqlnd/mysqlnd_wireprotocol.c @@ -587,6 +587,13 @@ php_mysqlnd_greet_read(void *_packet, MYSQLND *conn TSRMLS_DC) } else { packet->pre41 = TRUE; } + + DBG_INF_FMT("proto=%d server=%s thread_id=%ld", + packet->protocol_version, packet->server_version, packet->thread_id); + + DBG_INF_FMT("server_capabilities=%d charset_no=%d server_status=%d", + packet->server_capabilities, packet->charset_no, packet->server_status); + if (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", @@ -783,8 +790,8 @@ php_mysqlnd_ok_read(void *_packet, MYSQLND *conn TSRMLS_DC) } 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); + packet->affected_rows, packet->last_insert_id, packet->server_status, + packet->warning_count); if (p - begin > packet->header.size) { DBG_ERR_FMT("OK packet %d bytes shorter than expected", p - begin - packet->header.size); @@ -865,7 +872,8 @@ php_mysqlnd_eof_read(void *_packet, MYSQLND *conn TSRMLS_DC) p - begin - packet->header.size, getpid()); } - DBG_INF_FMT("EOF packet: status=%d warnings=%d", packet->server_status, packet->warning_count); + DBG_INF_FMT("EOF packet: fields=%d status=%d warnings=%d", + packet->field_count, packet->server_status, packet->warning_count); DBG_RETURN(PASS); } @@ -986,6 +994,7 @@ php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC) packet->field_count= php_mysqlnd_net_field_length(&p); switch (packet->field_count) { case MYSQLND_NULL_LENGTH: + DBG_INF("LOAD LOCAL"); /* First byte in the packet is the field count. Thus, the name is size - 1. And we add 1 for a trailing \0. @@ -997,6 +1006,7 @@ php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC) packet->info_or_local_file_len = len; break; case 0x00: + DBG_INF("UPSERT"); packet->affected_rows = php_mysqlnd_net_field_length_ll(&p); packet->last_insert_id= php_mysqlnd_net_field_length_ll(&p); packet->server_status = uint2korr(p); @@ -1010,13 +1020,17 @@ php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC) packet->info_or_local_file[len] = '\0'; packet->info_or_local_file_len = len; } + DBG_INF_FMT("affected_rows=%llu last_insert_id=%llu server_status=%d warning_count=%d", + packet->affected_rows, packet->last_insert_id, + packet->server_status, packet->warning_count); break; default: + DBG_INF("SELECT"); /* Result set */ break; } if (p - begin > packet->header.size) { - DBG_ERR_FMT("GREET packet %d bytes shorter than expected", p - begin - packet->header.size); + DBG_ERR_FMT("RSET_HEADER 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()); } @@ -1097,7 +1111,7 @@ php_mysqlnd_rset_field_read(void *_packet, MYSQLND *conn TSRMLS_DC) *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = 0; break; case MYSQLND_NULL_LENGTH: - goto faulty_fake; + goto faulty_or_fake; default: *(char **)(((char *)meta) + rset_field_offsets[i]) = (char *)p; *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = len; @@ -1156,7 +1170,7 @@ 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); + DBG_ERR_FMT("RSET 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. PID=%d", p - begin - packet->header.size, getpid()); } @@ -1210,7 +1224,7 @@ php_mysqlnd_rset_field_read(void *_packet, MYSQLND *conn TSRMLS_DC) */ DBG_RETURN(PASS); -faulty_fake: +faulty_or_fake: 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"); @@ -1629,6 +1643,8 @@ php_mysqlnd_rowp_read(void *_packet, MYSQLND *conn TSRMLS_DC) p += 2; packet->server_status = uint2korr(p); /* Seems we have 3 bytes reserved for future use */ + DBG_INF_FMT("server_status=%d warning_count=%d", + packet->server_status, packet->warning_count); } } else { MYSQLND_INC_CONN_STATISTIC(&conn->stats,