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: 853 1041 81.9 %
Date: 2014-07-25 Functions: 46 50 92.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10

Generated at Fri, 25 Jul 2014 16:54:23 +0000 (3 days ago)

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