PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LTP GCOV extension - code coverage report
Current view: directory - mysqlnd - mysqlnd_ps.c
Test: PHP Code Coverage
Date: 2009-11-21 Instrumented lines: 873
Code covered: 81.0 % Executed lines: 707
Legend: not executed executed

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

Generated by: LTP GCOV extension version 1.5

Generated at Sat, 21 Nov 2009 12:27:03 +0000 (3 days ago)

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