List:Commits« Previous MessageNext Message »
From:ahristov Date:November 19 2007 5:15pm
Subject:PHP mysqlnd svn commit: r1155 - in branches/experimental: mysqlnd php5/ext/mysqli php6/ext/mysqli
View as plain text  
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, &copy_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, &copy_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/mysqliahristov19 Nov