Back to index

php5  5.3.10
Defines | Functions | Variables
mysqlnd_result.c File Reference
#include "php.h"
#include "mysqlnd.h"
#include "mysqlnd_wireprotocol.h"
#include "mysqlnd_block_alloc.h"
#include "mysqlnd_priv.h"
#include "mysqlnd_result.h"
#include "mysqlnd_result_meta.h"
#include "mysqlnd_statistics.h"
#include "mysqlnd_debug.h"
#include "ext/standard/php_string.h"

Go to the source code of this file.

Defines

#define MYSQLND_SILENT
#define STORE_RESULT_PREALLOCATED_SET_IF_NOT_EMPTY   2

Functions

static enum_func_status MYSQLND_METHOD (mysqlnd_res, initialize_result_set_rest)
static void mysqlnd_rset_zval_ptr_dtor (zval **zv, enum_mysqlnd_res_type type, zend_bool *copy_ctor_called TSRMLS_DC)
static void MYSQLND_METHOD (mysqlnd_res, unbuffered_free_last_data)
static void MYSQLND_METHOD (mysqlnd_res, free_buffered_data)
static void MYSQLND_METHOD (mysqlnd_res, free_result_buffers)
static void mysqlnd_internal_free_result_contents (MYSQLND_RES *result TSRMLS_DC)
static void mysqlnd_internal_free_result (MYSQLND_RES *result TSRMLS_DC)
static enum_func_status MYSQLND_METHOD (mysqlnd_res, read_result_metadata)
enum_func_status mysqlnd_query_read_result_set_header (MYSQLND *conn, MYSQLND_STMT *s TSRMLS_DC)
static unsigned long * mysqlnd_fetch_lengths_buffered (MYSQLND_RES *const result TSRMLS_DC)
static unsigned long * mysqlnd_fetch_lengths_unbuffered (MYSQLND_RES *const result TSRMLS_DC)
PHPAPI unsigned long * _mysqlnd_fetch_lengths (MYSQLND_RES *const result TSRMLS_DC)
static MYSQLND_ROW_C mysqlnd_fetch_row_unbuffered_c (MYSQLND_RES *result TSRMLS_DC)
static enum_func_status mysqlnd_fetch_row_unbuffered (MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
static MYSQLND_RES * MYSQLND_METHOD (mysqlnd_res, use_result)
static MYSQLND_ROW_C mysqlnd_fetch_row_buffered_c (MYSQLND_RES *result TSRMLS_DC)
static enum_func_status mysqlnd_fetch_row_buffered (MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
enum_func_status MYSQLND_METHOD (mysqlnd_res, store_result_fetch_data)
static MYSQLND_RES * MYSQLND_METHOD (mysqlnd_res, store_result)
static enum_func_status MYSQLND_METHOD (mysqlnd_res, skip_result)
static enum_func_status MYSQLND_METHOD (mysqlnd_res, free_result)
static enum_func_status MYSQLND_METHOD (mysqlnd_res, data_seek)
static uint64_t MYSQLND_METHOD (mysqlnd_res, num_rows) const
static unsigned int MYSQLND_METHOD (mysqlnd_res, num_fields) const
static const MYSQLND_FIELDMYSQLND_METHOD (mysqlnd_res, fetch_field)
static const MYSQLND_FIELDMYSQLND_METHOD (mysqlnd_res, fetch_field_direct)
static const MYSQLND_FIELDMYSQLND_METHOD (mysqlnd_res, fetch_fields)
static MYSQLND_FIELD_OFFSET MYSQLND_METHOD (mysqlnd_res, field_seek)
static MYSQLND_FIELD_OFFSET MYSQLND_METHOD (mysqlnd_res, field_tell) const
static void MYSQLND_METHOD (mysqlnd_res, fetch_into)
static MYSQLND_ROW_C MYSQLND_METHOD (mysqlnd_res, fetch_row_c)
static void MYSQLND_METHOD (mysqlnd_res, fetch_all)
static void MYSQLND_METHOD (mysqlnd_res, fetch_field_data)
PHPAPI MYSQLND_RES * mysqlnd_result_init (unsigned int field_count, zend_bool persistent TSRMLS_DC)
PHPAPI void ** _mysqlnd_plugin_get_plugin_result_data (const MYSQLND_RES *result, unsigned int plugin_id TSRMLS_DC)
PHPAPI struct
st_mysqlnd_res_methods
mysqlnd_result_get_methods ()

Variables

static NULL
static mysqlnd_fetch_row_buffered
static mysqlnd_fetch_row_unbuffered
static mysqlnd_internal_free_result
static mysqlnd_internal_free_result_contents
static mysqlnd_result_meta_init MYSQLND_CLASS_METHODS_END

Define Documentation

#define MYSQLND_SILENT

Definition at line 32 of file mysqlnd_result.c.

Definition at line 1123 of file mysqlnd_result.c.


Function Documentation

PHPAPI unsigned long* _mysqlnd_fetch_lengths ( MYSQLND_RES *const result  TSRMLS_DC)

Definition at line 626 of file mysqlnd_result.c.

{
       return result->m.fetch_lengths? result->m.fetch_lengths(result TSRMLS_CC) : NULL;
}
PHPAPI void** _mysqlnd_plugin_get_plugin_result_data ( const MYSQLND_RES *  result,
unsigned int plugin_id  TSRMLS_DC 
)

Definition at line 1736 of file mysqlnd_result.c.

{
       DBG_ENTER("_mysqlnd_plugin_get_plugin_result_data");
       DBG_INF_FMT("plugin_id=%u", plugin_id);
       if (!result || plugin_id >= mysqlnd_plugin_count()) {
              return NULL;
       }
       DBG_RETURN((void *)((char *)result + sizeof(MYSQLND_RES) + plugin_id * sizeof(void *)));
}

Here is the call graph for this function:

static unsigned long* mysqlnd_fetch_lengths_buffered ( MYSQLND_RES *const result  TSRMLS_DC) [static]

Definition at line 586 of file mysqlnd_result.c.

{
       unsigned int i;
       zval **previous_row;
       MYSQLND_RES_BUFFERED *set = result->stored_data;

       /*
         If:
         - unbuffered result
         - first row has not been read
         - last_row has been read
       */
       if (set->data_cursor == NULL ||
              set->data_cursor == set->data ||
              ((set->data_cursor - set->data) > (set->row_count * result->meta->field_count) ))
       {
              return NULL;/* No rows or no more rows */
       }

       previous_row = set->data_cursor - result->meta->field_count;
       for (i = 0; i < result->meta->field_count; i++) {
              result->lengths[i] = (Z_TYPE_P(previous_row[i]) == IS_NULL)? 0:Z_STRLEN_P(previous_row[i]);
       }

       return result->lengths;
}

Here is the caller graph for this function:

static unsigned long* mysqlnd_fetch_lengths_unbuffered ( MYSQLND_RES *const result  TSRMLS_DC) [static]

Definition at line 617 of file mysqlnd_result.c.

{
       /* simulate output of libmysql */
       return (!result->unbuf || result->unbuf->last_row_data || result->unbuf->eof_reached)? result->lengths:NULL;
}

Here is the caller graph for this function:

static enum_func_status mysqlnd_fetch_row_buffered ( MYSQLND_RES *  result,
void *  param,
unsigned int  flags,
zend_bool *fetched_anything  TSRMLS_DC 
) [static]

Definition at line 1023 of file mysqlnd_result.c.

{
       unsigned int i;
       zval *row = (zval *) param;
       MYSQLND_RES_BUFFERED *set = result->stored_data;
       enum_func_status ret = FAIL;

       DBG_ENTER("mysqlnd_fetch_row_buffered");
       DBG_INF_FMT("flags=%u row=%p", flags, row);

       /* If we haven't read everything */
       if (set->data_cursor &&
              (set->data_cursor - set->data) < (set->row_count * result->meta->field_count))
       {
              zval **current_row = set->data_cursor;
              MYSQLND_FIELD *field = result->meta->fields;
              struct mysqlnd_field_hash_key * hash_key = result->meta->zend_hash_keys;

              if (NULL == current_row[0]) {
                     uint64_t row_num = (set->data_cursor - set->data) / result->meta->field_count;
                     enum_func_status rc = result->m.row_decoder(set->row_buffers[row_num],
                                                                             current_row,
                                                                             result->meta->field_count,
                                                                             result->meta->fields,
                                                                             result->stored_data->persistent,
                                                                             result->conn->options.numeric_and_datetime_as_unicode,
                                                                             result->conn->options.int_and_float_native,
                                                                             result->conn->stats TSRMLS_CC);
                     if (rc != PASS) {
                            DBG_RETURN(FAIL);
                     }
                     set->initialized_rows++;
                     for (i = 0; i < result->field_count; i++) {
                            /*
                              NULL fields are 0 length, 0 is not more than 0
                              String of zero size, definitely can't be the next max_length.
                              Thus for NULL and zero-length we are quite efficient.
                            */
                            if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
                                   unsigned long len = Z_STRLEN_P(current_row[i]);
                                   if (field->max_length < len) {
                                          field->max_length = len;
                                   }
                            }
                     }
              }

              for (i = 0; i < result->field_count; i++, field++, hash_key++) {
                     zval *data = current_row[i];

                     if (flags & MYSQLND_FETCH_NUM) {
                            Z_ADDREF_P(data);
                            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.
                            */
                            Z_ADDREF_P(data);
                            if (hash_key->is_numeric == FALSE) {
#if MYSQLND_UNICODE
                                   zend_u_hash_quick_update(Z_ARRVAL_P(row), 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),
                                                                         field->name,
                                                                         field->name_length + 1,
                                                                         hash_key->key,
                                                                         (void *) &data, sizeof(zval *), NULL);
#endif
                            } else {
                                   zend_hash_index_update(Z_ARRVAL_P(row),
                                                                         hash_key->key,
                                                                         (void *) &data, sizeof(zval *), NULL);
                            }
                     }
              }
              set->data_cursor += result->meta->field_count;
              *fetched_anything = TRUE;
              MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);
              ret = PASS;
       } else {
              set->data_cursor = NULL;
              *fetched_anything = FALSE;
              ret = PASS;
              DBG_INF("EOF reached");
       }
       DBG_INF_FMT("ret=PASS fetched=%u", *fetched_anything);
       DBG_RETURN(ret);
}

