List:Commits« Previous MessageNext Message »
From:ahristov Date:June 14 2007 11:22am
Subject:PHP mysqlnd svn commit: r370 - trunk/mysqlnd
View as plain text  
Author: ahristov
Date: 2007-06-14 13:22:25 +0200 (Thu, 14 Jun 2007)
New Revision: 370

Added:
   trunk/mysqlnd/mysqlnd_result.c
   trunk/mysqlnd/mysqlnd_result.h
Modified:
   trunk/mysqlnd/mysqlnd.c
   trunk/mysqlnd/mysqlnd_priv.h
   trunk/mysqlnd/mysqlnd_ps.c
Log:
Split mysqlnd.c by moving MYSQLND_RES related functions
to mysqlnd_result.c . Now both files are like 1300 lines of code
used to be 2.6kloc, too big.


Modified: trunk/mysqlnd/mysqlnd.c
===================================================================
--- trunk/mysqlnd/mysqlnd.c	2007-06-14 09:44:19 UTC (rev 369)
+++ trunk/mysqlnd/mysqlnd.c	2007-06-14 11:22:25 UTC (rev 370)
@@ -23,6 +23,7 @@
 #include "mysqlnd.h"
 #include "mysqlnd_wireprotocol.h"
 #include "mysqlnd_priv.h"
+#include "mysqlnd_result.h"
 #include "mysqlnd_statistics.h"
 #include "mysqlnd_charset.h"
 #include "ext/standard/basic_functions.h"
@@ -94,208 +95,6 @@
 /* }}} */
 
 
