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: 882 1074 82.1 %
Date: 2015-09-02 Functions: 49 52 94.2 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10

Generated at Wed, 02 Sep 2015 17:19:13 +0000 (1 hour ago)

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