Here is the call graph for this function:

static MYSQLND_ROW_C mysqlnd_fetch_row_buffered_c ( MYSQLND_RES *result  TSRMLS_DC) [static]

Definition at line 950 of file mysqlnd_result.c.

{
       MYSQLND_ROW_C ret = NULL;
       MYSQLND_RES_BUFFERED *set = result->stored_data;

       DBG_ENTER("mysqlnd_fetch_row_buffered_c");

       /* If we haven't read everything */
       if (set->data_cursor &&
              (set->data_cursor - set->data) < (set->row_count * result->meta->field_count))
       {
              zval **current_row = set->data_cursor;
              MYSQLND_FIELD *field = result->meta->fields;
              struct mysqlnd_field_hash_key * hash_key = result->meta->zend_hash_keys;
              unsigned int i;

              if (NULL == current_row[0]) {
                     uint64_t row_num = (set->data_cursor - set->data) / result->meta->field_count;
                     enum_func_status rc = result->m.row_decoder(set->row_buffers[row_num],
                                                                             current_row,
                                                                             result->meta->field_count,
                                                                             result->meta->fields,
                                                                             FALSE,
                                                                             result->conn->options.numeric_and_datetime_as_unicode,
                                                                             result->conn->options.int_and_float_native,
                                                                             result->conn->stats TSRMLS_CC);
                     if (rc != PASS) {
                            DBG_RETURN(ret);
                     }
                     set->initialized_rows++;
                     for (i = 0; i < result->field_count; i++) {
                            /*
                              NULL fields are 0 length, 0 is not more than 0
                              String of zero size, definitely can't be the next max_length.
                              Thus for NULL and zero-length we are quite efficient.
                            */
                            if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
                                   unsigned long len = Z_STRLEN_P(current_row[i]);
                                   if (field->max_length < len) {
                                          field->max_length = len;
                                   }
                            }
                     }
              }

              set->data_cursor += result->meta->field_count;
              MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);

              ret = mnd_malloc(result->field_count * sizeof(char *));
              if (ret) {
                     for (i = 0; i < result->field_count; i++, field++, hash_key++) {
                            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;
                            }
                     }
              }
              /* there is no conn handle in this function thus we can't set OOM in error_info */
       } else {
              set->data_cursor = NULL;
              DBG_INF("EOF reached");
       }
       DBG_RETURN(ret);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static enum_func_status mysqlnd_fetch_row_unbuffered ( MYSQLND_RES *  result,
void *  param,
unsigned int  flags,
zend_bool *fetched_anything  TSRMLS_DC 
) [static]

Definition at line 753 of file mysqlnd_result.c.

{
       enum_func_status     ret;
       zval                        *row = (zval *) param;
       MYSQLND_PACKET_ROW   *row_packet = result->row_packet;

       DBG_ENTER("mysqlnd_fetch_row_unbuffered");
       DBG_INF_FMT("flags=%u", flags);

       *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) {
                     HashTable *row_ht = Z_ARRVAL_P(row);
                     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;

                     enum_func_status rc = result->m.row_decoder(result->unbuf->last_row_buffer,
                                                                             result->unbuf->last_row_data,
                                                                             field_count,
                                                                             row_packet->fields_metadata,
                                                                             FALSE,
                                                                             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);
                     }
                     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) {
                                   Z_ADDREF_P(data);
                                   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.
                                   */
                                   Z_ADDREF_P(data);
                                   if (hash_key->is_numeric == FALSE) {
#if MYSQLND_UNICODE
                                          zend_u_hash_quick_update(Z_ARRVAL_P(row), 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),
                                                                                field->name,
                                                                                field->name_length + 1,
                                                                                hash_key->key,
                                                                                (void *) &data, sizeof(zval *), NULL);
#endif
                                   } else {
                                          zend_hash_index_update(Z_ARRVAL_P(row),
                                                                                hash_key->key,
                                                                                (void *) &data, sizeof(zval *), NULL);
                                   }
                            }
                            if (field->max_length < len) {
                                   field->max_length = len;
                            }
                     }
              }
              *fetched_anything = TRUE;
              result->unbuf->row_count++;
       } else if (ret == FAIL) {
              if (row_packet->error_info.error_no) {
                     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);
}