-/* {{{ php_mysqlnd_free_field_metadata */
-static
-void php_mysqlnd_free_field_metadata(MYSQLND_FIELD *meta, zend_bool persistent)
-{
-	if (meta) {
-		if (meta->root) {
-			pefree(meta->root, persistent);
-			meta->root = NULL;
-		}
-		if (meta->def) {
-			pefree(meta->def, persistent);
-			meta->def = NULL;
-		}
-	}
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_unbuffered_free_last_data */
-void mysqlnd_unbuffered_free_last_data(MYSQLND_RES *result TSRMLS_DC)
-{
-	MYSQLND_RES_UNBUFFERED *unbuf = result->unbuf;
-	if (!unbuf) {
-		return;
-	}
-
-	if (unbuf->last_row_data) {
-		unsigned int i, ctor_called_count = 0;
-		zend_bool copy_ctor_called;
-		MYSQLND_STATS *global_stats = result->conn? &result->conn->stats:NULL;
-		for (i = 0; i < result->field_count; i++) {
-			mysqlnd_palloc_zval_ptr_dtor(&(unbuf->last_row_data[i]),
-										 result->zval_cache,
-										 result->type == MYSQLND_RES_PS,
-										 &copy_ctor_called TSRMLS_CC);
-			if (copy_ctor_called) {
-				ctor_called_count++;
-			}
-		}
-		/* By using value3 macros we hold a mutex only once, there is no value2 */
-		MYSQLND_INC_CONN_STATISTIC_W_VALUE3(global_stats,
-											STAT_COPY_ON_WRITE_PERFORMED,
-											ctor_called_count,
-											STAT_COPY_ON_WRITE_SAVED,
-											result->field_count - ctor_called_count,
-											STAT_COPY_ON_WRITE_PERFORMED, 0);
-		
-		/* Free last row's zvals */
-		efree(unbuf->last_row_data);
-		unbuf->last_row_data = NULL;
-	}
-	if (unbuf->last_row_buffer) {
-		/* Nothing points to this buffer now, free it */
-		efree(unbuf->last_row_buffer);
-		unbuf->last_row_buffer = NULL;
-	}
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_free_buffered_data */
-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;
-	enum_mysqlnd_res_type result_type = result->type;
-	unsigned int field_count = result->field_count;
-	unsigned int row;
-
-	for (row = 0; row < result->data->row_count; 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;
-			mysqlnd_palloc_zval_ptr_dtor(&(current_row[col]), zval_cache,
-										 result_type == MYSQLND_RES_PS,
-										 &copy_ctor_called TSRMLS_CC);
-			MYSQLND_INC_CONN_STATISTIC(NULL, copy_ctor_called? STAT_COPY_ON_WRITE_PERFORMED:
-															   STAT_COPY_ON_WRITE_SAVED);
-		}
-		pefree(current_row, set->persistent);
-		pefree(current_buffer, set->persistent);
-	}
-	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);
-	}
-	pefree(set, set->persistent);
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_internal_free_result_buffers */
-void mysqlnd_internal_free_result_buffers(MYSQLND_RES *result TSRMLS_DC)
-{
-
-	if (result->unbuf) {
-		mysqlnd_unbuffered_free_last_data(result TSRMLS_CC);
-		efree(result->unbuf);
-		result->unbuf = NULL;
-	} else if (result->data) {
-		mysqlnd_free_buffered_data(result TSRMLS_CC);
-		result->data = NULL;
-	}
-
-	if (result->lengths) {
-		efree(result->lengths);
-		result->lengths = NULL;
-	}
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_internal_free_result_metadata */
-void mysqlnd_internal_free_result_metadata(MYSQLND_RES_METADATA *meta, zend_bool
persistent TSRMLS_DC)
-{
-	int i;
-	MYSQLND_FIELD *fields;
-
-	if ((fields = meta->fields)) {
-		i = meta->field_count;
-		while (i--) {
-			php_mysqlnd_free_field_metadata(fields++, persistent);
-		}
-		pefree(meta->fields, persistent);
-		meta->fields = NULL;
-	}
-
-	if (meta->zend_hash_keys) {
-#if PHP_MAJOR_VERSION >= 6
-		if (UG(unicode)) {
-			for (i = 0; i < meta->field_count; i++) {
-				if (meta->zend_hash_keys[i].ustr.v) {
-					pefree(meta->zend_hash_keys[i].ustr.v, persistent);
-				}
-			}
-		}
-#endif
-		pefree(meta->zend_hash_keys, persistent);
-		meta->zend_hash_keys = NULL;
-	}
-	pefree(meta, persistent);
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_internal_free_result_contents */
-void mysqlnd_internal_free_result_contents(MYSQLND_RES *result TSRMLS_DC)
-{
-
-	result->m.free_result_buffers(result TSRMLS_CC);
-
-	if (result->row_packet) {
-		if (result->type == MYSQLND_RES_NORMAL) {
-			PACKET_FREE(result->row_packet);
-		} else {
-			PACKET_FREE(result->row_packet);
-		}
-		result->row_packet = NULL;
-	}
-
-	result->conn = NULL;
-
-	if (result->meta) {
-		mysqlnd_internal_free_result_metadata(result->meta, FALSE TSRMLS_CC);
-		result->meta = NULL;
-	}
-
-	if (result->zval_cache) {
-		mysqlnd_palloc_free_thd_cache_reference(&result->zval_cache);
-		result->zval_cache = NULL;
-	}
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_internal_free_result */
-void mysqlnd_internal_free_result(MYSQLND_RES *result TSRMLS_DC)
-{
-	/*
-	  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 quit_sent is no
-	*/
-	if (result->conn) {
-		result->conn->m->free_reference(result->conn TSRMLS_CC);
-		result->conn = NULL;
-	}
-
-	mysqlnd_internal_free_result_contents(result TSRMLS_CC);
-	efree(result);
-}
-/* }}} */
-
-
 /* {{{ mysqlnd_conn_free_contents */
 static
 void mysqlnd_conn_free_contents(MYSQLND *conn TSRMLS_DC)
@@ -835,357 +634,6 @@
 /* }}} */
 
 
-/* {{{ mysqlnd_handle_numeric */
-/*
-  The following code is stolen from ZE - HANDLE_NUMERIC() macro from zend_hash.c
-  and modified for the needs of mysqlnd.
-*/
-static
-zend_bool mysqlnd_is_key_numeric(char *key, size_t length, long *idx)
-{
-	register char *tmp=key;
-
-	if (*tmp=='-') {
-		tmp++;
-	}
-	if ((*tmp>='0' && *tmp<='9')) {
-		do { /* possibly a numeric index */
-			char *end=key+length-1;
-
-			if (*tmp++=='0' && length>2) { /* don't accept numbers with leading zeros
*/
-				break;
-			}
-			while (tmp<end) {
-				if (!(*tmp>='0' && *tmp<='9')) {
-					break;
-				}
-				tmp++;
-			}
-			if (tmp==end && *tmp=='\0') { /* a numeric index */
-				if (*key=='-') {
-					*idx = strtol(key, NULL, 10);
-					if (*idx!=LONG_MIN) {
-						return TRUE;
-					}
-				} else {
-					*idx = strtol(key, NULL, 10);
-					if (*idx!=LONG_MAX) {
-						return TRUE;
-					}
-				}
-			}
-		} while (0);
-	}
-	return FALSE;
-}
-/* }}} */
-
-
-#if PHP_MAJOR_VERSION >= 6
-/* {{{ mysqlnd_unicode_is_key_numeric */
-static
-zend_bool mysqlnd_unicode_is_key_numeric(UChar *key, size_t length, long *idx)
-{
-	register UChar *tmp=key;
-
-	if (*tmp==0x2D /*'-'*/) {
-		tmp++;
-	}
-	if ((*tmp>=0x30 /*'0'*/ && *tmp<=0x39 /*'9'*/)) { /* possibly a numeric
index */
-		do {
-			UChar *end=key+length-1;
-
-			if (*tmp++==0x30 && length>2) { /* don't accept numbers with leading zeros
*/
-				break;
-			}
-			while (tmp<end) {
-				if (!(*tmp>=0x30 /*'0'*/ && *tmp<=0x39 /*'9'*/)) {
-					break;
-				}
-				tmp++;
-			}
-			if (tmp==end && *tmp==0) { /* a numeric index */
-				if (*key==0x2D /*'-'*/) {
-					*idx = zend_u_strtol(key, NULL, 10);
-					if (*idx!=LONG_MIN) {
-						return TRUE;
-					}
-				} else {
-					*idx = zend_u_strtol(key, NULL, 10);
-					if (*idx!=LONG_MAX) {
-						return TRUE;
-					}
-				}
-			}
-		} while (0);
-	}
-	return FALSE;
-}
-/* }}} */
-#endif
-
-
-/* {{{ mysqlnd_read_result_metadata */
-enum_func_status
-mysqlnd_read_result_metadata(MYSQLND *conn, MYSQLND_RES *result TSRMLS_DC)
-{
-	int i = 0;
-	php_mysql_packet_res_field field_packet;
-
-	/*
-	  Make it safe to call it repeatedly for PS -
-	  better free and allocate a new because the number of field might change 
-	  (select *) with altered table. Also for statements which skip the PS
-	  infrastructure.
-	*/
-	if (result->meta) {
-		mysqlnd_internal_free_result_metadata(result->meta, FALSE TSRMLS_CC);
-		result->meta = NULL;
-	}
-
-	/* +1 is to have empty marker at the end */
-	result->meta = ecalloc(1, sizeof(MYSQLND_RES_METADATA));
-	result->meta->field_count = result->field_count;
-	result->meta->fields = ecalloc(result->field_count + 1, sizeof(MYSQLND_FIELD));
-	result->meta->zend_hash_keys = ecalloc(result->field_count,
-									 		sizeof(struct mysqlnd_field_hash_key));
-
-	/* 1. Read all fields metadata */
-
-	/* It's safe to reread without freeing */
-	PACKET_INIT_ALLOCA(field_packet, PROT_RSET_FLD_PACKET);
-	for (;i < result->field_count; i++) {
-		long idx;
-
-		if (result->meta->fields[i].root) {
-			/* We re-read metadata for PS */
-			efree(result->meta->fields[i].root);
-			result->meta->fields[i].root = NULL;
-		}
-
-		field_packet.metadata = &(result->meta->fields[i]);
-		if (FAIL == PACKET_READ_ALLOCA(field_packet, conn)) {
-			PACKET_FREE_ALLOCA(field_packet);
-			goto error;
-		}
-
-		if (mysqlnd_ps_fetch_functions[result->meta->fields[i].type].func == NULL) {
-			SET_CLIENT_ERROR(result->conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE,
-							 "Uknown field type sent by the server"); 
-			php_error_docref(NULL TSRMLS_CC, E_WARNING,
-							 "Unknown type %d sent by the server. "
-							 "Please send a report to the developers",
-							 result->meta->fields[i].type);
-			goto error;
-		}
-
-
-#if PHP_MAJOR_VERSION >= 6
-		if (UG(unicode)) {
-			UChar *ustr;
-			int ulen;
-			zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen,
-								   result->meta->fields[i].name,
-								   result->meta->fields[i].name_length TSRMLS_CC);
-			if ((result->meta->zend_hash_keys[i].is_numeric =
-				 			mysqlnd_unicode_is_key_numeric(ustr, ulen + 1, &idx)))
-			{
-				result->meta->zend_hash_keys[i].key = idx;
-				efree(ustr);
-			} else {
-				result->meta->zend_hash_keys[i].ustr.u = ustr;
-				result->meta->zend_hash_keys[i].ulen = ulen;
-				result->meta->zend_hash_keys[i].key = zend_u_get_hash_value(IS_UNICODE,
ZSTR(ustr), ulen + 1);
-			}
-
-		} else 
-#endif
-		{
-			/* For BC we have to check whether the key is numeric and use it like this */
-			if ((result->meta->zend_hash_keys[i].is_numeric =
-						mysqlnd_is_key_numeric(field_packet.metadata->name,
-									   		   field_packet.metadata->name_length + 1,
-											   &idx)))
-			{
-				result->meta->zend_hash_keys[i].key = idx;
-			} else {
-				result->meta->zend_hash_keys[i].key =
-						zend_get_hash_value(field_packet.metadata->name,
-											field_packet.metadata->name_length + 1);
-			}
-		}
-	}
-	PACKET_FREE_ALLOCA(field_packet);
-
-	/*
-	  2. Follows an EOF packet, which the client of mysqlnd_read_result_metadata()
-		 should consume.
-	  3. If there is a result set, it follows. The last packet will have 'eof' set
-	  	 If PS, then no result set follows.
-	*/
-
-	return PASS;
-error:
-	mysqlnd_internal_free_result_contents(result TSRMLS_CC);
-	return FAIL;
-}
-/* }}} */
-
-
-enum_func_status
-mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC)
-{
-	enum_func_status ret;
-	php_mysql_packet_rset_header rset_header;
-
-	ret = FAIL;
-	PACKET_INIT_ALLOCA(rset_header, PROT_RSET_HEADER_PACKET);
-	do {
-		if (FAIL == (ret = PACKET_READ_ALLOCA(rset_header, conn))) {
-			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error reading result set's header");
-			break;
-		}
-
-		if (rset_header.error_info.error_no) {
-			/*
-			  Cover a protocol design error: error packet does not
-			  contain the server status. Therefore, the client has no way
-			  to find out whether there are more result sets of
-			  a multiple-result-set statement pending. Luckily, in 5.0 an
-			  error always aborts execution of a statement, wherever it is
-			  a multi-statement or a stored procedure, so it should be
-			  safe to unconditionally turn off the flag here.
-			*/
-			conn->upsert_status.server_status &= ~SERVER_MORE_RESULTS_EXISTS;
-			conn->upsert_status.affected_rows = -1;
-			/*
-			  This will copy the error code and the messages, as they
-			  are buffers in the struct
-			*/
-			conn->error_info = rset_header.error_info;
-			ret = FAIL;
-			break;
-		}
-		conn->error_info.error_no = 0;
-
-		switch (rset_header.field_count) {
-			case MYSQLND_NULL_LENGTH: {	/* LOAD DATA LOCAL INFILE */
-				zend_bool is_warning;
-				conn->last_query_type = QUERY_LOAD_LOCAL;
-				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;
-				MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_NON_RSET_QUERY);
-				break;
-			}
-			case 0:				/* UPSERT		*/
-				conn->last_query_type = QUERY_UPSERT;
-				conn->field_count = rset_header.field_count;
-				conn->upsert_status.warning_count = rset_header.warning_count;
-				conn->upsert_status.server_status = rset_header.server_status;
-				conn->upsert_status.affected_rows = rset_header.affected_rows;
-				conn->upsert_status.last_insert_id = rset_header.last_insert_id;
-				SET_NEW_MESSAGE(conn->last_message, conn->last_message_len,
-								rset_header.info_or_local_file, rset_header.info_or_local_file_len);
-				/* 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;
-				} else {
-					conn->state = CONN_READY;
-				}
-				ret = PASS;
-				MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_NON_RSET_QUERY);
-				break;
-			default:{			/* Result set	*/
-				php_mysql_packet_eof fields_eof;
-				MYSQLND_RES *result;
-				uint stat = -1;
-
-				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;
-				/* PS has already allocated it */
-				if (!stmt) {
-					conn->field_count = rset_header.field_count;
-					result =
-						conn->current_result=
-							mysqlnd_result_init(rset_header.field_count,
-												mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache));
-				} else {
-					if (!stmt->result) {
-						/*
-						  This is 'SHOW'/'EXPLAIN'-like query. Current implementation of
-						  prepared statements can't send result set metadata for these queries
-						  on prepare stage. Read it now.
-						*/
-						conn->field_count = rset_header.field_count;
-						result =
-							stmt->result =
-								mysqlnd_result_init(rset_header.field_count,
-													mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache));
-					} else {
-						/*
-						  Update result set metadata if it for some reason changed between
-						  prepare and execute, i.e.:
-						  - in case of 'SELECT ?' we don't know column type unless data was
-							supplied to mysql_stmt_execute, so updated column type is sent
-							now.
-						  - if data dictionary changed between prepare and execute, for
-							example a table used in the query was altered.
-						  Note, that now (4.1.3) we always send metadata in reply to
-						  COM_STMT_EXECUTE (even if it is not necessary), so either this or
-						  previous branch always works.
-						*/	
-					}
-					result = stmt->result;
-				}
-
-				if (FAIL == (ret = mysqlnd_read_result_metadata(conn, result TSRMLS_CC))) {
-					/* For PS, we leave them in Prepared state */
-					if (!stmt) {
-						efree(conn->current_result);
-						conn->current_result = NULL;
-					}
-					break;
-				}
-
-				/* Check for SERVER_STATUS_MORE_RESULTS if needed */
-				PACKET_INIT_ALLOCA(fields_eof, PROT_EOF_PACKET);
-				if (FAIL == (ret = PACKET_READ_ALLOCA(fields_eof, conn))) {
-					mysqlnd_internal_free_result_contents(result TSRMLS_CC);
-					efree(result);
-					if (!stmt) {
-						conn->current_result = NULL;
-					} else {
-						stmt->result = NULL;
-						memset(stmt, 0, sizeof(MYSQLND_STMT));
-						stmt->state = MYSQLND_STMT_INITTED;
-					}
-				} else {
-					conn->upsert_status.warning_count = fields_eof.warning_count;
-					conn->upsert_status.server_status = fields_eof.server_status;
-				}
-
-				if (fields_eof.server_status & MYSQLND_SERVER_QUERY_NO_GOOD_INDEX_USED) {
-					stat = STAT_BAD_INDEX_USED;
-				} else if (fields_eof.server_status & MYSQLND_SERVER_QUERY_NO_INDEX_USED) {
-					stat = STAT_NO_INDEX_USED;
-				}
-				if (stat != -1) {
-					MYSQLND_INC_CONN_STATISTIC(&conn->stats, stat);
-				}
-				PACKET_FREE_ALLOCA(fields_eof);
-
-				break;
-			}
-		}
-	} while (0);
-	PACKET_FREE_ALLOCA(rset_header);
-	return ret;
-}
-
-
 /* {{{ mysqlnd_query */
 /* 
   If conn->error_info.error_no is not zero, then we had an error.
@@ -1213,531 +661,8 @@
 /* }}} */
 
 
