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

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