Here is the call graph for this function:

static MYSQLND_ROW_C mysqlnd_fetch_row_unbuffered_c ( MYSQLND_RES *result  TSRMLS_DC) [static]

Definition at line 635 of file mysqlnd_result.c.

{
       enum_func_status     ret;
       MYSQLND_ROW_C        retrow = NULL;
       unsigned int         i,
                                          field_count = result->field_count;
       MYSQLND_PACKET_ROW   *row_packet = result->row_packet;
       unsigned long        *lengths = result->lengths;

       DBG_ENTER("mysqlnd_fetch_row_unbuffered_c");

       if (result->unbuf->eof_reached) {
              /* No more rows obviously */
              DBG_RETURN(retrow);
       }
       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(retrow);
       }
       if (!row_packet) {
              /* Not fully initialized object that is being cleaned up */
              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
         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->unbuf->row_count++;

              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;

                     enum_func_status rc = result->m.row_decoder(result->unbuf->last_row_buffer,
                                                                        result->unbuf->last_row_data,
                                                                        row_packet->field_count,
                                                                        row_packet->fields_metadata,
                                                                        FALSE,
                                                                        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(retrow);
                     }

                     retrow = mnd_malloc(result->field_count * sizeof(char *));
                     if (retrow) {
                            for (i = 0; i < field_count; i++, field++, hash_key++) {
                                   zval *data = result->unbuf->last_row_data[i];
                                   unsigned 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 {
                            SET_OOM_ERROR(result->conn->error_info);
                     }
              }
       } else if (ret == FAIL) {
              if (row_packet->error_info.error_no) {
                     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("warningss=%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_RETURN(retrow);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void mysqlnd_internal_free_result ( MYSQLND_RES *result  TSRMLS_DC) [static]

Definition at line 313 of file mysqlnd_result.c.

{
       DBG_ENTER("mysqlnd_internal_free_result");
       result->m.free_result_contents(result TSRMLS_CC);

       if (result->conn) {
              result->conn->m->free_reference(result->conn TSRMLS_CC);
              result->conn = NULL;
       }

       mnd_pefree(result, result->persistent);

       DBG_VOID_RETURN;
}

Here is the call graph for this function:

static void mysqlnd_internal_free_result_contents ( MYSQLND_RES *result  TSRMLS_DC) [static]

Definition at line 295 of file mysqlnd_result.c.

{
       DBG_ENTER("mysqlnd_internal_free_result_contents");

       result->m.free_result_buffers(result TSRMLS_CC);

       if (result->meta) {
              result->meta->m->free_metadata(result->meta TSRMLS_CC);
              result->meta = NULL;
       }

       DBG_VOID_RETURN;
}

Here is the call graph for this function:

static MYSQLND_METHOD ( mysqlnd_res  ,
initialize_result_set_rest   
) [static]

Definition at line 37 of file mysqlnd_result.c.

{
       unsigned int i;
       zval **data_cursor = result->stored_data? result->stored_data->data:NULL;
       zval **data_begin = result->stored_data? result->stored_data->data:NULL;
       unsigned int field_count = result->meta? result->meta->field_count : 0;
       uint64_t row_count = result->stored_data? result->stored_data->row_count:0;
       enum_func_status ret = PASS;
       DBG_ENTER("mysqlnd_res::initialize_result_set_rest");

       if (!data_cursor || row_count == result->stored_data->initialized_rows) {
              DBG_RETURN(ret);
       }
       while ((data_cursor - data_begin) < (int)(row_count * field_count)) {
              if (NULL == data_cursor[0]) {
                     enum_func_status rc = result->m.row_decoder(
                                                               result->stored_data->row_buffers[(data_cursor - data_begin) / field_count],
                                                               data_cursor,
                                                               result->meta->field_count,
                                                               result->meta->fields,
                                                               result->stored_data->persistent,
                                                               result->conn->options.numeric_and_datetime_as_unicode,
                                                               result->conn->options.int_and_float_native,
                                                               result->conn->stats TSRMLS_CC);
                     if (rc != PASS) {
                            ret = FAIL;
                            break;
                     }
                     result->stored_data->initialized_rows++;
                     for (i = 0; i < result->field_count; i++) {
                            /*
                              NULL fields are 0 length, 0 is not more than 0
                              String of zero size, definitely can't be the next max_length.
                              Thus for NULL and zero-length we are quite efficient.
                            */
                            if (Z_TYPE_P(data_cursor[i]) >= IS_STRING) {
                                   unsigned long len = Z_STRLEN_P(data_cursor[i]);
                                   if (result->meta->fields[i].max_length < len) {
                                          result->meta->fields[i].max_length = len;
                                   }
                            }
                     }
              }
              data_cursor += field_count;
       }
       DBG_RETURN(ret);
}

Here is the call graph for this function:

static MYSQLND_METHOD ( mysqlnd_res  ,
unbuffered_free_last_data   
) [static]

Definition at line 143 of file mysqlnd_result.c.

{
       MYSQLND_RES_UNBUFFERED *unbuf = result->unbuf;

       DBG_ENTER("mysqlnd_res::unbuffered_free_last_data");

       if (!unbuf) {
              DBG_VOID_RETURN;
       }

       DBG_INF_FMT("last_row_data=%p", unbuf->last_row_data);
       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;

              DBG_INF_FMT("%u columns to free", result->field_count);
              for (i = 0; i < result->field_count; i++) {
                     mysqlnd_rset_zval_ptr_dtor(&(unbuf->last_row_data[i]), result->type, &copy_ctor_called TSRMLS_CC);
                     if (copy_ctor_called) {
                            ++ctor_called_count;
                     }
              }
              DBG_INF_FMT("copy_ctor_called_count=%u", ctor_called_count);
              /* By using value3 macros we hold a mutex only once, there is no value2 */
              MYSQLND_INC_CONN_STATISTIC_W_VALUE2(global_stats,
                                                                             STAT_COPY_ON_WRITE_PERFORMED,
                                                                             ctor_called_count,
                                                                             STAT_COPY_ON_WRITE_SAVED,
                                                                             result->field_count - ctor_called_count);
              /* Free last row's zvals */
              mnd_efree(unbuf->last_row_data);
              unbuf->last_row_data = NULL;
       }
       if (unbuf->last_row_buffer) {
              DBG_INF("Freeing last row buffer");
              /* Nothing points to this buffer now, free it */
              unbuf->last_row_buffer->free_chunk(unbuf->last_row_buffer TSRMLS_CC);
              unbuf->last_row_buffer = NULL;
       }

       DBG_VOID_RETURN;
}

Here is the call graph for this function:

static MYSQLND_METHOD ( mysqlnd_res  ,
free_buffered_data   
) [static]

Definition at line 191 of file mysqlnd_result.c.

{
       MYSQLND_RES_BUFFERED *set = result->stored_data;
       unsigned int field_count = result->field_count;
       int64_t row;

       DBG_ENTER("mysqlnd_res::free_buffered_data");
       DBG_INF_FMT("Freeing "MYSQLND_LLU_SPEC" row(s)", set->row_count);

       DBG_INF("Freeing data & row_buffer");
       if (set->data) {
              unsigned int copy_on_write_performed = 0;
              unsigned int copy_on_write_saved = 0;

              DBG_INF_FMT("before: real_usage=%lu  usage=%lu", zend_memory_usage(TRUE TSRMLS_CC), zend_memory_usage(FALSE TSRMLS_CC));
              for (row = set->row_count - 1; row >= 0; row--) {
                     zval **current_row = set->data + row * field_count;
                     MYSQLND_MEMORY_POOL_CHUNK *current_buffer = set->row_buffers[row];
                     int64_t col;

                     if (current_row != NULL) {
                            for (col = field_count - 1; col >= 0; --col) {
                                   if (current_row[col]) {
                                          zend_bool copy_ctor_called;
                                          mysqlnd_rset_zval_ptr_dtor(&(current_row[col]), result->type, &copy_ctor_called TSRMLS_CC);
#if MYSQLND_DEBUG_MEMORY
                                          DBG_INF_FMT("Copy_ctor_called=%u", copy_ctor_called);
#endif
                                          if (copy_ctor_called) {
                                                 ++copy_on_write_performed;
                                          } else {
                                                 ++copy_on_write_saved;
                                          }
                                   }
                            }
                     }
#if MYSQLND_DEBUG_MEMORY
                     DBG_INF("Freeing current_row & current_buffer");
#endif
                     current_buffer->free_chunk(current_buffer TSRMLS_CC);
              }

              MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_COPY_ON_WRITE_PERFORMED, copy_on_write_performed,
                                                                               STAT_COPY_ON_WRITE_SAVED, copy_on_write_saved);
              mnd_pefree(set->data, set->persistent);
              set->data = NULL;
       }

       if (set->row_buffers) {
              mnd_pefree(set->row_buffers, set->persistent);
              set->row_buffers     = NULL;
       }
       set->data_cursor = NULL;
       set->row_count       = 0;

       DBG_INF("Freeing set");
       mnd_pefree(set, set->persistent);

       DBG_INF_FMT("after: real_usage=%lu  usage=%lu", zend_memory_usage(TRUE TSRMLS_CC), zend_memory_usage(FALSE TSRMLS_CC));
       DBG_VOID_RETURN;
}

Here is the call graph for this function:

static MYSQLND_METHOD ( mysqlnd_res  ,
free_result_buffers   
) [static]

Definition at line 257 of file mysqlnd_result.c.

{
       DBG_ENTER("mysqlnd_res::free_result_buffers");
       DBG_INF_FMT("%s", result->unbuf? "unbuffered":(result->stored_data? "buffered":"unknown"));

       if (result->unbuf) {
              result->m.unbuffered_free_last_data(result TSRMLS_CC);
              mnd_efree(result->unbuf);
              result->unbuf = NULL;
       } else if (result->stored_data) {
              result->m.free_buffered_data(result TSRMLS_CC);
              result->stored_data = NULL;
       }

       if (result->lengths) {
              mnd_efree(result->lengths);
              result->lengths = NULL;
       }

       if (result->row_packet) {
              DBG_INF("Freeing packet");
              PACKET_FREE(result->row_packet);
              result->row_packet = NULL;
       }

       if (result->result_set_memory_pool) {
              DBG_INF("Freeing memory pool");
              mysqlnd_mempool_destroy(result->result_set_memory_pool TSRMLS_CC);
              result->result_set_memory_pool = NULL;
       }

       DBG_VOID_RETURN;
}

Here is the call graph for this function:

static MYSQLND_METHOD ( mysqlnd_res  ,
read_result_metadata   
) [static]

Definition at line 332 of file mysqlnd_result.c.

{
       DBG_ENTER("mysqlnd_res::read_result_metadata");

       /*
         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) {
              result->meta->m->free_metadata(result->meta TSRMLS_CC);
              result->meta = NULL;
       }

       result->meta = result->m.result_meta_init(result->field_count, result->persistent TSRMLS_CC);
       if (!result->meta) {
              SET_OOM_ERROR(conn->error_info);
              DBG_RETURN(FAIL);
       }

       /* 1. Read all fields metadata */

       /* It's safe to reread without freeing */
       if (FAIL == result->meta->m->read_metadata(result->meta, conn TSRMLS_CC)) {
              result->m.free_result_contents(result TSRMLS_CC);
              DBG_RETURN(FAIL);
       }
       /* COM_FIELD_LIST is broken and has premature EOF, thus we need to hack here and in mysqlnd_res_meta.c */
       result->field_count = result->meta->field_count;

       /*
         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.
       */

       DBG_RETURN(PASS);
}

Here is the call graph for this function:

static MYSQLND_METHOD ( mysqlnd_res  ,
use_result   
) [static]

Definition at line 891 of file mysqlnd_result.c.

{
       DBG_ENTER("mysqlnd_res::use_result");
       DBG_INF_FMT("ps=%u", ps);

       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->m.row_decoder       = php_mysqlnd_rowp_read_text_protocol;
              result->lengths                    = mnd_ecalloc(result->field_count, sizeof(unsigned long));
              if (!result->lengths) {
                     goto oom;
              }
       } else {
              result->type                = MYSQLND_RES_PS_UNBUF;
              result->m.fetch_row         = NULL;
              /* result->m.fetch_row() will be set in mysqlnd_ps.c */
              result->m.fetch_lengths     = NULL; /* makes no sense */
              result->m.row_decoder       = php_mysqlnd_rowp_read_binary_protocol;
              result->lengths             = NULL;
       }

       result->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC);
       result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));
       if (!result->result_set_memory_pool || !result->unbuf) {
              goto oom;
       }

       /*
         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.
       */
       /* FALSE = non-persistent */
       result->row_packet = result->conn->protocol->m.get_row_packet(result->conn->protocol, FALSE TSRMLS_CC);
       if (!result->row_packet) {
              goto oom;
       }
       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->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;

       DBG_RETURN(result);
