]> granicus.if.org Git - php/commitdiff
Update ext/mysql's and ext/mysqli's tests
authorAndrey Hristov <andrey@php.net>
Thu, 24 Apr 2008 14:04:58 +0000 (14:04 +0000)
committerAndrey Hristov <andrey@php.net>
Thu, 24 Apr 2008 14:04:58 +0000 (14:04 +0000)
Add mysqli_stmt_more_result()/mysqli_stmt_next_result(), but only in
mysqlnd builds as libmysql doesn't support this feature.

23 files changed:
ext/mysql/tests/connect.inc
ext/mysql/tests/mysql_fetch_array.phpt
ext/mysqli/mysqli_api.c
ext/mysqli/mysqli_fe.c
ext/mysqli/php_mysqli_structs.h
ext/mysqli/tests/mysqli_class_mysqli_interface.phpt
ext/mysqli/tests/mysqli_class_mysqli_stmt_interface.phpt
ext/mysqli/tests/mysqli_fetch_all.phpt
ext/mysqli/tests/mysqli_fetch_all_oo.phpt
ext/mysqli/tests/mysqli_fetch_array.phpt
ext/mysqli/tests/mysqli_fetch_array_oo.phpt
ext/mysqli/tests/mysqli_fetch_field_flags.phpt
ext/mysqli/tests/mysqli_stmt_execute.phpt
ext/mysqlnd/mysqlnd.c
ext/mysqlnd/mysqlnd.h
ext/mysqlnd/mysqlnd_enum_n_def.h
ext/mysqlnd/mysqlnd_palloc.c
ext/mysqlnd/mysqlnd_priv.h
ext/mysqlnd/mysqlnd_ps.c
ext/mysqlnd/mysqlnd_ps_codec.c
ext/mysqlnd/mysqlnd_result.c
ext/mysqlnd/mysqlnd_structs.h
ext/mysqlnd/mysqlnd_wireprotocol.c

index 70017b9e2695abf71682982efe096f7cadac9b06..86bf7a9acdfa87fac1ed3ad64a335517622d54f1 100755 (executable)
@@ -62,4 +62,4 @@ $TEST_EXPERIMENTAL = (in_array(getenv("MYSQL_TEST_EXPERIMENTAL"), array(0, 1)))
        false;
 
 $IS_MYSQLND = stristr(mysql_get_client_info(), "mysqlnd");
-?>
\ No newline at end of file
+?>
index f197ae949473f731d1df8fd59e1d1984f25cc63b..c3a70ea55c611b0300410d92203f6874c1ca60f8 100644 (file)
@@ -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);
index 51381e1bb11f921509d02c8af6ae7a8d89777543..8c8c3c32b5f477e9c0df8a10df295a4b4b92068f 100644 (file)
@@ -1529,6 +1529,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) U
    Get number of fields in result */
 PHP_FUNCTION(mysqli_num_fields)
