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: 925 1119 82.7 %
Date: 2016-07-26 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@php.net>                             |
      16             :   |          Ulf Wendel <uw@php.net>                                     |
      17             :   +----------------------------------------------------------------------+
      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         486 : MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s)
      46             : {
      47             :         enum_func_status ret;
      48         486 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
      49         486 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
      50             :         MYSQLND_RES * result;
      51             : 
      52         486 :         DBG_ENTER("mysqlnd_stmt::store_result");
      53         486 :         if (!stmt || !conn || !stmt->result) {
      54           0 :                 DBG_RETURN(NULL);
      55             :         }
      56         486 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
      57             : 
      58             :         /* be compliant with libmysql - NULL will turn */
      59         486 :         if (!stmt->field_count) {
      60           0 :                 DBG_RETURN(NULL);
      61             :         }
      62             : 
      63         486 :         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         486 :         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         483 :         stmt->default_rset_handler = s->m->store_result;
      76             : 
      77         483 :         SET_EMPTY_ERROR(stmt->error_info);
      78         483 :         SET_EMPTY_ERROR(conn->error_info);
      79         483 :         MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_PS_BUFFERED_SETS);
      80             : 
      81         483 :         result = stmt->result;
      82         483 :         result->type                 = MYSQLND_RES_PS_BUF;
      83             : /*      result->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol; */
      84             : 
      85         483 :         result->stored_data  = (MYSQLND_RES_BUFFERED *) mysqlnd_result_buffered_zval_init(result->field_count, TRUE, result->persistent);
      86         483 :         if (!result->stored_data) {
      87           0 :                 SET_OOM_ERROR(conn->error_info);
      88           0 :                 DBG_RETURN(NULL);
      89             :         }
      90             : 
      91         483 :         ret = result->m.store_result_fetch_data(conn, result, result->meta, &result->stored_data->row_buffers, TRUE);
      92             : 
      93         483 :         result->stored_data->m.fetch_row = mysqlnd_stmt_fetch_row_buffered;
      94             : 
      95         483 :         if (PASS == ret) {
      96         483 :                 if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_ZVAL) {
      97         483 :                         MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data;
      98         483 :                         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         468 :                                 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         468 :                                 set->data = mnd_emalloc((size_t)(result->stored_data->row_count * result->meta->field_count * sizeof(zval)));
     106         468 :                                 if (!set->data) {
     107           0 :                                         SET_OOM_ERROR(conn->error_info);
     108           0 :                                         DBG_RETURN(NULL);
     109             :                                 }
     110         468 :                                 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         483 :                         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         483 :                 UPSERT_STATUS_SET_AFFECTED_ROWS(stmt->upsert_status, stmt->result->stored_data->row_count);
     120             : 
     121         483 :                 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         483 :         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        6632 : MYSQLND_METHOD(mysqlnd_stmt, more_results)(const MYSQLND_STMT * s)
     205             : {
     206        6632 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
     207        6632 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
     208        6632 :         DBG_ENTER("mysqlnd_stmt::more_results");
     209             :         /* (conn->state == CONN_NEXT_RESULT_PENDING) too */
     210        6632 :         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         828 : mysqlnd_stmt_skip_metadata(MYSQLND_STMT * s)
     247             : {
     248         828 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
     249         828 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
     250             :         /* Follows parameter metadata, we have just to skip it, as libmysql does */
     251         828 :         unsigned int i = 0;
     252         828 :         enum_func_status ret = FAIL;
     253             :         MYSQLND_PACKET_RES_FIELD * field_packet;
     254             : 
     255         828 :         DBG_ENTER("mysqlnd_stmt_skip_metadata");
     256         828 :         if (!stmt || !conn) {
     257           0 :                 DBG_RETURN(FAIL);
     258             :         }
     259         828 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
     260             : 
     261         828 :         field_packet = conn->payload_decoder_factory->m.get_result_field_packet(conn->payload_decoder_factory, FALSE);
     262         828 :         if (!field_packet) {
     263           0 :                 SET_OOM_ERROR(stmt->error_info);
     264           0 :                 SET_OOM_ERROR(conn->error_info);
     265             :         } else {
     266         828 :                 ret = PASS;
     267         828 :                 field_packet->skip_parsing = TRUE;
     268      135245 :                 for (;i < stmt->param_count; i++) {
     269      134417 :                         if (FAIL == PACKET_READ(field_packet)) {
     270           0 :                                 ret = FAIL;
     271           0 :                                 break;
     272             :                         }
     273             :                 }
     274         828 :                 PACKET_FREE(field_packet);
     275             :         }
     276             : 
     277         828 :         DBG_RETURN(ret);
     278             : }
     279             : /* }}} */
     280             : 
     281             : 
     282             : /* {{{ mysqlnd_stmt_read_prepare_response */
     283             : static enum_func_status
     284        4256 : mysqlnd_stmt_read_prepare_response(MYSQLND_STMT * s)
     285             : {
     286        4256 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
     287        4256 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
     288             :         MYSQLND_PACKET_PREPARE_RESPONSE * prepare_resp;
     289        4256 :         enum_func_status ret = FAIL;
     290             : 
     291        4256 :         DBG_ENTER("mysqlnd_stmt_read_prepare_response");
     292        4256 :         if (!stmt || !conn) {
     293           0 :                 DBG_RETURN(FAIL);
     294             :         }
     295        4256 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
     296             : 
     297        4256 :         prepare_resp = conn->payload_decoder_factory->m.get_prepare_response_packet(conn->payload_decoder_factory, FALSE);
     298        4256 :         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        4256 :         if (FAIL == PACKET_READ(prepare_resp)) {
     305           1 :                 goto done;
     306             :         }
     307             : 
     308        4255 :         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        4229 :         ret = PASS;
     314        4229 :         stmt->stmt_id = prepare_resp->stmt_id;
     315        4229 :         UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, prepare_resp->warning_count);
     316        4229 :         UPSERT_STATUS_SET_AFFECTED_ROWS(stmt->upsert_status, 0);  /* be like libmysql */
     317        4229 :         stmt->field_count = conn->field_count = prepare_resp->field_count;
     318        4229 :         stmt->param_count = prepare_resp->param_count;
     319             : done:
     320        4256 :         PACKET_FREE(prepare_resp);
     321             : 
     322        4256 :         DBG_RETURN(ret);
     323             : }
     324             : /* }}} */
     325             : 
     326             : 
     327             : /* {{{ mysqlnd_stmt_prepare_read_eof */
     328             : static enum_func_status
     329        3264 : mysqlnd_stmt_prepare_read_eof(MYSQLND_STMT * s)
     330             : {
     331        3264 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
     332        3264 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
     333             :         MYSQLND_PACKET_EOF * fields_eof;
     334        3264 :         enum_func_status ret = FAIL;
     335             : 
     336        3264 :         DBG_ENTER("mysqlnd_stmt_prepare_read_eof");
     337        3264 :         if (!stmt || !conn) {
     338           0 :                 DBG_RETURN(FAIL);
     339             :         }
     340        3264 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
     341             : 
     342        3264 :         fields_eof = conn->payload_decoder_factory->m.get_eof_packet(conn->payload_decoder_factory, FALSE);
     343        3264 :         if (!fields_eof) {
     344           0 :                 SET_OOM_ERROR(stmt->error_info);
     345           0 :                 SET_OOM_ERROR(conn->error_info);
     346             :         } else {
     347        3264 :                 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        3264 :                         UPSERT_STATUS_SET_SERVER_STATUS(stmt->upsert_status, fields_eof->server_status);
     361        3264 :                         UPSERT_STATUS_SET_WARNINGS(stmt->upsert_status, fields_eof->warning_count);
     362        3264 :                         stmt->state = MYSQLND_STMT_PREPARED;
     363             :                 }
     364        3264 :                 PACKET_FREE(fields_eof);
     365             :         }
     366             : 
     367        3264 :         DBG_RETURN(ret);
     368             : }
     369             : /* }}} */
     370             : 
     371             : 
     372             : /* {{{ mysqlnd_stmt::prepare */
     373             : static enum_func_status
     374        4260 : MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const s, const char * const query, const size_t query_len)
     375             : {
     376        4260 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
     377        4260 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
     378        4260 :         MYSQLND_STMT * s_to_prepare = s;
     379        4260 :         MYSQLND_STMT_DATA * stmt_to_prepare = stmt;
     380             : 
     381        4260 :         DBG_ENTER("mysqlnd_stmt::prepare");
     382        4260 :         if (!stmt || !conn) {
     383           0 :                 DBG_RETURN(FAIL);
     384             :         }
     385        4260 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
     386        4260 :         DBG_INF_FMT("query=%s", query);
     387             : 
     388        4260 :         UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(stmt->upsert_status);
     389        4260 :         UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
     390             : 
     391        4260 :         SET_EMPTY_ERROR(stmt->error_info);
     392        4260 :         SET_EMPTY_ERROR(conn->error_info);
     393             : 
     394        4260 :         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        4260 :                 enum_func_status ret = FAIL;
     418        4260 :                 const MYSQLND_CSTRING query_string = {query, query_len};
     419        4260 :                 struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_STMT_PREPARE, conn, query_string);
     420        4260 :                 if (command) {
     421        4260 :                         ret = command->run(command);
     422        4260 :                         command->free_command(command);
     423             :                 }
     424        4260 :                 if (FAIL == ret) {
     425           4 :                         goto fail;
     426             :                 }
     427             :         }
     428             : 
     429        4256 :         if (FAIL == mysqlnd_stmt_read_prepare_response(s_to_prepare)) {
     430          27 :                 goto fail;
     431             :         }
     432             : 
     433        4229 :         if (stmt_to_prepare->param_count) {
     434        1656 :                 if (FAIL == mysqlnd_stmt_skip_metadata(s_to_prepare) ||
     435         828 :                         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        4229 :         if (stmt_to_prepare->field_count) {
     447        2436 :                 MYSQLND_RES * result = conn->m->result_init(stmt_to_prepare->field_count, stmt_to_prepare->persistent);
     448        2436 :                 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        2436 :                 stmt_to_prepare->result = result;
     454             : 
     455        2436 :                 result->conn = conn->m->get_reference(conn);
     456             : 
     457        2436 :                 result->type = MYSQLND_RES_PS_BUF;
     458             : 
     459        4872 :                 if (FAIL == result->m.read_result_metadata(result, conn) ||
     460        2436 :                         FAIL == mysqlnd_stmt_prepare_read_eof(s_to_prepare))
     461             :                 {
     462             :                         goto fail;
     463             :                 }
     464             :         }
     465             : 
     466        4229 :         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        4229 :         stmt->state = MYSQLND_STMT_PREPARED;
     482        4229 :         DBG_INF("PASS");
     483        4229 :         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        4930 : mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s, enum_mysqlnd_parse_exec_response_type type)
     500             : {
     501        4930 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
     502        4930 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
     503             :         enum_func_status ret;
     504             : 
     505        4930 :         DBG_ENTER("mysqlnd_stmt_execute_parse_response");
     506        4930 :         if (!stmt || !conn) {
     507           0 :                 DBG_RETURN(FAIL);
     508             :         }
     509        4930 :         SET_CONNECTION_STATE(&conn->state, CONN_QUERY_SENT);
     510             : 
     511        4930 :         ret = conn->m->query_read_result_set_header(conn, s);
     512        4930 :         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        4922 :                 SET_EMPTY_ERROR(stmt->error_info);
     530        4922 :                 SET_EMPTY_ERROR(conn->error_info);
     531        4922 :                 UPSERT_STATUS_SET_WARNINGS(stmt->upsert_status, UPSERT_STATUS_GET_WARNINGS(conn->upsert_status));
     532        4922 :                 UPSERT_STATUS_SET_AFFECTED_ROWS(stmt->upsert_status, UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status));
     533        4922 :                 UPSERT_STATUS_SET_SERVER_STATUS(stmt->upsert_status, UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
     534        4922 :                 UPSERT_STATUS_SET_LAST_INSERT_ID(stmt->upsert_status, UPSERT_STATUS_GET_LAST_INSERT_ID(conn->upsert_status));
     535             : 
     536        4922 :                 stmt->state = MYSQLND_STMT_EXECUTED;
     537        4922 :                 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        2331 :                 stmt->result->type = MYSQLND_RES_PS_BUF;
     543        2331 :                 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        2331 :                 stmt->field_count = stmt->result->field_count = conn->field_count;
     553        2331 :                 if (stmt->result->stored_data) {
     554           0 :                         stmt->result->stored_data->lengths = NULL;
     555        2331 :                 } else if (stmt->result->unbuf) {
     556           0 :                         stmt->result->unbuf->lengths = NULL;
     557             :                 }
     558        2331 :                 if (stmt->field_count) {
     559        2331 :                         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        2331 :                         DBG_INF_FMT("server_status=%u cursor=%u", UPSERT_STATUS_GET_SERVER_STATUS(stmt->upsert_status),
     566        2331 :                                                 UPSERT_STATUS_GET_SERVER_STATUS(stmt->upsert_status) & SERVER_STATUS_CURSOR_EXISTS);
     567             : 
     568        2331 :                         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        2326 :                         } 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        2326 :                                 DBG_INF("no cursor");
     592             :                                 /* preferred is unbuffered read */
     593        2326 :                                 stmt->default_rset_handler = s->m->use_result;
     594        2326 :                                 DBG_INF("use_result");
     595             :                         }
     596             :                 }
     597             :         }
     598             : #ifndef MYSQLND_DONT_SKIP_OUT_PARAMS_RESULTSET
     599        2339 :         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        2339 :         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        2339 :         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        2339 :         DBG_INF(ret == PASS? "PASS":"FAIL");
     614        2339 :         DBG_RETURN(ret);
     615             : }
     616             : /* }}} */
     617             : 
     618             : 
     619             : /* {{{ mysqlnd_stmt::execute */
     620             : static enum_func_status
     621        4910 : MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const s)
     622             : {
     623        4910 :         DBG_ENTER("mysqlnd_stmt::execute");
     624        9813 :         if (FAIL == s->m->send_execute(s, MYSQLND_SEND_EXECUTE_IMPLICIT, NULL, NULL) ||
     625        4903 :                 FAIL == s->m->parse_execute_response(s, MYSQLND_PARSE_EXEC_RESPONSE_IMPLICIT))
     626             :         {
     627          15 :                 DBG_RETURN(FAIL);
     628             :         }
     629        4895 :         DBG_RETURN(PASS);
     630             : }
     631             : /* }}} */
     632             : 
     633             : 
     634             : /* {{{ mysqlnd_stmt::send_execute */
     635             : static enum_func_status
     636        4910 : 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        4910 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
     639        4910 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
     640             :         enum_func_status ret;
     641        4910 :         zend_uchar *request = NULL;
     642             :         size_t          request_len;
     643             :         zend_bool       free_request;
     644             : 
     645        4910 :         DBG_ENTER("mysqlnd_stmt::send_execute");
     646        4910 :         if (!stmt || !conn) {
     647           0 :                 DBG_RETURN(FAIL);
     648             :         }
     649        4910 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
     650             : 
     651        4910 :         UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(stmt->upsert_status);
     652        4910 :         UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
     653             : 
     654        7215 :         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        2305 :                 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        2305 :                 stmt->result->m.free_result_buffers(stmt->result);
     696             : 
     697        2305 :                 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        4908 :         if (stmt->param_count) {
     706        1446 :                 unsigned int i, not_bound = 0;
     707        1446 :                 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      137273 :                 for (i = 0; i < stmt->param_count; i++) {
     713      271656 :                         if (Z_ISUNDEF(stmt->param_bind[i].zv)) {
     714           0 :                                 not_bound++;
     715             :                         }
     716             :                 }
     717        1445 :                 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        4907 :         ret = s->m->generate_execute_request(s, &request, &request_len, &free_request);
     730        4907 :         if (ret == PASS) {
     731        4907 :                 const MYSQLND_CSTRING payload = {(const char*) request, request_len};
     732        4907 :                 struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_STMT_EXECUTE, conn, payload);
     733        4907 :                 ret = FAIL;
     734        4907 :                 if (command) {
     735        4907 :                         ret = command->run(command);
     736        4907 :                         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        4907 :         if (free_request) {
     743          18 :                 mnd_efree(request);
     744             :         }
     745             : 
     746        4907 :         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        4903 :         stmt->execute_count++;
     752             : 
     753        4903 :         DBG_RETURN(PASS);
     754             : }
     755             : /* }}} */
     756             : 
     757             : 
     758             : /* {{{ mysqlnd_stmt_fetch_row_buffered */
     759             : enum_func_status
     760        1769 : mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything)
     761             : {
     762        1769 :         MYSQLND_STMT * s = (MYSQLND_STMT *) param;
     763        1769 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
     764        1769 :         const MYSQLND_RES_METADATA * const meta = result->meta;
     765        1769 :         unsigned int field_count = meta->field_count;
     766             : 
     767        1769 :         DBG_ENTER("mysqlnd_stmt_fetch_row_buffered");
     768        1769 :         *fetched_anything = FALSE;
     769        1769 :         DBG_INF_FMT("stmt=%lu", stmt != NULL ? stmt->stmt_id : 0L);
     770             : 
     771             :         /* If we haven't read everything */
     772        1769 :         if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_ZVAL) {
     773        1769 :                 MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data;
     774        5221 :                 if (set->data_cursor &&
     775        1757 :                         (set->data_cursor - set->data) < (result->stored_data->row_count * field_count))
     776             :                 {
     777             :                         /* The user could have skipped binding - don't crash*/
     778        1695 :                         if (stmt->result_bind) {
     779             :                                 unsigned int i;
     780        1695 :                                 zval *current_row = set->data_cursor;
     781             : 
     782        1695 :                                 if (Z_ISUNDEF(current_row[0])) {
     783        1695 :                                         uint64_t row_num = (set->data_cursor - set->data) / field_count;
     784        6780 :                                         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        1695 :                                                                                                         meta->fields,
     788        1695 :                                                                                                         result->conn->options->int_and_float_native,
     789        3390 :                                                                                                         result->conn->stats);
     790        1695 :                                         if (PASS != rc) {
     791           0 :                                                 DBG_RETURN(FAIL);
     792             :                                         }
     793        1695 :                                         result->stored_data->initialized_rows++;
     794        1695 :                                         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        6159 :                                 for (i = 0; i < result->field_count; i++) {
     812        4464 :                                         zval *result = &stmt->result_bind[i].zv;
     813             : 
     814        4464 :                                         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        4464 :                                         if (stmt->result_bind[i].bound == TRUE) {
     821        8928 :                                                 DBG_INF_FMT("i=%u type=%u", i, Z_TYPE(current_row[i]));
     822        8928 :                                                 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        4456 :                                                         ZVAL_COPY_VALUE(result, &current_row[i]);
     832             : #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
     833        4456 :                                                         Z_TRY_ADDREF_P(result);
     834             : #endif
     835             :                                                 } else {
     836           8 :                                                         ZVAL_NULL(result);
     837             :                                                 }
     838             :                                         }
     839             :                                 }
     840             :                         }
     841        1695 :                         set->data_cursor += field_count;
     842        1695 :                         *fetched_anything = TRUE;
     843             :                         /* buffered result sets don't have a connection */
     844        1695 :                         MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_PS_BUF);
     845        1695 :                         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        1769 :         DBG_INF("PASS");
     854        1769 :         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       36786 :         if (PASS == (ret = PACKET_READ(row_packet)) && !row_packet->eof) {
     896       17764 :                 unsigned int i, field_count = result->field_count;
     897             : 
     898       17764 :                 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         165 :                         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         330 :                         row_packet->result_set_memory_pool->free_chunk(
     951         165 :                                 row_packet->result_set_memory_pool, row_packet->row_buffer);
     952         165 :                         row_packet->row_buffer = NULL;
     953             :                 }
     954             : 
     955       17764 :                 result->unbuf->row_count++;
     956       17764 :                 *fetched_anything = TRUE;
     957        1258 :         } else if (ret == FAIL) {
     958           0 :                 if (row_packet->error_info.error_no) {
     959           0 :                         COPY_CLIENT_ERROR(conn->error_info, row_packet->error_info);
     960           0 :                         COPY_CLIENT_ERROR(stmt->error_info, row_packet->error_info);
     961             :                 }
     962           0 :                 SET_CONNECTION_STATE(&conn->state, CONN_READY);
     963           0 :                 result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
     964        1258 :         } else if (row_packet->eof) {
     965        1258 :                 DBG_INF("EOF");
     966             :                 /* Mark the connection as usable again */
     967        1258 :                 result->unbuf->eof_reached = TRUE;
     968        1258 :                 UPSERT_STATUS_RESET(conn->upsert_status);
     969        1258 :                 UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, row_packet->warning_count);
     970        1258 :                 UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, row_packet->server_status);
     971             : 
     972             :                 /*
     973             :                   result->row_packet will be cleaned when
     974             :                   destroying the result object
     975             :                 */
     976        1258 :                 if (UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_MORE_RESULTS_EXISTS) {
     977          11 :                         SET_CONNECTION_STATE(&conn->state, CONN_NEXT_RESULT_PENDING);
     978             :                 } else {
     979        1247 :                         SET_CONNECTION_STATE(&conn->state, CONN_READY);
     980             :                 }
     981             :         }
     982             : 
     983       19022 :         DBG_INF_FMT("ret=%s fetched_anything=%u", ret == PASS? "PASS":"FAIL", *fetched_anything);
     984       19022 :         DBG_RETURN(ret);
     985             : }
     986             : /* }}} */
     987             : 
     988             : 
     989             : /* {{{ mysqlnd_stmt::use_result */
     990             : static MYSQLND_RES *
     991        1270 : MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s)
     992             : {
     993        1270 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
     994        1270 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
     995             :         MYSQLND_RES * result;
     996             : 
     997        1270 :         DBG_ENTER("mysqlnd_stmt::use_result");
     998        1270 :         if (!stmt || !conn || !stmt->result) {
     999           0 :                 DBG_RETURN(NULL);
    1000             :         }
    1001        1270 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
    1002             : 
    1003        6344 :         if (!stmt->field_count ||
    1004        2535 :                 (!stmt->cursor_exists && GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA) ||
    1005        1272 :                 (stmt->cursor_exists && GET_CONNECTION_STATE(&conn->state) != CONN_READY) ||
    1006        1267 :                 (stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE))
    1007             :         {
    1008           3 :                 SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
    1009           3 :                 DBG_ERR("command out of sync");
    1010           3 :                 DBG_RETURN(NULL);
    1011             :         }
    1012             : 
    1013        1267 :         SET_EMPTY_ERROR(stmt->error_info);
    1014             : 
    1015        1267 :         MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_PS_UNBUFFERED_SETS);
    1016        1267 :         result = stmt->result;
    1017             : 
    1018        1267 :         result->m.use_result(stmt->result, TRUE);
    1019        1267 :         result->unbuf->m.fetch_row        = stmt->cursor_exists? mysqlnd_fetch_stmt_row_cursor:
    1020             :                                                                                            mysqlnd_stmt_fetch_row_unbuffered;
    1021        1267 :         stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;
    1022             : 
    1023        1267 :         DBG_INF_FMT("%p", result);
    1024        1267 :         DBG_RETURN(result);
    1025             : }
    1026             : /* }}} */
    1027             : 
    1028             : 
    1029             : /* {{{ mysqlnd_fetch_row_cursor */
    1030             : enum_func_status
    1031          35 : mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything)
    1032             : {
    1033             :         enum_func_status ret;
    1034          35 :         MYSQLND_STMT * s = (MYSQLND_STMT *) param;
    1035          35 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1036          35 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
    1037             :         zend_uchar buf[MYSQLND_STMT_ID_LENGTH /* statement id */ + 4 /* number of rows to fetch */];
    1038             :         MYSQLND_PACKET_ROW * row_packet;
    1039             : 
    1040          35 :         DBG_ENTER("mysqlnd_fetch_stmt_row_cursor");
    1041             : 
    1042          35 :         if (!stmt || !stmt->conn || !result || !result->conn || !result->unbuf) {
    1043           4 :                 DBG_ERR("no statement");
    1044           4 :                 DBG_RETURN(FAIL);
    1045             :         }
    1046          31 :         DBG_INF_FMT("stmt=%lu flags=%u", stmt->stmt_id, flags);
    1047             : 
    1048          31 :         if (stmt->state < MYSQLND_STMT_USER_FETCHING) {
    1049             :                 /* Only initted - error */
    1050           0 :                 SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
    1051           0 :                 DBG_ERR("command out of sync");
    1052           0 :                 DBG_RETURN(FAIL);
    1053             :         }
    1054          31 :         if (!(row_packet = result->unbuf->row_packet)) {
    1055           0 :                 DBG_RETURN(FAIL);
    1056             :         }
    1057             : 
    1058          31 :         SET_EMPTY_ERROR(stmt->error_info);
    1059          31 :         SET_EMPTY_ERROR(conn->error_info);
    1060             : 
    1061          31 :         int4store(buf, stmt->stmt_id);
    1062          31 :         int4store(buf + MYSQLND_STMT_ID_LENGTH, 1); /* for now fetch only one row */
    1063             : 
    1064             :         {
    1065          31 :                 const MYSQLND_CSTRING payload = {(const char*) buf, sizeof(buf)};
    1066          31 :                 struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_STMT_FETCH, conn, payload);
    1067          31 :                 ret = FAIL;
    1068          31 :                 if (command) {
    1069          31 :                         ret = command->run(command);
    1070          31 :                         command->free_command(command);
    1071          31 :                         if (ret == FAIL) {
    1072           0 :                                 COPY_CLIENT_ERROR(stmt->error_info, *conn->error_info);   
    1073             :                         }
    1074             :                 }
    1075          31 :                 if (FAIL == ret) {
    1076           0 :                         DBG_RETURN(FAIL);
    1077             :                 }
    1078             : 
    1079             :         }
    1080             : 
    1081          31 :         row_packet->skip_extraction = stmt->result_bind? FALSE:TRUE;
    1082             : 
    1083          31 :         UPSERT_STATUS_RESET(stmt->upsert_status);
    1084          59 :         if (PASS == (ret = PACKET_READ(row_packet)) && !row_packet->eof) {
    1085          28 :                 const MYSQLND_RES_METADATA * const meta = result->meta;
    1086          28 :                 unsigned int i, field_count = result->field_count;
    1087             : 
    1088          28 :                 if (!row_packet->skip_extraction) {
    1089          28 :                         result->unbuf->m.free_last_data(result->unbuf, conn->stats);
    1090             : 
    1091          28 :                         result->unbuf->last_row_data = row_packet->fields;
    1092          28 :                         result->unbuf->last_row_buffer = row_packet->row_buffer;
    1093          28 :                         row_packet->fields = NULL;
    1094          28 :                         row_packet->row_buffer = NULL;
    1095             : 
    1096         112 :                         if (PASS != result->unbuf->m.row_decoder(result->unbuf->last_row_buffer,
    1097          28 :                                                                           result->unbuf->last_row_data,
    1098             :                                                                           row_packet->field_count,
    1099          28 :                                                                           row_packet->fields_metadata,
    1100          28 :                                                                           conn->options->int_and_float_native,
    1101             :                                                                           conn->stats))
    1102             :                         {
    1103           0 :                                 DBG_RETURN(FAIL);
    1104             :                         }
    1105             : 
    1106             :                         /* If no result bind, do nothing. We consumed the data */
    1107          62 :                         for (i = 0; i < field_count; i++) {
    1108          34 :                                 if (stmt->result_bind[i].bound == TRUE) {
    1109          34 :                                         zval *data = &result->unbuf->last_row_data[i];
    1110          34 :                                         zval *result = &stmt->result_bind[i].zv;
    1111             : 
    1112          34 :                                         ZVAL_DEREF(result);
    1113             :                                         /*
    1114             :                                           stmt->result_bind[i].zv has been already destructed
    1115             :                                           in result->unbuf->m.free_last_data()
    1116             :                                         */
    1117             : #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
    1118             :                                         zval_dtor(result);
    1119             : #endif
    1120         102 :                                         DBG_INF_FMT("i=%u bound_var=%p type=%u refc=%u", i, &stmt->result_bind[i].zv,
    1121          34 :                                                                 Z_TYPE_P(data), Z_REFCOUNTED(stmt->result_bind[i].zv)?
    1122          34 :                                                                 Z_REFCOUNT(stmt->result_bind[i].zv) : 0);
    1123             : 
    1124          34 :                                         if (!Z_ISNULL_P(data)) {
    1125          42 :                                                 if ((Z_TYPE_P(data) == IS_STRING) &&
    1126           9 :                                                                 (meta->fields[i].max_length < (zend_ulong) Z_STRLEN_P(data))) {
    1127           2 :                                                         meta->fields[i].max_length = Z_STRLEN_P(data);
    1128             :                                                 }
    1129          33 :                                                 ZVAL_COPY_VALUE(result, data);
    1130             :                                                 /* copied data, thus also the ownership. Thus null data */
    1131          33 :                                                 ZVAL_NULL(data);
    1132             :                                         } else {
    1133           1 :                                                 ZVAL_NULL(result);
    1134             :                                         }
    1135             :                                 }
    1136             :                         }
    1137             :                 } else {
    1138           0 :                         DBG_INF("skipping extraction");
    1139             :                         /*
    1140             :                           Data has been allocated and usually result->unbuf->m.free_last_data()
    1141             :                           frees it but we can't call this function as it will cause problems with
    1142             :                           the bound variables. Thus we need to do part of what it does or Zend will
    1143             :                           report leaks.
    1144             :                         */
    1145           0 :                         row_packet->result_set_memory_pool->free_chunk(
    1146           0 :                                 row_packet->result_set_memory_pool, row_packet->row_buffer);
    1147           0 :                         row_packet->row_buffer = NULL;
    1148             :                 }
    1149             :                 /* We asked for one row, the next one should be EOF, eat it */
    1150          28 :                 ret = PACKET_READ(row_packet);
    1151          28 :                 if (row_packet->row_buffer) {
    1152          56 :                         row_packet->result_set_memory_pool->free_chunk(
    1153          28 :                                 row_packet->result_set_memory_pool, row_packet->row_buffer);
    1154          28 :                         row_packet->row_buffer = NULL;
    1155             :                 }
    1156          28 :                 MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_CURSOR);
    1157             : 
    1158          28 :                 result->unbuf->row_count++;
    1159          28 :                 *fetched_anything = TRUE;
    1160             :         } else {
    1161           3 :                 *fetched_anything = FALSE;
    1162           3 :                 UPSERT_STATUS_SET_WARNINGS(stmt->upsert_status, row_packet->warning_count);
    1163           3 :                 UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, row_packet->warning_count);
    1164             : 
    1165           3 :                 UPSERT_STATUS_SET_SERVER_STATUS(stmt->upsert_status, row_packet->server_status);
    1166           3 :                 UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, row_packet->server_status);
    1167             : 
    1168           3 :                 result->unbuf->eof_reached = row_packet->eof;
    1169             :         }
    1170          31 :         UPSERT_STATUS_SET_WARNINGS(stmt->upsert_status, row_packet->warning_count);
    1171          31 :         UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, row_packet->warning_count);
    1172             : 
    1173          31 :         UPSERT_STATUS_SET_SERVER_STATUS(stmt->upsert_status, row_packet->server_status);
    1174          31 :         UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, row_packet->server_status);
    1175             : 
    1176         124 :         DBG_INF_FMT("ret=%s fetched=%u server_status=%u warnings=%u eof=%u",
    1177          31 :                                 ret == PASS? "PASS":"FAIL", *fetched_anything,
    1178          62 :                                 row_packet->server_status, row_packet->warning_count,
    1179          31 :                                 result->unbuf->eof_reached);
    1180          31 :         DBG_RETURN(ret);
    1181             : }
    1182             : /* }}} */
    1183             : 
    1184             : 
    1185             : /* {{{ mysqlnd_stmt::fetch */
    1186             : static enum_func_status
    1187       19576 : MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const s, zend_bool * const fetched_anything)
    1188             : {
    1189       19576 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1190       19576 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
    1191             :         enum_func_status ret;
    1192       19576 :         DBG_ENTER("mysqlnd_stmt::fetch");
    1193       19576 :         if (!stmt || !stmt->conn) {
    1194           0 :                 DBG_RETURN(FAIL);
    1195             :         }
    1196       19576 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
    1197             : 
    1198       19576 :         if (!stmt->result || stmt->state < MYSQLND_STMT_WAITING_USE_OR_STORE) {
    1199           8 :                 SET_CLIENT_ERROR(stmt->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
    1200           8 :                 DBG_ERR("command out of sync");
    1201           8 :                 DBG_RETURN(FAIL);
    1202       19568 :         } else if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
    1203             :                 /* Execute only once. We have to free the previous contents of user's bound vars */
    1204             : 
    1205        1249 :                 stmt->default_rset_handler(s);
    1206             :         }
    1207       19568 :         stmt->state = MYSQLND_STMT_USER_FETCHING;
    1208             : 
    1209       19568 :         SET_EMPTY_ERROR(stmt->error_info);
    1210       19568 :         SET_EMPTY_ERROR(conn->error_info);
    1211             : 
    1212       19568 :         DBG_INF_FMT("result_bind=%p separated_once=%u", &stmt->result_bind, stmt->result_zvals_separated_once);
    1213             :         /*
    1214             :           The user might have not bound any variables for result.
    1215             :           Do the binding once she does it.
    1216             :         */
    1217       19568 :         if (stmt->result_bind && !stmt->result_zvals_separated_once) {
    1218             :                 unsigned int i;
    1219             :                 /*
    1220             :                   mysqlnd_stmt_store_result() has been called free the bind
    1221             :                   variables to prevent leaking of their previous content.
    1222             :                 */
    1223        7811 :                 for (i = 0; i < stmt->result->field_count; i++) {
    1224        6198 :                         if (stmt->result_bind[i].bound == TRUE) {
    1225        6198 :                                 zval *result = &stmt->result_bind[i].zv;
    1226        6198 :                                 ZVAL_DEREF(result);
    1227             :                                 zval_dtor(result);
    1228        6198 :                                 ZVAL_NULL(result);
    1229             :                         }
    1230             :                 }
    1231        1613 :                 stmt->result_zvals_separated_once = TRUE;
    1232             :         }
    1233             : 
    1234       19568 :         ret = stmt->result->m.fetch_row(stmt->result, (void*)s, 0, fetched_anything);
    1235       19568 :         DBG_RETURN(ret);
    1236             : }
    1237             : /* }}} */
    1238             : 
    1239             : 
    1240             : /* {{{ mysqlnd_stmt::reset */
    1241             : static enum_func_status
    1242           6 : MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const s)
    1243             : {
    1244           6 :         enum_func_status ret = PASS;
    1245           6 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1246           6 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
    1247             : 
    1248           6 :         DBG_ENTER("mysqlnd_stmt::reset");
    1249           6 :         if (!stmt || !conn) {
    1250           0 :                 DBG_RETURN(FAIL);
    1251             :         }
    1252           6 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
    1253             : 
    1254           6 :         SET_EMPTY_ERROR(stmt->error_info);
    1255           6 :         SET_EMPTY_ERROR(conn->error_info);
    1256             : 
    1257           6 :         if (stmt->stmt_id) {
    1258           6 :                 MYSQLND_CONN_DATA * conn = stmt->conn;
    1259           6 :                 if (stmt->param_bind) {
    1260             :                         unsigned int i;
    1261           2 :                         DBG_INF("resetting long data");
    1262             :                         /* Reset Long Data */
    1263           4 :                         for (i = 0; i < stmt->param_count; i++) {
    1264           2 :                                 if (stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED) {
    1265           1 :                                         stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
    1266             :                                 }
    1267             :                         }
    1268             :                 }
    1269             : 
    1270           6 :                 s->m->flush(s);
    1271             : 
    1272             :                 /*
    1273             :                   Don't free now, let the result be usable. When the stmt will again be
    1274             :                   executed then the result set will be cleaned, the bound variables will
    1275             :                   be separated before that.
    1276             :                 */
    1277             : 
    1278           6 :                 if (GET_CONNECTION_STATE(&conn->state) == CONN_READY) {
    1279           6 :                         size_t stmt_id = stmt->stmt_id;
    1280           6 :                         struct st_mysqlnd_protocol_command * command = stmt->conn->command_factory(COM_STMT_RESET, stmt->conn, stmt_id);
    1281           6 :                         ret = FAIL;
    1282           6 :                         if (command) {
    1283           6 :                                 ret = command->run(command);
    1284           6 :                                 command->free_command(command);
    1285             : 
    1286           6 :                                 if (ret == FAIL) {
    1287           0 :                                         COPY_CLIENT_ERROR(stmt->error_info, *conn->error_info);
    1288             :                                 }
    1289             :                         }
    1290             :                 }
    1291           6 :                 *stmt->upsert_status = *conn->upsert_status;
    1292             :         }
    1293           6 :         DBG_INF(ret == PASS? "PASS":"FAIL");
    1294           6 :         DBG_RETURN(ret);
    1295             : }
    1296             : /* }}} */
    1297             : 
    1298             : 
    1299             : /* {{{ mysqlnd_stmt::flush */
    1300             : static enum_func_status
    1301        2311 : MYSQLND_METHOD(mysqlnd_stmt, flush)(MYSQLND_STMT * const s)
    1302             : {
    1303        2311 :         enum_func_status ret = PASS;
    1304        2311 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1305        2311 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
    1306             : 
    1307        2311 :         DBG_ENTER("mysqlnd_stmt::flush");
    1308        2311 :         if (!stmt || !conn) {
    1309           0 :                 DBG_RETURN(FAIL);
    1310             :         }
    1311        2311 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
    1312             : 
    1313        2311 :         if (stmt->stmt_id) {
    1314             :                 /*
    1315             :                   If the user decided to close the statement right after execute()
    1316             :                   We have to call the appropriate use_result() or store_result() and
    1317             :                   clean.
    1318             :                 */
    1319             :                 do {
    1320        2311 :                         if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
    1321           2 :                                 DBG_INF("fetching result set header");
    1322           2 :                                 stmt->default_rset_handler(s);
    1323           2 :                                 stmt->state = MYSQLND_STMT_USER_FETCHING;
    1324             :                         }
    1325             : 
    1326        2311 :                         if (stmt->result) {
    1327        2310 :                                 DBG_INF("skipping result");
    1328        2310 :                                 stmt->result->m.skip_result(stmt->result);
    1329             :                         }
    1330        2311 :                 } while (mysqlnd_stmt_more_results(s) && mysqlnd_stmt_next_result(s) == PASS);
    1331             :         }
    1332        2311 :         DBG_INF(ret == PASS? "PASS":"FAIL");
    1333        2311 :         DBG_RETURN(ret);
    1334             : }
    1335             : /* }}} */
    1336             : 
    1337             : 
    1338             : /* {{{ mysqlnd_stmt::send_long_data */
    1339             : static enum_func_status
    1340          16 : MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const s, unsigned int param_no,
    1341             :                                                                                          const char * const data, zend_ulong data_length)
    1342             : {
    1343          16 :         enum_func_status ret = FAIL;
    1344          16 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1345          16 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
    1346             :         zend_uchar * cmd_buf;
    1347             : 
    1348          16 :         DBG_ENTER("mysqlnd_stmt::send_long_data");
    1349          16 :         if (!stmt || !conn) {
    1350           0 :                 DBG_RETURN(FAIL);
    1351             :         }
    1352          16 :         DBG_INF_FMT("stmt=%lu param_no=%u data_len=%lu", stmt->stmt_id, param_no, data_length);
    1353             : 
    1354          16 :         SET_EMPTY_ERROR(stmt->error_info);
    1355          16 :         SET_EMPTY_ERROR(conn->error_info);
    1356             : 
    1357          16 :         if (stmt->state < MYSQLND_STMT_PREPARED) {
    1358           0 :                 SET_CLIENT_ERROR(stmt->error_info, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
    1359           0 :                 DBG_ERR("not prepared");
    1360           0 :                 DBG_RETURN(FAIL);
    1361             :         }
    1362          16 :         if (!stmt->param_bind) {
    1363           0 :                 SET_CLIENT_ERROR(stmt->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
    1364           0 :                 DBG_ERR("command out of sync");
    1365           0 :                 DBG_RETURN(FAIL);
    1366             :         }
    1367          16 :         if (param_no >= stmt->param_count) {
    1368           1 :                 SET_CLIENT_ERROR(stmt->error_info, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
    1369           1 :                 DBG_ERR("invalid param_no");
    1370           1 :                 DBG_RETURN(FAIL);
    1371             :         }
    1372          15 :         if (stmt->param_bind[param_no].type != MYSQL_TYPE_LONG_BLOB) {
    1373           0 :                 SET_CLIENT_ERROR(stmt->error_info, CR_INVALID_BUFFER_USE, UNKNOWN_SQLSTATE, mysqlnd_not_bound_as_blob);
    1374           0 :                 DBG_ERR("param_no is not of a blob type");
    1375           0 :                 DBG_RETURN(FAIL);
    1376             :         }
    1377             : 
    1378          15 :         if (GET_CONNECTION_STATE(&conn->state) == CONN_READY) {
    1379          15 :                 const size_t packet_len = MYSQLND_STMT_ID_LENGTH + 2 + data_length;
    1380          15 :                 cmd_buf = mnd_emalloc(packet_len);
    1381          15 :                 if (cmd_buf) {
    1382          15 :                         stmt->param_bind[param_no].flags |= MYSQLND_PARAM_BIND_BLOB_USED;
    1383             : 
    1384          15 :                         int4store(cmd_buf, stmt->stmt_id);
    1385          15 :                         int2store(cmd_buf + MYSQLND_STMT_ID_LENGTH, param_no);
    1386          15 :                         memcpy(cmd_buf + MYSQLND_STMT_ID_LENGTH + 2, data, data_length);
    1387             : 
    1388             :                         /* COM_STMT_SEND_LONG_DATA doesn't acknowledge with an OK packet */
    1389             :                         {
    1390          15 :                                 const MYSQLND_CSTRING payload = {(const char *) cmd_buf, packet_len};
    1391          15 :                                 struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_STMT_SEND_LONG_DATA, conn, payload);
    1392          15 :                                 ret = FAIL;
    1393          15 :                                 if (command) {
    1394          15 :                                         ret = command->run(command);
    1395          15 :                                         command->free_command(command);
    1396          15 :                                         if (ret == FAIL) {
    1397           0 :                                                 COPY_CLIENT_ERROR(stmt->error_info, *conn->error_info);
    1398             :                                         }
    1399             :                                 }
    1400             :                         }
    1401             : 
    1402          15 :                         mnd_efree(cmd_buf);
    1403             :                 } else {
    1404           0 :                         ret = FAIL;
    1405           0 :                         SET_OOM_ERROR(stmt->error_info);
    1406           0 :                         SET_OOM_ERROR(conn->error_info);
    1407             :                 }
    1408             :                 /*
    1409             :                   Cover protocol error: COM_STMT_SEND_LONG_DATA was designed to be quick and not
    1410             :                   sent response packets. According to documentation the only way to get an error
    1411             :                   is to have out-of-memory on the server-side. However, that's not true, as if
    1412             :                   max_allowed_packet_size is smaller than the chunk being sent to the server, the
    1413             :                   latter will complain with an error message. However, normally we don't expect
    1414             :                   an error message, thus we continue. When sending the next command, which expects
    1415             :                   response we will read the unexpected data and error message will look weird.
    1416             :                   Therefore we do non-blocking read to clean the line, if there is a need.
    1417             :                   Nevertheless, there is a built-in protection when sending a command packet, that
    1418             :                   checks if the line is clear - useful for debug purposes and to be switched off
    1419             :                   in release builds.
    1420             : 
    1421             :                   Maybe we can make it automatic by checking what's the value of
    1422             :                   max_allowed_packet_size on the server and resending the data.
    1423             :                 */
    1424             : #ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
    1425             : #if HAVE_USLEEP && !defined(PHP_WIN32)
    1426             :                 usleep(120000);
    1427             : #endif
    1428             :                 if ((packet_len = conn->protocol_frame_codec->m.consume_uneaten_data(conn->protocol_frame_codec, COM_STMT_SEND_LONG_DATA))) {
    1429             :                         php_error_docref(NULL, E_WARNING, "There was an error "
    1430             :                                                          "while sending long data. Probably max_allowed_packet_size "
    1431             :                                                          "is smaller than the data. You have to increase it or send "
    1432             :                                                          "smaller chunks of data. Answer was "MYSQLND_SZ_T_SPEC" bytes long.", packet_len);
    1433             :                         SET_CLIENT_ERROR(stmt->error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE,
    1434             :                                                         "Server responded to COM_STMT_SEND_LONG_DATA.");
    1435             :                         ret = FAIL;
    1436             :                 }
    1437             : #endif
    1438             :         }
    1439             : 
    1440          15 :         DBG_INF(ret == PASS? "PASS":"FAIL");
    1441          15 :         DBG_RETURN(ret);
    1442             : }
    1443             : /* }}} */
    1444             : 
    1445             : 
    1446             : /* {{{ mysqlnd_stmt::bind_parameters */
    1447             : static enum_func_status
    1448       20877 : MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const s, MYSQLND_PARAM_BIND * const param_bind)
    1449             : {
    1450       20877 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1451       20877 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
    1452             : 
    1453       20877 :         DBG_ENTER("mysqlnd_stmt::bind_param");
    1454       20877 :         if (!stmt || !conn) {
    1455           0 :                 DBG_RETURN(FAIL);
    1456             :         }
    1457       20877 :         DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count);
    1458             : 
    1459       20877 :         if (stmt->state < MYSQLND_STMT_PREPARED) {
    1460           0 :                 SET_CLIENT_ERROR(stmt->error_info, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
    1461           0 :                 DBG_ERR("not prepared");
    1462           0 :                 if (param_bind) {
    1463           0 :                         s->m->free_parameter_bind(s, param_bind);
    1464             :                 }
    1465           0 :                 DBG_RETURN(FAIL);
    1466             :         }
    1467             : 
    1468       20877 :         SET_EMPTY_ERROR(stmt->error_info);
    1469       20877 :         SET_EMPTY_ERROR(conn->error_info);
    1470             : 
    1471       20877 :         if (stmt->param_count) {
    1472       20877 :                 unsigned int i = 0;
    1473             : 
    1474       20877 :                 if (!param_bind) {
    1475           0 :                         SET_CLIENT_ERROR(stmt->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, "Re-binding (still) not supported");
    1476           0 :                         DBG_ERR("Re-binding (still) not supported");
    1477           0 :                         DBG_RETURN(FAIL);
    1478       20877 :                 } else if (stmt->param_bind) {
    1479       20435 :                         DBG_INF("Binding");
    1480             :                         /*
    1481             :                           There is already result bound.
    1482             :                           Forbid for now re-binding!!
    1483             :                         */
    1484       61305 :                         for (i = 0; i < stmt->param_count; i++) {
    1485             :                                 /*
    1486             :                                   We may have the last reference, then call zval_ptr_dtor() or we may leak memory.
    1487             :                                   Switching from bind_one_parameter to bind_parameters may result in zv being NULL
    1488             :                                 */
    1489       40870 :                                 zval_ptr_dtor(&stmt->param_bind[i].zv);
    1490             :                         }
    1491       20435 :                         if (stmt->param_bind != param_bind) {
    1492       20435 :                                 s->m->free_parameter_bind(s, stmt->param_bind);
    1493             :                         }
    1494             :                 }
    1495             : 
    1496       20877 :                 stmt->param_bind = param_bind;
    1497      195677 :                 for (i = 0; i < stmt->param_count; i++) {
    1498             :                         /* The client will use stmt_send_long_data */
    1499      174800 :                         DBG_INF_FMT("%u is of type %u", i, stmt->param_bind[i].type);
    1500             :                         /* Prevent from freeing */
    1501             :                         /* Don't update is_ref, or we will leak during conversion */
    1502      174800 :                         Z_TRY_ADDREF(stmt->param_bind[i].zv);
    1503      174800 :                         stmt->param_bind[i].flags = 0;
    1504      174800 :                         if (stmt->param_bind[i].type == MYSQL_TYPE_LONG_BLOB) {
    1505          24 :                                 stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
    1506             :                         }
    1507             :                 }
    1508       20877 :                 stmt->send_types_to_server = 1;
    1509             :         }
    1510       20877 :         DBG_INF("PASS");
    1511       20877 :         DBG_RETURN(PASS);
    1512             : }
    1513             : /* }}} */
    1514             : 
    1515             : 
    1516             : /* {{{ mysqlnd_stmt::bind_one_parameter */
    1517             : static enum_func_status
    1518         288 : MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter)(MYSQLND_STMT * const s, unsigned int param_no,
    1519             :                                                                                                  zval * const zv, zend_uchar type)
    1520             : {
    1521         288 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1522         288 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
    1523             : 
    1524         288 :         DBG_ENTER("mysqlnd_stmt::bind_one_parameter");
    1525         288 :         if (!stmt || !conn) {
    1526           0 :                 DBG_RETURN(FAIL);
    1527             :         }
    1528         288 :         DBG_INF_FMT("stmt=%lu param_no=%u param_count=%u type=%u", stmt->stmt_id, param_no, stmt->param_count, type);
    1529             : 
    1530         288 :         if (stmt->state < MYSQLND_STMT_PREPARED) {
    1531           0 :                 SET_CLIENT_ERROR(stmt->error_info, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
    1532           0 :                 DBG_ERR("not prepared");
    1533           0 :                 DBG_RETURN(FAIL);
    1534             :         }
    1535             : 
    1536         288 :         if (param_no >= stmt->param_count) {
    1537           0 :                 SET_CLIENT_ERROR(stmt->error_info, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
    1538           0 :                 DBG_ERR("invalid param_no");
    1539           0 :                 DBG_RETURN(FAIL);
    1540             :         }
    1541         288 :         SET_EMPTY_ERROR(stmt->error_info);
    1542         288 :         SET_EMPTY_ERROR(conn->error_info);
    1543             : 
    1544         288 :         if (stmt->param_count) {
    1545         288 :                 if (!stmt->param_bind) {
    1546         183 :                         stmt->param_bind = mnd_pecalloc(stmt->param_count, sizeof(MYSQLND_PARAM_BIND), stmt->persistent);
    1547         183 :                         if (!stmt->param_bind) {
    1548           0 :                                 DBG_RETURN(FAIL);
    1549             :                         }
    1550             :                 }
    1551             : 
    1552             :                 /* Prevent from freeing */
    1553             :                 /* Don't update is_ref, or we will leak during conversion */
    1554         288 :                 Z_TRY_ADDREF_P(zv);
    1555         288 :                 DBG_INF("Binding");
    1556             :                 /* Release what we had, if we had */
    1557         288 :                 zval_ptr_dtor(&stmt->param_bind[param_no].zv);
    1558         288 :                 if (type == MYSQL_TYPE_LONG_BLOB) {
    1559             :                         /* The client will use stmt_send_long_data */
    1560           0 :                         stmt->param_bind[param_no].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
    1561             :                 }
    1562         288 :                 ZVAL_COPY_VALUE(&stmt->param_bind[param_no].zv, zv);
    1563         288 :                 stmt->param_bind[param_no].type = type;
    1564             : 
    1565         288 :                 stmt->send_types_to_server = 1;
    1566             :         }
    1567         288 :         DBG_INF("PASS");
    1568         288 :         DBG_RETURN(PASS);
    1569             : }
    1570             : /* }}} */
    1571             : 
    1572             : 
    1573             : /* {{{ mysqlnd_stmt::refresh_bind_param */
    1574             : static enum_func_status
    1575           0 : MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param)(MYSQLND_STMT * const s)
    1576             : {
    1577           0 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1578           0 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
    1579             : 
    1580           0 :         DBG_ENTER("mysqlnd_stmt::refresh_bind_param");
    1581           0 :         if (!stmt || !conn) {
    1582           0 :                 DBG_RETURN(FAIL);
    1583             :         }
    1584           0 :         DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count);
    1585             : 
    1586           0 :         if (stmt->state < MYSQLND_STMT_PREPARED) {
    1587           0 :                 SET_CLIENT_ERROR(stmt->error_info, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
    1588           0 :                 DBG_ERR("not prepared");
    1589           0 :                 DBG_RETURN(FAIL);
    1590             :         }
    1591             : 
    1592           0 :         SET_EMPTY_ERROR(stmt->error_info);
    1593           0 :         SET_EMPTY_ERROR(conn->error_info);
    1594             : 
    1595           0 :         if (stmt->param_count) {
    1596           0 :                 stmt->send_types_to_server = 1;
    1597             :         }
    1598           0 :         DBG_RETURN(PASS);
    1599             : }
    1600             : /* }}} */
    1601             : 
    1602             : 
    1603             : /* {{{ mysqlnd_stmt::bind_result */
    1604             : static enum_func_status
    1605        1271 : MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const s,
    1606             :                                                                                   MYSQLND_RESULT_BIND * const result_bind)
    1607             : {
    1608        1271 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1609        1271 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
    1610             : 
    1611        1271 :         DBG_ENTER("mysqlnd_stmt::bind_result");
    1612        1271 :         if (!stmt || !conn) {
    1613           0 :                 DBG_RETURN(FAIL);
    1614             :         }
    1615        1271 :         DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count);
    1616             : 
    1617        1271 :         if (stmt->state < MYSQLND_STMT_PREPARED) {
    1618           0 :                 SET_CLIENT_ERROR(stmt->error_info, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
    1619           0 :                 if (result_bind) {
    1620           0 :                         s->m->free_result_bind(s, result_bind);
    1621             :                 }
    1622           0 :                 DBG_ERR("not prepared");
    1623           0 :                 DBG_RETURN(FAIL);
    1624             :         }
    1625             : 
    1626        1271 :         SET_EMPTY_ERROR(stmt->error_info);
    1627        1271 :         SET_EMPTY_ERROR(conn->error_info);
    1628             : 
    1629        1271 :         if (stmt->field_count) {
    1630        1271 :                 unsigned int i = 0;
    1631             : 
    1632        1271 :                 if (!result_bind) {
    1633           0 :                         DBG_ERR("no result bind passed");
    1634           0 :                         DBG_RETURN(FAIL);
    1635             :                 }
    1636             : 
    1637        1271 :                 mysqlnd_stmt_separate_result_bind(s);
    1638        1271 :                 stmt->result_zvals_separated_once = FALSE;
    1639        1271 :                 stmt->result_bind = result_bind;
    1640        6797 :                 for (i = 0; i < stmt->field_count; i++) {
    1641             :                         /* Prevent from freeing */
    1642        5526 :                         Z_TRY_ADDREF(stmt->result_bind[i].zv);
    1643             : 
    1644       11052 :                         DBG_INF_FMT("ref of %p = %u", &stmt->result_bind[i].zv,
    1645       11052 :                                         Z_REFCOUNTED(stmt->result_bind[i].zv)? Z_REFCOUNT(stmt->result_bind[i].zv) : 0);
    1646             :                         /*
    1647             :                           Don't update is_ref !!! it's not our job
    1648             :                           Otherwise either 009.phpt or mysqli_stmt_bind_result.phpt
    1649             :                           will fail.
    1650             :                         */
    1651        5526 :                         stmt->result_bind[i].bound = TRUE;
    1652             :                 }
    1653           0 :         } else if (result_bind) {
    1654           0 :                 s->m->free_result_bind(s, result_bind);
    1655             :         }
    1656        1271 :         DBG_INF("PASS");
    1657        1271 :         DBG_RETURN(PASS);
    1658             : }
    1659             : /* }}} */
    1660             : 
    1661             : 
    1662             : /* {{{ mysqlnd_stmt::bind_result */
    1663             : static enum_func_status
    1664         910 : MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const s, unsigned int param_no)
    1665             : {
    1666         910 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1667         910 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
    1668             : 
    1669         910 :         DBG_ENTER("mysqlnd_stmt::bind_result");
    1670         910 :         if (!stmt || !conn) {
    1671           0 :                 DBG_RETURN(FAIL);
    1672             :         }
    1673         910 :         DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count);
    1674             : 
    1675         910 :         if (stmt->state < MYSQLND_STMT_PREPARED) {
    1676           0 :                 SET_CLIENT_ERROR(stmt->error_info, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
    1677           0 :                 DBG_ERR("not prepared");
    1678           0 :                 DBG_RETURN(FAIL);
    1679             :         }
    1680             : 
    1681         910 :         if (param_no >= stmt->field_count) {
    1682           0 :                 SET_CLIENT_ERROR(stmt->error_info, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
    1683           0 :                 DBG_ERR("invalid param_no");
    1684           0 :                 DBG_RETURN(FAIL);
    1685             :         }
    1686             : 
    1687         910 :         SET_EMPTY_ERROR(stmt->error_info);
    1688         910 :         SET_EMPTY_ERROR(conn->error_info);
    1689             : 
    1690         910 :         if (stmt->field_count) {
    1691         910 :                 mysqlnd_stmt_separate_one_result_bind(s, param_no);
    1692             :                 /* Guaranteed is that stmt->result_bind is NULL */
    1693         910 :                 if (!stmt->result_bind) {
    1694         444 :                         stmt->result_bind = mnd_pecalloc(stmt->field_count, sizeof(MYSQLND_RESULT_BIND), stmt->persistent);
    1695             :                 } else {
    1696         466 :                         stmt->result_bind = mnd_perealloc(stmt->result_bind, stmt->field_count * sizeof(MYSQLND_RESULT_BIND), stmt->persistent);
    1697             :                 }
    1698         910 :                 if (!stmt->result_bind) {
    1699           0 :                         DBG_RETURN(FAIL);
    1700             :                 }
    1701         910 :                 ZVAL_NULL(&stmt->result_bind[param_no].zv);
    1702             :                 /*
    1703             :                   Don't update is_ref !!! it's not our job
    1704             :                   Otherwise either 009.phpt or mysqli_stmt_bind_result.phpt
    1705             :                   will fail.
    1706             :                 */
    1707         910 :                 stmt->result_bind[param_no].bound = TRUE;
    1708             :         }
    1709         910 :         DBG_INF("PASS");
    1710         910 :         DBG_RETURN(PASS);
    1711             : }
    1712             : /* }}} */
    1713             : 
    1714             : 
    1715             : /* {{{ mysqlnd_stmt::insert_id */
    1716             : static uint64_t
    1717          10 : MYSQLND_METHOD(mysqlnd_stmt, insert_id)(const MYSQLND_STMT * const s)
    1718             : {
    1719          10 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1720          10 :         return stmt? UPSERT_STATUS_GET_LAST_INSERT_ID(stmt->upsert_status) : 0;
    1721             : }
    1722             : /* }}} */
    1723             : 
    1724             : 
    1725             : /* {{{ mysqlnd_stmt::affected_rows */
    1726             : static uint64_t
    1727         770 : MYSQLND_METHOD(mysqlnd_stmt, affected_rows)(const MYSQLND_STMT * const s)
    1728             : {
    1729         770 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1730         770 :         return stmt? UPSERT_STATUS_GET_AFFECTED_ROWS(stmt->upsert_status) : 0;
    1731             : }
    1732             : /* }}} */
    1733             : 
    1734             : 
    1735             : /* {{{ mysqlnd_stmt::num_rows */
    1736             : static uint64_t
    1737          24 : MYSQLND_METHOD(mysqlnd_stmt, num_rows)(const MYSQLND_STMT * const s)
    1738             : {
    1739          24 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1740          24 :         return stmt && stmt->result? mysqlnd_num_rows(stmt->result):0;
    1741             : }
    1742             : /* }}} */
    1743             : 
    1744             : 
    1745             : /* {{{ mysqlnd_stmt::warning_count */
    1746             : static unsigned int
    1747           3 : MYSQLND_METHOD(mysqlnd_stmt, warning_count)(const MYSQLND_STMT * const s)
    1748             : {
    1749           3 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1750           3 :         return stmt? UPSERT_STATUS_GET_WARNINGS(stmt->upsert_status) : 0;
    1751             : }
    1752             : /* }}} */
    1753             : 
    1754             : 
    1755             : /* {{{ mysqlnd_stmt::server_status */
    1756             : static unsigned int
    1757           0 : MYSQLND_METHOD(mysqlnd_stmt, server_status)(const MYSQLND_STMT * const s)
    1758             : {
    1759           0 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1760           0 :         return stmt? UPSERT_STATUS_GET_SERVER_STATUS(stmt->upsert_status) : 0;
    1761             : }
    1762             : /* }}} */
    1763             : 
    1764             : 
    1765             : /* {{{ mysqlnd_stmt::field_count */
    1766             : static unsigned int
    1767        2525 : MYSQLND_METHOD(mysqlnd_stmt, field_count)(const MYSQLND_STMT * const s)
    1768             : {
    1769        2525 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1770        2525 :         return stmt? stmt->field_count : 0;
    1771             : }
    1772             : /* }}} */
    1773             : 
    1774             : 
    1775             : /* {{{ mysqlnd_stmt::param_count */
    1776             : static unsigned int
    1777       21763 : MYSQLND_METHOD(mysqlnd_stmt, param_count)(const MYSQLND_STMT * const s)
    1778             : {
    1779       21763 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1780       21763 :         return stmt? stmt->param_count : 0;
    1781             : }
    1782             : /* }}} */
    1783             : 
    1784             : 
    1785             : /* {{{ mysqlnd_stmt::errno */
    1786             : static unsigned int
    1787          21 : MYSQLND_METHOD(mysqlnd_stmt, errno)(const MYSQLND_STMT * const s)
    1788             : {
    1789          21 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1790          21 :         return stmt? stmt->error_info->error_no : 0;
    1791             : }
    1792             : /* }}} */
    1793             : 
    1794             : 
    1795             : /* {{{ mysqlnd_stmt::error */
    1796             : static const char *
    1797          11 : MYSQLND_METHOD(mysqlnd_stmt, error)(const MYSQLND_STMT * const s)
    1798             : {
    1799          11 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1800          11 :         return stmt? stmt->error_info->error : 0;
    1801             : }
    1802             : /* }}} */
    1803             : 
    1804             : 
    1805             : /* {{{ mysqlnd_stmt::sqlstate */
    1806             : static const char *
    1807          12 : MYSQLND_METHOD(mysqlnd_stmt, sqlstate)(const MYSQLND_STMT * const s)
    1808             : {
    1809          12 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1810          12 :         return stmt && stmt->error_info->sqlstate[0] ? stmt->error_info->sqlstate:MYSQLND_SQLSTATE_NULL;
    1811             : }
    1812             : /* }}} */
    1813             : 
    1814             : 
    1815             : /* {{{ mysqlnd_stmt::data_seek */
    1816             : static enum_func_status
    1817           3 : MYSQLND_METHOD(mysqlnd_stmt, data_seek)(const MYSQLND_STMT * const s, uint64_t row)
    1818             : {
    1819           3 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1820           3 :         return stmt && stmt->result? stmt->result->m.seek_data(stmt->result, row) : FAIL;
    1821             : }
    1822             : /* }}} */
    1823             : 
    1824             : 
    1825             : /* {{{ mysqlnd_stmt::param_metadata */
    1826             : static MYSQLND_RES *
    1827           0 : MYSQLND_METHOD(mysqlnd_stmt, param_metadata)(MYSQLND_STMT * const s)
    1828             : {
    1829           0 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1830           0 :         if (!stmt || !stmt->param_count) {
    1831           0 :                 return NULL;
    1832             :         }
    1833           0 :         return NULL;
    1834             : }
    1835             : /* }}} */
    1836             : 
    1837             : 
    1838             : /* {{{ mysqlnd_stmt::result_metadata */
    1839             : static MYSQLND_RES *
    1840         961 : MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const s)
    1841             : {
    1842         961 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1843         961 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
    1844         961 :         MYSQLND_RES * result_meta = NULL;
    1845             : 
    1846         961 :         DBG_ENTER("mysqlnd_stmt::result_metadata");
    1847         961 :         if (!stmt || ! conn) {
    1848           0 :                 DBG_RETURN(NULL);
    1849             :         }
    1850         961 :         DBG_INF_FMT("stmt=%u field_count=%u", stmt->stmt_id, stmt->field_count);
    1851             : 
    1852         961 :         if (!stmt->field_count || !stmt->result || !stmt->result->meta) {
    1853         293 :                 DBG_INF("NULL");
    1854         293 :                 DBG_RETURN(NULL);
    1855             :         }
    1856             : 
    1857         668 :         if (stmt->update_max_length && stmt->result->stored_data) {
    1858             :                 /* stored result, we have to update the max_length before we clone the meta data :( */
    1859           3 :                 stmt->result->stored_data->m.initialize_result_set_rest(stmt->result->stored_data,
    1860           1 :                                                                                                                                 stmt->result->meta,
    1861             :                                                                                                                                 conn->stats,
    1862           1 :                                                                                                                                 conn->options->int_and_float_native);
    1863             :         }
    1864             :         /*
    1865             :           TODO: This implementation is kind of a hack,
    1866             :                         find a better way to do it. In different functions I have put
    1867             :                         fuses to check for result->m.fetch_row() being NULL. This should
    1868             :                         be handled in a better way.
    1869             :         */
    1870             :         do {
    1871         668 :                 result_meta = conn->m->result_init(stmt->field_count, stmt->persistent);
    1872         668 :                 if (!result_meta) {
    1873           0 :                         break;
    1874             :                 }
    1875         668 :                 result_meta->type = MYSQLND_RES_NORMAL;
    1876         668 :                 result_meta->unbuf = mysqlnd_result_unbuffered_init(stmt->field_count, TRUE, result_meta->persistent);
    1877         668 :                 if (!result_meta->unbuf) {
    1878           0 :                         break;
    1879             :                 }
    1880         668 :                 result_meta->unbuf->eof_reached = TRUE;
    1881         668 :                 result_meta->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE);
    1882         668 :                 if (!result_meta->meta) {
    1883           0 :                         break;
    1884             :                 }
    1885             : 
    1886         668 :                 DBG_INF_FMT("result_meta=%p", result_meta);
    1887         668 :                 DBG_RETURN(result_meta);
    1888             :         } while (0);
    1889             : 
    1890           0 :         SET_OOM_ERROR(conn->error_info);
    1891           0 :         if (result_meta) {
    1892           0 :                 result_meta->m.free_result(result_meta, TRUE);
    1893             :         }
    1894           0 :         DBG_RETURN(NULL);
    1895             : }
    1896             : /* }}} */
    1897             : 
    1898             : 
    1899             : /* {{{ mysqlnd_stmt::attr_set */
    1900             : static enum_func_status
    1901        1111 : MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const s,
    1902             :                                                                            enum mysqlnd_stmt_attr attr_type,
    1903             :                                                                            const void * const value)
    1904             : {
    1905        1111 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1906        1111 :         DBG_ENTER("mysqlnd_stmt::attr_set");
    1907        1111 :         if (!stmt) {
    1908           0 :                 DBG_RETURN(FAIL);
    1909             :         }
    1910        1111 :         DBG_INF_FMT("stmt=%lu attr_type=%u", stmt->stmt_id, attr_type);
    1911             : 
    1912        1111 :         switch (attr_type) {
    1913             :                 case STMT_ATTR_UPDATE_MAX_LENGTH:{
    1914           2 :                         zend_uchar bval = *(zend_uchar *) value;
    1915             :                         /*
    1916             :                           XXX : libmysql uses my_bool, but mysqli uses ulong as storage on the stack
    1917             :                           and mysqlnd won't be used out of the scope of PHP -> use ulong.
    1918             :                         */
    1919           2 :                         stmt->update_max_length = bval? TRUE:FALSE;
    1920           2 :                         break;
    1921             :                 }
    1922             :                 case STMT_ATTR_CURSOR_TYPE: {
    1923          10 :                         unsigned int ival = *(unsigned int *) value;
    1924          10 :                         if (ival > (zend_ulong) CURSOR_TYPE_READ_ONLY) {
    1925           2 :                                 SET_CLIENT_ERROR(stmt->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
    1926           2 :                                 DBG_INF("FAIL");
    1927           2 :                                 DBG_RETURN(FAIL);
    1928             :                         }
    1929           8 :                         stmt->flags = ival;
    1930           8 :                         break;
    1931             :                 }
    1932             :                 case STMT_ATTR_PREFETCH_ROWS: {
    1933           1 :                         unsigned int ival = *(unsigned int *) value;
    1934           1 :                         if (ival == 0) {
    1935           0 :                                 ival = MYSQLND_DEFAULT_PREFETCH_ROWS;
    1936           1 :                         } else if (ival > 1) {
    1937           0 :                                 SET_CLIENT_ERROR(stmt->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
    1938           0 :                                 DBG_INF("FAIL");
    1939           0 :                                 DBG_RETURN(FAIL);
    1940             :                         }
    1941           1 :                         stmt->prefetch_rows = ival;
    1942           1 :                         break;
    1943             :                 }
    1944             :                 default:
    1945        1098 :                         SET_CLIENT_ERROR(stmt->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
    1946        1098 :                         DBG_RETURN(FAIL);
    1947             :         }
    1948          11 :         DBG_INF("PASS");
    1949          11 :         DBG_RETURN(PASS);
    1950             : }
    1951             : /* }}} */
    1952             : 
    1953             : 
    1954             : /* {{{ mysqlnd_stmt::attr_get */
    1955             : static enum_func_status
    1956           6 : MYSQLND_METHOD(mysqlnd_stmt, attr_get)(const MYSQLND_STMT * const s,
    1957             :                                                                            enum mysqlnd_stmt_attr attr_type,
    1958             :                                                                            void * const value)
    1959             : {
    1960           6 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1961           6 :         DBG_ENTER("mysqlnd_stmt::attr_set");
    1962           6 :         if (!stmt) {
    1963           0 :                 DBG_RETURN(FAIL);
    1964             :         }
    1965           6 :         DBG_INF_FMT("stmt=%lu attr_type=%u", stmt->stmt_id, attr_type);
    1966             : 
    1967           6 :         switch (attr_type) {
    1968             :                 case STMT_ATTR_UPDATE_MAX_LENGTH:
    1969           3 :                         *(zend_bool *) value= stmt->update_max_length;
    1970           3 :                         break;
    1971             :                 case STMT_ATTR_CURSOR_TYPE:
    1972           1 :                         *(zend_ulong *) value= stmt->flags;
    1973           1 :                         break;
    1974             :                 case STMT_ATTR_PREFETCH_ROWS:
    1975           1 :                         *(zend_ulong *) value= stmt->prefetch_rows;
    1976           1 :                         break;
    1977             :                 default:
    1978           1 :                         DBG_RETURN(FAIL);
    1979             :         }
    1980           5 :         DBG_INF_FMT("value=%lu", value);
    1981           5 :         DBG_RETURN(PASS);
    1982             : }
    1983             : /* }}} */
    1984             : 
    1985             : 
    1986             : /* free_result() doesn't actually free stmt->result but only the buffers */
    1987             : /* {{{ mysqlnd_stmt::free_result */
    1988             : static enum_func_status
    1989         261 : MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const s)
    1990             : {
    1991         261 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    1992         261 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
    1993             : 
    1994         261 :         DBG_ENTER("mysqlnd_stmt::free_result");
    1995         261 :         if (!stmt || !conn) {
    1996           0 :                 DBG_RETURN(FAIL);
    1997             :         }
    1998         261 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
    1999             : 
    2000         261 :         if (!stmt->result) {
    2001           6 :                 DBG_INF("no result");
    2002           6 :                 DBG_RETURN(PASS);
    2003             :         }
    2004             : 
    2005             :         /*
    2006             :           If right after execute() we have to call the appropriate
    2007             :           use_result() or store_result() and clean.
    2008             :         */
    2009         255 :         if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
    2010           3 :                 DBG_INF("fetching result set header");
    2011             :                 /* Do implicit use_result and then flush the result */
    2012           3 :                 stmt->default_rset_handler = s->m->use_result;
    2013           3 :                 stmt->default_rset_handler(s);
    2014             :         }
    2015             : 
    2016         255 :         if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) {
    2017         254 :                 DBG_INF("skipping result");
    2018             :                 /* Flush if anything is left and unbuffered set */
    2019         254 :                 stmt->result->m.skip_result(stmt->result);
    2020             :                 /*
    2021             :                   Separate the bound variables, which point to the result set, then
    2022             :                   destroy the set.
    2023             :                 */
    2024         254 :                 mysqlnd_stmt_separate_result_bind(s);
    2025             : 
    2026             :                 /* Now we can destroy the result set */
    2027         254 :                 stmt->result->m.free_result_buffers(stmt->result);
    2028             :         }
    2029             : 
    2030         255 :         if (stmt->state > MYSQLND_STMT_PREPARED) {
    2031             :                 /* As the buffers have been freed, we should go back to PREPARED */
    2032         254 :                 stmt->state = MYSQLND_STMT_PREPARED;
    2033             :         }
    2034             : 
    2035             :         /* Line is free! */
    2036         255 :         SET_CONNECTION_STATE(&conn->state, CONN_READY);
    2037             : 
    2038         255 :         DBG_RETURN(PASS);
    2039             : }
    2040             : /* }}} */
    2041             : 
    2042             : 
    2043             : /* {{{ mysqlnd_stmt_separate_result_bind */
    2044             : static void
    2045        5811 : mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const s)
    2046             : {
    2047        5811 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    2048             :         unsigned int i;
    2049             : 
    2050        5811 :         DBG_ENTER("mysqlnd_stmt_separate_result_bind");
    2051        5811 :         if (!stmt) {
    2052           0 :                 DBG_VOID_RETURN;
    2053             :         }
    2054        5811 :         DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u", stmt->stmt_id, stmt->result_bind, stmt->field_count);
    2055             : 
    2056        5811 :         if (!stmt->result_bind) {
    2057        4096 :                 DBG_VOID_RETURN;
    2058             :         }
    2059             : 
    2060             :         /*
    2061             :           Because only the bound variables can point to our internal buffers, then
    2062             :           separate or free only them. Free is possible because the user could have
    2063             :           lost reference.
    2064             :         */
    2065        8112 :         for (i = 0; i < stmt->field_count; i++) {
    2066             :                 /* Let's try with no cache */
    2067        6397 :                 if (stmt->result_bind[i].bound == TRUE) {
    2068       12201 :                         DBG_INF_FMT("%u has refcount=%u", i, Z_REFCOUNTED(stmt->result_bind[i].zv)? Z_REFCOUNT(stmt->result_bind[i].zv) : 0);
    2069        6397 :                         zval_ptr_dtor(&stmt->result_bind[i].zv);
    2070             :                 }
    2071             :         }
    2072             : 
    2073        1715 :         s->m->free_result_bind(s, stmt->result_bind);
    2074        1715 :         stmt->result_bind = NULL;
    2075             : 
    2076        1715 :         DBG_VOID_RETURN;
    2077             : }
    2078             : /* }}} */
    2079             : 
    2080             : 
    2081             : /* {{{ mysqlnd_stmt_separate_one_result_bind */
    2082             : static void
    2083         910 : mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const s, const unsigned int param_no)
    2084             : {
    2085         910 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    2086         910 :         DBG_ENTER("mysqlnd_stmt_separate_one_result_bind");
    2087         910 :         if (!stmt) {
    2088           0 :                 DBG_VOID_RETURN;
    2089             :         }
    2090         910 :         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);
    2091             : 
    2092         910 :         if (!stmt->result_bind) {
    2093         444 :                 DBG_VOID_RETURN;
    2094             :         }
    2095             : 
    2096             :         /*
    2097             :           Because only the bound variables can point to our internal buffers, then
    2098             :           separate or free only them. Free is possible because the user could have
    2099             :           lost reference.
    2100             :         */
    2101             :         /* Let's try with no cache */
    2102         466 :         if (stmt->result_bind[param_no].bound == TRUE) {
    2103          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);
    2104          39 :                 zval_ptr_dtor(&stmt->result_bind[param_no].zv);
    2105             :         }
    2106             : 
    2107         466 :         DBG_VOID_RETURN;
    2108             : }
    2109             : /* }}} */
    2110             : 
    2111             : 
    2112             : /* {{{ mysqlnd_stmt::free_stmt_result */
    2113             : static void
    2114        4286 : MYSQLND_METHOD(mysqlnd_stmt, free_stmt_result)(MYSQLND_STMT * const s)
    2115             : {
    2116        4286 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    2117        4286 :         DBG_ENTER("mysqlnd_stmt::free_stmt_result");
    2118        4286 :         if (!stmt) {
    2119           0 :                 DBG_VOID_RETURN;
    2120             :         }
    2121             : 
    2122             :         /*
    2123             :           First separate the bound variables, which point to the result set, then
    2124             :           destroy the set.
    2125             :         */
    2126        4286 :         mysqlnd_stmt_separate_result_bind(s);
    2127             :         /* Not every statement has a result set attached */
    2128        4286 :         if (stmt->result) {
    2129        2470 :                 stmt->result->m.free_result_internal(stmt->result);
    2130        2470 :                 stmt->result = NULL;
    2131             :         }
    2132        4286 :         if (stmt->error_info->error_list) {
    2133        4259 :                 zend_llist_clean(stmt->error_info->error_list);
    2134        4259 :                 mnd_pefree(stmt->error_info->error_list, s->persistent);
    2135        4259 :                 stmt->error_info->error_list = NULL;
    2136             :         }
    2137             : 
    2138        4286 :         DBG_VOID_RETURN;
    2139             : }
    2140             : /* }}} */
    2141             : 
    2142             : 
    2143             : /* {{{ mysqlnd_stmt::free_stmt_content */
    2144             : static void
    2145        4259 : MYSQLND_METHOD(mysqlnd_stmt, free_stmt_content)(MYSQLND_STMT * const s)
    2146             : {
    2147        4259 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    2148        4259 :         DBG_ENTER("mysqlnd_stmt::free_stmt_content");
    2149        4259 :         if (!stmt) {
    2150           0 :                 DBG_VOID_RETURN;
    2151             :         }
    2152        4259 :         DBG_INF_FMT("stmt=%lu param_bind=%p param_count=%u", stmt->stmt_id, stmt->param_bind, stmt->param_count);
    2153             : 
    2154             :         /* Destroy the input bind */
    2155        4259 :         if (stmt->param_bind) {
    2156             :                 unsigned int i;
    2157             :                 /*
    2158             :                   Because only the bound variables can point to our internal buffers, then
    2159             :                   separate or free only them. Free is possible because the user could have
    2160             :                   lost reference.
    2161             :                 */
    2162      134832 :                 for (i = 0; i < stmt->param_count; i++) {
    2163             :                         /*
    2164             :                           If bind_one_parameter was used, but not everything was
    2165             :                           bound and nothing was fetched, then some `zv` could be NULL
    2166             :                         */
    2167      134207 :                         zval_ptr_dtor(&stmt->param_bind[i].zv);
    2168             :                 }
    2169         625 :                 s->m->free_parameter_bind(s, stmt->param_bind);
    2170         625 :                 stmt->param_bind = NULL;
    2171             :         }
    2172             : 
    2173        4259 :         s->m->free_stmt_result(s);
    2174        4259 :         DBG_VOID_RETURN;
    2175             : }
    2176             : /* }}} */
    2177             : 
    2178             : 
    2179             : /* {{{ mysqlnd_stmt::close_on_server */
    2180             : static enum_func_status
    2181        4259 : MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, close_on_server)(MYSQLND_STMT * const s, zend_bool implicit)
    2182             : {
    2183        4259 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    2184        4259 :         MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
    2185        4259 :         enum_mysqlnd_collected_stats statistic = STAT_LAST;
    2186             : 
    2187        4259 :         DBG_ENTER("mysqlnd_stmt::close_on_server");
    2188        4259 :         if (!stmt || !conn) {
    2189           0 :                 DBG_RETURN(FAIL);
    2190             :         }
    2191        4259 :         DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
    2192             : 
    2193        4259 :         SET_EMPTY_ERROR(stmt->error_info);
    2194        4259 :         SET_EMPTY_ERROR(conn->error_info);
    2195             : 
    2196             :         /*
    2197             :           If the user decided to close the statement right after execute()
    2198             :           We have to call the appropriate use_result() or store_result() and
    2199             :           clean.
    2200             :         */
    2201             :         do {
    2202        4260 :                 if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
    2203          15 :                         DBG_INF("fetching result set header");
    2204          15 :                         stmt->default_rset_handler(s);
    2205          15 :                         stmt->state = MYSQLND_STMT_USER_FETCHING;
    2206             :                 }
    2207             : 
    2208             :                 /* unbuffered set not fetched to the end ? Clean the line */
    2209        4260 :                 if (stmt->result) {
    2210        2444 :                         DBG_INF("skipping result");
    2211        2444 :                         stmt->result->m.skip_result(stmt->result);
    2212             :                 }
    2213        4260 :         } while (mysqlnd_stmt_more_results(s) && mysqlnd_stmt_next_result(s) == PASS);
    2214             :         /*
    2215             :           After this point we are allowed to free the result set,
    2216             :           as we have cleaned the line
    2217             :         */
    2218        4259 :         if (stmt->stmt_id) {
    2219        4229 :                 MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE?  STAT_FREE_RESULT_IMPLICIT:
    2220             :                                                                                                                 STAT_FREE_RESULT_EXPLICIT);
    2221             : 
    2222        4229 :                 if (GET_CONNECTION_STATE(&conn->state) == CONN_READY) {
    2223        4204 :                         enum_func_status ret = FAIL;
    2224        4204 :                         size_t stmt_id = stmt->stmt_id;
    2225        4204 :                         struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_STMT_CLOSE, conn, stmt_id);
    2226        4204 :                         if (command) {
    2227        4204 :                                 ret = command->run(command);
    2228        4204 :                                 command->free_command(command);
    2229             : 
    2230        4204 :                                 if (ret == FAIL) {
    2231           0 :                                         COPY_CLIENT_ERROR(stmt->error_info, *conn->error_info);
    2232             :                                 }
    2233             :                         }
    2234        4204 :                         if (ret == FAIL) {
    2235           0 :                                 DBG_RETURN(FAIL);
    2236             :                         }
    2237             :                 }
    2238             :         }
    2239        4259 :         switch (stmt->execute_count) {
    2240             :                 case 0:
    2241         262 :                         statistic = STAT_PS_PREPARED_NEVER_EXECUTED;
    2242         262 :                         break;
    2243             :                 case 1:
    2244        3677 :                         statistic = STAT_PS_PREPARED_ONCE_USED;
    2245             :                         break;
    2246             :                 default:
    2247             :                         break;
    2248             :         }
    2249        4259 :         if (statistic != STAT_LAST) {
    2250        3939 :                 MYSQLND_INC_CONN_STATISTIC(conn->stats, statistic);
    2251             :         }
    2252             : 
    2253        4259 :         if (stmt->execute_cmd_buffer.buffer) {
    2254        4259 :                 mnd_pefree(stmt->execute_cmd_buffer.buffer, stmt->persistent);
    2255        4259 :                 stmt->execute_cmd_buffer.buffer = NULL;
    2256             :         }
    2257             : 
    2258        4259 :         s->m->free_stmt_content(s);
    2259             : 
    2260        4259 :         if (conn) {
    2261        4259 :                 conn->m->free_reference(conn);
    2262        4259 :                 stmt->conn = NULL;
    2263             :         }
    2264             : 
    2265        4259 :         DBG_RETURN(PASS);
    2266             : }
    2267             : /* }}} */
    2268             : 
    2269             : /* {{{ mysqlnd_stmt::dtor */
    2270             : static enum_func_status
    2271        4259 : MYSQLND_METHOD(mysqlnd_stmt, dtor)(MYSQLND_STMT * const s, zend_bool implicit)
    2272             : {
    2273        4259 :         MYSQLND_STMT_DATA * stmt = (s != NULL) ? s->data:NULL;
    2274        4259 :         enum_func_status ret = FAIL;
    2275        4259 :         zend_bool persistent = (s != NULL) ? s->persistent : 0;
    2276             : 
    2277        4259 :         DBG_ENTER("mysqlnd_stmt::dtor");
    2278        4259 :         if (stmt) {
    2279        4259 :                 DBG_INF_FMT("stmt=%p", stmt);
    2280             : 
    2281        4259 :                 MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE?  STAT_STMT_CLOSE_IMPLICIT:
    2282             :                                                                                                                 STAT_STMT_CLOSE_EXPLICIT);
    2283             : 
    2284        4259 :                 ret = s->m->close_on_server(s, implicit);
    2285        4259 :                 mnd_pefree(stmt, persistent);
    2286             :         }
    2287        4259 :         mnd_pefree(s, persistent);
    2288             : 
    2289        4259 :         DBG_INF(ret == PASS? "PASS":"FAIL");
    2290        4259 :         DBG_RETURN(ret);
    2291             : }
    2292             : /* }}} */
    2293             : 
    2294             : 
    2295             : /* {{{ mysqlnd_stmt::alloc_param_bind */
    2296             : static MYSQLND_PARAM_BIND *
    2297       20879 : MYSQLND_METHOD(mysqlnd_stmt, alloc_param_bind)(MYSQLND_STMT * const s)
    2298             : {
    2299       20879 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    2300       20879 :         DBG_ENTER("mysqlnd_stmt::alloc_param_bind");
    2301       20879 :         if (!stmt) {
    2302           0 :                 DBG_RETURN(NULL);
    2303             :         }
    2304       20879 :         DBG_RETURN(mnd_pecalloc(stmt->param_count, sizeof(MYSQLND_PARAM_BIND), stmt->persistent));
    2305             : }
    2306             : /* }}} */
    2307             : 
    2308             : 
    2309             : /* {{{ mysqlnd_stmt::alloc_result_bind */
    2310             : static MYSQLND_RESULT_BIND *
    2311        1271 : MYSQLND_METHOD(mysqlnd_stmt, alloc_result_bind)(MYSQLND_STMT * const s)
    2312             : {
    2313        1271 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    2314        1271 :         DBG_ENTER("mysqlnd_stmt::alloc_result_bind");
    2315        1271 :         if (!stmt) {
    2316           0 :                 DBG_RETURN(NULL);
    2317             :         }
    2318        1271 :         DBG_RETURN(mnd_pecalloc(stmt->field_count, sizeof(MYSQLND_RESULT_BIND), stmt->persistent));
    2319             : }
    2320             : /* }}} */
    2321             : 
    2322             : 
    2323             : /* {{{ param_bind::free_parameter_bind */
    2324             : PHPAPI void
    2325       21062 : MYSQLND_METHOD(mysqlnd_stmt, free_parameter_bind)(MYSQLND_STMT * const s, MYSQLND_PARAM_BIND * param_bind)
    2326             : {
    2327       21062 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    2328       21062 :         if (stmt) {
    2329       21062 :                 mnd_pefree(param_bind, stmt->persistent);
    2330             :         }
    2331       21062 : }
    2332             : /* }}} */
    2333             : 
    2334             : 
    2335             : /* {{{ mysqlnd_stmt::free_result_bind */
    2336             : PHPAPI void
    2337        1715 : MYSQLND_METHOD(mysqlnd_stmt, free_result_bind)(MYSQLND_STMT * const s, MYSQLND_RESULT_BIND * result_bind)
    2338             : {
    2339        1715 :         MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
    2340        1715 :         if (stmt) {
    2341        1715 :                 mnd_pefree(result_bind, stmt->persistent);
    2342             :         }
    2343        1715 : }
    2344             : /* }}} */
    2345             : 
    2346             : 
    2347             : 
    2348             : MYSQLND_CLASS_METHODS_START(mysqlnd_stmt)
    2349             :         MYSQLND_METHOD(mysqlnd_stmt, prepare),
    2350             :         MYSQLND_METHOD(mysqlnd_stmt, send_execute),
    2351             :         MYSQLND_METHOD(mysqlnd_stmt, execute),
    2352             :         MYSQLND_METHOD(mysqlnd_stmt, use_result),
    2353             :         MYSQLND_METHOD(mysqlnd_stmt, store_result),
    2354             :         MYSQLND_METHOD(mysqlnd_stmt, get_result),
    2355             :         MYSQLND_METHOD(mysqlnd_stmt, more_results),
    2356             :         MYSQLND_METHOD(mysqlnd_stmt, next_result),
    2357             :         MYSQLND_METHOD(mysqlnd_stmt, free_result),
    2358             :         MYSQLND_METHOD(mysqlnd_stmt, data_seek),
    2359             :         MYSQLND_METHOD(mysqlnd_stmt, reset),
    2360             :         MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, close_on_server),
    2361             :         MYSQLND_METHOD(mysqlnd_stmt, dtor),
    2362             : 
    2363             :         MYSQLND_METHOD(mysqlnd_stmt, fetch),
    2364             : 
    2365             :         MYSQLND_METHOD(mysqlnd_stmt, bind_parameters),
    2366             :         MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter),
    2367             :         MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param),
    2368             :         MYSQLND_METHOD(mysqlnd_stmt, bind_result),
    2369             :         MYSQLND_METHOD(mysqlnd_stmt, bind_one_result),
    2370             :         MYSQLND_METHOD(mysqlnd_stmt, send_long_data),
    2371             :         MYSQLND_METHOD(mysqlnd_stmt, param_metadata),
    2372             :         MYSQLND_METHOD(mysqlnd_stmt, result_metadata),
    2373             : 
    2374             :         MYSQLND_METHOD(mysqlnd_stmt, insert_id),
    2375             :         MYSQLND_METHOD(mysqlnd_stmt, affected_rows),
    2376             :         MYSQLND_METHOD(mysqlnd_stmt, num_rows),
    2377             : 
    2378             :         MYSQLND_METHOD(mysqlnd_stmt, param_count),
    2379             :         MYSQLND_METHOD(mysqlnd_stmt, field_count),
    2380             :         MYSQLND_METHOD(mysqlnd_stmt, warning_count),
    2381             : 
    2382             :         MYSQLND_METHOD(mysqlnd_stmt, errno),
    2383             :         MYSQLND_METHOD(mysqlnd_stmt, error),
    2384             :         MYSQLND_METHOD(mysqlnd_stmt, sqlstate),
    2385             : 
    2386             :         MYSQLND_METHOD(mysqlnd_stmt, attr_get),
    2387             :         MYSQLND_METHOD(mysqlnd_stmt, attr_set),
    2388             : 
    2389             : 
    2390             :         MYSQLND_METHOD(mysqlnd_stmt, alloc_param_bind),
    2391             :         MYSQLND_METHOD(mysqlnd_stmt, alloc_result_bind),
    2392             :         MYSQLND_METHOD(mysqlnd_stmt, free_parameter_bind),
    2393             :         MYSQLND_METHOD(mysqlnd_stmt, free_result_bind),
    2394             :         MYSQLND_METHOD(mysqlnd_stmt, server_status),
    2395             :         mysqlnd_stmt_execute_generate_request,
    2396             :         mysqlnd_stmt_execute_parse_response,
    2397             :         MYSQLND_METHOD(mysqlnd_stmt, free_stmt_content),
    2398             :         MYSQLND_METHOD(mysqlnd_stmt, flush),
    2399             :         MYSQLND_METHOD(mysqlnd_stmt, free_stmt_result)
    2400             : MYSQLND_CLASS_METHODS_END;
    2401             : 
    2402             : 
    2403             : /* {{{ _mysqlnd_init_ps_subsystem */
    2404       23453 : void _mysqlnd_init_ps_subsystem()
    2405             : {
    2406       23453 :         mysqlnd_stmt_set_methods(&MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_stmt));
    2407       23453 :         _mysqlnd_init_ps_fetch_subsystem();
    2408       23453 : }
    2409             : /* }}} */
    2410             : 
    2411             : 
    2412             : /*
    2413             :  * Local variables:
    2414             :  * tab-width: 4
    2415             :  * c-basic-offset: 4
    2416             :  * End:
    2417             :  * vim600: noet sw=4 ts=4 fdm=marker
    2418             :  * vim<600: noet sw=4 ts=4
    2419             :  */

Generated by: LCOV version 1.10

Generated at Tue, 26 Jul 2016 17:07:38 +0000 (48 minutes ago)

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