oom:
       SET_OOM_ERROR(result->conn->error_info);
       DBG_RETURN(NULL);
}

Here is the call graph for this function:

static MYSQLND_METHOD ( mysqlnd_res  ,
store_result_fetch_data   
)

Definition at line 1127 of file mysqlnd_result.c.

{
       enum_func_status ret;
       MYSQLND_PACKET_ROW *row_packet = NULL;
       unsigned int next_extend = STORE_RESULT_PREALLOCATED_SET_IF_NOT_EMPTY, free_rows = 1;
       MYSQLND_RES_BUFFERED *set;

       DBG_ENTER("mysqlnd_res::store_result_fetch_data");
       DBG_INF_FMT("conn=%llu binary_proto=%u to_cache=%u",
                            conn->thread_id, binary_protocol, to_cache);

       result->stored_data  = set = mnd_pecalloc(1, sizeof(MYSQLND_RES_BUFFERED), to_cache);
       if (!set) {
              SET_OOM_ERROR(conn->error_info);
              ret = FAIL;
              goto end;
       }
       if (free_rows) {
              set->row_buffers = mnd_pemalloc((size_t)(free_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)), to_cache);
              if (!set->row_buffers) {
                     SET_OOM_ERROR(conn->error_info);
                     ret = FAIL;
                     goto end;
              }
       }
       set->persistent      = to_cache;
       set->references      = 1;

       result->m.row_decoder = binary_protocol? php_mysqlnd_rowp_read_binary_protocol:
                                                                              php_mysqlnd_rowp_read_text_protocol;

       /* non-persistent */
       row_packet = conn->protocol->m.get_row_packet(conn->protocol, FALSE TSRMLS_CC);
       if (!row_packet) {
              SET_OOM_ERROR(conn->error_info);
              ret = FAIL;
              goto end;
       }
       row_packet->result_set_memory_pool = result->result_set_memory_pool;
       row_packet->field_count = meta->field_count;
       row_packet->binary_protocol = binary_protocol;
       row_packet->fields_metadata = meta->fields;
       row_packet->bit_fields_count       = meta->bit_fields_count;
       row_packet->bit_fields_total_len = meta->bit_fields_total_len;

       row_packet->skip_extraction = TRUE; /* let php_mysqlnd_rowp_read() not allocate row_packet->fields, we will do it */

       while (FAIL != (ret = PACKET_READ(row_packet, conn)) && !row_packet->eof) {
              if (!free_rows) {
                     uint64_t total_allocated_rows = free_rows = next_extend = next_extend * 11 / 10; /* extend with 10% */
                     MYSQLND_MEMORY_POOL_CHUNK ** new_row_buffers;
                     total_allocated_rows += set->row_count;

                     /* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
                     if (total_allocated_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *) > SIZE_MAX) {
                            SET_OOM_ERROR(conn->error_info);
                            ret = FAIL;
                            goto end;
                     }
                     new_row_buffers = mnd_perealloc(set->row_buffers,
                                                                             (size_t)(total_allocated_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)),
                                                                             set->persistent);
                     if (!new_row_buffers) {
                            SET_OOM_ERROR(conn->error_info);
                            ret = FAIL;
                            goto end;
                     }
                     set->row_buffers = new_row_buffers;
              }
              free_rows--;
              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;

              /*
                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. 
              */
       }
       /* Overflow ? */
       if (set->row_count) {
              /* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
              if (set->row_count * meta->field_count * sizeof(zval *) > SIZE_MAX) {
                     SET_OOM_ERROR(conn->error_info);
                     ret = FAIL;
                     goto end;
              }
              /* if pecalloc is used valgrind barks gcc version 4.3.1 20080507 (prerelease) [gcc-4_3-branch revision 135036] (SUSE Linux) */
              set->data = mnd_pemalloc((size_t)(set->row_count * meta->field_count * sizeof(zval *)), to_cache);
              if (!set->data) {
                     SET_OOM_ERROR(conn->error_info);
                     ret = FAIL;
                     goto end;
              }
              memset(set->data, 0, (size_t)(set->row_count * meta->field_count * sizeof(zval *)));
       }

       MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats,
                                                                  binary_protocol? STAT_ROWS_BUFFERED_FROM_CLIENT_PS:
                                                                                                  STAT_ROWS_BUFFERED_FROM_CLIENT_NORMAL,
                                                                  set->row_count);

       /* Finally clean */
       if (row_packet->eof) { 
              conn->upsert_status.warning_count = row_packet->warning_count;
              conn->upsert_status.server_status = row_packet->server_status;
       }
       /* save some memory */
       if (free_rows) {
              /* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
              if (set->row_count * sizeof(MYSQLND_MEMORY_POOL_CHUNK *) > SIZE_MAX) {
                     SET_OOM_ERROR(conn->error_info);
                     ret = FAIL;
                     goto end;
              }
              set->row_buffers = mnd_perealloc(set->row_buffers,
                                                                       (size_t) (set->row_count * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)),
                                                                       set->persistent);
       }

       if (conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) {
              CONN_SET_STATE(conn, CONN_NEXT_RESULT_PENDING);
       } else {
              CONN_SET_STATE(conn, CONN_READY);
       }

       if (ret == FAIL) {
              set->error_info = row_packet->error_info;
       } else {
              /* Position at the first row */
              set->data_cursor = set->data;

              /* libmysql's documentation says it should be so for SELECT statements */
              conn->upsert_status.affected_rows = set->row_count;
       }
       DBG_INF_FMT("ret=%s row_count=%u warnings=%u server_status=%u",
                            ret == PASS? "PASS":"FAIL", (uint) set->row_count, conn->upsert_status.warning_count, conn->upsert_status.server_status);
