List:Commits« Previous MessageNext Message »
From:ahristov Date:March 12 2007 9:58am
Subject:PHP mysqlnd svn commit: r95 - trunk/ext/mysqli/mysqlnd
View as plain text  
Author: ahristov
Date: 2007-03-12 10:58:08 +0100 (Mon, 12 Mar 2007)
New Revision: 95

Modified:
   trunk/ext/mysqli/mysqlnd/mysqlnd.c
   trunk/ext/mysqli/mysqlnd/mysqlnd.h
   trunk/ext/mysqli/mysqlnd/mysqlnd_ps.c
   trunk/ext/mysqli/mysqlnd/mysqlnd_wireprotocol.c
Log:
Fix stmt_metadata crash, because we didn't close the
metadata and were doing double free.


Modified: trunk/ext/mysqli/mysqlnd/mysqlnd.c
===================================================================
--- trunk/ext/mysqli/mysqlnd/mysqlnd.c	2007-03-08 09:15:49 UTC (rev 94)
+++ trunk/ext/mysqli/mysqlnd/mysqlnd.c	2007-03-12 09:58:08 UTC (rev 95)
@@ -803,6 +803,7 @@
 		result->fields = NULL;
 
 		efree(result->zend_hash_keys);
+		result->zend_hash_keys = NULL;
 	}
 	/* +1 is to have empty marker at the end */
 	result->fields = ecalloc(result->field_count + 1, sizeof(MYSQLND_FIELD));
@@ -1030,27 +1031,29 @@
 	MYSQLND_ROW ret = NULL;
 	zend_bool fetched_anything;
 
-	MAKE_STD_ZVAL(row);
-	mysqlnd_array_init(row, result->field_count);
+	if (result->m.fetch_row) {
+		MAKE_STD_ZVAL(row);
+		mysqlnd_array_init(row, result->field_count);
 
-	if (PASS == result->m.fetch_row(result, row, flags, &fetched_anything TSRMLS_CC)
&&
-		fetched_anything == TRUE)
-	{
-		zval **entry;
-		uint i = 0;
-		if (!result->last_row) {
-			result->last_row = emalloc(result->field_count * sizeof(char *));
-		}
-		ret = result->last_row;
+		if (PASS == result->m.fetch_row(result, row, flags, &fetched_anything TSRMLS_CC)
&&
+			fetched_anything == TRUE)
+		{
+			zval **entry;
+			uint i = 0;
+			if (!result->last_row) {
+				result->last_row = emalloc(result->field_count * sizeof(char *));
+			}
+			ret = result->last_row;
 
-		zend_hash_internal_pointer_reset(Z_ARRVAL_P(row));
-		while (zend_hash_get_current_data(Z_ARRVAL_P(row), (void **)&entry) == SUCCESS) {
-			/* Everything is a string */
-			ret[i++] = Z_STRVAL_PP(entry);
-			zend_hash_move_forward(Z_ARRVAL_P(row));
+			zend_hash_internal_pointer_reset(Z_ARRVAL_P(row));
+			while (zend_hash_get_current_data(Z_ARRVAL_P(row), (void **)&entry) == SUCCESS) {
+				/* Everything is a string */
+				ret[i++] = Z_STRVAL_PP(entry);
+				zend_hash_move_forward(Z_ARRVAL_P(row));
+			}
 		}
+		zval_ptr_dtor(&row);
 	}
-	zval_ptr_dtor(&row);
 	return ret;
 }
 /* }}} */
@@ -1223,7 +1226,7 @@
 	conn->current_result = NULL;
 
 	result->type			= MYSQLND_RES_NORMAL;
-	result->m.fetch_row		= mysqlnd_fetch_row_unbuffered;
+	result->m.fetch_row		= result->m.fetch_row_normal_unbuffered;
 	result->m.fetch_lengths	= mysqlnd_fetch_lengths_unbuffered;
 	result->m.fetch_row_old_way = _mysqlnd_fetch_row;
 
@@ -1319,7 +1322,7 @@
 	conn->current_result = NULL;
 
 	result->type			= MYSQLND_RES_NORMAL;
-	result->m.fetch_row		= mysqlnd_fetch_row_buffered;
+	result->m.fetch_row		= result->m.fetch_row_normal_buffered;
 	result->m.fetch_lengths	= mysqlnd_fetch_lengths_buffered;
 	result->m.fetch_row_old_way = _mysqlnd_fetch_row;
 
@@ -1330,7 +1333,7 @@
 	/* Create room for 'next_extend' rows */
 
 	result->conn = NULL;	/* store result does not reference  the connection */
-	result->data 	= emalloc(next_extend * sizeof(zval **));
+	result->data 		= emalloc(next_extend * sizeof(zval **));
 	result->row_buffers = emalloc(next_extend * sizeof(zend_uchar *));
 
 	free_rows = next_extend;
@@ -1367,7 +1370,8 @@
 			*/
 			if (Z_TYPE_P(current_row[i]) != IS_NULL &&
 				(len = Z_STRLEN_P(current_row[i])) &&
-				result->fields[i].max_length < len) {
+				result->fields[i].max_length < len)
+			{
 				result->fields[i].max_length = len;
 			}
 		}
