root/ext/mysqlnd/mysqlnd_ps.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. MYSQLND_METHOD
  2. MYSQLND_METHOD
  3. MYSQLND_METHOD
  4. MYSQLND_METHOD
  5. mysqlnd_stmt_skip_metadata
  6. mysqlnd_stmt_read_prepare_response
  7. mysqlnd_stmt_prepare_read_eof
  8. mysqlnd_stmt_execute_parse_response
  9. MYSQLND_METHOD
  10. mysqlnd_stmt_fetch_row_buffered
  11. mysqlnd_stmt_fetch_row_unbuffered
  12. MYSQLND_METHOD
  13. mysqlnd_fetch_stmt_row_cursor
  14. MYSQLND_METHOD
  15. MYSQLND_METHOD
  16. MYSQLND_METHOD
  17. MYSQLND_METHOD
  18. MYSQLND_METHOD
  19. MYSQLND_METHOD
  20. MYSQLND_METHOD
  21. MYSQLND_METHOD
  22. MYSQLND_METHOD
  23. MYSQLND_METHOD
  24. MYSQLND_METHOD
  25. MYSQLND_METHOD
  26. MYSQLND_METHOD
  27. MYSQLND_METHOD
  28. MYSQLND_METHOD
  29. MYSQLND_METHOD
  30. mysqlnd_stmt_separate_result_bind
  31. mysqlnd_stmt_separate_one_result_bind
  32. MYSQLND_METHOD
  33. MYSQLND_METHOD
  34. MYSQLND_METHOD
  35. MYSQLND_METHOD
  36. MYSQLND_CLASS_METHODS_START
  37. _mysqlnd_init_ps_subsystem

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

/* [<][>][^][v][top][bottom][index][help] */