end:
       PACKET_FREE(row_packet);

       DBG_RETURN(ret);
}

Here is the call graph for this function:

static MYSQLND_METHOD ( mysqlnd_res  ,
store_result   
) [static]

Definition at line 1283 of file mysqlnd_result.c.

{
       enum_func_status ret;
       zend_bool to_cache = FALSE;

       DBG_ENTER("mysqlnd_res::store_result");
       DBG_INF_FMT("conn=%u ps_protocol=%u", conn->thread_id, ps_protocol);

       /* We need the conn because we are doing lazy zval initialization in buffered_fetch_row */
       result->conn                = conn->m->get_reference(conn TSRMLS_CC);
       result->type                = MYSQLND_RES_NORMAL;
       result->m.fetch_row         = result->m.fetch_row_normal_buffered;
       result->m.fetch_lengths     = mysqlnd_fetch_lengths_buffered;

       result->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC);
       result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long));
       if (!result->result_set_memory_pool || !result->lengths) {
              SET_OOM_ERROR(conn->error_info);
              DBG_RETURN(NULL);
       }

       CONN_SET_STATE(conn, CONN_FETCHING_DATA);

       ret = result->m.store_result_fetch_data(conn, result, result->meta, ps_protocol, to_cache TSRMLS_CC);
       if (FAIL == ret) {
              if (result->stored_data) {
                     conn->error_info = result->stored_data->error_info;
              } else {
                     SET_OOM_ERROR(conn->error_info);
              }
              DBG_RETURN(NULL);
       }
       /* libmysql's documentation says it should be so for SELECT statements */
       conn->upsert_status.affected_rows = result->stored_data->row_count;

       DBG_RETURN(result);
}

Here is the call graph for this function:

static MYSQLND_METHOD ( mysqlnd_res  ,
skip_result   
) [static]