-/* {{{ mysqlnd_fetch_lengths_unbuffered */
-static
-unsigned long * mysqlnd_fetch_lengths_unbuffered(MYSQLND_RES * const result)
-{
-	return result->lengths;
-}
-/* }}} */
 
 
-/* {{{ mysqlnd_fetch_lengths_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_buffered(MYSQLND_RES * const result)
-{
-	int i;
-	zval **previous_row;
-
-	/*
-	  If:
-	  - unbuffered result
-	  - first row hs 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))
-	{
-		return NULL;/* No rows or no more rows */
-	}
-
-	previous_row = *(result->data->data_cursor - 1);
-	for (i = 0; i < result->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 */
-PHPAPI unsigned long * _mysqlnd_fetch_lengths(MYSQLND_RES * const result)
-{
-	return result->m.fetch_lengths? result->m.fetch_lengths(result):NULL;
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_fetch_row_unbuffered */
-static enum_func_status
-mysqlnd_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flags,
-							zend_bool *fetched_anything TSRMLS_DC)
-{
-	enum_func_status		ret;
-	zval 					*row = (zval *) param;
-	unsigned int			i,
-							field_count = result->field_count;
-	php_mysql_packet_row	*row_packet = result->row_packet;
-	unsigned long			*lengths = result->lengths;
-
-	if (result->unbuf->eof_reached) {
-		/* No more rows obviously */
-		*fetched_anything = FALSE;
-		return PASS;
-	}
-	if (result->conn->state != CONN_FETCHING_DATA) {
-		SET_CLIENT_ERROR(result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
-						 UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); 
-		return FAIL;
-	}
-	/* Let the row packet fill our buffer and skip additional malloc + memcpy */
-	row_packet->skip_extraction = row? FALSE:TRUE;
-
-	/*
-	  If we skip rows (row == NULL) we have to
-	  mysqlnd_unbuffered_free_last_data() before it. The function returns always true.
-	*/
-	if (PASS == (ret = PACKET_READ(row_packet, result->conn)) &&
!row_packet->eof) {
-		result->unbuf->row_count++;
-		*fetched_anything = TRUE;
-
-		mysqlnd_unbuffered_free_last_data(result TSRMLS_CC);
-
-		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;
-
-		MYSQLND_INC_CONN_STATISTIC(&result->conn->stats,
STAT_ROWS_FETCHED_FROM_CLIENT);
-
-		if (!row_packet->skip_extraction) {
-			HashTable *row_ht = Z_ARRVAL_P(row);
-
-			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);
-
-				if (lengths) {
-					lengths[i] = len;
-				}
-
-				/* Forbid ZE to free it, we will clean it */
-				ZVAL_ADDREF(data);
-
-				if ((flags & MYSQLND_FETCH_BOTH) == MYSQLND_FETCH_BOTH) {
-					ZVAL_ADDREF(data);
-				}
-				if (flags & MYSQLND_FETCH_NUM) {
-					zend_hash_next_index_insert(row_ht, &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);
-					}
-				}
-				if (result->meta->fields[i].max_length < len) {
-					result->meta->fields[i].max_length = len;
-				}
-			}
-		}
-	} else if (ret == FAIL) {
-		if (row_packet->error_info.error_no) {
-			result->conn->error_info = row_packet->error_info; 
-		}
-		*fetched_anything = FALSE;
-		result->conn->state = 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 */
-
-		result->unbuf->eof_reached = TRUE;
-		result->conn->upsert_status.warning_count = row_packet->warning_count;
-		result->conn->upsert_status.server_status = row_packet->server_status;
-		/*
-		  result->row_packet will be cleaned when
-		  destroying the result object
-		*/
-		if (result->conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) {
-			result->conn->state = CONN_NEXT_RESULT_PENDING;
-		} else {
-			result->conn->state = CONN_READY;
-		}
-		mysqlnd_unbuffered_free_last_data(result TSRMLS_CC);
-		*fetched_anything = FALSE;
-	}
-
-	return PASS;
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_use_result */
-static
-MYSQLND_RES * _mysqlnd_use_result(MYSQLND * const conn TSRMLS_DC)
-{
-	MYSQLND_RES *result;
-
-	if (!conn->current_result) {
-		return NULL;
-	}
-
-	/* Nothing to store for UPSERT/LOAD DATA */
-	if (conn->last_query_type != QUERY_SELECT || conn->state != CONN_FETCHING_DATA) {
-		SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
-						 mysqlnd_out_of_sync); 
-		return NULL;
-	}
-
-	MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_UNBUFFERED_SETS);
-
-	result = conn->current_result;
-	conn->current_result = NULL;
-
-	result->type			= MYSQLND_RES_NORMAL;
-	result->m.fetch_row		= result->m.fetch_row_normal_unbuffered;
-	result->m.fetch_lengths	= mysqlnd_fetch_lengths_unbuffered;
-	result->unbuf			= ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));
-
-	result->conn = conn->m->get_reference(conn);
-	/*
-	  Will be freed in the mysqlnd_internal_free_result_contents() called
-	  by the resource destructor. mysqlnd_fetch_row_unbuffered() expects
-	  this to be not NULL.
-	*/
-	PACKET_INIT(result->row_packet, PROT_ROW_PACKET, php_mysql_packet_row *);
-	result->row_packet->field_count = result->field_count;
-	result->row_packet->binary_protocol = FALSE;
-	result->row_packet->fields_metadata = result->meta->fields;
-	result->lengths = ecalloc(result->field_count, sizeof(unsigned long));
-
-	/* No multithreading issues as we don't share the connection :) */
-
-	return result;
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_fetch_row_buffered */
-static enum_func_status
-mysqlnd_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags,
-						   zend_bool *fetched_anything TSRMLS_DC)
-{
-	unsigned int i;
-	zval *row = (zval *) param;
-
-	/* If we haven't read everything */
-	if (result->data->data_cursor &&
-		(result->data->data_cursor - result->data->data) <
result->data->row_count)
-	{
-		zval **current_row = *result->data->data_cursor;
-		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.
-			*/
-			ZVAL_ADDREF(data);
-			
-			if ((flags & MYSQLND_FETCH_BOTH) == MYSQLND_FETCH_BOTH) {
-				ZVAL_ADDREF(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);
-				}
-			}
-		}
-		result->data->data_cursor++;
-		*fetched_anything = TRUE;
-	} else {
-		result->data->data_cursor = NULL;
-		*fetched_anything = FALSE;
-#ifndef MYSQLND_SILENT
-		php_printf("NO MORE DATA\n ");
-#endif
-	}
-	return PASS;
-}
-/* }}} */
-
-
-#define STORE_RESULT_PREALLOCATED_SET 32
-
-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;
-	unsigned int next_extend = STORE_RESULT_PREALLOCATED_SET, free_rows;
-	MYSQLND_RES_BUFFERED *set;
-
-	free_rows = next_extend;
-
-	result->data	= set = pecalloc(1, sizeof(MYSQLND_RES_BUFFERED), to_cache);
-	set->data		= pemalloc(STORE_RESULT_PREALLOCATED_SET * sizeof(zval **), to_cache);
-	set->row_buffers= pemalloc(STORE_RESULT_PREALLOCATED_SET * sizeof(zend_uchar *),
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;
-	/* 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;
-
-		if (!free_rows) {
-			unsigned long total_rows = free_rows = next_extend = next_extend * 5 / 3; /* extend
with 33% */
-			total_rows += set->row_count;
-			set->data = perealloc(set->data, total_rows * sizeof(zval **),
set->persistent);
-
-			set->row_buffers = perealloc(set->row_buffers,
-										 total_rows * sizeof(zend_uchar *), 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_count++;
-
-		/* So row_packet's destructor function won't efree() it */
-		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.
-		  'fields' is reused because the ownership of the strings has been
-		  transfered above. 
-		*/
-	}
-	MYSQLND_INC_CONN_STATISTIC_W_VALUE(&conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT,
-									   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;
-	}
-	/* save some memory */
-	if (free_rows) {
-		set->data = perealloc(set->data,
-							  set->row_count * sizeof(zval **),
-							  set->persistent);
-		set->row_buffers = perealloc(set->row_buffers,
-									 set->row_count * sizeof(zend_uchar *),
-									 set->persistent);
-	}
-
-	if (conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) {
-		conn->state = CONN_NEXT_RESULT_PENDING;
-	} else {
-		conn->state = CONN_READY;
-	}
-
-	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 = result->data->row_count;
-	}
-	PACKET_FREE_ALLOCA(row_packet);
-
-	return ret;
-}
-
-
-/* {{{ mysqlnd_store_result */
-static
-MYSQLND_RES *_mysqlnd_store_result(MYSQLND * const conn TSRMLS_DC)
-{
-	enum_func_status ret;
-	MYSQLND_RES *result;
-	zend_bool to_cache = FALSE;
-
-	if (!conn->current_result) {
-		return NULL;
-	}
-
-	/* Nothing to store for UPSERT/LOAD DATA*/
-	if (conn->last_query_type != QUERY_SELECT || conn->state != CONN_FETCHING_DATA) {
-		SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
-						 mysqlnd_out_of_sync); 
-		return NULL;
-	}
-
-	MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_BUFFERED_SETS);
-
-	result = conn->current_result;
-	conn->current_result = NULL;
-
-	result->conn			= NULL;	/* store result does not reference  the connection */
-	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;
-
-	result->lengths = ecalloc(result->field_count, sizeof(unsigned long));
-
-	ret = mysqlnd_store_result_fetch_data(conn, result, result->meta,
-										  FALSE, TRUE, 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;
-	} else {
-		conn->error_info = result->data->error_info;
-		mysqlnd_internal_free_result(result TSRMLS_CC);
-		result = NULL;
-	}
-	return result;
-}
-/* }}} */
-
-
-/* {{{ _mysqlnd_unbuffered_skip_result */
-static enum_func_status
-_mysqlnd_unbuffered_skip_result(MYSQLND_RES * const result TSRMLS_DC)
-{
-	zend_bool fetched_anything;
-
-	/*
-	  Unbuffered sets
-	  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 &&
-		!result->unbuf->eof_reached && result->m.fetch_row)
-	{
-		/* We have to fetch all data to clean the line */
-		MYSQLND_INC_CONN_STATISTIC(&result->conn->stats,
-									result->type == MYSQLND_RES_NORMAL? STAT_FLUSHED_NORMAL_SETS:
-																		STAT_FLUSHED_PS_SETS);
-
-		while ((PASS == result->m.fetch_row(result, NULL, 0, &fetched_anything
TSRMLS_CC)) &&
-			   fetched_anything == TRUE) {
-			/* do nothing */;
-		}
-	}
-	return PASS;
-}
-/* }}} */
-
-
-/* {{{ _mysqlnd_free_result */
-static enum_func_status
-_mysqlnd_free_result(MYSQLND_RES *result, zend_bool implicit TSRMLS_DC)
-{
-	result->m.skip_result(result TSRMLS_CC);
-	MYSQLND_INC_CONN_STATISTIC(result->conn? &result->conn->stats : NULL,
-							   implicit == TRUE?	STAT_FREE_RESULT_EXPLICIT:
-													STAT_FREE_RESULT_IMPLICIT);
-
-	mysqlnd_internal_free_result(result TSRMLS_CC);
-	return PASS;
-}
-/* }}} */
-
-
-/* {{{ _mysqlnd_data_seek */
-static enum_func_status
-_mysqlnd_data_seek(MYSQLND_RES *result, mynd_ulonglong row)
-{
-	if (!result->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;
-	} else {
-		result->data->data_cursor = result->data->data + row;
-	}
-
-	return PASS;
-}
-/* }}} */
-
-
 /* {{{ mysqlnd_errno */
 static
 unsigned int _mysqlnd_errno(const MYSQLND * const conn)
