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

Generated by: LCOV version 1.10

Generated at Wed, 22 Oct 2014 07:24:52 +0000 (2 days ago)

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