Definition at line 1327 of file mysqlnd_result.c.

{
       zend_bool fetched_anything;

       DBG_ENTER("mysqlnd_res::skip_result");
       /*
         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->stored_data && result->unbuf &&
              !result->unbuf->eof_reached && result->m.fetch_row)
       {
              DBG_INF("skipping result");
              /* 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 */;
              }
       }
       DBG_RETURN(PASS);
}

Here is the call graph for this function:

static MYSQLND_METHOD ( mysqlnd_res  ,
free_result   
) [static]

Definition at line 1357 of file mysqlnd_result.c.

{
       DBG_ENTER("mysqlnd_res::free_result");
       DBG_INF_FMT("implicit=%u", implicit);

       result->m.skip_result(result TSRMLS_CC);
       MYSQLND_INC_CONN_STATISTIC(result->conn? result->conn->stats : NULL,
                                                    implicit == TRUE? STAT_FREE_RESULT_IMPLICIT:
                                                                                           STAT_FREE_RESULT_EXPLICIT);

       result->m.free_result_internal(result TSRMLS_CC);
       DBG_RETURN(PASS);
}

Here is the call graph for this function:

static MYSQLND_METHOD ( mysqlnd_res  ,
data_seek   
) [static]

Definition at line 1375 of file mysqlnd_result.c.

{
       DBG_ENTER("mysqlnd_res::data_seek");
       DBG_INF_FMT("row=%lu", row);

       if (!result->stored_data) {
              return FAIL;
       }

       /* libmysql just moves to the end, it does traversing of a linked list */
       if (row >= result->stored_data->row_count) {
              result->stored_data->data_cursor = NULL;
       } else {
              result->stored_data->data_cursor = result->stored_data->data + row * result->meta->field_count;
       }

       DBG_RETURN(PASS);
}

Here is the call graph for this function:

static MYSQLND_METHOD ( mysqlnd_res  ,
num_rows   
) const [static]

Definition at line 1398 of file mysqlnd_result.c.

{
       /* Be compatible with libmysql. We count row_count, but will return 0 */
       return result->stored_data? result->stored_data->row_count:(result->unbuf && result->unbuf->eof_reached? result->unbuf->row_count:0);
}
static MYSQLND_METHOD ( mysqlnd_res  ,
num_fields   
) const [static]

Definition at line 1408 of file mysqlnd_result.c.

{
       return result->field_count;
}
static MYSQLND_METHOD ( mysqlnd_res  ,
fetch_field   
) [static]

Definition at line 1417 of file mysqlnd_result.c.

{
       DBG_ENTER("mysqlnd_res::fetch_field");
       do {
              if (result->meta) {
                     /*
                       We optimize the result set, so we don't convert all the data from raw buffer format to
                       zval arrays during store. In the case someone doesn't read all the lines this will
                       save time. However, when a metadata call is done, we need to calculate max_length.
                       We don't have control whether max_length will be used, unfortunately. Otherwise we
                       could have been able to skip that step.
                       Well, if the mysqli API switches from returning stdClass to class like mysqli_field_metadata,
                       then we can have max_length as dynamic property, which will be calculated during runtime and
                       not during mysqli_fetch_field() time.
                     */
                     if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
                            DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request");
                            /* we have to initialize the rest to get the updated max length */
                            if (PASS != result->m.initialize_result_set_rest(result TSRMLS_CC)) {
                                   break;
                            }
                     }
                     DBG_RETURN(result->meta->m->fetch_field(result->meta TSRMLS_CC));
              }
       } while (0);
       DBG_RETURN(NULL);
}

Here is the call graph for this function:

static MYSQLND_METHOD ( mysqlnd_res  ,
fetch_field_direct   
) [static]

Definition at line 1449 of file mysqlnd_result.c.

{
       DBG_ENTER("mysqlnd_res::fetch_field_direct");
       do {
              if (result->meta) {
                     /*
                       We optimize the result set, so we don't convert all the data from raw buffer format to
                       zval arrays during store. In the case someone doesn't read all the lines this will
                       save time. However, when a metadata call is done, we need to calculate max_length.
                       We don't have control whether max_length will be used, unfortunately. Otherwise we
                       could have been able to skip that step.
                       Well, if the mysqli API switches from returning stdClass to class like mysqli_field_metadata,
                       then we can have max_length as dynamic property, which will be calculated during runtime and
                       not during mysqli_fetch_field_direct() time.
                     */
                     if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
                            DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request");
                            /* we have to initialized the rest to get the updated max length */
                            if (PASS != result->m.initialize_result_set_rest(result TSRMLS_CC)) {
                                   break;
                            }
                     }
                     DBG_RETURN(result->meta->m->fetch_field_direct(result->meta, fieldnr TSRMLS_CC));
              }
       } while (0);

       DBG_RETURN(NULL);
}

Here is the call graph for this function:

static MYSQLND_METHOD ( mysqlnd_res  ,
fetch_fields   
) [static]

Definition at line 1482 of file mysqlnd_result.c.

{
       DBG_ENTER("mysqlnd_res::fetch_fields");
       do {
              if (result->meta) {
                     if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
                            /* we have to initialize the rest to get the updated max length */
                            if (PASS != result->m.initialize_result_set_rest(result TSRMLS_CC)) {
                                   break;
                            }
                     }
                     DBG_RETURN(result->meta->m->fetch_fields(result->meta TSRMLS_CC));
              }
       } while (0);
       DBG_RETURN(NULL);
}

Here is the call graph for this function:

static MYSQLND_METHOD ( mysqlnd_res  ,
field_seek   
) [static]

Definition at line 1504 of file mysqlnd_result.c.

{
       MYSQLND_FIELD_OFFSET return_value = 0;
       if (result->meta) {
              return_value = result->meta->current_field;
              result->meta->current_field = field_offset;
       }
       return return_value;
}
static MYSQLND_METHOD ( mysqlnd_res  ,
field_tell   
) const [static]

Definition at line 1518 of file mysqlnd_result.c.

{
       return result->meta? result->meta->m->field_tell(result->meta TSRMLS_CC) : 0;
}
static MYSQLND_METHOD ( mysqlnd_res  ,
fetch_into   
) [static]

Definition at line 1530 of file mysqlnd_result.c.

{
       zend_bool fetched_anything;

       DBG_ENTER("mysqlnd_res::fetch_into");
       DBG_INF_FMT("flags=%u mysqlnd_extension=%u", flags, extension);

       if (!result->m.fetch_row) {
              RETVAL_NULL();
              DBG_VOID_RETURN;
       }
       /*
         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");
              RETVAL_FALSE;
       } else if (fetched_anything == FALSE) {
              zval_dtor(return_value);
              switch (extension) {
                     case MYSQLND_MYSQLI:
                            RETVAL_NULL();
                            break;
                     case MYSQLND_MYSQL:
                            RETVAL_FALSE;
                            break;
                     default:exit(0);
              }
       } 
       /*
         return_value is IS_NULL for no more data and an array for data. Thus it's ok
         to return here.
       */
       DBG_VOID_RETURN;
}