@@ -2209,6 +2213,9 @@
 {
 	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.
@@ -2338,6 +2345,8 @@
 	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;
 }

Modified: trunk/ext/mysqli/mysqlnd/mysqlnd.h
===================================================================
--- trunk/ext/mysqli/mysqlnd/mysqlnd.h	2007-03-08 09:15:49 UTC (rev 94)
+++ trunk/ext/mysqli/mysqlnd/mysqlnd.h	2007-03-12 09:58:08 UTC (rev 95)
@@ -371,6 +371,7 @@
 	unsigned int charsetnr;		/* Character set */
 	enum mysqlnd_field_types type;	/* Type of field. See mysql_com.h for types */
 	char *root;
+	size_t root_len;
 } MYSQLND_FIELD;
 
 
@@ -545,6 +546,8 @@
 
 struct st_mysqlnd_res_methods {
 	mysqlnd_fetch_row_func	fetch_row;
+	mysqlnd_fetch_row_func	fetch_row_normal_buffered; /* private */
+	mysqlnd_fetch_row_func	fetch_row_normal_unbuffered; /* private */
 	void 				(*fetch_into)(MYSQLND_RES *result, unsigned int flags, zval *return_value
TSRMLS_DC ZEND_FILE_LINE_DC);
 	void 				(*fetch_all)(MYSQLND_RES *result, unsigned int flags, zval *return_value
TSRMLS_DC ZEND_FILE_LINE_DC);
 	mynd_ulonglong		(*num_rows)(const MYSQLND_RES * const result);
@@ -578,7 +581,8 @@
 	enum_func_status	(*bind_result)(MYSQLND_STMT * const stmt, MYSQLND_RESULT_BIND * const
result_bind);
 	enum_func_status	(*send_long_data)(MYSQLND_STMT * const stmt, unsigned int param_num,
 										  const char * const data, unsigned long length TSRMLS_DC);
-	MYSQLND_RES			(*get_param_metadata)(MYSQLND_STMT * const stmt);
+	MYSQLND_RES	*		(*get_parameter_metadata)(MYSQLND_STMT * const stmt);
+	MYSQLND_RES	*		(*get_result_metadata)(MYSQLND_STMT * const stmt);
 
 	mynd_ulonglong		(*get_last_insert_id)(const MYSQLND_STMT * const stmt);
 	mynd_ulonglong		(*get_affected_rows)(const MYSQLND_STMT * const stmt);
@@ -951,7 +955,8 @@
 #define mysqlnd_stmt_send_long_data(s,p,d,l) (s)->m->send_long_data((s), (p), (d),
(l) TSRMLS_CC)
 #define mysqlnd_stmt_bind_param(stmt,bind)	(stmt)->m->bind_param((stmt), (bind))
 #define mysqlnd_stmt_bind_result(stmt,bind)	(stmt)->m->bind_result((stmt), (bind))
-#define mysqlnd_stmt_param_metadata(stmt)	(stmt)->m->get_param_metadata((stmt))
+#define mysqlnd_stmt_param_metadata(stmt)	(stmt)->m->get_parameter_metadata((stmt))
+#define mysqlnd_stmt_result_metadata(stmt)	(stmt)->m->get_result_metadata((stmt))
 
 #define	mysqlnd_stmt_free_result(stmt)		(stmt)->m->free_result((stmt) TSRMLS_CC)
 #define	mysqlnd_stmt_close(stmt, implicit)	(stmt)->m->dtor((stmt), (implicit)
TSRMLS_CC)

Modified: trunk/ext/mysqli/mysqlnd/mysqlnd_ps.c
===================================================================
--- trunk/ext/mysqli/mysqlnd/mysqlnd_ps.c	2007-03-08 09:15:49 UTC (rev 94)
+++ trunk/ext/mysqli/mysqlnd/mysqlnd_ps.c	2007-03-12 09:58:08 UTC (rev 95)
@@ -1141,6 +1141,76 @@
 /* }}} */
 
 
