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: 923 1116 82.7 %
Date: 2016-07-01 Functions: 48 51 94.1 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10

Generated at Fri, 01 Jul 2016 16:15:13 +0000 (17 hours ago)

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