@@ -2009,23 +934,6 @@
 /* }}} */
 
 
-/* {{{ _mysqlnd_num_fields */
-unsigned int _mysqlnd_num_fields(const MYSQLND_RES * const res)
-{
-	return res->field_count;
-}
-/* }}} */
-
-
-/* {{{ _mysqlnd_num_fields */
-mynd_ulonglong _mysqlnd_num_rows(const MYSQLND_RES * const res)
-{
-	/* Be compatible with libmysql. We count row_count, but will return 0 */
-	return res->data? res->data->row_count:0;
-}
-/* }}} */
-
-
 /* {{{ mysqlnd_field_count */
 static
 unsigned int _mysqlnd_field_count(const MYSQLND * const conn)
@@ -2035,51 +943,6 @@
 /* }}} */
 
 
-/* {{{ mysqlnd_fetch_field */
-static MYSQLND_FIELD *
-_mysqlnd_fetch_field(MYSQLND_RES * const result)
-{
-	if (!result->meta || result->meta->current_field >= result->field_count)
-		return NULL;
-	return &result->meta->fields[result->meta->current_field++];
-}
-/* }}} */
-
-
-/* {{{ _mysqlnd_fetch_field_direct */
-static MYSQLND_FIELD *
-_mysqlnd_fetch_field_direct(const MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET
fieldnr)
-{
-	return result->meta? &result->meta->fields[fieldnr]:NULL;
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_field_seek */
-static MYSQLND_FIELD_OFFSET
-_mysqlnd_field_seek(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET field_offset)
-{
-	MYSQLND_FIELD_OFFSET return_value = 0;
-	if (result->meta) {
-		return_value = result->meta->current_field;
-		result->meta->current_field = field_offset;
-	}
-	return return_value;
-}
-/* }}} */
-
-
-/* {{{ _mysqlnd_field_tell */
-static MYSQLND_FIELD_OFFSET
-_mysqlnd_field_tell(const MYSQLND_RES * const result)
-{
-	return result->meta? result->meta->current_field:0;
-}
-/* }}} */
-
-
-
-
 /* {{{ _mysqlnd_insert_id */
 static
 mynd_ulonglong _mysqlnd_insert_id(const MYSQLND * const conn)
@@ -2431,62 +1294,6 @@
 /* }}} */
 
 
-/* {{{ _mysqlnd_fetch_into */
-static
-void _mysqlnd_fetch_into(MYSQLND_RES *result, unsigned int flags, zval *return_value
TSRMLS_DC ZEND_FILE_LINE_DC)
-{
-	zend_bool fetched_anything;
-
-	if (!result->m.fetch_row) {
-		RETURN_NULL();
-	}
-	/*
-	  Hint Zend how many elements we will have in the hash. Thus it won't
-	  extend and rehash the hash constantly.
-	*/
-	mysqlnd_array_init(return_value, mysqlnd_num_fields(result) * 2);
-	if (FAIL == result->m.fetch_row(result, (void *)return_value, flags,
&fetched_anything TSRMLS_CC)) {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading a row");
-		RETURN_FALSE;
-	} else if (fetched_anything == FALSE) {
-		zval_dtor(return_value);
-		RETURN_NULL();
-	}
-	/*
-	  return_value is IS_NULL for no more data and an array for data. Thus it's ok
-	  to return here.
-	*/
-}
-/* }}} */
-
-
-/* {{{ _mysqlnd_fetch_all */
-static
-void _mysqlnd_fetch_all(MYSQLND_RES *result, unsigned int flags, zval *return_value
TSRMLS_DC ZEND_FILE_LINE_DC)
-{
-	zval  *row;
-	ulong i = 0;
-
-	/* mysqlnd_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) {
-		RETURN_NULL();
-	}	
-
-	mysqlnd_array_init(return_value, result->data->row_count);
-
-	while (result->data->data_cursor &&
-		   (result->data->data_cursor - result->data->data) <
result->data->row_count)
-	{
-		MAKE_STD_ZVAL(row);
-		mysqlnd_fetch_into(result, flags, row);
-		add_index_zval(return_value, i++, row);
-	}
-}
-/* }}} */
-
-
 /* {{{ _mysqlnd_get_reference */
 static
 MYSQLND * _mysqlnd_get_reference(MYSQLND * const conn)
@@ -2514,61 +1321,6 @@
 /* }}} */
 
 
-/* {{{ mysqlnd_result_metadata_clone_metadata */
-static
-MYSQLND_RES_METADATA * _mysqlnd_clone_metadata(const MYSQLND_RES_METADATA * const meta,
zend_bool persistent)
-{
-	unsigned int i;
-	/* +1 is to have empty marker at the end */
-	MYSQLND_RES_METADATA *new_meta = pemalloc(sizeof(MYSQLND_RES_METADATA), persistent);
-	MYSQLND_FIELD *new_fields = pecalloc(meta->field_count + 1, sizeof(MYSQLND_FIELD),
persistent);
-	MYSQLND_FIELD *original_fields = meta->fields;
-	size_t len = meta->field_count * sizeof(struct mysqlnd_field_hash_key);
-
-	new_meta->zend_hash_keys = pemalloc(len, persistent);
-	memcpy(new_meta->zend_hash_keys, meta->zend_hash_keys, len);
-
-	/*
-	  This will copy also the strings and the root, which we will have
-	  to adjust in the loop
-	*/
-	memcpy(new_fields, original_fields, (meta->field_count) * sizeof(MYSQLND_FIELD));
-	for (i = 0; i < meta->field_count; i++) {
-		/* First copy the root, then field by field adjust the pointers */
-		new_fields[i].root = pemalloc(original_fields[i].root_len, persistent);
-		memcpy(new_fields[i].root, original_fields[i].root, new_fields[i].root_len);
-
-		new_fields[i].name		= new_fields[i].root + (original_fields[i].name -
original_fields[i].root);
-		new_fields[i].org_name	= new_fields[i].root + (original_fields[i].org_name -
original_fields[i].root);
-
-		new_fields[i].table		= new_fields[i].root + (original_fields[i].table -
original_fields[i].root);
-		new_fields[i].org_table	= new_fields[i].root + (original_fields[i].org_table -
original_fields[i].root);
-
-		new_fields[i].db		= new_fields[i].root + (original_fields[i].db -
original_fields[i].root);
-		new_fields[i].catalog	= new_fields[i].root + (original_fields[i].catalog -
original_fields[i].root);
-		/* def is not on the root, if allocated at all */
-		if (original_fields[i].def) {
-			new_fields[i].def	= pemalloc(original_fields[i].def_length + 1, persistent);
-			/* copy the trailing \0 too */
-			memcpy(new_fields[i].def, original_fields[i].def, original_fields[i].def_length + 1);
-		}
-#if PHP_MAJOR_VERSION >= 6
-		if (new_meta->zend_hash_keys[i].ustr.u) {
-			new_meta->zend_hash_keys[i].ustr.u =
-					eustrndup(new_meta->zend_hash_keys[i].ustr.u,
new_meta->zend_hash_keys[i].ulen);
-		}
-#endif 
-	}
-	new_meta->current_field = 0;
-	new_meta->field_count = meta->field_count;
-
-	new_meta->fields = new_fields;
-
-	return new_meta;
-}
-/* }}} */
-
-
 MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn);
 
 
@@ -2638,34 +1390,7 @@
 /* }}} */
 
 
-/* {{{ mysqlnd_result_init */
-MYSQLND_RES *mysqlnd_result_init(unsigned int field_count, MYSQLND_THD_ZVAL_PCACHE
*cache)
-{
-	MYSQLND_RES *ret = ecalloc(1, sizeof(MYSQLND_RES));
 
-	ret->field_count	= field_count;
-	ret->zval_cache		= cache;
-	ret->references		= 1;
-	ret->m.free_result	= _mysqlnd_free_result;
-	ret->m.clone_metadata = _mysqlnd_clone_metadata;
-	ret->m.free_result_buffers = mysqlnd_internal_free_result_buffers;
-	ret->m.seek_data	= _mysqlnd_data_seek;
-	ret->m.skip_result	= _mysqlnd_unbuffered_skip_result;
-	ret->m.num_rows		= _mysqlnd_num_rows;
-	ret->m.num_fields	= _mysqlnd_num_fields;
-	ret->m.fetch_into	= _mysqlnd_fetch_into;
-	ret->m.fetch_all	= _mysqlnd_fetch_all;
-	ret->m.seek_field	= _mysqlnd_field_seek;
-	ret->m.field_tell	= _mysqlnd_field_tell;
-	ret->m.fetch_field	= _mysqlnd_fetch_field;
-	ret->m.fetch_field_direct = _mysqlnd_fetch_field_direct;
-	ret->m.fetch_row_normal_buffered = mysqlnd_fetch_row_buffered;
-	ret->m.fetch_row_normal_unbuffered = mysqlnd_fetch_row_unbuffered;
-
-	return ret;
-}
-/* }}} */
-
 /*
  * Local variables:
  * tab-width: 4

Modified: trunk/mysqlnd/mysqlnd_priv.h
===================================================================
--- trunk/mysqlnd/mysqlnd_priv.h	2007-06-14 09:44:19 UTC (rev 369)
+++ trunk/mysqlnd/mysqlnd_priv.h	2007-06-14 11:22:25 UTC (rev 370)
@@ -154,26 +154,6 @@
 extern const char * mysqlnd_out_of_sync;
 
 
-enum_func_status mysqlnd_read_result_metadata(MYSQLND *conn, MYSQLND_RES *result
TSRMLS_DC);
-
-MYSQLND_RES *mysqlnd_result_init(unsigned int field_count, MYSQLND_THD_ZVAL_PCACHE
*cache);							
-void mysqlnd_unbuffered_free_last_data(MYSQLND_RES *result TSRMLS_DC);
-void mysqlnd_internal_free_result_contents(MYSQLND_RES *result TSRMLS_DC);
-void mysqlnd_internal_free_result_buffers(MYSQLND_RES *result TSRMLS_DC);
-void mysqlnd_internal_free_result(MYSQLND_RES *result TSRMLS_DC);
-
-
-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);
-
-
-void mysqlnd_internal_free_result_metadata(MYSQLND_RES_METADATA *meta, zend_bool
persistent TSRMLS_DC);
-
-
 enum_func_status mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename,
zend_bool *is_warning TSRMLS_DC);
 
 
@@ -182,7 +162,6 @@
 void mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uchar
**p,
 										size_t *buf_len, unsigned int null_byte_offset);
 
-enum_func_status mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt
TSRMLS_DC);
 
 int mysqlnd_set_sock_no_delay(php_stream *stream);
 #endif	/* MYSQLND_PRIV_H */

Modified: trunk/mysqlnd/mysqlnd_ps.c
===================================================================
--- trunk/mysqlnd/mysqlnd_ps.c	2007-06-14 09:44:19 UTC (rev 369)
+++ trunk/mysqlnd/mysqlnd_ps.c	2007-06-14 11:22:25 UTC (rev 370)
@@ -23,6 +23,7 @@
 #include "mysqlnd.h"
 #include "mysqlnd_wireprotocol.h"
 #include "mysqlnd_priv.h"
+#include "mysqlnd_result.h"
 #include "mysqlnd_statistics.h"
 
 

Added: trunk/mysqlnd/mysqlnd_result.c
===================================================================
--- trunk/mysqlnd/mysqlnd_result.c	2007-06-14 09:44:19 UTC (rev 369)
+++ trunk/mysqlnd/mysqlnd_result.c	2007-06-14 11:22:25 UTC (rev 370)
@@ -0,0 +1,1307 @@
+/*
+  +----------------------------------------------------------------------+
+  | 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: header,v 1.17 2006/01/01 13:09:48 sniper Exp $ */
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_result.h"
+#include "mysqlnd_statistics.h"
+#include "mysqlnd_charset.h"
+#include "ext/standard/basic_functions.h"
+
+#define MYSQLND_SILENT
+
+/* {{{ php_mysqlnd_free_field_metadata */
+static
+void php_mysqlnd_free_field_metadata(MYSQLND_FIELD *meta, zend_bool persistent)
+{
+	if (meta) {
+		if (meta->root) {
+			pefree(meta->root, persistent);
+			meta->root = NULL;
+		}
+		if (meta->def) {
+			pefree(meta->def, persistent);
+			meta->def = NULL;
+		}
+	}
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_unbuffered_free_last_data */
+void mysqlnd_unbuffered_free_last_data(MYSQLND_RES *result TSRMLS_DC)
+{
+	MYSQLND_RES_UNBUFFERED *unbuf = result->unbuf;
+	if (!unbuf) {
+		return;
+	}
+
+	if (unbuf->last_row_data) {
+		unsigned int i, ctor_called_count = 0;
+		zend_bool copy_ctor_called;
+		MYSQLND_STATS *global_stats = result->conn? &result->conn->stats:NULL;
+		for (i = 0; i < result->field_count; i++) {
+			mysqlnd_palloc_zval_ptr_dtor(&(unbuf->last_row_data[i]),
+										 result->zval_cache,
+										 result->type == MYSQLND_RES_PS,
+										 &copy_ctor_called TSRMLS_CC);
+			if (copy_ctor_called) {
+				ctor_called_count++;
+			}
+		}
+		/* By using value3 macros we hold a mutex only once, there is no value2 */
+		MYSQLND_INC_CONN_STATISTIC_W_VALUE3(global_stats,
+											STAT_COPY_ON_WRITE_PERFORMED,
+											ctor_called_count,
+											STAT_COPY_ON_WRITE_SAVED,
+											result->field_count - ctor_called_count,
+											STAT_COPY_ON_WRITE_PERFORMED, 0);
+		
+		/* Free last row's zvals */
+		efree(unbuf->last_row_data);
+		unbuf->last_row_data = NULL;
+	}
+	if (unbuf->last_row_buffer) {
+		/* Nothing points to this buffer now, free it */
+		efree(unbuf->last_row_buffer);
+		unbuf->last_row_buffer = NULL;
+	}
+}
+/* }}} */
+
+/* {{{ mysqlnd_free_buffered_data */
+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;
+	enum_mysqlnd_res_type result_type = result->type;
+	unsigned int field_count = result->field_count;
+	unsigned int row;
+
+	for (row = 0; row < result->data->row_count; 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;
+			mysqlnd_palloc_zval_ptr_dtor(&(current_row[col]), zval_cache,
+										 result_type == MYSQLND_RES_PS,
+										 &copy_ctor_called TSRMLS_CC);
+			MYSQLND_INC_CONN_STATISTIC(NULL, copy_ctor_called? STAT_COPY_ON_WRITE_PERFORMED:
+															   STAT_COPY_ON_WRITE_SAVED);
+		}
+		pefree(current_row, set->persistent);
+		pefree(current_buffer, set->persistent);
+	}
+	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);
+	}
+	pefree(set, set->persistent);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_internal_free_result_buffers */
+void mysqlnd_internal_free_result_buffers(MYSQLND_RES *result TSRMLS_DC)
+{
+
+	if (result->unbuf) {
+		mysqlnd_unbuffered_free_last_data(result TSRMLS_CC);
+		efree(result->unbuf);
+		result->unbuf = NULL;
+	} else if (result->data) {
+		mysqlnd_free_buffered_data(result TSRMLS_CC);
+		result->data = NULL;
+	}
+
+	if (result->lengths) {
+		efree(result->lengths);
+		result->lengths = NULL;
+	}
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_internal_free_result_metadata */
+void mysqlnd_internal_free_result_metadata(MYSQLND_RES_METADATA *meta, zend_bool
persistent TSRMLS_DC)
+{
+	int i;
+	MYSQLND_FIELD *fields;
+
+	if ((fields = meta->fields)) {
+		i = meta->field_count;
+		while (i--) {
+			php_mysqlnd_free_field_metadata(fields++, persistent);
+		}
+		pefree(meta->fields, persistent);
+		meta->fields = NULL;
+	}
+
+	if (meta->zend_hash_keys) {
+#if PHP_MAJOR_VERSION >= 6
+		if (UG(unicode)) {
+			for (i = 0; i < meta->field_count; i++) {
+				if (meta->zend_hash_keys[i].ustr.v) {
+					pefree(meta->zend_hash_keys[i].ustr.v, persistent);
+				}
+			}
+		}
+#endif
+		pefree(meta->zend_hash_keys, persistent);
+		meta->zend_hash_keys = NULL;
+	}
+	pefree(meta, persistent);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_internal_free_result_contents */
+void mysqlnd_internal_free_result_contents(MYSQLND_RES *result TSRMLS_DC)
+{
+
+	result->m.free_result_buffers(result TSRMLS_CC);
+
+	if (result->row_packet) {
+		if (result->type == MYSQLND_RES_NORMAL) {
+			PACKET_FREE(result->row_packet);
+		} else {
+			PACKET_FREE(result->row_packet);
+		}
+		result->row_packet = NULL;
+	}
+
+	result->conn = NULL;
+
+	if (result->meta) {
+		mysqlnd_internal_free_result_metadata(result->meta, FALSE TSRMLS_CC);
+		result->meta = NULL;
+	}
+
+	if (result->zval_cache) {
+		mysqlnd_palloc_free_thd_cache_reference(&result->zval_cache);
+		result->zval_cache = NULL;
+	}
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_internal_free_result */
+void mysqlnd_internal_free_result(MYSQLND_RES *result TSRMLS_DC)
+{
+	/*
+	  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 quit_sent is no
+	*/
+	if (result->conn) {
+		result->conn->m->free_reference(result->conn TSRMLS_CC);
+		result->conn = NULL;
+	}
+
+	mysqlnd_internal_free_result_contents(result TSRMLS_CC);
+	efree(result);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_handle_numeric */
+/*
+  The following code is stolen from ZE - HANDLE_NUMERIC() macro from zend_hash.c
+  and modified for the needs of mysqlnd.
+*/
+static
+zend_bool mysqlnd_is_key_numeric(char *key, size_t length, long *idx)
+{
+	register char *tmp=key;
+
+	if (*tmp=='-') {
+		tmp++;
+	}
+	if ((*tmp>='0' && *tmp<='9')) {
+		do { /* possibly a numeric index */
+			char *end=key+length-1;
+
+			if (*tmp++=='0' && length>2) { /* don't accept numbers with leading zeros
*/
+				break;
+			}
+			while (tmp<end) {
+				if (!(*tmp>='0' && *tmp<='9')) {
+					break;
+				}
+				tmp++;
+			}
+			if (tmp==end && *tmp=='\0') { /* a numeric index */
+				if (*key=='-') {
+					*idx = strtol(key, NULL, 10);
+					if (*idx!=LONG_MIN) {
+						return TRUE;
+					}
+				} else {
+					*idx = strtol(key, NULL, 10);
+					if (*idx!=LONG_MAX) {
+						return TRUE;
+					}
+				}
+			}
+		} while (0);
+	}
+	return FALSE;
+}
+/* }}} */
+
+
+#if PHP_MAJOR_VERSION >= 6
+/* {{{ mysqlnd_unicode_is_key_numeric */
+static
+zend_bool mysqlnd_unicode_is_key_numeric(UChar *key, size_t length, long *idx)
+{
+	register UChar *tmp=key;
+
+	if (*tmp==0x2D /*'-'*/) {
+		tmp++;
+	}
+	if ((*tmp>=0x30 /*'0'*/ && *tmp<=0x39 /*'9'*/)) { /* possibly a numeric
index */
+		do {
+			UChar *end=key+length-1;
+
+			if (*tmp++==0x30 && length>2) { /* don't accept numbers with leading zeros
*/
+				break;
+			}
+			while (tmp<end) {
+				if (!(*tmp>=0x30 /*'0'*/ && *tmp<=0x39 /*'9'*/)) {
+					break;
+				}
+				tmp++;
+			}
+			if (tmp==end && *tmp==0) { /* a numeric index */
+				if (*key==0x2D /*'-'*/) {
+					*idx = zend_u_strtol(key, NULL, 10);
+					if (*idx!=LONG_MIN) {
+						return TRUE;
+					}
+				} else {
+					*idx = zend_u_strtol(key, NULL, 10);
+					if (*idx!=LONG_MAX) {
+						return TRUE;
+					}
+				}
+			}
+		} while (0);
+	}
+	return FALSE;
+}
+/* }}} */
+#endif
+
+
+/* {{{ mysqlnd_read_result_metadata */
+enum_func_status
+mysqlnd_read_result_metadata(MYSQLND *conn, MYSQLND_RES *result TSRMLS_DC)
+{
+	int i = 0;
+	php_mysql_packet_res_field field_packet;
+
+	/*
+	  Make it safe to call it repeatedly for PS -
+	  better free and allocate a new because the number of field might change 
+	  (select *) with altered table. Also for statements which skip the PS
+	  infrastructure.
+	*/
+	if (result->meta) {
+		mysqlnd_internal_free_result_metadata(result->meta, FALSE TSRMLS_CC);
+		result->meta = NULL;
+	}
+
+	/* +1 is to have empty marker at the end */
+	result->meta = ecalloc(1, sizeof(MYSQLND_RES_METADATA));
+	result->meta->field_count = result->field_count;
+	result->meta->fields = ecalloc(result->field_count + 1, sizeof(MYSQLND_FIELD));
+	result->meta->zend_hash_keys = ecalloc(result->field_count,
+									 		sizeof(struct mysqlnd_field_hash_key));
+
+	/* 1. Read all fields metadata */
+
+	/* It's safe to reread without freeing */
+	PACKET_INIT_ALLOCA(field_packet, PROT_RSET_FLD_PACKET);
+	for (;i < result->field_count; i++) {
+		long idx;
+
+		if (result->meta->fields[i].root) {
+			/* We re-read metadata for PS */
+			efree(result->meta->fields[i].root);
+			result->meta->fields[i].root = NULL;
+		}
+
+		field_packet.metadata = &(result->meta->fields[i]);
+		if (FAIL == PACKET_READ_ALLOCA(field_packet, conn)) {
+			PACKET_FREE_ALLOCA(field_packet);
+			goto error;
+		}
+
+		if (mysqlnd_ps_fetch_functions[result->meta->fields[i].type].func == NULL) {
+			SET_CLIENT_ERROR(result->conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE,
+							 "Uknown field type sent by the server"); 
+			php_error_docref(NULL TSRMLS_CC, E_WARNING,
+							 "Unknown type %d sent by the server. "
+							 "Please send a report to the developers",
+							 result->meta->fields[i].type);
+			goto error;
+		}
+
+
+#if PHP_MAJOR_VERSION >= 6
+		if (UG(unicode)) {
+			UChar *ustr;
+			int ulen;
+			zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen,
+								   result->meta->fields[i].name,
+								   result->meta->fields[i].name_length TSRMLS_CC);
+			if ((result->meta->zend_hash_keys[i].is_numeric =
+				 			mysqlnd_unicode_is_key_numeric(ustr, ulen + 1, &idx)))
+			{
+				result->meta->zend_hash_keys[i].key = idx;
+				efree(ustr);
+			} else {
+				result->meta->zend_hash_keys[i].ustr.u = ustr;
+				result->meta->zend_hash_keys[i].ulen = ulen;
+				result->meta->zend_hash_keys[i].key = zend_u_get_hash_value(IS_UNICODE,
ZSTR(ustr), ulen + 1);
+			}
+
+		} else 
+#endif
+		{
+			/* For BC we have to check whether the key is numeric and use it like this */
+			if ((result->meta->zend_hash_keys[i].is_numeric =
+						mysqlnd_is_key_numeric(field_packet.metadata->name,
+									   		   field_packet.metadata->name_length + 1,
+											   &idx)))
+			{
+				result->meta->zend_hash_keys[i].key = idx;
+			} else {
+				result->meta->zend_hash_keys[i].key =
+						zend_get_hash_value(field_packet.metadata->name,
+											field_packet.metadata->name_length + 1);
+			}
+		}
+	}
+	PACKET_FREE_ALLOCA(field_packet);
+
+	/*
+	  2. Follows an EOF packet, which the client of mysqlnd_read_result_metadata()
+		 should consume.
+	  3. If there is a result set, it follows. The last packet will have 'eof' set
+	  	 If PS, then no result set follows.
+	*/
+
+	return PASS;
+error:
+	mysqlnd_internal_free_result_contents(result TSRMLS_CC);
+	return FAIL;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_query_read_result_set_header */
+enum_func_status
+mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC)
+{
+	enum_func_status ret;
+	php_mysql_packet_rset_header rset_header;
+
+	ret = FAIL;
+	PACKET_INIT_ALLOCA(rset_header, PROT_RSET_HEADER_PACKET);
+	do {
+		if (FAIL == (ret = PACKET_READ_ALLOCA(rset_header, conn))) {
+			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error reading result set's header");
+			break;
+		}
+
+		if (rset_header.error_info.error_no) {
+			/*
+			  Cover a protocol design error: error packet does not
+			  contain the server status. Therefore, the client has no way
+			  to find out whether there are more result sets of
+			  a multiple-result-set statement pending. Luckily, in 5.0 an
+			  error always aborts execution of a statement, wherever it is
+			  a multi-statement or a stored procedure, so it should be
+			  safe to unconditionally turn off the flag here.
+			*/
+			conn->upsert_status.server_status &= ~SERVER_MORE_RESULTS_EXISTS;
+			conn->upsert_status.affected_rows = -1;
+			/*
+			  This will copy the error code and the messages, as they
+			  are buffers in the struct
+			*/
+			conn->error_info = rset_header.error_info;
+			ret = FAIL;
+			break;
+		}
+		conn->error_info.error_no = 0;
+
+		switch (rset_header.field_count) {
+			case MYSQLND_NULL_LENGTH: {	/* LOAD DATA LOCAL INFILE */
+				zend_bool is_warning;
+				conn->last_query_type = QUERY_LOAD_LOCAL;
+				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;
+				MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_NON_RSET_QUERY);
+				break;
+			}
+			case 0:				/* UPSERT		*/
+				conn->last_query_type = QUERY_UPSERT;
+				conn->field_count = rset_header.field_count;
+				conn->upsert_status.warning_count = rset_header.warning_count;
+				conn->upsert_status.server_status = rset_header.server_status;
+				conn->upsert_status.affected_rows = rset_header.affected_rows;
+				conn->upsert_status.last_insert_id = rset_header.last_insert_id;
+				SET_NEW_MESSAGE(conn->last_message, conn->last_message_len,
+								rset_header.info_or_local_file, rset_header.info_or_local_file_len);
+				/* 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;
+				} else {
+					conn->state = CONN_READY;
+				}
+				ret = PASS;
+				MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_NON_RSET_QUERY);
+				break;
+			default:{			/* Result set	*/
+				php_mysql_packet_eof fields_eof;
+				MYSQLND_RES *result;
+				uint stat = -1;
+
+				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;
+				/* PS has already allocated it */
+				if (!stmt) {
+					conn->field_count = rset_header.field_count;
+					result =
+						conn->current_result=
+							mysqlnd_result_init(rset_header.field_count,
+												mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache));
+				} else {
+					if (!stmt->result) {
+						/*
+						  This is 'SHOW'/'EXPLAIN'-like query. Current implementation of
+						  prepared statements can't send result set metadata for these queries
+						  on prepare stage. Read it now.
+						*/
+						conn->field_count = rset_header.field_count;
+						result =
+							stmt->result =
+								mysqlnd_result_init(rset_header.field_count,
+													mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache));
+					} else {
+						/*
+						  Update result set metadata if it for some reason changed between
+						  prepare and execute, i.e.:
+						  - in case of 'SELECT ?' we don't know column type unless data was
+							supplied to mysql_stmt_execute, so updated column type is sent
+							now.
+						  - if data dictionary changed between prepare and execute, for
+							example a table used in the query was altered.
+						  Note, that now (4.1.3) we always send metadata in reply to
+						  COM_STMT_EXECUTE (even if it is not necessary), so either this or
+						  previous branch always works.
+						*/	
+					}
+					result = stmt->result;
+				}
+
+				if (FAIL == (ret = mysqlnd_read_result_metadata(conn, result TSRMLS_CC))) {
+					/* For PS, we leave them in Prepared state */
+					if (!stmt) {
+						efree(conn->current_result);
+						conn->current_result = NULL;
+					}
+					break;
+				}
+
+				/* Check for SERVER_STATUS_MORE_RESULTS if needed */
+				PACKET_INIT_ALLOCA(fields_eof, PROT_EOF_PACKET);
+				if (FAIL == (ret = PACKET_READ_ALLOCA(fields_eof, conn))) {
+					mysqlnd_internal_free_result_contents(result TSRMLS_CC);
+					efree(result);
+					if (!stmt) {
+						conn->current_result = NULL;
+					} else {
+						stmt->result = NULL;
+						memset(stmt, 0, sizeof(MYSQLND_STMT));
+						stmt->state = MYSQLND_STMT_INITTED;
+					}
+				} else {
+					conn->upsert_status.warning_count = fields_eof.warning_count;
+					conn->upsert_status.server_status = fields_eof.server_status;
+				}
+
+				if (fields_eof.server_status & MYSQLND_SERVER_QUERY_NO_GOOD_INDEX_USED) {
+					stat = STAT_BAD_INDEX_USED;
+				} else if (fields_eof.server_status & MYSQLND_SERVER_QUERY_NO_INDEX_USED) {
+					stat = STAT_NO_INDEX_USED;
+				}
+				if (stat != -1) {
+					MYSQLND_INC_CONN_STATISTIC(&conn->stats, stat);
+				}
+				PACKET_FREE_ALLOCA(fields_eof);
+
+				break;
+			}
+		}
+	} while (0);
+	PACKET_FREE_ALLOCA(rset_header);
+	return ret;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_fetch_lengths_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_buffered(MYSQLND_RES * const result)
+{
+	int i;
+	zval **previous_row;
+
+	/*
+	  If:
+	  - unbuffered result
+	  - first row hs 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))
+	{
+		return NULL;/* No rows or no more rows */
+	}
+
+	previous_row = *(result->data->data_cursor - 1);
+	for (i = 0; i < result->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)
+{
+	return result->lengths;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_fetch_lengths */
+PHPAPI unsigned long * _mysqlnd_fetch_lengths(MYSQLND_RES * const result)
+{
+	return result->m.fetch_lengths? result->m.fetch_lengths(result):NULL;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_fetch_row_unbuffered */
+static enum_func_status
+mysqlnd_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flags,
+							zend_bool *fetched_anything TSRMLS_DC)
+{
+	enum_func_status		ret;
+	zval 					*row = (zval *) param;
+	unsigned int			i,
+							field_count = result->field_count;
+	php_mysql_packet_row	*row_packet = result->row_packet;
+	unsigned long			*lengths = result->lengths;
+
+	if (result->unbuf->eof_reached) {
+		/* No more rows obviously */
+		*fetched_anything = FALSE;
+		return PASS;
+	}
+	if (result->conn->state != CONN_FETCHING_DATA) {
+		SET_CLIENT_ERROR(result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
+						 UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); 
+		return FAIL;
+	}
+	/* Let the row packet fill our buffer and skip additional malloc + memcpy */
+	row_packet->skip_extraction = row? FALSE:TRUE;
+
+	/*
+	  If we skip rows (row == NULL) we have to
+	  mysqlnd_unbuffered_free_last_data() before it. The function returns always true.
+	*/
+	if (PASS == (ret = PACKET_READ(row_packet, result->conn)) &&
!row_packet->eof) {
+		result->unbuf->row_count++;
+		*fetched_anything = TRUE;
+
+		mysqlnd_unbuffered_free_last_data(result TSRMLS_CC);
+
+		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;
+
+		MYSQLND_INC_CONN_STATISTIC(&result->conn->stats,
STAT_ROWS_FETCHED_FROM_CLIENT);
+
+		if (!row_packet->skip_extraction) {
+			HashTable *row_ht = Z_ARRVAL_P(row);
+
+			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);
+
+				if (lengths) {
+					lengths[i] = len;
+				}
+
+				/* Forbid ZE to free it, we will clean it */
+				ZVAL_ADDREF(data);
+
+				if ((flags & MYSQLND_FETCH_BOTH) == MYSQLND_FETCH_BOTH) {
+					ZVAL_ADDREF(data);
+				}
+				if (flags & MYSQLND_FETCH_NUM) {
+					zend_hash_next_index_insert(row_ht, &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);
+					}
+				}
+				if (result->meta->fields[i].max_length < len) {
+					result->meta->fields[i].max_length = len;
+				}
+			}
+		}
+	} else if (ret == FAIL) {
+		if (row_packet->error_info.error_no) {
+			result->conn->error_info = row_packet->error_info; 
+		}
+		*fetched_anything = FALSE;
+		result->conn->state = 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 */
+
+		result->unbuf->eof_reached = TRUE;
+		result->conn->upsert_status.warning_count = row_packet->warning_count;
+		result->conn->upsert_status.server_status = row_packet->server_status;
+		/*
+		  result->row_packet will be cleaned when
+		  destroying the result object
+		*/
+		if (result->conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) {
+			result->conn->state = CONN_NEXT_RESULT_PENDING;
+		} else {
+			result->conn->state = CONN_READY;
+		}
+		mysqlnd_unbuffered_free_last_data(result TSRMLS_CC);
+		*fetched_anything = FALSE;
+	}
+
+	return PASS;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_use_result */
+MYSQLND_RES * _mysqlnd_use_result(MYSQLND * const conn TSRMLS_DC)
+{
+	MYSQLND_RES *result;
+
+	if (!conn->current_result) {
+		return NULL;
+	}
+
+	/* Nothing to store for UPSERT/LOAD DATA */
+	if (conn->last_query_type != QUERY_SELECT || conn->state != CONN_FETCHING_DATA) {
+		SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
+						 mysqlnd_out_of_sync); 
+		return NULL;
+	}
+
+	MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_UNBUFFERED_SETS);
+
+	result = conn->current_result;
+	conn->current_result = NULL;
+
+	result->type			= MYSQLND_RES_NORMAL;
+	result->m.fetch_row		= result->m.fetch_row_normal_unbuffered;
+	result->m.fetch_lengths	= mysqlnd_fetch_lengths_unbuffered;
+	result->unbuf			= ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));
+
+	result->conn = conn->m->get_reference(conn);
+	/*
+	  Will be freed in the mysqlnd_internal_free_result_contents() called
+	  by the resource destructor. mysqlnd_fetch_row_unbuffered() expects
+	  this to be not NULL.
+	*/
+	PACKET_INIT(result->row_packet, PROT_ROW_PACKET, php_mysql_packet_row *);
+	result->row_packet->field_count = result->field_count;
+	result->row_packet->binary_protocol = FALSE;
+	result->row_packet->fields_metadata = result->meta->fields;
+	result->lengths = ecalloc(result->field_count, sizeof(unsigned long));
+
+	/* No multithreading issues as we don't share the connection :) */
+
+	return result;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_fetch_row_buffered */
+static enum_func_status
+mysqlnd_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags,
+						   zend_bool *fetched_anything TSRMLS_DC)
+{
+	unsigned int i;
+	zval *row = (zval *) param;
+
+	/* If we haven't read everything */
+	if (result->data->data_cursor &&
+		(result->data->data_cursor - result->data->data) <
result->data->row_count)
+	{
+		zval **current_row = *result->data->data_cursor;
+		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.
+			*/
+			ZVAL_ADDREF(data);
+			
+			if ((flags & MYSQLND_FETCH_BOTH) == MYSQLND_FETCH_BOTH) {
+				ZVAL_ADDREF(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);
+				}
+			}
+		}
+		result->data->data_cursor++;
+		*fetched_anything = TRUE;
+	} else {
+		result->data->data_cursor = NULL;
+		*fetched_anything = FALSE;
+#ifndef MYSQLND_SILENT
+		php_printf("NO MORE DATA\n ");
+#endif
+	}
+	return PASS;
+}
+/* }}} */
+
+
+#define STORE_RESULT_PREALLOCATED_SET 32
+
+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;
+	unsigned int next_extend = STORE_RESULT_PREALLOCATED_SET, free_rows;
+	MYSQLND_RES_BUFFERED *set;
+
+	free_rows = next_extend;
+
+	result->data	= set = pecalloc(1, sizeof(MYSQLND_RES_BUFFERED), to_cache);
+	set->data		= pemalloc(STORE_RESULT_PREALLOCATED_SET * sizeof(zval **), to_cache);
+	set->row_buffers= pemalloc(STORE_RESULT_PREALLOCATED_SET * sizeof(zend_uchar *),
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;
+	/* 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;
+
+		if (!free_rows) {
+			unsigned long total_rows = free_rows = next_extend = next_extend * 5 / 3; /* extend
with 33% */
+			total_rows += set->row_count;
+			set->data = perealloc(set->data, total_rows * sizeof(zval **),
set->persistent);
+
+			set->row_buffers = perealloc(set->row_buffers,
+										 total_rows * sizeof(zend_uchar *), 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_count++;
+
+		/* So row_packet's destructor function won't efree() it */
+		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.
+		  'fields' is reused because the ownership of the strings has been
+		  transfered above. 
+		*/
+	}
+	MYSQLND_INC_CONN_STATISTIC_W_VALUE(&conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT,
+									   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;
+	}
+	/* save some memory */
+	if (free_rows) {
+		set->data = perealloc(set->data,
+							  set->row_count * sizeof(zval **),
+							  set->persistent);
+		set->row_buffers = perealloc(set->row_buffers,
+									 set->row_count * sizeof(zend_uchar *),
+									 set->persistent);
+	}
+
+	if (conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) {
+		conn->state = CONN_NEXT_RESULT_PENDING;
+	} else {
+		conn->state = CONN_READY;
+	}
+
+	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 = result->data->row_count;
+	}
+	PACKET_FREE_ALLOCA(row_packet);
+
+	return ret;
+}
+
+
+/* {{{ _mysqlnd_store_result */
+MYSQLND_RES * _mysqlnd_store_result(MYSQLND * const conn TSRMLS_DC)
+{
+	enum_func_status ret;
+	MYSQLND_RES *result;
+	zend_bool to_cache = FALSE;
+
+	if (!conn->current_result) {
+		return NULL;
+	}
+
+	/* Nothing to store for UPSERT/LOAD DATA*/
+	if (conn->last_query_type != QUERY_SELECT || conn->state != CONN_FETCHING_DATA) {
+		SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
+						 mysqlnd_out_of_sync); 
+		return NULL;
+	}
+
+	MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_BUFFERED_SETS);
+
+	result = conn->current_result;
+	conn->current_result = NULL;
+
+	result->conn			= NULL;	/* store result does not reference  the connection */
+	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;
+
+	result->lengths = ecalloc(result->field_count, sizeof(unsigned long));
+
+	ret = mysqlnd_store_result_fetch_data(conn, result, result->meta,
+										  FALSE, TRUE, 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;
+	} else {
+		conn->error_info = result->data->error_info;
+		mysqlnd_internal_free_result(result TSRMLS_CC);
+		result = NULL;
+	}
+	return result;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_unbuffered_skip_result */
+static enum_func_status
+_mysqlnd_unbuffered_skip_result(MYSQLND_RES * const result TSRMLS_DC)
+{
+	zend_bool fetched_anything;
+
+	/*
+	  Unbuffered sets
+	  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 &&
+		!result->unbuf->eof_reached && result->m.fetch_row)
+	{
+		/* We have to fetch all data to clean the line */
+		MYSQLND_INC_CONN_STATISTIC(&result->conn->stats,
+									result->type == MYSQLND_RES_NORMAL? STAT_FLUSHED_NORMAL_SETS:
+																		STAT_FLUSHED_PS_SETS);
+
+		while ((PASS == result->m.fetch_row(result, NULL, 0, &fetched_anything
TSRMLS_CC)) &&
+			   fetched_anything == TRUE) {
+			/* do nothing */;
+		}
+	}
+	return PASS;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_free_result */
+static enum_func_status
+_mysqlnd_free_result(MYSQLND_RES *result, zend_bool implicit TSRMLS_DC)
+{
+	result->m.skip_result(result TSRMLS_CC);
+	MYSQLND_INC_CONN_STATISTIC(result->conn? &result->conn->stats : NULL,
+							   implicit == TRUE?	STAT_FREE_RESULT_EXPLICIT:
+													STAT_FREE_RESULT_IMPLICIT);
+
+	mysqlnd_internal_free_result(result TSRMLS_CC);
+	return PASS;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_data_seek */
+static enum_func_status
+_mysqlnd_data_seek(MYSQLND_RES *result, mynd_ulonglong row)
+{
+	if (!result->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;
+	} else {
+		result->data->data_cursor = result->data->data + row;
+	}
+
+	return PASS;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_num_fields */
+mynd_ulonglong _mysqlnd_num_rows(const MYSQLND_RES * const res)
+{
+	/* Be compatible with libmysql. We count row_count, but will return 0 */
+	return res->data? res->data->row_count:0;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_num_fields */
+unsigned int _mysqlnd_num_fields(const MYSQLND_RES * const res)
+{
+	return res->field_count;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_fetch_field */
+static MYSQLND_FIELD *
+_mysqlnd_fetch_field(MYSQLND_RES * const result)
+{
+	if (!result->meta || result->meta->current_field >= result->field_count)
+		return NULL;
+	return &result->meta->fields[result->meta->current_field++];
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_fetch_field_direct */
+static MYSQLND_FIELD *
+_mysqlnd_fetch_field_direct(const MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET
fieldnr)
+{
+	return result->meta? &result->meta->fields[fieldnr]:NULL;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_field_seek */
+static MYSQLND_FIELD_OFFSET
+_mysqlnd_field_seek(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET field_offset)
+{
+	MYSQLND_FIELD_OFFSET return_value = 0;
+	if (result->meta) {
+		return_value = result->meta->current_field;
+		result->meta->current_field = field_offset;
+	}
+	return return_value;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_field_tell */
+static MYSQLND_FIELD_OFFSET
+_mysqlnd_field_tell(const MYSQLND_RES * const result)
+{
+	return result->meta? result->meta->current_field:0;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_fetch_into */
+static
+void _mysqlnd_fetch_into(MYSQLND_RES *result, unsigned int flags, zval *return_value
TSRMLS_DC ZEND_FILE_LINE_DC)
+{
+	zend_bool fetched_anything;
+
+	if (!result->m.fetch_row) {
+		RETURN_NULL();
+	}
+	/*
+	  Hint Zend how many elements we will have in the hash. Thus it won't
+	  extend and rehash the hash constantly.
+	*/
+	mysqlnd_array_init(return_value, mysqlnd_num_fields(result) * 2);
+	if (FAIL == result->m.fetch_row(result, (void *)return_value, flags,
&fetched_anything TSRMLS_CC)) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading a row");
+		RETURN_FALSE;
+	} else if (fetched_anything == FALSE) {
+		zval_dtor(return_value);
+		RETURN_NULL();
+	}
+	/*
+	  return_value is IS_NULL for no more data and an array for data. Thus it's ok
+	  to return here.
+	*/
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_fetch_all */
+static
+void _mysqlnd_fetch_all(MYSQLND_RES *result, unsigned int flags, zval *return_value
TSRMLS_DC ZEND_FILE_LINE_DC)
+{
+	zval  *row;
+	ulong i = 0;
+
+	/* mysqlnd_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) {
+		RETURN_NULL();
+	}	
+
+	mysqlnd_array_init(return_value, result->data->row_count);
+
+	while (result->data->data_cursor &&
+		   (result->data->data_cursor - result->data->data) <
result->data->row_count)
+	{
+		MAKE_STD_ZVAL(row);
+		mysqlnd_fetch_into(result, flags, row);
+		add_index_zval(return_value, i++, row);
+	}
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_result_metadata_clone_metadata */
+static
+MYSQLND_RES_METADATA * _mysqlnd_clone_metadata(const MYSQLND_RES_METADATA * const meta,
zend_bool persistent)
+{
+	unsigned int i;
+	/* +1 is to have empty marker at the end */
+	MYSQLND_RES_METADATA *new_meta = pemalloc(sizeof(MYSQLND_RES_METADATA), persistent);
+	MYSQLND_FIELD *new_fields = pecalloc(meta->field_count + 1, sizeof(MYSQLND_FIELD),
persistent);
+	MYSQLND_FIELD *original_fields = meta->fields;
+	size_t len = meta->field_count * sizeof(struct mysqlnd_field_hash_key);
+
+	new_meta->zend_hash_keys = pemalloc(len, persistent);
+	memcpy(new_meta->zend_hash_keys, meta->zend_hash_keys, len);
+
+	/*
+	  This will copy also the strings and the root, which we will have
+	  to adjust in the loop
+	*/
+	memcpy(new_fields, original_fields, (meta->field_count) * sizeof(MYSQLND_FIELD));
+	for (i = 0; i < meta->field_count; i++) {
+		/* First copy the root, then field by field adjust the pointers */
+		new_fields[i].root = pemalloc(original_fields[i].root_len, persistent);
+		memcpy(new_fields[i].root, original_fields[i].root, new_fields[i].root_len);
+
+		new_fields[i].name		= new_fields[i].root + (original_fields[i].name -
original_fields[i].root);
+		new_fields[i].org_name	= new_fields[i].root + (original_fields[i].org_name -
original_fields[i].root);
+
+		new_fields[i].table		= new_fields[i].root + (original_fields[i].table -
original_fields[i].root);
+		new_fields[i].org_table	= new_fields[i].root + (original_fields[i].org_table -
original_fields[i].root);
+
+		new_fields[i].db		= new_fields[i].root + (original_fields[i].db -
original_fields[i].root);
+		new_fields[i].catalog	= new_fields[i].root + (original_fields[i].catalog -
original_fields[i].root);
+		/* def is not on the root, if allocated at all */
+		if (original_fields[i].def) {
+			new_fields[i].def	= pemalloc(original_fields[i].def_length + 1, persistent);
+			/* copy the trailing \0 too */
+			memcpy(new_fields[i].def, original_fields[i].def, original_fields[i].def_length + 1);
+		}
+#if PHP_MAJOR_VERSION >= 6
+		if (new_meta->zend_hash_keys[i].ustr.u) {
+			new_meta->zend_hash_keys[i].ustr.u =
+					eustrndup(new_meta->zend_hash_keys[i].ustr.u,
new_meta->zend_hash_keys[i].ulen);
+		}
+#endif 
+	}
+	new_meta->current_field = 0;
+	new_meta->field_count = meta->field_count;
+
+	new_meta->fields = new_fields;
+
+	return new_meta;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_result_init */
+MYSQLND_RES *mysqlnd_result_init(unsigned int field_count, MYSQLND_THD_ZVAL_PCACHE
*cache)
+{
+	MYSQLND_RES *ret = ecalloc(1, sizeof(MYSQLND_RES));
+
+	ret->field_count	= field_count;
+	ret->zval_cache		= cache;
+	ret->references		= 1;
+	ret->m.free_result	= _mysqlnd_free_result;
+	ret->m.clone_metadata = _mysqlnd_clone_metadata;
+	ret->m.free_result_buffers = mysqlnd_internal_free_result_buffers;
+	ret->m.seek_data	= _mysqlnd_data_seek;
+	ret->m.skip_result	= _mysqlnd_unbuffered_skip_result;
+	ret->m.num_rows		= _mysqlnd_num_rows;
+	ret->m.num_fields	= _mysqlnd_num_fields;
+	ret->m.fetch_into	= _mysqlnd_fetch_into;
+	ret->m.fetch_all	= _mysqlnd_fetch_all;
+	ret->m.seek_field	= _mysqlnd_field_seek;
+	ret->m.field_tell	= _mysqlnd_field_tell;
+	ret->m.fetch_field	= _mysqlnd_fetch_field;
+	ret->m.fetch_field_direct = _mysqlnd_fetch_field_direct;
+	ret->m.fetch_row_normal_buffered = mysqlnd_fetch_row_buffered;
+	ret->m.fetch_row_normal_unbuffered = mysqlnd_fetch_row_unbuffered;
+
+	return ret;
+}
+/* }}} */

Added: trunk/mysqlnd/mysqlnd_result.h
===================================================================
--- trunk/mysqlnd/mysqlnd_result.h	2007-06-14 09:44:19 UTC (rev 369)
+++ trunk/mysqlnd/mysqlnd_result.h	2007-06-14 11:22:25 UTC (rev 370)
@@ -0,0 +1,51 @@
+/*
+  +----------------------------------------------------------------------+
+  | 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: header,v 1.17 2006/01/01 13:09:48 sniper Exp $ */
+
+#ifndef MYSQLND_RESULT_H
+#define MYSQLND_RESULT_H
+
+MYSQLND_RES *mysqlnd_result_init(unsigned int field_count, MYSQLND_THD_ZVAL_PCACHE
*cache);
+
+void mysqlnd_unbuffered_free_last_data(MYSQLND_RES *result TSRMLS_DC);
+MYSQLND_RES *mysqlnd_result_init(unsigned int field_count, MYSQLND_THD_ZVAL_PCACHE
*cache);							
+void mysqlnd_internal_free_result_contents(MYSQLND_RES *result TSRMLS_DC);
+void mysqlnd_internal_free_result_buffers(MYSQLND_RES *result TSRMLS_DC);
+void mysqlnd_internal_free_result(MYSQLND_RES *result TSRMLS_DC);
+
+
+enum_func_status mysqlnd_read_result_metadata(MYSQLND *conn, MYSQLND_RES *result
TSRMLS_DC);
+void mysqlnd_internal_free_result_metadata(MYSQLND_RES_METADATA *meta, zend_bool
persistent TSRMLS_DC);
+
+enum_func_status
+mysqlnd_store_result_fetch_data(MYSQLND * const conn, MYSQLND_RES *result,
+								MYSQLND_RES_METADATA *meta,
+								zend_bool binary_protocol,
+								zend_bool update_max_length,
+								zend_bool to_cache TSRMLS_DC);
+
+enum_func_status mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt
TSRMLS_DC);
+
+MYSQLND_RES * _mysqlnd_use_result(MYSQLND * const conn TSRMLS_DC);
+MYSQLND_RES * _mysqlnd_store_result(MYSQLND * const conn TSRMLS_DC);
+
+
+#endif

Thread
PHP mysqlnd svn commit: r370 - trunk/mysqlndahristov14 Jun