PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - ext/mysqlnd - mysqlnd_ps.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 865 1058 81.8 %
Date: 2014-04-16 Functions: 47 50 94.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   +----------------------------------------------------------------------+
       3             :   | PHP Version 5                                                        |
       4             :   +----------------------------------------------------------------------+
       5             :   | Copyright (c) 2006-2014 The PHP Group                                |
       6             :   +----------------------------------------------------------------------+
       7             :   | This source file is subject to version 3.01 of the PHP license,      |
       8             :   | that is bundled with this package in the file LICENSE, and is        |
       9             :   | available through the world-wide-web at the following url:           |
      10             :   | http://www.php.net/license/3_01.txt                                  |
      11             :   | If you did not receive a copy of the PHP license and are unable to   |
      12             :   | obtain it through the world-wide-web, please send a note to          |
      13             :   | license@php.net so we can mail you a copy immediately.               |
      14             :   +----------------------------------------------------------------------+
      15             :   | Authors: Andrey Hristov <andrey@mysql.com>                           |
      16             :   |          Ulf Wendel <uwendel@mysql.com>                              |
      17             :   |          Georg Richter <georg@mysql.com>                             |
      18             :   +----------------------------------------------------------------------+
      19             : */
      20             : 
      21             : /* $Id$ */
      22             : #include "php.h"
      23             : #include "mysqlnd.h"
      24             : #include "mysqlnd_wireprotocol.h"
      25             : #include "mysqlnd_priv.h"
      26             : #include "mysqlnd_result.h"
      27             : #include "mysqlnd_result_meta.h"
      28             : #include "mysqlnd_statistics.h"
      29             : #include "mysqlnd_debug.h"
      30             : #include "mysqlnd_block_alloc.h"
      31             : #include "mysqlnd_ext_plugin.h"
      32             : 
      33             : #define MYSQLND_SILENT
      34             : 
      35             : 
      36             : const char * const mysqlnd_not_bound_as_blob = "Can't send long data for non-string/non-binary data types";
      37             : const char * const mysqlnd_stmt_not_prepared = "Statement not prepared";
      38             : 
      39             : /* Exported by mysqlnd_ps_codec.c */
      40             : enum_func_status mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, zend_bool * free_buffer TSRMLS_DC);
      41             : enum_func_status mysqlnd_stmt_execute_batch_generate_request(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, zend_bool * free_buffer TSRMLS_DC);
      42             : 
      43             : static void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC);
      44             : static void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, unsigned int param_no TSRMLS_DC);
      45             : 
      46             : 
      47             : /* {{{ mysqlnd_stmt::store_result */
      48             : static MYSQLND_RES *
      49         475 : MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s TSRMLS_DC)
      50             : {
      51         475 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
      52             :         enum_func_status ret;
      53             :         MYSQLND_CONN_DATA * conn;
      54             :         MYSQLND_RES * result;
      55             : 
      56         475 :         DBG_ENTER("mysqlnd_stmt::store_result");
      57         475 :         if (!stmt || !stmt->conn || !stmt->result) {
      58           0 :                 DBG_RETURN(NULL);
      59             :         }
      60         475 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
      61             : 
      62         475 :         conn = stmt->conn;
      63             : 
      64             :         /* be compliant with libmysql - NULL will turn */
      65         475 :         if (!stmt->field_count) {
      66           0 :                 DBG_RETURN(NULL);
      67             :         }
      68             : 
      69         475 :         if (stmt->cursor_exists) {
      70             :                 /* Silently convert buffered to unbuffered, for now */
      71           0 :                 DBG_RETURN(s->m->use_result(s TSRMLS_CC));
      72             :         }
      73             : 
      74             :         /* Nothing to store for UPSERT/LOAD DATA*/
      75         947 :         if (CONN_GET_STATE(conn) != CONN_FETCHING_DATA ||
      76         472 :                 stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE)
      77             :         {
      78           3 :                 SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
      79             :                                                  UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
      80           3 :                 DBG_RETURN(NULL);
      81             :         }
      82             : 
      83         472 :         stmt->default_rset_handler = s->m->store_result;
      84             : 
      85         472 :         SET_EMPTY_ERROR(*stmt->error_info);
      86         472 :         SET_EMPTY_ERROR(*conn->error_info);
      87         472 :         MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_PS_BUFFERED_SETS);
      88             : 
      89         472 :         result = stmt->result;
      90         472 :         result->type                 = MYSQLND_RES_PS_BUF;
      91             : /*      result->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol; */
      92             : 
      93         472 :         result->stored_data  = (MYSQLND_RES_BUFFERED *) mysqlnd_result_buffered_zval_init(result->field_count, TRUE, result->persistent TSRMLS_CC);
      94         472 :         if (!result->stored_data) {
      95           0 :                 SET_OOM_ERROR(*conn->error_info);
      96           0 :                 DBG_RETURN(NULL);
      97             :         }
      98             : 
      99         472 :         ret = result->m.store_result_fetch_data(conn, result, result->meta, &result->stored_data->row_buffers, TRUE TSRMLS_CC);
     100             : 
     101         472 :         result->stored_data->m.fetch_row = mysqlnd_stmt_fetch_row_buffered;
     102             : 
     103         472 :         if (PASS == ret) {
     104             :                 /* Overflow ? */
     105         472 :                 if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_ZVAL) {
     106         472 :                         MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data;
     107         472 :                         if (result->stored_data->row_count) {
     108             :                                 /* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
     109         457 :                                 if (result->stored_data->row_count * result->meta->field_count * sizeof(zval *) > SIZE_MAX) {
     110             :                                         SET_OOM_ERROR(*conn->error_info);
     111             :                                         DBG_RETURN(NULL);
     112             :                                 }
     113             :                                 /* if pecalloc is used valgrind barks gcc version 4.3.1 20080507 (prerelease) [gcc-4_3-branch revision 135036] (SUSE Linux) */
     114         457 :                                 set->data = mnd_emalloc((size_t)(result->stored_data->row_count * result->meta->field_count * sizeof(zval *)));
     115         457 :                                 if (!set->data) {
     116           0 :                                         SET_OOM_ERROR(*conn->error_info);
     117           0 :                                         DBG_RETURN(NULL);
     118             :                                 }
     119         457 :                                 memset(set->data, 0, (size_t)(result->stored_data->row_count * result->meta->field_count * sizeof(zval *)));
     120             :                         }
     121             :                         /* Position at the first row */
     122         472 :                         set->data_cursor = set->data;
     123           0 :                 } else if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_ZVAL) {
     124             :                         /*TODO*/
     125             :                 }
     126             : 
     127             :                 /* libmysql API docs say it should be so for SELECT statements */
     128         472 :                 stmt->upsert_status->affected_rows = stmt->result->stored_data->row_count;
     129             : 
     130         472 :                 stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;
     131             :         } else {
     132           0 :                 COPY_CLIENT_ERROR(*conn->error_info, result->stored_data->error_info);
     133           0 :                 stmt->result->m.free_result_contents(stmt->result TSRMLS_CC);
     134           0 :                 mnd_efree(stmt->result);
     135           0 :                 stmt->result = NULL;
     136           0 :                 stmt->state = MYSQLND_STMT_PREPARED;
     137             :         }
     138             : 
     139         472 :         DBG_RETURN(result);
     140             : }
     141             : /* }}} */
     142             : 
     143             : 
     144             : /* {{{ mysqlnd_stmt::get_result */
     145             : static MYSQLND_RES *
     146         584 : MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s TSRMLS_DC)
     147             : {
     148         584 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
     149             :         MYSQLND_CONN_DATA * conn;
     150             :         MYSQLND_RES *result;
     151             : 
     152         584 :         DBG_ENTER("mysqlnd_stmt::get_result");
     153         584 :         if (!stmt || !stmt->conn || !stmt->result) {
     154           0 :                 DBG_RETURN(NULL);
     155             :         }
     156         584 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
     157             : 
     158         584 :         conn = stmt->conn;
     159             : 
     160             :         /* be compliant with libmysql - NULL will turn */
     161         584 :         if (!stmt->field_count) {
     162           0 :                 DBG_RETURN(NULL);
     163             :         }
     164             : 
     165         584 :         if (stmt->cursor_exists) {
     166             :                 /* Silently convert buffered to unbuffered, for now */
     167           0 :                 DBG_RETURN(s->m->use_result(s TSRMLS_CC));
     168             :         }
     169             : 
     170             :         /* Nothing to store for UPSERT/LOAD DATA*/
     171         584 :         if (CONN_GET_STATE(conn) != CONN_FETCHING_DATA || stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE) {
     172           6 :                 SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
     173             :                                                  UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
     174           6 :                 DBG_RETURN(NULL);
     175             :         }
     176             : 
     177         578 :         SET_EMPTY_ERROR(*stmt->error_info);
     178         578 :         SET_EMPTY_ERROR(*conn->error_info);
     179         578 :         MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS);
     180             : 
     181             :         do {
     182         578 :                 result = conn->m->result_init(stmt->result->field_count, stmt->persistent TSRMLS_CC);
     183         578 :                 if (!result) {
     184           0 :                         SET_OOM_ERROR(*conn->error_info);
     185           0 :                         break;
     186             :                 }
     187             : 
     188         578 :                 result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC);
     189         578 :                 if (!result->meta) {
     190           0 :                         SET_OOM_ERROR(*conn->error_info);
     191           0 :                         break;
     192             :                 }
     193             : 
     194         578 :                 if ((result = result->m.store_result(result, conn, MYSQLND_STORE_PS | MYSQLND_STORE_NO_COPY TSRMLS_CC))) {
     195         578 :                         stmt->upsert_status->affected_rows = result->stored_data->row_count;
     196         578 :                         stmt->state = MYSQLND_STMT_PREPARED;
     197         578 :                         result->type = MYSQLND_RES_PS_BUF;
     198             :                 } else {
     199           0 :                         COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
     200           0 :                         stmt->state = MYSQLND_STMT_PREPARED;
     201           0 :                         break;
     202             :                 }
     203         578 :                 DBG_RETURN(result);
     204             :         } while (0);
     205             : 
     206           0 :         if (result) {
     207           0 :                 result->m.free_result(result, TRUE TSRMLS_CC);
     208             :         }
     209           0 :         DBG_RETURN(NULL);
     210             : }
     211             : /* }}} */
     212             : 
     213             : 
     214             : /* {{{ mysqlnd_stmt::more_results */
     215             : static zend_bool
     216        6584 : MYSQLND_METHOD(mysqlnd_stmt, more_results)(const MYSQLND_STMT * s TSRMLS_DC)
     217             : {
     218        6584 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
     219        6584 :         DBG_ENTER("mysqlnd_stmt::more_results");
     220             :         /* (conn->state == CONN_NEXT_RESULT_PENDING) too */
     221        6584 :         DBG_RETURN((stmt && stmt->conn && (stmt->conn->m->get_server_status(stmt->conn TSRMLS_CC) & SERVER_MORE_RESULTS_EXISTS))?
     222             :                                                                         TRUE:
     223             :                                                                         FALSE);
     224             : }
     225             : /* }}} */
     226             : 
     227             : 
     228             : /* {{{ mysqlnd_stmt::next_result */
     229             : static enum_func_status
     230          19 : MYSQLND_METHOD(mysqlnd_stmt, next_result)(MYSQLND_STMT * s TSRMLS_DC)
     231             : {
     232          19 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
     233             :         MYSQLND_CONN_DATA * conn;
     234             : 
     235          19 :         DBG_ENTER("mysqlnd_stmt::next_result");
     236          19 :         if (!stmt || !stmt->conn || !stmt->result) {
     237           1 :                 DBG_RETURN(FAIL);
     238             :         }
     239          18 :         conn = stmt->conn;
     240          18 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
     241             : 
     242          18 :         if (CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING || !(conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS)) {
     243           1 :                 DBG_RETURN(FAIL);
     244             :         }
     245             : 
     246          17 :         DBG_INF_FMT("server_status=%u cursor=%u", stmt->upsert_status->server_status, stmt->upsert_status->server_status & SERVER_STATUS_CURSOR_EXISTS);
     247             : 
     248             :         /* Free space for next result */
     249          17 :         s->m->free_stmt_content(s TSRMLS_CC);
     250             :         {
     251          17 :                 enum_func_status ret = s->m->parse_execute_response(s TSRMLS_CC);
     252          17 :                 DBG_RETURN(ret);
     253             :         }
     254             : }
     255             : /* }}} */
     256             : 
     257             : 
     258             : /* {{{ mysqlnd_stmt_skip_metadata */
     259             : static enum_func_status
     260         825 : mysqlnd_stmt_skip_metadata(MYSQLND_STMT * s TSRMLS_DC)
     261             : {
     262         825 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
     263             :         /* Follows parameter metadata, we have just to skip it, as libmysql does */
     264         825 :         unsigned int i = 0;
     265         825 :         enum_func_status ret = FAIL;
     266             :         MYSQLND_PACKET_RES_FIELD * field_packet;
     267             : 
     268         825 :         DBG_ENTER("mysqlnd_stmt_skip_metadata");
     269         825 :         if (!stmt || !stmt->conn || !stmt->conn->protocol) {
     270           0 :                 DBG_RETURN(FAIL);
     271             :         }
     272         825 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
     273             : 
     274         825 :         field_packet = stmt->conn->protocol->m.get_result_field_packet(stmt->conn->protocol, FALSE TSRMLS_CC);
     275         825 :         if (!field_packet) {
     276           0 :                 SET_OOM_ERROR(*stmt->error_info);
     277           0 :                 SET_OOM_ERROR(*stmt->conn->error_info);
     278             :         } else {
     279         825 :                 ret = PASS;
     280         825 :                 field_packet->skip_parsing = TRUE;
     281      135239 :                 for (;i < stmt->param_count; i++) {
     282      134414 :                         if (FAIL == PACKET_READ(field_packet, stmt->conn)) {
     283           0 :                                 ret = FAIL;
     284           0 :                                 break;
     285             :                         }
     286             :                 }
     287         825 :                 PACKET_FREE(field_packet);
     288             :         }
     289             : 
     290         825 :         DBG_RETURN(ret);
     291             : }
     292             : /* }}} */
     293             : 
     294             : 
     295             : /* {{{ mysqlnd_stmt_read_prepare_response */
     296             : static enum_func_status
     297        4242 : mysqlnd_stmt_read_prepare_response(MYSQLND_STMT * s TSRMLS_DC)
     298             : {
     299        4242 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
     300             :         MYSQLND_PACKET_PREPARE_RESPONSE * prepare_resp;
     301        4242 :         enum_func_status ret = FAIL;
     302             : 
     303        4242 :         DBG_ENTER("mysqlnd_stmt_read_prepare_response");
     304        4242 :         if (!stmt || !stmt->conn || !stmt->conn->protocol) {
     305           0 :                 DBG_RETURN(FAIL);
     306             :         }
     307        4242 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
     308             : 
     309        4242 :         prepare_resp = stmt->conn->protocol->m.get_prepare_response_packet(stmt->conn->protocol, FALSE TSRMLS_CC);
     310        4242 :         if (!prepare_resp) {
     311           0 :                 SET_OOM_ERROR(*stmt->error_info);
     312           0 :                 SET_OOM_ERROR(*stmt->conn->error_info);
     313           0 :                 goto done;
     314             :         }
     315             : 
     316        4242 :         if (FAIL == PACKET_READ(prepare_resp, stmt->conn)) {
     317           1 :                 goto done;
     318             :         }
     319             : 
     320        4241 :         if (0xFF == prepare_resp->error_code) {
     321          26 :                 COPY_CLIENT_ERROR(*stmt->error_info, prepare_resp->error_info);
     322          26 :                 COPY_CLIENT_ERROR(*stmt->conn->error_info, prepare_resp->error_info);
     323          26 :                 goto done;
     324             :         }
     325        4215 :         ret = PASS;
     326        4215 :         stmt->stmt_id = prepare_resp->stmt_id;
     327        4215 :         stmt->warning_count = stmt->conn->upsert_status->warning_count = prepare_resp->warning_count;
     328        4215 :         stmt->field_count = stmt->conn->field_count = prepare_resp->field_count;
     329        4215 :         stmt->param_count = prepare_resp->param_count;
     330        4215 :         stmt->upsert_status->affected_rows = 0; /* be like libmysql */
     331             : done:
     332        4242 :         PACKET_FREE(prepare_resp);
     333             : 
     334        4242 :         DBG_RETURN(ret);
     335             : }
     336             : /* }}} */
     337             : 
     338             : 
     339             : /* {{{ mysqlnd_stmt_prepare_read_eof */
     340             : static enum_func_status
     341        3253 : mysqlnd_stmt_prepare_read_eof(MYSQLND_STMT * s TSRMLS_DC)
     342             : {
     343        3253 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
     344             :         MYSQLND_PACKET_EOF * fields_eof;
     345        3253 :         enum_func_status ret = FAIL;
     346             : 
     347        3253 :         DBG_ENTER("mysqlnd_stmt_prepare_read_eof");
     348        3253 :         if (!stmt || !stmt->conn || !stmt->conn->protocol) {
     349           0 :                 DBG_RETURN(FAIL);
     350             :         }
     351        3253 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
     352             : 
     353        3253 :         fields_eof = stmt->conn->protocol->m.get_eof_packet(stmt->conn->protocol, FALSE TSRMLS_CC);
     354        3253 :         if (!fields_eof) {
     355           0 :                 SET_OOM_ERROR(*stmt->error_info);
     356           0 :                 SET_OOM_ERROR(*stmt->conn->error_info);
     357             :         } else {
     358        3253 :                 if (FAIL == (ret = PACKET_READ(fields_eof, stmt->conn))) {
     359           0 :                         if (stmt->result) {
     360           0 :                                 stmt->result->m.free_result_contents(stmt->result TSRMLS_CC);
     361           0 :                                 mnd_efree(stmt->result);
     362           0 :                                 memset(stmt, 0, sizeof(MYSQLND_STMT_DATA));
     363           0 :                                 stmt->state = MYSQLND_STMT_INITTED;
     364             :                         }
     365             :                 } else {
     366        3253 :                         stmt->upsert_status->server_status = fields_eof->server_status;
     367        3253 :                         stmt->upsert_status->warning_count = fields_eof->warning_count;
     368        3253 :                         stmt->state = MYSQLND_STMT_PREPARED;
     369             :                 }
     370        3253 :                 PACKET_FREE(fields_eof);
     371             :         }
     372             : 
     373        3253 :         DBG_RETURN(ret);
     374             : }
     375             : /* }}} */
     376             : 
     377             : 
     378             : /* {{{ mysqlnd_stmt::prepare */
     379             : static enum_func_status
     380        4246 : MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const s, const char * const query, unsigned int query_len TSRMLS_DC)
     381             : {
     382        4246 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
     383        4246 :         MYSQLND_STMT * s_to_prepare = s;
     384        4246 :         MYSQLND_STMT_DATA * stmt_to_prepare = stmt;
     385             : 
     386        4246 :         DBG_ENTER("mysqlnd_stmt::prepare");
     387        4246 :         if (!stmt || !stmt->conn) {
     388           0 :                 DBG_RETURN(FAIL);
     389             :         }
     390        4246 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
     391        4246 :         DBG_INF_FMT("query=%s", query);
     392             : 
     393        4246 :         SET_ERROR_AFF_ROWS(stmt);
     394        4246 :         SET_ERROR_AFF_ROWS(stmt->conn);
     395             : 
     396        4246 :         SET_EMPTY_ERROR(*stmt->error_info);
     397        4246 :         SET_EMPTY_ERROR(*stmt->conn->error_info);
     398             : 
     399        4246 :         if (stmt->state > MYSQLND_STMT_INITTED) {
     400             :                 /* See if we have to clean the wire */
     401        2424 :                 if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
     402             :                         /* Do implicit use_result and then flush the result */
     403           1 :                         stmt->default_rset_handler = s->m->use_result;
     404           1 :                         stmt->default_rset_handler(s TSRMLS_CC);
     405             :                 }
     406             :                 /* No 'else' here please :) */
     407        2424 :                 if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE && stmt->result) {
     408         954 :                         stmt->result->m.skip_result(stmt->result TSRMLS_CC);
     409             :                 }
     410             :                 /*
     411             :                   Create a new test statement, which we will prepare, but if anything
     412             :                   fails, we will scrap it.
     413             :                 */
     414        2424 :                 s_to_prepare = stmt->conn->m->stmt_init(stmt->conn TSRMLS_CC);
     415        2424 :                 if (!s_to_prepare) {
     416           0 :                         goto fail;
     417             :                 }
     418        2424 :                 stmt_to_prepare = s_to_prepare->data;
     419             :         }
     420             : 
     421        8488 :         if (FAIL == stmt_to_prepare->conn->m->simple_command(stmt_to_prepare->conn, COM_STMT_PREPARE, (const zend_uchar *) query, query_len, PROT_LAST, FALSE, TRUE TSRMLS_CC) ||
     422        4242 :                 FAIL == mysqlnd_stmt_read_prepare_response(s_to_prepare TSRMLS_CC))
     423             :         {
     424             :                 goto fail;
     425             :         }
     426             : 
     427        4215 :         if (stmt_to_prepare->param_count) {
     428        1650 :                 if (FAIL == mysqlnd_stmt_skip_metadata(s_to_prepare TSRMLS_CC) ||
     429         825 :                         FAIL == mysqlnd_stmt_prepare_read_eof(s_to_prepare TSRMLS_CC))
     430             :                 {
     431             :                         goto fail;
     432             :                 }
     433             :         }
     434             : 
     435             :         /*
     436             :           Read metadata only if there is actual result set.
     437             :           Beware that SHOW statements bypass the PS framework and thus they send
     438             :           no metadata at prepare.
     439             :         */
     440        4215 :         if (stmt_to_prepare->field_count) {
     441        2428 :                 MYSQLND_RES * result = stmt->conn->m->result_init(stmt_to_prepare->field_count, stmt_to_prepare->persistent TSRMLS_CC);
     442        2428 :                 if (!result) {
     443           0 :                         SET_OOM_ERROR(*stmt->conn->error_info);
     444           0 :                         goto fail;
     445             :                 }
     446             :                 /* Allocate the result now as it is needed for the reading of metadata */
     447        2428 :                 stmt_to_prepare->result = result; 
     448             : 
     449        2428 :                 result->conn = stmt_to_prepare->conn->m->get_reference(stmt_to_prepare->conn TSRMLS_CC);
     450             : 
     451        2428 :                 result->type = MYSQLND_RES_PS_BUF;
     452             : 
     453        4856 :                 if (FAIL == result->m.read_result_metadata(result, stmt_to_prepare->conn TSRMLS_CC) ||
     454        2428 :                         FAIL == mysqlnd_stmt_prepare_read_eof(s_to_prepare TSRMLS_CC))
     455             :                 {
     456             :                         goto fail;
     457             :                 }
     458             :         }
     459             : 
     460        4215 :         if (stmt_to_prepare != stmt) {
     461             :                 /* swap */
     462        2421 :                 size_t real_size = sizeof(MYSQLND_STMT) + mysqlnd_plugin_count() * sizeof(void *);
     463        2421 :                 char * tmp_swap = mnd_malloc(real_size);
     464        2421 :                 memcpy(tmp_swap, s, real_size);
     465        2421 :                 memcpy(s, s_to_prepare, real_size);
     466        2421 :                 memcpy(s_to_prepare, tmp_swap, real_size);
     467        2421 :                 mnd_free(tmp_swap);
     468             :                 {
     469        2421 :                         MYSQLND_STMT_DATA * tmp_swap_data = stmt_to_prepare;
     470        2421 :                         stmt_to_prepare = stmt;
     471        2421 :                         stmt = tmp_swap_data;
     472             :                 }
     473        2421 :                 s_to_prepare->m->dtor(s_to_prepare, TRUE TSRMLS_CC);
     474             :         }
     475        4215 :         stmt->state = MYSQLND_STMT_PREPARED;
     476        4215 :         DBG_INF("PASS");
     477        4215 :         DBG_RETURN(PASS);
     478             : 
     479             : fail:
     480          31 :         if (stmt_to_prepare != stmt && s_to_prepare) {
     481           3 :                 s_to_prepare->m->dtor(s_to_prepare, TRUE TSRMLS_CC);
     482             :         }
     483          31 :         stmt->state = MYSQLND_STMT_INITTED;
     484             : 
     485          31 :         DBG_INF("FAIL");
     486          31 :         DBG_RETURN(FAIL);
     487             : }
     488             : /* }}} */
     489             : 
     490             : 
     491             : /* {{{ mysqlnd_stmt_execute_parse_response */
     492             : static enum_func_status
     493        4906 : mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s TSRMLS_DC)
     494             : {
     495        4906 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
     496             :         enum_func_status ret;
     497             :         MYSQLND_CONN_DATA * conn;
     498             : 
     499        4906 :         DBG_ENTER("mysqlnd_stmt_execute_parse_response");
     500        4906 :         if (!stmt || !stmt->conn) {
     501           0 :                 DBG_RETURN(FAIL);
     502             :         }
     503        4906 :         conn = stmt->conn;
     504        4906 :         CONN_SET_STATE(conn, CONN_QUERY_SENT);
     505             : 
     506        4906 :         ret = mysqlnd_query_read_result_set_header(stmt->conn, s TSRMLS_CC);
     507        4906 :         if (ret == FAIL) {
     508           8 :                 COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
     509           8 :                 memset(stmt->upsert_status, 0, sizeof(*stmt->upsert_status));
     510           8 :                 stmt->upsert_status->affected_rows = conn->upsert_status->affected_rows;
     511           8 :                 if (CONN_GET_STATE(conn) == CONN_QUIT_SENT) {
     512             :                         /* close the statement here, the connection has been closed */
     513             :                 }
     514           8 :                 stmt->state = MYSQLND_STMT_PREPARED;
     515           8 :                 stmt->send_types_to_server = 1;
     516             :         } else {
     517             :                 /*
     518             :                   stmt->send_types_to_server has already been set to 0 in
     519             :                   mysqlnd_stmt_execute_generate_request / mysqlnd_stmt_execute_store_params
     520             :                   In case there is a situation in which binding was done for integer and the
     521             :                   value is > LONG_MAX or < LONG_MIN, there is string conversion and we have
     522             :                   to resend the types. Next execution will also need to resend the type.
     523             :                 */
     524        4898 :                 SET_EMPTY_ERROR(*stmt->error_info);
     525        4898 :                 SET_EMPTY_ERROR(*stmt->conn->error_info);
     526        4898 :                 *stmt->upsert_status = *conn->upsert_status; /* copy status */
     527        4898 :                 stmt->state = MYSQLND_STMT_EXECUTED;
     528        4898 :                 if (conn->last_query_type == QUERY_UPSERT || conn->last_query_type == QUERY_LOAD_LOCAL) {
     529        2585 :                         DBG_INF("PASS");
     530        2585 :                         DBG_RETURN(PASS);
     531             :                 }
     532             : 
     533        2313 :                 stmt->result->type = MYSQLND_RES_PS_BUF;
     534        2313 :                 if (!stmt->result->conn) {
     535             :                         /*
     536             :                           For SHOW we don't create (bypasses PS in server)
     537             :                           a result set at prepare and thus a connection was missing
     538             :                         */
     539          24 :                         stmt->result->conn = stmt->conn->m->get_reference(stmt->conn TSRMLS_CC);
     540             :                 }
     541             : 
     542             :                 /* Update stmt->field_count as SHOW sets it to 0 at prepare */
     543        2313 :                 stmt->field_count = stmt->result->field_count = conn->field_count;
     544        2313 :                 if (stmt->result->stored_data) {
     545           0 :                         stmt->result->stored_data->lengths = NULL;
     546        2313 :                 } else if (stmt->result->unbuf) {
     547           0 :                         stmt->result->unbuf->lengths = NULL;
     548             :                 }
     549        2313 :                 if (stmt->field_count) {
     550        2313 :                         stmt->state = MYSQLND_STMT_WAITING_USE_OR_STORE;
     551             :                         /*
     552             :                           We need to set this because the user might not call
     553             :                           use_result() or store_result() and we should be able to scrap the
     554             :                           data on the line, if he just decides to close the statement.
     555             :                         */
     556        2313 :                         DBG_INF_FMT("server_status=%u cursor=%u", stmt->upsert_status->server_status,
     557        2313 :                                                 stmt->upsert_status->server_status & SERVER_STATUS_CURSOR_EXISTS);
     558             : 
     559        2313 :                         if (stmt->upsert_status->server_status & SERVER_STATUS_CURSOR_EXISTS) {
     560           4 :                                 DBG_INF("cursor exists");
     561           4 :                                 stmt->cursor_exists = TRUE;
     562           4 :                                 CONN_SET_STATE(conn, CONN_READY);
     563             :                                 /* Only cursor read */
     564           4 :                                 stmt->default_rset_handler = s->m->use_result;
     565           4 :                                 DBG_INF("use_result");
     566        2309 :                         } else if (stmt->flags & CURSOR_TYPE_READ_ONLY) {
     567           0 :                                 DBG_INF("asked for cursor but got none");
     568             :                                 /*
     569             :                                   We have asked for CURSOR but got no cursor, because the condition
     570             :                                   above is not fulfilled. Then...
     571             : 
     572             :                                   This is a single-row result set, a result set with no rows, EXPLAIN,
     573             :                                   SHOW VARIABLES, or some other command which either a) bypasses the
     574             :                                   cursors framework in the server and writes rows directly to the
     575             :                                   network or b) is more efficient if all (few) result set rows are
     576             :                                   precached on client and server's resources are freed.
     577             :                                 */
     578             :                                 /* preferred is buffered read */
     579           0 :                                 stmt->default_rset_handler = s->m->store_result;
     580           0 :                                 DBG_INF("store_result");
     581             :                         } else {
     582        2309 :                                 DBG_INF("no cursor");
     583             :                                 /* preferred is unbuffered read */
     584        2309 :                                 stmt->default_rset_handler = s->m->use_result;
     585        2309 :                                 DBG_INF("use_result");
     586             :                         }
     587             :                 }
     588             :         }
     589             : #ifndef MYSQLND_DONT_SKIP_OUT_PARAMS_RESULTSET
     590        2321 :         if (stmt->upsert_status->server_status & SERVER_PS_OUT_PARAMS) {
     591           0 :                 s->m->free_stmt_content(s TSRMLS_CC);
     592           0 :                 DBG_INF("PS OUT Variable RSet, skipping");
     593             :                 /* OUT params result set. Skip for now to retain compatibility */
     594           0 :                 ret = mysqlnd_stmt_execute_parse_response(s TSRMLS_CC);
     595             :         }
     596             : #endif
     597             : 
     598        2321 :         DBG_INF(ret == PASS? "PASS":"FAIL");
     599        2321 :         DBG_RETURN(ret);
     600             : }
     601             : /* }}} */
     602             : 
     603             : 
     604             : /* {{{ mysqlnd_stmt::execute */
     605             : static enum_func_status
     606        4896 : MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const s TSRMLS_DC)
     607             : {
     608        4896 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
     609             :         enum_func_status ret;
     610             :         MYSQLND_CONN_DATA * conn;
     611        4896 :         zend_uchar *request = NULL;
     612             :         size_t          request_len;
     613             :         zend_bool       free_request;
     614             : 
     615        4896 :         DBG_ENTER("mysqlnd_stmt::execute");
     616        4896 :         if (!stmt || !stmt->conn) {
     617           0 :                 DBG_RETURN(FAIL);
     618             :         }
     619        4896 :         conn = stmt->conn;
     620        4896 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
     621             : 
     622        4896 :         SET_ERROR_AFF_ROWS(stmt);
     623        4896 :         SET_ERROR_AFF_ROWS(stmt->conn);
     624             : 
     625        7193 :         if (stmt->result && stmt->state >= MYSQLND_STMT_PREPARED && stmt->field_count) {
     626             :                 /*
     627             :                   We don need to copy the data from the buffers which we will clean.
     628             :                   Because it has already been copied. See
     629             :                   #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
     630             :                 */
     631             : #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
     632             :                 if (stmt->result_bind &&
     633             :                         stmt->result_zvals_separated_once == TRUE && 
     634             :                         stmt->state >= MYSQLND_STMT_USER_FETCHING)
     635             :                 {
     636             :                         /*
     637             :                           We need to copy the data from the buffers which we will clean.
     638             :                           The bound variables point to them only if the user has started
     639             :                           to fetch data (MYSQLND_STMT_USER_FETCHING).
     640             :                           We need to check 'result_zvals_separated_once' or we will leak
     641             :                           in the following scenario
     642             :                           prepare("select 1 from dual");
     643             :                           execute();
     644             :                           fetch(); <-- no binding, but that's not a problem
     645             :                           bind_result();
     646             :                           execute(); <-- here we will leak because we separate without need
     647             :                         */
     648             :                         unsigned int i;
     649             :                         for (i = 0; i < stmt->field_count; i++) {
     650             :                                 if (stmt->result_bind[i].bound == TRUE) {
     651             :                                         zval_copy_ctor(stmt->result_bind[i].zv);
     652             :                                 }
     653             :                         }
     654             :                 }
     655             : #endif
     656             : 
     657        2297 :                 s->m->flush(s TSRMLS_CC);
     658             : 
     659             :                 /*
     660             :                   Executed, but the user hasn't started to fetch
     661             :                   This will clean also the metadata, but after the EXECUTE call we will
     662             :                   have it again.
     663             :                 */
     664        2297 :                 stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC);
     665             : 
     666        2297 :                 stmt->state = MYSQLND_STMT_PREPARED;
     667        2599 :         } else if (stmt->state < MYSQLND_STMT_PREPARED) {
     668             :                 /* Only initted - error */
     669           2 :                 SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
     670             :                                                  mysqlnd_out_of_sync);
     671           2 :                 SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
     672           2 :                 DBG_INF("FAIL");
     673           2 :                 DBG_RETURN(FAIL);
     674             :         }
     675             : 
     676        4894 :         if (stmt->param_count) {
     677        1443 :                 unsigned int i, not_bound = 0;
     678        1443 :                 if (!stmt->param_bind) {
     679           1 :                         SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE,
     680             :                                                          "No data supplied for parameters in prepared statement");
     681           1 :                         DBG_INF("FAIL");
     682           1 :                         DBG_RETURN(FAIL);
     683             :                 }
     684      137267 :                 for (i = 0; i < stmt->param_count; i++) {
     685      135825 :                         if (stmt->param_bind[i].zv == NULL) {
     686           0 :                                 not_bound++;
     687             :                         }
     688             :                 }
     689        1442 :                 if (not_bound) {
     690             :                         char * msg;
     691           0 :                         mnd_sprintf(&msg, 0, "No data supplied for %u parameter%s in prepared statement",
     692             :                                                 not_bound, not_bound>1 ?"s":"");
     693           0 :                         SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE, msg);
     694           0 :                         if (msg) {
     695           0 :                                 mnd_sprintf_free(msg);
     696             :                         }
     697           0 :                         DBG_INF("FAIL");
     698           0 :                         DBG_RETURN(FAIL);
     699             :                 }
     700             :         }
     701        4893 :         ret = s->m->generate_execute_request(s, &request, &request_len, &free_request TSRMLS_CC);
     702        4893 :         if (ret == PASS) {
     703             :                 /* support for buffer types should be added here ! */
     704        4893 :                 ret = stmt->conn->m->simple_command(stmt->conn, COM_STMT_EXECUTE, request, request_len,
     705             :                                                                                         PROT_LAST /* we will handle the response packet*/,
     706             :                                                                                         FALSE, FALSE TSRMLS_CC);
     707             :         } else {
     708           0 :                 SET_STMT_ERROR(stmt, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Couldn't generate the request. Possibly OOM.");
     709             :         }
     710             : 
     711        4893 :         if (free_request) {
     712          18 :                 mnd_efree(request);
     713             :         }
     714             : 
     715        4893 :         if (ret == FAIL) {
     716           4 :                 COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
     717           4 :                 DBG_INF("FAIL");
     718           4 :                 DBG_RETURN(FAIL);
     719             :         }
     720        4889 :         stmt->execute_count++;
     721             : 
     722        4889 :         ret = s->m->parse_execute_response(s TSRMLS_CC);
     723             : 
     724        4889 :         DBG_INF_FMT("server_status=%u cursor=%u", stmt->upsert_status->server_status, stmt->upsert_status->server_status & SERVER_STATUS_CURSOR_EXISTS);
     725             : 
     726        4889 :         if (ret == PASS && conn->last_query_type == QUERY_UPSERT && stmt->upsert_status->affected_rows) {
     727        2425 :                 MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_PS, stmt->upsert_status->affected_rows);
     728             :         }
     729        4889 :         DBG_RETURN(ret);
     730             : }
     731             : /* }}} */
     732             : 
     733             : 
     734             : /* {{{ mysqlnd_stmt_fetch_row_buffered */
     735             : enum_func_status
     736        1734 : mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC)
     737             : {
     738        1734 :         MYSQLND_STMT * s = (MYSQLND_STMT *) param;
     739        1734 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
     740        1734 :         const MYSQLND_RES_METADATA * const meta = result->meta;
     741        1734 :         unsigned int field_count = meta->field_count;
     742             : 
     743        1734 :         DBG_ENTER("mysqlnd_stmt_fetch_row_buffered");
     744        1734 :         *fetched_anything = FALSE;
     745        1734 :         DBG_INF_FMT("stmt=%lu", stmt != NULL ? stmt->stmt_id : 0L);
     746             : 
     747             :         /* If we haven't read everything */
     748        1734 :         if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_ZVAL) {
     749        1734 :                 MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data;
     750        5120 :                 if (set->data_cursor &&
     751        1722 :                         (set->data_cursor - set->data) < (result->stored_data->row_count * field_count))
     752             :                 {
     753             :                         /* The user could have skipped binding - don't crash*/
     754        1664 :                         if (stmt->result_bind) {
     755             :                                 unsigned int i;
     756        1664 :                                 zval **current_row = set->data_cursor;
     757             : 
     758        1664 :                                 if (NULL == current_row[0]) {
     759        1664 :                                         uint64_t row_num = (set->data_cursor - set->data) / field_count;
     760        6656 :                                         enum_func_status rc = result->stored_data->m.row_decoder(result->stored_data->row_buffers[row_num],
     761             :                                                                                                         current_row,
     762             :                                                                                                         meta->field_count,
     763        1664 :                                                                                                         meta->fields,
     764        1664 :                                                                                                         result->conn->options->int_and_float_native,
     765        3328 :                                                                                                         result->conn->stats TSRMLS_CC);
     766        1664 :                                         if (PASS != rc) {
     767           0 :                                                 DBG_RETURN(FAIL);
     768             :                                         }
     769        1664 :                                         result->stored_data->initialized_rows++;
     770        1664 :                                         if (stmt->update_max_length) {
     771           0 :                                                 for (i = 0; i < result->field_count; i++) {
     772             :                                                         /*
     773             :                                                           NULL fields are 0 length, 0 is not more than 0
     774             :                                                           String of zero size, definitely can't be the next max_length.
     775             :                                                           Thus for NULL and zero-length we are quite efficient.
     776             :                                                         */
     777           0 :                                                         if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
     778           0 :                                                                 unsigned long len = Z_STRLEN_P(current_row[i]);
     779           0 :                                                                 if (meta->fields[i].max_length < len) {
     780           0 :                                                                         meta->fields[i].max_length = len;
     781             :                                                                 }
     782             :                                                         }
     783             :                                                 }
     784             :                                         }
     785             :                                 }
     786             : 
     787        6067 :                                 for (i = 0; i < result->field_count; i++) {
     788             :                                         /* Clean what we copied last time */
     789             : #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
     790        4403 :                                         if (stmt->result_bind[i].zv) {
     791        4403 :                                                 zval_dtor(stmt->result_bind[i].zv);
     792             :                                         }
     793             : #endif
     794             :                                         /* copy the type */
     795        4403 :                                         if (stmt->result_bind[i].bound == TRUE) {
     796        4403 :                                                 DBG_INF_FMT("i=%u type=%u", i, Z_TYPE_P(current_row[i]));
     797        4403 :                                                 if (Z_TYPE_P(current_row[i]) != IS_NULL) {
     798             :                                                         /*
     799             :                                                           Copy the value.
     800             :                                                           Pre-condition is that the zvals in the result_bind buffer
     801             :                                                           have been  ZVAL_NULL()-ed or to another simple type
     802             :                                                           (int, double, bool but not string). Because of the reference
     803             :                                                           counting the user can't delete the strings the variables point to.
     804             :                                                         */
     805             : 
     806        4395 :                                                         Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(current_row[i]);
     807        4395 :                                                         stmt->result_bind[i].zv->value = current_row[i]->value;
     808             : #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
     809        4395 :                                                         zval_copy_ctor(stmt->result_bind[i].zv);
     810             : #endif
     811             :                                                 } else {
     812           8 :                                                         ZVAL_NULL(stmt->result_bind[i].zv);
     813             :                                                 }
     814             :                                         }
     815             :                                 }
     816             :                         }
     817        1664 :                         set->data_cursor += field_count;
     818        1664 :                         *fetched_anything = TRUE;
     819             :                         /* buffered result sets don't have a connection */
     820        1664 :                         MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_PS_BUF);
     821        1664 :                         DBG_INF("row fetched");
     822             :                 } else {
     823          70 :                         set->data_cursor = NULL;
     824          70 :                         DBG_INF("no more data");
     825             :                 }
     826           0 :         } else if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_C) {
     827             :                 /*TODO*/
     828             :         }
     829        1734 :         DBG_INF("PASS");
     830        1734 :         DBG_RETURN(PASS);
     831             : }
     832             : /* }}} */
     833             : 
     834             : 
     835             : /* {{{ mysqlnd_stmt_fetch_row_unbuffered */
     836             : enum_func_status
     837       19004 : mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC)
     838             : {
     839             :         enum_func_status ret;
     840       19004 :         MYSQLND_STMT * s = (MYSQLND_STMT *) param;
     841       19004 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
     842             :         MYSQLND_PACKET_ROW * row_packet;
     843       19004 :         const MYSQLND_RES_METADATA * const meta = result->meta;
     844             : 
     845       19004 :         DBG_ENTER("mysqlnd_stmt_fetch_row_unbuffered");
     846             : 
     847       19004 :         *fetched_anything = FALSE;
     848             : 
     849       19004 :         if (result->unbuf->eof_reached) {
     850             :                 /* No more rows obviously */
     851           0 :                 DBG_INF("EOF already reached");
     852           0 :                 DBG_RETURN(PASS);
     853             :         }
     854       19004 :         if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) {
     855           8 :                 SET_CLIENT_ERROR(*result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
     856             :                                                  UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
     857           8 :                 DBG_ERR("command out of sync");
     858           8 :                 DBG_RETURN(FAIL);
     859             :         }
     860       18996 :         if (!(row_packet = result->unbuf->row_packet)) {
     861           0 :                 DBG_RETURN(FAIL);
     862             :         }
     863             : 
     864             :         /* Let the row packet fill our buffer and skip additional malloc + memcpy */
     865       18996 :         row_packet->skip_extraction = stmt && stmt->result_bind? FALSE:TRUE;
     866             : 
     867             :         /*
     868             :           If we skip rows (stmt == NULL || stmt->result_bind == NULL) we have to
     869             :           result->unbuf->m.free_last_data() before it. The function returns always true.
     870             :         */
     871       36740 :         if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
     872       17744 :                 unsigned int i, field_count = result->field_count;
     873             : 
     874       17744 :                 if (!row_packet->skip_extraction) {
     875       17584 :                         result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL TSRMLS_CC);
     876             : 
     877       17584 :                         result->unbuf->last_row_data = row_packet->fields;
     878       17584 :                         result->unbuf->last_row_buffer = row_packet->row_buffer;
     879       17584 :                         row_packet->fields = NULL;
     880       17584 :                         row_packet->row_buffer = NULL;
     881             : 
     882       87920 :                         if (PASS != result->unbuf->m.row_decoder(result->unbuf->last_row_buffer,
     883       17584 :                                                                         result->unbuf->last_row_data,
     884             :                                                                         row_packet->field_count,
     885       17584 :                                                                         row_packet->fields_metadata,
     886       17584 :                                                                         result->conn->options->int_and_float_native,
     887       17584 :                                                                         result->conn->stats TSRMLS_CC))
     888             :                         {
     889           0 :                                 DBG_RETURN(FAIL);
     890             :                         }
     891             : 
     892       54713 :                         for (i = 0; i < field_count; i++) {
     893       37129 :                                 if (stmt->result_bind[i].bound == TRUE) {
     894       37129 :                                         zval *data = result->unbuf->last_row_data[i];
     895             :                                         /*
     896             :                                           stmt->result_bind[i].zv has been already destructed
     897             :                                           in result->unbuf->m.free_last_data()
     898             :                                         */
     899             : #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
     900       37129 :                                         zval_dtor(stmt->result_bind[i].zv);
     901             : #endif
     902       37129 :                                         if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data)) ) {
     903       36547 :                                                 if ((Z_TYPE_P(data) == IS_STRING) && (meta->fields[i].max_length < (unsigned long) Z_STRLEN_P(data))) {
     904         623 :                                                         meta->fields[i].max_length = Z_STRLEN_P(data);
     905             :                                                 }
     906       36547 :                                                 stmt->result_bind[i].zv->value = data->value;
     907             :                                                 /* copied data, thus also the ownership. Thus null data */
     908       36547 :                                                 ZVAL_NULL(data);
     909             :                                         }
     910             :                                 }
     911             :                         }
     912       17584 :                         MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_UNBUF);
     913             :                 } else {
     914         160 :                         DBG_INF("skipping extraction");
     915             :                         /*
     916             :                           Data has been allocated and usually result->unbuf->m.free_last_data()
     917             :                           frees it but we can't call this function as it will cause problems with
     918             :                           the bound variables. Thus we need to do part of what it does or Zend will
     919             :                           report leaks.
     920             :                         */
     921         160 :                         row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC);
     922         160 :                         row_packet->row_buffer = NULL;
     923             :                 }
     924             : 
     925       17744 :                 result->unbuf->row_count++;
     926       17744 :                 *fetched_anything = TRUE;
     927        1252 :         } else if (ret == FAIL) {
     928           0 :                 if (row_packet->error_info.error_no) {
     929           0 :                         COPY_CLIENT_ERROR(*stmt->conn->error_info, row_packet->error_info);
     930           0 :                         COPY_CLIENT_ERROR(*stmt->error_info, row_packet->error_info);
     931             :                 }
     932           0 :                 CONN_SET_STATE(result->conn, CONN_READY);
     933           0 :                 result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
     934        1252 :         } else if (row_packet->eof) {
     935        1252 :                 DBG_INF("EOF");
     936             :                 /* Mark the connection as usable again */
     937        1252 :                 result->unbuf->eof_reached = TRUE;
     938        1252 :                 memset(result->conn->upsert_status, 0, sizeof(*result->conn->upsert_status));
     939        1252 :                 result->conn->upsert_status->warning_count = row_packet->warning_count;
     940        1252 :                 result->conn->upsert_status->server_status = row_packet->server_status;
     941             :                 /*
     942             :                   result->row_packet will be cleaned when
     943             :                   destroying the result object
     944             :                 */
     945        1252 :                 if (result->conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
     946           5 :                         CONN_SET_STATE(result->conn, CONN_NEXT_RESULT_PENDING);
     947             :                 } else {
     948        1247 :                         CONN_SET_STATE(result->conn, CONN_READY);
     949             :                 }
     950             :         }
     951             : 
     952       18996 :         DBG_INF_FMT("ret=%s fetched_anything=%u", ret == PASS? "PASS":"FAIL", *fetched_anything);
     953       18996 :         DBG_RETURN(ret);
     954             : }
     955             : /* }}} */
     956             : 
     957             : 
     958             : /* {{{ mysqlnd_stmt::use_result */
     959             : static MYSQLND_RES *
     960        1263 : MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s TSRMLS_DC)
     961             : {
     962        1263 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
     963             :         MYSQLND_RES * result;
     964             :         MYSQLND_CONN_DATA * conn;
     965             : 
     966        1263 :         DBG_ENTER("mysqlnd_stmt::use_result");
     967        1263 :         if (!stmt || !stmt->conn || !stmt->result) {
     968           0 :                 DBG_RETURN(NULL);
     969             :         }
     970        1263 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
     971             : 
     972        1263 :         conn = stmt->conn;
     973             : 
     974        6309 :         if (!stmt->field_count ||
     975        2522 :                 (!stmt->cursor_exists && CONN_GET_STATE(conn) != CONN_FETCHING_DATA) ||
     976        1264 :                 (stmt->cursor_exists && CONN_GET_STATE(conn) != CONN_READY) ||
     977        1260 :                 (stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE))
     978             :         {
     979           3 :                 SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
     980             :                                                  UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
     981           3 :                 DBG_ERR("command out of sync");
     982           3 :                 DBG_RETURN(NULL);
     983             :         }
     984             : 
     985        1260 :         SET_EMPTY_ERROR(*stmt->error_info);
     986             : 
     987        1260 :         MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_PS_UNBUFFERED_SETS);
     988        1260 :         result = stmt->result;
     989             : 
     990        1260 :         result->m.use_result(stmt->result, TRUE TSRMLS_CC);
     991        1260 :         result->unbuf->m.fetch_row        = stmt->cursor_exists? mysqlnd_fetch_stmt_row_cursor:
     992             :                                                                                            mysqlnd_stmt_fetch_row_unbuffered;
     993        1260 :         stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;
     994             : 
     995        1260 :         DBG_INF_FMT("%p", result);
     996        1260 :         DBG_RETURN(result);
     997             : }
     998             : /* }}} */
     999             : 
    1000             : 
    1001             : #define STMT_ID_LENGTH 4
    1002             : 
    1003             : /* {{{ mysqlnd_fetch_row_cursor */
    1004             : enum_func_status
    1005          30 : mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC)
    1006             : {
    1007             :         enum_func_status ret;
    1008          30 :         MYSQLND_STMT * s = (MYSQLND_STMT *) param;
    1009          30 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1010             :         zend_uchar buf[STMT_ID_LENGTH /* statement id */ + 4 /* number of rows to fetch */];
    1011             :         MYSQLND_PACKET_ROW * row_packet;
    1012             : 
    1013          30 :         DBG_ENTER("mysqlnd_fetch_stmt_row_cursor");
    1014             : 
    1015          30 :         if (!stmt || !stmt->conn || !result || !result->conn || !result->unbuf) {
    1016           4 :                 DBG_ERR("no statement");
    1017           4 :                 DBG_RETURN(FAIL);
    1018             :         }
    1019             : 
    1020          26 :         DBG_INF_FMT("stmt=%lu flags=%u", stmt->stmt_id, flags);
    1021             : 
    1022          26 :         if (stmt->state < MYSQLND_STMT_USER_FETCHING) {
    1023             :                 /* Only initted - error */
    1024           0 :                 SET_CLIENT_ERROR(*stmt->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
    1025             :                                                 mysqlnd_out_of_sync);
    1026           0 :                 DBG_ERR("command out of sync");
    1027           0 :                 DBG_RETURN(FAIL);
    1028             :         }
    1029          26 :         if (!(row_packet = result->unbuf->row_packet)) {
    1030           0 :                 DBG_RETURN(FAIL);
    1031             :         }
    1032             : 
    1033          26 :         SET_EMPTY_ERROR(*stmt->error_info);
    1034          26 :         SET_EMPTY_ERROR(*stmt->conn->error_info);
    1035             : 
    1036          26 :         int4store(buf, stmt->stmt_id);
    1037          26 :         int4store(buf + STMT_ID_LENGTH, 1); /* for now fetch only one row */
    1038             : 
    1039          26 :         if (FAIL == stmt->conn->m->simple_command(stmt->conn, COM_STMT_FETCH, buf, sizeof(buf),
    1040             :                                                                                           PROT_LAST /* we will handle the response packet*/,
    1041             :                                                                                           FALSE, TRUE TSRMLS_CC)) {
    1042           0 :                 COPY_CLIENT_ERROR(*stmt->error_info, *stmt->conn->error_info);
    1043           0 :                 DBG_RETURN(FAIL);
    1044             :         }
    1045             : 
    1046          26 :         row_packet->skip_extraction = stmt->result_bind? FALSE:TRUE;
    1047             : 
    1048          26 :         memset(stmt->upsert_status, 0, sizeof(*stmt->upsert_status));
    1049          50 :         if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
    1050          24 :                 const MYSQLND_RES_METADATA * const meta = result->meta;
    1051          24 :                 unsigned int i, field_count = result->field_count;
    1052             : 
    1053          24 :                 if (!row_packet->skip_extraction) {
    1054          24 :                         result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL TSRMLS_CC);
    1055             : 
    1056          24 :                         result->unbuf->last_row_data = row_packet->fields;
    1057          24 :                         result->unbuf->last_row_buffer = row_packet->row_buffer;
    1058          24 :                         row_packet->fields = NULL;
    1059          24 :                         row_packet->row_buffer = NULL;
    1060             : 
    1061         120 :                         if (PASS != result->unbuf->m.row_decoder(result->unbuf->last_row_buffer,
    1062          24 :                                                                           result->unbuf->last_row_data,
    1063             :                                                                           row_packet->field_count,
    1064          24 :                                                                           row_packet->fields_metadata,
    1065          24 :                                                                           result->conn->options->int_and_float_native,
    1066          24 :                                                                           result->conn->stats TSRMLS_CC))
    1067             :                         {
    1068           0 :                                 DBG_RETURN(FAIL);                                                 
    1069             :                         }
    1070             : 
    1071             :                         /* If no result bind, do nothing. We consumed the data */
    1072          54 :                         for (i = 0; i < field_count; i++) {
    1073          30 :                                 if (stmt->result_bind[i].bound == TRUE) {
    1074          30 :                                         zval *data = result->unbuf->last_row_data[i];
    1075             :                                         /*
    1076             :                                           stmt->result_bind[i].zv has been already destructed
    1077             :                                           in result->unbuf->m.free_last_data()
    1078             :                                         */
    1079             : #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
    1080          30 :                                         zval_dtor(stmt->result_bind[i].zv);
    1081             : #endif
    1082          60 :                                         DBG_INF_FMT("i=%u bound_var=%p type=%u refc=%u", i, stmt->result_bind[i].zv,
    1083          60 :                                                                 Z_TYPE_P(data), Z_REFCOUNT_P(stmt->result_bind[i].zv));
    1084          30 :                                         if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data))) {
    1085          30 :                                                 if ((Z_TYPE_P(data) == IS_STRING) && (meta->fields[i].max_length < (unsigned long) Z_STRLEN_P(data))) {
    1086           1 :                                                         meta->fields[i].max_length = Z_STRLEN_P(data);
    1087             :                                                 }
    1088          30 :                                                 stmt->result_bind[i].zv->value = data->value;
    1089             :                                                 /* copied data, thus also the ownership. Thus null data */
    1090          30 :                                                 ZVAL_NULL(data);
    1091             :                                         }
    1092             :                                 }
    1093             :                         }
    1094             :                 } else {
    1095           0 :                         DBG_INF("skipping extraction");
    1096             :                         /*
    1097             :                           Data has been allocated and usually result->unbuf->m.free_last_data()
    1098             :                           frees it but we can't call this function as it will cause problems with
    1099             :                           the bound variables. Thus we need to do part of what it does or Zend will
    1100             :                           report leaks.
    1101             :                         */
    1102           0 :                         row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC);
    1103           0 :                         row_packet->row_buffer = NULL;
    1104             :                 }
    1105             :                 /* We asked for one row, the next one should be EOF, eat it */
    1106          24 :                 ret = PACKET_READ(row_packet, result->conn);
    1107          24 :                 if (row_packet->row_buffer) {
    1108          24 :                         row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC);
    1109          24 :                         row_packet->row_buffer = NULL;
    1110             :                 }
    1111          24 :                 MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_CURSOR);
    1112             : 
    1113          24 :                 result->unbuf->row_count++;
    1114          24 :                 *fetched_anything = TRUE;
    1115             :         } else {
    1116           2 :                 *fetched_anything = FALSE;
    1117             : 
    1118           6 :                 stmt->upsert_status->warning_count =
    1119           6 :                         stmt->conn->upsert_status->warning_count =
    1120           2 :                                 row_packet->warning_count;
    1121             : 
    1122           6 :                 stmt->upsert_status->server_status = 
    1123           6 :                         stmt->conn->upsert_status->server_status =
    1124           2 :                                 row_packet->server_status;
    1125             : 
    1126           2 :                 result->unbuf->eof_reached = row_packet->eof;
    1127             :         }
    1128          78 :         stmt->upsert_status->warning_count =
    1129          78 :                 stmt->conn->upsert_status->warning_count =
    1130          26 :                         row_packet->warning_count;
    1131          78 :         stmt->upsert_status->server_status = 
    1132          78 :                 stmt->conn->upsert_status->server_status =
    1133          26 :                         row_packet->server_status;
    1134             : 
    1135         104 :         DBG_INF_FMT("ret=%s fetched=%u server_status=%u warnings=%u eof=%u",
    1136          26 :                                 ret == PASS? "PASS":"FAIL", *fetched_anything,
    1137          52 :                                 row_packet->server_status, row_packet->warning_count,
    1138          26 :                                 result->unbuf->eof_reached);
    1139          26 :         DBG_RETURN(ret);
    1140             : }
    1141             : /* }}} */
    1142             : 
    1143             : 
    1144             : /* {{{ mysqlnd_stmt::fetch */
    1145             : static enum_func_status
    1146       19514 : MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const s, zend_bool * const fetched_anything TSRMLS_DC)
    1147             : {
    1148       19514 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1149             :         enum_func_status ret;
    1150       19514 :         DBG_ENTER("mysqlnd_stmt::fetch");
    1151       19514 :         if (!stmt || !stmt->conn) {
    1152           0 :                 DBG_RETURN(FAIL);
    1153             :         }
    1154       19514 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
    1155             : 
    1156       39028 :         if (!stmt->result ||
    1157       19514 :                 stmt->state < MYSQLND_STMT_WAITING_USE_OR_STORE) {
    1158           8 :                 SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
    1159             : 
    1160           8 :                 DBG_ERR("command out of sync");
    1161           8 :                 DBG_RETURN(FAIL);
    1162       19506 :         } else if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
    1163             :                 /* Execute only once. We have to free the previous contents of user's bound vars */
    1164             : 
    1165        1241 :                 stmt->default_rset_handler(s TSRMLS_CC);
    1166             :         }
    1167       19506 :         stmt->state = MYSQLND_STMT_USER_FETCHING;
    1168             : 
    1169       19506 :         SET_EMPTY_ERROR(*stmt->error_info);
    1170       19506 :         SET_EMPTY_ERROR(*stmt->conn->error_info);
    1171             : 
    1172       19506 :         DBG_INF_FMT("result_bind=%p separated_once=%u", stmt->result_bind, stmt->result_zvals_separated_once);
    1173             :         /*
    1174             :           The user might have not bound any variables for result.
    1175             :           Do the binding once she does it.
    1176             :         */
    1177       19506 :         if (stmt->result_bind && !stmt->result_zvals_separated_once) {
    1178             :                 unsigned int i;
    1179             :                 /*
    1180             :                   mysqlnd_stmt_store_result() has been called free the bind
    1181             :                   variables to prevent leaking of their previous content.
    1182             :                 */
    1183        7777 :                 for (i = 0; i < stmt->result->field_count; i++) {
    1184        6179 :                         if (stmt->result_bind[i].bound == TRUE) {
    1185        6179 :                                 zval_dtor(stmt->result_bind[i].zv);
    1186        6179 :                                 ZVAL_NULL(stmt->result_bind[i].zv);
    1187             :                         }
    1188             :                 }
    1189        1598 :                 stmt->result_zvals_separated_once = TRUE;
    1190             :         }
    1191             : 
    1192       19506 :         ret = stmt->result->m.fetch_row(stmt->result, (void*)s, 0, fetched_anything TSRMLS_CC);
    1193       19506 :         DBG_RETURN(ret);
    1194             : }
    1195             : /* }}} */
    1196             : 
    1197             : 
    1198             : /* {{{ mysqlnd_stmt::reset */
    1199             : static enum_func_status
    1200           6 : MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const s TSRMLS_DC)
    1201             : {
    1202           6 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1203           6 :         enum_func_status ret = PASS;
    1204             :         zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */];
    1205             : 
    1206           6 :         DBG_ENTER("mysqlnd_stmt::reset");
    1207           6 :         if (!stmt || !stmt->conn) {
    1208           0 :                 DBG_RETURN(FAIL);
    1209             :         }
    1210           6 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
    1211             : 
    1212           6 :         SET_EMPTY_ERROR(*stmt->error_info);
    1213           6 :         SET_EMPTY_ERROR(*stmt->conn->error_info);
    1214             : 
    1215           6 :         if (stmt->stmt_id) {
    1216           6 :                 MYSQLND_CONN_DATA * conn = stmt->conn;
    1217           6 :                 if (stmt->param_bind) {
    1218             :                         unsigned int i;
    1219           2 :                         DBG_INF("resetting long data");
    1220             :                         /* Reset Long Data */
    1221           4 :                         for (i = 0; i < stmt->param_count; i++) {
    1222           2 :                                 if (stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED) {
    1223           1 :                                         stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
    1224             :                                 }
    1225             :                         }
    1226             :                 }
    1227             : 
    1228           6 :                 s->m->flush(s TSRMLS_CC);
    1229             : 
    1230             :                 /*
    1231             :                   Don't free now, let the result be usable. When the stmt will again be
    1232             :                   executed then the result set will be cleaned, the bound variables will
    1233             :                   be separated before that.
    1234             :                 */
    1235             : 
    1236           6 :                 int4store(cmd_buf, stmt->stmt_id);
    1237          12 :                 if (CONN_GET_STATE(conn) == CONN_READY &&
    1238           6 :                         FAIL == (ret = conn->m->simple_command(conn, COM_STMT_RESET, cmd_buf,
    1239             :                                                                                                   sizeof(cmd_buf), PROT_OK_PACKET,
    1240             :                                                                                                   FALSE, TRUE TSRMLS_CC))) {
    1241           0 :                         COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
    1242             :                 }
    1243           6 :                 *stmt->upsert_status = *conn->upsert_status;
    1244             :         }
    1245           6 :         DBG_INF(ret == PASS? "PASS":"FAIL");
    1246           6 :         DBG_RETURN(ret);
    1247             : }
    1248             : /* }}} */
    1249             : 
    1250             : 
    1251             : /* {{{ mysqlnd_stmt::flush */
    1252             : static enum_func_status
    1253        2303 : MYSQLND_METHOD(mysqlnd_stmt, flush)(MYSQLND_STMT * const s TSRMLS_DC)
    1254             : {
    1255        2303 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1256        2303 :         enum_func_status ret = PASS;
    1257             : 
    1258        2303 :         DBG_ENTER("mysqlnd_stmt::flush");
    1259        2303 :         if (!stmt || !stmt->conn) {
    1260           0 :                 DBG_RETURN(FAIL);
    1261             :         }
    1262        2303 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
    1263             : 
    1264        2303 :         if (stmt->stmt_id) {
    1265             :                 /*
    1266             :                   If the user decided to close the statement right after execute()
    1267             :                   We have to call the appropriate use_result() or store_result() and
    1268             :                   clean.
    1269             :                 */
    1270             :                 do {
    1271        2303 :                         if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
    1272           2 :                                 DBG_INF("fetching result set header");
    1273           2 :                                 stmt->default_rset_handler(s TSRMLS_CC);
    1274           2 :                                 stmt->state = MYSQLND_STMT_USER_FETCHING;
    1275             :                         }
    1276             : 
    1277        2303 :                         if (stmt->result) {
    1278        2302 :                                 DBG_INF("skipping result");
    1279        2302 :                                 stmt->result->m.skip_result(stmt->result TSRMLS_CC);
    1280             :                         }
    1281        2303 :                 } while (mysqlnd_stmt_more_results(s) && mysqlnd_stmt_next_result(s) == PASS);
    1282             :         }
    1283        2303 :         DBG_INF(ret == PASS? "PASS":"FAIL");
    1284        2303 :         DBG_RETURN(ret);
    1285             : }
    1286             : /* }}} */
    1287             : 
    1288             : 
    1289             : /* {{{ mysqlnd_stmt::send_long_data */
    1290             : static enum_func_status
    1291          16 : MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const s, unsigned int param_no,
    1292             :                                                                                          const char * const data, unsigned long length TSRMLS_DC)
    1293             : {
    1294          16 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1295          16 :         enum_func_status ret = FAIL;
    1296             :         MYSQLND_CONN_DATA * conn;
    1297             :         zend_uchar * cmd_buf;
    1298          16 :         enum php_mysqlnd_server_command cmd = COM_STMT_SEND_LONG_DATA;
    1299             : 
    1300          16 :         DBG_ENTER("mysqlnd_stmt::send_long_data");
    1301          16 :         if (!stmt || !stmt->conn) {
    1302           0 :                 DBG_RETURN(FAIL);
    1303             :         }
    1304          16 :         DBG_INF_FMT("stmt=%lu param_no=%u data_len=%lu", stmt->stmt_id, param_no, length);
    1305             : 
    1306          16 :         conn = stmt->conn;
    1307             : 
    1308          16 :         SET_EMPTY_ERROR(*stmt->error_info);
    1309          16 :         SET_EMPTY_ERROR(*stmt->conn->error_info);
    1310             : 
    1311          16 :         if (stmt->state < MYSQLND_STMT_PREPARED) {
    1312           0 :                 SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
    1313           0 :                 DBG_ERR("not prepared");
    1314           0 :                 DBG_RETURN(FAIL);
    1315             :         }
    1316          16 :         if (!stmt->param_bind) {
    1317           0 :                 SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
    1318           0 :                 DBG_ERR("command out of sync");
    1319           0 :                 DBG_RETURN(FAIL);
    1320             :         }
    1321          16 :         if (param_no >= stmt->param_count) {
    1322           1 :                 SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
    1323           1 :                 DBG_ERR("invalid param_no");
    1324           1 :                 DBG_RETURN(FAIL);
    1325             :         }
    1326          15 :         if (stmt->param_bind[param_no].type != MYSQL_TYPE_LONG_BLOB) {
    1327           0 :                 SET_STMT_ERROR(stmt, CR_INVALID_BUFFER_USE, UNKNOWN_SQLSTATE, mysqlnd_not_bound_as_blob);
    1328           0 :                 DBG_ERR("param_no is not of a blob type");
    1329           0 :                 DBG_RETURN(FAIL);
    1330             :         }
    1331             : 
    1332             :         /*
    1333             :           XXX:  Unfortunately we have to allocate additional buffer to be able the
    1334             :                         additional data, which is like a header inside the payload.
    1335             :                         This should be optimised, but it will be a pervasive change, so
    1336             :                         conn->m->simple_command() will accept not a buffer, but actually MYSQLND_STRING*
    1337             :                         terminated by NULL, to send. If the strings are not big, we can collapse them
    1338             :                         on the buffer every connection has, but otherwise we will just send them
    1339             :                         one by one to the wire.
    1340             :         */
    1341             : 
    1342          15 :         if (CONN_GET_STATE(conn) == CONN_READY) {
    1343             :                 size_t packet_len;
    1344          15 :                 cmd_buf = mnd_emalloc(packet_len = STMT_ID_LENGTH + 2 + length);
    1345          15 :                 if (cmd_buf) {
    1346          15 :                         stmt->param_bind[param_no].flags |= MYSQLND_PARAM_BIND_BLOB_USED;
    1347             : 
    1348          15 :                         int4store(cmd_buf, stmt->stmt_id);
    1349          15 :                         int2store(cmd_buf + STMT_ID_LENGTH, param_no);
    1350          15 :                         memcpy(cmd_buf + STMT_ID_LENGTH + 2, data, length);
    1351             : 
    1352             :                         /* COM_STMT_SEND_LONG_DATA doesn't send an OK packet*/
    1353          15 :                         ret = conn->m->simple_command(conn, cmd, cmd_buf, packet_len, PROT_LAST , FALSE, TRUE TSRMLS_CC);
    1354          15 :                         mnd_efree(cmd_buf);
    1355          15 :                         if (FAIL == ret) {
    1356           0 :                                 COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
    1357             :                         }
    1358             :                 } else {
    1359           0 :                         ret = FAIL;
    1360           0 :                         SET_OOM_ERROR(*stmt->error_info);
    1361           0 :                         SET_OOM_ERROR(*conn->error_info);
    1362             :                 }
    1363             :                 /*
    1364             :                   Cover protocol error: COM_STMT_SEND_LONG_DATA was designed to be quick and not
    1365             :                   sent response packets. According to documentation the only way to get an error
    1366             :                   is to have out-of-memory on the server-side. However, that's not true, as if
    1367             :                   max_allowed_packet_size is smaller than the chunk being sent to the server, the
    1368             :                   latter will complain with an error message. However, normally we don't expect
    1369             :                   an error message, thus we continue. When sending the next command, which expects
    1370             :                   response we will read the unexpected data and error message will look weird.
    1371             :                   Therefore we do non-blocking read to clean the line, if there is a need.
    1372             :                   Nevertheless, there is a built-in protection when sending a command packet, that
    1373             :                   checks if the line is clear - useful for debug purposes and to be switched off
    1374             :                   in release builds.
    1375             : 
    1376             :                   Maybe we can make it automatic by checking what's the value of
    1377             :                   max_allowed_packet_size on the server and resending the data.
    1378             :                 */
    1379             : #ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
    1380             : #if HAVE_USLEEP && !defined(PHP_WIN32)
    1381             :                 usleep(120000);
    1382             : #endif
    1383             :                 if ((packet_len = conn->net->m.consume_uneaten_data(conn->net, cmd TSRMLS_CC))) {
    1384             :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "There was an error "
    1385             :                                                          "while sending long data. Probably max_allowed_packet_size "
    1386             :                                                          "is smaller than the data. You have to increase it or send "
    1387             :                                                          "smaller chunks of data. Answer was "MYSQLND_SZ_T_SPEC" bytes long.", packet_len);
    1388             :                         SET_STMT_ERROR(stmt, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE,
    1389             :                                                         "Server responded to COM_STMT_SEND_LONG_DATA.");
    1390             :                         ret = FAIL;
    1391             :                 }
    1392             : #endif
    1393             :         }
    1394             : 
    1395          15 :         DBG_INF(ret == PASS? "PASS":"FAIL");
    1396          15 :         DBG_RETURN(ret);
    1397             : }
    1398             : /* }}} */
    1399             : 
    1400             : 
    1401             : /* {{{ mysqlnd_stmt::bind_parameters */
    1402             : static enum_func_status
    1403       20877 : MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const s, MYSQLND_PARAM_BIND * const param_bind TSRMLS_DC)
    1404             : {
    1405       20877 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1406       20877 :         DBG_ENTER("mysqlnd_stmt::bind_param");
    1407       20877 :         if (!stmt || !stmt->conn) {
    1408           0 :                 DBG_RETURN(FAIL);
    1409             :         }
    1410       20877 :         DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count);
    1411             : 
    1412       20877 :         if (stmt->state < MYSQLND_STMT_PREPARED) {
    1413           0 :                 SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
    1414           0 :                 DBG_ERR("not prepared");
    1415           0 :                 if (param_bind) {
    1416           0 :                         s->m->free_parameter_bind(s, param_bind TSRMLS_CC);
    1417             :                 }
    1418           0 :                 DBG_RETURN(FAIL);
    1419             :         }
    1420             : 
    1421       20877 :         SET_EMPTY_ERROR(*stmt->error_info);
    1422       20877 :         SET_EMPTY_ERROR(*stmt->conn->error_info);
    1423             : 
    1424       20877 :         if (stmt->param_count) {
    1425       20877 :                 unsigned int i = 0;
    1426             : 
    1427       20877 :                 if (!param_bind) {
    1428           0 :                         SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, "Re-binding (still) not supported");
    1429           0 :                         DBG_ERR("Re-binding (still) not supported");
    1430           0 :                         DBG_RETURN(FAIL);
    1431       20877 :                 } else if (stmt->param_bind) {
    1432       20435 :                         DBG_INF("Binding");
    1433             :                         /*
    1434             :                           There is already result bound.
    1435             :                           Forbid for now re-binding!!
    1436             :                         */
    1437       61305 :                         for (i = 0; i < stmt->param_count; i++) {
    1438             :                                 /*
    1439             :                                   We may have the last reference, then call zval_ptr_dtor() or we may leak memory.
    1440             :                                   Switching from bind_one_parameter to bind_parameters may result in zv being NULL
    1441             :                                 */
    1442       40870 :                                 if (stmt->param_bind[i].zv) {
    1443       40870 :                                         zval_ptr_dtor(&stmt->param_bind[i].zv);
    1444             :                                 }
    1445             :                         }
    1446       20435 :                         if (stmt->param_bind != param_bind) {
    1447       20435 :                                 s->m->free_parameter_bind(s, stmt->param_bind TSRMLS_CC);
    1448             :                         }
    1449             :                 }
    1450             : 
    1451       20877 :                 stmt->param_bind = param_bind;
    1452      195677 :                 for (i = 0; i < stmt->param_count; i++) {
    1453             :                         /* The client will use stmt_send_long_data */
    1454      174800 :                         DBG_INF_FMT("%u is of type %u", i, stmt->param_bind[i].type);
    1455             :                         /* Prevent from freeing */
    1456             :                         /* Don't update is_ref, or we will leak during conversion */
    1457      174800 :                         Z_ADDREF_P(stmt->param_bind[i].zv);
    1458      174800 :                         stmt->param_bind[i].flags = 0;
    1459      174800 :                         if (stmt->param_bind[i].type == MYSQL_TYPE_LONG_BLOB) {
    1460          24 :                                 stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
    1461             :                         }
    1462             :                 }
    1463       20877 :                 stmt->send_types_to_server = 1;
    1464             :         }
    1465       20877 :         DBG_INF("PASS");
    1466       20877 :         DBG_RETURN(PASS);
    1467             : }
    1468             : /* }}} */
    1469             : 
    1470             : 
    1471             : /* {{{ mysqlnd_stmt::bind_one_parameter */
    1472             : static enum_func_status
    1473         285 : MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter)(MYSQLND_STMT * const s, unsigned int param_no,
    1474             :                                                                                                  zval * const zv, zend_uchar type TSRMLS_DC)
    1475             : {
    1476         285 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1477         285 :         DBG_ENTER("mysqlnd_stmt::bind_one_parameter");
    1478         285 :         if (!stmt || !stmt->conn) {
    1479           0 :                 DBG_RETURN(FAIL);
    1480             :         }
    1481         285 :         DBG_INF_FMT("stmt=%lu param_no=%u param_count=%u type=%u", stmt->stmt_id, param_no, stmt->param_count, type);
    1482             : 
    1483         285 :         if (stmt->state < MYSQLND_STMT_PREPARED) {
    1484           0 :                 SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
    1485           0 :                 DBG_ERR("not prepared");
    1486           0 :                 DBG_RETURN(FAIL);
    1487             :         }
    1488             : 
    1489         285 :         if (param_no >= stmt->param_count) {
    1490           0 :                 SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
    1491           0 :                 DBG_ERR("invalid param_no");
    1492           0 :                 DBG_RETURN(FAIL);
    1493             :         }
    1494         285 :         SET_EMPTY_ERROR(*stmt->error_info);
    1495         285 :         SET_EMPTY_ERROR(*stmt->conn->error_info);
    1496             : 
    1497         285 :         if (stmt->param_count) {
    1498         285 :                 if (!stmt->param_bind) {
    1499         184 :                         stmt->param_bind = mnd_pecalloc(stmt->param_count, sizeof(MYSQLND_PARAM_BIND), stmt->persistent);
    1500         184 :                         if (!stmt->param_bind) {
    1501           0 :                                 DBG_RETURN(FAIL);
    1502             :                         }
    1503             :                 }
    1504             : 
    1505             :                 /* Prevent from freeing */
    1506             :                 /* Don't update is_ref, or we will leak during conversion */
    1507             :                 Z_ADDREF_P(zv);
    1508         285 :                 DBG_INF("Binding");
    1509             :                 /* Release what we had, if we had */
    1510         285 :                 if (stmt->param_bind[param_no].zv) {
    1511           8 :                         zval_ptr_dtor(&stmt->param_bind[param_no].zv);
    1512             :                 }
    1513         285 :                 if (type == MYSQL_TYPE_LONG_BLOB) {
    1514             :                         /* The client will use stmt_send_long_data */
    1515           0 :                         stmt->param_bind[param_no].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
    1516             :                 }
    1517         285 :                 stmt->param_bind[param_no].zv = zv;
    1518         285 :                 stmt->param_bind[param_no].type = type;
    1519             : 
    1520         285 :                 stmt->send_types_to_server = 1;
    1521             :         }
    1522         285 :         DBG_INF("PASS");
    1523         285 :         DBG_RETURN(PASS);
    1524             : }
    1525             : /* }}} */
    1526             : 
    1527             : 
    1528             : /* {{{ mysqlnd_stmt::refresh_bind_param */
    1529             : static enum_func_status
    1530           0 : MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param)(MYSQLND_STMT * const s TSRMLS_DC)
    1531             : {
    1532           0 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1533           0 :         DBG_ENTER("mysqlnd_stmt::refresh_bind_param");
    1534           0 :         if (!stmt || !stmt->conn) {
    1535           0 :                 DBG_RETURN(FAIL);
    1536             :         }
    1537           0 :         DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count);
    1538             : 
    1539           0 :         if (stmt->state < MYSQLND_STMT_PREPARED) {
    1540           0 :                 SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
    1541           0 :                 DBG_ERR("not prepared");
    1542           0 :                 DBG_RETURN(FAIL);
    1543             :         }
    1544             : 
    1545           0 :         SET_EMPTY_ERROR(*stmt->error_info);
    1546           0 :         SET_EMPTY_ERROR(*stmt->conn->error_info);
    1547             : 
    1548           0 :         if (stmt->param_count) {
    1549           0 :                 stmt->send_types_to_server = 1;
    1550             :         }
    1551           0 :         DBG_RETURN(PASS);
    1552             : }
    1553             : /* }}} */
    1554             : 
    1555             : 
    1556             : /* {{{ mysqlnd_stmt::bind_result */
    1557             : static enum_func_status
    1558        1267 : MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const s,
    1559             :                                                                                   MYSQLND_RESULT_BIND * const result_bind TSRMLS_DC)
    1560             : {
    1561        1267 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1562        1267 :         DBG_ENTER("mysqlnd_stmt::bind_result");
    1563        1267 :         if (!stmt || !stmt->conn) {
    1564           0 :                 DBG_RETURN(FAIL);
    1565             :         }
    1566        1267 :         DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count);
    1567             : 
    1568        1267 :         if (stmt->state < MYSQLND_STMT_PREPARED) {
    1569           0 :                 SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
    1570           0 :                 if (result_bind) {
    1571           0 :                         s->m->free_result_bind(s, result_bind TSRMLS_CC);
    1572             :                 }
    1573           0 :                 DBG_ERR("not prepared");
    1574           0 :                 DBG_RETURN(FAIL);
    1575             :         }
    1576             : 
    1577        1267 :         SET_EMPTY_ERROR(*stmt->error_info);
    1578        1267 :         SET_EMPTY_ERROR(*stmt->conn->error_info);
    1579             : 
    1580        1267 :         if (stmt->field_count) {
    1581        1267 :                 unsigned int i = 0;
    1582             : 
    1583        1267 :                 if (!result_bind) {
    1584           0 :                         DBG_ERR("no result bind passed");
    1585           0 :                         DBG_RETURN(FAIL);
    1586             :                 }
    1587             : 
    1588        1267 :                 mysqlnd_stmt_separate_result_bind(s TSRMLS_CC);
    1589        1267 :                 stmt->result_zvals_separated_once = FALSE;
    1590        1267 :                 stmt->result_bind = result_bind;
    1591        6785 :                 for (i = 0; i < stmt->field_count; i++) {
    1592             :                         /* Prevent from freeing */
    1593        5518 :                         Z_ADDREF_P(stmt->result_bind[i].zv);
    1594       11036 :                         DBG_INF_FMT("ref of %p = %u", stmt->result_bind[i].zv, Z_REFCOUNT_P(stmt->result_bind[i].zv));
    1595             :                         /*
    1596             :                           Don't update is_ref !!! it's not our job
    1597             :                           Otherwise either 009.phpt or mysqli_stmt_bind_result.phpt
    1598             :                           will fail.
    1599             :                         */
    1600        5518 :                         stmt->result_bind[i].bound = TRUE;
    1601             :                 }
    1602           0 :         } else if (result_bind) {
    1603           0 :                 s->m->free_result_bind(s, result_bind TSRMLS_CC);
    1604             :         }
    1605        1267 :         DBG_INF("PASS");
    1606        1267 :         DBG_RETURN(PASS);
    1607             : }
    1608             : /* }}} */
    1609             : 
    1610             : 
    1611             : /* {{{ mysqlnd_stmt::bind_result */
    1612             : static enum_func_status
    1613         891 : MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const s, unsigned int param_no TSRMLS_DC)
    1614             : {
    1615         891 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1616         891 :         DBG_ENTER("mysqlnd_stmt::bind_result");
    1617         891 :         if (!stmt || !stmt->conn) {
    1618           0 :                 DBG_RETURN(FAIL);
    1619             :         }
    1620         891 :         DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count);
    1621             : 
    1622         891 :         if (stmt->state < MYSQLND_STMT_PREPARED) {
    1623           0 :                 SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
    1624           0 :                 DBG_ERR("not prepared");
    1625           0 :                 DBG_RETURN(FAIL);
    1626             :         }
    1627             : 
    1628         891 :         if (param_no >= stmt->field_count) {
    1629           0 :                 SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
    1630           0 :                 DBG_ERR("invalid param_no");
    1631           0 :                 DBG_RETURN(FAIL);
    1632             :         }
    1633             : 
    1634         891 :         SET_EMPTY_ERROR(*stmt->error_info);
    1635         891 :         SET_EMPTY_ERROR(*stmt->conn->error_info);
    1636             : 
    1637         891 :         if (stmt->field_count) {
    1638         891 :                 mysqlnd_stmt_separate_one_result_bind(s, param_no TSRMLS_CC);
    1639             :                 /* Guaranteed is that stmt->result_bind is NULL */
    1640         891 :                 if (!stmt->result_bind) {
    1641         429 :                         stmt->result_bind = mnd_pecalloc(stmt->field_count, sizeof(MYSQLND_RESULT_BIND), stmt->persistent);
    1642             :                 } else {
    1643         462 :                         stmt->result_bind = mnd_perealloc(stmt->result_bind, stmt->field_count * sizeof(MYSQLND_RESULT_BIND), stmt->persistent);
    1644             :                 }
    1645         891 :                 if (!stmt->result_bind) {
    1646           0 :                         DBG_RETURN(FAIL);
    1647             :                 }
    1648         891 :                 ALLOC_INIT_ZVAL(stmt->result_bind[param_no].zv);
    1649             :                 /*
    1650             :                   Don't update is_ref !!! it's not our job
    1651             :                   Otherwise either 009.phpt or mysqli_stmt_bind_result.phpt
    1652             :                   will fail.
    1653             :                 */
    1654         891 :                 stmt->result_bind[param_no].bound = TRUE;
    1655             :         }
    1656         891 :         DBG_INF("PASS");
    1657         891 :         DBG_RETURN(PASS);
    1658             : }
    1659             : /* }}} */
    1660             : 
    1661             : 
    1662             : /* {{{ mysqlnd_stmt::insert_id */
    1663             : static uint64_t
    1664          10 : MYSQLND_METHOD(mysqlnd_stmt, insert_id)(const MYSQLND_STMT * const s TSRMLS_DC)
    1665             : {
    1666          10 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1667          10 :         return stmt? stmt->upsert_status->last_insert_id : 0;
    1668             : }
    1669             : /* }}} */
    1670             : 
    1671             : 
    1672             : /* {{{ mysqlnd_stmt::affected_rows */
    1673             : static uint64_t
    1674         754 : MYSQLND_METHOD(mysqlnd_stmt, affected_rows)(const MYSQLND_STMT * const s TSRMLS_DC)
    1675             : {
    1676         754 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1677         754 :         return stmt? stmt->upsert_status->affected_rows : 0;
    1678             : }
    1679             : /* }}} */
    1680             : 
    1681             : 
    1682             : /* {{{ mysqlnd_stmt::num_rows */
    1683             : static uint64_t
    1684          24 : MYSQLND_METHOD(mysqlnd_stmt, num_rows)(const MYSQLND_STMT * const s TSRMLS_DC)
    1685             : {
    1686          24 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1687          24 :         return stmt && stmt->result? mysqlnd_num_rows(stmt->result):0;
    1688             : }
    1689             : /* }}} */
    1690             : 
    1691             : 
    1692             : /* {{{ mysqlnd_stmt::warning_count */
    1693             : static unsigned int
    1694           3 : MYSQLND_METHOD(mysqlnd_stmt, warning_count)(const MYSQLND_STMT * const s TSRMLS_DC)
    1695             : {
    1696           3 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1697           3 :         return stmt? stmt->upsert_status->warning_count : 0;
    1698             : }
    1699             : /* }}} */
    1700             : 
    1701             : 
    1702             : /* {{{ mysqlnd_stmt::server_status */
    1703             : static unsigned int
    1704           0 : MYSQLND_METHOD(mysqlnd_stmt, server_status)(const MYSQLND_STMT * const s TSRMLS_DC)
    1705             : {
    1706           0 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1707           0 :         return stmt? stmt->upsert_status->server_status : 0;
    1708             : }
    1709             : /* }}} */
    1710             : 
    1711             : 
    1712             : /* {{{ mysqlnd_stmt::field_count */
    1713             : static unsigned int
    1714        2495 : MYSQLND_METHOD(mysqlnd_stmt, field_count)(const MYSQLND_STMT * const s TSRMLS_DC)
    1715             : {
    1716        2495 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1717        2495 :         return stmt? stmt->field_count : 0;
    1718             : }
    1719             : /* }}} */
    1720             : 
    1721             : 
    1722             : /* {{{ mysqlnd_stmt::param_count */
    1723             : static unsigned int
    1724       21751 : MYSQLND_METHOD(mysqlnd_stmt, param_count)(const MYSQLND_STMT * const s TSRMLS_DC)
    1725             : {
    1726       21751 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1727       21751 :         return stmt? stmt->param_count : 0;
    1728             : }
    1729             : /* }}} */
    1730             : 
    1731             : 
    1732             : /* {{{ mysqlnd_stmt::errno */
    1733             : static unsigned int
    1734          21 : MYSQLND_METHOD(mysqlnd_stmt, errno)(const MYSQLND_STMT * const s TSRMLS_DC)
    1735             : {
    1736          21 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1737          21 :         return stmt? stmt->error_info->error_no : 0;
    1738             : }
    1739             : /* }}} */
    1740             : 
    1741             : 
    1742             : /* {{{ mysqlnd_stmt::error */
    1743             : static const char *
    1744          11 : MYSQLND_METHOD(mysqlnd_stmt, error)(const MYSQLND_STMT * const s TSRMLS_DC)
    1745             : {
    1746          11 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1747          11 :         return stmt? stmt->error_info->error : 0;
    1748             : }
    1749             : /* }}} */
    1750             : 
    1751             : 
    1752             : /* {{{ mysqlnd_stmt::sqlstate */
    1753             : static const char *
    1754          12 : MYSQLND_METHOD(mysqlnd_stmt, sqlstate)(const MYSQLND_STMT * const s TSRMLS_DC)
    1755             : {
    1756          12 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1757          12 :         return stmt && stmt->error_info->sqlstate[0] ? stmt->error_info->sqlstate:MYSQLND_SQLSTATE_NULL;
    1758             : }
    1759             : /* }}} */
    1760             : 
    1761             : 
    1762             : /* {{{ mysqlnd_stmt::data_seek */
    1763             : static enum_func_status
    1764           3 : MYSQLND_METHOD(mysqlnd_stmt, data_seek)(const MYSQLND_STMT * const s, uint64_t row TSRMLS_DC)
    1765             : {
    1766           3 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1767           3 :         return stmt && stmt->result? stmt->result->m.seek_data(stmt->result, row TSRMLS_CC) : FAIL;
    1768             : }
    1769             : /* }}} */
    1770             : 
    1771             : 
    1772             : /* {{{ mysqlnd_stmt::param_metadata */
    1773             : static MYSQLND_RES *
    1774           0 : MYSQLND_METHOD(mysqlnd_stmt, param_metadata)(MYSQLND_STMT * const s TSRMLS_DC)
    1775             : {
    1776           0 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1777           0 :         if (!stmt || !stmt->param_count) {
    1778           0 :                 return NULL;
    1779             :         }
    1780           0 :         return NULL;
    1781             : }
    1782             : /* }}} */
    1783             : 
    1784             : 
    1785             : /* {{{ mysqlnd_stmt::result_metadata */
    1786             : static MYSQLND_RES *
    1787         945 : MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const s TSRMLS_DC)
    1788             : {
    1789         945 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1790             :         MYSQLND_RES *result;
    1791             : 
    1792         945 :         DBG_ENTER("mysqlnd_stmt::result_metadata");
    1793         945 :         if (!stmt) {
    1794           0 :                 DBG_RETURN(NULL);
    1795             :         }
    1796         945 :         DBG_INF_FMT("stmt=%u field_count=%u", stmt->stmt_id, stmt->field_count);
    1797             : 
    1798         945 :         if (!stmt->field_count || !stmt->conn || !stmt->result || !stmt->result->meta) {
    1799         292 :                 DBG_INF("NULL");
    1800         292 :                 DBG_RETURN(NULL);
    1801             :         }
    1802             : 
    1803         653 :         if (stmt->update_max_length && stmt->result->stored_data) {
    1804             :                 /* stored result, we have to update the max_length before we clone the meta data :( */
    1805           2 :                 stmt->result->stored_data->m.initialize_result_set_rest(stmt->result->stored_data, stmt->result->meta, stmt->conn->stats,
    1806           1 :                                                                                                                                 stmt->conn->options->int_and_float_native TSRMLS_CC);
    1807             :         }
    1808             :         /*
    1809             :           TODO: This implementation is kind of a hack,
    1810             :                         find a better way to do it. In different functions I have put
    1811             :                         fuses to check for result->m.fetch_row() being NULL. This should
    1812             :                         be handled in a better way.
    1813             : 
    1814             :           In the meantime we don't need a zval cache reference for this fake
    1815             :           result set, so we don't get one.
    1816             :         */
    1817             :         do {
    1818         653 :                 result = stmt->conn->m->result_init(stmt->field_count, stmt->persistent TSRMLS_CC);
    1819         653 :                 if (!result) {
    1820           0 :                         break;
    1821             :                 }
    1822         653 :                 result->type = MYSQLND_RES_NORMAL;
    1823         653 :                 result->unbuf = mysqlnd_result_unbuffered_init(stmt->field_count, TRUE, result->persistent TSRMLS_CC);
    1824         653 :                 if (!result->unbuf) {
    1825           0 :                         break;
    1826             :                 }
    1827         653 :                 result->unbuf->eof_reached = TRUE;
    1828         653 :                 result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC);
    1829         653 :                 if (!result->meta) {
    1830           0 :                         break;
    1831             :                 }
    1832             : 
    1833         653 :                 DBG_INF_FMT("result=%p", result);
    1834         653 :                 DBG_RETURN(result);
    1835             :         } while (0);
    1836             : 
    1837           0 :         SET_OOM_ERROR(*stmt->conn->error_info);
    1838           0 :         if (result) {
    1839           0 :                 result->m.free_result(result, TRUE TSRMLS_CC);
    1840             :         }
    1841           0 :         DBG_RETURN(NULL);
    1842             : }
    1843             : /* }}} */
    1844             : 
    1845             : 
    1846             : /* {{{ mysqlnd_stmt::attr_set */
    1847             : static enum_func_status
    1848        1110 : MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const s,
    1849             :                                                                            enum mysqlnd_stmt_attr attr_type,
    1850             :                                                                            const void * const value TSRMLS_DC)
    1851             : {
    1852        1110 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1853        1110 :         DBG_ENTER("mysqlnd_stmt::attr_set");
    1854        1110 :         if (!stmt) {
    1855           0 :                 DBG_RETURN(FAIL);
    1856             :         }
    1857        1110 :         DBG_INF_FMT("stmt=%lu attr_type=%u", stmt->stmt_id, attr_type);
    1858             : 
    1859        1110 :         switch (attr_type) {
    1860             :                 case STMT_ATTR_UPDATE_MAX_LENGTH:{
    1861           2 :                         zend_uchar bval = *(zend_uchar *) value;
    1862             :                         /*
    1863             :                           XXX : libmysql uses my_bool, but mysqli uses ulong as storage on the stack
    1864             :                           and mysqlnd won't be used out of the scope of PHP -> use ulong.
    1865             :                         */
    1866           2 :                         stmt->update_max_length = bval? TRUE:FALSE;
    1867           2 :                         break;
    1868             :                 }
    1869             :                 case STMT_ATTR_CURSOR_TYPE: {
    1870           9 :                         unsigned int ival = *(unsigned int *) value;
    1871           9 :                         if (ival > (unsigned long) CURSOR_TYPE_READ_ONLY) {
    1872           2 :                                 SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
    1873           2 :                                 DBG_INF("FAIL");
    1874           2 :                                 DBG_RETURN(FAIL);
    1875             :                         }
    1876           7 :                         stmt->flags = ival;
    1877           7 :                         break;
    1878             :                 }
    1879             :                 case STMT_ATTR_PREFETCH_ROWS: {
    1880           1 :                         unsigned int ival = *(unsigned int *) value;
    1881           1 :                         if (ival == 0) {
    1882           0 :                                 ival = MYSQLND_DEFAULT_PREFETCH_ROWS;
    1883           1 :                         } else if (ival > 1) {
    1884           0 :                                 SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
    1885           0 :                                 DBG_INF("FAIL");
    1886           0 :                                 DBG_RETURN(FAIL);
    1887             :                         }
    1888           1 :                         stmt->prefetch_rows = ival;
    1889           1 :                         break;
    1890             :                 }
    1891             :                 default:
    1892        1098 :                         SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
    1893        1098 :                         DBG_RETURN(FAIL);
    1894             :         }
    1895          10 :         DBG_INF("PASS");
    1896          10 :         DBG_RETURN(PASS);
    1897             : }
    1898             : /* }}} */
    1899             : 
    1900             : 
    1901             : /* {{{ mysqlnd_stmt::attr_get */
    1902             : static enum_func_status
    1903           6 : MYSQLND_METHOD(mysqlnd_stmt, attr_get)(const MYSQLND_STMT * const s,
    1904             :                                                                            enum mysqlnd_stmt_attr attr_type,
    1905             :                                                                            void * const value TSRMLS_DC)
    1906             : {
    1907           6 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1908           6 :         DBG_ENTER("mysqlnd_stmt::attr_set");
    1909           6 :         if (!stmt) {
    1910           0 :                 DBG_RETURN(FAIL);
    1911             :         }
    1912           6 :         DBG_INF_FMT("stmt=%lu attr_type=%u", stmt->stmt_id, attr_type);
    1913             : 
    1914           6 :         switch (attr_type) {
    1915             :                 case STMT_ATTR_UPDATE_MAX_LENGTH:
    1916           3 :                         *(zend_bool *) value= stmt->update_max_length;
    1917           3 :                         break;
    1918             :                 case STMT_ATTR_CURSOR_TYPE:
    1919           1 :                         *(unsigned long *) value= stmt->flags;
    1920           1 :                         break;
    1921             :                 case STMT_ATTR_PREFETCH_ROWS:
    1922           1 :                         *(unsigned long *) value= stmt->prefetch_rows;
    1923           1 :                         break;
    1924             :                 default:
    1925           1 :                         DBG_RETURN(FAIL);
    1926             :         }
    1927           5 :         DBG_INF_FMT("value=%lu", value);
    1928           5 :         DBG_RETURN(PASS);
    1929             : }
    1930             : /* }}} */
    1931             : 
    1932             : 
    1933             : /* free_result() doesn't actually free stmt->result but only the buffers */
    1934             : /* {{{ mysqlnd_stmt::free_result */
    1935             : static enum_func_status
    1936         260 : MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const s TSRMLS_DC)
    1937             : {
    1938         260 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1939         260 :         DBG_ENTER("mysqlnd_stmt::free_result");
    1940         260 :         if (!stmt || !stmt->conn) {
    1941           0 :                 DBG_RETURN(FAIL);
    1942             :         }
    1943         260 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
    1944             : 
    1945         260 :         if (!stmt->result) {
    1946           6 :                 DBG_INF("no result");
    1947           6 :                 DBG_RETURN(PASS);
    1948             :         }
    1949             : 
    1950             :         /*
    1951             :           If right after execute() we have to call the appropriate
    1952             :           use_result() or store_result() and clean.
    1953             :         */
    1954         254 :         if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
    1955           3 :                 DBG_INF("fetching result set header");
    1956             :                 /* Do implicit use_result and then flush the result */
    1957           3 :                 stmt->default_rset_handler = s->m->use_result;
    1958           3 :                 stmt->default_rset_handler(s TSRMLS_CC);
    1959             :         }
    1960             : 
    1961         254 :         if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) {
    1962         253 :                 DBG_INF("skipping result");
    1963             :                 /* Flush if anything is left and unbuffered set */
    1964         253 :                 stmt->result->m.skip_result(stmt->result TSRMLS_CC);
    1965             :                 /*
    1966             :                   Separate the bound variables, which point to the result set, then
    1967             :                   destroy the set.
    1968             :                 */
    1969         253 :                 mysqlnd_stmt_separate_result_bind(s TSRMLS_CC);
    1970             : 
    1971             :                 /* Now we can destroy the result set */
    1972         253 :                 stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC);
    1973             :         }
    1974             : 
    1975         254 :         if (stmt->state > MYSQLND_STMT_PREPARED) {
    1976             :                 /* As the buffers have been freed, we should go back to PREPARED */
    1977         253 :                 stmt->state = MYSQLND_STMT_PREPARED;
    1978             :         }
    1979             : 
    1980             :         /* Line is free! */
    1981         254 :         CONN_SET_STATE(stmt->conn, CONN_READY);
    1982             : 
    1983         254 :         DBG_RETURN(PASS);
    1984             : }
    1985             : /* }}} */
    1986             : 
    1987             : 
    1988             : /* {{{ mysqlnd_stmt_separate_result_bind */
    1989             : static void
    1990        5781 : mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const s TSRMLS_DC)
    1991             : {
    1992        5781 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    1993             :         unsigned int i;
    1994             : 
    1995        5781 :         DBG_ENTER("mysqlnd_stmt_separate_result_bind");
    1996        5781 :         if (!stmt) {
    1997           0 :                 DBG_VOID_RETURN;
    1998             :         }
    1999        5781 :         DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u", stmt->stmt_id, stmt->result_bind, stmt->field_count);
    2000             : 
    2001        5781 :         if (!stmt->result_bind) {
    2002        4085 :                 DBG_VOID_RETURN;
    2003             :         }
    2004             : 
    2005             :         /*
    2006             :           Because only the bound variables can point to our internal buffers, then
    2007             :           separate or free only them. Free is possible because the user could have
    2008             :           lost reference.
    2009             :         */
    2010        8066 :         for (i = 0; i < stmt->field_count; i++) {
    2011             :                 /* Let's try with no cache */
    2012        6370 :                 if (stmt->result_bind[i].bound == TRUE) {
    2013       12740 :                         DBG_INF_FMT("%u has refcount=%u", i, Z_REFCOUNT_P(stmt->result_bind[i].zv));
    2014             :                         /*
    2015             :                           We have to separate the actual zval value of the bound
    2016             :                           variable from our allocated zvals or we will face double-free
    2017             :                         */
    2018       12740 :                         if (Z_REFCOUNT_P(stmt->result_bind[i].zv) > 1) {
    2019             : #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
    2020             :                                 zval_copy_ctor(stmt->result_bind[i].zv);
    2021             : #endif
    2022        5508 :                                 zval_ptr_dtor(&stmt->result_bind[i].zv);
    2023             :                         } else {
    2024             :                                 /*
    2025             :                                   If it is a string, what is pointed will be freed
    2026             :                                   later in free_result(). We need to remove the variable to
    2027             :                                   which the user has lost reference.
    2028             :                                 */
    2029             : #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
    2030             :                                 ZVAL_NULL(stmt->result_bind[i].zv);
    2031             : #endif
    2032         862 :                                 zval_ptr_dtor(&stmt->result_bind[i].zv);
    2033             :                         }
    2034             :                 }
    2035             :         }
    2036        1696 :         s->m->free_result_bind(s, stmt->result_bind TSRMLS_CC);
    2037        1696 :         stmt->result_bind = NULL;
    2038             : 
    2039        1696 :         DBG_VOID_RETURN;
    2040             : }
    2041             : /* }}} */
    2042             : 
    2043             : 
    2044             : /* {{{ mysqlnd_stmt_separate_one_result_bind */
    2045             : static void
    2046         891 : mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const s, unsigned int param_no TSRMLS_DC)
    2047             : {
    2048         891 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    2049         891 :         DBG_ENTER("mysqlnd_stmt_separate_one_result_bind");
    2050         891 :         if (!stmt) {
    2051           0 :                 DBG_VOID_RETURN;
    2052             :         }
    2053         891 :         DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u param_no=%u", stmt->stmt_id, stmt->result_bind, stmt->field_count, param_no);
    2054             : 
    2055         891 :         if (!stmt->result_bind) {
    2056         429 :                 DBG_VOID_RETURN;
    2057             :         }
    2058             : 
    2059             :         /*
    2060             :           Because only the bound variables can point to our internal buffers, then
    2061             :           separate or free only them. Free is possible because the user could have
    2062             :           lost reference.
    2063             :         */
    2064             :         /* Let's try with no cache */
    2065         462 :         if (stmt->result_bind[param_no].bound == TRUE) {
    2066          78 :                 DBG_INF_FMT("%u has refcount=%u", param_no, Z_REFCOUNT_P(stmt->result_bind[param_no].zv));
    2067             :                 /*
    2068             :                   We have to separate the actual zval value of the bound
    2069             :                   variable from our allocated zvals or we will face double-free
    2070             :                 */
    2071          78 :                 if (Z_REFCOUNT_P(stmt->result_bind[param_no].zv) > 1) {
    2072             : #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
    2073             :                         zval_copy_ctor(stmt->result_bind[param_no].zv);
    2074             : #endif
    2075           0 :                         zval_ptr_dtor(&stmt->result_bind[param_no].zv);
    2076             :                 } else {
    2077             :                         /*
    2078             :                           If it is a string, what is pointed will be freed
    2079             :                           later in free_result(). We need to remove the variable to
    2080             :                           which the user has lost reference.
    2081             :                         */
    2082             : #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
    2083             :                         ZVAL_NULL(stmt->result_bind[param_no].zv);
    2084             : #endif
    2085          39 :                         zval_ptr_dtor(&stmt->result_bind[param_no].zv);
    2086             :                 }
    2087             :         }
    2088             : 
    2089         462 :         DBG_VOID_RETURN;
    2090             : }
    2091             : /* }}} */
    2092             : 
    2093             : 
    2094             : /* {{{ mysqlnd_stmt::free_stmt_content */
    2095             : static void
    2096        4261 : MYSQLND_METHOD(mysqlnd_stmt, free_stmt_content)(MYSQLND_STMT * const s TSRMLS_DC)
    2097             : {
    2098        4261 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    2099        4261 :         DBG_ENTER("mysqlnd_stmt::free_stmt_content");
    2100        4261 :         if (!stmt) {
    2101           0 :                 DBG_VOID_RETURN;
    2102             :         }
    2103        4261 :         DBG_INF_FMT("stmt=%lu param_bind=%p param_count=%u", stmt->stmt_id, stmt->param_bind, stmt->param_count);
    2104             : 
    2105             :         /* Destroy the input bind */
    2106        4261 :         if (stmt->param_bind) {
    2107             :                 unsigned int i;
    2108             :                 /*
    2109             :                   Because only the bound variables can point to our internal buffers, then
    2110             :                   separate or free only them. Free is possible because the user could have
    2111             :                   lost reference.
    2112             :                 */
    2113      134834 :                 for (i = 0; i < stmt->param_count; i++) {
    2114             :                         /*
    2115             :                           If bind_one_parameter was used, but not everything was
    2116             :                           bound and nothing was fetched, then some `zv` could be NULL
    2117             :                         */
    2118      134208 :                         if (stmt->param_bind[i].zv) {
    2119      134207 :                                 zval_ptr_dtor(&stmt->param_bind[i].zv);
    2120             :                         }
    2121             :                 }
    2122         626 :                 s->m->free_parameter_bind(s, stmt->param_bind TSRMLS_CC);
    2123         626 :                 stmt->param_bind = NULL;
    2124             :         }
    2125             : 
    2126             :         /*
    2127             :           First separate the bound variables, which point to the result set, then
    2128             :           destroy the set.
    2129             :         */
    2130        4261 :         mysqlnd_stmt_separate_result_bind(s TSRMLS_CC);
    2131             :         /* Not every statement has a result set attached */
    2132        4261 :         if (stmt->result) {
    2133        2452 :                 stmt->result->m.free_result_internal(stmt->result TSRMLS_CC);
    2134        2452 :                 stmt->result = NULL;
    2135             :         }
    2136        4261 :         if (stmt->error_info->error_list) {
    2137        4244 :                 zend_llist_clean(stmt->error_info->error_list);
    2138        4244 :                 mnd_pefree(stmt->error_info->error_list, s->persistent);
    2139        4244 :                 stmt->error_info->error_list = NULL;
    2140             :         }
    2141             : 
    2142        4261 :         DBG_VOID_RETURN;
    2143             : }
    2144             : /* }}} */
    2145             : 
    2146             : 
    2147             : /* {{{ mysqlnd_stmt::net_close */
    2148             : static enum_func_status
    2149        4244 : MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const s, zend_bool implicit TSRMLS_DC)
    2150             : {
    2151        4244 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    2152             :         MYSQLND_CONN_DATA * conn;
    2153             :         zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */];
    2154        4244 :         enum_mysqlnd_collected_stats statistic = STAT_LAST;
    2155             : 
    2156        4244 :         DBG_ENTER("mysqlnd_stmt::net_close");
    2157        4244 :         if (!stmt || !stmt->conn) {
    2158           0 :                 DBG_RETURN(FAIL);
    2159             :         }
    2160        4244 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
    2161             : 
    2162        4244 :         conn = stmt->conn;
    2163             : 
    2164        4244 :         SET_EMPTY_ERROR(*stmt->error_info);
    2165        4244 :         SET_EMPTY_ERROR(*stmt->conn->error_info);
    2166             : 
    2167             :         /*
    2168             :           If the user decided to close the statement right after execute()
    2169             :           We have to call the appropriate use_result() or store_result() and
    2170             :           clean.
    2171             :         */
    2172             :         do {
    2173        4245 :                 if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
    2174          16 :                         DBG_INF("fetching result set header");
    2175          16 :                         stmt->default_rset_handler(s TSRMLS_CC);
    2176          16 :                         stmt->state = MYSQLND_STMT_USER_FETCHING;
    2177             :                 }
    2178             : 
    2179             :                 /* unbuffered set not fetched to the end ? Clean the line */
    2180        4245 :                 if (stmt->result) {
    2181        2436 :                         DBG_INF("skipping result");
    2182        2436 :                         stmt->result->m.skip_result(stmt->result TSRMLS_CC);
    2183             :                 }
    2184        4245 :         } while (mysqlnd_stmt_more_results(s) && mysqlnd_stmt_next_result(s) == PASS);
    2185             :         /*
    2186             :           After this point we are allowed to free the result set,
    2187             :           as we have cleaned the line
    2188             :         */
    2189        4244 :         if (stmt->stmt_id) {
    2190        4215 :                 MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE?  STAT_FREE_RESULT_IMPLICIT:
    2191             :                                                                                                                 STAT_FREE_RESULT_EXPLICIT);
    2192             : 
    2193        4215 :                 int4store(cmd_buf, stmt->stmt_id);
    2194        8405 :                 if (CONN_GET_STATE(conn) == CONN_READY &&
    2195        4190 :                         FAIL == conn->m->simple_command(conn, COM_STMT_CLOSE, cmd_buf, sizeof(cmd_buf),
    2196             :                                                                                    PROT_LAST /* COM_STMT_CLOSE doesn't send an OK packet*/,
    2197             :                                                                                    FALSE, TRUE TSRMLS_CC)) {
    2198           0 :                         COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
    2199           0 :                         DBG_RETURN(FAIL);
    2200             :                 }
    2201             :         }
    2202        4244 :         switch (stmt->execute_count) {
    2203             :                 case 0:
    2204         261 :                         statistic = STAT_PS_PREPARED_NEVER_EXECUTED;
    2205         261 :                         break;
    2206             :                 case 1:
    2207        3663 :                         statistic = STAT_PS_PREPARED_ONCE_USED;
    2208             :                         break;
    2209             :                 default:
    2210             :                         break;
    2211             :         }
    2212        4244 :         if (statistic != STAT_LAST) {
    2213        3924 :                 MYSQLND_INC_CONN_STATISTIC(conn->stats, statistic);
    2214             :         }
    2215             : 
    2216        4244 :         if (stmt->execute_cmd_buffer.buffer) {
    2217        4244 :                 mnd_pefree(stmt->execute_cmd_buffer.buffer, stmt->persistent);
    2218        4244 :                 stmt->execute_cmd_buffer.buffer = NULL;
    2219             :         }
    2220             : 
    2221        4244 :         s->m->free_stmt_content(s TSRMLS_CC);
    2222             : 
    2223        4244 :         if (stmt->conn) {
    2224        4244 :                 stmt->conn->m->free_reference(stmt->conn TSRMLS_CC);
    2225        4244 :                 stmt->conn = NULL;
    2226             :         }
    2227             : 
    2228        4244 :         DBG_RETURN(PASS);
    2229             : }
    2230             : /* }}} */
    2231             : 
    2232             : /* {{{ mysqlnd_stmt::dtor */
    2233             : static enum_func_status
    2234        4244 : MYSQLND_METHOD(mysqlnd_stmt, dtor)(MYSQLND_STMT * const s, zend_bool implicit TSRMLS_DC)
    2235             : {
    2236        4244 :         MYSQLND_STMT_DATA * stmt = (s != NULL) ? s->data:NULL;
    2237        4244 :         enum_func_status ret = FAIL;
    2238        4244 :         zend_bool persistent = (s != NULL) ? s->persistent : 0;
    2239             : 
    2240        4244 :         DBG_ENTER("mysqlnd_stmt::dtor");
    2241        4244 :         if (stmt) {
    2242        4244 :                 DBG_INF_FMT("stmt=%p", stmt);
    2243             : 
    2244        4244 :                 MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE?  STAT_STMT_CLOSE_IMPLICIT:
    2245             :                                                                                                                 STAT_STMT_CLOSE_EXPLICIT);
    2246             : 
    2247        4244 :                 ret = s->m->net_close(s, implicit TSRMLS_CC);
    2248        4244 :                 mnd_pefree(stmt, persistent);
    2249             :         }
    2250        4244 :         mnd_pefree(s, persistent);
    2251             : 
    2252        4244 :         DBG_INF(ret == PASS? "PASS":"FAIL");
    2253        4244 :         DBG_RETURN(ret);
    2254             : }
    2255             : /* }}} */
    2256             : 
    2257             : 
    2258             : /* {{{ mysqlnd_stmt::alloc_param_bind */
    2259             : static MYSQLND_PARAM_BIND *
    2260       20879 : MYSQLND_METHOD(mysqlnd_stmt, alloc_param_bind)(MYSQLND_STMT * const s TSRMLS_DC)
    2261             : {
    2262       20879 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    2263       20879 :         DBG_ENTER("mysqlnd_stmt::alloc_param_bind");
    2264       20879 :         if (!stmt) {
    2265           0 :                 DBG_RETURN(NULL);
    2266             :         }
    2267       20879 :         DBG_RETURN(mnd_pecalloc(stmt->param_count, sizeof(MYSQLND_PARAM_BIND), stmt->persistent));
    2268             : }
    2269             : /* }}} */
    2270             : 
    2271             : 
    2272             : /* {{{ mysqlnd_stmt::alloc_result_bind */
    2273             : static MYSQLND_RESULT_BIND *
    2274        1267 : MYSQLND_METHOD(mysqlnd_stmt, alloc_result_bind)(MYSQLND_STMT * const s TSRMLS_DC)
    2275             : {
    2276        1267 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    2277        1267 :         DBG_ENTER("mysqlnd_stmt::alloc_result_bind");
    2278        1267 :         if (!stmt) {
    2279           0 :                 DBG_RETURN(NULL);
    2280             :         }
    2281        1267 :         DBG_RETURN(mnd_pecalloc(stmt->field_count, sizeof(MYSQLND_RESULT_BIND), stmt->persistent));
    2282             : }
    2283             : /* }}} */
    2284             : 
    2285             : 
    2286             : /* {{{ param_bind::free_parameter_bind */
    2287             : PHPAPI void
    2288       21063 : MYSQLND_METHOD(mysqlnd_stmt, free_parameter_bind)(MYSQLND_STMT * const s, MYSQLND_PARAM_BIND * param_bind TSRMLS_DC)
    2289             : {
    2290       21063 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    2291       21063 :         if (stmt) {
    2292       21063 :                 mnd_pefree(param_bind, stmt->persistent);
    2293             :         }
    2294       21063 : }
    2295             : /* }}} */
    2296             : 
    2297             : 
    2298             : /* {{{ mysqlnd_stmt::free_result_bind */
    2299             : PHPAPI void
    2300        1696 : MYSQLND_METHOD(mysqlnd_stmt, free_result_bind)(MYSQLND_STMT * const s, MYSQLND_RESULT_BIND * result_bind TSRMLS_DC)
    2301             : {
    2302        1696 :         MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
    2303        1696 :         if (stmt) {
    2304        1696 :                 mnd_pefree(result_bind, stmt->persistent);
    2305             :         }
    2306        1696 : }
    2307             : /* }}} */
    2308             : 
    2309             : 
    2310             : 
    2311             : MYSQLND_CLASS_METHODS_START(mysqlnd_stmt)
    2312             :         MYSQLND_METHOD(mysqlnd_stmt, prepare),
    2313             :         MYSQLND_METHOD(mysqlnd_stmt, execute),
    2314             :         MYSQLND_METHOD(mysqlnd_stmt, use_result),
    2315             :         MYSQLND_METHOD(mysqlnd_stmt, store_result),
    2316             :         MYSQLND_METHOD(mysqlnd_stmt, get_result),
    2317             :         MYSQLND_METHOD(mysqlnd_stmt, more_results),
    2318             :         MYSQLND_METHOD(mysqlnd_stmt, next_result),
    2319             :         MYSQLND_METHOD(mysqlnd_stmt, free_result),
    2320             :         MYSQLND_METHOD(mysqlnd_stmt, data_seek),
    2321             :         MYSQLND_METHOD(mysqlnd_stmt, reset),
    2322             :         MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close),
    2323             :         MYSQLND_METHOD(mysqlnd_stmt, dtor),
    2324             : 
    2325             :         MYSQLND_METHOD(mysqlnd_stmt, fetch),
    2326             : 
    2327             :         MYSQLND_METHOD(mysqlnd_stmt, bind_parameters),
    2328             :         MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter),
    2329             :         MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param),
    2330             :         MYSQLND_METHOD(mysqlnd_stmt, bind_result),
    2331             :         MYSQLND_METHOD(mysqlnd_stmt, bind_one_result),
    2332             :         MYSQLND_METHOD(mysqlnd_stmt, send_long_data),
    2333             :         MYSQLND_METHOD(mysqlnd_stmt, param_metadata),
    2334             :         MYSQLND_METHOD(mysqlnd_stmt, result_metadata),
    2335             : 
    2336             :         MYSQLND_METHOD(mysqlnd_stmt, insert_id),
    2337             :         MYSQLND_METHOD(mysqlnd_stmt, affected_rows),
    2338             :         MYSQLND_METHOD(mysqlnd_stmt, num_rows),
    2339             : 
    2340             :         MYSQLND_METHOD(mysqlnd_stmt, param_count),
    2341             :         MYSQLND_METHOD(mysqlnd_stmt, field_count),
    2342             :         MYSQLND_METHOD(mysqlnd_stmt, warning_count),
    2343             : 
    2344             :         MYSQLND_METHOD(mysqlnd_stmt, errno),
    2345             :         MYSQLND_METHOD(mysqlnd_stmt, error),
    2346             :         MYSQLND_METHOD(mysqlnd_stmt, sqlstate),
    2347             : 
    2348             :         MYSQLND_METHOD(mysqlnd_stmt, attr_get),
    2349             :         MYSQLND_METHOD(mysqlnd_stmt, attr_set),
    2350             : 
    2351             : 
    2352             :         MYSQLND_METHOD(mysqlnd_stmt, alloc_param_bind),
    2353             :         MYSQLND_METHOD(mysqlnd_stmt, alloc_result_bind),
    2354             :         MYSQLND_METHOD(mysqlnd_stmt, free_parameter_bind),
    2355             :         MYSQLND_METHOD(mysqlnd_stmt, free_result_bind),
    2356             :         MYSQLND_METHOD(mysqlnd_stmt, server_status),
    2357             :         mysqlnd_stmt_execute_generate_request,
    2358             :         mysqlnd_stmt_execute_parse_response,
    2359             :         MYSQLND_METHOD(mysqlnd_stmt, free_stmt_content),
    2360             :         MYSQLND_METHOD(mysqlnd_stmt, flush)
    2361             : MYSQLND_CLASS_METHODS_END;
    2362             : 
    2363             : 
    2364             : /* {{{ _mysqlnd_stmt_init */
    2365             : MYSQLND_STMT *
    2366        4244 : _mysqlnd_stmt_init(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
    2367             : {
    2368             :         MYSQLND_STMT * ret;
    2369        4244 :         DBG_ENTER("_mysqlnd_stmt_init");
    2370        4244 :         ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_prepared_statement(conn TSRMLS_CC);
    2371        4244 :         DBG_RETURN(ret);
    2372             : }
    2373             : /* }}} */
    2374             : 
    2375             : 
    2376             : /* {{{ _mysqlnd_init_ps_subsystem */
    2377       21159 : void _mysqlnd_init_ps_subsystem()
    2378             : {
    2379       21159 :         mysqlnd_stmt_set_methods(&MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_stmt));
    2380       21159 :         _mysqlnd_init_ps_fetch_subsystem();
    2381       21159 : }
    2382             : /* }}} */
    2383             : 
    2384             : 
    2385             : /*
    2386             :  * Local variables:
    2387             :  * tab-width: 4
    2388             :  * c-basic-offset: 4
    2389             :  * End:
    2390             :  * vim600: noet sw=4 ts=4 fdm=marker
    2391             :  * vim<600: noet sw=4 ts=4
    2392             :  */

Generated by: LCOV version 1.10

Generated at Wed, 16 Apr 2014 12:47:51 +0000 (3 days ago)

Copyright © 2005-2014 The PHP Group
All rights reserved.