MFB: More optimizations - less MM calls

Clearly separated fetching (physical reading) from decoding phases (data
interpretation). Threaded fetching added but disabled as needs more work for
Windows. For Linux needs some touches to add pthreads if this is enabled,
probably with a compile-time switch.
The code reorganisation makes it easy to add also async API, similar to
cURL's one.
This commit is contained in:
Andrey Hristov 2008-01-28 18:27:49 +00:00
parent 618a29411d
commit 18c8e6501b
18 changed files with 1380 additions and 292 deletions

View File

@ -33,9 +33,6 @@
#include "php_mysqli_structs.h"
#include "zend_exceptions.h"
#define MYSQLI_STORE_RESULT 0
#define MYSQLI_USE_RESULT 1
ZEND_DECLARE_MODULE_GLOBALS(mysqli)
static PHP_GINIT_FUNCTION(mysqli);
@ -685,8 +682,11 @@ PHP_MINIT_FUNCTION(mysqli)
REGISTER_LONG_CONSTANT("MYSQLI_CLIENT_FOUND_ROWS", CLIENT_FOUND_ROWS, CONST_CS | CONST_PERSISTENT);
/* for mysqli_query */
REGISTER_LONG_CONSTANT("MYSQLI_STORE_RESULT", 0, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MYSQLI_USE_RESULT", 1, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MYSQLI_STORE_RESULT", MYSQLI_STORE_RESULT, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MYSQLI_USE_RESULT", MYSQLI_USE_RESULT, CONST_CS | CONST_PERSISTENT);
#if defined(HAVE_MYSQLND) && defined(MYSQLND_THREADING)
REGISTER_LONG_CONSTANT("MYSQLI_BG_STORE_RESULT", MYSQLI_BG_STORE_RESULT, CONST_CS | CONST_PERSISTENT);
#endif
/* for mysqli_fetch_assoc */
REGISTER_LONG_CONSTANT("MYSQLI_ASSOC", MYSQLI_ASSOC, CONST_CS | CONST_PERSISTENT);
@ -952,7 +952,7 @@ Parameters:
ZEND_FUNCTION(mysqli_result_construct)
{
MY_MYSQL *mysql;
MYSQL_RES *result;
MYSQL_RES *result = NULL;
zval *mysql_link;
MYSQLI_RESOURCE *mysqli_resource;
long resmode = MYSQLI_STORE_RESULT;
@ -967,10 +967,6 @@ ZEND_FUNCTION(mysqli_result_construct)
if (zend_parse_parameters(2 TSRMLS_CC, "Ol", &mysql_link, mysqli_link_class_entry, &resmode)==FAILURE) {
return;
}
if (resmode != MYSQLI_USE_RESULT && resmode != MYSQLI_STORE_RESULT) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid value for resultmode");
RETURN_FALSE;
}
break;
default:
WRONG_PARAM_COUNT;
@ -978,8 +974,21 @@ ZEND_FUNCTION(mysqli_result_construct)
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
result = (resmode == MYSQLI_STORE_RESULT) ? mysql_store_result(mysql->mysql) :
mysql_use_result(mysql->mysql);
switch (resmode) {
case MYSQLI_STORE_RESULT:
result = mysql_store_result(mysql->mysql);
break;
case MYSQLI_USE_RESULT:
result = mysql_use_result(mysql->mysql);
break;
#if defined(HAVE_MYSQLND) && defined(MYSQLND_THREADING)
case MYSQLI_BG_STORE_RESULT:
result = mysqli_bg_store_result(mysql->mysql);
break;
#endif
default:
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid value for resultmode");
}
if (!result) {
RETURN_FALSE;

View File

@ -37,5 +37,6 @@
#define mysqli_close(c, how) mysqlnd_close((c), (how))
#define mysqli_stmt_close(c, implicit) mysqlnd_stmt_close((c), (implicit))
#define mysqli_free_result(r, implicit) mysqlnd_free_result((r), (implicit))
#define mysqli_bg_store_result(r) mysqlnd_bg_store_result((r))
#endif

View File

@ -473,7 +473,11 @@ PHP_FUNCTION(mysqli_query)
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty query");
RETURN_FALSE;
}
if (resultmode != MYSQLI_USE_RESULT && resultmode != MYSQLI_STORE_RESULT) {
if (resultmode != MYSQLI_USE_RESULT && resultmode != MYSQLI_STORE_RESULT
#if defined(HAVE_MYSQLND) && defined(MYSQLND_THREADING)
&& resultmode != MYSQLI_BG_STORE_RESULT
#endif
) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid value for resultmode");
RETURN_FALSE;
}
@ -495,8 +499,19 @@ PHP_FUNCTION(mysqli_query)
RETURN_TRUE;
}
result = (resultmode == MYSQLI_USE_RESULT) ? mysql_use_result(mysql->mysql) : mysql_store_result(mysql->mysql);
switch (resultmode) {
case MYSQLI_STORE_RESULT:
result = mysql_store_result(mysql->mysql);
break;
case MYSQLI_USE_RESULT:
result = mysql_use_result(mysql->mysql);
break;
#if defined(HAVE_MYSQLND) && defined(MYSQLND_THREADING)
case MYSQLI_BG_STORE_RESULT:
result = mysqli_bg_store_result(mysql->mysql);
break;
#endif
}
if (!result) {
php_mysqli_throw_sql_exception((char *)mysql_sqlstate(mysql->mysql), mysql_errno(mysql->mysql) TSRMLS_CC,
"%s", mysql_error(mysql->mysql));

View File

@ -213,7 +213,7 @@ static int result_type_read(mysqli_object *obj, zval **retval TSRMLS_DC)
if (!p) {
ZVAL_NULL(*retval);
} else {
ZVAL_LONG(*retval, (p->data) ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT);
ZVAL_LONG(*retval, mysqli_result_is_unbuffered(p) ? MYSQLI_USE_RESULT:MYSQLI_STORE_RESULT);
}
return SUCCESS;
}

View File

