false;
$IS_MYSQLND = stristr(mysql_get_client_info(), "mysqlnd");
-?>
\ No newline at end of file
+?>
// 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);
}
/* }}} */
+
+#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)
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)
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)
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);
--TEST--
Interface of the class mysqli
--SKIPIF--
-<?php
+<?php
require_once('skipif.inc');
require_once('skipifemb.inc');
require_once('skipifconnectfailure.inc');
'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])) {
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);
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);
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);
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);
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',
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(
'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',
);
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)) {
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);
}
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!";
/* {{{ 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);
}
/* }}} */
{
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 :)
/* {{{ 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);
}
/* }}} */
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);
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();
#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)
#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)
#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)
#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);
#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
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
/* }}} */
-/* {{{ 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);
}
/* }}} */
{
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);
++cache->free_items;
#ifdef ZTS
memset(&((*p)->thread_id), 0, sizeof(THREAD_T));
-#endif
+#endif
p++;
}
UNLOCK_PCACHE(cache);
-#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 */
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 *
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 */
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 */
/* }}} */
+/* {{{ 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)
/* 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;
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));
/* }}} */
+/* {{{ 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)
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)
}
}
}
+#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
}
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));
}
/* }}} */
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) {
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);
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,
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);
DBG_RETURN(FAIL);
}
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(stmt->conn->error_info);
+
if (stmt->field_count) {
uint i = 0;
}
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 */
/* }}} */
+/* {{{ 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,
/* {{{ 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)
{
}
/* }}} */
-
+/* 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)
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 */
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);
/* }}} */
+/* {{{ 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",
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 */];
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;
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);
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),
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),
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;
/*
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);
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;
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;
}
}
/* }}} */
+
/* {{{ mysqlnd_free_buffered_data */
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)
/* }}} */
#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");
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;
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;
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;
}
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);
}
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;
}
/* }}} */
+
#ifdef MYSQLND_THREADED
/* {{{ mysqlnd_fetch_row_async_buffered */
static enum_func_status
}
/* }}} */
+
/* {{{ mysqlnd_background_store_result_fetch_data */
enum_func_status
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);
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) {
} 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);
}
/* {{{ 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
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;
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);
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);
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);
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);
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);
};
struct st_mysqlnd_result_bind
{
zval *zv;
- zend_uchar original_type;
zend_bool bound;
};
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 *);
} 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",
}
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);
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);
}
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.
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);
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());
}
*(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;
}
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());
}
*/
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");
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,