Author: ahristov
Date: 2007-11-19 18:15:39 +0100 (Mon, 19 Nov 2007)
New Revision: 1155
Added:
branches/experimental/mysqlnd/mysqlnd_block_alloc.c
Removed:
branches/experimental/mysqlnd/mysqlnd_alloc.c
Modified:
branches/experimental/mysqlnd/mysqlnd.c
branches/experimental/mysqlnd/mysqlnd.h
branches/experimental/mysqlnd/mysqlnd_enum_n_def.h
branches/experimental/mysqlnd/mysqlnd_libmysql_compat.h
branches/experimental/mysqlnd/mysqlnd_priv.h
branches/experimental/mysqlnd/mysqlnd_ps.c
branches/experimental/mysqlnd/mysqlnd_result.c
branches/experimental/mysqlnd/mysqlnd_result.h
branches/experimental/mysqlnd/mysqlnd_result_meta.c
branches/experimental/mysqlnd/mysqlnd_statistics.c
branches/experimental/mysqlnd/mysqlnd_structs.h
branches/experimental/mysqlnd/mysqlnd_wireprotocol.c
branches/experimental/mysqlnd/mysqlnd_wireprotocol.h
branches/experimental/php5/ext/mysqli/mysqli.c
branches/experimental/php5/ext/mysqli/mysqli_mysqlnd.h
branches/experimental/php5/ext/mysqli/mysqli_nonapi.c
branches/experimental/php5/ext/mysqli/mysqli_prop.c
branches/experimental/php5/ext/mysqli/php_mysqli_structs.h
branches/experimental/php6/ext/mysqli/mysqli.c
branches/experimental/php6/ext/mysqli/mysqli_mysqlnd.h
branches/experimental/php6/ext/mysqli/mysqli_nonapi.c
branches/experimental/php6/ext/mysqli/mysqli_prop.c
branches/experimental/php6/ext/mysqli/php_mysqli_structs.h
Log:
Add the patch
Modified: branches/experimental/mysqlnd/mysqlnd.c
===================================================================
--- branches/experimental/mysqlnd/mysqlnd.c 2007-11-19 16:35:37 UTC (rev 1154)
+++ branches/experimental/mysqlnd/mysqlnd.c 2007-11-19 17:15:39 UTC (rev 1155)
@@ -66,12 +66,204 @@
MYSQLND_STATS *mysqlnd_global_stats = NULL;
static zend_bool mysqlnd_library_initted = FALSE;
+MYSQLND_MEMORY_POOL mysqlnd_memory_pool;
enum_func_status mysqlnd_send_close(MYSQLND * conn TSRMLS_DC);
+#define MYSQLND_SILENT 1
+
+/* {{{ _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;
+}
+/* }}} */
+
+
+/************************************************************************************************/
+/* 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 +273,13 @@
#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 +287,12 @@
/* {{{ 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 +431,7 @@
mnd_pefree(conn->net.cmd_buffer.buffer, pers);
conn->net.cmd_buffer.buffer = NULL;
}
+
conn->charset = NULL;
conn->greet_charset = NULL;
@@ -246,6 +449,22 @@
conn->m->free_contents(conn TSRMLS_CC);
+#ifdef ZTS
+ 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 +582,7 @@
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 +700,13 @@
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 +770,7 @@
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 +882,7 @@
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 +906,7 @@
}
}
} 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 +978,23 @@
DBG_INF("unicode set");
}
#endif
+#ifdef ZTS
+ {
+ 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,14 +1317,14 @@
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);
}
/* }}} */
-/* {{{ _mysqlnd_conn::set_charset */
+/* {{{ mysqlnd_conn::set_charset */
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn, set_charset)(MYSQLND * const conn, const char * const csname
TSRMLS_DC)
{
@@ -1163,7 +1399,7 @@
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 +1435,7 @@
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 +1472,6 @@
ret = conn->m->free_reference(conn TSRMLS_CC);
-
DBG_RETURN(ret);
}
/* }}} */
@@ -1273,6 +1508,46 @@
/* }}} */
+/* {{{ mysqlnd_conn::get_state */
+#ifdef ZTS
+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 ZTS
+ tsrm_mutex_lock(conn->LOCK_state);
+#endif
+ conn->state = new_state;
+#ifdef ZTS
+ 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 +1695,7 @@
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 +1708,7 @@
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);
@@ -1696,7 +1971,7 @@
/* }}} */
-/* {{{ _mysqlnd_conn::use_result */
+/* {{{ mysqlnd_conn::use_result */
MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_conn, use_result)(MYSQLND * const conn TSRMLS_DC)
{
@@ -1710,7 +1985,7 @@
}
/* 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");
@@ -1729,7 +2004,7 @@
/* }}} */
-/* {{{ _mysqlnd_conn::store_result */
+/* {{{ mysqlnd_conn::store_result */
MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_conn, store_result)(MYSQLND * const conn TSRMLS_DC)
{
@@ -1743,7 +2018,7 @@
}
/* 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 +2036,44 @@
/* }}} */
+/* {{{ 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 */
void
MYSQLND_METHOD(mysqlnd_conn, get_connection_stats)(const MYSQLND * const conn,
@@ -1784,6 +2097,7 @@
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 +2143,8 @@
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 +2162,15 @@
ret->m = & mysqlnd_mysqlnd_conn_methods;
ret->m->get_reference(ret);
+#ifdef ZTS
+ 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 +2310,7 @@
{
REGISTER_INI_ENTRIES();
- mysqlnd_library_init();
+ mysqlnd_library_init(TSRMLS_C);
return SUCCESS;
}
/* }}} */
@@ -1995,7 +2320,7 @@
*/
static PHP_MSHUTDOWN_FUNCTION(mysqlnd)
{
- mysqlnd_library_end();
+ mysqlnd_library_end(TSRMLS_C);
UNREGISTER_INI_ENTRIES();
return SUCCESS;
Modified: branches/experimental/mysqlnd/mysqlnd.h
===================================================================
--- branches/experimental/mysqlnd/mysqlnd.h 2007-11-19 16:35:37 UTC (rev 1154)
+++ branches/experimental/mysqlnd/mysqlnd.h 2007-11-19 17:15:39 UTC (rev 1155)
@@ -97,6 +97,7 @@
#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)
@@ -241,6 +242,7 @@
/* 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)
Deleted: branches/experimental/mysqlnd/mysqlnd_alloc.c
===================================================================
--- branches/experimental/mysqlnd/mysqlnd_alloc.c 2007-11-19 16:35:37 UTC (rev 1154)
+++ branches/experimental/mysqlnd/mysqlnd_alloc.c 2007-11-19 17:15:39 UTC (rev 1155)
@@ -1,285 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | PHP Version 6 |
- +----------------------------------------------------------------------+
- | Copyright (c) 2006-2007 The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@stripped so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Georg Richter <georg@stripped> |
- | Andrey Hristov <andrey@stripped> |
- | Ulf Wendel <uwendel@stripped> |
- +----------------------------------------------------------------------+
-
-*/
-
-/* $Id: mysqlnd_alloc.c,v 1.1.2.2 2007/10/07 21:47:36 davidw Exp $ */
-#include "php.h"
-#include "mysqlnd.h"
-#include "mysqlnd_priv.h"
-#include "mysqlnd_palloc.h"
-
-
-#define MYSQLND_SILENT
-#define MYSQLND_DONT_DUMP_STATS
-
-#define MYSQLND_ZVALS_MAX_CACHE 5000
-
-
-#if A0
-/* Caching zval allocator */
-zval * mysqlnd_alloc_get_zval(MYSQLND_ZVAL_CACHE * const cache);
-void mysqlnd_alloc_zval_ptr_dtor(zval **zv, MYSQLND_ZVAL_CACHE * const cache);
-MYSQLND_ZVAL_CACHE* mysqlnd_alloc_init_cache();
-MYSQLND_ZVAL_CACHE* mysqlnd_alloc_get_cache_reference(MYSQLND_ZVAL_CACHE *cache);
-void mysqlnd_alloc_free_cache_reference(MYSQLND_ZVAL_CACHE **cache);
-#endif
-
-
-/*
- The cache line is a big contiguous array of zval pointers.
- Because the CPU cache will cache starting from an address, and not
- before it, then we have to organize our structure according to this.
- Thus, if 'last_added' is valid pointer (not NULL) then last_added is
- increased. When zval is cached, if there is room, last_added is decreased
- and then the zval pointer will be assigned to it. This means that some
- positions may become hot points and stay in the cache.
- Imagine we have 5 pointers in a line
- 1. last_added = list_item->ptr_line + cache->max_items;
- 2. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL
- 3. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL
- 4. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL
- 0x0
- 0x0
- 0x0
- 0x0
- 0x0
- ---
- empty_position, always 0x0 <-- last_added
-
- 5. free_zval -> if (free_items++ != max_items) {// we can add more
- *(--last_added) = zval_ptr;
- }
- (memory addresses increase downwards)
- 0x0
- 0x0
- 0x0
- 0x0
- 0xA <-- last_added
- ---
- 0x0
-
- 6. free_zval -> if (free_items++ != max_items) {// we can add more
- *(--last_added) = zval_ptr;
- }
- 0x0
- 0x0
- 0x0
- 0xB <-- last_added
- 0xA
- ---
- 0x0
-
- 7. free_zval -> if (free_items++ != max_items) {// we can add more
- *(--last_added) = zval_ptr;
- }
- 0x0
- 0x0
- 0xC <-- last_added
- 0xB
- 0xA
- ---
- 0x0
-
- 8. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL;
- 0x0
- 0x0
- 0x0
- 0xB <-- last_added
- 0xA
- ---
- 0x0
-
- 9. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL;
- 0x0
- 0x0
- 0x0
- 0x0
- 0xA <-- last_added
- ---
- 0x0
-
-10. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL;
- 0x0
- 0x0
- 0x0
- 0x0
- 0x0
- ---
- 0x0 <-- last_added
-
-*/
-
-
-zval * mysqlnd_alloc_get_zval(MYSQLND_ZVAL_CACHE * const cache)
-{
- zval *ret = NULL;
-
-#ifndef MYSQLND_SILENT
- php_printf("[mysqlnd_alloc_get_zval %p] *last_added=%p free_items=%d ", cache, cache?
cache->free_list->last_added:NULL, cache->free_items);
-#endif
-
- if (cache) {
- if ((ret = *cache->free_list->last_added)) {
- *cache->free_list->last_added++ = NULL;
-
- --cache->free_items;
- ++cache->get_hits;
- } else {
- ++cache->get_misses;
- }
- }
- if (!ret) {
- ALLOC_ZVAL(ret);
- }
- INIT_PZVAL(ret);
-
-#ifndef MYSQLND_SILENT
- php_printf("ret=%p\n", ret);
-#endif
- return ret;
-}
-
-static
-void mysqlnd_alloc_cache_prealloc(MYSQLND_ZVAL_CACHE * const cache, unsigned int count)
-{
- zval *data;
- cache->free_items = count;
- while (count--) {
- MAKE_STD_ZVAL(data);
- ZVAL_NULL(data);
-#ifndef MYSQLND_SILENT
- php_printf("[mysqlnd_alloc_prealloc %p] items=%d data=%p\n", cache,
cache->free_items, data);
-#endif
-
- *(--cache->free_list->last_added) = data;
- }
-}
-
-void mysqlnd_alloc_zval_ptr_dtor(zval **zv, MYSQLND_ZVAL_CACHE * const cache)
-{
- if (!cache || Z_REFCOUNT_PP(zv) > 1 || cache->max_items == cache->free_items) {
-#ifndef MYSQLND_SILENT
- php_printf("[mysqlnd_alloc_zval_ptr_dtor %p]1 last_added-1=%p *zv=%p\n",
cache->free_list->last_added, *zv);
-#endif
- /* We can't cache zval's with refcount > 1 */
- zval_ptr_dtor(zv);
- if (cache) {
- if (cache->max_items == cache->free_items) {
- ++cache->put_full_misses;
- } else {
- ++cache->put_refcount_misses;
- }
- }
- } else {
- /* refcount is 1 and there is place. Go, cache it! */
- ++cache->free_items;
- zval_dtor(*zv);
- ZVAL_NULL(*zv);
- *(--cache->free_list->last_added) = *zv;
- ++cache->put_hits;
- }
-#ifndef MYSQLND_SILENT
- php_printf("[mysqlnd_alloc_zval_ptr_dtor %p] free_items=%d\n", cache,
cache->free_items);
-#endif
-}
-
-
-MYSQLND_ZVAL_CACHE* mysqlnd_alloc_init_cache(void)
-{
- MYSQLND_ZVAL_CACHE *ret = ecalloc(1, sizeof(MYSQLND_ZVAL_CACHE));
-
-#ifndef MYSQLND_SILENT
- php_printf("[mysqlnd_alloc_init_cache %p]\n", ret);
-#endif
-
- ret->max_items = MYSQLND_ZVALS_MAX_CACHE;
- ret->free_items = 0;
- ret->references = 1;
-
- /* Let's have always one, so we don't need to do a check in get_zval */
- ret->free_list = ecalloc(1, sizeof(struct st_mysqlnd_zval_list));
-
- /* One more for empty position of last_added */
- ret->free_list->ptr_line = ecalloc(ret->max_items + 1, sizeof(zval *));
- ret->free_list->last_added = ret->free_list->ptr_line + ret->max_items;
-
- mysqlnd_alloc_cache_prealloc(ret, (ret->max_items / 100) * 100);
-
- return ret;
-}
-
-
-MYSQLND_ZVAL_CACHE* mysqlnd_alloc_get_cache_reference(MYSQLND_ZVAL_CACHE *cache)
-{
- if (cache) {
- cache->references++;
- }
- return cache;
-}
-
-
-static
-void mysqlnd_alloc_free_cache(MYSQLND_ZVAL_CACHE *cache)
-{
-#ifndef MYSQLND_SILENT
- uint i = 0;
- php_printf("[mysqlnd_alloc_free_cache %p]\n", cache);
-#endif
-
- while (*cache->free_list->last_added) {
-#ifndef MYSQLND_SILENT
- php_printf("\t[free_item %d %p]\n", i++, *cache->free_list->last_added);
-#endif
- zval_ptr_dtor(cache->free_list->last_added);
- cache->free_list->last_added++;
- }
-#ifndef MYSQLND_DONT_DUMP_STATS
- php_printf("CACHE STATS:\n\tGET\n\t\tHITS:%lu\n\t\tMISSES=%lu\n\t\tHIT RATIO=%1.3f\n\t"
- "PUT\n\t\tHITS:%lu\n\t\tFULL_MISS=%lu\n\t\tREFC_MISS=%lu\n\t\tHIT RATIO=%1.3f\n\n",
- cache->get_hits, cache->get_misses, (1.0*cache->get_hits/(cache->get_hits +
cache->get_misses)),
- cache->put_hits, cache->put_full_misses, cache->put_refcount_misses,
- (1.0 * cache->put_hits / (cache->put_hits + cache->put_full_misses +
cache->put_refcount_misses)));
-#endif
- efree(cache->free_list->ptr_line);
- efree(cache->free_list);
- efree(cache);
-}
-
-
-
-void mysqlnd_alloc_free_cache_reference(MYSQLND_ZVAL_CACHE **cache)
-{
-#ifndef MYSQLND_SILENT
- php_printf("[mysqlnd_alloc_free_cache_reference %p] refs=%d\n", *cache,
(*cache)->references);
-#endif
- if (*cache && --(*cache)->references == 0) {
- mysqlnd_alloc_free_cache(*cache);
- }
- *cache = NULL;
-}
-
-/*
- * Local variables:
- * tab-width: 4
- * c-basic-offset: 4
- * End:
- * vim600: noet sw=4 ts=4 fdm=marker
- * vim<600: noet sw=4 ts=4
- */
Copied: branches/experimental/mysqlnd/mysqlnd_block_alloc.c (from rev 1153,
branches/experimental/mysqlnd/mysqlnd_alloc.c)
===================================================================
--- branches/experimental/mysqlnd/mysqlnd_block_alloc.c (rev 0)
+++ branches/experimental/mysqlnd/mysqlnd_block_alloc.c 2007-11-19 17:15:39 UTC (rev 1155)
@@ -0,0 +1,285 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@stripped so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@stripped> |
+ | Andrey Hristov <andrey@stripped> |
+ | Ulf Wendel <uwendel@stripped> |
+ +----------------------------------------------------------------------+
+
+*/
+
+/* $Id: mysqlnd_alloc.c,v 1.1.2.2 2007/10/07 21:47:36 davidw Exp $ */
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_palloc.h"
+
+
+#define MYSQLND_SILENT
+#define MYSQLND_DONT_DUMP_STATS
+
+#define MYSQLND_ZVALS_MAX_CACHE 5000
+
+
+#if A0
+/* Caching zval allocator */
+zval * mysqlnd_alloc_get_zval(MYSQLND_ZVAL_CACHE * const cache);
+void mysqlnd_alloc_zval_ptr_dtor(zval **zv, MYSQLND_ZVAL_CACHE * const cache);
+MYSQLND_ZVAL_CACHE* mysqlnd_alloc_init_cache();
+MYSQLND_ZVAL_CACHE* mysqlnd_alloc_get_cache_reference(MYSQLND_ZVAL_CACHE *cache);
+void mysqlnd_alloc_free_cache_reference(MYSQLND_ZVAL_CACHE **cache);
+#endif
+
+
+/*
+ The cache line is a big contiguous array of zval pointers.
+ Because the CPU cache will cache starting from an address, and not
+ before it, then we have to organize our structure according to this.
+ Thus, if 'last_added' is valid pointer (not NULL) then last_added is
+ increased. When zval is cached, if there is room, last_added is decreased
+ and then the zval pointer will be assigned to it. This means that some
+ positions may become hot points and stay in the cache.
+ Imagine we have 5 pointers in a line
+ 1. last_added = list_item->ptr_line + cache->max_items;
+ 2. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL
+ 3. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL
+ 4. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ ---
+ empty_position, always 0x0 <-- last_added
+
+ 5. free_zval -> if (free_items++ != max_items) {// we can add more
+ *(--last_added) = zval_ptr;
+ }
+ (memory addresses increase downwards)
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0xA <-- last_added
+ ---
+ 0x0
+
+ 6. free_zval -> if (free_items++ != max_items) {// we can add more
+ *(--last_added) = zval_ptr;
+ }
+ 0x0
+ 0x0
+ 0x0
+ 0xB <-- last_added
+ 0xA
+ ---
+ 0x0
+
+ 7. free_zval -> if (free_items++ != max_items) {// we can add more
+ *(--last_added) = zval_ptr;
+ }
+ 0x0
+ 0x0
+ 0xC <-- last_added
+ 0xB
+ 0xA
+ ---
+ 0x0
+
+ 8. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL;
+ 0x0
+ 0x0
+ 0x0
+ 0xB <-- last_added
+ 0xA
+ ---
+ 0x0
+
+ 9. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL;
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0xA <-- last_added
+ ---
+ 0x0
+
+10. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL;
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ ---
+ 0x0 <-- last_added
+
+*/
+
+
+zval * mysqlnd_alloc_get_zval(MYSQLND_ZVAL_CACHE * const cache)
+{
+ zval *ret = NULL;
+
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_alloc_get_zval %p] *last_added=%p free_items=%d ", cache, cache?
cache->free_list->last_added:NULL, cache->free_items);
+#endif
+
+ if (cache) {
+ if ((ret = *cache->free_list->last_added)) {
+ *cache->free_list->last_added++ = NULL;
+
+ --cache->free_items;
+ ++cache->get_hits;
+ } else {
+ ++cache->get_misses;
+ }
+ }
+ if (!ret) {
+ ALLOC_ZVAL(ret);
+ }
+ INIT_PZVAL(ret);
+
+#ifndef MYSQLND_SILENT
+ php_printf("ret=%p\n", ret);
+#endif
+ return ret;
+}
+
+static
+void mysqlnd_alloc_cache_prealloc(MYSQLND_ZVAL_CACHE * const cache, unsigned int count)
+{
+ zval *data;
+ cache->free_items = count;
+ while (count--) {
+ MAKE_STD_ZVAL(data);
+ ZVAL_NULL(data);
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_alloc_prealloc %p] items=%d data=%p\n", cache,
cache->free_items, data);
+#endif
+
+ *(--cache->free_list->last_added) = data;
+ }
+}
+
+void mysqlnd_alloc_zval_ptr_dtor(zval **zv, MYSQLND_ZVAL_CACHE * const cache)
+{
+ if (!cache || Z_REFCOUNT_PP(zv) > 1 || cache->max_items == cache->free_items) {
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_alloc_zval_ptr_dtor %p]1 last_added-1=%p *zv=%p\n",
cache->free_list->last_added, *zv);
+#endif
+ /* We can't cache zval's with refcount > 1 */
+ zval_ptr_dtor(zv);
+ if (cache) {
+ if (cache->max_items == cache->free_items) {
+ ++cache->put_full_misses;
+ } else {
+ ++cache->put_refcount_misses;
+ }
+ }
+ } else {
+ /* refcount is 1 and there is place. Go, cache it! */
+ ++cache->free_items;
+ zval_dtor(*zv);
+ ZVAL_NULL(*zv);
+ *(--cache->free_list->last_added) = *zv;
+ ++cache->put_hits;
+ }
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_alloc_zval_ptr_dtor %p] free_items=%d\n", cache,
cache->free_items);
+#endif
+}
+
+
+MYSQLND_ZVAL_CACHE* mysqlnd_alloc_init_cache(void)
+{
+ MYSQLND_ZVAL_CACHE *ret = ecalloc(1, sizeof(MYSQLND_ZVAL_CACHE));
+
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_alloc_init_cache %p]\n", ret);
+#endif
+
+ ret->max_items = MYSQLND_ZVALS_MAX_CACHE;
+ ret->free_items = 0;
+ ret->references = 1;
+
+ /* Let's have always one, so we don't need to do a check in get_zval */
+ ret->free_list = ecalloc(1, sizeof(struct st_mysqlnd_zval_list));
+
+ /* One more for empty position of last_added */
+ ret->free_list->ptr_line = ecalloc(ret->max_items + 1, sizeof(zval *));
+ ret->free_list->last_added = ret->free_list->ptr_line + ret->max_items;
+
+ mysqlnd_alloc_cache_prealloc(ret, (ret->max_items / 100) * 100);
+
+ return ret;
+}
+
+
+MYSQLND_ZVAL_CACHE* mysqlnd_alloc_get_cache_reference(MYSQLND_ZVAL_CACHE *cache)
+{
+ if (cache) {
+ cache->references++;
+ }
+ return cache;
+}
+
+
+static
+void mysqlnd_alloc_free_cache(MYSQLND_ZVAL_CACHE *cache)
+{
+#ifndef MYSQLND_SILENT
+ uint i = 0;
+ php_printf("[mysqlnd_alloc_free_cache %p]\n", cache);
+#endif
+
+ while (*cache->free_list->last_added) {
+#ifndef MYSQLND_SILENT
+ php_printf("\t[free_item %d %p]\n", i++, *cache->free_list->last_added);
+#endif
+ zval_ptr_dtor(cache->free_list->last_added);
+ cache->free_list->last_added++;
+ }
+#ifndef MYSQLND_DONT_DUMP_STATS
+ php_printf("CACHE STATS:\n\tGET\n\t\tHITS:%lu\n\t\tMISSES=%lu\n\t\tHIT RATIO=%1.3f\n\t"
+ "PUT\n\t\tHITS:%lu\n\t\tFULL_MISS=%lu\n\t\tREFC_MISS=%lu\n\t\tHIT RATIO=%1.3f\n\n",
+ cache->get_hits, cache->get_misses, (1.0*cache->get_hits/(cache->get_hits +
cache->get_misses)),
+ cache->put_hits, cache->put_full_misses, cache->put_refcount_misses,
+ (1.0 * cache->put_hits / (cache->put_hits + cache->put_full_misses +
cache->put_refcount_misses)));
+#endif
+ efree(cache->free_list->ptr_line);
+ efree(cache->free_list);
+ efree(cache);
+}
+
+
+
+void mysqlnd_alloc_free_cache_reference(MYSQLND_ZVAL_CACHE **cache)
+{
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_alloc_free_cache_reference %p] refs=%d\n", *cache,
(*cache)->references);
+#endif
+ if (*cache && --(*cache)->references == 0) {
+ mysqlnd_alloc_free_cache(*cache);
+ }
+ *cache = NULL;
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
Modified: branches/experimental/mysqlnd/mysqlnd_enum_n_def.h
===================================================================
--- branches/experimental/mysqlnd/mysqlnd_enum_n_def.h 2007-11-19 16:35:37 UTC (rev 1154)
+++ branches/experimental/mysqlnd/mysqlnd_enum_n_def.h 2007-11-19 17:15:39 UTC (rev 1155)
@@ -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
Modified: branches/experimental/mysqlnd/mysqlnd_libmysql_compat.h
===================================================================
--- branches/experimental/mysqlnd/mysqlnd_libmysql_compat.h 2007-11-19 16:35:37 UTC (rev
1154)
+++ branches/experimental/mysqlnd/mysqlnd_libmysql_compat.h 2007-11-19 17:15:39 UTC (rev
1155)
@@ -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 */
Modified: branches/experimental/mysqlnd/mysqlnd_priv.h
===================================================================
--- branches/experimental/mysqlnd/mysqlnd_priv.h 2007-11-19 16:35:37 UTC (rev 1154)
+++ branches/experimental/mysqlnd/mysqlnd_priv.h 2007-11-19 17:15:39 UTC (rev 1155)
@@ -156,7 +156,15 @@
#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,
uint pack_len, zend_uchar **row,
@@ -175,7 +183,9 @@
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);
Modified: branches/experimental/mysqlnd/mysqlnd_ps.c
===================================================================
--- branches/experimental/mysqlnd/mysqlnd_ps.c 2007-11-19 16:35:37 UTC (rev 1154)
+++ branches/experimental/mysqlnd/mysqlnd_ps.c 2007-11-19 17:15:39 UTC (rev 1155)
@@ -83,7 +83,7 @@
}
/* 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,
@@ -105,6 +105,73 @@
/* 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,
+ 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 */
+
/* Not set for SHOW statements at PREPARE stage */
if (result->conn) {
result->conn->m->free_reference(result->conn TSRMLS_CC);
@@ -112,16 +179,15 @@
}
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_stmt::get_result */
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const stmt TSRMLS_DC)
@@ -155,7 +220,7 @@
}
/* 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 @@
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 @@
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 @@
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 @@
{
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++;
+ mynd_ulonglong 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 @@
}
}
}
- 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
/* 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 @@
/* 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 @@
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);
@@ -1307,13 +1407,15 @@
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
@@ -1447,7 +1549,7 @@
stmt->state = MYSQLND_STMT_PREPARED;
/* Line is free! */
- stmt->conn->state = CONN_READY;
+ CONN_SET_STATE(stmt->conn, CONN_READY);
DBG_RETURN(PASS);
}
@@ -1597,7 +1699,7 @@
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)) {
@@ -1654,6 +1756,7 @@
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),
Modified: branches/experimental/mysqlnd/mysqlnd_result.c
===================================================================
--- branches/experimental/mysqlnd/mysqlnd_result.c 2007-11-19 16:35:37 UTC (rev 1154)
+++ branches/experimental/mysqlnd/mysqlnd_result.c 2007-11-19 17:15:39 UTC (rev 1155)
@@ -32,7 +32,46 @@
#define MYSQLND_SILENT
+/* {{{ mysqlnd_res_initialize_result_set_rest */
+void mysqlnd_res_initialize_result_set_rest(MYSQLND_RES * const result TSRMLS_DC)
+{
+ uint i;
+ zval **data_cursor = result->stored_data->data;
+ zval **data_begin = result->stored_data->data;
+ uint field_count = result->meta->field_count;
+ uint row_count = result->stored_data->row_count;
+ if (!data_cursor || row_count == result->stored_data->initialized_rows) {
+ return;
+ }
+ while ((data_cursor - data_begin) < (row_count * field_count)) {
+ if (NULL == data_cursor[0]) {
+ result->stored_data->initialized_rows++;
+ result->m.row_decoder(
+ result->stored_data->row_buffers[(data_cursor - data_begin) / field_count],
+ data_cursor,
+ result->meta->field_count,
+ result->meta->fields,
+ result->conn TSRMLS_CC);
+ 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(data_cursor[i]) >= IS_STRING) {
+ unsigned long len = Z_STRLEN_P(data_cursor[i]);
+ if (result->meta->fields[i].max_length < len) {
+ result->meta->fields[i].max_length = len;
+ }
+ }
+ }
+ }
+ data_cursor += field_count;
+ }
+}
+/* }}} */
+
/* {{{ mysqlnd_unbuffered_free_last_data */
void mysqlnd_unbuffered_free_last_data(MYSQLND_RES *result TSRMLS_DC)
{
@@ -70,7 +109,7 @@
}
if (unbuf->last_row_buffer) {
/* Nothing points to this buffer now, free it */
- efree(unbuf->last_row_buffer);
+ unbuf->last_row_buffer->free_chunk(unbuf->last_row_buffer, TRUE TSRMLS_CC);
unbuf->last_row_buffer = NULL;
}
@@ -82,21 +121,24 @@
void mysqlnd_free_buffered_data(MYSQLND_RES *result TSRMLS_DC)
{
MYSQLND_THD_ZVAL_PCACHE *zval_cache = result->zval_cache;
- MYSQLND_RES_BUFFERED *set = result->data;
+ MYSQLND_RES_BUFFERED *set = result->stored_data;
unsigned int field_count = result->field_count;
- unsigned int row;
+ int row;
DBG_ENTER("mysqlnd_free_buffered_data");
- DBG_INF_FMT("Freeing "MYSQLND_LLU_SPEC" row(s)", result->data->row_count);
+ DBG_INF_FMT("Freeing "MYSQLND_LLU_SPEC" row(s)", set->row_count);
DBG_INF_FMT("before: real_usage=%lu usage=%lu", zend_memory_usage(TRUE TSRMLS_CC),
zend_memory_usage(FALSE TSRMLS_CC));
- for (row = 0; row < result->data->row_count; row++) {
+ for (row = set->row_count - 1; row >= 0; row--) {
+ zval **current_row = set->data + row * field_count;
+ MYSQLND_MEMORY_POOL_CHUNK *current_buffer = set->row_buffers[row];
unsigned int col;
- zval **current_row = current_row = set->data[row];
- zend_uchar *current_buffer = set->row_buffers[row];
for (col = 0; col < field_count; col++) {
zend_bool copy_ctor_called;
+ if (current_row[col] == NULL) {
+ break;/* row that was never initialized */
+ }
mysqlnd_palloc_zval_ptr_dtor(&(current_row[col]), zval_cache,
result->type, ©_ctor_called TSRMLS_CC);
#if MYSQLND_DEBUG_MEMORY
@@ -108,8 +150,7 @@
#if MYSQLND_DEBUG_MEMORY
DBG_INF("Freeing current_row & current_buffer");
#endif
- pefree(current_row, set->persistent);
- pefree(current_buffer, set->persistent);
+ current_buffer->free_chunk(current_buffer, TRUE TSRMLS_CC);
}
DBG_INF("Freeing data & row_buffer");
pefree(set->data, set->persistent);
@@ -121,6 +162,7 @@
if (set->qcache) {
mysqlnd_qcache_free_cache_reference(&set->qcache);
}
+
DBG_INF("Freeing set");
pefree(set, set->persistent);
@@ -130,20 +172,101 @@
/* }}} */
+/* {{{ mysqlnd_free_background_buffered_data */
+void mysqlnd_free_background_buffered_data(MYSQLND_RES *result TSRMLS_DC)
+{
+ MYSQLND_THD_ZVAL_PCACHE *zval_cache = result->zval_cache;
+ MYSQLND_RES_BG_BUFFERED *set = result->bg_stored_data;
+ unsigned int field_count = result->field_count;
+ int row;
+
+ DBG_ENTER("mysqlnd_free_buffered_data");
+ DBG_INF_FMT("Freeing "MYSQLND_LLU_SPEC" row(s)", set->row_count);
+
+ do {
+ tsrm_mutex_lock(set->LOCK);
+ if (set->bg_fetch_finished) {
+ tsrm_mutex_unlock(set->LOCK);
+ break;
+ }
+ tsrm_mutex_unlock(set->LOCK);
+#if HAVE_USLEEP
+ usleep(2000);
+#else
+ {
+ volatile int i;
+ for (i = 0; i < 1000; i++);
+ }
+#endif
+ } while (1);
+
+ DBG_INF_FMT("before: real_usage=%lu usage=%lu", zend_memory_usage(TRUE TSRMLS_CC),
zend_memory_usage(FALSE TSRMLS_CC));
+ for (row = set->row_count - 1; row >= 0; row--) {
+ MYSQLND_MEMORY_POOL_CHUNK *current_buffer = set->row_buffers[row];
+ /* It could be the case that the user fetched no rows - then no set->data */
+ if (row < set->data_size && set->data[row]) {
+ zval **current_row = set->data[row];
+ unsigned int col;
+
+ for (col = 0; col < field_count; col++) {
+ zend_bool copy_ctor_called;
+ mysqlnd_palloc_zval_ptr_dtor(&(current_row[col]), zval_cache,
+ result->type, ©_ctor_called TSRMLS_CC);
+#if MYSQLND_DEBUG_MEMORY
+ DBG_INF_FMT("Copy_ctor_called=%d", copy_ctor_called);
+#endif
+ MYSQLND_INC_GLOBAL_STATISTIC(copy_ctor_called? STAT_COPY_ON_WRITE_PERFORMED:
+ STAT_COPY_ON_WRITE_SAVED);
+ }
+#if MYSQLND_DEBUG_MEMORY
+ DBG_INF("Freeing current_row & current_buffer");
+#endif
+ pefree(current_row, set->persistent);
+ }
+ current_buffer->free_chunk(current_buffer, TRUE TSRMLS_CC);
+ }
+ DBG_INF("Freeing data & row_buffer");
+ pefree(set->data, set->persistent);
+ pefree(set->row_buffers, set->persistent);
+ set->data = NULL;
+ set->row_buffers = NULL;
+ set->data_cursor = NULL;
+ set->row_count = 0;
+ if (set->qcache) {
+ mysqlnd_qcache_free_cache_reference(&set->qcache);
+ }
+#ifdef ZTS
+ if (set->LOCK) {
+ tsrm_mutex_free(set->LOCK);
+ }
+#endif
+
+ DBG_INF("Freeing set");
+ pefree(set, set->persistent);
+
+ DBG_INF_FMT("after: real_usage=%lu usage=%lu", zend_memory_usage(TRUE TSRMLS_CC),
zend_memory_usage(FALSE TSRMLS_CC));
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
/* {{{ mysqlnd_res::free_result_buffers */
void
MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES *result TSRMLS_DC)
{
DBG_ENTER("mysqlnd_res::free_result_buffers");
- DBG_INF_FMT("%s", result->unbuf? "unbuffered":(result->data?
"buffered":"unknown"));
+ DBG_INF_FMT("%s", result->unbuf? "unbuffered":(result->stored_data?
"buffered":"unknown"));
if (result->unbuf) {
mysqlnd_unbuffered_free_last_data(result TSRMLS_CC);
efree(result->unbuf);
result->unbuf = NULL;
- } else if (result->data) {
+ } else if (result->stored_data) {
mysqlnd_free_buffered_data(result TSRMLS_CC);
- result->data = NULL;
+ result->stored_data = NULL;
+ } else if (result->bg_stored_data) {
+ mysqlnd_free_background_buffered_data(result TSRMLS_CC);
+ result->bg_stored_data = NULL;
}
if (result->lengths) {
@@ -193,11 +316,6 @@
void mysqlnd_internal_free_result(MYSQLND_RES *result TSRMLS_DC)
{
DBG_ENTER("mysqlnd_internal_free_result");
- /*
- result->conn is an address if this is an unbuffered query.
- In this case, decrement the reference counter in the connection
- object and if needed free the latter.
- */
if (result->conn) {
result->conn->m->free_reference(result->conn TSRMLS_CC);
result->conn = NULL;
@@ -295,9 +413,9 @@
zend_bool is_warning;
DBG_INF("LOAD DATA");
conn->last_query_type = QUERY_LOAD_LOCAL;
- conn->state = CONN_SENDING_LOAD_DATA;
+ CONN_SET_STATE(conn, CONN_SENDING_LOAD_DATA);
ret = mysqlnd_handle_local_infile(conn, rset_header.info_or_local_file,
&is_warning TSRMLS_CC);
- conn->state = (ret == PASS || is_warning == TRUE)? CONN_READY:CONN_QUIT_SENT;
+ CONN_SET_STATE(conn, (ret == PASS || is_warning == TRUE)?
CONN_READY:CONN_QUIT_SENT);
MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_NON_RSET_QUERY);
break;
}
@@ -314,9 +432,9 @@
conn->persistent);
/* Result set can follow UPSERT statement, check server_status */
if (conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) {
- conn->state = CONN_NEXT_RESULT_PENDING;
+ CONN_SET_STATE(conn, CONN_NEXT_RESULT_PENDING);
} else {
- conn->state = CONN_READY;
+ CONN_SET_STATE(conn, CONN_READY);
}
ret = PASS;
MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_NON_RSET_QUERY);
@@ -332,7 +450,7 @@
MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_RSET_QUERY);
memset(&conn->upsert_status, 0, sizeof(conn->upsert_status));
conn->last_query_type = QUERY_SELECT;
- conn->state = CONN_FETCHING_DATA;
+ CONN_SET_STATE(conn, CONN_FETCHING_DATA);
/* PS has already allocated it */
if (!stmt) {
conn->field_count = rset_header.field_count;
@@ -405,11 +523,11 @@
stat = STAT_NO_INDEX_USED;
}
if (stat != STAT_LAST) {
+#if A0
char *backtrace = mysqlnd_get_backtrace(TSRMLS_C);
-#if A0
php_log_err(backtrace TSRMLS_CC);
+ efree(backtrace);
#endif
- efree(backtrace);
MYSQLND_INC_CONN_STATISTIC(&conn->stats, stat);
}
}
@@ -440,6 +558,7 @@
{
int i;
zval **previous_row;
+ MYSQLND_RES_BUFFERED *set = result->stored_data;
/*
If:
@@ -447,15 +566,15 @@
- first row has not been read
- last_row has been read
*/
- if (result->data->data_cursor == NULL ||
- result->data->data_cursor == result->data->data ||
- ((result->data->data_cursor - result->data->data) >
result->data->row_count))
+ if (set->data_cursor == NULL ||
+ set->data_cursor == set->data ||
+ ((set->data_cursor - set->data) > (set->row_count *
result->meta->field_count) ))
{
return NULL;/* No rows or no more rows */
}
- previous_row = *(result->data->data_cursor - 1);
- for (i = 0; i < result->field_count; i++) {
+ previous_row = set->data_cursor - result->meta->field_count;
+ for (i = 0; i < result->meta->field_count; i++) {
result->lengths[i] = (Z_TYPE_P(previous_row[i]) == IS_NULL)?
0:Z_STRLEN_P(previous_row[i]);
}
@@ -464,6 +583,43 @@
/* }}} */
+/* {{{ mysqlnd_fetch_lengths_async_buffered */
+/*
+ Do lazy initialization for buffered results. As PHP strings have
+ length inside, this function makes not much sense in the context
+ of PHP, to be called as separate function. But let's have it for
+ completeness.
+*/
+static
+unsigned long * mysqlnd_fetch_lengths_async_buffered(MYSQLND_RES * const result)
+{
+ int i;
+ zval **previous_row;
+ MYSQLND_RES_BG_BUFFERED *set = result->bg_stored_data;
+
+ /*
+ If:
+ - unbuffered result
+ - first row has not been read
+ - last_row has been read
+ */
+ if (set->data_cursor == NULL ||
+ set->data_cursor == set->data ||
+ ((set->data_cursor - set->data) > set->row_count))
+ {
+ return NULL;/* No rows or no more rows */
+ }
+
+ previous_row = *(set->data_cursor - 1);
+ for (i = 0; i < result->meta->field_count; i++) {
+ result->lengths[i] = (Z_TYPE_P(previous_row[i]) == IS_NULL)?
0:Z_STRLEN_P(previous_row[i]);
+ }
+
+ return result->lengths;
+}
+/* }}} */
+
+
/* {{{ mysqlnd_fetch_lengths_unbuffered */
static
unsigned long * mysqlnd_fetch_lengths_unbuffered(MYSQLND_RES * const result)
@@ -501,7 +657,7 @@
*fetched_anything = FALSE;
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_RETURN(FAIL);
@@ -524,11 +680,18 @@
row_packet->fields = NULL;
row_packet->row_buffer = NULL;
+
MYSQLND_INC_CONN_STATISTIC(&result->conn->stats,
STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF);
if (!row_packet->skip_extraction) {
HashTable *row_ht = Z_ARRVAL_P(row);
+ 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++) {
zval *data = result->unbuf->last_row_data[i];
int len = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data);
@@ -589,7 +752,7 @@
DBG_ERR_FMT("errorno=%d error=%s", row_packet->error_info.error_no,
row_packet->error_info.error);
}
*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) {
/* Mark the connection as usable again */
@@ -602,9 +765,9 @@
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);
}
mysqlnd_unbuffered_free_last_data(result TSRMLS_CC);
*fetched_anything = FALSE;
@@ -630,13 +793,14 @@
result->m.fetch_row = result->m.fetch_row_normal_unbuffered;
result->m.fetch_lengths = mysqlnd_fetch_lengths_unbuffered;
result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long));
+ result->m.row_decoder = php_mysqlnd_rowp_read_text_protocol;
} else {
result->type = MYSQLND_RES_PS_UNBUF;
/* result->m.fetch_row() will be set in mysqlnd_ps.c */
result->m.fetch_lengths = NULL; /* makes no sense */
result->lengths = NULL;
+ result->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol;
}
-
result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));
/*
@@ -663,15 +827,40 @@
{
unsigned int i;
zval *row = (zval *) param;
+ MYSQLND_RES_BUFFERED *set = result->stored_data;
DBG_ENTER("mysqlnd_fetch_row_buffered");
DBG_INF_FMT("flags=%u row=%p", flags, row);
/* 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 *
result->meta->field_count))
{
- zval **current_row = *result->data->data_cursor;
+ zval **current_row = set->data_cursor;
+
+ if (NULL == current_row[0]) {
+ set->initialized_rows++;
+ mynd_ulonglong row_num = (set->data_cursor - set->data) /
result->meta->field_count;
+ result->m.row_decoder(set->row_buffers[row_num],
+ current_row,
+ result->meta->field_count,
+ result->meta->fields,
+ result->conn TSRMLS_CC);
+ 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++) {
zval *data = current_row[i];
@@ -719,11 +908,11 @@
}
}
}
- result->data->data_cursor++;
+ set->data_cursor += result->meta->field_count;
*fetched_anything = TRUE;
MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);
} else {
- result->data->data_cursor = NULL;
+ set->data_cursor = NULL;
*fetched_anything = FALSE;
DBG_INF("EOF reached");
}
@@ -733,78 +922,62 @@
/* }}} */
-#define STORE_RESULT_PREALLOCATED_SET 32
+#define STORE_RESULT_PREALLOCATED_SET 10
/* {{{ mysqlnd_store_result_fetch_data */
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 ret;
- php_mysql_packet_row row_packet;
+ php_mysql_packet_row *row_packet;
unsigned int next_extend = STORE_RESULT_PREALLOCATED_SET, free_rows;
MYSQLND_RES_BUFFERED *set;
DBG_ENTER("mysqlnd_store_result_fetch_data");
- DBG_INF_FMT("conn=%llu binary_proto=%d update_max_len=%d to_cache=%d",
- conn->thread_id, binary_protocol, update_max_length, to_cache);
+ DBG_INF_FMT("conn=%llu binary_proto=%d to_cache=%d",
+ conn->thread_id, binary_protocol, to_cache);
free_rows = next_extend;
- result->data = set = mnd_pecalloc(1, sizeof(MYSQLND_RES_BUFFERED), to_cache);
- set->data = mnd_pemalloc(STORE_RESULT_PREALLOCATED_SET * sizeof(zval **), to_cache);
- set->row_buffers= mnd_pemalloc(STORE_RESULT_PREALLOCATED_SET * sizeof(zend_uchar *),
to_cache);
+ result->stored_data = set = mnd_pecalloc(1, sizeof(MYSQLND_RES_BUFFERED), to_cache);
+ set->row_buffers= mnd_pemalloc(STORE_RESULT_PREALLOCATED_SET *
sizeof(MYSQLND_MEMORY_POOL_CHUNK *), to_cache);
set->persistent = to_cache;
set->qcache = to_cache? mysqlnd_qcache_get_cache_reference(conn->qcache):NULL;
set->references = 1;
- PACKET_INIT_ALLOCA(row_packet, PROT_ROW_PACKET);
- row_packet.field_count = meta->field_count;
- row_packet.binary_protocol = binary_protocol;
- row_packet.fields_metadata = meta->fields;
- row_packet.bit_fields_count = meta->bit_fields_count;
- row_packet.bit_fields_total_len = meta->bit_fields_total_len;
- /* Let the row packet fill our buffer and skip additional malloc + memcpy */
- while (FAIL != (ret = PACKET_READ_ALLOCA(row_packet, conn)) && !row_packet.eof)
{
- int i;
- zval **current_row;
+ PACKET_INIT(row_packet, PROT_ROW_PACKET, php_mysql_packet_row *);
+ row_packet->field_count = meta->field_count;
+ row_packet->binary_protocol = binary_protocol;
+ row_packet->fields_metadata = meta->fields;
+ row_packet->bit_fields_count = meta->bit_fields_count;
+ row_packet->bit_fields_total_len = meta->bit_fields_total_len;
+ row_packet->skip_extraction = TRUE; /* let php_mysqlnd_rowp_read() not allocate
row_packet->fields, we will do it */
+
+ while (FAIL != (ret = PACKET_READ(row_packet, conn)) && !row_packet->eof) {
+
if (!free_rows) {
mynd_ulonglong total_rows = free_rows = next_extend = next_extend * 5 / 3; /* extend
with 33% */
total_rows += set->row_count;
- set->data = mnd_perealloc(set->data, total_rows * sizeof(zval **),
set->persistent);
-
set->row_buffers = mnd_perealloc(set->row_buffers,
- total_rows * sizeof(zend_uchar *), set->persistent);
+ total_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *),
+ set->persistent);
}
free_rows--;
- current_row = set->data[set->row_count] = row_packet.fields;
- set->row_buffers[set->row_count] = row_packet.row_buffer;
+ set->row_buffers[set->row_count] = row_packet->row_buffer;
+
+ result->m.row_decoder = binary_protocol? php_mysqlnd_rowp_read_binary_protocol:
+ php_mysqlnd_rowp_read_text_protocol;
+
set->row_count++;
/* So row_packet's destructor function won't efree() it */
- row_packet.fields = NULL;
- row_packet.row_buffer = NULL;
+ row_packet->fields = NULL;
+ row_packet->row_buffer = NULL;
-
- if (update_max_length == TRUE) {
- for (i = 0; i < row_packet.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 (meta->fields[i].max_length < len) {
- meta->fields[i].max_length = len;
- }
- }
- }
- }
/*
No need to FREE_ALLOCA as we can reuse the
'lengths' and 'fields' arrays. For lengths its absolutely safe.
@@ -812,42 +985,41 @@
transfered above.
*/
}
+ /* Overflow ? */
+ set->data = mnd_pecalloc(set->row_count * meta->field_count, sizeof(zval *),
to_cache);
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);
/* Finally clean */
- if (row_packet.eof) {
- conn->upsert_status.warning_count = row_packet.warning_count;
- conn->upsert_status.server_status = row_packet.server_status;
+ if (row_packet->eof) {
+ conn->upsert_status.warning_count = row_packet->warning_count;
+ conn->upsert_status.server_status = row_packet->server_status;
}
/* save some memory */
if (free_rows) {
- set->data = mnd_perealloc(set->data,
- (size_t) set->row_count * sizeof(zval **),
- set->persistent);
set->row_buffers = mnd_perealloc(set->row_buffers,
- (size_t) set->row_count * sizeof(zend_uchar *),
- set->persistent);
+ set->row_count * sizeof(MYSQLND_MEMORY_POOL_CHUNK *),
+ set->persistent);
}
if (conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) {
- conn->state = CONN_NEXT_RESULT_PENDING;
+ CONN_SET_STATE(conn, CONN_NEXT_RESULT_PENDING);
} else {
- conn->state = CONN_READY;
+ CONN_SET_STATE(conn, CONN_READY);
}
if (ret == FAIL) {
- set->error_info = row_packet.error_info;
+ set->error_info = row_packet->error_info;
} else {
/* Position at the first row */
set->data_cursor = set->data;
/* libmysql's documentation says it should be so for SELECT statements */
- conn->upsert_status.affected_rows = result->data->row_count;
+ conn->upsert_status.affected_rows = set->row_count;
}
- PACKET_FREE_ALLOCA(row_packet);
+ PACKET_FREE(row_packet);
DBG_INF_FMT("ret=%s row_count=%u warns=%u status=%u", ret == PASS? "PASS":"FAIL",
set->row_count, conn->upsert_status.warning_count,
conn->upsert_status.server_status);
@@ -868,22 +1040,23 @@
DBG_ENTER("mysqlnd_res::store_result");
DBG_INF_FMT("conn=%d ps_protocol=%d", conn->thread_id, ps_protocol);
- result->conn = NULL; /* store result does not reference the connection */
+ /* We need the conn because we are doing lazy zval initialization in buffered_fetch_row
*/
+ result->conn = conn->m->get_reference(conn);
result->type = MYSQLND_RES_NORMAL;
result->m.fetch_row = result->m.fetch_row_normal_buffered;
result->m.fetch_lengths = mysqlnd_fetch_lengths_buffered;
- conn->state = CONN_FETCHING_DATA;
+ CONN_SET_STATE(conn, CONN_FETCHING_DATA);
result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long));
ret = mysqlnd_store_result_fetch_data(conn, result, result->meta,
- ps_protocol, TRUE, to_cache TSRMLS_CC);
+ ps_protocol, to_cache TSRMLS_CC);
if (PASS == ret) {
/* libmysql's documentation says it should be so for SELECT statements */
- conn->upsert_status.affected_rows = result->data->row_count;
+ conn->upsert_status.affected_rows = result->stored_data->row_count;
} else {
- conn->error_info = result->data->error_info;
+ conn->error_info = result->stored_data->error_info;
result->m.free_result_internal(result TSRMLS_CC);
result = NULL;
}
@@ -893,6 +1066,303 @@
/* }}} */
+/* {{{ mysqlnd_fetch_row_async_buffered */
+static enum_func_status
+mysqlnd_fetch_row_async_buffered(MYSQLND_RES *result, void *param, unsigned int flags,
+ zend_bool *fetched_anything TSRMLS_DC)
+{
+ unsigned int i;
+ zval *row = (zval *) param;
+ MYSQLND_RES_BG_BUFFERED *set = result->bg_stored_data;
+
+ DBG_ENTER("mysqlnd_fetch_row_async_buffered");
+ DBG_INF_FMT("flags=%u row=%p", flags, row);
+
+ do {
+ tsrm_mutex_lock(set->LOCK);
+ if (set->bg_fetch_finished == TRUE) {
+ break;
+ }
+ if (!set->data_cursor || (set->data_cursor - set->data) <
(set->row_count)) {
+#if HAVE_USLEEP
+ tsrm_mutex_unlock(set->LOCK);
+ usleep(2000);
+#else
+ volatile int i = 0;
+ for (int i = 0; i < 100; i++);
+#endif
+ } else {
+ break;
+ }
+ } while (1);
+
+ /* At the point we are still under LOCK */
+ if (set->data_cursor && (set->data_cursor - set->data) <
(set->row_count)) {
+ mynd_ulonglong row_num = set->data_cursor - set->data;
+ zval **current_row = *set->data_cursor++;
+ set->initialized_rows++;
+ /* We don't forget to release the lock */
+ tsrm_mutex_unlock(set->LOCK);
+
+ if (set->decode_in_foreground == TRUE) {
+ MYSQLND_MEMORY_POOL_CHUNK *current_buffer = set->row_buffers[row_num];
+ result->m.row_decoder(current_buffer,
+ current_row,
+ result->meta->field_count,
+ result->meta->fields,
+ result->conn TSRMLS_CC);
+
+ 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++) {
+ zval *data = current_row[i];
+
+ /*
+ Let us later know what to do with this zval. If ref_count > 1, we will just
+ decrease it, otherwise free it. zval_ptr_dtor() make this very easy job.
+ */
+ Z_ADDREF_P(data);
+
+ if ((flags & MYSQLND_FETCH_BOTH) == MYSQLND_FETCH_BOTH) {
+ Z_ADDREF_P(data);
+ }
+ if (flags & MYSQLND_FETCH_NUM) {
+ zend_hash_next_index_insert(Z_ARRVAL_P(row), &data, sizeof(zval *), NULL);
+ }
+ if (flags & MYSQLND_FETCH_ASSOC) {
+ /* zend_hash_quick_update needs length + trailing zero */
+ /* QQ: Error handling ? */
+ /*
+ zend_hash_quick_update does not check, as add_assoc_zval_ex do, whether
+ the index is a numeric and convert it to it. This however means constant
+ hashing of the column name, which is not needed as it can be precomputed.
+ */
+ if (result->meta->zend_hash_keys[i].is_numeric == FALSE) {
+#if PHP_MAJOR_VERSION >= 6
+ if (UG(unicode)) {
+ zend_u_hash_quick_update(Z_ARRVAL_P(row), IS_UNICODE,
+ result->meta->zend_hash_keys[i].ustr,
+ result->meta->zend_hash_keys[i].ulen + 1,
+ result->meta->zend_hash_keys[i].key,
+ (void *) &data, sizeof(zval *), NULL);
+ } else
+#endif
+ {
+ zend_hash_quick_update(Z_ARRVAL_P(row),
+ result->meta->fields[i].name,
+ result->meta->fields[i].name_length + 1,
+ result->meta->zend_hash_keys[i].key,
+ (void *) &data, sizeof(zval *), NULL);
+ }
+ } else {
+ zend_hash_index_update(Z_ARRVAL_P(row),
+ result->meta->zend_hash_keys[i].key,
+ (void *) &data, sizeof(zval *), NULL);
+ }
+ }
+ }
+ *fetched_anything = TRUE;
+ MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);
+ } else {
+ set->data_cursor = NULL;
+ /* We don't forget to release the lock */
+ tsrm_mutex_unlock(set->LOCK);
+ *fetched_anything = FALSE;
+ DBG_INF("EOF reached");
+ }
+
+ DBG_INF_FMT("ret=PASS fetched=%d", *fetched_anything);
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_background_store_result_fetch_data */
+enum_func_status
+mysqlnd_background_store_result_fetch_data(MYSQLND_RES *result TSRMLS_DC)
+{
+ enum_func_status ret;
+ php_mysql_packet_row *row_packet;
+ unsigned int next_extend = STORE_RESULT_PREALLOCATED_SET, free_rows;
+ MYSQLND_RES_BG_BUFFERED *set = result->bg_stored_data;
+ MYSQLND *conn = result->conn;
+
+ DBG_ENTER("mysqlnd_background_store_result_fetch_data");
+
+ free_rows = next_extend;
+
+ PACKET_INIT(row_packet, PROT_ROW_PACKET, php_mysql_packet_row *);
+ row_packet->field_count = result->meta->field_count;
+ row_packet->binary_protocol = result->m.row_decoder ==
php_mysqlnd_rowp_read_binary_protocol;
+ row_packet->fields_metadata = result->meta->fields;
+ row_packet->bit_fields_count = result->meta->bit_fields_count;
+ row_packet->bit_fields_total_len= result->meta->bit_fields_total_len;
+
+// row_packet->skip_extraction = TRUE; /* let php_mysqlnd_rowp_read() not allocate
row_packet->fields, we will do it */
+
+ while (FAIL != (ret = PACKET_READ(row_packet, conn)) && !row_packet->eof) {
+ tsrm_mutex_lock(set->LOCK);
+ if (!free_rows) {
+ mynd_ulonglong total_rows = free_rows = next_extend = next_extend * 5 / 3; /* extend
with 33% */
+ mynd_ulonglong old_size;
+ total_rows += set->row_count;
+
+ 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
**));
+ set->row_buffers = mnd_perealloc(set->row_buffers,
+ total_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *),
+ set->persistent);
+ }
+ set->row_buffers[set->row_count] = row_packet->row_buffer;
+ set->data[set->row_count] = row_packet->fields;
+
+ if (set->decode_in_foreground == FALSE) {
+ uint i;
+ result->m.row_decoder(set->row_buffers[set->row_count],
+ set->data[set->row_count],
+ result->meta->field_count,
+ result->meta->fields,
+ result->conn TSRMLS_CC);
+
+ 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(set->data[set->row_count][i]) >= IS_STRING) {
+ unsigned long len = Z_STRLEN_P(set->data[set->row_count][i]);
+ if (result->meta->fields[i].max_length < len) {
+ result->meta->fields[i].max_length = len;
+ }
+ }
+ }
+ }
+ set->row_count++;
+
+ tsrm_mutex_unlock(set->LOCK);
+ free_rows--;
+
+ /* So row_packet's destructor function won't efree() it */
+ row_packet->row_buffer = NULL;
+ row_packet->fields = NULL;
+
+ /*
+ No need to FREE_ALLOCA as we can reuse the
+ 'lengths' and 'fields' arrays. For lengths its absolutely safe.
+ 'fields' is reused because the ownership of the strings has been
+ 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);
+
+ tsrm_mutex_lock(set->LOCK);
+ /* Finally clean */
+ if (row_packet->eof) {
+ set->upsert_status.warning_count = row_packet->warning_count;
+ set->upsert_status.server_status = row_packet->server_status;
+ }
+ /* save some memory */
+ if (free_rows) {
+ set->data_size = set->row_count;
+ set->data = mnd_perealloc(set->data,
+ (size_t) set->data_size * sizeof(zval **), set->persistent);
+ set->row_buffers = mnd_perealloc(set->row_buffers,
+ set->row_count * sizeof(MYSQLND_MEMORY_POOL_CHUNK *),
+ set->persistent);
+ }
+ if (ret == FAIL) {
+ set->error_info = row_packet->error_info;
+ } else {
+ /* Position at the first row */
+ set->data_cursor = set->data;
+
+ /* libmysql's documentation says it should be so for SELECT statements */
+ conn->upsert_status.affected_rows = set->row_count;
+ set->upsert_status.affected_rows = set->row_count;
+ }
+ set->bg_fetch_finished = TRUE;
+ tsrm_mutex_unlock(set->LOCK);
+
+ PACKET_FREE(row_packet);
+
+ if (conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) {
+ CONN_SET_STATE(conn, CONN_NEXT_RESULT_PENDING);
+ } else {
+ CONN_SET_STATE(conn, CONN_READY);
+ }
+ DBG_INF_FMT("ret=%s row_count=%u warns=%u status=%u", ret == PASS? "PASS":"FAIL",
+ set->row_count, conn->upsert_status.warning_count,
conn->upsert_status.server_status);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::background_store_result */
+MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_res, background_store_result)(MYSQLND_RES * result, MYSQLND *
const conn, zend_bool ps TSRMLS_DC)
+{
+ enum_func_status ret;
+ zend_bool to_cache = FALSE;
+
+ DBG_ENTER("mysqlnd_res::background_store_result");
+ DBG_INF_FMT("conn=%d ps_protocol=%d", conn->thread_id, ps);
+
+#ifndef ZTS
+ DBG_RETURN(result->m.store_result(result, conn, ps_protocol));
+#else
+
+ /* We need the conn because we are doing lazy zval initialization in buffered_fetch_row
*/
+ result->conn = conn->m->get_reference(conn);
+ result->type = MYSQLND_RES_NORMAL;
+ result->m.fetch_row = mysqlnd_fetch_row_async_buffered;
+ result->m.fetch_lengths = mysqlnd_fetch_lengths_async_buffered;
+
+ result->bg_stored_data = mnd_pecalloc(1, sizeof(MYSQLND_RES_BG_BUFFERED), to_cache);
+ result->bg_stored_data->data_size = STORE_RESULT_PREALLOCATED_SET;
+ result->bg_stored_data->data =
mnd_pecalloc(result->bg_stored_data->data_size, sizeof(zval **), to_cache);
+ result->bg_stored_data->row_buffers = mnd_pemalloc(STORE_RESULT_PREALLOCATED_SET *
sizeof(MYSQLND_MEMORY_POOL_CHUNK *), to_cache);
+ result->bg_stored_data->persistent = to_cache;
+ result->bg_stored_data->qcache = to_cache?
mysqlnd_qcache_get_cache_reference(conn->qcache):NULL;
+ result->bg_stored_data->references = 1;
+
+ result->bg_stored_data->LOCK = tsrm_mutex_alloc();
+
+ result->m.row_decoder = ps? php_mysqlnd_rowp_read_binary_protocol:
+ php_mysqlnd_rowp_read_text_protocol;
+
+ CONN_SET_STATE(conn, CONN_FETCHING_DATA);
+ result->bg_stored_data->decode_in_foreground = FALSE;
+
+ result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long));
+
+ ret = mysqlnd_background_store_result_fetch_data(result TSRMLS_CC);
+
+ DBG_RETURN(result);
+#endif
+}
+/* }}} */
+
+
/* {{{ mysqlnd_res::skip_result */
static enum_func_status
MYSQLND_METHOD(mysqlnd_res, skip_result)(MYSQLND_RES * const result TSRMLS_DC)
@@ -905,7 +1375,7 @@
A PS could be prepared - there is metadata and thus a stmt->result but the
fetch_row function isn't actually set (NULL), thus we have to skip these.
*/
- if (!result->data && result->conn && result->unbuf &&
+ if (!result->stored_data && result->unbuf &&
!result->unbuf->eof_reached && result->m.fetch_row)
{
DBG_INF("skipping result");
@@ -950,15 +1420,15 @@
DBG_ENTER("mysqlnd_res::data_seek");
DBG_INF_FMT("row=%lu", row);
- if (!result->data) {
+ if (!result->stored_data) {
return FAIL;
}
/* libmysql just moves to the end, it does traversing of a linked list */
- if (row >= result->data->row_count) {
- result->data->data_cursor = NULL;
+ if (row >= result->stored_data->row_count) {
+ result->stored_data->data_cursor = NULL;
} else {
- result->data->data_cursor = result->data->data + row;
+ result->stored_data->data_cursor = result->stored_data->data + row *
result->meta->field_count;
}
DBG_RETURN(PASS);
@@ -968,19 +1438,19 @@
/* {{{ mysqlnd_res::num_fields */
mynd_ulonglong
-MYSQLND_METHOD(mysqlnd_res, num_rows)(const MYSQLND_RES * const res)
+MYSQLND_METHOD(mysqlnd_res, num_rows)(const MYSQLND_RES * const result)
{
/* Be compatible with libmysql. We count row_count, but will return 0 */
- return res->data? res->data->row_count:0;
+ return result->stored_data? result->stored_data->row_count:0;
}
/* }}} */
/* {{{ mysqlnd_res::num_fields */
unsigned int
-MYSQLND_METHOD(mysqlnd_res, num_fields)(const MYSQLND_RES * const res)
+MYSQLND_METHOD(mysqlnd_res, num_fields)(const MYSQLND_RES * const result)
{
- return res->field_count;
+ return result->field_count;
}
/* }}} */
@@ -990,18 +1460,33 @@
MYSQLND_METHOD(mysqlnd_res, fetch_field)(MYSQLND_RES * const result TSRMLS_DC)
{
DBG_ENTER("mysqlnd_res::fetch_field");
- DBG_RETURN(result->meta? result->meta->m->fetch_field(result->meta
TSRMLS_CC):NULL);
+ if (result->meta) {
+ if (result->stored_data && (result->stored_data->initialized_rows <
result->stored_data->row_count)) {
+ /* we have to initialize the rest to get the updated max length */
+ mysqlnd_res_initialize_result_set_rest(result TSRMLS_CC);
+ }
+ DBG_RETURN(result->meta->m->fetch_field(result->meta TSRMLS_CC));
+ }
+ DBG_RETURN(NULL);
}
/* }}} */
/* {{{ mysqlnd_res::fetch_field_direct */
static MYSQLND_FIELD *
-MYSQLND_METHOD(mysqlnd_res, fetch_field_direct)(const MYSQLND_RES * const result,
+MYSQLND_METHOD(mysqlnd_res, fetch_field_direct)(MYSQLND_RES * const result,
MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC)
{
DBG_ENTER("mysqlnd_res::fetch_field_direct");
- DBG_RETURN(result->meta?
result->meta->m->fetch_field_direct(result->meta, fieldnr TSRMLS_CC):NULL);
+ if (result->meta) {
+ if (result->stored_data && (result->stored_data->initialized_rows <
result->stored_data->row_count)) {
+ /* we have to initialized the rest to get the updated max length */
+ mysqlnd_res_initialize_result_set_rest(result TSRMLS_CC);
+ }
+ DBG_RETURN(result->meta->m->fetch_field_direct(result->meta, fieldnr
TSRMLS_CC));
+ }
+
+ DBG_RETURN(NULL);
}
/* }}} */
@@ -1081,23 +1566,24 @@
{
zval *row;
ulong i = 0;
+ MYSQLND_RES_BUFFERED *set = result->stored_data;
DBG_ENTER("mysqlnd_res::fetch_all");
DBG_INF_FMT("flags=%u", flags);
/* mysqlnd_res::fetch_all works with buffered resultsets only */
- if (result->conn || !result->data ||
- !result->data->row_count || !result->data->data_cursor ||
- result->data->data_cursor >= result->data->data +
result->data->row_count)
+ if (!set ||
+ !set->row_count || !set->data_cursor ||
+ set->data_cursor >= set->data + set->row_count)
{
RETVAL_NULL();
DBG_VOID_RETURN;
}
- mysqlnd_array_init(return_value, (uint) result->data->row_count);
+ mysqlnd_array_init(return_value, (uint) set->row_count);
- while (result->data->data_cursor &&
- (result->data->data_cursor - result->data->data) <
result->data->row_count)
+ while (set->data_cursor &&
+ (set->data_cursor - set->data) < (set->row_count *
result->meta->field_count))
{
MAKE_STD_ZVAL(row);
mysqlnd_fetch_into(result, flags, row, MYSQLND_MYSQLI);
@@ -1109,7 +1595,7 @@
/* }}} */
-/* {{{ mysqlnd_res::fetch_into */
+/* {{{ mysqlnd_res::fetch_field_data */
static void
MYSQLND_METHOD(mysqlnd_res, fetch_field_data)(MYSQLND_RES *result, unsigned int offset,
zval *return_value TSRMLS_DC)
@@ -1167,6 +1653,7 @@
ret->m.use_result = MYSQLND_METHOD(mysqlnd_res, use_result);
ret->m.store_result = MYSQLND_METHOD(mysqlnd_res, store_result);
+ ret->m.background_store_result = MYSQLND_METHOD(mysqlnd_res,
background_store_result);
ret->m.free_result = MYSQLND_METHOD(mysqlnd_res, free_result);
ret->m.seek_data = MYSQLND_METHOD(mysqlnd_res, data_seek);
ret->m.num_rows = MYSQLND_METHOD(mysqlnd_res, num_rows);
@@ -1187,6 +1674,7 @@
ret->m.read_result_metadata = MYSQLND_METHOD(mysqlnd_res, read_result_metadata);
ret->m.fetch_row_normal_buffered = mysqlnd_fetch_row_buffered;
ret->m.fetch_row_normal_unbuffered = mysqlnd_fetch_row_unbuffered;
+ ret->m.row_decoder = NULL;
DBG_RETURN(ret);
}
Modified: branches/experimental/mysqlnd/mysqlnd_result.h
===================================================================
--- branches/experimental/mysqlnd/mysqlnd_result.h 2007-11-19 16:35:37 UTC (rev 1154)
+++ branches/experimental/mysqlnd/mysqlnd_result.h 2007-11-19 17:15:39 UTC (rev 1155)
@@ -31,11 +31,14 @@
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 */
/*
Modified: branches/experimental/mysqlnd/mysqlnd_result_meta.c
===================================================================
--- branches/experimental/mysqlnd/mysqlnd_result_meta.c 2007-11-19 16:35:37 UTC (rev 1154)
+++ branches/experimental/mysqlnd/mysqlnd_result_meta.c 2007-11-19 17:15:39 UTC (rev 1155)
@@ -421,8 +421,8 @@
/* +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);
Modified: branches/experimental/mysqlnd/mysqlnd_statistics.c
===================================================================
--- branches/experimental/mysqlnd/mysqlnd_statistics.c 2007-11-19 16:35:37 UTC (rev 1154)
+++ branches/experimental/mysqlnd/mysqlnd_statistics.c 2007-11-19 17:15:39 UTC (rev 1155)
@@ -92,7 +92,7 @@
{ STR_W_LEN("mem_calloc_ammount") },
{ STR_W_LEN("mem_realloc_calloc") },
{ STR_W_LEN("mem_realloc_ammount") },
- { STR_W_LEN("mem_free_count") }
+ { STR_W_LEN("mem_free_count") },
};
/* }}} */
Modified: branches/experimental/mysqlnd/mysqlnd_structs.h
===================================================================
--- branches/experimental/mysqlnd/mysqlnd_structs.h 2007-11-19 16:35:37 UTC (rev 1154)
+++ branches/experimental/mysqlnd/mysqlnd_structs.h 2007-11-19 17:15:39 UTC (rev 1155)
@@ -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
+{
+ mynd_ulonglong 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;
@@ -163,6 +196,7 @@
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;
@@ -206,6 +240,7 @@
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);
@@ -250,6 +285,8 @@
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);
};
@@ -261,6 +298,7 @@
MYSQLND_RES * (*use_result)(MYSQLND_RES * const result, zend_bool ps 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);
void (*fetch_all)(MYSQLND_RES *result, unsigned int flags, zval *return_value
TSRMLS_DC ZEND_FILE_LINE_DC);
void (*fetch_field_data)(MYSQLND_RES *result, unsigned int offset, zval
*return_value TSRMLS_DC);
@@ -271,7 +309,7 @@
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 @@
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 @@
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, mynd_ulonglong row
TSRMLS_DC);
@@ -404,6 +446,20 @@
/* 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;
};
@@ -435,18 +491,43 @@
};
-struct st_mysqlnd_buffered_result
+struct st_mysqlnd_background_buffered_result
{
zval ***data;
+ mynd_ulonglong data_size;
zval ***data_cursor;
- zend_uchar **row_buffers;
+ MYSQLND_MEMORY_POOL_CHUNK **row_buffers;
mynd_ulonglong row_count;
+ mynd_ulonglong initialized_rows;
zend_bool persistent;
MYSQLND_QCACHE *qcache;
unsigned int references;
- zend_bool async_invalid;
+ 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;
+ mynd_ulonglong row_count;
+ mynd_ulonglong initialized_rows;
+ zend_bool persistent;
+
+ MYSQLND_QCACHE *qcache;
+ unsigned int references;
+
mysqlnd_error_info error_info;
};
@@ -455,7 +536,7 @@
{
/* For unbuffered (both normal and PS) */
zval **last_row_data;
- zend_uchar *last_row_buffer;
+ MYSQLND_MEMORY_POOL_CHUNK *last_row_buffer;
mynd_ulonglong row_count;
zend_bool eof_reached;
@@ -474,10 +555,10 @@
MYSQLND_RES_METADATA *meta;
/* To be used with store_result() - both normal and PS */
- MYSQLND_RES_BUFFERED *data;
+ MYSQLND_RES_BUFFERED *stored_data;
+ MYSQLND_RES_BG_BUFFERED *bg_stored_data;
+ MYSQLND_RES_UNBUFFERED *unbuf;
- MYSQLND_RES_UNBUFFERED *unbuf;
-
/*
Column lengths of current row - both buffered and unbuffered.
For buffered results it duplicates the data found in **data
Modified: branches/experimental/mysqlnd/mysqlnd_wireprotocol.c
===================================================================
--- branches/experimental/mysqlnd/mysqlnd_wireprotocol.c 2007-11-19 16:35:37 UTC (rev
1154)
+++ branches/experimental/mysqlnd/mysqlnd_wireprotocol.c 2007-11-19 17:15:39 UTC (rev
1155)
@@ -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 @@
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);
}
@@ -1233,13 +1232,13 @@
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,
+ mynd_ulonglong *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");
@@ -1261,13 +1260,14 @@
*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) {
@@ -1280,9 +1280,9 @@
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)) {
@@ -1296,8 +1296,9 @@
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);
@@ -1305,11 +1306,11 @@
/* {{{ 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)
{
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;
@@ -1318,14 +1319,14 @@
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
@@ -1343,8 +1344,8 @@
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)) {
@@ -1352,8 +1353,6 @@
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;
}
@@ -1361,14 +1360,15 @@
/* {{{ 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)
{
int i;
zend_bool last_field_was_string;
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;
@@ -1376,7 +1376,7 @@
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;
@@ -1417,7 +1417,7 @@
} 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
@@ -1452,7 +1452,7 @@
*(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
@@ -1463,7 +1463,7 @@
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
@@ -1531,7 +1531,7 @@
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 */
@@ -1560,7 +1560,7 @@
}
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;
@@ -1579,10 +1579,10 @@
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;
+ mynd_ulonglong data_size = 0;
DBG_ENTER("php_mysqlnd_rowp_read");
@@ -1592,17 +1592,18 @@
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:
@@ -1645,15 +1646,9 @@
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);
+ packet->fields = (zval **) mnd_pecalloc(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);
- }
} else {
MYSQLND_INC_CONN_STATISTIC(&conn->stats,
packet->binary_protocol? STAT_ROWS_SKIPPED_PS:
@@ -1674,7 +1669,7 @@
{
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;
}
/*
Modified: branches/experimental/mysqlnd/mysqlnd_wireprotocol.h
===================================================================
--- branches/experimental/mysqlnd/mysqlnd_wireprotocol.h 2007-11-19 16:35:37 UTC (rev
1154)
+++ branches/experimental/mysqlnd/mysqlnd_wireprotocol.h 2007-11-19 17:15:39 UTC (rev
1155)
@@ -258,7 +258,7 @@
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 @@
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 */
/*
Modified: branches/experimental/php5/ext/mysqli/mysqli.c
===================================================================
--- branches/experimental/php5/ext/mysqli/mysqli.c 2007-11-19 16:35:37 UTC (rev 1154)
+++ branches/experimental/php5/ext/mysqli/mysqli.c 2007-11-19 17:15:39 UTC (rev 1155)
@@ -31,9 +31,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);
@@ -688,8 +685,11 @@
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)
+ 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);
@@ -962,7 +962,7 @@
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;
@@ -977,10 +977,6 @@
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;
@@ -988,8 +984,21 @@
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;
+#ifdef HAVE_MYSQLND
+ 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;
Modified: branches/experimental/php5/ext/mysqli/mysqli_mysqlnd.h
===================================================================
--- branches/experimental/php5/ext/mysqli/mysqli_mysqlnd.h 2007-11-19 16:35:37 UTC (rev
1154)
+++ branches/experimental/php5/ext/mysqli/mysqli_mysqlnd.h 2007-11-19 17:15:39 UTC (rev
1155)
@@ -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
Modified: branches/experimental/php5/ext/mysqli/mysqli_nonapi.c
===================================================================
--- branches/experimental/php5/ext/mysqli/mysqli_nonapi.c 2007-11-19 16:35:37 UTC (rev
1154)
+++ branches/experimental/php5/ext/mysqli/mysqli_nonapi.c 2007-11-19 17:15:39 UTC (rev
1155)
@@ -459,7 +459,11 @@
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
+#ifdef HAVE_MYSQLND
+ && resultmode != MYSQLI_BG_STORE_RESULT
+#endif
+ ) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid value for resultmode");
RETURN_FALSE;
}
@@ -468,6 +472,7 @@
MYSQLI_DISABLE_MQ;
+
if (mysql_real_query(mysql->mysql, query, query_len)) {
MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
RETURN_FALSE;
@@ -481,7 +486,19 @@
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;
+#ifdef HAVE_MYSQLND
+ 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,
Modified: branches/experimental/php5/ext/mysqli/mysqli_prop.c
===================================================================
--- branches/experimental/php5/ext/mysqli/mysqli_prop.c 2007-11-19 16:35:37 UTC (rev 1154)
+++ branches/experimental/php5/ext/mysqli/mysqli_prop.c 2007-11-19 17:15:39 UTC (rev 1155)
@@ -211,7 +211,7 @@
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;
}
Modified: branches/experimental/php5/ext/mysqli/php_mysqli_structs.h
===================================================================
--- branches/experimental/php5/ext/mysqli/php_mysqli_structs.h 2007-11-19 16:35:37 UTC
(rev 1154)
+++ branches/experimental/php5/ext/mysqli/php_mysqli_structs.h 2007-11-19 17:15:39 UTC
(rev 1155)
@@ -298,6 +298,9 @@
#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
Modified: branches/experimental/php6/ext/mysqli/mysqli.c
===================================================================
--- branches/experimental/php6/ext/mysqli/mysqli.c 2007-11-19 16:35:37 UTC (rev 1154)
+++ branches/experimental/php6/ext/mysqli/mysqli.c 2007-11-19 17:15:39 UTC (rev 1155)
@@ -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 @@
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)
+ 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);
@@ -959,7 +959,7 @@
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;
@@ -974,10 +974,6 @@
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;
@@ -985,8 +981,21 @@
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;
+#ifdef HAVE_MYSQLND
+ 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;
Modified: branches/experimental/php6/ext/mysqli/mysqli_mysqlnd.h
===================================================================
--- branches/experimental/php6/ext/mysqli/mysqli_mysqlnd.h 2007-11-19 16:35:37 UTC (rev
1154)
+++ branches/experimental/php6/ext/mysqli/mysqli_mysqlnd.h 2007-11-19 17:15:39 UTC (rev
1155)
@@ -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
Modified: branches/experimental/php6/ext/mysqli/mysqli_nonapi.c
===================================================================
--- branches/experimental/php6/ext/mysqli/mysqli_nonapi.c 2007-11-19 16:35:37 UTC (rev
1154)
+++ branches/experimental/php6/ext/mysqli/mysqli_nonapi.c 2007-11-19 17:15:39 UTC (rev
1155)
@@ -473,7 +473,11 @@
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
+#ifdef HAVE_MYSQLND
+ && resultmode != MYSQLI_BG_STORE_RESULT
+#endif
+ ) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid value for resultmode");
RETURN_FALSE;
}
@@ -495,8 +499,19 @@
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;
+#ifdef HAVE_MYSQLND
+ 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));
Modified: branches/experimental/php6/ext/mysqli/mysqli_prop.c
===================================================================
--- branches/experimental/php6/ext/mysqli/mysqli_prop.c 2007-11-19 16:35:37 UTC (rev 1154)
+++ branches/experimental/php6/ext/mysqli/mysqli_prop.c 2007-11-19 17:15:39 UTC (rev 1155)
@@ -213,7 +213,7 @@
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;
}
Modified: branches/experimental/php6/ext/mysqli/php_mysqli_structs.h
===================================================================
--- branches/experimental/php6/ext/mysqli/php_mysqli_structs.h 2007-11-19 16:35:37 UTC
(rev 1154)
+++ branches/experimental/php6/ext/mysqli/php_mysqli_structs.h 2007-11-19 17:15:39 UTC
(rev 1155)
@@ -311,6 +311,9 @@
#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
| Thread |
|---|
| • PHP mysqlnd svn commit: r1155 - in branches/experimental: mysqlnd php5/ext/mysqli php6/ext/mysqli | ahristov | 19 Nov |