summaryrefslogtreecommitdiff
path: root/ext/mysqlnd
diff options
context:
space:
mode:
authorAndrey Hristov <andrey@php.net>2008-04-24 14:22:19 +0000
committerAndrey Hristov <andrey@php.net>2008-04-24 14:22:19 +0000
commitf4e659d2af2ba68eb6d9092560eb0247a33fb827 (patch)
tree0dd088ae74cb248da04cb842b65f00a356f50dbf /ext/mysqlnd
parent2667f76d7b7f3fce7bc5a8374632dce5e34cb0b6 (diff)
downloadphp-git-f4e659d2af2ba68eb6d9092560eb0247a33fb827.tar.gz
Update ext/mysql's and ext/mysqli's tests
Add mysqli_stmt_more_result()/mysqli_stmt_next_result(), but only in mysqlnd builds as libmysql doesn't support this feature.
Diffstat (limited to 'ext/mysqlnd')
-rw-r--r--ext/mysqlnd/mysqlnd.c17
-rw-r--r--ext/mysqlnd/mysqlnd.h8
-rw-r--r--ext/mysqlnd/mysqlnd_enum_n_def.h18
-rw-r--r--ext/mysqlnd/mysqlnd_palloc.c12
-rw-r--r--ext/mysqlnd/mysqlnd_priv.h17
-rw-r--r--ext/mysqlnd/mysqlnd_ps.c411
-rw-r--r--ext/mysqlnd/mysqlnd_ps_codec.c8
-rw-r--r--ext/mysqlnd/mysqlnd_result.c45
-rw-r--r--ext/mysqlnd/mysqlnd_structs.h14
-rw-r--r--ext/mysqlnd/mysqlnd_wireprotocol.c30
10 files changed, 409 insertions, 171 deletions
diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c
index d7d6b95d68..988b5e0745 100644
--- a/ext/mysqlnd/mysqlnd.c
+++ b/ext/mysqlnd/mysqlnd.c
@@ -1274,10 +1274,12 @@ MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn, enum_connection_close_type c
/* {{{ mysqlnd_conn::get_reference */
static MYSQLND *
-MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_reference)(MYSQLND * const conn)
+MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_reference)(MYSQLND * const conn TSRMLS_DC)
{
+ DBG_ENTER("mysqlnd_conn::get_reference");
++conn->refcount;
- return conn;
+ DBG_INF_FMT("conn=%llu new_refcount=%u", conn->thread_id, conn->refcount);
+ DBG_RETURN(conn);
}
/* }}} */
@@ -1288,7 +1290,7 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_conn, free_reference)(MYSQLND * const conn TSRMLS
{
enum_func_status ret = PASS;
DBG_ENTER("mysqlnd_conn::free_reference");
- DBG_INF_FMT("conn=%llu conn->refcount=%u", conn->thread_id, conn->refcount);
+ DBG_INF_FMT("conn=%llu old_refcount=%u", conn->thread_id, conn->refcount);
if (!(--conn->refcount)) {
/*
No multithreading issues as we don't share the connection :)
@@ -1474,10 +1476,11 @@ MYSQLND_METHOD(mysqlnd_conn, get_server_version)(const MYSQLND * const conn)
/* {{{ mysqlnd_conn::more_results */
static zend_bool
-MYSQLND_METHOD(mysqlnd_conn,more_results)(const MYSQLND * const conn)
+MYSQLND_METHOD(mysqlnd_conn, more_results)(const MYSQLND * const conn TSRMLS_DC)
{
+ DBG_ENTER("mysqlnd_conn::more_results");
/* (conn->state == CONN_NEXT_RESULT_PENDING) too */
- return conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS? TRUE:FALSE;
+ DBG_RETURN(conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS? TRUE:FALSE);
}
/* }}} */
@@ -1792,7 +1795,7 @@ MYSQLND_METHOD(mysqlnd_conn, use_result)(MYSQLND * const conn TSRMLS_DC)
result = conn->current_result;
conn->current_result = NULL;
- result->conn = conn->m->get_reference(conn);
+ result->conn = conn->m->get_reference(conn TSRMLS_CC);
result = result->m.use_result(result, FALSE TSRMLS_CC);
DBG_RETURN(result);
@@ -1956,7 +1959,7 @@ PHPAPI MYSQLND *_mysqlnd_init(zend_bool persistent TSRMLS_DC)
ret->persistent = persistent;
ret->m = & mysqlnd_mysqlnd_conn_methods;
- ret->m->get_reference(ret);
+ ret->m->get_reference(ret TSRMLS_CC);
#ifdef MYSQLND_THREADED
ret->LOCK_state = tsrm_mutex_alloc();
diff --git a/ext/mysqlnd/mysqlnd.h b/ext/mysqlnd/mysqlnd.h
index 86c11cfe53..d41a66fca8 100644
--- a/ext/mysqlnd/mysqlnd.h
+++ b/ext/mysqlnd/mysqlnd.h
@@ -112,7 +112,7 @@ PHPAPI void _mysqlnd_debug(const char *mode TSRMLS_DC);
#define mysqlnd_store_result(conn) (conn)->m->store_result((conn) TSRMLS_CC)
#define mysqlnd_bg_store_result(conn) (conn)->m->background_store_result((conn) TSRMLS_CC)
#define mysqlnd_next_result(conn) (conn)->m->next_result((conn) TSRMLS_CC)
-#define mysqlnd_more_results(conn) (conn)->m->more_results((conn))
+#define mysqlnd_more_results(conn) (conn)->m->more_results((conn) TSRMLS_CC)
#define mysqlnd_free_result(r,e_or_i) ((MYSQLND_RES*)r)->m.free_result(((MYSQLND_RES*)(r)), (e_or_i) TSRMLS_CC)
#define mysqlnd_data_seek(result, row) (result)->m.seek_data((result), (row) TSRMLS_CC)
@@ -262,6 +262,8 @@ PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, size
#define mysqlnd_stmt_store_result(stmt) (!mysqlnd_stmt_field_count((stmt)) ? PASS:((stmt)->m->store_result((stmt) TSRMLS_CC)? PASS:FAIL))
#define mysqlnd_stmt_bg_store_result(stmt) (!mysqlnd_stmt_field_count((stmt)) ? PASS:((stmt)->m->background_store_result((stmt) TSRMLS_CC)? PASS:FAIL))
#define mysqlnd_stmt_get_result(stmt) (stmt)->m->get_result((stmt) TSRMLS_CC)
+#define mysqlnd_stmt_more_results(stmt) (stmt)->m->more_results((stmt) TSRMLS_CC)
+#define mysqlnd_stmt_next_result(stmt) (stmt)->m->next_result((stmt) TSRMLS_CC)
#define mysqlnd_stmt_data_seek(stmt, row) (stmt)->m->seek_data((stmt), (row) TSRMLS_CC)
#define mysqlnd_stmt_prepare(stmt, q, qlen) (stmt)->m->prepare((stmt), (q), (qlen) TSRMLS_CC)
#define mysqlnd_stmt_execute(stmt) (stmt)->m->execute((stmt) TSRMLS_CC)
@@ -271,6 +273,7 @@ PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, size
#define mysqlnd_stmt_refresh_bind_param(s) (s)->m->refresh_bind_param((s) TSRMLS_CC)
#define mysqlnd_stmt_set_param_bind_dtor(s,d) (s)->m->set_param_bind_dtor((s), (d) TSRMLS_CC)
#define mysqlnd_stmt_bind_result(stmt,bind) (stmt)->m->bind_result((stmt), (bind) TSRMLS_CC)
+#define mysqlnd_stmt_bind_one_result(s,no) (s)->m->bind_one_result((s), (no) TSRMLS_CC)
#define mysqlnd_stmt_set_result_bind_dtor(s,d) (s)->m->set_result_bind_dtor((s), (d) TSRMLS_CC)
#define mysqlnd_stmt_param_metadata(stmt) (stmt)->m->get_parameter_metadata((stmt))
#define mysqlnd_stmt_result_metadata(stmt) (stmt)->m->get_result_metadata((stmt) TSRMLS_CC)
@@ -305,9 +308,10 @@ PHPAPI void _mysqlnd_palloc_rshutdown(MYSQLND_THD_ZVAL_PCACHE * cache TSRM
#define mysqlnd_palloc_init_thd_cache(cache) _mysqlnd_palloc_init_thd_cache((cache) TSRMLS_CC)
#define mysqlnd_palloc_free_thd_cache_reference(cache) _mysqlnd_palloc_free_thd_cache_reference((cache) TSRMLS_CC)
+#define mysqlnd_palloc_get_thd_cache_reference(cache) _mysqlnd_palloc_get_thd_cache_reference((cache) TSRMLS_CC)
PHPAPI MYSQLND_THD_ZVAL_PCACHE* _mysqlnd_palloc_init_thd_cache(MYSQLND_ZVAL_PCACHE * const cache TSRMLS_DC);
-MYSQLND_THD_ZVAL_PCACHE* mysqlnd_palloc_get_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE * const cache);
+MYSQLND_THD_ZVAL_PCACHE* _mysqlnd_palloc_get_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE * const cache TSRMLS_DC);
PHPAPI void _mysqlnd_palloc_free_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE **cache TSRMLS_DC);
diff --git a/ext/mysqlnd/mysqlnd_enum_n_def.h b/ext/mysqlnd/mysqlnd_enum_n_def.h
index 940a6f7872..9c97cf7796 100644
--- a/ext/mysqlnd/mysqlnd_enum_n_def.h
+++ b/ext/mysqlnd/mysqlnd_enum_n_def.h
@@ -28,8 +28,26 @@
#define MYSQLND_SQLSTATE_LENGTH 5
#define MYSQLND_SQLSTATE_NULL "00000"
+#define SERVER_STATUS_IN_TRANS 1 /* Transaction has started */
+#define SERVER_STATUS_AUTOCOMMIT 2 /* Server in auto_commit mode */
+#define SERVER_MORE_RESULTS_EXISTS 8 /* Multi query - next query exists */
#define MYSQLND_SERVER_QUERY_NO_GOOD_INDEX_USED 16
#define MYSQLND_SERVER_QUERY_NO_INDEX_USED 32
+/*
+ The server was able to fulfill the clients request and opened a
+ read-only non-scrollable cursor for a query. This flag comes
+ in reply to COM_STMT_EXECUTE and COM_STMT_FETCH commands.
+*/
+#define SERVER_STATUS_CURSOR_EXISTS 64
+/*
+ This flag is sent when a read-only cursor is exhausted, in reply to
+ COM_STMT_FETCH command.
+*/
+#define SERVER_STATUS_LAST_ROW_SENT 128
+#define SERVER_STATUS_DB_DROPPED 256 /* A database was dropped */
+#define SERVER_STATUS_NO_BACKSLASH_ESCAPES 512
+#define SERVER_QUERY_WAS_SLOW 1024
+
#define MYSQLND_NO_DATA 100
#define MYSQLND_DATA_TRUNCATED 101
diff --git a/ext/mysqlnd/mysqlnd_palloc.c b/ext/mysqlnd/mysqlnd_palloc.c
index 976e473733..f64500b5bf 100644
--- a/ext/mysqlnd/mysqlnd_palloc.c
+++ b/ext/mysqlnd/mysqlnd_palloc.c
@@ -151,14 +151,16 @@ PHPAPI MYSQLND_THD_ZVAL_PCACHE* _mysqlnd_palloc_init_thd_cache(MYSQLND_ZVAL_PCAC
/* }}} */
-/* {{{ mysqlnd_palloc_get_thd_cache_reference */
-MYSQLND_THD_ZVAL_PCACHE* mysqlnd_palloc_get_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE * const cache)
+/* {{{ _mysqlnd_palloc_get_thd_cache_reference */
+MYSQLND_THD_ZVAL_PCACHE* _mysqlnd_palloc_get_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE * const cache TSRMLS_DC)
{
+ DBG_ENTER("_mysqlnd_palloc_get_thd_cache_reference");
if (cache) {
++cache->references;
+ DBG_INF_FMT("cache=%p new_refc=%d", *cache, cache->references);
mysqlnd_palloc_get_cache_reference(cache->parent);
}
- return cache;
+ DBG_RETURN(cache);
}
/* }}} */
@@ -190,8 +192,8 @@ PHPAPI void _mysqlnd_palloc_free_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE **c
{
DBG_ENTER("_mysqlnd_palloc_free_thd_cache_reference");
if (*cache) {
- DBG_INF_FMT("cache=%p refs=%d", *cache, (*cache)->references);
--(*cache)->parent->references;
+ DBG_INF_FMT("cache=%p references_left=%d", *cache, (*cache)->references);
if (--(*cache)->references == 0) {
mysqlnd_palloc_free_thd_cache(*cache TSRMLS_CC);
@@ -493,7 +495,7 @@ PHPAPI void _mysqlnd_palloc_rshutdown(MYSQLND_THD_ZVAL_PCACHE * thd_cache TSRMLS
++cache->free_items;
#ifdef ZTS
memset(&((*p)->thread_id), 0, sizeof(THREAD_T));
-#endif
+#endif
p++;
}
UNLOCK_PCACHE(cache);
diff --git a/ext/mysqlnd/mysqlnd_priv.h b/ext/mysqlnd/mysqlnd_priv.h
index 7ffbc67eaf..229dd903b7 100644
--- a/ext/mysqlnd/mysqlnd_priv.h
+++ b/ext/mysqlnd/mysqlnd_priv.h
@@ -74,23 +74,6 @@
-#define SERVER_STATUS_IN_TRANS 1 /* Transaction has started */
-#define SERVER_STATUS_AUTOCOMMIT 2 /* Server in auto_commit mode */
-#define SERVER_MORE_RESULTS_EXISTS 8 /* Multi query - next query exists */
-/*
- The server was able to fulfill the clients request and opened a
- read-only non-scrollable cursor for a query. This flag comes
- in reply to COM_STMT_EXECUTE and COM_STMT_FETCH commands.
-*/
-#define SERVER_STATUS_CURSOR_EXISTS 64
-/*
- This flag is sent when a read-only cursor is exhausted, in reply to
- COM_STMT_FETCH command.
-*/
-#define SERVER_STATUS_LAST_ROW_SENT 128
-#define SERVER_STATUS_DB_DROPPED 256 /* A database was dropped */
-#define SERVER_STATUS_NO_BACKSLASH_ESCAPES 512
-#define SERVER_QUERY_WAS_SLOW 1024
/* Client Error codes */
diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c
index 59e9a96f63..acacee22ef 100644
--- a/ext/mysqlnd/mysqlnd_ps.c
+++ b/ext/mysqlnd/mysqlnd_ps.c
@@ -57,7 +57,10 @@ enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param,
zend_bool *fetched_anything TSRMLS_DC);
static void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC);
+static void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, uint param_no TSRMLS_DC);
+static void mysqlnd_internal_free_stmt_content(MYSQLND_STMT * const stmt TSRMLS_DC);
+static enum_func_status mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const stmt TSRMLS_DC);
/* {{{ mysqlnd_stmt::store_result */
static MYSQLND_RES *
@@ -100,7 +103,9 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC)
result->type = MYSQLND_RES_PS_BUF;
result->m.fetch_row = mysqlnd_fetch_stmt_row_buffered;
result->m.fetch_lengths = NULL;/* makes no sense */
- result->zval_cache = mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache);
+ if (!result->zval_cache) {
+ result->zval_cache = mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache);
+ }
/* Create room for 'next_extend' rows */
@@ -167,7 +172,9 @@ MYSQLND_METHOD(mysqlnd_stmt, background_store_result)(MYSQLND_STMT * const stmt
result->type = MYSQLND_RES_PS_BUF;
result->m.fetch_row = mysqlnd_fetch_stmt_row_buffered;
result->m.fetch_lengths = NULL;/* makes no sense */
- result->zval_cache = mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache);
+ if (!result->zval_cache) {
+ result->zval_cache = mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache);
+ }
/* Create room for 'next_extend' rows */
@@ -248,6 +255,45 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const stmt TSRMLS_DC)
/* }}} */
+/* {{{ mysqlnd_stmt::more_results */
+static zend_bool
+MYSQLND_METHOD(mysqlnd_stmt, more_results)(const MYSQLND_STMT * stmt TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_stmt::more_results");
+ /* (conn->state == CONN_NEXT_RESULT_PENDING) too */
+ DBG_RETURN((stmt->conn && (stmt->conn->upsert_status.server_status &
+ SERVER_MORE_RESULTS_EXISTS))?
+ TRUE:
+ FALSE);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::next_result */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, next_result)(MYSQLND_STMT * stmt TSRMLS_DC)
+{
+ MYSQLND *conn = stmt->conn;
+
+ DBG_ENTER("mysqlnd_stmt::next_result");
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ if (!conn ||
+ CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING ||
+ !(conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) ||
+ !stmt->result)
+ {
+ DBG_RETURN(FAIL);
+ }
+
+ /* Free space for next result */
+ mysqlnd_internal_free_stmt_content(stmt TSRMLS_CC);
+
+ DBG_RETURN(mysqlnd_stmt_execute_parse_response(stmt TSRMLS_CC));
+}
+/* }}} */
+
+
/* {{{ mysqlnd_stmt_skip_metadata */
static enum_func_status
mysqlnd_stmt_skip_metadata(MYSQLND_STMT *stmt TSRMLS_DC)
@@ -399,7 +445,7 @@ MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const stmt, const char * co
/* Allocate the result now as it is needed for the reading of metadata */
stmt_to_prepare->result = result;
- result->conn = stmt_to_prepare->conn->m->get_reference(stmt_to_prepare->conn);
+ result->conn = stmt_to_prepare->conn->m->get_reference(stmt_to_prepare->conn TSRMLS_CC);
result->type = MYSQLND_RES_PS_BUF;
@@ -411,7 +457,7 @@ MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const stmt, const char * co
if (stmt_to_prepare != stmt) {
/* Free old buffers, binding and resources on server */
- stmt->m->close(stmt, TRUE TSRMLS_CC);
+ stmt->m->net_close(stmt, TRUE TSRMLS_CC);
memcpy(stmt, stmt_to_prepare, sizeof(MYSQLND_STMT));
@@ -434,6 +480,92 @@ fail:
/* }}} */
+/* {{{ mysqlnd_stmt_execute_parse_response */
+static enum_func_status
+mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const stmt TSRMLS_DC)
+{
+ enum_func_status ret;
+ MYSQLND *conn = stmt->conn;
+
+ DBG_ENTER("mysqlnd_stmt_execute_parse_response");
+
+ CONN_SET_STATE(conn, CONN_QUERY_SENT);
+
+ ret = mysqlnd_query_read_result_set_header(stmt->conn, stmt TSRMLS_CC);
+ if (ret == FAIL) {
+ stmt->error_info = conn->error_info;
+ stmt->upsert_status.affected_rows = conn->upsert_status.affected_rows;
+ if (CONN_GET_STATE(conn) == CONN_QUIT_SENT) {
+ /* close the statement here, the connection has been closed */
+ }
+ stmt->state = MYSQLND_STMT_PREPARED;
+ } else {
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(stmt->conn->error_info);
+ stmt->send_types_to_server = 0;
+ stmt->upsert_status = conn->upsert_status;
+ stmt->state = MYSQLND_STMT_EXECUTED;
+ if (conn->last_query_type == QUERY_UPSERT || conn->last_query_type == QUERY_LOAD_LOCAL) {
+ DBG_INF("PASS");
+ DBG_RETURN(PASS);
+ }
+
+ stmt->result->type = MYSQLND_RES_PS_BUF;
+ if (!stmt->result->conn) {
+ /*
+ For SHOW we don't create (bypasses PS in server)
+ a result set at prepare and thus a connection was missing
+ */
+ stmt->result->conn = stmt->conn->m->get_reference(stmt->conn TSRMLS_CC);
+ }
+
+ /* Update stmt->field_count as SHOW sets it to 0 at prepare */
+ stmt->field_count = stmt->result->field_count = conn->field_count;
+ stmt->result->lengths = NULL;
+ if (stmt->field_count) {
+ stmt->state = MYSQLND_STMT_WAITING_USE_OR_STORE;
+ /*
+ We need to set this because the user might not call
+ use_result() or store_result() and we should be able to scrap the
+ data on the line, if he just decides to close the statement.
+ */
+ DBG_INF_FMT("server_status=%d cursor=%d", stmt->upsert_status.server_status,
+ stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS);
+
+ if (stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS) {
+ stmt->cursor_exists = TRUE;
+ CONN_SET_STATE(conn, CONN_READY);
+ /* Only cursor read */
+ stmt->default_rset_handler = stmt->m->use_result;
+ DBG_INF("use_result");
+ } else if (stmt->flags & CURSOR_TYPE_READ_ONLY) {
+ /*
+ We have asked for CURSOR but got no cursor, because the condition
+ above is not fulfilled. Then...
+
+ This is a single-row result set, a result set with no rows, EXPLAIN,
+ SHOW VARIABLES, or some other command which either a) bypasses the
+ cursors framework in the server and writes rows directly to the
+ network or b) is more efficient if all (few) result set rows are
+ precached on client and server's resources are freed.
+ */
+ /* preferred is buffered read */
+ stmt->default_rset_handler = stmt->m->store_result;
+ DBG_INF("store_result");
+ } else {
+ /* preferred is unbuffered read */
+ stmt->default_rset_handler = stmt->m->use_result;
+ DBG_INF("use_result");
+ }
+ }
+ }
+
+ DBG_INF(ret == PASS? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
/* {{{ mysqlnd_stmt::execute */
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC)
@@ -451,6 +583,12 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC)
SET_ERROR_AFF_ROWS(stmt->conn);
if (stmt->state > MYSQLND_STMT_PREPARED && stmt->field_count) {
+ /*
+ We don need to copy the data from the buffers which we will clean.
+ Because it has already been copied. See
+ #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
+ */
+#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
if (stmt->result_bind &&
stmt->result_zvals_separated_once == TRUE &&
stmt->state >= MYSQLND_STMT_USER_FETCHING)
@@ -474,6 +612,29 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC)
}
}
}
+#endif
+
+ /*
+ If right after execute() we have to call the appropriate
+ use_result() or store_result() and clean.
+ */
+ if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
+ DBG_INF("fetching result set header");
+ /* Do implicit use_result and then flush the result */
+ stmt->default_rset_handler = stmt->m->use_result;
+ stmt->default_rset_handler(stmt TSRMLS_CC);
+ }
+
+ if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) {
+ DBG_INF("skipping result");
+ /* Flush if anything is left and unbuffered set */
+ stmt->result->m.skip_result(stmt->result TSRMLS_CC);
+ }
+
+ if (stmt->state > MYSQLND_STMT_PREPARED) {
+ /* As the buffers have been freed, we should go back to PREPARED */
+ stmt->state = MYSQLND_STMT_PREPARED;
+ }
/*
Executed, but the user hasn't started to fetch
@@ -534,79 +695,7 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC)
}
stmt->execute_count++;
- CONN_SET_STATE(conn, CONN_QUERY_SENT);
-
- ret = mysqlnd_query_read_result_set_header(stmt->conn, stmt TSRMLS_CC);
- if (ret == FAIL) {
- stmt->error_info = conn->error_info;
- stmt->upsert_status.affected_rows = conn->upsert_status.affected_rows;
- if (CONN_GET_STATE(conn) == CONN_QUIT_SENT) {
- /* close the statement here, the connection has been closed */
- }
- stmt->state = MYSQLND_STMT_PREPARED;
- } else {
- SET_EMPTY_ERROR(stmt->error_info);
- SET_EMPTY_ERROR(stmt->conn->error_info);
- stmt->send_types_to_server = 0;
- stmt->upsert_status = conn->upsert_status;
- stmt->state = MYSQLND_STMT_EXECUTED;
- if (conn->last_query_type == QUERY_UPSERT || conn->last_query_type == QUERY_LOAD_LOCAL) {
- DBG_INF("PASS");
- DBG_RETURN(PASS);
- }
-
- stmt->result->type = MYSQLND_RES_PS_BUF;
- if (!stmt->result->conn) {
- /*
- For SHOW we don't create (bypasses PS in server)
- a result set at prepare and thus a connection was missing
- */
- stmt->result->conn = stmt->conn->m->get_reference(stmt->conn);
- }
-
- /* Update stmt->field_count as SHOW sets it to 0 at prepare */
- stmt->field_count = stmt->result->field_count = conn->field_count;
- stmt->result->lengths = NULL;
- if (stmt->field_count) {
- stmt->state = MYSQLND_STMT_WAITING_USE_OR_STORE;
- /*
- We need to set this because the user might not call
- use_result() or store_result() and we should be able to scrap the
- data on the line, if he just decides to close the statement.
- */
- DBG_INF_FMT("server_status=%d cursor=%d", stmt->upsert_status.server_status,
- stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS);
-
- if (stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS) {
- stmt->cursor_exists = TRUE;
- CONN_SET_STATE(conn, CONN_READY);
- /* Only cursor read */
- stmt->default_rset_handler = stmt->m->use_result;
- DBG_INF("use_result");
- } else if (stmt->flags & CURSOR_TYPE_READ_ONLY) {
- /*
- We have asked for CURSOR but got no cursor, because the condition
- above is not fulfilled. Then...
-
- This is a single-row result set, a result set with no rows, EXPLAIN,
- SHOW VARIABLES, or some other command which either a) bypasses the
- cursors framework in the server and writes rows directly to the
- network or b) is more efficient if all (few) result set rows are
- precached on client and server's resources are freed.
- */
- /* preferred is buffered read */
- stmt->default_rset_handler = stmt->m->store_result;
- DBG_INF("store_result");
- } else {
- /* preferred is unbuffered read */
- stmt->default_rset_handler = stmt->m->use_result;
- DBG_INF("use_result");
- }
- }
- }
-
- DBG_INF(ret == PASS? "PASS":"FAIL");
- DBG_RETURN(ret);
+ DBG_RETURN(mysqlnd_stmt_execute_parse_response(stmt TSRMLS_CC));
}
/* }}} */
@@ -661,7 +750,9 @@ mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int f
for (i = 0; i < result->field_count; i++) {
/* Clean what we copied last time */
#ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
- zval_dtor(stmt->result_bind[i].zv);
+ if (stmt->result_bind[i].zv) {
+ zval_dtor(stmt->result_bind[i].zv);
+ }
#endif
/* copy the type */
if (stmt->result_bind[i].bound == TRUE) {
@@ -978,7 +1069,7 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla
stmt->conn->upsert_status.server_status =
row_packet->server_status;
- DBG_INF_FMT("ret=%s fetched=%d s_status=%d warns=%d eof=%d",
+ DBG_INF_FMT("ret=%s fetched=%d server_status=%d warnings=%d eof=%d",
ret == PASS? "PASS":"FAIL", *fetched_anything,
row_packet->server_status, row_packet->warning_count,
result->unbuf->eof_reached);
@@ -1079,8 +1170,13 @@ MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const stmt TSRMLS_DC)
DBG_INF("skipping result");
stmt->result->m.skip_result(stmt->result TSRMLS_CC);
}
+
/* Now the line should be free, if it wasn't */
+ DBG_INF("freeing result");
+ /* free_result() doesn't actually free stmt->result but only the buffers */
+ stmt->m->free_result(stmt TSRMLS_CC);
+
int4store(cmd_buf, stmt->stmt_id);
if (CONN_GET_STATE(conn) == CONN_READY &&
FAIL == (ret = mysqlnd_simple_command(conn, COM_STMT_RESET, (char *)cmd_buf,
@@ -1365,8 +1461,6 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt,
DBG_ENTER("mysqlnd_stmt::bind_result");
DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count);
- SET_EMPTY_ERROR(stmt->error_info);
- SET_EMPTY_ERROR(stmt->conn->error_info);
if (stmt->state < MYSQLND_STMT_PREPARED) {
SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
@@ -1377,6 +1471,9 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt,
DBG_RETURN(FAIL);
}
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(stmt->conn->error_info);
+
if (stmt->field_count) {
uint i = 0;
@@ -1386,7 +1483,7 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt,
}
mysqlnd_stmt_separate_result_bind(stmt TSRMLS_CC);
-
+ stmt->result_zvals_separated_once = FALSE;
stmt->result_bind = result_bind;
for (i = 0; i < stmt->field_count; i++) {
/* Prevent from freeing */
@@ -1407,6 +1504,50 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt,
/* }}} */
+/* {{{ mysqlnd_stmt::bind_result */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const stmt, uint param_no TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_stmt::bind_result");
+ DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count);
+
+ if (stmt->state < MYSQLND_STMT_PREPARED) {
+ SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
+ DBG_ERR("not prepared");
+ DBG_RETURN(FAIL);
+ }
+
+ if (param_no < 0 || param_no >= stmt->field_count) {
+ SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
+ DBG_ERR("invalid param_no");
+ DBG_RETURN(FAIL);
+ }
+
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(stmt->conn->error_info);
+
+ if (stmt->field_count) {
+ mysqlnd_stmt_separate_one_result_bind(stmt, param_no TSRMLS_CC);
+ /* Guaranteed is that stmt->result_bind is NULL */
+ if (!stmt->result_bind) {
+ stmt->result_bind = ecalloc(stmt->field_count, sizeof(MYSQLND_RESULT_BIND));
+ } else {
+ stmt->result_bind = erealloc(stmt->result_bind, stmt->field_count * sizeof(MYSQLND_RESULT_BIND));
+ }
+ ALLOC_INIT_ZVAL(stmt->result_bind[param_no].zv);
+ /*
+ Don't update is_ref !!! it's not our job
+ Otherwise either 009.phpt or mysqli_stmt_bind_result.phpt
+ will fail.
+ */
+ stmt->result_bind[param_no].bound = TRUE;
+ }
+ DBG_INF("PASS");
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
/* {{{ mysqlnd_stmt::set_bind_result_dtor */
static void
MYSQLND_METHOD(mysqlnd_stmt, set_result_bind_dtor)(MYSQLND_STMT * const stmt,
@@ -1613,7 +1754,7 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const stmt,
/* {{{ mysqlnd_stmt::attr_get */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_stmt, attr_get)(MYSQLND_STMT * const stmt,
+MYSQLND_METHOD(mysqlnd_stmt, attr_get)(const MYSQLND_STMT * const stmt,
enum mysqlnd_stmt_attr attr_type,
void * const value TSRMLS_DC)
{
@@ -1638,7 +1779,7 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_get)(MYSQLND_STMT * const stmt,
}
/* }}} */
-
+/* free_result() doesn't actually free stmt->result but only the buffers */
/* {{{ mysqlnd_stmt::free_result */
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC)
@@ -1651,6 +1792,10 @@ MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC)
DBG_RETURN(PASS);
}
+ /*
+ If right after execute() we have to call the appropriate
+ use_result() or store_result() and clean.
+ */
if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
DBG_INF("fetching result set header");
/* Do implicit use_result and then flush the result */
@@ -1672,8 +1817,10 @@ MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC)
stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC);
}
- /* As the buffers have been freed, we should go back to PREPARED */
- stmt->state = MYSQLND_STMT_PREPARED;
+ if (stmt->state > MYSQLND_STMT_PREPARED) {
+ /* As the buffers have been freed, we should go back to PREPARED */
+ stmt->state = MYSQLND_STMT_PREPARED;
+ }
/* Line is free! */
CONN_SET_STATE(stmt->conn, CONN_READY);
@@ -1737,9 +1884,55 @@ void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC)
/* }}} */
+/* {{{ mysqlnd_stmt_separate_one_result_bind */
+void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, uint param_no TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_stmt_separate_one_result_bind");
+ DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u param_no=%d",
+ stmt->stmt_id, stmt->result_bind, stmt->field_count, param_no);
+
+ if (!stmt->result_bind) {
+ DBG_VOID_RETURN;
+ }
+
+ /*
+ Because only the bound variables can point to our internal buffers, then
+ separate or free only them. Free is possible because the user could have
+ lost reference.
+ */
+ /* Let's try with no cache */
+ if (stmt->result_bind[param_no].bound == TRUE) {
+ DBG_INF_FMT("%d has refcount=%u", param_no, Z_REFCOUNT_P(stmt->result_bind[param_no].zv));
+ /*
+ We have to separate the actual zval value of the bound
+ variable from our allocated zvals or we will face double-free
+ */
+ if (Z_REFCOUNT_P(stmt->result_bind[param_no].zv) > 1) {
+#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
+ zval_copy_ctor(stmt->result_bind[param_no].zv);
+#endif
+ zval_ptr_dtor(&stmt->result_bind[param_no].zv);
+ } else {
+ /*
+ If it is a string, what is pointed will be freed
+ later in free_result(). We need to remove the variable to
+ which the user has lost reference.
+ */
+#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
+ ZVAL_NULL(stmt->result_bind[param_no].zv);
+#endif
+ zval_ptr_dtor(&stmt->result_bind[param_no].zv);
+ }
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
/* {{{ mysqlnd_internal_free_stmt_content */
static
-void mysqlnd_internal_free_stmt_content(MYSQLND_STMT *stmt TSRMLS_DC)
+void mysqlnd_internal_free_stmt_content(MYSQLND_STMT * const stmt TSRMLS_DC)
{
DBG_ENTER("mysqlnd_internal_free_stmt_content");
DBG_INF_FMT("stmt=%lu param_bind=%p param_count=%u",
@@ -1778,24 +1971,15 @@ void mysqlnd_internal_free_stmt_content(MYSQLND_STMT *stmt TSRMLS_DC)
stmt->result->m.free_result_internal(stmt->result TSRMLS_CC);
stmt->result = NULL;
}
- if (stmt->cmd_buffer.buffer) {
- mnd_efree(stmt->cmd_buffer.buffer);
- stmt->cmd_buffer.buffer = NULL;
- }
-
- if (stmt->conn) {
- stmt->conn->m->free_reference(stmt->conn TSRMLS_CC);
- stmt->conn = NULL;
- }
DBG_VOID_RETURN;
}
/* }}} */
-/* {{{ mysqlnd_stmt::close */
+/* {{{ mysqlnd_stmt::net_close */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_stmt, close)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC)
+MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC)
{
MYSQLND * conn = stmt->conn;
zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */];
@@ -1854,16 +2038,25 @@ MYSQLND_METHOD(mysqlnd_stmt, close)(MYSQLND_STMT * const stmt, zend_bool implici
MYSQLND_INC_CONN_STATISTIC(&conn->stats, stat);
}
+ if (stmt->execute_cmd_buffer.buffer) {
+ mnd_efree(stmt->execute_cmd_buffer.buffer);
+ stmt->execute_cmd_buffer.buffer = NULL;
+ }
+
mysqlnd_internal_free_stmt_content(stmt TSRMLS_CC);
+ if (stmt->conn) {
+ stmt->conn->m->free_reference(stmt->conn TSRMLS_CC);
+ stmt->conn = NULL;
+ }
+
DBG_RETURN(PASS);
}
/* }}} */
-
/* {{{ mysqlnd_stmt::dtor */
static enum_func_status
-MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, dtor)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC)
+MYSQLND_METHOD(mysqlnd_stmt, dtor)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC)
{
enum_func_status ret;
@@ -1873,9 +2066,8 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, dtor)(MYSQLND_STMT * const stmt, zend_bool
MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE? STAT_STMT_CLOSE_IMPLICIT:
STAT_STMT_CLOSE_EXPLICIT);
- if (PASS == (ret = stmt->m->close(stmt, implicit TSRMLS_CC))) {
- mnd_efree(stmt);
- }
+ ret = stmt->m->net_close(stmt, implicit TSRMLS_CC);
+ mnd_efree(stmt);
DBG_INF(ret == PASS? "PASS":"FAIL");
DBG_RETURN(ret);
@@ -1891,11 +2083,13 @@ struct st_mysqlnd_stmt_methods mysqlnd_stmt_methods = {
MYSQLND_METHOD(mysqlnd_stmt, store_result),
MYSQLND_METHOD(mysqlnd_stmt, background_store_result),
MYSQLND_METHOD(mysqlnd_stmt, get_result),
+ MYSQLND_METHOD(mysqlnd_stmt, more_results),
+ MYSQLND_METHOD(mysqlnd_stmt, next_result),
MYSQLND_METHOD(mysqlnd_stmt, free_result),
MYSQLND_METHOD(mysqlnd_stmt, data_seek),
MYSQLND_METHOD(mysqlnd_stmt, reset),
- MYSQLND_METHOD(mysqlnd_stmt, close),
- MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, dtor),
+ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close),
+ MYSQLND_METHOD(mysqlnd_stmt, dtor),
MYSQLND_METHOD(mysqlnd_stmt, fetch),
@@ -1904,6 +2098,7 @@ struct st_mysqlnd_stmt_methods mysqlnd_stmt_methods = {
MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param),
MYSQLND_METHOD(mysqlnd_stmt, set_param_bind_dtor),
MYSQLND_METHOD(mysqlnd_stmt, bind_result),
+ MYSQLND_METHOD(mysqlnd_stmt, bind_one_result),
MYSQLND_METHOD(mysqlnd_stmt, set_result_bind_dtor),
MYSQLND_METHOD(mysqlnd_stmt, send_long_data),
MYSQLND_METHOD(mysqlnd_stmt, param_metadata),
@@ -1936,8 +2131,8 @@ MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC)
stmt->m = &mysqlnd_stmt_methods;
stmt->state = MYSQLND_STMT_INITTED;
- stmt->cmd_buffer.length = 4096;
- stmt->cmd_buffer.buffer = mnd_emalloc(stmt->cmd_buffer.length);
+ stmt->execute_cmd_buffer.length = 4096;
+ stmt->execute_cmd_buffer.buffer = mnd_emalloc(stmt->execute_cmd_buffer.length);
stmt->prefetch_rows = MYSQLND_DEFAULT_PREFETCH_ROWS;
/*
@@ -1945,7 +2140,7 @@ MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC)
be destructed till there is open statements. The last statement
or normal query result will close it then.
*/
- stmt->conn = conn->m->get_reference(conn);
+ stmt->conn = conn->m->get_reference(conn TSRMLS_CC);
stmt->m->set_param_bind_dtor(stmt, mysqlnd_efree_param_bind_dtor TSRMLS_CC);
stmt->m->set_result_bind_dtor(stmt, mysqlnd_efree_result_bind_dtor TSRMLS_CC);
diff --git a/ext/mysqlnd/mysqlnd_ps_codec.c b/ext/mysqlnd/mysqlnd_ps_codec.c
index c832c6b463..0e2c4345db 100644
--- a/ext/mysqlnd/mysqlnd_ps_codec.c
+++ b/ext/mysqlnd/mysqlnd_ps_codec.c
@@ -771,9 +771,9 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uch
zend_uchar* mysqlnd_stmt_execute_generate_request(MYSQLND_STMT *stmt, size_t *request_len,
zend_bool *free_buffer TSRMLS_DC)
{
- zend_uchar *p = stmt->cmd_buffer.buffer,
- *cmd_buffer = stmt->cmd_buffer.buffer;
- size_t cmd_buffer_length = stmt->cmd_buffer.length;
+ zend_uchar *p = stmt->execute_cmd_buffer.buffer,
+ *cmd_buffer = stmt->execute_cmd_buffer.buffer;
+ size_t cmd_buffer_length = stmt->execute_cmd_buffer.length;
unsigned int null_byte_offset,
null_count= (stmt->param_count + 7) / 8;
@@ -801,7 +801,7 @@ zend_uchar* mysqlnd_stmt_execute_generate_request(MYSQLND_STMT *stmt, size_t *re
mysqlnd_stmt_execute_store_params(stmt, &cmd_buffer, &p, &cmd_buffer_length, null_byte_offset TSRMLS_CC);
- *free_buffer = (cmd_buffer != stmt->cmd_buffer.buffer);
+ *free_buffer = (cmd_buffer != stmt->execute_cmd_buffer.buffer);
*request_len = (p - cmd_buffer);
return cmd_buffer;
}
diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c
index fac01c1047..b30f90409f 100644
--- a/ext/mysqlnd/mysqlnd_result.c
+++ b/ext/mysqlnd/mysqlnd_result.c
@@ -177,6 +177,7 @@ void mysqlnd_unbuffered_free_last_data(MYSQLND_RES *result TSRMLS_DC)
}
/* }}} */
+
/* {{{ mysqlnd_free_buffered_data */
void mysqlnd_free_buffered_data(MYSQLND_RES *result TSRMLS_DC)
{
@@ -231,6 +232,7 @@ void mysqlnd_free_buffered_data(MYSQLND_RES *result TSRMLS_DC)
}
/* }}} */
+
#ifdef MYSQLND_THREADED
/* {{{ mysqlnd_free_background_buffered_data */
void mysqlnd_free_background_buffered_data(MYSQLND_RES *result TSRMLS_DC)
@@ -309,8 +311,9 @@ void mysqlnd_free_background_buffered_data(MYSQLND_RES *result TSRMLS_DC)
/* }}} */
#endif /* MYSQL_THREADING */
+
/* {{{ mysqlnd_res::free_result_buffers */
-void
+static void
MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES *result TSRMLS_DC)
{
DBG_ENTER("mysqlnd_res::free_result_buffers");
@@ -580,8 +583,15 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC
stmt->state = MYSQLND_STMT_INITTED;
}
} else {
- DBG_INF_FMT("warns=%u status=%u", fields_eof.warning_count, fields_eof.server_status);
+ DBG_INF_FMT("warnings=%u server_status=%u", fields_eof.warning_count, fields_eof.server_status);
conn->upsert_status.warning_count = fields_eof.warning_count;
+ /*
+ If SERVER_MORE_RESULTS_EXISTS is set then this is either MULTI_QUERY or a CALL()
+ The first packet after sending the query/com_execute has the bit set only
+ in this cases. Not sure why it's a needed but it marks that the whole stream
+ will include many result sets. What actually matters are the bits set at the end
+ of every result set (the EOF packet).
+ */
conn->upsert_status.server_status = fields_eof.server_status;
if (fields_eof.server_status & MYSQLND_SERVER_QUERY_NO_GOOD_INDEX_USED) {
stat = STAT_BAD_INDEX_USED;
@@ -789,7 +799,7 @@ mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES *result TSRMLS_DC)
result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
} else if (row_packet->eof) {
/* Mark the connection as usable again */
- DBG_INF_FMT("warns=%u status=%u", row_packet->warning_count, row_packet->server_status);
+ DBG_INF_FMT("warningss=%u server_status=%u", row_packet->warning_count, row_packet->server_status);
result->unbuf->eof_reached = TRUE;
result->conn->upsert_status.warning_count = row_packet->warning_count;
result->conn->upsert_status.server_status = row_packet->server_status;
@@ -929,7 +939,7 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flag
result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
} else if (row_packet->eof) {
/* Mark the connection as usable again */
- DBG_INF_FMT("warns=%u status=%u", row_packet->warning_count, row_packet->server_status);
+ DBG_INF_FMT("warnings=%u server_status=%u", row_packet->warning_count, row_packet->server_status);
result->unbuf->eof_reached = TRUE;
result->conn->upsert_status.warning_count = row_packet->warning_count;
result->conn->upsert_status.server_status = row_packet->server_status;
@@ -1261,7 +1271,7 @@ mysqlnd_store_result_fetch_data(MYSQLND * const conn, MYSQLND_RES *result,
}
PACKET_FREE(row_packet);
- DBG_INF_FMT("ret=%s row_count=%u warns=%u status=%u", ret == PASS? "PASS":"FAIL",
+ DBG_INF_FMT("ret=%s row_count=%u warnings=%u server_status=%u", ret == PASS? "PASS":"FAIL",
set->row_count, conn->upsert_status.warning_count, conn->upsert_status.server_status);
DBG_RETURN(ret);
}
@@ -1281,7 +1291,7 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result,
DBG_INF_FMT("conn=%d ps_protocol=%d", conn->thread_id, ps_protocol);
/* We need the conn because we are doing lazy zval initialization in buffered_fetch_row */
- result->conn = conn->m->get_reference(conn);
+ result->conn = conn->m->get_reference(conn TSRMLS_CC);
result->type = MYSQLND_RES_NORMAL;
result->m.fetch_row = result->m.fetch_row_normal_buffered;
result->m.fetch_lengths = mysqlnd_fetch_lengths_buffered;
@@ -1305,6 +1315,7 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result,
}
/* }}} */
+
#ifdef MYSQLND_THREADED
/* {{{ mysqlnd_fetch_row_async_buffered */
static enum_func_status
@@ -1432,6 +1443,7 @@ mysqlnd_fetch_row_async_buffered(MYSQLND_RES *result, void *param, unsigned int
}
/* }}} */
+
/* {{{ mysqlnd_background_store_result_fetch_data */
enum_func_status
mysqlnd_background_store_result_fetch_data(MYSQLND_RES *result TSRMLS_DC)
@@ -1465,7 +1477,9 @@ mysqlnd_background_store_result_fetch_data(MYSQLND_RES *result TSRMLS_DC)
old_size = set->data_size;
set->data_size = total_rows;
set->data = mnd_perealloc(set->data, set->data_size * sizeof(zval **), set->persistent);
-// memset(set->data + old_size, 0, (set->data_size - old_size) * sizeof(zval **));
+#if 0
+ memset(set->data + old_size, 0, (set->data_size - old_size) * sizeof(zval **));
+#endif
set->row_buffers = mnd_perealloc(set->row_buffers,
total_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *),
set->persistent);
@@ -1511,11 +1525,12 @@ mysqlnd_background_store_result_fetch_data(MYSQLND_RES *result TSRMLS_DC)
transfered above.
*/
}
-// MYSQLND_INC_CONN_STATISTIC_W_VALUE(&conn->stats,
-// binary_protocol? STAT_ROWS_BUFFERED_FROM_CLIENT_PS:
-// STAT_ROWS_BUFFERED_FROM_CLIENT_NORMAL,
-// set->row_count);
-
+#if 0
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE(&conn->stats,
+ binary_protocol? STAT_ROWS_BUFFERED_FROM_CLIENT_PS:
+ STAT_ROWS_BUFFERED_FROM_CLIENT_NORMAL,
+ set->row_count);
+#endif
tsrm_mutex_lock(set->LOCK);
/* Finally clean */
if (row_packet->eof) {
@@ -1551,7 +1566,7 @@ mysqlnd_background_store_result_fetch_data(MYSQLND_RES *result TSRMLS_DC)
} else {
CONN_SET_STATE(conn, CONN_READY);
}
- DBG_INF_FMT("ret=%s row_count=%u warns=%u status=%u", ret == PASS? "PASS":"FAIL",
+ DBG_INF_FMT("ret=%s row_count=%u warnings=%u server_status=%u", ret == PASS? "PASS":"FAIL",
set->row_count, conn->upsert_status.warning_count, conn->upsert_status.server_status);
DBG_RETURN(ret);
}
@@ -1560,7 +1575,7 @@ mysqlnd_background_store_result_fetch_data(MYSQLND_RES *result TSRMLS_DC)
/* {{{ mysqlnd_res::background_store_result */
-MYSQLND_RES *
+static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_res, background_store_result)(MYSQLND_RES * result, MYSQLND * const conn, zend_bool ps TSRMLS_DC)
{
#ifndef MYSQLND_THREADED
@@ -1573,7 +1588,7 @@ MYSQLND_METHOD(mysqlnd_res, background_store_result)(MYSQLND_RES * result, MYSQL
DBG_INF_FMT("conn=%d ps_protocol=%d", conn->thread_id, ps);
/* We need the conn because we are doing lazy zval initialization in buffered_fetch_row */
- result->conn = conn->m->get_reference(conn);
+ result->conn = conn->m->get_reference(conn TSRMLS_CC);
result->type = MYSQLND_RES_NORMAL;
result->m.fetch_row = mysqlnd_fetch_row_async_buffered;
result->m.fetch_lengths = mysqlnd_fetch_lengths_async_buffered;
diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h
index d9cc7351f1..2f6dcfa2e1 100644
--- a/ext/mysqlnd/mysqlnd_structs.h
+++ b/ext/mysqlnd/mysqlnd_structs.h
@@ -241,7 +241,7 @@ struct st_mysqlnd_conn_methods
MYSQLND_RES * (*store_result)(MYSQLND * const conn TSRMLS_DC);
MYSQLND_RES * (*background_store_result)(MYSQLND * const conn TSRMLS_DC);
enum_func_status (*next_result)(MYSQLND * const conn TSRMLS_DC);
- zend_bool (*more_results)(const MYSQLND * const conn);
+ zend_bool (*more_results)(const MYSQLND * const conn TSRMLS_DC);
MYSQLND_STMT * (*stmt_init)(MYSQLND * const conn TSRMLS_DC);
@@ -282,7 +282,7 @@ struct st_mysqlnd_conn_methods
enum_func_status (*close)(MYSQLND *conn, enum_connection_close_type close_type TSRMLS_DC);
void (*dtor)(MYSQLND *conn TSRMLS_DC); /* private */
- MYSQLND * (*get_reference)(MYSQLND * const conn);
+ MYSQLND * (*get_reference)(MYSQLND * const conn TSRMLS_DC);
enum_func_status (*free_reference)(MYSQLND * const conn TSRMLS_DC);
enum mysqlnd_connection_state (*get_state)(MYSQLND * const conn TSRMLS_DC);
void (*set_state)(MYSQLND * const conn, enum mysqlnd_connection_state new_state TSRMLS_DC);
@@ -344,10 +344,12 @@ struct st_mysqlnd_stmt_methods
MYSQLND_RES * (*store_result)(MYSQLND_STMT * const stmt TSRMLS_DC);
MYSQLND_RES * (*background_store_result)(MYSQLND_STMT * const stmt TSRMLS_DC);
MYSQLND_RES * (*get_result)(MYSQLND_STMT * const stmt TSRMLS_DC);
+ zend_bool (*more_results)(const MYSQLND_STMT * const stmt TSRMLS_DC);
+ enum_func_status (*next_result)(MYSQLND_STMT * const stmt TSRMLS_DC);
enum_func_status (*free_result)(MYSQLND_STMT * const stmt TSRMLS_DC);
enum_func_status (*seek_data)(const MYSQLND_STMT * const stmt, uint64 row TSRMLS_DC);
enum_func_status (*reset)(MYSQLND_STMT * const stmt TSRMLS_DC);
- enum_func_status (*close)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC); /* private */
+ enum_func_status (*net_close)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC); /* private */
enum_func_status (*dtor)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC); /* use this for mysqlnd_stmt_close */
enum_func_status (*fetch)(MYSQLND_STMT * const stmt, zend_bool * const fetched_anything TSRMLS_DC);
@@ -357,6 +359,7 @@ struct st_mysqlnd_stmt_methods
enum_func_status (*refresh_bind_param)(MYSQLND_STMT * const stmt TSRMLS_DC);
void (*set_param_bind_dtor)(MYSQLND_STMT * const stmt, void (*param_bind_dtor)(MYSQLND_PARAM_BIND *) TSRMLS_DC);
enum_func_status (*bind_result)(MYSQLND_STMT * const stmt, MYSQLND_RESULT_BIND * const result_bind TSRMLS_DC);
+ enum_func_status (*bind_one_result)(MYSQLND_STMT * const stmt, uint param_no TSRMLS_DC);
void (*set_result_bind_dtor)(MYSQLND_STMT * const stmt, void (*result_bind_dtor)(MYSQLND_RESULT_BIND *) TSRMLS_DC);
enum_func_status (*send_long_data)(MYSQLND_STMT * const stmt, unsigned int param_num,
const char * const data, unsigned long length TSRMLS_DC);
@@ -375,7 +378,7 @@ struct st_mysqlnd_stmt_methods
const char * (*get_error_str)(const MYSQLND_STMT * const stmt);
const char * (*get_sqlstate)(const MYSQLND_STMT * const stmt);
- enum_func_status (*get_attribute)(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, void * const value TSRMLS_DC);
+ enum_func_status (*get_attribute)(const MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, void * const value TSRMLS_DC);
enum_func_status (*set_attribute)(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, const void * const value TSRMLS_DC);
};
@@ -587,7 +590,6 @@ struct st_mysqlnd_param_bind
struct st_mysqlnd_result_bind
{
zval *zv;
- zend_uchar original_type;
zend_bool bound;
};
@@ -617,7 +619,7 @@ struct st_mysqlnd_stmt
zend_bool cursor_exists;
mysqlnd_stmt_use_or_store_func default_rset_handler;
- MYSQLND_CMD_BUFFER cmd_buffer;
+ MYSQLND_CMD_BUFFER execute_cmd_buffer;
unsigned int execute_count;/* count how many times the stmt was executed */
void (*param_bind_dtor)(MYSQLND_PARAM_BIND *);
diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c
index f4eaeefd22..4816a4fb37 100644
--- a/ext/mysqlnd/mysqlnd_wireprotocol.c
+++ b/ext/mysqlnd/mysqlnd_wireprotocol.c
@@ -587,6 +587,13 @@ php_mysqlnd_greet_read(void *_packet, MYSQLND *conn TSRMLS_DC)
} else {
packet->pre41 = TRUE;
}
+
+ DBG_INF_FMT("proto=%d server=%s thread_id=%ld",
+ packet->protocol_version, packet->server_version, packet->thread_id);
+
+ DBG_INF_FMT("server_capabilities=%d charset_no=%d server_status=%d",
+ packet->server_capabilities, packet->charset_no, packet->server_status);
+
if (p - begin > packet->header.size) {
DBG_ERR_FMT("GREET packet %d bytes shorter than expected", p - begin - packet->header.size);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet %d bytes shorter than expected. PID=%d",
@@ -783,8 +790,8 @@ php_mysqlnd_ok_read(void *_packet, MYSQLND *conn TSRMLS_DC)
}
DBG_INF_FMT("OK packet: aff_rows=%lld last_ins_id=%ld server_status=%d warnings=%d",
- packet->affected_rows, packet->last_insert_id, packet->server_status,
- packet->warning_count);
+ packet->affected_rows, packet->last_insert_id, packet->server_status,
+ packet->warning_count);
if (p - begin > packet->header.size) {
DBG_ERR_FMT("OK packet %d bytes shorter than expected", p - begin - packet->header.size);
@@ -865,7 +872,8 @@ php_mysqlnd_eof_read(void *_packet, MYSQLND *conn TSRMLS_DC)
p - begin - packet->header.size, getpid());
}
- DBG_INF_FMT("EOF packet: status=%d warnings=%d", packet->server_status, packet->warning_count);
+ DBG_INF_FMT("EOF packet: fields=%d status=%d warnings=%d",
+ packet->field_count, packet->server_status, packet->warning_count);
DBG_RETURN(PASS);
}
@@ -986,6 +994,7 @@ php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC)
packet->field_count= php_mysqlnd_net_field_length(&p);
switch (packet->field_count) {
case MYSQLND_NULL_LENGTH:
+ DBG_INF("LOAD LOCAL");
/*
First byte in the packet is the field count.
Thus, the name is size - 1. And we add 1 for a trailing \0.
@@ -997,6 +1006,7 @@ php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC)
packet->info_or_local_file_len = len;
break;
case 0x00:
+ DBG_INF("UPSERT");
packet->affected_rows = php_mysqlnd_net_field_length_ll(&p);
packet->last_insert_id= php_mysqlnd_net_field_length_ll(&p);
packet->server_status = uint2korr(p);
@@ -1010,13 +1020,17 @@ php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC)
packet->info_or_local_file[len] = '\0';
packet->info_or_local_file_len = len;
}
+ DBG_INF_FMT("affected_rows=%llu last_insert_id=%llu server_status=%d warning_count=%d",
+ packet->affected_rows, packet->last_insert_id,
+ packet->server_status, packet->warning_count);
break;
default:
+ DBG_INF("SELECT");
/* Result set */
break;
}
if (p - begin > packet->header.size) {
- DBG_ERR_FMT("GREET packet %d bytes shorter than expected", p - begin - packet->header.size);
+ DBG_ERR_FMT("RSET_HEADER packet %d bytes shorter than expected", p - begin - packet->header.size);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet %d bytes shorter than expected. PID=%d",
p - begin - packet->header.size, getpid());
}
@@ -1097,7 +1111,7 @@ php_mysqlnd_rset_field_read(void *_packet, MYSQLND *conn TSRMLS_DC)
*(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = 0;
break;
case MYSQLND_NULL_LENGTH:
- goto faulty_fake;
+ goto faulty_or_fake;
default:
*(char **)(((char *)meta) + rset_field_offsets[i]) = (char *)p;
*(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = len;
@@ -1156,7 +1170,7 @@ php_mysqlnd_rset_field_read(void *_packet, MYSQLND *conn TSRMLS_DC)
}
if (p - begin > packet->header.size) {
- DBG_ERR_FMT("Result set field packet %d bytes shorter than expected", p - begin - packet->header.size);
+ DBG_ERR_FMT("RSET field packet %d bytes shorter than expected", p - begin - packet->header.size);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Result set field packet %d bytes "
"shorter than expected. PID=%d", p - begin - packet->header.size, getpid());
}
@@ -1210,7 +1224,7 @@ php_mysqlnd_rset_field_read(void *_packet, MYSQLND *conn TSRMLS_DC)
*/
DBG_RETURN(PASS);
-faulty_fake:
+faulty_or_fake:
DBG_ERR_FMT("Protocol error. Server sent NULL_LENGTH. The server is faulty");
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Protocol error. Server sent NULL_LENGTH."
" The server is faulty");
@@ -1629,6 +1643,8 @@ php_mysqlnd_rowp_read(void *_packet, MYSQLND *conn TSRMLS_DC)
p += 2;
packet->server_status = uint2korr(p);
/* Seems we have 3 bytes reserved for future use */
+ DBG_INF_FMT("server_status=%d warning_count=%d",
+ packet->server_status, packet->warning_count);
}
} else {
MYSQLND_INC_CONN_STATISTIC(&conn->stats,