diff options
Diffstat (limited to 'ext/mysqlnd/mysqlnd_result.c')
| -rw-r--r-- | ext/mysqlnd/mysqlnd_result.c | 238 | 
1 files changed, 202 insertions, 36 deletions
diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c index ce5322b7ac..9a19f39789 100644 --- a/ext/mysqlnd/mysqlnd_result.c +++ b/ext/mysqlnd/mysqlnd_result.c @@ -438,7 +438,7 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC  static   unsigned long * mysqlnd_fetch_lengths_buffered(MYSQLND_RES * const result)  { -	int i; +	unsigned int i;  	zval **previous_row;  	/* @@ -481,6 +481,102 @@ PHPAPI unsigned long * mysqlnd_fetch_lengths(MYSQLND_RES * const result)  /* }}} */ +/* {{{ mysqlnd_fetch_row_unbuffered_c */ +static MYSQLND_ROW_C +mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES *result TSRMLS_DC) +{ +	enum_func_status		ret; +	MYSQLND_ROW_C 			retrow = NULL; +	unsigned int			i, +							field_count = result->field_count; +	php_mysql_packet_row	*row_packet = result->row_packet; +	unsigned long			*lengths = result->lengths; + +	DBG_ENTER("mysqlnd_fetch_row_unbuffered"); + +	if (result->unbuf->eof_reached) { +		/* No more rows obviously */ +		DBG_RETURN(retrow); +	} +	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);  +		DBG_RETURN(retrow); +	} +	/* Let the row packet fill our buffer and skip additional mnd_malloc + memcpy */ +	row_packet->skip_extraction = FALSE; + +	/* +	  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) { +		MYSQLND_FIELD *field = result->meta->fields; + +		result->unbuf->row_count++; + +		retrow = mnd_malloc(result->field_count * sizeof(char *)); + +		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_NORMAL_UNBUF); + +		for (i = 0; i < field_count; i++, field++) { +			zval *data = result->unbuf->last_row_data[i]; +			int len; + +			if (Z_TYPE_P(data) != IS_NULL) { +				convert_to_string(data); +				retrow[i] = Z_STRVAL_P(data); +				len = Z_STRLEN_P(data); +			} else { +				retrow[i] = NULL; +				len = 0; +			} + +			if (lengths) { +				lengths[i] = len; +			} + +			if (field->max_length < len) { +				field->max_length = len; +			} +		} +	} else if (ret == FAIL) { +		if (row_packet->error_info.error_no) { +			result->conn->error_info = row_packet->error_info; +			DBG_ERR_FMT("errorno=%d error=%s", row_packet->error_info.error_no, row_packet->error_info.error);  +		} +		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 */ +		DBG_INF_FMT("warns=%u status=%u", row_packet->warning_count, row_packet->server_status); +		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); +	} + +	DBG_RETURN(retrow); +} +/* }}} */ + +  /* {{{ mysqlnd_fetch_row_unbuffered */  static enum_func_status  mysqlnd_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flags, @@ -528,11 +624,12 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flag  		if (!row_packet->skip_extraction) {  			HashTable *row_ht = Z_ARRVAL_P(row); +			MYSQLND_FIELD *field = result->meta->fields; +			struct mysqlnd_field_hash_key *zend_hash_key = result->meta->zend_hash_keys; -			for (i = 0; i < field_count; i++) { +			for (i = 0; i < field_count; i++, field++, zend_hash_key++) {  				zval *data = result->unbuf->last_row_data[i];  				int len = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data); -				MYSQLND_RES_METADATA *meta = result->meta;  				if (lengths) {  					lengths[i] = len; @@ -555,31 +652,31 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flag  					  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 (meta->zend_hash_keys[i].is_numeric == FALSE) { +					if (zend_hash_key->is_numeric == FALSE) {  #if PHP_MAJOR_VERSION >= 6  						if (UG(unicode)) {  							zend_u_hash_quick_update(Z_ARRVAL_P(row), IS_UNICODE, -													 meta->zend_hash_keys[i].ustr, -													 meta->zend_hash_keys[i].ulen + 1, -													 meta->zend_hash_keys[i].key, +													 zend_hash_key->ustr, +													 zend_hash_key->ulen + 1, +													 zend_hash_key->key,  													 (void *) &data, sizeof(zval *), NULL);  						} else  #endif  						{  							zend_hash_quick_update(Z_ARRVAL_P(row), -												   meta->fields[i].name, -												   meta->fields[i].name_length + 1, -												   meta->zend_hash_keys[i].key, +												   field->name, +												   field->name_length + 1, +												   zend_hash_key->key,  												   (void *) &data, sizeof(zval *), NULL);  						}  					} else {  						zend_hash_index_update(Z_ARRVAL_P(row), -											   meta->zend_hash_keys[i].key, +											   zend_hash_key->key,  											   (void *) &data, sizeof(zval *), NULL);  					}  				} -				if (meta->fields[i].max_length < len) { -					meta->fields[i].max_length = len; +				if (field->max_length < len) { +					field->max_length = len;  				}  			}  		} @@ -617,16 +714,27 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flag  /* {{{ mysqlnd_res::use_result */ -MYSQLND_RES * +static MYSQLND_RES *  MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps TSRMLS_DC)  {  	DBG_ENTER("mysqlnd_res::use_result");  	DBG_INF_FMT("ps=%d", ps); -	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			= mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); +	SET_EMPTY_ERROR(result->conn->error_info); + +	if (ps == FALSE) { +		result->type			= MYSQLND_RES_NORMAL; +		result->m.fetch_row		= result->m.fetch_row_normal_unbuffered; +		result->m.fetch_lengths	= mysqlnd_fetch_lengths_unbuffered; +		result->lengths			= mnd_ecalloc(result->field_count, sizeof(unsigned long)); +	} else { +		result->type			= MYSQLND_RES_PS_UNBUF; +		/* result->m.fetch_row() will be set in mysqlnd_ps.c */ +		result->m.fetch_lengths	= NULL; /* makes no sense */ +		result->lengths 		= NULL; +	} + +	result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));  	/*  	  Will be freed in the mysqlnd_internal_free_result_contents() called @@ -635,19 +743,52 @@ MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps  	*/  	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->binary_protocol = ps;  	result->row_packet->fields_metadata = result->meta->fields;  	result->row_packet->bit_fields_count = result->meta->bit_fields_count;  	result->row_packet->bit_fields_total_len = result->meta->bit_fields_total_len; -	result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long)); - -	/* No multithreading issues as we don't share the connection :) */  	DBG_RETURN(result);  }  /* }}} */ +/* {{{ mysqlnd_fetch_row_buffered_c */ +static MYSQLND_ROW_C +mysqlnd_fetch_row_buffered_c(MYSQLND_RES *result TSRMLS_DC) +{ +	MYSQLND_ROW_C ret = NULL; +	unsigned int i; + +	DBG_ENTER("mysqlnd_fetch_row_buffered_c"); + +	/* If we haven't read everything */ +	if (result->data->data_cursor && +		(result->data->data_cursor - result->data->data) < result->data->row_count) +	{ +		ret = mnd_malloc(result->field_count * sizeof(char *)); + +		zval **current_row = *result->data->data_cursor; +		for (i = 0; i < result->field_count; i++) { +			zval *data = current_row[i]; +			if (Z_TYPE_P(data) != IS_NULL) { +				convert_to_string(data); +				ret[i] = Z_STRVAL_P(data); +			} else { +				ret[i] = NULL; +			} +		} +		result->data->data_cursor++; +		MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF); +	} else { +		result->data->data_cursor = NULL; +		DBG_INF("EOF reached"); +	} +	DBG_RETURN(ret); +} +/* }}} */ + +  /* {{{ mysqlnd_fetch_row_buffered */  static enum_func_status  mysqlnd_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, @@ -664,7 +805,10 @@ mysqlnd_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags,  		(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++) { +		MYSQLND_FIELD *field = result->meta->fields; +		struct mysqlnd_field_hash_key *zend_hash_key = result->meta->zend_hash_keys; + +		for (i = 0; i < result->field_count; i++, field++, zend_hash_key++) {  			zval *data = current_row[i];  			/* @@ -687,26 +831,26 @@ mysqlnd_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags,  				  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 (zend_hash_key->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, +												 zend_hash_key->ustr, +												 zend_hash_key->ulen + 1, +												 zend_hash_key->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, +											   field->name, +											   field->name_length + 1, +											   zend_hash_key->key,  											   (void *) &data, sizeof(zval *), NULL);  					}  				} else {  					zend_hash_index_update(Z_ARRVAL_P(row), -										   result->meta->zend_hash_keys[i].key, +										   zend_hash_key->key,  										   (void *) &data, sizeof(zval *), NULL);  				}  			} @@ -761,7 +905,7 @@ mysqlnd_store_result_fetch_data(MYSQLND * const conn, MYSQLND_RES *result,  	row_packet.bit_fields_total_len = meta->bit_fields_total_len;  	/* Let the row packet fill our buffer and skip additional malloc + memcpy */  	while (FAIL != (ret = PACKET_READ_ALLOCA(row_packet, conn)) && !row_packet.eof) { -		int i; +		unsigned int i;  		zval **current_row;  		if (!free_rows) { @@ -849,7 +993,7 @@ mysqlnd_store_result_fetch_data(MYSQLND * const conn, MYSQLND_RES *result,  /* {{{ mysqlnd_res::store_result */ -MYSQLND_RES * +static MYSQLND_RES *  MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result,  										  MYSQLND * const conn,  										  zend_bool ps_protocol TSRMLS_DC) @@ -959,7 +1103,7 @@ MYSQLND_METHOD(mysqlnd_res, data_seek)(MYSQLND_RES *result, uint64 row TSRMLS_DC  /* {{{ mysqlnd_res::num_fields */ -uint64 +static uint64  MYSQLND_METHOD(mysqlnd_res, num_rows)(const MYSQLND_RES * const res)  {  	/* Be compatible with libmysql. We count row_count, but will return 0 */ @@ -969,7 +1113,7 @@ MYSQLND_METHOD(mysqlnd_res, num_rows)(const MYSQLND_RES * const res)  /* {{{ mysqlnd_res::num_fields */ -unsigned int +static unsigned int  MYSQLND_METHOD(mysqlnd_res, num_fields)(const MYSQLND_RES * const res)  {  	return res->field_count; @@ -1066,6 +1210,27 @@ MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES *result, unsigned int flags,  /* }}} */ +/* {{{ mysqlnd_res::fetch_row_c */ +static MYSQLND_ROW_C +MYSQLND_METHOD(mysqlnd_res, fetch_row_c)(MYSQLND_RES *result TSRMLS_DC) +{ +	MYSQLND_ROW_C ret = NULL; +	DBG_ENTER("mysqlnd_res::fetch_row_c"); + +	if (result->m.fetch_row) { +		if (result->m.fetch_row == result->m.fetch_row_normal_buffered) { +			return mysqlnd_fetch_row_buffered_c(result TSRMLS_CC); +		} else if (result->m.fetch_row == result->m.fetch_row_normal_unbuffered) { +			return mysqlnd_fetch_row_unbuffered_c(result TSRMLS_CC); +		} else { +			*((int*)NULL) = 1; +		} +	} +	DBG_RETURN(ret); +} +/* }}} */ + +  /* {{{ mysqlnd_res::fetch_all */  static void  MYSQLND_METHOD(mysqlnd_res, fetch_all)(MYSQLND_RES *result, unsigned int flags, @@ -1108,7 +1273,7 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field_data)(MYSQLND_RES *result, unsigned int  {  	zval row;  	zval **entry; -	uint i = 0; +	unsigned int i = 0;  	DBG_ENTER("mysqlnd_res::fetch_field_data");  	DBG_INF_FMT("offset=%u", offset); @@ -1164,6 +1329,7 @@ MYSQLND_RES *mysqlnd_result_init(unsigned int field_count, MYSQLND_THD_ZVAL_PCAC  	ret->m.num_rows		= MYSQLND_METHOD(mysqlnd_res, num_rows);  	ret->m.num_fields	= MYSQLND_METHOD(mysqlnd_res, num_fields);  	ret->m.fetch_into	= MYSQLND_METHOD(mysqlnd_res, fetch_into); +	ret->m.fetch_row_c	= MYSQLND_METHOD(mysqlnd_res, fetch_row_c);  	ret->m.fetch_all	= MYSQLND_METHOD(mysqlnd_res, fetch_all);  	ret->m.fetch_field_data	= MYSQLND_METHOD(mysqlnd_res, fetch_field_data);  	ret->m.seek_field	= MYSQLND_METHOD(mysqlnd_res, field_seek);  | 