+/* {{{ mysqlnd_result_metadata_clone_metadata */
+static
+MYSQLND_FIELD * mysqlnd_result_metadata_clone_metadata(MYSQLND_RES * const result)
+{
+	unsigned int i;
+	/* +1 is to have empty marker at the end */
+	MYSQLND_FIELD *new_meta = ecalloc(result->field_count + 1, sizeof(MYSQLND_FIELD));
+	MYSQLND_FIELD *original_meta = result->fields;
+
+	/*
+	  This will copy also the strings and the root, which we will have
+	  to adjust in the loop
+	*/
+	memcpy(new_meta, original_meta, (result->field_count) * sizeof(MYSQLND_FIELD));
+	for (i = 0; i < result->field_count; i++) {
+		/* First copy the root, then field by field adjust the pointers */
+		new_meta[i].root = emalloc(original_meta[i].root_len);
+		memcpy(new_meta[i].root, original_meta[i].root, new_meta[i].root_len);
+
+		new_meta[i].name 		= new_meta[i].root + (original_meta[i].name -
original_meta[i].root);	
+		new_meta[i].org_name	= new_meta[i].root + (original_meta[i].org_name -
original_meta[i].root);	
+
+		new_meta[i].table		= new_meta[i].root + (original_meta[i].table -
original_meta[i].root);	
+		new_meta[i].org_table	= new_meta[i].root + (original_meta[i].org_table -
original_meta[i].root);	
+
+		new_meta[i].db		= new_meta[i].root + (original_meta[i].db - original_meta[i].root);
+		new_meta[i].catalog	= new_meta[i].root + (original_meta[i].catalog -
original_meta[i].root);	
+		/* def is not on the root, if allocated at all */
+		if (original_meta[i].def) {
+			new_meta[i].def 	= emalloc(original_meta[i].def_length + 1);
+			/* copy the trailing \0 too */
+			memcpy(new_meta[i].def, original_meta[i].def, original_meta[i].def_length + 1);
+		}
+	}
+
+	return new_meta;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_stmt_param_metadata */
+static
+MYSQLND_RES * _mysqlnd_stmt_result_metadata(MYSQLND_STMT * const stmt)
+{
+	MYSQLND_RES *result;
+	MYSQLND_ZVAL_PCACHE * cache;
+
+	if (!stmt->field_count || !stmt->conn || !stmt->result) {
+		return NULL;
+	}
+
+	/*
+	  TODO: This implementation is kind of a hack,
+	        find a better way to do it. In different functions I have put
+			fuses to check for result->m.fetch_row() being NULL. This should
+			be handled in a better way.
+	
+	*/
+	cache = mysqlnd_palloc_get_cache_reference(stmt->conn->zval_cache);
+	result = mysqlnd_result_init(stmt->field_count, cache);
+	result->type = MYSQLND_RES_NORMAL;
+	result->m.fetch_row = result->m.fetch_row_normal_unbuffered;
+	result->eof_reached = TRUE;
+	result->fields=	mysqlnd_result_metadata_clone_metadata(stmt->result);
+
+	return result;
+}
+/* }}} */
+
+
 /* {{{ _mysqlnd_stmt_attr_set */
 static enum_func_status
 _mysqlnd_stmt_attr_set(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, const
void * const value)
@@ -1354,6 +1424,7 @@
 	_mysqlnd_stmt_bind_result,
 	_mysqlnd_stmt_send_long_data,
 	_mysqlnd_stmt_param_metadata,
+	_mysqlnd_stmt_result_metadata,
 
 	_mysqlnd_stmt_insert_id,
 	_mysqlnd_stmt_affected_rows,

Modified: trunk/ext/mysqli/mysqlnd/mysqlnd_wireprotocol.c
===================================================================
--- trunk/ext/mysqli/mysqlnd/mysqlnd_wireprotocol.c	2007-03-08 09:15:49 UTC (rev 94)
+++ trunk/ext/mysqli/mysqlnd/mysqlnd_wireprotocol.c	2007-03-12 09:58:08 UTC (rev 95)
@@ -959,6 +959,7 @@
 	/* 2 byte filler */
 	p +=2;
 
+	/* def could be empty, thus don't allocate on the root */
 	if (packet->header.size > (p - buf) && (len =
php_mysqlnd_net_field_length(&p))) {
 		meta->def = emalloc(len + 1);
 		memcpy(meta->def, p, len);
@@ -968,6 +969,7 @@
 	}
 
 	root_ptr = meta->root = emalloc(total_len);
+	meta->root_len = total_len;
 	/* Now do allocs */
 	if (meta->catalog) {
 		len = meta->catalog_length;

Thread
PHP mysqlnd svn commit: r95 - trunk/ext/mysqli/mysqlndahristov12 Mar