Here is the call graph for this function:

static MYSQLND_METHOD ( mysqlnd_res  ,
fetch_row_c   
) [static]

Definition at line 1574 of file mysqlnd_result.c.

{
       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) {
                     DBG_RETURN(mysqlnd_fetch_row_buffered_c(result TSRMLS_CC));
              } else if (result->m.fetch_row == result->m.fetch_row_normal_unbuffered) {
                     DBG_RETURN(mysqlnd_fetch_row_unbuffered_c(result TSRMLS_CC));
              } else {
                     php_error_docref(NULL TSRMLS_CC, E_ERROR, "result->m.fetch_row has invalid value. Report to the developers");
              }
       }
       DBG_RETURN(ret);
}

Here is the call graph for this function:

static MYSQLND_METHOD ( mysqlnd_res  ,
fetch_all   
) [static]

Definition at line 1595 of file mysqlnd_result.c.

{
       zval  *row;
       ulong i = 0;
       MYSQLND_RES_BUFFERED *set = result->stored_data;

       DBG_ENTER("mysqlnd_res::fetch_all");
       DBG_INF_FMT("flags=%u", flags);

       if ((!result->unbuf && !set)) {
              php_error_docref(NULL TSRMLS_CC, E_WARNING, "fetch_all can be used only with buffered sets");
              if (result->conn) {
                     SET_CLIENT_ERROR(result->conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "fetch_all can be used only with buffered sets");
              }
              RETVAL_NULL();
              DBG_VOID_RETURN;
       }

       /* 4 is a magic value. The cast is safe, if larger then the array will be later extended - no big deal :) */
       mysqlnd_array_init(return_value, set? (unsigned int) set->row_count : 4); 

       do {
              MAKE_STD_ZVAL(row);
              mysqlnd_fetch_into(result, flags, row, MYSQLND_MYSQLI);
              if (Z_TYPE_P(row) != IS_ARRAY) {
                     zval_ptr_dtor(&row);
                     break;
              }
              add_index_zval(return_value, i++, row);
       } while (1);

       DBG_VOID_RETURN;
}

Here is the call graph for this function:

static MYSQLND_METHOD ( mysqlnd_res  ,
fetch_field_data   
) [static]

Definition at line 1633 of file mysqlnd_result.c.

{
       zval row;
       zval **entry;
       unsigned int i = 0;

       DBG_ENTER("mysqlnd_res::fetch_field_data");
       DBG_INF_FMT("offset=%u", offset);

       if (!result->m.fetch_row) {
              RETVAL_NULL();
              DBG_VOID_RETURN;
       }
       /*
         Hint Zend how many elements we will have in the hash. Thus it won't
         extend and rehash the hash constantly.
       */
       INIT_PZVAL(&row);
       mysqlnd_fetch_into(result, MYSQLND_FETCH_NUM, &row, MYSQLND_MYSQL);
       if (Z_TYPE(row) != IS_ARRAY) {
              zval_dtor(&row);
              RETVAL_NULL();
              DBG_VOID_RETURN;
       }
       zend_hash_internal_pointer_reset(Z_ARRVAL(row));
       while (i++ < offset) {
              zend_hash_move_forward(Z_ARRVAL(row));
              zend_hash_get_current_data(Z_ARRVAL(row), (void **)&entry);
       }

       zend_hash_get_current_data(Z_ARRVAL(row), (void **)&entry);

       *return_value = **entry;
       zval_copy_ctor(return_value);
       Z_SET_REFCOUNT_P(return_value, 1);
       zval_dtor(&row);

       DBG_VOID_RETURN;
}

Here is the call graph for this function:

enum_func_status mysqlnd_query_read_result_set_header ( MYSQLND *  conn,
MYSQLND_STMT *s  TSRMLS_DC 
)

Definition at line 377 of file mysqlnd_result.c.

