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