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