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

Generated by: LCOV version 1.10

Generated at Sat, 13 Sep 2014 16:24:28 +0000 (7 days ago)

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