@ -311,6 +311,9 @@ PHP_MYSQLI_EXPORT(zend_object_value) mysqli_objects_new(zend_class_entry * TSRML
#define MYSQLI_STORE_RESULT 0
#define MYSQLI_USE_RESULT 1
#ifdef HAVE_MYSQLND
#define MYSQLI_BG_STORE_RESULT 101
#endif
/* for mysqli_fetch_assoc */
#define MYSQLI_ASSOC 1

View File

@ -969,9 +969,9 @@ array(61) {
["mem_efree_count"]=>
string(1) "0"
["mem_malloc_count"]=>
string(1) "0"
string(1) "1"
["mem_malloc_ammount"]=>
string(1) "0"
string(%d) "%d"
["mem_calloc_count"]=>
string(1) "0"
["mem_calloc_ammount"]=>
@ -1106,9 +1106,9 @@ array(61) {
[u"mem_efree_count"]=>
unicode(1) "0"
[u"mem_malloc_count"]=>
unicode(1) "0"
unicode(1) "1"
[u"mem_malloc_ammount"]=>
unicode(1) "0"
unicode(%d) "%d"
[u"mem_calloc_count"]=>
unicode(1) "0"
[u"mem_calloc_ammount"]=>

View File

@ -66,12 +66,205 @@ const char * mysqlnd_out_of_sync = "Commands out of sync; you can't run this com
MYSQLND_STATS *mysqlnd_global_stats = NULL;
static zend_bool mysqlnd_library_initted = FALSE;
MYSQLND_MEMORY_POOL mysqlnd_memory_pool;
static enum_func_status mysqlnd_send_close(MYSQLND * conn TSRMLS_DC);
#define MYSQLND_SILENT 1
#ifdef MYSQLND_THREADED
/* {{{ _mysqlnd_fetch_thread */
void * _mysqlnd_fetch_thread(void *arg)
{
MYSQLND *conn = (MYSQLND *) arg;
MYSQLND_RES * result = NULL;
void ***tsrm_ls = conn->tsrm_ls;
#ifndef MYSQLND_SILENT
printf("conn=%p tsrm_ls=%p\n", conn, conn->tsrm_ls);
#endif
do {
pthread_mutex_lock(&conn->LOCK_work);
while (conn->thread_killed == FALSE /* && there is work */) {
#ifndef MYSQLND_SILENT
printf("Waiting for work in %s\n", __FUNCTION__);
#endif
pthread_cond_wait(&conn->COND_work, &conn->LOCK_work);
}
if (conn->thread_killed == TRUE) {
#ifndef MYSQLND_SILENT
printf("Thread killed in %s\n", __FUNCTION__);
#endif
pthread_cond_signal(&conn->COND_thread_ended);
pthread_mutex_unlock(&conn->LOCK_work);
break;
}
#ifndef MYSQLND_SILENT
printf("Got work in %s\n", __FUNCTION__);
#endif
CONN_SET_STATE(conn, CONN_FETCHING_DATA);
result = conn->current_result;
conn->current_result = NULL;
pthread_mutex_unlock(&conn->LOCK_work);
mysqlnd_background_store_result_fetch_data(result TSRMLS_CC);
/* do fetch the data from the wire */
pthread_mutex_lock(&conn->LOCK_work);
CONN_SET_STATE(conn, CONN_READY);
pthread_cond_signal(&conn->COND_work_done);
#ifndef MYSQLND_SILENT
printf("Signaling work done in %s\n", __FUNCTION__);
#endif
pthread_mutex_unlock(&conn->LOCK_work);
} while (1);
#ifndef MYSQLND_SILENT
printf("Exiting worker thread in %s\n", __FUNCTION__);
#endif
return NULL;
}
/* }}} */
#endif /* MYSQLND_THREADED */
/************************************************************************************************/
/* Let's don't use pool allocation for now */
/* {{{ mysqlnd_mempool_free_chunk */
static
void mysqlnd_mempool_free_contents(MYSQLND_MEMORY_POOL * pool TSRMLS_DC)
{
DBG_ENTER("mysqlnd_mempool_dtor");
uint i;
for (i = 0; i < pool->free_chunk_list_elements; i++) {
MYSQLND_MEMORY_POOL_CHUNK * chunk = pool->free_chunk_list[i];
chunk->free_chunk(chunk, FALSE TSRMLS_CC);
}
DBG_VOID_RETURN;
}
/* }}} */
/* Let's don't use pool allocation for now */
/* {{{ mysqlnd_mempool_free_chunk */
static
void mysqlnd_mempool_free_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk, zend_bool cache_it TSRMLS_DC)
{
DBG_ENTER("mysqlnd_mempool_free_chunk");
MYSQLND_MEMORY_POOL * pool = chunk->pool;
if (chunk->from_pool) {
/* Try to back-off and guess if this is the last block allocated */
if (chunk->ptr == (pool->arena + (pool->arena_size - pool->free_size - chunk->size))) {
/*
This was the last allocation. Lucky us, we can free
a bit of memory from the pool. Next time we will return from the same ptr.
*/
pool->free_size += chunk->size;
}
pool->refcount--;
} else {
mnd_free(chunk->ptr);
}
if (cache_it && pool->free_chunk_list_elements < MYSQLND_MEMORY_POOL_CHUNK_LIST_SIZE) {
chunk->ptr = NULL;
pool->free_chunk_list[pool->free_chunk_list_elements++] = chunk;
} else {
/* We did not cache it -> free it */
mnd_free(chunk);
}
DBG_VOID_RETURN;
}
/* }}} */
/* {{{ mysqlnd_mempool_resize_chunk */
static void
mysqlnd_mempool_resize_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk, uint size TSRMLS_DC)
{
DBG_ENTER("mysqlnd_mempool_resize_chunk");
if (chunk->from_pool) {
MYSQLND_MEMORY_POOL * pool = chunk->pool;
/* Try to back-off and guess if this is the last block allocated */
if (chunk->ptr == (pool->arena + (pool->arena_size - pool->free_size - chunk->size))) {
/*
This was the last allocation. Lucky us, we can free
a bit of memory from the pool. Next time we will return from the same ptr.
*/
if ((chunk->size + pool->free_size) < size) {
zend_uchar *new_ptr;
new_ptr = mnd_malloc(size);
memcpy(new_ptr, chunk->ptr, chunk->size);
chunk->ptr = new_ptr;
pool->free_size += chunk->size;
chunk->size = size;
chunk->pool = NULL; /* now we have no pool memory */
pool->refcount--;
} else {
/* If the chunk is > than asked size then free_memory increases, otherwise decreases*/
pool->free_size += (chunk->size - size);
}
} else {
/* Not last chunk, if the user asks for less, give it to him */
if (chunk->size >= size) {
; /* nop */
} else {
zend_uchar *new_ptr;
new_ptr = mnd_malloc(size);
memcpy(new_ptr, chunk->ptr, chunk->size);
chunk->ptr = new_ptr;
chunk->size = size;
chunk->pool = NULL; /* now we have no pool memory */
pool->refcount--;
}
}
} else {
chunk->ptr = mnd_realloc(chunk->ptr, size);
}
DBG_VOID_RETURN;
}
/* }}} */
/* {{{ mysqlnd_mempool_get_chunk */
static
MYSQLND_MEMORY_POOL_CHUNK * mysqlnd_mempool_get_chunk(MYSQLND_MEMORY_POOL * pool, uint size TSRMLS_DC)
{
MYSQLND_MEMORY_POOL_CHUNK *chunk = NULL;
DBG_ENTER("mysqlnd_mempool_get_chunk");
if (pool->free_chunk_list_elements) {
chunk = pool->free_chunk_list[--pool->free_chunk_list_elements];
} else {
chunk = mnd_malloc(sizeof(MYSQLND_MEMORY_POOL_CHUNK));
}
chunk->free_chunk = mysqlnd_mempool_free_chunk;
chunk->resize_chunk = mysqlnd_mempool_resize_chunk;
chunk->size = size;
/*
Should not go over MYSQLND_MAX_PACKET_SIZE, since we
expect non-arena memory in mysqlnd_wireprotocol.c . We
realloc the non-arena memory.
*/
chunk->pool = pool;
if (size > pool->free_size) {
chunk->ptr = mnd_malloc(size);
chunk->from_pool = FALSE;
} else {
chunk->from_pool = TRUE;
++pool->refcount;
chunk->ptr = pool->arena + (pool->arena_size - pool->free_size);
/* Last step, update free_size */
pool->free_size -= size;
}
DBG_RETURN(chunk);
}
/* }}} */
/************************************************************************************************/
/* {{{ mysqlnd_library_init */
static
void mysqlnd_library_init()
void mysqlnd_library_init(TSRMLS_D)
{
if (mysqlnd_library_initted == FALSE) {
mysqlnd_library_initted = TRUE;
@ -81,6 +274,13 @@ void mysqlnd_library_init()
#ifdef ZTS
mysqlnd_global_stats->LOCK_access = tsrm_mutex_alloc();
#endif
mysqlnd_memory_pool.arena_size = 16000;
mysqlnd_memory_pool.free_size = mysqlnd_memory_pool.arena_size;
mysqlnd_memory_pool.refcount = 0;
/* OOM ? */
mysqlnd_memory_pool.arena = mnd_malloc(mysqlnd_memory_pool.arena_size);
mysqlnd_memory_pool.get_chunk = mysqlnd_mempool_get_chunk;
mysqlnd_memory_pool.free_contents = mysqlnd_mempool_free_contents;
}
}
/* }}} */
@ -88,9 +288,12 @@ void mysqlnd_library_init()
/* {{{ mysqlnd_library_end */
static
void mysqlnd_library_end()
void mysqlnd_library_end(TSRMLS_D)
{
if (mysqlnd_library_initted == TRUE) {
/* mnd_free will reference LOCK_access and won't crash...*/
mysqlnd_memory_pool.free_contents(&mysqlnd_memory_pool TSRMLS_CC);
free(mysqlnd_memory_pool.arena);
#ifdef ZTS
tsrm_mutex_free(mysqlnd_global_stats->LOCK_access);
#endif
@ -229,6 +432,7 @@ MYSQLND_METHOD(mysqlnd_conn, free_contents)(MYSQLND *conn TSRMLS_DC)
mnd_pefree(conn->net.cmd_buffer.buffer, pers);
conn->net.cmd_buffer.buffer = NULL;
}
conn->charset = NULL;
conn->greet_charset = NULL;
@ -246,6 +450,22 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND *conn TSRMLS_DC)
conn->m->free_contents(conn TSRMLS_CC);
#ifdef MYSQLND_THREADED
if (conn->thread_is_running) {
pthread_mutex_lock(&conn->LOCK_work);
conn->thread_killed = TRUE;
pthread_cond_signal(&conn->COND_work);
pthread_cond_wait(&conn->COND_thread_ended, &conn->LOCK_work);
pthread_mutex_unlock(&conn->LOCK_work);
}
tsrm_mutex_free(conn->LOCK_state);
pthread_cond_destroy(&conn->COND_work);
pthread_cond_destroy(&conn->COND_work_done);
pthread_mutex_destroy(&conn->LOCK_work);
#endif
mnd_pefree(conn, conn->persistent);
DBG_VOID_RETURN;
@ -363,7 +583,7 @@ mysqlnd_simple_command(MYSQLND *conn, enum php_mysqlnd_server_command command,
DBG_ENTER("mysqlnd_simple_command");
DBG_INF_FMT("command=%s ok_packet=%d silent=%d", mysqlnd_command_to_text[command], ok_packet, silent);
switch (conn->state) {
switch (CONN_GET_STATE(conn)) {
case CONN_READY:
break;
case CONN_QUIT_SENT:
@ -481,13 +701,13 @@ PHPAPI MYSQLND *mysqlnd_connect(MYSQLND *conn,
DBG_ENTER("mysqlnd_connect");
DBG_INF_FMT("host=%s user=%s db=%s port=%d flags=%d persistent=%d state=%d",
host?host:"", user?user:"", db?db:"", port, mysql_flags,
conn? conn->persistent:0, conn? conn->state:-1);
conn? conn->persistent:0, conn? CONN_GET_STATE(conn):-1);
DBG_INF_FMT("state=%d", conn->state);
if (conn && conn->state > CONN_ALLOCED && conn->state ) {
DBG_INF_FMT("state=%d", CONN_GET_STATE(conn));
if (conn && CONN_GET_STATE(conn) > CONN_ALLOCED && CONN_GET_STATE(conn) ) {
DBG_INF("Connecting on a connected handle.");
if (conn->state < CONN_QUIT_SENT) {
if (CONN_GET_STATE(conn) < CONN_QUIT_SENT) {
MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CLOSE_IMPLICIT);
reconnect = TRUE;
mysqlnd_send_close(conn TSRMLS_CC);
@ -551,7 +771,7 @@ PHPAPI MYSQLND *mysqlnd_connect(MYSQLND *conn,
self_alloced = TRUE;
}
conn->state = CONN_ALLOCED;
CONN_SET_STATE(conn, CONN_ALLOCED);
conn->net.packet_no = 0;
if (conn->options.timeout_connect) {
@ -663,7 +883,7 @@ PHPAPI MYSQLND *mysqlnd_connect(MYSQLND *conn,
conn->scramble = auth_packet->server_scramble_buf = mnd_pemalloc(SCRAMBLE_LENGTH, conn->persistent);
memcpy(auth_packet->server_scramble_buf, greet_packet.scramble_buf, SCRAMBLE_LENGTH);
if (!PACKET_WRITE(auth_packet, conn)) {
conn->state = CONN_QUIT_SENT;
CONN_SET_STATE(conn, CONN_QUIT_SENT);
SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
goto err;
}
@ -687,7 +907,7 @@ PHPAPI MYSQLND *mysqlnd_connect(MYSQLND *conn,
}
}
} else {
conn->state = CONN_READY;
CONN_SET_STATE(conn, CONN_READY);
conn->user = pestrdup(user, conn->persistent);
conn->passwd = pestrndup(passwd, passwd_len, conn->persistent);
@ -759,6 +979,23 @@ PHPAPI MYSQLND *mysqlnd_connect(MYSQLND *conn,
DBG_INF("unicode set");
}
#endif
#ifdef MYSQLND_THREADED
{
pthread_t th;
pthread_attr_t connection_attrib;
conn->tsrm_ls = tsrm_ls;
pthread_attr_init(&connection_attrib);
pthread_attr_setdetachstate(&connection_attrib, PTHREAD_CREATE_DETACHED);
conn->thread_is_running = TRUE;
if (pthread_create(&th, &connection_attrib, _mysqlnd_fetch_thread, (void*)conn)) {
conn->thread_is_running = FALSE;
}
}
#endif
DBG_RETURN(conn);
}
err:
@ -1081,7 +1318,7 @@ MYSQLND_METHOD(mysqlnd_conn, kill)(MYSQLND *conn, unsigned int pid TSRMLS_DC)
SET_ERROR_AFF_ROWS(conn);
} else if (PASS == (ret = mysqlnd_simple_command(conn, COM_PROCESS_KILL, buff,
4, PROT_LAST, FALSE TSRMLS_CC))) {
conn->state = CONN_QUIT_SENT;
CONN_SET_STATE(conn, CONN_QUIT_SENT);
}
DBG_RETURN(ret);
}
@ -1154,7 +1391,7 @@ MYSQLND_METHOD(mysqlnd_conn, shutdown)(MYSQLND * const conn, unsigned long level
/* {{{ mysqlnd_send_close */
enum_func_status
static enum_func_status
mysqlnd_send_close(MYSQLND * conn TSRMLS_DC)
{
enum_func_status ret = PASS;
@ -1163,7 +1400,7 @@ mysqlnd_send_close(MYSQLND * conn TSRMLS_DC)
DBG_INF_FMT("conn=%llu conn->net.stream->abstract=%p",
conn->thread_id, conn->net.stream? conn->net.stream->abstract:NULL);
switch (conn->state) {
switch (CONN_GET_STATE(conn)) {
case CONN_READY:
DBG_INF("Connection clean, sending COM_QUIT");
ret = mysqlnd_simple_command(conn, COM_QUIT, NULL, 0, PROT_LAST,
@ -1199,7 +1436,7 @@ mysqlnd_send_close(MYSQLND * conn TSRMLS_DC)
We hold one reference, and every other object which needs the
connection does increase it by 1.
*/
conn->state = CONN_QUIT_SENT;
CONN_SET_STATE(conn, CONN_QUIT_SENT);
DBG_RETURN(ret);
}
@ -1236,7 +1473,6 @@ MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn, enum_connection_close_type c
ret = conn->m->free_reference(conn TSRMLS_CC);
DBG_RETURN(ret);
}
/* }}} */
@ -1273,6 +1509,46 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_conn, free_reference)(MYSQLND * const conn TSRMLS
/* }}} */
/* {{{ mysqlnd_conn::get_state */
#ifdef MYSQLND_THREADED
static enum mysqlnd_connection_state
MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_state)(MYSQLND * const conn TSRMLS_DC)
{
enum mysqlnd_connection_state state;
DBG_ENTER("mysqlnd_conn::get_state");
tsrm_mutex_lock(conn->LOCK_state);
state = conn->state;
tsrm_mutex_unlock(conn->LOCK_state);
DBG_RETURN(state);
}
#else
static enum mysqlnd_connection_state
MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_state)(MYSQLND * const conn TSRMLS_DC)
{
DBG_ENTER("mysqlnd_conn::get_state");
DBG_RETURN(conn->state);
}
#endif
/* }}} */
/* {{{ mysqlnd_conn::set_state */
static void
MYSQLND_METHOD_PRIVATE(mysqlnd_conn, set_state)(MYSQLND * const conn, enum mysqlnd_connection_state new_state TSRMLS_DC)
{
DBG_ENTER("mysqlnd_conn::set_state");
#ifdef MYSQLND_THREADED
tsrm_mutex_lock(conn->LOCK_state);
#endif
conn->state = new_state;
#ifdef MYSQLND_THREADED
tsrm_mutex_unlock(conn->LOCK_state);
#endif
DBG_VOID_RETURN;
}
/* }}} */
/* {{{ mysqlnd_conn::field_count */
static unsigned int
MYSQLND_METHOD(mysqlnd_conn, field_count)(const MYSQLND * const conn)
@ -1420,7 +1696,7 @@ MYSQLND_METHOD(mysqlnd_conn, next_result)(MYSQLND * const conn TSRMLS_DC)
DBG_ENTER("mysqlnd_conn::next_result");
DBG_INF_FMT("conn=%llu", conn->thread_id);
if (conn->state != CONN_NEXT_RESULT_PENDING) {
if (CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING) {
DBG_RETURN(FAIL);
}
@ -1433,7 +1709,7 @@ MYSQLND_METHOD(mysqlnd_conn, next_result)(MYSQLND * const conn TSRMLS_DC)
if (FAIL == (ret = mysqlnd_query_read_result_set_header(conn, NULL TSRMLS_CC))) {
DBG_ERR_FMT("Serious error. %s::%d", __FILE__, __LINE__);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Serious error. PID=%d", getpid());
conn->state = CONN_QUIT_SENT;
CONN_SET_STATE(conn, CONN_QUIT_SENT);
}
DBG_RETURN(ret);
@ -1710,7 +1986,7 @@ MYSQLND_METHOD(mysqlnd_conn, use_result)(MYSQLND * const conn TSRMLS_DC)
}
/* Nothing to store for UPSERT/LOAD DATA */
if (conn->last_query_type != QUERY_SELECT || conn->state != CONN_FETCHING_DATA) {
if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
mysqlnd_out_of_sync);
DBG_ERR("Command out of sync");
@ -1743,7 +2019,7 @@ MYSQLND_METHOD(mysqlnd_conn, store_result)(MYSQLND * const conn TSRMLS_DC)
}
/* Nothing to store for UPSERT/LOAD DATA*/
if (conn->last_query_type != QUERY_SELECT || conn->state != CONN_FETCHING_DATA) {
if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
mysqlnd_out_of_sync);
DBG_ERR("Command out of sync");
@ -1761,6 +2037,44 @@ MYSQLND_METHOD(mysqlnd_conn, store_result)(MYSQLND * const conn TSRMLS_DC)
/* }}} */
/* {{{ mysqlnd_conn::background_store_result */
MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_conn, background_store_result)(MYSQLND * const conn TSRMLS_DC)
{
MYSQLND_RES *result;
DBG_ENTER("mysqlnd_conn::store_result");
DBG_INF_FMT("conn=%llu", conn->thread_id);
if (!conn->current_result) {
DBG_RETURN(NULL);
}
/* Nothing to store for UPSERT/LOAD DATA*/
if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
mysqlnd_out_of_sync);
DBG_ERR("Command out of sync");
DBG_RETURN(NULL);
}
MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_BUFFERED_SETS);
result = conn->current_result;
result = result->m.background_store_result(result, conn, FALSE TSRMLS_CC);
/*
Should be here, because current_result is used by the fetching thread to get data info
The thread is contacted in mysqlnd_res::background_store_result().
*/
conn->current_result = NULL;
DBG_RETURN(result);
}
/* }}} */
/* {{{ mysqlnd_conn::get_connection_stats */
static void
MYSQLND_METHOD(mysqlnd_conn, get_connection_stats)(const MYSQLND * const conn,
@ -1784,6 +2098,7 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn)
MYSQLND_METHOD(mysqlnd_conn, query),
MYSQLND_METHOD(mysqlnd_conn, use_result),
MYSQLND_METHOD(mysqlnd_conn, store_result),
MYSQLND_METHOD(mysqlnd_conn, background_store_result),
MYSQLND_METHOD(mysqlnd_conn, next_result),
MYSQLND_METHOD(mysqlnd_conn, more_results),
@ -1829,6 +2144,8 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn)
MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_reference),
MYSQLND_METHOD_PRIVATE(mysqlnd_conn, free_reference),
MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_state),
MYSQLND_METHOD_PRIVATE(mysqlnd_conn, set_state),
MYSQLND_CLASS_METHODS_END;
@ -1846,6 +2163,15 @@ PHPAPI MYSQLND *_mysqlnd_init(zend_bool persistent TSRMLS_DC)
ret->m = & mysqlnd_mysqlnd_conn_methods;
ret->m->get_reference(ret);
#ifdef MYSQLND_THREADED
ret->LOCK_state = tsrm_mutex_alloc();
pthread_mutex_init(&ret->LOCK_work, NULL);
pthread_cond_init(&ret->COND_work, NULL);
pthread_cond_init(&ret->COND_work_done, NULL);
pthread_cond_init(&ret->COND_thread_ended, NULL);
#endif
DBG_RETURN(ret);
}
/* }}} */
@ -1985,7 +2311,7 @@ static PHP_MINIT_FUNCTION(mysqlnd)
{
REGISTER_INI_ENTRIES();
mysqlnd_library_init();
mysqlnd_library_init(TSRMLS_C);
return SUCCESS;
}
/* }}} */
@ -1995,7 +2321,7 @@ static PHP_MINIT_FUNCTION(mysqlnd)
*/
static PHP_MSHUTDOWN_FUNCTION(mysqlnd)
{
mysqlnd_library_end();
mysqlnd_library_end(TSRMLS_C);
UNREGISTER_INI_ENTRIES();
return SUCCESS;

View File

@ -29,6 +29,9 @@
/* This forces inlining of some accessor functions */
#define MYSQLND_USE_OPTIMISATIONS 0
//#define MYSQLND_THREADING
/* #define MYSQLND_STRING_TO_INT_CONVERSION */
/*
This force mysqlnd to do a single (or more depending on ammount of data)
@ -98,6 +101,7 @@ void _mysqlnd_debug(const char *mode TSRMLS_DC);
#define mysqlnd_use_result(conn) (conn)->m->use_result((conn) TSRMLS_CC)
#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_free_result(r,e_or_i) ((MYSQLND_RES*)r)->m.free_result(((MYSQLND_RES*)(r)), (e_or_i) TSRMLS_CC)
@ -242,6 +246,7 @@ PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, size
/* PS */
#define mysqlnd_stmt_init(conn) (conn)->m->stmt_init((conn) 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_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)

View File

@ -22,6 +22,7 @@
#ifndef MYSQLND_ENUM_N_DEF_H
#define MYSQLND_ENUM_N_DEF_H
#define MYSQLND_MAX_PACKET_SIZE (256L*256L*256L-1)
#define MYSQLND_ERRMSG_SIZE 512
#define MYSQLND_SQLSTATE_LENGTH 5

View File

@ -108,6 +108,7 @@
#define mysql_free_result(r) mysqlnd_free_result((r), FALSE)
#define mysql_store_result(r) mysqlnd_store_result((r))
#define mysql_use_result(r) mysqlnd_use_result((r))
#define mysql_async_store_result(r) mysqlnd_async_store_result((r))
#define mysql_thread_id(r) mysqlnd_thread_id((r))
#define mysql_get_client_info() mysqlnd_get_client_info()
#define mysql_get_client_version() mysqlnd_get_client_version()
@ -116,6 +117,6 @@
#define mysql_get_server_info(r) mysqlnd_get_server_info((r))
#define mysql_get_server_version(r) mysqlnd_get_server_version((r))
#define mysql_warning_count(r) mysqlnd_warning_count((r))
#define mysql_eof(r) (((r)->unbuf && (r)->unbuf->eof_reached) || (r)->data)
#define mysql_eof(r) (((r)->unbuf && (r)->unbuf->eof_reached) || (r)->stored_data)
#endif /* MYSQLND_LIBMYSQL_COMPAT_H */

View File

@ -156,6 +156,14 @@
#define SET_STMT_ERROR(stmt, a, b, c) SET_CLIENT_ERROR(stmt->error_info, a, b, c)
#ifdef ZTS
#define CONN_GET_STATE(c) (c)->m->get_state((c) TSRMLS_CC)
#define CONN_SET_STATE(c, s) (c)->m->set_state((c), (s) TSRMLS_CC)
#else
#define CONN_GET_STATE(c) (c)->state
#define CONN_SET_STATE(c, s) (c)->state = s
#endif
/* PS stuff */
typedef void (*ps_field_fetch_func)(zval *zv, const MYSQLND_FIELD * const field,
@ -175,6 +183,8 @@ extern struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST +
extern const char * mysqlnd_out_of_sync;
extern const char * mysqlnd_server_gone;
extern MYSQLND_MEMORY_POOL mysqlnd_memory_pool;
enum_func_status mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename, zend_bool *is_warning TSRMLS_DC);

View File

@ -83,7 +83,74 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC)
}
/* Nothing to store for UPSERT/LOAD DATA*/
if (conn->state != CONN_FETCHING_DATA ||
if (CONN_GET_STATE(conn) != CONN_FETCHING_DATA ||
stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE)
{
SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_RETURN(NULL);
}
stmt->default_rset_handler = stmt->m->store_result;
SET_EMPTY_ERROR(stmt->error_info);
SET_EMPTY_ERROR(stmt->conn->error_info);
MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_PS_BUFFERED_SETS);
result = stmt->result;
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);
/* Create room for 'next_extend' rows */
ret = mysqlnd_store_result_fetch_data(conn, result, result->meta,
TRUE, to_cache TSRMLS_CC);
if (PASS == ret) {
/* libmysql API docs say it should be so for SELECT statements */
stmt->upsert_status.affected_rows = stmt->result->stored_data->row_count;
stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;
} else {
conn->error_info = result->stored_data->error_info;
stmt->result->m.free_result_contents(stmt->result TSRMLS_CC);
mnd_efree(stmt->result);
stmt->result = NULL;
stmt->state = MYSQLND_STMT_PREPARED;
}
DBG_RETURN(result);
}
/* }}} */
/* {{{ mysqlnd_stmt::background_store_result */
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_stmt, background_store_result)(MYSQLND_STMT * const stmt TSRMLS_DC)
{
enum_func_status ret;
MYSQLND *conn = stmt->conn;
MYSQLND_RES *result;
zend_bool to_cache = FALSE;
DBG_ENTER("mysqlnd_stmt::store_result");
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
/* be compliant with libmysql - NULL will turn */
if (!stmt->field_count) {
DBG_RETURN(NULL);
}
if (stmt->cursor_exists) {
/* Silently convert buffered to unbuffered, for now */
MYSQLND_RES * res = stmt->m->use_result(stmt TSRMLS_CC);
DBG_RETURN(res);
}
/* Nothing to store for UPSERT/LOAD DATA*/
if (CONN_GET_STATE(conn) != CONN_FETCHING_DATA ||
stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE)
{
SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
@ -112,16 +179,15 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC)
}
ret = mysqlnd_store_result_fetch_data(conn, result, result->meta,
TRUE, stmt->update_max_length,
to_cache TSRMLS_CC);
TRUE, to_cache TSRMLS_CC);
if (PASS == ret) {
/* libmysql API docs say it should be so for SELECT statements */
stmt->upsert_status.affected_rows = stmt->result->data->row_count;
stmt->upsert_status.affected_rows = stmt->result->stored_data->row_count;
stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;
} else {
conn->error_info = result->data->error_info;
conn->error_info = result->stored_data->error_info;
stmt->result->m.free_result_contents(stmt->result TSRMLS_CC);
mnd_efree(stmt->result);
stmt->result = NULL;
@ -132,7 +198,6 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC)
}
/* }}} */
/* {{{ mysqlnd_stmt::get_result */
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const stmt TSRMLS_DC)
@ -155,7 +220,7 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const stmt TSRMLS_DC)
}
/* Nothing to store for UPSERT/LOAD DATA*/
if (conn->state != CONN_FETCHING_DATA || stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE) {
if (CONN_GET_STATE(conn) != CONN_FETCHING_DATA || stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE) {
SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_RETURN(NULL);
@ -170,14 +235,8 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const stmt TSRMLS_DC)
result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC);
/* Not set for SHOW statements at PREPARE stage */
if (stmt->result->conn) {
stmt->result->conn->m->free_reference(stmt->result->conn TSRMLS_CC);
stmt->result->conn = NULL; /* store result does not reference the connection */
}
if ((result = result->m.store_result(result, conn, TRUE TSRMLS_CC))) {
stmt->upsert_status.affected_rows = result->data->row_count;
stmt->upsert_status.affected_rows = result->stored_data->row_count;
stmt->state = MYSQLND_STMT_PREPARED;
result->type = MYSQLND_RES_PS_BUF;
} else {
@ -458,7 +517,7 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC)
if (ret == FAIL) {
stmt->error_info = conn->error_info;
stmt->upsert_status.affected_rows = conn->upsert_status.affected_rows;
if (conn->state == CONN_QUIT_SENT) {
if (CONN_GET_STATE(conn) == CONN_QUIT_SENT) {
/* close the statement here, the connection has been closed */
}
} else {
@ -500,7 +559,7 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC)
if (stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS) {
stmt->cursor_exists = TRUE;
conn->state = CONN_READY;
CONN_SET_STATE(conn, CONN_READY);
/* Only cursor read */
stmt->default_rset_handler = stmt->m->use_result;
DBG_INF("use_result");
@ -539,17 +598,45 @@ mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int f
{
unsigned int i;
MYSQLND_STMT *stmt = (MYSQLND_STMT *) param;
uint field_count = result->meta->field_count;
MYSQLND_RES_BUFFERED *set = result->stored_data;
DBG_ENTER("mysqlnd_fetch_stmt_row_buffered");
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
/* If we haven't read everything */
if (result->data->data_cursor &&
(result->data->data_cursor - result->data->data) < result->data->row_count)
if (set->data_cursor &&
(set->data_cursor - set->data) < (set->row_count * field_count))
{
/* The user could have skipped binding - don't crash*/
if (stmt->result_bind) {
zval **current_row = *result->data->data_cursor;
zval **current_row = set->data_cursor;
if (NULL == current_row[0]) {
set->initialized_rows++;
uint64 row_num = (set->data_cursor - set->data) / field_count;
result->m.row_decoder(set->row_buffers[row_num],
current_row,
result->meta->field_count,
result->meta->fields,
result->conn TSRMLS_CC);
if (stmt->update_max_length) {
for (i = 0; i < result->field_count; i++) {
/*
NULL fields are 0 length, 0 is not more than 0
String of zero size, definitely can't be the next max_length.
Thus for NULL and zero-length we are quite efficient.
*/
if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
unsigned long len = Z_STRLEN_P(current_row[i]);
if (result->meta->fields[i].max_length < len) {
result->meta->fields[i].max_length = len;
}
}
}
}
}
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
@ -578,13 +665,13 @@ mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int f
}
}
}
result->data->data_cursor++;
set->data_cursor += field_count;
*fetched_anything = TRUE;
/* buffered result sets don't have a connection */
MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_PS_BUF);
DBG_INF("row fetched");
} else {
result->data->data_cursor = NULL;
set->data_cursor = NULL;
*fetched_anything = FALSE;
DBG_INF("no more data");
}
@ -612,7 +699,7 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int
DBG_INF("eof reached");
DBG_RETURN(PASS);
}
if (result->conn->state != CONN_FETCHING_DATA) {
if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) {
SET_CLIENT_ERROR(result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_ERR("command out of sync");
@ -638,6 +725,12 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int
row_packet->fields = NULL;
row_packet->row_buffer = NULL;
result->m.row_decoder(result->unbuf->last_row_buffer,
result->unbuf->last_row_data,
row_packet->field_count,
row_packet->fields_metadata,
result->conn TSRMLS_CC);
for (i = 0; i < field_count; i++) {
if (stmt->result_bind[i].bound == TRUE) {
zval *data = result->unbuf->last_row_data[i];
@ -676,7 +769,7 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int
the bound variables. Thus we need to do part of what it does or Zend will
report leaks.
*/
mnd_efree(row_packet->row_buffer);
row_packet->row_buffer->free_chunk(row_packet->row_buffer, TRUE TSRMLS_CC);
row_packet->row_buffer = NULL;
}
} else if (ret == FAIL) {
@ -685,7 +778,7 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int
stmt->error_info = row_packet->error_info;
}
*fetched_anything = FALSE;
result->conn->state = CONN_READY;
CONN_SET_STATE(result->conn, CONN_READY);
result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
} else if (row_packet->eof) {
DBG_INF("EOF");
@ -698,9 +791,9 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int
destroying the result object
*/
if (result->conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) {
result->conn->state = CONN_NEXT_RESULT_PENDING;
CONN_SET_STATE(result->conn, CONN_NEXT_RESULT_PENDING);
} else {
result->conn->state = CONN_READY;
CONN_SET_STATE(result->conn, CONN_READY);
}
*fetched_anything = FALSE;
}
@ -722,8 +815,8 @@ MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT *stmt TSRMLS_DC)
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
if (!stmt->field_count ||
(!stmt->cursor_exists && conn->state != CONN_FETCHING_DATA) ||
(stmt->cursor_exists && conn->state != CONN_READY) ||
(!stmt->cursor_exists && CONN_GET_STATE(conn) != CONN_FETCHING_DATA) ||
(stmt->cursor_exists && CONN_GET_STATE(conn) != CONN_READY) ||
(stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE))
{
SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
@ -740,7 +833,6 @@ MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT *stmt TSRMLS_DC)
result->m.use_result(stmt->result, TRUE TSRMLS_CC);
result->m.fetch_row = stmt->cursor_exists? mysqlnd_fetch_stmt_row_cursor:
mysqlnd_stmt_fetch_row_unbuffered;
stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;
DBG_INF_FMT("%p", result);
@ -798,9 +890,17 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla
result->unbuf->last_row_data = row_packet->fields;
result->unbuf->last_row_buffer = row_packet->row_buffer;
row_packet->fields = NULL;
row_packet->row_buffer = NULL;
if (!row_packet->skip_extraction) {
result->m.row_decoder(result->unbuf->last_row_buffer,
result->unbuf->last_row_data,
row_packet->field_count,
row_packet->fields_metadata,
result->conn TSRMLS_CC);
/* If no result bind, do nothing. We consumed the data */
for (i = 0; i < field_count; i++) {
if (stmt->result_bind[i].bound == TRUE) {
@ -833,7 +933,7 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla
/* We asked for one row, the next one should be EOF, eat it */
ret = PACKET_READ(row_packet, result->conn);
if (row_packet->row_buffer) {
mnd_efree(row_packet->row_buffer);
row_packet->row_buffer->free_chunk(row_packet->row_buffer, TRUE TSRMLS_CC);
row_packet->row_buffer = NULL;
}
MYSQLND_INC_CONN_STATISTIC(&stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_CURSOR);
@ -961,7 +1061,7 @@ MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const stmt TSRMLS_DC)
/* Now the line should be free, if it wasn't */
int4store(cmd_buf, stmt->stmt_id);
if (conn->state == CONN_READY &&
if (CONN_GET_STATE(conn) == CONN_READY &&
FAIL == (ret = mysqlnd_simple_command(conn, COM_STMT_RESET, (char *)cmd_buf,
sizeof(cmd_buf), PROT_OK_PACKET,
FALSE TSRMLS_CC))) {
@ -1026,7 +1126,7 @@ MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const stmt, unsigned
one by one to the wire.
*/
if (conn->state == CONN_READY) {
if (CONN_GET_STATE(conn) == CONN_READY) {
stmt->param_bind[param_no].flags |= MYSQLND_PARAM_BIND_BLOB_USED;
cmd_buf = mnd_emalloc(packet_len = STMT_ID_LENGTH + 2 + length);
@ -1149,6 +1249,8 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_param)(MYSQLND_STMT * const stmt,
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param)(MYSQLND_STMT * const stmt TSRMLS_DC)
{
unsigned int i = 0;
DBG_ENTER("mysqlnd_stmt::refresh_bind_param");
DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count);
@ -1332,13 +1434,15 @@ MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const stmt TSRMLS_D
DBG_ENTER("mysqlnd_stmt::result_metadata");
DBG_INF_FMT("stmt=%u field_count=%u", stmt->stmt_id, stmt->field_count);
if (!stmt->field_count || !stmt->conn || !stmt->result ||
!stmt->result->meta)
{
if (!stmt->field_count || !stmt->conn || !stmt->result || !stmt->result->meta) {
DBG_INF("NULL");
DBG_RETURN(NULL);
}
if (stmt->update_max_length && stmt->result->stored_data) {
/* stored result, we have to update the max_length before we clone the meta data :( */
mysqlnd_res_initialize_result_set_rest(stmt->result TSRMLS_CC);
}
/*
TODO: This implementation is kind of a hack,
find a better way to do it. In different functions I have put
@ -1472,7 +1576,7 @@ MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC)
stmt->state = MYSQLND_STMT_PREPARED;
/* Line is free! */
stmt->conn->state = CONN_READY;
CONN_SET_STATE(stmt->conn, CONN_READY);
DBG_RETURN(PASS);
}
@ -1622,7 +1726,7 @@ MYSQLND_METHOD(mysqlnd_stmt, close)(MYSQLND_STMT * const stmt, zend_bool implici
STAT_FREE_RESULT_EXPLICIT);
int4store(cmd_buf, stmt->stmt_id);
if (conn->state == CONN_READY &&
if (CONN_GET_STATE(conn) == CONN_READY &&
FAIL == mysqlnd_simple_command(conn, COM_STMT_CLOSE, (char *)cmd_buf, sizeof(cmd_buf),
PROT_LAST /* COM_STMT_CLOSE doesn't send an OK packet*/,
FALSE TSRMLS_CC)) {
@ -1679,6 +1783,7 @@ struct st_mysqlnd_stmt_methods mysqlnd_stmt_methods = {
MYSQLND_METHOD(mysqlnd_stmt, execute),
MYSQLND_METHOD(mysqlnd_stmt, use_result),
MYSQLND_METHOD(mysqlnd_stmt, store_result),
MYSQLND_METHOD(mysqlnd_stmt, background_store_result),
MYSQLND_METHOD(mysqlnd_stmt, get_result),
MYSQLND_METHOD(mysqlnd_stmt, free_result),
MYSQLND_METHOD(mysqlnd_stmt, data_seek),

File diff suppressed because it is too large Load Diff

View File

@ -31,11 +31,14 @@ enum_func_status
mysqlnd_store_result_fetch_data(MYSQLND * const conn, MYSQLND_RES *result,
MYSQLND_RES_METADATA *meta,
zend_bool binary_protocol,
zend_bool update_max_length,
zend_bool to_cache TSRMLS_DC);
enum_func_status mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC);
void mysqlnd_res_initialize_result_set_rest(MYSQLND_RES * const result TSRMLS_DC);
enum_func_status mysqlnd_background_store_result_fetch_data(MYSQLND_RES *result TSRMLS_DC);
#endif /* MYSQLND_RESULT_H */
/*

View File

@ -421,8 +421,8 @@ MYSQLND_RES_METADATA *mysqlnd_result_meta_init(unsigned int field_count TSRMLS_D
/* +1 is to have empty marker at the end */
ret = mnd_ecalloc(1, sizeof(MYSQLND_RES_METADATA));
ret->field_count = field_count;
ret->fields = ecalloc(field_count + 1, sizeof(MYSQLND_FIELD));
ret->zend_hash_keys = ecalloc(field_count, sizeof(struct mysqlnd_field_hash_key));
ret->fields = mnd_ecalloc(field_count + 1, sizeof(MYSQLND_FIELD));
ret->zend_hash_keys = mnd_ecalloc(field_count, sizeof(struct mysqlnd_field_hash_key));
ret->m = & mysqlnd_mysqlnd_res_meta_methods;
DBG_INF_FMT("meta=%p", ret);

View File

@ -23,6 +23,39 @@
#ifndef MYSQLND_STRUCTS_H
#define MYSQLND_STRUCTS_H
typedef struct st_mysqlnd_memory_pool MYSQLND_MEMORY_POOL;
typedef struct st_mysqlnd_memory_pool_chunk MYSQLND_MEMORY_POOL_CHUNK;
typedef struct st_mysqlnd_memory_pool_chunk_llist MYSQLND_MEMORY_POOL_CHUNK_LLIST;
#define MYSQLND_MEMORY_POOL_CHUNK_LIST_SIZE 100
struct st_mysqlnd_memory_pool
{
zend_uchar *arena;
uint refcount;
uint arena_size;
uint free_size;
MYSQLND_MEMORY_POOL_CHUNK* free_chunk_list[MYSQLND_MEMORY_POOL_CHUNK_LIST_SIZE];
uint free_chunk_list_elements;
MYSQLND_MEMORY_POOL_CHUNK* (*get_chunk)(MYSQLND_MEMORY_POOL * pool, uint size TSRMLS_DC);
void (*free_contents)(MYSQLND_MEMORY_POOL * pool TSRMLS_DC);
};
struct st_mysqlnd_memory_pool_chunk
{
uint64 app;
MYSQLND_MEMORY_POOL *pool;
zend_uchar *ptr;
uint size;
void (*resize_chunk)(MYSQLND_MEMORY_POOL_CHUNK * chunk, uint size TSRMLS_DC);
void (*free_chunk)(MYSQLND_MEMORY_POOL_CHUNK * chunk, zend_bool cache_it TSRMLS_DC);
zend_bool from_pool;
};
typedef struct st_mysqlnd_cmd_buffer
{
zend_uchar *buffer;
@ -162,6 +195,7 @@ typedef struct st_mysqlnd_result_bind MYSQLND_RESULT_BIND;
typedef struct st_mysqlnd_result_metadata MYSQLND_RES_METADATA;
typedef struct st_mysqlnd_buffered_result MYSQLND_RES_BUFFERED;
typedef struct st_mysqlnd_background_buffered_result MYSQLND_RES_BG_BUFFERED;
typedef struct st_mysqlnd_unbuffered_result MYSQLND_RES_UNBUFFERED;
typedef struct st_mysqlnd_debug MYSQLND_DEBUG;
@ -205,6 +239,7 @@ struct st_mysqlnd_conn_methods
enum_func_status (*query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC);
MYSQLND_RES * (*use_result)(MYSQLND * const conn TSRMLS_DC);
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);
@ -249,6 +284,8 @@ struct st_mysqlnd_conn_methods
MYSQLND * (*get_reference)(MYSQLND * const conn);
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);
};
@ -260,6 +297,7 @@ struct st_mysqlnd_res_methods
MYSQLND_RES * (*use_result)(MYSQLND_RES * const result, zend_bool ps_protocol TSRMLS_DC);
MYSQLND_RES * (*store_result)(MYSQLND_RES * result, MYSQLND * const conn, zend_bool ps TSRMLS_DC);
MYSQLND_RES * (*background_store_result)(MYSQLND_RES * result, MYSQLND * const conn, zend_bool ps TSRMLS_DC);
void (*fetch_into)(MYSQLND_RES *result, unsigned int flags, zval *return_value, enum_mysqlnd_extension ext TSRMLS_DC ZEND_FILE_LINE_DC);
MYSQLND_ROW_C (*fetch_row_c)(MYSQLND_RES *result TSRMLS_DC);
void (*fetch_all)(MYSQLND_RES *result, unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC);
@ -271,7 +309,7 @@ struct st_mysqlnd_res_methods
MYSQLND_FIELD_OFFSET (*seek_field)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET field_offset);
MYSQLND_FIELD_OFFSET (*field_tell)(const MYSQLND_RES * const result);
MYSQLND_FIELD * (*fetch_field)(MYSQLND_RES * const result TSRMLS_DC);
MYSQLND_FIELD * (*fetch_field_direct)(const MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC);
MYSQLND_FIELD * (*fetch_field_direct)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC);
enum_func_status (*read_result_metadata)(MYSQLND_RES *result, MYSQLND *conn TSRMLS_DC);
unsigned long * (*fetch_lengths)(MYSQLND_RES * const result);
@ -279,6 +317,9 @@ struct st_mysqlnd_res_methods
enum_func_status (*free_result)(MYSQLND_RES * result, zend_bool implicit TSRMLS_DC);
void (*free_result_internal)(MYSQLND_RES *result TSRMLS_DC);
void (*free_result_contents)(MYSQLND_RES *result TSRMLS_DC);
/* for decoding - binary or text protocol */
void (*row_decoder)(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, uint field_count, MYSQLND_FIELD *fields_metadata, MYSQLND *conn TSRMLS_DC);
};
@ -299,6 +340,7 @@ struct st_mysqlnd_stmt_methods
enum_func_status (*execute)(MYSQLND_STMT * const stmt TSRMLS_DC);
MYSQLND_RES * (*use_result)(MYSQLND_STMT * const stmt 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);
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);
@ -405,6 +447,20 @@ struct st_mysqlnd_connection
/* stats */
MYSQLND_STATS stats;
#ifdef ZTS
MUTEX_T LOCK_state;
pthread_cond_t COND_work_done;
pthread_mutex_t LOCK_work;
pthread_cond_t COND_work;
pthread_cond_t COND_thread_ended;
zend_bool thread_is_running;
zend_bool thread_killed;
void *** tsrm_ls;
#endif
struct st_mysqlnd_conn_methods *m;
};
@ -436,18 +492,43 @@ struct st_mysqlnd_result_metadata
};
struct st_mysqlnd_buffered_result
struct st_mysqlnd_background_buffered_result
{
zval ***data;
uint64 data_size;
zval ***data_cursor;
zend_uchar **row_buffers;
MYSQLND_MEMORY_POOL_CHUNK **row_buffers;
uint64 row_count;
uint64 initialized_rows;
zend_bool persistent;
MYSQLND_QCACHE *qcache;
unsigned int references;
zend_bool decode_in_foreground;
#ifdef ZTS
zend_bool bg_fetch_finished;
MUTEX_T LOCK;
#endif
mysqlnd_error_info error_info;
mysqlnd_upsert_status upsert_status;
};
struct st_mysqlnd_buffered_result
{
zval **data;
zval **data_cursor;
MYSQLND_MEMORY_POOL_CHUNK **row_buffers;
uint64 row_count;
uint64 initialized_rows;
zend_bool persistent;
MYSQLND_QCACHE *qcache;
unsigned int references;
zend_bool async_invalid;
mysqlnd_error_info error_info;
};
@ -456,7 +537,7 @@ struct st_mysqlnd_unbuffered_result
{
/* For unbuffered (both normal and PS) */
zval **last_row_data;
zend_uchar *last_row_buffer;
MYSQLND_MEMORY_POOL_CHUNK *last_row_buffer;
uint64 row_count;
zend_bool eof_reached;
@ -475,9 +556,9 @@ struct st_mysqlnd_res
MYSQLND_RES_METADATA *meta;
/* To be used with store_result() - both normal and PS */
MYSQLND_RES_BUFFERED *data;
MYSQLND_RES_UNBUFFERED *unbuf;
MYSQLND_RES_BUFFERED *stored_data;
MYSQLND_RES_BG_BUFFERED *bg_stored_data;
MYSQLND_RES_UNBUFFERED *unbuf;
/*
Column lengths of current row - both buffered and unbuffered.

View File

@ -43,12 +43,11 @@
#define MYSQLND_DUMP_HEADER_N_BODY2
#define MYSQLND_DUMP_HEADER_N_BODY_FULL2
#define MYSQLND_MAX_PACKET_SIZE (256L*256L*256L-1)
#define PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_size, packet_type) \
{ \
if (FAIL == mysqlnd_read_header((conn), &((packet)->header) TSRMLS_CC)) {\
conn->state = CONN_QUIT_SENT; \
CONN_SET_STATE(conn, CONN_QUIT_SENT); \
SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysqlnd_server_gone); \
DBG_ERR_FMT("Can't read %s's header", (packet_type)); \
@ -60,7 +59,7 @@
}\
if (!mysqlnd_read_body((conn), (buf), \
MIN((buf_size), (packet)->header.size) TSRMLS_CC)) { \
conn->state = CONN_QUIT_SENT; \
CONN_SET_STATE(conn, CONN_QUIT_SENT); \
SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysqlnd_server_gone); \
DBG_ERR_FMT("Empty %s packet body", (packet_type)); \
@ -490,7 +489,7 @@ size_t mysqlnd_read_body(MYSQLND *conn, zend_uchar *buf, size_t size TSRMLS_DC)
net->stream->chunk_size = MIN(size, conn->options.net_read_buffer_size);
do {
size -= (ret = php_stream_read(net->stream, p, size));
if (size || iter++) {
if (size > 0 || iter++) {
DBG_INF_FMT("read=%d buf=%p p=%p chunk_size=%d left=%d",
ret, buf, p , net->stream->chunk_size, size);
}
@ -1234,13 +1233,13 @@ void php_mysqlnd_rset_field_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
static enum_func_status
php_mysqlnd_read_row_ex(MYSQLND *conn, zend_uchar **buf, int buf_size,
size_t *data_size, zend_bool persistent_alloc,
php_mysqlnd_read_row_ex(MYSQLND *conn, MYSQLND_MEMORY_POOL_CHUNK **buffer,
uint64 *data_size, zend_bool persistent_alloc,
unsigned int prealloc_more_bytes TSRMLS_DC)
{
enum_func_status ret = PASS;
mysqlnd_packet_header header;
zend_uchar *new_buf = NULL, *p = *buf;
zend_uchar *p = NULL;
zend_bool first_iteration = TRUE;
DBG_ENTER("php_mysqlnd_read_row_ex");
@ -1262,13 +1261,14 @@ php_mysqlnd_read_row_ex(MYSQLND *conn, zend_uchar **buf, int buf_size,
*data_size += header.size;
if (first_iteration && header.size > buf_size) {
if (first_iteration) {
first_iteration = FALSE;
/*
We need a trailing \0 for the last string, in case of text-mode,
to be able to implement read-only variables. Thus, we add + 1.
*/
p = new_buf = mnd_pemalloc(*data_size + 1, persistent_alloc);
*buffer = mysqlnd_memory_pool.get_chunk(&mysqlnd_memory_pool, *data_size + 1 TSRMLS_CC);
p = (*buffer)->ptr;
} else if (!first_iteration) {
/* Empty packet after MYSQLND_MAX_PACKET_SIZE packet. That's ok, break */
if (!header.size) {
@ -1281,9 +1281,9 @@ php_mysqlnd_read_row_ex(MYSQLND *conn, zend_uchar **buf, int buf_size,
We need a trailing \0 for the last string, in case of text-mode,
to be able to implement read-only variables.
*/
new_buf = mnd_perealloc(new_buf, *data_size + 1, persistent_alloc);
(*buffer)->resize_chunk((*buffer), *data_size + 1 TSRMLS_CC);
/* The position could have changed, recalculate */
p = new_buf + (*data_size - header.size);
p = (*buffer)->ptr + (*data_size - header.size);
}
if (!mysqlnd_read_body(conn, p, header.size TSRMLS_CC)) {
@ -1297,8 +1297,9 @@ php_mysqlnd_read_row_ex(MYSQLND *conn, zend_uchar **buf, int buf_size,
break;
}
}
if (ret == PASS && new_buf) {
*buf = new_buf;
if (ret == FAIL) {
(*buffer)->free_chunk((*buffer), TRUE TSRMLS_CC);
*buffer = NULL;
}
*data_size -= prealloc_more_bytes;
DBG_RETURN(ret);
@ -1306,11 +1307,11 @@ php_mysqlnd_read_row_ex(MYSQLND *conn, zend_uchar **buf, int buf_size,
/* {{{ php_mysqlnd_rowp_read_binary_protocol */
static
void php_mysqlnd_rowp_read_binary_protocol(php_mysql_packet_row *packet, MYSQLND *conn,
zend_uchar *p, size_t data_size TSRMLS_DC)
void php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
uint field_count, MYSQLND_FIELD *fields_metadata, MYSQLND *conn TSRMLS_DC)
{
unsigned int i;
int i;
zend_uchar *p = row_buffer->ptr;
zend_uchar *null_ptr, bit;
zval **current_field, **end_field, **start_field;
zend_bool as_unicode = conn->options.numeric_and_datetime_as_unicode;
@ -1319,14 +1320,14 @@ void php_mysqlnd_rowp_read_binary_protocol(php_mysql_packet_row *packet, MYSQLND
DBG_ENTER("php_mysqlnd_rowp_read_binary_protocol");
end_field = (current_field = start_field = packet->fields) + packet->field_count;
end_field = (current_field = start_field = fields) + field_count;
/* skip the first byte, not 0xFE -> 0x0, status */
p++;
null_ptr= p;
p += (packet->field_count + 9)/8; /* skip null bits */
bit = 4; /* first 2 bits are reserved */
p += (field_count + 9)/8; /* skip null bits */
bit = 4; /* first 2 bits are reserved */
for (i = 0; current_field < end_field; current_field++, i++) {
#if 1
@ -1344,8 +1345,8 @@ void php_mysqlnd_rowp_read_binary_protocol(php_mysql_packet_row *packet, MYSQLND
if (*null_ptr & bit) {
ZVAL_NULL(*current_field);
} else {
enum_mysqlnd_field_types type = packet->fields_metadata[i].type;
mysqlnd_ps_fetch_functions[type].func(*current_field, &packet->fields_metadata[i],
enum_mysqlnd_field_types type = fields_metadata[i].type;
mysqlnd_ps_fetch_functions[type].func(*current_field, &fields_metadata[i],
0, &p, as_unicode TSRMLS_CC);
}
if (!((bit<<=1) & 255)) {
@ -1353,8 +1354,6 @@ void php_mysqlnd_rowp_read_binary_protocol(php_mysql_packet_row *packet, MYSQLND
null_ptr++;
}
}
/* Normal queries: The buffer has one more byte at the end, because we need it */
packet->row_buffer[data_size] = '\0';
DBG_VOID_RETURN;
}
@ -1362,14 +1361,15 @@ void php_mysqlnd_rowp_read_binary_protocol(php_mysql_packet_row *packet, MYSQLND
/* {{{ php_mysqlnd_rowp_read_text_protocol */
static
void php_mysqlnd_rowp_read_text_protocol(php_mysql_packet_row *packet, MYSQLND *conn,
zend_uchar *p, size_t data_size TSRMLS_DC)
void php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
uint field_count, MYSQLND_FIELD *fields_metadata, MYSQLND *conn TSRMLS_DC)
{
unsigned int i;
zend_bool last_field_was_string;
int i;
zend_bool last_field_was_string = FALSE;
zval **current_field, **end_field, **start_field;
zend_uchar *bit_area = packet->row_buffer + data_size + 1; /* we allocate from here */
zend_uchar *p = row_buffer->ptr;
size_t data_size = row_buffer->app;
zend_uchar *bit_area = (zend_uchar*) row_buffer->ptr + data_size + 1; /* we allocate from here */
zend_bool as_unicode = conn->options.numeric_and_datetime_as_unicode;
#ifdef MYSQLND_STRING_TO_INT_CONVERSION
zend_bool as_int = conn->options.int_and_year_as_int;
@ -1377,7 +1377,7 @@ void php_mysqlnd_rowp_read_text_protocol(php_mysql_packet_row *packet, MYSQLND *
DBG_ENTER("php_mysqlnd_rowp_read_text_protocol");
end_field = (current_field = start_field = packet->fields) + packet->field_count;
end_field = (current_field = start_field = fields) + field_count;
for (i = 0; current_field < end_field; current_field++, i++) {
/* Don't reverse the order. It is significant!*/
void *obj;
@ -1418,7 +1418,7 @@ void php_mysqlnd_rowp_read_text_protocol(php_mysql_packet_row *packet, MYSQLND *
} else {
#if PHP_MAJOR_VERSION >= 6 || defined(MYSQLND_STRING_TO_INT_CONVERSION)
struct st_mysqlnd_perm_bind perm_bind =
mysqlnd_ps_fetch_functions[packet->fields_metadata[i].type];
mysqlnd_ps_fetch_functions[fields_metadata[i].type];
#endif
#ifdef MYSQLND_STRING_TO_INT_CONVERSION
@ -1453,7 +1453,7 @@ void php_mysqlnd_rowp_read_text_protocol(php_mysql_packet_row *packet, MYSQLND *
*(p + len) = save;
} else
#endif
if (packet->fields_metadata[i].type == MYSQL_TYPE_BIT) {
if (fields_metadata[i].type == MYSQL_TYPE_BIT) {
/*
BIT fields are specially handled. As they come as bit mask, we have
to convert it to human-readable representation. As the bits take
@ -1464,7 +1464,7 @@ void php_mysqlnd_rowp_read_text_protocol(php_mysql_packet_row *packet, MYSQLND *
Definitely not nice, _hackish_ :(, but works.
*/
zend_uchar *start = bit_area;
ps_fetch_from_1_to_8_bytes(*current_field, &(packet->fields_metadata[i]),
ps_fetch_from_1_to_8_bytes(*current_field, &(fields_metadata[i]),
0, &p, as_unicode, len TSRMLS_CC);
/*
We have advanced in ps_fetch_from_1_to_8_bytes. We should go back because
@ -1532,7 +1532,7 @@ void php_mysqlnd_rowp_read_text_protocol(php_mysql_packet_row *packet, MYSQLND *
which will make with this `if` an `else if`.
*/
if ((perm_bind.is_possibly_blob == TRUE &&
packet->fields_metadata[i].charsetnr == MYSQLND_BINARY_CHARSET_NR) ||
fields_metadata[i].charsetnr == MYSQLND_BINARY_CHARSET_NR) ||
(!as_unicode && perm_bind.can_ret_as_str_in_uni == TRUE))
{
/* BLOB - no conversion please */
@ -1561,7 +1561,7 @@ void php_mysqlnd_rowp_read_text_protocol(php_mysql_packet_row *packet, MYSQLND *
}
if (last_field_was_string) {
/* Normal queries: The buffer has one more byte at the end, because we need it */
packet->row_buffer[data_size] = '\0';
row_buffer->ptr[data_size] = '\0';
}
DBG_VOID_RETURN;
@ -1580,10 +1580,10 @@ php_mysqlnd_rowp_read(void *_packet, MYSQLND *conn TSRMLS_DC)
MYSQLND_NET *net = &conn->net;
zend_uchar *p;
enum_func_status ret = PASS;
size_t data_size = 0;
size_t old_chunk_size = net->stream->chunk_size;
php_mysql_packet_row *packet= (php_mysql_packet_row *) _packet;
size_t post_alloc_for_bit_fields = 0;
uint64 data_size = 0;
DBG_ENTER("php_mysqlnd_rowp_read");
@ -1593,17 +1593,18 @@ php_mysqlnd_rowp_read(void *_packet, MYSQLND *conn TSRMLS_DC)
packet->bit_fields_total_len + packet->bit_fields_count;
}
ret = php_mysqlnd_read_row_ex(conn, &packet->row_buffer, 0, &data_size,
ret = php_mysqlnd_read_row_ex(conn, &packet->row_buffer, &data_size,
packet->persistent_alloc, post_alloc_for_bit_fields
TSRMLS_CC);
if (FAIL == ret) {
goto end;
}
/* packet->row_buffer is of size 'data_size + 1' */
/* packet->row_buffer->ptr is of size 'data_size + 1' */
packet->header.size = data_size;
packet->row_buffer->app = data_size;
if ((*(p = packet->row_buffer)) == 0xFF) {
if ((*(p = packet->row_buffer->ptr)) == 0xFF) {
/*
Error message as part of the result set,
not good but we should not hang. See:
@ -1646,14 +1647,8 @@ php_mysqlnd_rowp_read(void *_packet, MYSQLND *conn TSRMLS_DC)
but mostly like old-API unbuffered and thus will populate this array with
value.
*/
packet->fields = (zval **) mnd_pemalloc(packet->field_count * sizeof(zval *),
packet->persistent_alloc);
}
if (packet->binary_protocol) {
php_mysqlnd_rowp_read_binary_protocol(packet, conn, p, data_size TSRMLS_CC);
} else {
php_mysqlnd_rowp_read_text_protocol(packet, conn, p, data_size TSRMLS_CC);
packet->fields = (zval **) mnd_pecalloc(packet->field_count, sizeof(zval *),
packet->persistent_alloc);
}
} else {
MYSQLND_INC_CONN_STATISTIC(&conn->stats,
@ -1675,7 +1670,7 @@ void php_mysqlnd_rowp_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
{
php_mysql_packet_row *p= (php_mysql_packet_row *) _packet;
if (p->row_buffer) {
mnd_pefree(p->row_buffer, p->persistent_alloc);
p->row_buffer->free_chunk(p->row_buffer, TRUE TSRMLS_CC);
p->row_buffer = NULL;
}
/*

View File

@ -258,7 +258,7 @@ struct st_php_mysql_packet_row {
mysqlnd_2b warning_count;
mysqlnd_2b server_status;
zend_uchar *row_buffer;
struct st_mysqlnd_memory_pool_chunk *row_buffer;
zend_bool skip_extraction;
zend_bool binary_protocol;
@ -323,6 +323,13 @@ zend_uchar * php_mysqlnd_net_store_length(zend_uchar *packet, uint64 length);
extern char * const mysqlnd_empty_string;
void php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
uint field_count, MYSQLND_FIELD *fields_metadata, MYSQLND *conn TSRMLS_DC);
void php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
uint field_count, MYSQLND_FIELD *fields_metadata, MYSQLND *conn TSRMLS_DC);
#endif /* MYSQLND_WIREPROTOCOL_H */
/*