Index: ext/mysqlnd/mysqlnd_structs.h =================================================================== --- ext/mysqlnd/mysqlnd_structs.h (revision 316335) +++ ext/mysqlnd/mysqlnd_structs.h (working copy) @@ -224,7 +224,7 @@ typedef struct st_mysqlnd_debug MYSQLND_DEBUG; -typedef MYSQLND_RES* (*mysqlnd_stmt_use_or_store_func)(MYSQLND_STMT * const TSRMLS_DC); +typedef MYSQLND_RES* (*mysqlnd_stmt_use_or_store_func)(MYSQLND_STMT * const, uint flags TSRMLS_DC); typedef enum_func_status (*mysqlnd_fetch_row_func)(MYSQLND_RES *result, void *param, unsigned int flags, @@ -357,8 +357,8 @@ typedef enum_func_status (*func_mysqlnd_conn__query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC); typedef enum_func_status (*func_mysqlnd_conn__send_query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC); typedef enum_func_status (*func_mysqlnd_conn__reap_query)(MYSQLND *conn TSRMLS_DC); -typedef MYSQLND_RES * (*func_mysqlnd_conn__use_result)(MYSQLND * const conn TSRMLS_DC); -typedef MYSQLND_RES * (*func_mysqlnd_conn__store_result)(MYSQLND * const conn TSRMLS_DC); +typedef MYSQLND_RES * (*func_mysqlnd_conn__use_result)(MYSQLND * const conn, uint flags TSRMLS_DC); +typedef MYSQLND_RES * (*func_mysqlnd_conn__store_result)(MYSQLND * const conn, uint flags TSRMLS_DC); typedef enum_func_status (*func_mysqlnd_conn__next_result)(MYSQLND * const conn TSRMLS_DC); typedef zend_bool (*func_mysqlnd_conn__more_results)(const MYSQLND * const conn TSRMLS_DC); @@ -508,8 +508,8 @@ typedef mysqlnd_fetch_row_func fetch_row_normal_buffered; /* private */ typedef mysqlnd_fetch_row_func fetch_row_normal_unbuffered; /* private */ -typedef MYSQLND_RES * (*func_mysqlnd_res__use_result)(MYSQLND_RES * const result, zend_bool ps_protocol TSRMLS_DC); -typedef MYSQLND_RES * (*func_mysqlnd_res__store_result)(MYSQLND_RES * result, MYSQLND * const conn, zend_bool ps TSRMLS_DC); +typedef MYSQLND_RES * (*func_mysqlnd_res__use_result)(MYSQLND_RES * const result, uint flags TSRMLS_DC); +typedef MYSQLND_RES * (*func_mysqlnd_res__store_result)(MYSQLND_RES * result, MYSQLND * const conn, uint flags TSRMLS_DC); typedef void (*func_mysqlnd_res__fetch_into)(MYSQLND_RES *result, unsigned int flags, zval *return_value, enum_mysqlnd_extension ext TSRMLS_DC ZEND_FILE_LINE_DC); typedef MYSQLND_ROW_C (*func_mysqlnd_res__fetch_row_c)(MYSQLND_RES *result TSRMLS_DC); typedef void (*func_mysqlnd_res__fetch_all)(MYSQLND_RES *result, unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); @@ -581,11 +581,11 @@ func_mysqlnd_res__result_meta_init result_meta_init; + mysqlnd_fetch_row_func fetch_row_normal_unbuffered_json; /* private */ void * unused1; void * unused2; void * unused3; void * unused4; - void * unused5; }; @@ -611,9 +611,9 @@ typedef enum_func_status (*func_mysqlnd_stmt__prepare)(MYSQLND_STMT * const stmt, const char * const query, unsigned int query_len TSRMLS_DC); typedef enum_func_status (*func_mysqlnd_stmt__execute)(MYSQLND_STMT * const stmt TSRMLS_DC); -typedef MYSQLND_RES * (*func_mysqlnd_stmt__use_result)(MYSQLND_STMT * const stmt TSRMLS_DC); -typedef MYSQLND_RES * (*func_mysqlnd_stmt__store_result)(MYSQLND_STMT * const stmt TSRMLS_DC); -typedef MYSQLND_RES * (*func_mysqlnd_stmt__get_result)(MYSQLND_STMT * const stmt TSRMLS_DC); +typedef MYSQLND_RES * (*func_mysqlnd_stmt__use_result)(MYSQLND_STMT * const stmt, uint flags TSRMLS_DC); +typedef MYSQLND_RES * (*func_mysqlnd_stmt__store_result)(MYSQLND_STMT * const stmt, uint flags TSRMLS_DC); +typedef MYSQLND_RES * (*func_mysqlnd_stmt__get_result)(MYSQLND_STMT * const stmt, uint flags TSRMLS_DC); typedef zend_bool (*func_mysqlnd_stmt__more_results)(const MYSQLND_STMT * const stmt TSRMLS_DC); typedef enum_func_status (*func_mysqlnd_stmt__next_result)(MYSQLND_STMT * const stmt TSRMLS_DC); typedef enum_func_status (*func_mysqlnd_stmt__free_result)(MYSQLND_STMT * const stmt TSRMLS_DC); Index: ext/mysqlnd/mysqlnd.c =================================================================== --- ext/mysqlnd/mysqlnd.c (revision 316335) +++ ext/mysqlnd/mysqlnd.c (working copy) @@ -925,7 +925,7 @@ goto err; } if (conn->last_query_type == QUERY_SELECT) { - MYSQLND_RES * result = conn->m->use_result(conn TSRMLS_CC); + MYSQLND_RES * result = conn->m->use_result(conn, MYSQLND_RESULT_FLAG_ARRAY TSRMLS_CC); if (result) { result->m.free_result(result, TRUE TSRMLS_CC); } @@ -1346,7 +1346,7 @@ } if (PASS == conn->m->query(conn, show_query, show_query_len TSRMLS_CC)) { - result = conn->m->store_result(conn TSRMLS_CC); + result = conn->m->store_result(conn, MYSQLND_RESULT_FLAG_ARRAY TSRMLS_CC); } if (show_query != query) { mnd_sprintf_free(show_query); @@ -2280,12 +2280,12 @@ /* {{{ mysqlnd_conn::use_result */ static MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_conn, use_result)(MYSQLND * const conn TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, use_result)(MYSQLND * const conn, uint flags TSRMLS_DC) { MYSQLND_RES * result; DBG_ENTER("mysqlnd_conn::use_result"); - DBG_INF_FMT("conn=%llu", conn->thread_id); + DBG_INF_FMT("conn=%llu flags=%u", conn->thread_id, flags); if (!conn->current_result) { DBG_RETURN(NULL); @@ -2301,7 +2301,7 @@ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_UNBUFFERED_SETS); conn->current_result->conn = conn->m->get_reference(conn TSRMLS_CC); - result = conn->current_result->m.use_result(conn->current_result, FALSE TSRMLS_CC); + result = conn->current_result->m.use_result(conn->current_result, flags & ~MYSQLND_RESULT_FLAG_PS TSRMLS_CC); if (!result) { conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC); @@ -2315,12 +2315,12 @@ /* {{{ mysqlnd_conn::store_result */ static MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_conn, store_result)(MYSQLND * const conn TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, store_result)(MYSQLND * const conn, uint flags TSRMLS_DC) { MYSQLND_RES *result; DBG_ENTER("mysqlnd_conn::store_result"); - DBG_INF_FMT("conn=%llu", conn->thread_id); + DBG_INF_FMT("conn=%llu flags=%u", conn->thread_id, flags); if (!conn->current_result) { DBG_RETURN(NULL); @@ -2335,7 +2335,7 @@ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS); - result = conn->current_result->m.store_result(conn->current_result, conn, FALSE TSRMLS_CC); + result = conn->current_result->m.store_result(conn->current_result, conn, (flags & ~MYSQLND_RESULT_FLAG_PS) TSRMLS_CC); if (!result) { conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC); } Index: ext/mysqlnd/mysqlnd_ps.c =================================================================== --- ext/mysqlnd/mysqlnd_ps.c (revision 316335) +++ ext/mysqlnd/mysqlnd_ps.c (working copy) @@ -70,7 +70,7 @@ /* {{{ mysqlnd_stmt::store_result */ static MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s, uint flags TSRMLS_DC) { MYSQLND_STMT_DATA * stmt = s? s->data:NULL; enum_func_status ret; @@ -92,7 +92,9 @@ if (stmt->cursor_exists) { /* Silently convert buffered to unbuffered, for now */ - DBG_RETURN(s->m->use_result(s TSRMLS_CC)); + result = s->m->use_result(s, flags | MYSQLND_RESULT_FLAG_PS TSRMLS_CC); + result->m.fetch_row = mysqlnd_fetch_stmt_row_cursor; + DBG_RETURN(result); } /* Nothing to store for UPSERT/LOAD DATA*/ @@ -140,7 +142,7 @@ /* {{{ mysqlnd_stmt::get_result */ static MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s, uint flags TSRMLS_DC) { MYSQLND_STMT_DATA * stmt = s? s->data:NULL; MYSQLND * conn; @@ -161,7 +163,7 @@ if (stmt->cursor_exists) { /* Silently convert buffered to unbuffered, for now */ - DBG_RETURN(s->m->use_result(s TSRMLS_CC)); + DBG_RETURN(s->m->use_result(s, flags | MYSQLND_RESULT_FLAG_PS TSRMLS_CC)); } /* Nothing to store for UPSERT/LOAD DATA*/ @@ -188,7 +190,7 @@ break; } - if ((result = result->m.store_result(result, conn, TRUE TSRMLS_CC))) { + if ((result = result->m.store_result(result, conn, MYSQLND_RESULT_FLAG_PS TSRMLS_CC))) { stmt->upsert_status.affected_rows = result->stored_data->row_count; stmt->state = MYSQLND_STMT_PREPARED; result->type = MYSQLND_RES_PS_BUF; @@ -398,7 +400,7 @@ if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { /* Do implicit use_result and then flush the result */ stmt->default_rset_handler = s->m->use_result; - stmt->default_rset_handler(s TSRMLS_CC); + stmt->default_rset_handler(s, MYSQLND_RESULT_FLAG_ARRAY TSRMLS_CC); } /* No 'else' here please :) */ if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE && stmt->result) { @@ -950,7 +952,7 @@ /* {{{ mysqlnd_stmt::use_result */ static MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s, uint flags TSRMLS_DC) { MYSQLND_STMT_DATA * stmt = s? s->data:NULL; MYSQLND_RES *result; @@ -980,7 +982,7 @@ MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_PS_UNBUFFERED_SETS); result = stmt->result; - result->m.use_result(stmt->result, TRUE TSRMLS_CC); + result->m.use_result(stmt->result, flags | MYSQLND_RESULT_FLAG_PS TSRMLS_CC); result->m.fetch_row = stmt->cursor_exists? mysqlnd_fetch_stmt_row_cursor: mysqlnd_stmt_fetch_row_unbuffered; stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED; @@ -1160,7 +1162,7 @@ } else if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { /* Execute only once. We have to free the previous contents of user's bound vars */ - stmt->default_rset_handler(s TSRMLS_CC); + stmt->default_rset_handler(s, 0 TSRMLS_CC); } stmt->state = MYSQLND_STMT_USER_FETCHING; @@ -1270,7 +1272,7 @@ do { if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { DBG_INF("fetching result set header"); - stmt->default_rset_handler(s TSRMLS_CC); + stmt->default_rset_handler(s, 0 TSRMLS_CC); stmt->state = MYSQLND_STMT_USER_FETCHING; } @@ -1957,7 +1959,7 @@ DBG_INF("fetching result set header"); /* Do implicit use_result and then flush the result */ stmt->default_rset_handler = s->m->use_result; - stmt->default_rset_handler(s TSRMLS_CC); + stmt->default_rset_handler(s, 0 TSRMLS_CC); } if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) { @@ -2174,7 +2176,7 @@ do { if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { DBG_INF("fetching result set header"); - stmt->default_rset_handler(s TSRMLS_CC); + stmt->default_rset_handler(s, 0 TSRMLS_CC); stmt->state = MYSQLND_STMT_USER_FETCHING; } Index: ext/mysqlnd/mysqlnd.h =================================================================== --- ext/mysqlnd/mysqlnd.h (revision 316335) +++ ext/mysqlnd/mysqlnd.h (working copy) @@ -142,8 +142,8 @@ PHPAPI enum_func_status _mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long sec, long usec, uint * desc_num TSRMLS_DC); -#define mysqlnd_use_result(conn) (conn)->m->use_result((conn) TSRMLS_CC) -#define mysqlnd_store_result(conn) (conn)->m->store_result((conn) TSRMLS_CC) +#define mysqlnd_use_result(conn, f) (conn)->m->use_result((conn), (f) TSRMLS_CC) +#define mysqlnd_store_result(conn, f) (conn)->m->store_result((conn), (f) TSRMLS_CC) #define mysqlnd_next_result(conn) (conn)->m->next_result((conn) TSRMLS_CC) #define mysqlnd_more_results(conn) (conn)->m->more_results((conn) TSRMLS_CC) #define mysqlnd_free_result(r,e_or_i) ((MYSQLND_RES*)r)->m.free_result(((MYSQLND_RES*)(r)), (e_or_i) TSRMLS_CC) @@ -242,8 +242,8 @@ /* PS */ #define mysqlnd_stmt_init(conn) (conn)->m->stmt_init((conn) TSRMLS_CC) -#define mysqlnd_stmt_store_result(stmt) (!mysqlnd_stmt_field_count((stmt)) ? PASS:((stmt)->m->store_result((stmt) TSRMLS_CC)? PASS:FAIL)) -#define mysqlnd_stmt_get_result(stmt) (stmt)->m->get_result((stmt) TSRMLS_CC) +#define mysqlnd_stmt_store_result(stmt) (!mysqlnd_stmt_field_count((stmt)) ? PASS:((stmt)->m->store_result((stmt), 0 TSRMLS_CC)? PASS:FAIL)) +#define mysqlnd_stmt_get_result(stmt, f) (stmt)->m->get_result((stmt), (f) TSRMLS_CC) #define mysqlnd_stmt_more_results(stmt) (stmt)->m->more_results((stmt) TSRMLS_CC) #define mysqlnd_stmt_next_result(stmt) (stmt)->m->next_result((stmt) TSRMLS_CC) #define mysqlnd_stmt_data_seek(stmt, row) (stmt)->m->seek_data((stmt), (row) TSRMLS_CC) Index: ext/mysqlnd/mysqlnd_enum_n_def.h =================================================================== --- ext/mysqlnd/mysqlnd_enum_n_def.h (revision 316335) +++ ext/mysqlnd/mysqlnd_enum_n_def.h (working copy) @@ -104,6 +104,10 @@ #define MYSQLND_NET_FLAG_USE_COMPRESSION 1 +#define MYSQLND_RESULT_FLAG_PS 1 +#define MYSQLND_RESULT_FLAG_ARRAY 2 +#define MYSQLND_RESULT_FLAG_JSON 4 + typedef enum mysqlnd_extension { MYSQLND_MYSQL = 0, Index: ext/mysqlnd/mysqlnd_result.c =================================================================== --- ext/mysqlnd/mysqlnd_result.c (revision 316335) +++ ext/mysqlnd/mysqlnd_result.c (working copy) @@ -28,6 +28,8 @@ #include "mysqlnd_result_meta.h" #include "mysqlnd_statistics.h" #include "mysqlnd_debug.h" +#include "ext/standard/php_smart_str.h" +#include "ext/json/php_json.h" #define MYSQLND_SILENT @@ -773,11 +775,10 @@ MYSQLND_INC_CONN_STATISTIC(result->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF); if (!row_packet->skip_extraction) { - HashTable *row_ht = Z_ARRVAL_P(row); - MYSQLND_FIELD *field = result->meta->fields; + MYSQLND_FIELD * field = result->meta->fields; struct mysqlnd_field_hash_key * hash_key = result->meta->zend_hash_keys; unsigned int i, field_count = result->field_count; - unsigned long *lengths = result->lengths; + unsigned long * lengths = result->lengths; enum_func_status rc = result->m.row_decoder(result->unbuf->last_row_buffer, result->unbuf->last_row_data, @@ -790,7 +791,8 @@ DBG_RETURN(FAIL); } for (i = 0; i < field_count; i++, field++, hash_key++) { - zval *data = result->unbuf->last_row_data[i]; + HashTable * row_ht = Z_ARRVAL_P(row); + zval * data = result->unbuf->last_row_data[i]; unsigned int len = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data); if (lengths) { @@ -812,20 +814,20 @@ Z_ADDREF_P(data); if (hash_key->is_numeric == FALSE) { #if MYSQLND_UNICODE - zend_u_hash_quick_update(Z_ARRVAL_P(row), IS_UNICODE, + zend_u_hash_quick_update(row_ht, IS_UNICODE, hash_key->ustr, hash_key->ulen + 1, hash_key->key, (void *) &data, sizeof(zval *), NULL); #else - zend_hash_quick_update(Z_ARRVAL_P(row), + zend_hash_quick_update(row_ht, field->name, field->name_length + 1, hash_key->key, (void *) &data, sizeof(zval *), NULL); #endif } else { - zend_hash_index_update(Z_ARRVAL_P(row), + zend_hash_index_update(row_ht, hash_key->key, (void *) &data, sizeof(zval *), NULL); } @@ -868,18 +870,149 @@ /* }}} */ +/* {{{ mysqlnd_fetch_row_unbuffered_json */ +static enum_func_status +mysqlnd_fetch_row_unbuffered_json(MYSQLND_RES * result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) +{ + enum_func_status ret; + zval *row = (zval *) param; + MYSQLND_PACKET_ROW *row_packet = result->row_packet; + + DBG_ENTER("mysqlnd_fetch_row_unbuffered_json"); + + *fetched_anything = FALSE; + if (result->unbuf->eof_reached) { + /* No more rows obviously */ + DBG_RETURN(PASS); + } + if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) { + SET_CLIENT_ERROR(result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); + DBG_RETURN(FAIL); + } + if (!row_packet) { + /* Not fully initialized object that is being cleaned up */ + DBG_RETURN(FAIL); + } + /* Let the row packet fill our buffer and skip additional mnd_malloc + memcpy */ + row_packet->skip_extraction = row? FALSE:TRUE; + + /* + If we skip rows (row == NULL) we have to + result->m.unbuffered_free_last_data() before it. The function returns always true. + */ + if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) { + result->m.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); + + if (!row_packet->skip_extraction) { + MYSQLND_FIELD *field = result->meta->fields; + struct mysqlnd_field_hash_key * hash_key = result->meta->zend_hash_keys; + unsigned int i, field_count = result->field_count; + unsigned long *lengths = result->lengths; + smart_str retval = {0}; + + enum_func_status rc = result->m.row_decoder(result->unbuf->last_row_buffer, + result->unbuf->last_row_data, + field_count, + row_packet->fields_metadata, + result->conn->options.numeric_and_datetime_as_unicode, + result->conn->options.int_and_float_native, + result->conn->stats TSRMLS_CC); + if (PASS != rc) { + DBG_RETURN(FAIL); + } + smart_str_appendc(&retval, flags == MYSQLND_FETCH_NUM? '[':'{'); + + for (i = 0; i < field_count; i++, field++, hash_key++) { + zval * data = result->unbuf->last_row_data[i]; + unsigned int len = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data); + + if (lengths) { + lengths[i] = len; + } + + if (flags & MYSQLND_FETCH_NUM) { + php_json_encode(&retval, data, PHP_JSON_BIGINT_AS_STRING TSRMLS_CC); + smart_str_appendc(&retval, ','); + } + if (flags & MYSQLND_FETCH_ASSOC) { + /* why is json_escape_string() static ? */ + zval tmp; + INIT_ZVAL(tmp); + ZVAL_STRINGL(&tmp, field->name, field->name_length, 0); + php_json_encode(&retval, &tmp, PHP_JSON_BIGINT_AS_STRING TSRMLS_CC); + + smart_str_appendc(&retval, ':'); + php_json_encode(&retval, data, PHP_JSON_BIGINT_AS_STRING TSRMLS_CC); + smart_str_appendc(&retval, ','); + } + if (field->max_length < len) { + field->max_length = len; + } + } + if (',' == retval.c[retval.len - 1]) { + retval.c[retval.len - 1] = (flags == MYSQLND_FETCH_NUM? ']':'}'); + } else { + smart_str_appendc(&retval, flags == MYSQLND_FETCH_NUM? ']':'}'); + } + smart_str_appendc(&retval, '\0'); + zval_dtor(row); + ZVAL_STRINGL(row, retval.c, retval.len - 1, 0); + } + *fetched_anything = TRUE; + result->unbuf->row_count++; + } else if (ret == FAIL) { + if (row_packet->error_info.error_no) { + COPY_CLIENT_ERROR(result->conn->error_info, row_packet->error_info); + DBG_ERR_FMT("errorno=%u error=%s", row_packet->error_info.error_no, row_packet->error_info.error); + } + CONN_SET_STATE(result->conn, CONN_READY); + result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */ + } else if (row_packet->eof) { + /* Mark the connection as usable again */ + DBG_INF_FMT("warnings=%u server_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) { + CONN_SET_STATE(result->conn, CONN_NEXT_RESULT_PENDING); + } else { + CONN_SET_STATE(result->conn, CONN_READY); + } + result->m.unbuffered_free_last_data(result TSRMLS_CC); + } + + DBG_INF_FMT("ret=%s fetched=%u", ret == PASS? "PASS":"FAIL", *fetched_anything); + DBG_RETURN(PASS); +} +/* }}} */ + + /* {{{ mysqlnd_res::use_result */ static MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, uint flags TSRMLS_DC) { DBG_ENTER("mysqlnd_res::use_result"); + DBG_INF_FMT("result=%p flags=%u", result, flags); SET_EMPTY_ERROR(result->conn->error_info); - - if (ps == FALSE) { + if (!(flags & MYSQLND_RESULT_FLAG_PS)) { result->type = MYSQLND_RES_NORMAL; - result->m.fetch_row = result->m.fetch_row_normal_unbuffered; + result->m.fetch_row = (flags & MYSQLND_RESULT_FLAG_JSON)? + result->m.fetch_row_normal_unbuffered_json: + result->m.fetch_row_normal_unbuffered; + result->m.fetch_lengths = mysqlnd_fetch_lengths_unbuffered; result->m.row_decoder = php_mysqlnd_rowp_read_text_protocol; result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long)); @@ -913,7 +1046,7 @@ } result->row_packet->result_set_memory_pool = result->result_set_memory_pool; result->row_packet->field_count = result->field_count; - result->row_packet->binary_protocol = ps; + result->row_packet->binary_protocol = (flags & MYSQLND_RESULT_FLAG_PS)? TRUE:FALSE; 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; @@ -1103,7 +1236,7 @@ /* {{{ mysqlnd_res::store_result_fetch_data */ enum_func_status MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND * const conn, MYSQLND_RES * result, - MYSQLND_RES_METADATA *meta, + MYSQLND_RES_METADATA * meta, zend_bool binary_protocol TSRMLS_DC) { enum_func_status ret; @@ -1249,7 +1382,7 @@ static MYSQLND_RES * MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result, MYSQLND * const conn, - zend_bool ps_protocol TSRMLS_DC) + uint flags TSRMLS_DC) { enum_func_status ret; @@ -1260,7 +1393,7 @@ result->type = MYSQLND_RES_NORMAL; result->m.fetch_row = result->m.fetch_row_normal_buffered; result->m.fetch_lengths = mysqlnd_fetch_lengths_buffered; - result->m.row_decoder = ps_protocol? php_mysqlnd_rowp_read_binary_protocol: + result->m.row_decoder = (flags & MYSQLND_RESULT_FLAG_PS)? php_mysqlnd_rowp_read_binary_protocol: php_mysqlnd_rowp_read_text_protocol; result->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC); @@ -1273,7 +1406,7 @@ CONN_SET_STATE(conn, CONN_FETCHING_DATA); - ret = result->m.store_result_fetch_data(conn, result, result->meta, ps_protocol TSRMLS_CC); + ret = result->m.store_result_fetch_data(conn, result, result->meta, (flags & MYSQLND_RESULT_FLAG_PS)? TRUE:FALSE TSRMLS_CC); if (FAIL == ret) { if (result->stored_data) { COPY_CLIENT_ERROR(conn->error_info, result->stored_data->error_info); @@ -1578,7 +1711,7 @@ do { MAKE_STD_ZVAL(row); mysqlnd_fetch_into(result, flags, row, MYSQLND_MYSQLI); - if (Z_TYPE_P(row) != IS_ARRAY) { + if (Z_TYPE_P(row) == IS_NULL || Z_TYPE_P(row) == IS_BOOL) { zval_ptr_dtor(&row); break; } @@ -1667,7 +1800,12 @@ MYSQLND_METHOD(mysqlnd_res, unbuffered_free_last_data), NULL /* row_decoder */, - mysqlnd_result_meta_init + mysqlnd_result_meta_init, + mysqlnd_fetch_row_unbuffered_json, + NULL, + NULL, + NULL, + NULL MYSQLND_CLASS_METHODS_END; Index: ext/mysqli/mysqli_warning.c =================================================================== --- ext/mysqli/mysqli_warning.c (revision 316335) +++ ext/mysqli/mysqli_warning.c (working copy) @@ -133,8 +133,11 @@ return NULL; } +#ifndef MYSQL_USE_MYSQLND result = mysql_use_result(mysql); - +#else + result = mysqlnd_use_result(mysql, MYSQLND_RESULT_FLAG_ARRAY); +#endif for (;;) { zval **entry; int errno; Index: ext/mysqli/mysqli_api.c =================================================================== --- ext/mysqli/mysqli_api.c (revision 316335) +++ ext/mysqli/mysqli_api.c (working copy) @@ -2457,7 +2457,7 @@ } #endif - if (mysql_stmt_store_result(stmt->stmt)){ + if (mysql_stmt_store_result(stmt->stmt)) { MYSQLI_REPORT_STMT_ERROR(stmt->stmt); RETURN_FALSE; } @@ -2495,7 +2495,11 @@ } MYSQLI_FETCH_RESOURCE_CONN(mysql, &mysql_link, MYSQLI_STATUS_VALID); +#ifndef MYSQL_USE_MYSQLND if (!(result = mysql_store_result(mysql->mysql))) { +#else + if (!(result = mysqlnd_store_result(mysql->mysql, MYSQLND_RESULT_FLAG_ARRAY))) { +#endif MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql); RETURN_FALSE; } @@ -2549,7 +2553,11 @@ } MYSQLI_FETCH_RESOURCE_CONN(mysql, &mysql_link, MYSQLI_STATUS_VALID); +#ifndef MYSQL_USE_MYSQLND if (!(result = mysql_use_result(mysql->mysql))) { +#else + if (!(result = mysqlnd_use_result(mysql->mysql, MYSQLND_RESULT_FLAG_ARRAY))) { +#endif MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql); RETURN_FALSE; } Index: ext/mysqli/mysqli.c =================================================================== --- ext/mysqli/mysqli.c (revision 316335) +++ ext/mysqli/mysqli.c (working copy) @@ -711,6 +711,7 @@ REGISTER_LONG_CONSTANT("MYSQLI_USE_RESULT", MYSQLI_USE_RESULT, CONST_CS | CONST_PERSISTENT); #if defined (MYSQLI_USE_MYSQLND) REGISTER_LONG_CONSTANT("MYSQLI_ASYNC", MYSQLI_ASYNC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("MYSQLI_JSON_RESULT", MYSQLI_JSON_RESULT, CONST_CS | CONST_PERSISTENT); #endif /* for mysqli_fetch_assoc */ @@ -1092,10 +1093,18 @@ switch (resmode) { case MYSQLI_STORE_RESULT: +#ifndef MYSQLI_USE_MYSQLND result = mysql_store_result(mysql->mysql); +#else + result = mysqlnd_store_result(mysql->mysql, MYSQLND_RESULT_FLAG_ARRAY); +#endif break; case MYSQLI_USE_RESULT: +#ifndef MYSQLI_USE_MYSQLND result = mysql_use_result(mysql->mysql); +#else + result = mysqlnd_use_result(mysql->mysql, MYSQLND_RESULT_FLAG_ARRAY); +#endif break; default: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid value for resultmode"); Index: ext/mysqli/mysqli_nonapi.c =================================================================== --- ext/mysqli/mysqli_nonapi.c (revision 316335) +++ ext/mysqli/mysqli_nonapi.c (working copy) @@ -568,6 +568,7 @@ char *query = NULL; int query_len; long resultmode = MYSQLI_STORE_RESULT; + long real_result_mode = 0; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l", &mysql_link, mysqli_link_class_entry, &query, &query_len, &resultmode) == FAILURE) { return; @@ -577,7 +578,8 @@ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty query"); RETURN_FALSE; } - if ((resultmode & ~MYSQLI_ASYNC) != MYSQLI_USE_RESULT && (resultmode & ~MYSQLI_ASYNC) != MYSQLI_STORE_RESULT) { + real_result_mode = (resultmode & ~(MYSQLI_ASYNC | MYSQLI_JSON_RESULT)); + if (real_result_mode != MYSQLI_USE_RESULT && real_result_mode != MYSQLI_STORE_RESULT) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid value for resultmode"); RETURN_FALSE; } @@ -586,7 +588,6 @@ MYSQLI_DISABLE_MQ; - #ifdef MYSQLI_USE_MYSQLND if (resultmode & MYSQLI_ASYNC) { if (mysqli_async_query(mysql->mysql, query, query_len)) { @@ -611,12 +612,25 @@ RETURN_TRUE; } - switch (resultmode) { + switch (real_result_mode) { case MYSQLI_STORE_RESULT: +#ifdef MYSQLI_USE_MYSQLND + result = mysqlnd_store_result(mysql->mysql, (resultmode & MYSQLI_JSON_RESULT)? + MYSQLND_RESULT_FLAG_JSON: + MYSQLND_RESULT_FLAG_ARRAY); +#else result = mysql_store_result(mysql->mysql); +#endif break; case MYSQLI_USE_RESULT: +#ifdef MYSQLI_USE_MYSQLND + result = mysqlnd_use_result(mysql->mysql, (resultmode & MYSQLI_JSON_RESULT)? + MYSQLND_RESULT_FLAG_JSON: + MYSQLND_RESULT_FLAG_ARRAY + ); +#else result = mysql_use_result(mysql->mysql); +#endif break; } if (!result) { @@ -844,13 +858,23 @@ RETURN_TRUE; } - switch (mysql->async_result_fetch_type) { - case MYSQLI_STORE_RESULT: - result = mysql_store_result(mysql->mysql); - break; - case MYSQLI_USE_RESULT: - result = mysql_use_result(mysql->mysql); - break; + if (mysql->async_result_fetch_type & MYSQLI_STORE_RESULT) { +#ifdef MYSQLI_USE_MYSQLND + result = mysqlnd_store_result(mysql->mysql, (mysql->async_result_fetch_type & MYSQLI_JSON_RESULT)? + MYSQLND_RESULT_FLAG_JSON: + MYSQLND_RESULT_FLAG_ARRAY); +#else + result = mysql_store_result(mysql->mysql); +#endif + } else if (mysql->async_result_fetch_type & MYSQLI_USE_RESULT) { +#ifdef MYSQLI_USE_MYSQLND + result = mysqlnd_use_result(mysql->mysql, (mysql->async_result_fetch_type & MYSQLI_JSON_RESULT)? + MYSQLND_RESULT_FLAG_JSON: + MYSQLND_RESULT_FLAG_ARRAY + ); +#else + result = mysql_use_result(mysql->mysql); +#endif } if (!result) { @@ -885,7 +909,7 @@ } MYSQLI_FETCH_RESOURCE_STMT(stmt, &mysql_stmt, MYSQLI_STATUS_VALID); - if (!(result = mysqlnd_stmt_get_result(stmt->stmt))) { + if (!(result = mysqlnd_stmt_get_result(stmt->stmt, MYSQLND_RESULT_FLAG_ARRAY))) { MYSQLI_REPORT_STMT_ERROR(stmt->stmt); RETURN_FALSE; } Index: ext/mysqli/mysqli_priv.h =================================================================== --- ext/mysqli/mysqli_priv.h (revision 316335) +++ ext/mysqli/mysqli_priv.h (working copy) @@ -114,6 +114,12 @@ /* libmysql */ #define MYSQLI_ASYNC 0 #endif +#ifdef MYSQLI_USE_MYSQLND +#define MYSQLI_JSON_RESULT 16 +#else +/* libmysql */ +#define MYSQLI_JSON_RESULT 0 +#endif /* for mysqli_fetch_assoc */ #define MYSQLI_ASSOC 1 Index: ext/mysql/php_mysql.c =================================================================== --- ext/mysql/php_mysql.c (revision 316335) +++ ext/mysql/php_mysql.c (working copy) @@ -1506,9 +1506,17 @@ } #endif if(use_store == MYSQL_USE_RESULT) { - mysql_result=mysql_use_result(mysql->conn); +#ifndef MYSQL_USE_MYSQLND + mysql_result = mysql_use_result(mysql->conn); +#else + mysql_result = mysqlnd_use_result(mysql->conn, MYSQLND_RESULT_FLAG_ARRAY); +#endif } else { - mysql_result=mysql_store_result(mysql->conn); +#ifndef MYSQL_USE_MYSQLND + mysql_result = mysql_store_result(mysql->conn); +#else + mysql_result = mysqlnd_store_result(mysql->conn, MYSQLND_RESULT_FLAG_ARRAY); +#endif } if (!mysql_result) { if (PHP_MYSQL_VALID_RESULT(mysql->conn)) { /* query should have returned rows */