index 7aec65b1e1392811868eac4a7211578faf0a4fb7..14df05862fd49b48074eb72e641df64005f82dad 100644 (file)
@@ -157,6 +157,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)
@@ -281,6 +285,10 @@ const zend_function_entry mysqli_stmt_methods[] = {
        PHP_FALIAS(fetch,mysqli_stmt_fetch,NULL)
        PHP_FALIAS(get_warnings, mysqli_stmt_get_warnings,      NULL)
        PHP_FALIAS(result_metadata, mysqli_stmt_result_metadata,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(num_rows, mysqli_stmt_num_rows,NULL)
        PHP_FALIAS(send_long_data,mysqli_stmt_send_long_data,NULL)
        PHP_FALIAS(stmt,mysqli_prepare,NULL)
index 2c527a22f00531472282b56a1f0ab79dfe18ca6e..a1be651df2e2fcff742ea95299c8d25919796ae0 100644 (file)
@@ -477,6 +477,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);
index c1dc6a590a8ce806ace3f1c2f3fba25a0c0f3198..84f39fa8a44136055dc21a5f8b708ef96f22577b 100644 (file)
@@ -1,7 +1,7 @@
 --TEST--
 Interface of the class mysqli
 --SKIPIF--
-<?php 
+<?php
 require_once('skipif.inc');
 require_once('skipifemb.inc');
 require_once('skipifconnectfailure.inc');
index cc604aaef0d002ea5b778a05cd7118bdce870bf9..dce75af3581a40f4f402883c8dbd6f7f1aaf741b 100644 (file)
@@ -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])) {
index f93d204a67ffa8a372e41d38ad0fe2d8d6489740..7559b05034474ccc4e7b6ca947e4a0ad6b0a7e7f 100644 (file)
@@ -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);
index fcbc52e7823e66a3fb92b2dd79181aca8ad0ee52..b0901e01a9ed22dd8ee1230148b421b7fd76d66f 100644 (file)
@@ -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);
index 798d75096a6e48ffaf3f1424cb29645245979c0d..1b46d0d3566ca2bae971d217cc9b57e5d58e5565 100644 (file)
@@ -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);
index a13b131e5f0dc6e7f36073b8647b51757a86a4a6..816ea9fe193fd2c617e4ba05f5474cc4410d615b 100644 (file)
@@ -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);
index ebaab10cffff7d833b15ce85b7a77fb05ee757e9..9c74408c20e067740142de91896c5efa999dd86b 100644 (file)
@@ -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--
 <?php
        include "connect.inc";
 
+/* TODO: mysqli.c needs to export a few more constants - see all the defined() calls! */
+
        $flags = array(
                MYSQLI_NOT_NULL_FLAG => '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);
        }
index 3106e8e38f318733a14a24e074185dbe5b7113c3..6f3df35d6b666f2eb9940d32e374fc150feb58e2 100644 (file)
@@ -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!";
index d7d6b95d68cbdc7721d76cabe7678282427a6b3c..988b5e07458572c4c179fb76cd05be9e6c75e061 100644 (file)
@@ -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();
index 86c11cfe53210aeb0ccd4b2c2ad351e04451947c..d41a66fca8a7f9bacb29aea34a40b3c2f098ca2e 100644 (file)
@@ -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);
 
 
index 940a6f7872391b94b52eadfb79cb09567578fd43..9c97cf779635cd95c58aafcf6a308b54ae2baa72 100644 (file)
 #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
index 976e47373358a8234727a66ad8d3eac04f562bcd..33ee402ccaeb84f08317ebc6c398c555c30032b3 100644 (file)
@@ -132,7 +132,6 @@ PHPAPI MYSQLND_THD_ZVAL_PCACHE* _mysqlnd_palloc_init_thd_cache(MYSQLND_ZVAL_PCAC
        MYSQLND_THD_ZVAL_PCACHE *ret = calloc(1, sizeof(MYSQLND_THD_ZVAL_PCACHE));
        DBG_ENTER("_mysqlnd_palloc_init_thd_cache");
        DBG_INF_FMT("ret = %p", ret);
-
        ret->parent = mysqlnd_palloc_get_cache_reference(cache);
 
 #ifdef ZTS
@@ -151,14 +150,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 +191,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 +494,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);
index 7ffbc67eaf44a5b31509d82fc1958d733a3a701a..229dd903b731a451a7490587445df0313e7f8efa 100644 (file)
 
 
 
-#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 */
index 59e9a96f631ac5dd2c5ec82a9b4f68a8249c264b..acacee22ef768d071d645ce4ccb01470d1edae2a 100644 (file)
@@ -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); 
index c832c6b4637e460b344340e8727abcbf4125a5cf..0e2c4345dbf24f5a641bcd802038d048a7cad24f 100644 (file)
@@ -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;
 }
index fac01c1047b71605eeb84a1af286bef457ab9f79..b30f90409f844e1e22a41170f2d375f435562230 100644 (file)
@@ -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;
index d9cc7351f15a183cef76eb82ab93a41e085ef4d7..2f6dcfa2e14be644337e4700b2855b3c345f2592 100644 (file)
@@ -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 *);
index f4eaeefd22d08746452c10b2a76e031952da1b8a..4816a4fb37f591cd2d0c334d3f591437f27fa7de 100644 (file)
@@ -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,