{
       MYSQLND_STMT_DATA * stmt = s ? s->data:NULL;
       enum_func_status ret;
       MYSQLND_PACKET_RSET_HEADER * rset_header = NULL;
       MYSQLND_PACKET_EOF * fields_eof = NULL;

       DBG_ENTER("mysqlnd_query_read_result_set_header");
       DBG_INF_FMT("stmt=%lu", stmt? stmt->stmt_id:0);

       ret = FAIL;
       do {
              rset_header = conn->protocol->m.get_rset_header_packet(conn->protocol, FALSE TSRMLS_CC);
              if (!rset_header) {
                     SET_OOM_ERROR(conn->error_info);
                     ret = FAIL;
                     break;
              }

              SET_ERROR_AFF_ROWS(conn);

              if (FAIL == (ret = PACKET_READ(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;
                     /*
                       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;
                     DBG_ERR_FMT("error=%s", rset_header->error_info.error);
                     /* Return back from CONN_QUERY_SENT */
                     CONN_SET_STATE(conn, CONN_READY);
                     break;
              }
              conn->error_info.error_no = 0;

              switch (rset_header->field_count) {
                     case MYSQLND_NULL_LENGTH: { /* LOAD DATA LOCAL INFILE */
                            zend_bool is_warning;
                            DBG_INF("LOAD DATA");
                            conn->last_query_type = QUERY_LOAD_LOCAL;
                            conn->field_count = 0; /* overwrite previous value, or the last value could be used and lead to bug#53503 */
                            CONN_SET_STATE(conn, CONN_SENDING_LOAD_DATA);
                            ret = mysqlnd_handle_local_infile(conn, rset_header->info_or_local_file, &is_warning TSRMLS_CC);
                            CONN_SET_STATE(conn,  (ret == PASS || is_warning == TRUE)? CONN_READY:CONN_QUIT_SENT);
                            MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_NON_RSET_QUERY);
                            break;
                     }
                     case 0:                            /* UPSERT */
                            DBG_INF("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,
                                                        conn->persistent);
                            /* Result set can follow UPSERT statement, check server_status */
                            if (conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) {
                                   CONN_SET_STATE(conn, CONN_NEXT_RESULT_PENDING);
                            } else {
                                   CONN_SET_STATE(conn, CONN_READY);
                            }
                            ret = PASS;
                            MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_NON_RSET_QUERY);
                            break;
                     default: do {               /* Result set */
                            MYSQLND_RES * result;
                            enum_mysqlnd_collected_stats statistic = STAT_LAST;

                            DBG_INF("Result set pending");
                            SET_EMPTY_MESSAGE(conn->last_message, conn->last_message_len, conn->persistent);

                            MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_RSET_QUERY);
                            memset(&conn->upsert_status, 0, sizeof(conn->upsert_status));
                            /* restore after zeroing */
                            SET_ERROR_AFF_ROWS(conn);

                            conn->last_query_type = QUERY_SELECT;
                            CONN_SET_STATE(conn, CONN_FETCHING_DATA);
                            /* PS has already allocated it */
                            conn->field_count = rset_header->field_count;
                            if (!stmt) {
                                   result = conn->current_result = conn->m->result_init(rset_header->field_count, conn->persistent TSRMLS_CC);
                            } else {
                                   if (!stmt->result) {
                                          DBG_INF("This is 'SHOW'/'EXPLAIN'-like query.");
                                          /*
                                            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.
                                          */
                                          result = stmt->result = conn->m->result_init(rset_header->field_count, stmt->persistent TSRMLS_CC);
                                   } 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 (!result) {
                                   SET_OOM_ERROR(conn->error_info);
                                   ret = FAIL;
                                   break;
                            }

                            if (FAIL == (ret = result->m.read_result_metadata(result, conn TSRMLS_CC))) {
                                   /* For PS, we leave them in Prepared state */
                                   if (!stmt && conn->current_result) {
                                          mnd_efree(conn->current_result);
                                          conn->current_result = NULL;
                                   }
                                   DBG_ERR("Error ocurred while reading metadata");
                                   break;
                            }

                            /* Check for SERVER_STATUS_MORE_RESULTS if needed */
                            fields_eof = conn->protocol->m.get_eof_packet(conn->protocol, FALSE TSRMLS_CC);
                            if (!fields_eof) {
                                   SET_OOM_ERROR(conn->error_info);
                                   ret = FAIL;
                                   break;
                            }
                            if (FAIL == (ret = PACKET_READ(fields_eof, conn))) {
                                   DBG_ERR("Error ocurred while reading the EOF packet");
                                   result->m.free_result_contents(result TSRMLS_CC);
                                   mnd_efree(result);
                                   if (!stmt) {
                                          conn->current_result = NULL;
                                   } else {
                                          stmt->result = NULL;
                                          memset(stmt, 0, sizeof(MYSQLND_STMT));
                                          stmt->state = MYSQLND_STMT_INITTED;
                                   }
                            } else {
                                   unsigned int to_log = MYSQLND_G(log_mask);
                                   to_log &= fields_eof->server_status;
                                   DBG_INF_FMT("warnings=%u server_status=%u", fields_eof->warning_count, fields_eof->server_status);
                                   conn->upsert_status.warning_count = fields_eof->warning_count;
                                   /*
                                     If SERVER_MORE_RESULTS_EXISTS is set then this is either MULTI_QUERY or a CALL()
                                     The first packet after sending the query/com_execute has the bit set only
                                     in this cases. Not sure why it's a needed but it marks that the whole stream
                                     will include many result sets. What actually matters are the bits set at the end
                                     of every result set (the EOF packet).
                                   */
                                   conn->upsert_status.server_status = fields_eof->server_status;
                                   if (fields_eof->server_status & SERVER_QUERY_NO_GOOD_INDEX_USED) {
                                          statistic = STAT_BAD_INDEX_USED;
                                   } else if (fields_eof->server_status & SERVER_QUERY_NO_INDEX_USED) {
                                          statistic = STAT_NO_INDEX_USED;
                                   } else if (fields_eof->server_status & SERVER_QUERY_WAS_SLOW) {
                                          statistic = STAT_QUERY_WAS_SLOW;
                                   }
                                   if (to_log) {
#if A0
                                          char *backtrace = mysqlnd_get_backtrace(TSRMLS_C);
                                          php_log_err(backtrace TSRMLS_CC);
                                          efree(backtrace);
#endif
                                   }
                                   MYSQLND_INC_CONN_STATISTIC(conn->stats, statistic);
                            }
                     } while (0);
                     PACKET_FREE(fields_eof);
                     break; /* switch break */
              }
       } while (0);
       PACKET_FREE(rset_header);

       DBG_INF(ret == PASS? "PASS":"FAIL");
       DBG_RETURN(ret);
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 1750 of file mysqlnd_result.c.

{
       return &mysqlnd_mysqlnd_res_methods;
}
PHPAPI MYSQLND_RES* mysqlnd_result_init ( unsigned int  field_count,
zend_bool persistent  TSRMLS_DC 
)

Definition at line 1714 of file mysqlnd_result.c.

{
       size_t alloc_size = sizeof(MYSQLND_RES) + mysqlnd_plugin_count() * sizeof(void *);
       MYSQLND_RES *ret = mnd_pecalloc(1, alloc_size, persistent);

       DBG_ENTER("mysqlnd_result_init");
       DBG_INF_FMT("field_count=%u", field_count);

       if (!ret) {
              DBG_RETURN(NULL);
       }

       ret->persistent             = persistent;
       ret->field_count     = field_count;
       ret->m = mysqlnd_mysqlnd_res_methods;

       DBG_RETURN(ret);
}

Here is the call graph for this function:

static void mysqlnd_rset_zval_ptr_dtor ( zval **  zv,
enum_mysqlnd_res_type  type,
zend_bool *copy_ctor_called  TSRMLS_DC 
) [static]

Definition at line 89 of file mysqlnd_result.c.

{
       DBG_ENTER("mysqlnd_rset_zval_ptr_dtor");
       if (!zv || !*zv) {
              *copy_ctor_called = FALSE;
              DBG_ERR_FMT("zv was NULL");
              DBG_VOID_RETURN;
       }
       /*
         This zval is not from the cache block.
         Thus the refcount is -1 than of a zval from the cache,
         because the zvals from the cache are owned by it.
       */
       if (type == MYSQLND_RES_PS_BUF || type == MYSQLND_RES_PS_UNBUF) {
              *copy_ctor_called = FALSE;
              ; /* do nothing, zval_ptr_dtor will do the job*/
       } else if (Z_REFCOUNT_PP(zv) > 1) {
              /*
                Not a prepared statement, then we have to
                call copy_ctor and then zval_ptr_dtor()

                In Unicode mode the destruction  of the zvals should not call
                zval_copy_ctor() because then we will leak.
                I suppose we can use UG(unicode) in mysqlnd.c when freeing a result set
                to check if we need to call copy_ctor().

                If the type is IS_UNICODE, which can happen with PHP6, then we don't
                need to copy_ctor, as the data doesn't point to our internal buffers.
                If it's string (in PHP5 always) and in PHP6 if data is binary, then
                it still points to internal buffers and has to be copied.
              */
              if (Z_TYPE_PP(zv) == IS_STRING) {
                     zval_copy_ctor(*zv);
              }
              *copy_ctor_called = TRUE;
       } else {
              /*
                noone but us point to this, so we can safely ZVAL_NULL the zval,
                so Zend does not try to free what the zval points to - which is
                in result set buffers
              */
              *copy_ctor_called = FALSE;
              if (Z_TYPE_PP(zv) == IS_STRING) {
                     ZVAL_NULL(*zv);
              }
       }
       zval_ptr_dtor(zv);
       DBG_VOID_RETURN;
}

Here is the call graph for this function:

Here is the caller graph for this function:


Variable Documentation

Definition at line 1707 of file mysqlnd_result.c.

Definition at line 1677 of file mysqlnd_result.c.

Definition at line 1677 of file mysqlnd_result.c.

Definition at line 1702 of file mysqlnd_result.c.

Definition at line 1702 of file mysqlnd_result.c.

NULL [static]

Definition at line 1677 of file mysqlnd_result.c.