Annotation of embedaddon/php/ext/mysqlnd/mysqlnd_ps.c, revision 1.1.1.3
1.1 misho 1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
1.1.1.3 ! misho 5: | Copyright (c) 2006-2013 The PHP Group |
1.1 misho 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);
1.1.1.3 ! misho 639:
! 640: stmt->state = MYSQLND_STMT_PREPARED;
1.1 misho 641: } else if (stmt->state < MYSQLND_STMT_PREPARED) {
642: /* Only initted - error */
1.1.1.2 misho 643: SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
1.1 misho 644: mysqlnd_out_of_sync);
645: SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
646: DBG_INF("FAIL");
647: DBG_RETURN(FAIL);
648: }
649:
650: if (stmt->param_count) {
651: unsigned int i, not_bound = 0;
652: if (!stmt->param_bind) {
653: SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE,
654: "No data supplied for parameters in prepared statement");
655: DBG_INF("FAIL");
656: DBG_RETURN(FAIL);
657: }
658: for (i = 0; i < stmt->param_count; i++) {
659: if (stmt->param_bind[i].zv == NULL) {
660: not_bound++;
661: }
662: }
663: if (not_bound) {
664: char * msg;
1.1.1.2 misho 665: mnd_sprintf(&msg, 0, "No data supplied for %u parameter%s in prepared statement",
666: not_bound, not_bound>1 ?"s":"");
1.1 misho 667: SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE, msg);
668: if (msg) {
1.1.1.2 misho 669: mnd_sprintf_free(msg);
1.1 misho 670: }
671: DBG_INF("FAIL");
672: DBG_RETURN(FAIL);
673: }
674: }
675: ret = s->m->generate_execute_request(s, &request, &request_len, &free_request TSRMLS_CC);
676: if (ret == PASS) {
677: /* support for buffer types should be added here ! */
1.1.1.2 misho 678: ret = stmt->conn->m->simple_command(stmt->conn, COM_STMT_EXECUTE, request, request_len,
1.1 misho 679: PROT_LAST /* we will handle the response packet*/,
680: FALSE, FALSE TSRMLS_CC);
681: } else {
682: SET_STMT_ERROR(stmt, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Couldn't generate the request. Possibly OOM.");
683: }
684:
685: if (free_request) {
686: mnd_efree(request);
687: }
688:
689: if (ret == FAIL) {
1.1.1.2 misho 690: COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
1.1 misho 691: DBG_INF("FAIL");
692: DBG_RETURN(FAIL);
693: }
694: stmt->execute_count++;
695:
696: ret = s->m->parse_execute_response(s TSRMLS_CC);
697:
1.1.1.2 misho 698: 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 699:
1.1.1.2 misho 700: if (ret == PASS && conn->last_query_type == QUERY_UPSERT && stmt->upsert_status->affected_rows) {
701: MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_PS, stmt->upsert_status->affected_rows);
1.1 misho 702: }
703: DBG_RETURN(ret);
704: }
705: /* }}} */
706:
707:
708: /* {{{ mysqlnd_stmt_fetch_row_buffered */
709: enum_func_status
710: mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
711: {
712: MYSQLND_STMT * s = (MYSQLND_STMT *) param;
713: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
714: MYSQLND_RES_BUFFERED *set = result->stored_data;
715: unsigned int field_count = result->meta->field_count;
716:
717: DBG_ENTER("mysqlnd_stmt_fetch_row_buffered");
718: *fetched_anything = FALSE;
719: DBG_INF_FMT("stmt=%lu", stmt != NULL ? stmt->stmt_id : 0L);
720:
721: /* If we haven't read everything */
722: if (set->data_cursor &&
723: (set->data_cursor - set->data) < (set->row_count * field_count))
724: {
725: /* The user could have skipped binding - don't crash*/
726: if (stmt->result_bind) {
727: unsigned int i;
728: MYSQLND_RES_METADATA * meta = result->meta;
729: zval **current_row = set->data_cursor;
730:
731: if (NULL == current_row[0]) {
732: uint64_t row_num = (set->data_cursor - set->data) / field_count;
733: enum_func_status rc = result->m.row_decoder(set->row_buffers[row_num],
734: current_row,
735: meta->field_count,
736: meta->fields,
1.1.1.2 misho 737: result->conn->options->numeric_and_datetime_as_unicode,
738: result->conn->options->int_and_float_native,
1.1 misho 739: result->conn->stats TSRMLS_CC);
740: if (PASS != rc) {
741: DBG_RETURN(FAIL);
742: }
743: set->initialized_rows++;
744: if (stmt->update_max_length) {
745: for (i = 0; i < result->field_count; i++) {
746: /*
747: NULL fields are 0 length, 0 is not more than 0
748: String of zero size, definitely can't be the next max_length.
749: Thus for NULL and zero-length we are quite efficient.
750: */
751: if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
752: unsigned long len = Z_STRLEN_P(current_row[i]);
753: if (meta->fields[i].max_length < len) {
754: meta->fields[i].max_length = len;
755: }
756: }
757: }
758: }
759: }
760:
761: for (i = 0; i < result->field_count; i++) {
762: /* Clean what we copied last time */
763: #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
764: if (stmt->result_bind[i].zv) {
765: zval_dtor(stmt->result_bind[i].zv);
766: }
767: #endif
768: /* copy the type */
769: if (stmt->result_bind[i].bound == TRUE) {
770: DBG_INF_FMT("i=%u type=%u", i, Z_TYPE_P(current_row[i]));
771: if (Z_TYPE_P(current_row[i]) != IS_NULL) {
772: /*
773: Copy the value.
774: Pre-condition is that the zvals in the result_bind buffer
775: have been ZVAL_NULL()-ed or to another simple type
776: (int, double, bool but not string). Because of the reference
777: counting the user can't delete the strings the variables point to.
778: */
779:
780: Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(current_row[i]);
781: stmt->result_bind[i].zv->value = current_row[i]->value;
782: #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
783: zval_copy_ctor(stmt->result_bind[i].zv);
784: #endif
785: } else {
786: ZVAL_NULL(stmt->result_bind[i].zv);
787: }
788: }
789: }
790: }
791: set->data_cursor += field_count;
792: *fetched_anything = TRUE;
793: /* buffered result sets don't have a connection */
794: MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_PS_BUF);
795: DBG_INF("row fetched");
796: } else {
797: set->data_cursor = NULL;
798: DBG_INF("no more data");
799: }
800: DBG_INF("PASS");
801: DBG_RETURN(PASS);
802: }
803: /* }}} */
804:
805:
806: /* {{{ mysqlnd_stmt_fetch_row_unbuffered */
807: static enum_func_status
808: mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
809: {
810: enum_func_status ret;
811: MYSQLND_STMT * s = (MYSQLND_STMT *) param;
812: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
813: MYSQLND_PACKET_ROW * row_packet;
814:
815: DBG_ENTER("mysqlnd_stmt_fetch_row_unbuffered");
816:
817: *fetched_anything = FALSE;
818:
819: if (result->unbuf->eof_reached) {
820: /* No more rows obviously */
1.1.1.2 misho 821: DBG_INF("EOF already reached");
1.1 misho 822: DBG_RETURN(PASS);
823: }
824: if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) {
1.1.1.2 misho 825: SET_CLIENT_ERROR(*result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
1.1 misho 826: UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
827: DBG_ERR("command out of sync");
828: DBG_RETURN(FAIL);
829: }
830: if (!(row_packet = result->row_packet)) {
831: DBG_RETURN(FAIL);
832: }
833:
834: /* Let the row packet fill our buffer and skip additional malloc + memcpy */
835: row_packet->skip_extraction = stmt && stmt->result_bind? FALSE:TRUE;
836:
837: /*
838: If we skip rows (stmt == NULL || stmt->result_bind == NULL) we have to
839: result->m.unbuffered_free_last_data() before it. The function returns always true.
840: */
841: if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
842: unsigned int i, field_count = result->field_count;
843:
844: if (!row_packet->skip_extraction) {
845: result->m.unbuffered_free_last_data(result TSRMLS_CC);
846:
847: result->unbuf->last_row_data = row_packet->fields;
848: result->unbuf->last_row_buffer = row_packet->row_buffer;
849: row_packet->fields = NULL;
850: row_packet->row_buffer = NULL;
851:
852: if (PASS != result->m.row_decoder(result->unbuf->last_row_buffer,
853: result->unbuf->last_row_data,
854: row_packet->field_count,
855: row_packet->fields_metadata,
1.1.1.2 misho 856: result->conn->options->numeric_and_datetime_as_unicode,
857: result->conn->options->int_and_float_native,
1.1 misho 858: result->conn->stats TSRMLS_CC))
859: {
860: DBG_RETURN(FAIL);
861: }
862:
863: for (i = 0; i < field_count; i++) {
864: if (stmt->result_bind[i].bound == TRUE) {
865: 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: zval_dtor(stmt->result_bind[i].zv);
872: #endif
873: if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data)) ) {
874: if (
875: (Z_TYPE_P(data) == IS_STRING
876: #if MYSQLND_UNICODE
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: result->meta->fields[i].max_length = Z_STRLEN_P(data);
883: }
884: stmt->result_bind[i].zv->value = data->value;
885: /* copied data, thus also the ownership. Thus null data */
886: ZVAL_NULL(data);
887: }
888: }
889: }
890: MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_UNBUF);
891: } else {
892: 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: row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC);
900: row_packet->row_buffer = NULL;
901: }
902:
903: result->unbuf->row_count++;
904: *fetched_anything = TRUE;
905: } else if (ret == FAIL) {
906: if (row_packet->error_info.error_no) {
1.1.1.2 misho 907: COPY_CLIENT_ERROR(*stmt->conn->error_info, row_packet->error_info);
908: COPY_CLIENT_ERROR(*stmt->error_info, row_packet->error_info);
1.1 misho 909: }
910: CONN_SET_STATE(result->conn, CONN_READY);
911: result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
912: } else if (row_packet->eof) {
913: DBG_INF("EOF");
914: /* Mark the connection as usable again */
915: result->unbuf->eof_reached = TRUE;
1.1.1.2 misho 916: result->conn->upsert_status->warning_count = row_packet->warning_count;
917: result->conn->upsert_status->server_status = row_packet->server_status;
1.1 misho 918: /*
919: result->row_packet will be cleaned when
920: destroying the result object
921: */
1.1.1.2 misho 922: if (result->conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
1.1 misho 923: CONN_SET_STATE(result->conn, CONN_NEXT_RESULT_PENDING);
924: } else {
925: CONN_SET_STATE(result->conn, CONN_READY);
926: }
927: }
928:
929: DBG_INF_FMT("ret=%s fetched_anything=%u", ret == PASS? "PASS":"FAIL", *fetched_anything);
930: DBG_RETURN(ret);
931: }
932: /* }}} */
933:
934:
935: /* {{{ mysqlnd_stmt::use_result */
936: static MYSQLND_RES *
937: MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s TSRMLS_DC)
938: {
939: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1.1.1.2 misho 940: MYSQLND_RES * result;
941: MYSQLND_CONN_DATA * conn;
1.1 misho 942:
943: DBG_ENTER("mysqlnd_stmt::use_result");
944: if (!stmt || !stmt->conn || !stmt->result) {
945: DBG_RETURN(NULL);
946: }
947: DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
948:
949: conn = stmt->conn;
950:
951: if (!stmt->field_count ||
952: (!stmt->cursor_exists && CONN_GET_STATE(conn) != CONN_FETCHING_DATA) ||
953: (stmt->cursor_exists && CONN_GET_STATE(conn) != CONN_READY) ||
954: (stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE))
955: {
1.1.1.2 misho 956: SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
1.1 misho 957: UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
958: DBG_ERR("command out of sync");
959: DBG_RETURN(NULL);
960: }
961:
1.1.1.2 misho 962: SET_EMPTY_ERROR(*stmt->error_info);
1.1 misho 963:
964: MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_PS_UNBUFFERED_SETS);
965: result = stmt->result;
966:
967: result->m.use_result(stmt->result, TRUE TSRMLS_CC);
968: result->m.fetch_row = stmt->cursor_exists? mysqlnd_fetch_stmt_row_cursor:
969: mysqlnd_stmt_fetch_row_unbuffered;
970: stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;
971:
972: DBG_INF_FMT("%p", result);
973: DBG_RETURN(result);
974: }
975: /* }}} */
976:
977:
978: #define STMT_ID_LENGTH 4
979:
980: /* {{{ mysqlnd_fetch_row_cursor */
981: enum_func_status
982: mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
983: {
984: enum_func_status ret;
985: MYSQLND_STMT * s = (MYSQLND_STMT *) param;
986: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
987: zend_uchar buf[STMT_ID_LENGTH /* statement id */ + 4 /* number of rows to fetch */];
988: MYSQLND_PACKET_ROW * row_packet;
989:
990: DBG_ENTER("mysqlnd_fetch_stmt_row_cursor");
991:
992: if (!stmt || !stmt->conn || !result || !result->conn || !result->unbuf) {
993: DBG_ERR("no statement");
994: DBG_RETURN(FAIL);
995: }
996:
997: DBG_INF_FMT("stmt=%lu flags=%u", stmt->stmt_id, flags);
998:
999: if (stmt->state < MYSQLND_STMT_USER_FETCHING) {
1000: /* Only initted - error */
1.1.1.2 misho 1001: SET_CLIENT_ERROR(*stmt->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
1.1 misho 1002: mysqlnd_out_of_sync);
1003: DBG_ERR("command out of sync");
1004: DBG_RETURN(FAIL);
1005: }
1006: if (!(row_packet = result->row_packet)) {
1007: DBG_RETURN(FAIL);
1008: }
1009:
1.1.1.2 misho 1010: SET_EMPTY_ERROR(*stmt->error_info);
1011: SET_EMPTY_ERROR(*stmt->conn->error_info);
1.1 misho 1012:
1013: int4store(buf, stmt->stmt_id);
1014: int4store(buf + STMT_ID_LENGTH, 1); /* for now fetch only one row */
1015:
1.1.1.2 misho 1016: if (FAIL == stmt->conn->m->simple_command(stmt->conn, COM_STMT_FETCH, buf, sizeof(buf),
1.1 misho 1017: PROT_LAST /* we will handle the response packet*/,
1018: FALSE, TRUE TSRMLS_CC)) {
1.1.1.2 misho 1019: COPY_CLIENT_ERROR(*stmt->error_info, *stmt->conn->error_info);
1.1 misho 1020: DBG_RETURN(FAIL);
1021: }
1022:
1023: row_packet->skip_extraction = stmt->result_bind? FALSE:TRUE;
1024:
1025: if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
1026: unsigned int i, field_count = result->field_count;
1027:
1028: if (!row_packet->skip_extraction) {
1029: result->m.unbuffered_free_last_data(result TSRMLS_CC);
1030:
1031: result->unbuf->last_row_data = row_packet->fields;
1032: result->unbuf->last_row_buffer = row_packet->row_buffer;
1033: row_packet->fields = NULL;
1034: row_packet->row_buffer = NULL;
1035:
1036: if (PASS != result->m.row_decoder(result->unbuf->last_row_buffer,
1037: result->unbuf->last_row_data,
1038: row_packet->field_count,
1039: row_packet->fields_metadata,
1.1.1.2 misho 1040: result->conn->options->numeric_and_datetime_as_unicode,
1041: result->conn->options->int_and_float_native,
1.1 misho 1042: result->conn->stats TSRMLS_CC))
1043: {
1044: DBG_RETURN(FAIL);
1045: }
1046:
1047: /* If no result bind, do nothing. We consumed the data */
1048: for (i = 0; i < field_count; i++) {
1049: if (stmt->result_bind[i].bound == TRUE) {
1050: zval *data = result->unbuf->last_row_data[i];
1051: /*
1052: stmt->result_bind[i].zv has been already destructed
1053: in result->m.unbuffered_free_last_data()
1054: */
1055: #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
1056: zval_dtor(stmt->result_bind[i].zv);
1057: #endif
1058: DBG_INF_FMT("i=%u bound_var=%p type=%u refc=%u", i, stmt->result_bind[i].zv,
1059: Z_TYPE_P(data), Z_REFCOUNT_P(stmt->result_bind[i].zv));
1060: if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data))) {
1061: if ((Z_TYPE_P(data) == IS_STRING
1062: #if MYSQLND_UNICODE
1063: || Z_TYPE_P(data) == IS_UNICODE
1064: #endif
1065: )
1066: && (result->meta->fields[i].max_length < (unsigned long) Z_STRLEN_P(data)))
1067: {
1068: result->meta->fields[i].max_length = Z_STRLEN_P(data);
1069: }
1070: stmt->result_bind[i].zv->value = data->value;
1071: /* copied data, thus also the ownership. Thus null data */
1072: ZVAL_NULL(data);
1073: }
1074: }
1075: }
1076: } else {
1077: DBG_INF("skipping extraction");
1078: /*
1079: Data has been allocated and usually result->m.unbuffered_free_last_data()
1080: frees it but we can't call this function as it will cause problems with
1081: the bound variables. Thus we need to do part of what it does or Zend will
1082: report leaks.
1083: */
1084: row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC);
1085: row_packet->row_buffer = NULL;
1086: }
1087: /* We asked for one row, the next one should be EOF, eat it */
1088: ret = PACKET_READ(row_packet, result->conn);
1089: if (row_packet->row_buffer) {
1090: row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC);
1091: row_packet->row_buffer = NULL;
1092: }
1093: MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_CURSOR);
1094:
1095: result->unbuf->row_count++;
1096: *fetched_anything = TRUE;
1097: } else {
1098: *fetched_anything = FALSE;
1099:
1.1.1.2 misho 1100: stmt->upsert_status->warning_count =
1101: stmt->conn->upsert_status->warning_count =
1.1 misho 1102: row_packet->warning_count;
1103:
1.1.1.2 misho 1104: stmt->upsert_status->server_status =
1105: stmt->conn->upsert_status->server_status =
1.1 misho 1106: row_packet->server_status;
1107:
1108: result->unbuf->eof_reached = row_packet->eof;
1109: }
1.1.1.2 misho 1110: stmt->upsert_status->warning_count =
1111: stmt->conn->upsert_status->warning_count =
1.1 misho 1112: row_packet->warning_count;
1.1.1.2 misho 1113: stmt->upsert_status->server_status =
1114: stmt->conn->upsert_status->server_status =
1.1 misho 1115: row_packet->server_status;
1116:
1117: DBG_INF_FMT("ret=%s fetched=%u server_status=%u warnings=%u eof=%u",
1118: ret == PASS? "PASS":"FAIL", *fetched_anything,
1119: row_packet->server_status, row_packet->warning_count,
1120: result->unbuf->eof_reached);
1121: DBG_RETURN(ret);
1122: }
1123: /* }}} */
1124:
1125:
1126: /* {{{ mysqlnd_stmt::fetch */
1127: static enum_func_status
1128: MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const s, zend_bool * const fetched_anything TSRMLS_DC)
1129: {
1130: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1131: enum_func_status ret;
1132: DBG_ENTER("mysqlnd_stmt::fetch");
1133: if (!stmt || !stmt->conn) {
1134: DBG_RETURN(FAIL);
1135: }
1136: DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
1137:
1138: if (!stmt->result ||
1139: stmt->state < MYSQLND_STMT_WAITING_USE_OR_STORE) {
1140: SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
1141:
1142: DBG_ERR("command out of sync");
1143: DBG_RETURN(FAIL);
1144: } else if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
1145: /* Execute only once. We have to free the previous contents of user's bound vars */
1146:
1147: stmt->default_rset_handler(s TSRMLS_CC);
1148: }
1149: stmt->state = MYSQLND_STMT_USER_FETCHING;
1150:
1.1.1.2 misho 1151: SET_EMPTY_ERROR(*stmt->error_info);
1152: SET_EMPTY_ERROR(*stmt->conn->error_info);
1.1 misho 1153:
1154: DBG_INF_FMT("result_bind=%p separated_once=%u", stmt->result_bind, stmt->result_zvals_separated_once);
1155: /*
1156: The user might have not bound any variables for result.
1157: Do the binding once she does it.
1158: */
1159: if (stmt->result_bind && !stmt->result_zvals_separated_once) {
1160: unsigned int i;
1161: /*
1162: mysqlnd_stmt_store_result() has been called free the bind
1163: variables to prevent leaking of their previous content.
1164: */
1165: for (i = 0; i < stmt->result->field_count; i++) {
1166: if (stmt->result_bind[i].bound == TRUE) {
1167: zval_dtor(stmt->result_bind[i].zv);
1168: ZVAL_NULL(stmt->result_bind[i].zv);
1169: }
1170: }
1171: stmt->result_zvals_separated_once = TRUE;
1172: }
1173:
1174: ret = stmt->result->m.fetch_row(stmt->result, (void*)s, 0, fetched_anything TSRMLS_CC);
1175: DBG_RETURN(ret);
1176: }
1177: /* }}} */
1178:
1179:
1180: /* {{{ mysqlnd_stmt::reset */
1181: static enum_func_status
1182: MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const s TSRMLS_DC)
1183: {
1184: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1185: enum_func_status ret = PASS;
1186: zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */];
1187:
1188: DBG_ENTER("mysqlnd_stmt::reset");
1189: if (!stmt || !stmt->conn) {
1190: DBG_RETURN(FAIL);
1191: }
1192: DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
1193:
1.1.1.2 misho 1194: SET_EMPTY_ERROR(*stmt->error_info);
1195: SET_EMPTY_ERROR(*stmt->conn->error_info);
1.1 misho 1196:
1197: if (stmt->stmt_id) {
1.1.1.2 misho 1198: MYSQLND_CONN_DATA * conn = stmt->conn;
1.1 misho 1199: if (stmt->param_bind) {
1200: unsigned int i;
1201: DBG_INF("resetting long data");
1202: /* Reset Long Data */
1203: for (i = 0; i < stmt->param_count; i++) {
1204: if (stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED) {
1205: stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
1206: }
1207: }
1208: }
1209:
1.1.1.2 misho 1210: s->m->flush(s TSRMLS_CC);
1211:
1212: /*
1213: Don't free now, let the result be usable. When the stmt will again be
1214: executed then the result set will be cleaned, the bound variables will
1215: be separated before that.
1216: */
1217:
1218: int4store(cmd_buf, stmt->stmt_id);
1219: if (CONN_GET_STATE(conn) == CONN_READY &&
1220: FAIL == (ret = conn->m->simple_command(conn, COM_STMT_RESET, cmd_buf,
1221: sizeof(cmd_buf), PROT_OK_PACKET,
1222: FALSE, TRUE TSRMLS_CC))) {
1223: COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
1224: }
1225: *stmt->upsert_status = *conn->upsert_status;
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: DBG_INF(ret == PASS? "PASS":"FAIL");
1266: DBG_RETURN(ret);
1267: }
1268: /* }}} */
1269:
1270:
1271: /* {{{ mysqlnd_stmt::send_long_data */
1272: static enum_func_status
1273: MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const s, unsigned int param_no,
1274: const char * const data, unsigned long length TSRMLS_DC)
1275: {
1276: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1277: enum_func_status ret = FAIL;
1.1.1.2 misho 1278: MYSQLND_CONN_DATA * conn;
1279: zend_uchar * cmd_buf;
1.1 misho 1280: enum php_mysqlnd_server_command cmd = COM_STMT_SEND_LONG_DATA;
1281:
1282: DBG_ENTER("mysqlnd_stmt::send_long_data");
1283: if (!stmt || !stmt->conn) {
1284: DBG_RETURN(FAIL);
1285: }
1286: DBG_INF_FMT("stmt=%lu param_no=%u data_len=%lu", stmt->stmt_id, param_no, length);
1287:
1288: conn = stmt->conn;
1289:
1.1.1.2 misho 1290: SET_EMPTY_ERROR(*stmt->error_info);
1291: SET_EMPTY_ERROR(*stmt->conn->error_info);
1.1 misho 1292:
1293: if (stmt->state < MYSQLND_STMT_PREPARED) {
1294: SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
1295: DBG_ERR("not prepared");
1296: DBG_RETURN(FAIL);
1297: }
1298: if (!stmt->param_bind) {
1299: SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
1300: DBG_ERR("command out of sync");
1301: DBG_RETURN(FAIL);
1302: }
1303: if (param_no >= stmt->param_count) {
1304: SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
1305: DBG_ERR("invalid param_no");
1306: DBG_RETURN(FAIL);
1307: }
1308: if (stmt->param_bind[param_no].type != MYSQL_TYPE_LONG_BLOB) {
1309: SET_STMT_ERROR(stmt, CR_INVALID_BUFFER_USE, UNKNOWN_SQLSTATE, mysqlnd_not_bound_as_blob);
1310: DBG_ERR("param_no is not of a blob type");
1311: DBG_RETURN(FAIL);
1312: }
1313:
1314: /*
1315: XXX: Unfortunately we have to allocate additional buffer to be able the
1316: additional data, which is like a header inside the payload.
1317: This should be optimised, but it will be a pervasive change, so
1318: conn->m->simple_command() will accept not a buffer, but actually MYSQLND_STRING*
1319: terminated by NULL, to send. If the strings are not big, we can collapse them
1320: on the buffer every connection has, but otherwise we will just send them
1321: one by one to the wire.
1322: */
1323:
1324: if (CONN_GET_STATE(conn) == CONN_READY) {
1325: size_t packet_len;
1326: cmd_buf = mnd_emalloc(packet_len = STMT_ID_LENGTH + 2 + length);
1327: if (cmd_buf) {
1328: stmt->param_bind[param_no].flags |= MYSQLND_PARAM_BIND_BLOB_USED;
1329:
1330: int4store(cmd_buf, stmt->stmt_id);
1331: int2store(cmd_buf + STMT_ID_LENGTH, param_no);
1332: memcpy(cmd_buf + STMT_ID_LENGTH + 2, data, length);
1333:
1334: /* COM_STMT_SEND_LONG_DATA doesn't send an OK packet*/
1.1.1.2 misho 1335: ret = conn->m->simple_command(conn, cmd, cmd_buf, packet_len, PROT_LAST , FALSE, TRUE TSRMLS_CC);
1.1 misho 1336: mnd_efree(cmd_buf);
1337: if (FAIL == ret) {
1.1.1.2 misho 1338: COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
1.1 misho 1339: }
1340: } else {
1341: ret = FAIL;
1.1.1.2 misho 1342: SET_OOM_ERROR(*stmt->error_info);
1343: SET_OOM_ERROR(*conn->error_info);
1.1 misho 1344: }
1345: /*
1346: Cover protocol error: COM_STMT_SEND_LONG_DATA was designed to be quick and not
1347: sent response packets. According to documentation the only way to get an error
1348: is to have out-of-memory on the server-side. However, that's not true, as if
1349: max_allowed_packet_size is smaller than the chunk being sent to the server, the
1350: latter will complain with an error message. However, normally we don't expect
1351: an error message, thus we continue. When sending the next command, which expects
1352: response we will read the unexpected data and error message will look weird.
1353: Therefore we do non-blocking read to clean the line, if there is a need.
1354: Nevertheless, there is a built-in protection when sending a command packet, that
1355: checks if the line is clear - useful for debug purposes and to be switched off
1356: in release builds.
1357:
1358: Maybe we can make it automatic by checking what's the value of
1359: max_allowed_packet_size on the server and resending the data.
1360: */
1361: #ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
1362: #if HAVE_USLEEP && !defined(PHP_WIN32)
1363: usleep(120000);
1364: #endif
1365: if ((packet_len = conn->net->m.consume_uneaten_data(conn->net, cmd TSRMLS_CC))) {
1366: php_error_docref(NULL TSRMLS_CC, E_WARNING, "There was an error "
1367: "while sending long data. Probably max_allowed_packet_size "
1368: "is smaller than the data. You have to increase it or send "
1369: "smaller chunks of data. Answer was "MYSQLND_SZ_T_SPEC" bytes long.", packet_len);
1370: SET_STMT_ERROR(stmt, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE,
1371: "Server responded to COM_STMT_SEND_LONG_DATA.");
1372: ret = FAIL;
1373: }
1374: #endif
1375: }
1376:
1377: DBG_INF(ret == PASS? "PASS":"FAIL");
1378: DBG_RETURN(ret);
1379: }
1380: /* }}} */
1381:
1382:
1383: /* {{{ mysqlnd_stmt::bind_parameters */
1384: static enum_func_status
1385: MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const s, MYSQLND_PARAM_BIND * const param_bind TSRMLS_DC)
1386: {
1387: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1388: DBG_ENTER("mysqlnd_stmt::bind_param");
1389: if (!stmt || !stmt->conn) {
1390: DBG_RETURN(FAIL);
1391: }
1392: DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count);
1393:
1394: if (stmt->state < MYSQLND_STMT_PREPARED) {
1395: SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
1396: DBG_ERR("not prepared");
1397: if (param_bind) {
1398: s->m->free_parameter_bind(s, param_bind TSRMLS_CC);
1399: }
1400: DBG_RETURN(FAIL);
1401: }
1402:
1.1.1.2 misho 1403: SET_EMPTY_ERROR(*stmt->error_info);
1404: SET_EMPTY_ERROR(*stmt->conn->error_info);
1.1 misho 1405:
1406: if (stmt->param_count) {
1407: unsigned int i = 0;
1408:
1409: if (!param_bind) {
1410: SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, "Re-binding (still) not supported");
1411: DBG_ERR("Re-binding (still) not supported");
1412: DBG_RETURN(FAIL);
1413: } else if (stmt->param_bind) {
1414: DBG_INF("Binding");
1415: /*
1416: There is already result bound.
1417: Forbid for now re-binding!!
1418: */
1419: for (i = 0; i < stmt->param_count; i++) {
1420: /*
1.1.1.2 misho 1421: We may have the last reference, then call zval_ptr_dtor() or we may leak memory.
1.1 misho 1422: Switching from bind_one_parameter to bind_parameters may result in zv being NULL
1423: */
1424: if (stmt->param_bind[i].zv) {
1425: zval_ptr_dtor(&stmt->param_bind[i].zv);
1426: }
1427: }
1428: if (stmt->param_bind != param_bind) {
1429: s->m->free_parameter_bind(s, stmt->param_bind TSRMLS_CC);
1430: }
1431: }
1432:
1433: stmt->param_bind = param_bind;
1434: for (i = 0; i < stmt->param_count; i++) {
1435: /* The client will use stmt_send_long_data */
1436: DBG_INF_FMT("%u is of type %u", i, stmt->param_bind[i].type);
1437: /* Prevent from freeing */
1438: /* Don't update is_ref, or we will leak during conversion */
1439: Z_ADDREF_P(stmt->param_bind[i].zv);
1440: stmt->param_bind[i].flags = 0;
1441: if (stmt->param_bind[i].type == MYSQL_TYPE_LONG_BLOB) {
1442: stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
1443: }
1444: }
1445: stmt->send_types_to_server = 1;
1446: }
1447: DBG_INF("PASS");
1448: DBG_RETURN(PASS);
1449: }
1450: /* }}} */
1451:
1452:
1453: /* {{{ mysqlnd_stmt::bind_one_parameter */
1454: static enum_func_status
1455: MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter)(MYSQLND_STMT * const s, unsigned int param_no,
1456: zval * const zv, zend_uchar type TSRMLS_DC)
1457: {
1458: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1459: DBG_ENTER("mysqlnd_stmt::bind_one_parameter");
1460: if (!stmt || !stmt->conn) {
1461: DBG_RETURN(FAIL);
1462: }
1.1.1.2 misho 1463: 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 1464:
1465: if (stmt->state < MYSQLND_STMT_PREPARED) {
1466: SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
1467: DBG_ERR("not prepared");
1468: DBG_RETURN(FAIL);
1469: }
1470:
1471: if (param_no >= stmt->param_count) {
1472: SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
1473: DBG_ERR("invalid param_no");
1474: DBG_RETURN(FAIL);
1475: }
1.1.1.2 misho 1476: SET_EMPTY_ERROR(*stmt->error_info);
1477: SET_EMPTY_ERROR(*stmt->conn->error_info);
1.1 misho 1478:
1479: if (stmt->param_count) {
1480: if (!stmt->param_bind) {
1.1.1.3 ! misho 1481: stmt->param_bind = mnd_pecalloc(stmt->param_count, sizeof(MYSQLND_PARAM_BIND), stmt->persistent);
1.1 misho 1482: if (!stmt->param_bind) {
1483: DBG_RETURN(FAIL);
1484: }
1485: }
1486:
1487: /* Prevent from freeing */
1488: /* Don't update is_ref, or we will leak during conversion */
1489: Z_ADDREF_P(zv);
1490: DBG_INF("Binding");
1491: /* Release what we had, if we had */
1492: if (stmt->param_bind[param_no].zv) {
1493: zval_ptr_dtor(&stmt->param_bind[param_no].zv);
1494: }
1495: if (type == MYSQL_TYPE_LONG_BLOB) {
1496: /* The client will use stmt_send_long_data */
1497: stmt->param_bind[param_no].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
1498: }
1499: stmt->param_bind[param_no].zv = zv;
1500: stmt->param_bind[param_no].type = type;
1501:
1502: stmt->send_types_to_server = 1;
1503: }
1504: DBG_INF("PASS");
1505: DBG_RETURN(PASS);
1506: }
1507: /* }}} */
1508:
1509:
1510: /* {{{ mysqlnd_stmt::refresh_bind_param */
1511: static enum_func_status
1512: MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param)(MYSQLND_STMT * const s TSRMLS_DC)
1513: {
1514: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1515: DBG_ENTER("mysqlnd_stmt::refresh_bind_param");
1516: if (!stmt || !stmt->conn) {
1517: DBG_RETURN(FAIL);
1518: }
1519: DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count);
1520:
1521: if (stmt->state < MYSQLND_STMT_PREPARED) {
1522: SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
1523: DBG_ERR("not prepared");
1524: DBG_RETURN(FAIL);
1525: }
1526:
1.1.1.2 misho 1527: SET_EMPTY_ERROR(*stmt->error_info);
1528: SET_EMPTY_ERROR(*stmt->conn->error_info);
1.1 misho 1529:
1530: if (stmt->param_count) {
1531: stmt->send_types_to_server = 1;
1532: }
1533: DBG_RETURN(PASS);
1534: }
1535: /* }}} */
1536:
1537:
1538: /* {{{ mysqlnd_stmt::bind_result */
1539: static enum_func_status
1540: MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const s,
1541: MYSQLND_RESULT_BIND * const result_bind TSRMLS_DC)
1542: {
1543: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1544: DBG_ENTER("mysqlnd_stmt::bind_result");
1545: if (!stmt || !stmt->conn) {
1546: DBG_RETURN(FAIL);
1547: }
1548: DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count);
1549:
1550: if (stmt->state < MYSQLND_STMT_PREPARED) {
1551: SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
1552: if (result_bind) {
1553: s->m->free_result_bind(s, result_bind TSRMLS_CC);
1554: }
1555: DBG_ERR("not prepared");
1556: DBG_RETURN(FAIL);
1557: }
1558:
1.1.1.2 misho 1559: SET_EMPTY_ERROR(*stmt->error_info);
1560: SET_EMPTY_ERROR(*stmt->conn->error_info);
1.1 misho 1561:
1562: if (stmt->field_count) {
1563: unsigned int i = 0;
1564:
1565: if (!result_bind) {
1566: DBG_ERR("no result bind passed");
1567: DBG_RETURN(FAIL);
1568: }
1569:
1570: mysqlnd_stmt_separate_result_bind(s TSRMLS_CC);
1571: stmt->result_zvals_separated_once = FALSE;
1572: stmt->result_bind = result_bind;
1573: for (i = 0; i < stmt->field_count; i++) {
1574: /* Prevent from freeing */
1575: Z_ADDREF_P(stmt->result_bind[i].zv);
1576: DBG_INF_FMT("ref of %p = %u", stmt->result_bind[i].zv, Z_REFCOUNT_P(stmt->result_bind[i].zv));
1577: /*
1578: Don't update is_ref !!! it's not our job
1579: Otherwise either 009.phpt or mysqli_stmt_bind_result.phpt
1580: will fail.
1581: */
1582: stmt->result_bind[i].bound = TRUE;
1583: }
1584: } else if (result_bind) {
1585: s->m->free_result_bind(s, result_bind TSRMLS_CC);
1586: }
1587: DBG_INF("PASS");
1588: DBG_RETURN(PASS);
1589: }
1590: /* }}} */
1591:
1592:
1593: /* {{{ mysqlnd_stmt::bind_result */
1594: static enum_func_status
1595: MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const s, unsigned int param_no TSRMLS_DC)
1596: {
1597: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1598: DBG_ENTER("mysqlnd_stmt::bind_result");
1599: if (!stmt || !stmt->conn) {
1600: DBG_RETURN(FAIL);
1601: }
1602: DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count);
1603:
1604: if (stmt->state < MYSQLND_STMT_PREPARED) {
1605: SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
1606: DBG_ERR("not prepared");
1607: DBG_RETURN(FAIL);
1608: }
1609:
1610: if (param_no >= stmt->field_count) {
1611: SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
1612: DBG_ERR("invalid param_no");
1613: DBG_RETURN(FAIL);
1614: }
1615:
1.1.1.2 misho 1616: SET_EMPTY_ERROR(*stmt->error_info);
1617: SET_EMPTY_ERROR(*stmt->conn->error_info);
1.1 misho 1618:
1619: if (stmt->field_count) {
1620: mysqlnd_stmt_separate_one_result_bind(s, param_no TSRMLS_CC);
1621: /* Guaranteed is that stmt->result_bind is NULL */
1622: if (!stmt->result_bind) {
1.1.1.3 ! misho 1623: stmt->result_bind = mnd_pecalloc(stmt->field_count, sizeof(MYSQLND_RESULT_BIND), stmt->persistent);
1.1 misho 1624: } else {
1.1.1.3 ! misho 1625: stmt->result_bind = mnd_perealloc(stmt->result_bind, stmt->field_count * sizeof(MYSQLND_RESULT_BIND), stmt->persistent);
1.1 misho 1626: }
1627: if (!stmt->result_bind) {
1628: DBG_RETURN(FAIL);
1629: }
1630: ALLOC_INIT_ZVAL(stmt->result_bind[param_no].zv);
1631: /*
1632: Don't update is_ref !!! it's not our job
1633: Otherwise either 009.phpt or mysqli_stmt_bind_result.phpt
1634: will fail.
1635: */
1636: stmt->result_bind[param_no].bound = TRUE;
1637: }
1638: DBG_INF("PASS");
1639: DBG_RETURN(PASS);
1640: }
1641: /* }}} */
1642:
1643:
1644: /* {{{ mysqlnd_stmt::insert_id */
1645: static uint64_t
1646: MYSQLND_METHOD(mysqlnd_stmt, insert_id)(const MYSQLND_STMT * const s TSRMLS_DC)
1647: {
1648: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1.1.1.2 misho 1649: return stmt? stmt->upsert_status->last_insert_id : 0;
1.1 misho 1650: }
1651: /* }}} */
1652:
1653:
1654: /* {{{ mysqlnd_stmt::affected_rows */
1655: static uint64_t
1656: MYSQLND_METHOD(mysqlnd_stmt, affected_rows)(const MYSQLND_STMT * const s TSRMLS_DC)
1657: {
1658: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1.1.1.2 misho 1659: return stmt? stmt->upsert_status->affected_rows : 0;
1.1 misho 1660: }
1661: /* }}} */
1662:
1663:
1664: /* {{{ mysqlnd_stmt::num_rows */
1665: static uint64_t
1666: MYSQLND_METHOD(mysqlnd_stmt, num_rows)(const MYSQLND_STMT * const s TSRMLS_DC)
1667: {
1668: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1669: return stmt && stmt->result? mysqlnd_num_rows(stmt->result):0;
1670: }
1671: /* }}} */
1672:
1673:
1674: /* {{{ mysqlnd_stmt::warning_count */
1675: static unsigned int
1676: MYSQLND_METHOD(mysqlnd_stmt, warning_count)(const MYSQLND_STMT * const s TSRMLS_DC)
1677: {
1678: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1.1.1.2 misho 1679: return stmt? stmt->upsert_status->warning_count : 0;
1.1 misho 1680: }
1681: /* }}} */
1682:
1683:
1684: /* {{{ mysqlnd_stmt::server_status */
1685: static unsigned int
1686: MYSQLND_METHOD(mysqlnd_stmt, server_status)(const MYSQLND_STMT * const s TSRMLS_DC)
1687: {
1688: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1.1.1.2 misho 1689: return stmt? stmt->upsert_status->server_status : 0;
1.1 misho 1690: }
1691: /* }}} */
1692:
1693:
1694: /* {{{ mysqlnd_stmt::field_count */
1695: static unsigned int
1696: MYSQLND_METHOD(mysqlnd_stmt, field_count)(const MYSQLND_STMT * const s TSRMLS_DC)
1697: {
1698: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1699: return stmt? stmt->field_count : 0;
1700: }
1701: /* }}} */
1702:
1703:
1704: /* {{{ mysqlnd_stmt::param_count */
1705: static unsigned int
1706: MYSQLND_METHOD(mysqlnd_stmt, param_count)(const MYSQLND_STMT * const s TSRMLS_DC)
1707: {
1708: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1709: return stmt? stmt->param_count : 0;
1710: }
1711: /* }}} */
1712:
1713:
1714: /* {{{ mysqlnd_stmt::errno */
1715: static unsigned int
1716: MYSQLND_METHOD(mysqlnd_stmt, errno)(const MYSQLND_STMT * const s TSRMLS_DC)
1717: {
1718: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1.1.1.2 misho 1719: return stmt? stmt->error_info->error_no : 0;
1.1 misho 1720: }
1721: /* }}} */
1722:
1723:
1724: /* {{{ mysqlnd_stmt::error */
1725: static const char *
1726: MYSQLND_METHOD(mysqlnd_stmt, error)(const MYSQLND_STMT * const s TSRMLS_DC)
1727: {
1728: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1.1.1.2 misho 1729: return stmt? stmt->error_info->error : 0;
1.1 misho 1730: }
1731: /* }}} */
1732:
1733:
1734: /* {{{ mysqlnd_stmt::sqlstate */
1735: static const char *
1736: MYSQLND_METHOD(mysqlnd_stmt, sqlstate)(const MYSQLND_STMT * const s TSRMLS_DC)
1737: {
1738: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1.1.1.2 misho 1739: return stmt && stmt->error_info->sqlstate[0] ? stmt->error_info->sqlstate:MYSQLND_SQLSTATE_NULL;
1.1 misho 1740: }
1741: /* }}} */
1742:
1743:
1744: /* {{{ mysqlnd_stmt::data_seek */
1745: static enum_func_status
1746: MYSQLND_METHOD(mysqlnd_stmt, data_seek)(const MYSQLND_STMT * const s, uint64_t row TSRMLS_DC)
1747: {
1748: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1749: return stmt && stmt->result? stmt->result->m.seek_data(stmt->result, row TSRMLS_CC) : FAIL;
1750: }
1751: /* }}} */
1752:
1753:
1754: /* {{{ mysqlnd_stmt::param_metadata */
1755: static MYSQLND_RES *
1756: MYSQLND_METHOD(mysqlnd_stmt, param_metadata)(MYSQLND_STMT * const s TSRMLS_DC)
1757: {
1758: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1759: if (!stmt || !stmt->param_count) {
1760: return NULL;
1761: }
1762: return NULL;
1763: }
1764: /* }}} */
1765:
1766:
1767: /* {{{ mysqlnd_stmt::result_metadata */
1768: static MYSQLND_RES *
1769: MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const s TSRMLS_DC)
1770: {
1771: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1772: MYSQLND_RES *result;
1773:
1774: DBG_ENTER("mysqlnd_stmt::result_metadata");
1775: if (!stmt) {
1776: DBG_RETURN(NULL);
1777: }
1778: DBG_INF_FMT("stmt=%u field_count=%u", stmt->stmt_id, stmt->field_count);
1779:
1780: if (!stmt->field_count || !stmt->conn || !stmt->result || !stmt->result->meta) {
1781: DBG_INF("NULL");
1782: DBG_RETURN(NULL);
1783: }
1784:
1785: if (stmt->update_max_length && stmt->result->stored_data) {
1786: /* stored result, we have to update the max_length before we clone the meta data :( */
1787: stmt->result->m.initialize_result_set_rest(stmt->result TSRMLS_CC);
1788: }
1789: /*
1790: TODO: This implementation is kind of a hack,
1791: find a better way to do it. In different functions I have put
1792: fuses to check for result->m.fetch_row() being NULL. This should
1793: be handled in a better way.
1794:
1795: In the meantime we don't need a zval cache reference for this fake
1796: result set, so we don't get one.
1797: */
1798: do {
1799: result = stmt->conn->m->result_init(stmt->field_count, stmt->persistent TSRMLS_CC);
1800: if (!result) {
1801: break;
1802: }
1803: result->type = MYSQLND_RES_NORMAL;
1804: result->m.fetch_row = result->m.fetch_row_normal_unbuffered;
1805: result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));
1806: if (!result->unbuf) {
1807: break;
1808: }
1809: result->unbuf->eof_reached = TRUE;
1810: result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC);
1811: if (!result->meta) {
1812: break;
1813: }
1814:
1815: DBG_INF_FMT("result=%p", result);
1816: DBG_RETURN(result);
1817: } while (0);
1818:
1.1.1.2 misho 1819: SET_OOM_ERROR(*stmt->conn->error_info);
1.1 misho 1820: if (result) {
1821: result->m.free_result(result, TRUE TSRMLS_CC);
1822: }
1823: DBG_RETURN(NULL);
1824: }
1825: /* }}} */
1826:
1827:
1828: /* {{{ mysqlnd_stmt::attr_set */
1829: static enum_func_status
1830: MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const s,
1831: enum mysqlnd_stmt_attr attr_type,
1832: const void * const value TSRMLS_DC)
1833: {
1834: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1835: DBG_ENTER("mysqlnd_stmt::attr_set");
1836: if (!stmt) {
1837: DBG_RETURN(FAIL);
1838: }
1839: DBG_INF_FMT("stmt=%lu attr_type=%u", stmt->stmt_id, attr_type);
1840:
1841: switch (attr_type) {
1842: case STMT_ATTR_UPDATE_MAX_LENGTH:{
1843: zend_uchar bval = *(zend_uchar *) value;
1844: /*
1845: XXX : libmysql uses my_bool, but mysqli uses ulong as storage on the stack
1846: and mysqlnd won't be used out of the scope of PHP -> use ulong.
1847: */
1848: stmt->update_max_length = bval? TRUE:FALSE;
1849: break;
1850: }
1851: case STMT_ATTR_CURSOR_TYPE: {
1852: unsigned int ival = *(unsigned int *) value;
1853: if (ival > (unsigned long) CURSOR_TYPE_READ_ONLY) {
1854: SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
1855: DBG_INF("FAIL");
1856: DBG_RETURN(FAIL);
1857: }
1858: stmt->flags = ival;
1859: break;
1860: }
1861: case STMT_ATTR_PREFETCH_ROWS: {
1862: unsigned int ival = *(unsigned int *) value;
1863: if (ival == 0) {
1864: ival = MYSQLND_DEFAULT_PREFETCH_ROWS;
1865: } else if (ival > 1) {
1866: SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
1867: DBG_INF("FAIL");
1868: DBG_RETURN(FAIL);
1869: }
1870: stmt->prefetch_rows = ival;
1871: break;
1872: }
1873: default:
1874: SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
1875: DBG_RETURN(FAIL);
1876: }
1877: DBG_INF("PASS");
1878: DBG_RETURN(PASS);
1879: }
1880: /* }}} */
1881:
1882:
1883: /* {{{ mysqlnd_stmt::attr_get */
1884: static enum_func_status
1885: MYSQLND_METHOD(mysqlnd_stmt, attr_get)(const MYSQLND_STMT * const s,
1886: enum mysqlnd_stmt_attr attr_type,
1887: void * const value TSRMLS_DC)
1888: {
1889: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1890: DBG_ENTER("mysqlnd_stmt::attr_set");
1891: if (!stmt) {
1892: DBG_RETURN(FAIL);
1893: }
1894: DBG_INF_FMT("stmt=%lu attr_type=%u", stmt->stmt_id, attr_type);
1895:
1896: switch (attr_type) {
1897: case STMT_ATTR_UPDATE_MAX_LENGTH:
1898: *(zend_bool *) value= stmt->update_max_length;
1899: break;
1900: case STMT_ATTR_CURSOR_TYPE:
1901: *(unsigned long *) value= stmt->flags;
1902: break;
1903: case STMT_ATTR_PREFETCH_ROWS:
1904: *(unsigned long *) value= stmt->prefetch_rows;
1905: break;
1906: default:
1907: DBG_RETURN(FAIL);
1908: }
1909: DBG_INF_FMT("value=%lu", value);
1910: DBG_RETURN(PASS);
1911: }
1912: /* }}} */
1913:
1.1.1.2 misho 1914:
1.1 misho 1915: /* free_result() doesn't actually free stmt->result but only the buffers */
1916: /* {{{ mysqlnd_stmt::free_result */
1917: static enum_func_status
1918: MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const s TSRMLS_DC)
1919: {
1920: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1921: DBG_ENTER("mysqlnd_stmt::free_result");
1922: if (!stmt || !stmt->conn) {
1923: DBG_RETURN(FAIL);
1924: }
1925: DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
1926:
1927: if (!stmt->result) {
1928: DBG_INF("no result");
1929: DBG_RETURN(PASS);
1930: }
1931:
1932: /*
1933: If right after execute() we have to call the appropriate
1934: use_result() or store_result() and clean.
1935: */
1936: if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
1937: DBG_INF("fetching result set header");
1938: /* Do implicit use_result and then flush the result */
1939: stmt->default_rset_handler = s->m->use_result;
1940: stmt->default_rset_handler(s TSRMLS_CC);
1941: }
1942:
1943: if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) {
1944: DBG_INF("skipping result");
1945: /* Flush if anything is left and unbuffered set */
1946: stmt->result->m.skip_result(stmt->result TSRMLS_CC);
1947: /*
1948: Separate the bound variables, which point to the result set, then
1949: destroy the set.
1950: */
1951: mysqlnd_stmt_separate_result_bind(s TSRMLS_CC);
1952:
1953: /* Now we can destroy the result set */
1954: stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC);
1955: }
1956:
1957: if (stmt->state > MYSQLND_STMT_PREPARED) {
1958: /* As the buffers have been freed, we should go back to PREPARED */
1959: stmt->state = MYSQLND_STMT_PREPARED;
1960: }
1961:
1962: /* Line is free! */
1963: CONN_SET_STATE(stmt->conn, CONN_READY);
1964:
1965: DBG_RETURN(PASS);
1966: }
1967: /* }}} */
1968:
1969:
1970: /* {{{ mysqlnd_stmt_separate_result_bind */
1971: static void
1972: mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const s TSRMLS_DC)
1973: {
1974: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1975: unsigned int i;
1976:
1977: DBG_ENTER("mysqlnd_stmt_separate_result_bind");
1978: if (!stmt) {
1979: DBG_VOID_RETURN;
1980: }
1.1.1.2 misho 1981: DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u", stmt->stmt_id, stmt->result_bind, stmt->field_count);
1.1 misho 1982:
1983: if (!stmt->result_bind) {
1984: DBG_VOID_RETURN;
1985: }
1986:
1987: /*
1988: Because only the bound variables can point to our internal buffers, then
1989: separate or free only them. Free is possible because the user could have
1990: lost reference.
1991: */
1992: for (i = 0; i < stmt->field_count; i++) {
1993: /* Let's try with no cache */
1994: if (stmt->result_bind[i].bound == TRUE) {
1995: DBG_INF_FMT("%u has refcount=%u", i, Z_REFCOUNT_P(stmt->result_bind[i].zv));
1996: /*
1997: We have to separate the actual zval value of the bound
1998: variable from our allocated zvals or we will face double-free
1999: */
2000: if (Z_REFCOUNT_P(stmt->result_bind[i].zv) > 1) {
2001: #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
2002: zval_copy_ctor(stmt->result_bind[i].zv);
2003: #endif
2004: zval_ptr_dtor(&stmt->result_bind[i].zv);
2005: } else {
2006: /*
2007: If it is a string, what is pointed will be freed
2008: later in free_result(). We need to remove the variable to
2009: which the user has lost reference.
2010: */
2011: #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
2012: ZVAL_NULL(stmt->result_bind[i].zv);
2013: #endif
2014: zval_ptr_dtor(&stmt->result_bind[i].zv);
2015: }
2016: }
2017: }
2018: s->m->free_result_bind(s, stmt->result_bind TSRMLS_CC);
2019: stmt->result_bind = NULL;
2020:
2021: DBG_VOID_RETURN;
2022: }
2023: /* }}} */
2024:
2025:
2026: /* {{{ mysqlnd_stmt_separate_one_result_bind */
2027: static void
2028: mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const s, unsigned int param_no TSRMLS_DC)
2029: {
2030: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
2031: DBG_ENTER("mysqlnd_stmt_separate_one_result_bind");
2032: if (!stmt) {
2033: DBG_VOID_RETURN;
2034: }
1.1.1.2 misho 2035: 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 2036:
2037: if (!stmt->result_bind) {
2038: DBG_VOID_RETURN;
2039: }
2040:
2041: /*
2042: Because only the bound variables can point to our internal buffers, then
2043: separate or free only them. Free is possible because the user could have
2044: lost reference.
2045: */
2046: /* Let's try with no cache */
2047: if (stmt->result_bind[param_no].bound == TRUE) {
2048: DBG_INF_FMT("%u has refcount=%u", param_no, Z_REFCOUNT_P(stmt->result_bind[param_no].zv));
2049: /*
2050: We have to separate the actual zval value of the bound
2051: variable from our allocated zvals or we will face double-free
2052: */
2053: if (Z_REFCOUNT_P(stmt->result_bind[param_no].zv) > 1) {
2054: #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
2055: zval_copy_ctor(stmt->result_bind[param_no].zv);
2056: #endif
2057: zval_ptr_dtor(&stmt->result_bind[param_no].zv);
2058: } else {
2059: /*
2060: If it is a string, what is pointed will be freed
2061: later in free_result(). We need to remove the variable to
2062: which the user has lost reference.
2063: */
2064: #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
2065: ZVAL_NULL(stmt->result_bind[param_no].zv);
2066: #endif
2067: zval_ptr_dtor(&stmt->result_bind[param_no].zv);
2068: }
2069: }
2070:
2071: DBG_VOID_RETURN;
2072: }
2073: /* }}} */
2074:
2075:
2076: /* {{{ mysqlnd_stmt::free_stmt_content */
2077: static void
2078: MYSQLND_METHOD(mysqlnd_stmt, free_stmt_content)(MYSQLND_STMT * const s TSRMLS_DC)
2079: {
2080: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
2081: DBG_ENTER("mysqlnd_stmt::free_stmt_content");
2082: if (!stmt) {
2083: DBG_VOID_RETURN;
2084: }
1.1.1.2 misho 2085: DBG_INF_FMT("stmt=%lu param_bind=%p param_count=%u", stmt->stmt_id, stmt->param_bind, stmt->param_count);
1.1 misho 2086:
2087: /* Destroy the input bind */
2088: if (stmt->param_bind) {
2089: unsigned int i;
2090: /*
2091: Because only the bound variables can point to our internal buffers, then
2092: separate or free only them. Free is possible because the user could have
2093: lost reference.
2094: */
2095: for (i = 0; i < stmt->param_count; i++) {
2096: /*
2097: If bind_one_parameter was used, but not everything was
2098: bound and nothing was fetched, then some `zv` could be NULL
2099: */
2100: if (stmt->param_bind[i].zv) {
2101: zval_ptr_dtor(&stmt->param_bind[i].zv);
2102: }
2103: }
2104: s->m->free_parameter_bind(s, stmt->param_bind TSRMLS_CC);
2105: stmt->param_bind = NULL;
2106: }
2107:
2108: /*
2109: First separate the bound variables, which point to the result set, then
2110: destroy the set.
2111: */
2112: mysqlnd_stmt_separate_result_bind(s TSRMLS_CC);
2113: /* Not every statement has a result set attached */
2114: if (stmt->result) {
2115: stmt->result->m.free_result_internal(stmt->result TSRMLS_CC);
2116: stmt->result = NULL;
2117: }
1.1.1.2 misho 2118: if (stmt->error_info->error_list) {
2119: zend_llist_clean(stmt->error_info->error_list);
2120: mnd_pefree(stmt->error_info->error_list, s->persistent);
2121: stmt->error_info->error_list = NULL;
2122: }
1.1 misho 2123:
2124: DBG_VOID_RETURN;
2125: }
2126: /* }}} */
2127:
2128:
2129: /* {{{ mysqlnd_stmt::net_close */
2130: static enum_func_status
2131: MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const s, zend_bool implicit TSRMLS_DC)
2132: {
2133: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
1.1.1.2 misho 2134: MYSQLND_CONN_DATA * conn;
1.1 misho 2135: zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */];
2136: enum_mysqlnd_collected_stats statistic = STAT_LAST;
2137:
2138: DBG_ENTER("mysqlnd_stmt::net_close");
2139: if (!stmt || !stmt->conn) {
2140: DBG_RETURN(FAIL);
2141: }
2142: DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
2143:
2144: conn = stmt->conn;
2145:
1.1.1.2 misho 2146: SET_EMPTY_ERROR(*stmt->error_info);
2147: SET_EMPTY_ERROR(*stmt->conn->error_info);
1.1 misho 2148:
2149: /*
2150: If the user decided to close the statement right after execute()
2151: We have to call the appropriate use_result() or store_result() and
2152: clean.
2153: */
2154: do {
2155: if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
2156: DBG_INF("fetching result set header");
2157: stmt->default_rset_handler(s TSRMLS_CC);
2158: stmt->state = MYSQLND_STMT_USER_FETCHING;
2159: }
2160:
2161: /* unbuffered set not fetched to the end ? Clean the line */
2162: if (stmt->result) {
2163: DBG_INF("skipping result");
2164: stmt->result->m.skip_result(stmt->result TSRMLS_CC);
2165: }
2166: } while (mysqlnd_stmt_more_results(s) && mysqlnd_stmt_next_result(s) == PASS);
2167: /*
2168: After this point we are allowed to free the result set,
2169: as we have cleaned the line
2170: */
2171: if (stmt->stmt_id) {
2172: MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE? STAT_FREE_RESULT_IMPLICIT:
2173: STAT_FREE_RESULT_EXPLICIT);
2174:
2175: int4store(cmd_buf, stmt->stmt_id);
2176: if (CONN_GET_STATE(conn) == CONN_READY &&
1.1.1.2 misho 2177: FAIL == conn->m->simple_command(conn, COM_STMT_CLOSE, cmd_buf, sizeof(cmd_buf),
1.1 misho 2178: PROT_LAST /* COM_STMT_CLOSE doesn't send an OK packet*/,
2179: FALSE, TRUE TSRMLS_CC)) {
1.1.1.2 misho 2180: COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
1.1 misho 2181: DBG_RETURN(FAIL);
2182: }
2183: }
2184: switch (stmt->execute_count) {
2185: case 0:
2186: statistic = STAT_PS_PREPARED_NEVER_EXECUTED;
2187: break;
2188: case 1:
2189: statistic = STAT_PS_PREPARED_ONCE_USED;
2190: break;
2191: default:
2192: break;
2193: }
2194: if (statistic != STAT_LAST) {
2195: MYSQLND_INC_CONN_STATISTIC(conn->stats, statistic);
2196: }
2197:
2198: if (stmt->execute_cmd_buffer.buffer) {
2199: mnd_pefree(stmt->execute_cmd_buffer.buffer, stmt->persistent);
2200: stmt->execute_cmd_buffer.buffer = NULL;
2201: }
2202:
2203: s->m->free_stmt_content(s TSRMLS_CC);
2204:
2205: if (stmt->conn) {
2206: stmt->conn->m->free_reference(stmt->conn TSRMLS_CC);
2207: stmt->conn = NULL;
2208: }
2209:
2210: DBG_RETURN(PASS);
2211: }
2212: /* }}} */
2213:
2214: /* {{{ mysqlnd_stmt::dtor */
2215: static enum_func_status
2216: MYSQLND_METHOD(mysqlnd_stmt, dtor)(MYSQLND_STMT * const s, zend_bool implicit TSRMLS_DC)
2217: {
2218: MYSQLND_STMT_DATA * stmt = (s != NULL) ? s->data:NULL;
2219: enum_func_status ret = FAIL;
2220: zend_bool persistent = (s != NULL) ? s->persistent : 0;
2221:
2222: DBG_ENTER("mysqlnd_stmt::dtor");
2223: if (stmt) {
2224: DBG_INF_FMT("stmt=%p", stmt);
2225:
2226: MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE? STAT_STMT_CLOSE_IMPLICIT:
2227: STAT_STMT_CLOSE_EXPLICIT);
2228:
2229: ret = s->m->net_close(s, implicit TSRMLS_CC);
2230: mnd_pefree(stmt, persistent);
2231: }
2232: mnd_pefree(s, persistent);
2233:
2234: DBG_INF(ret == PASS? "PASS":"FAIL");
2235: DBG_RETURN(ret);
2236: }
2237: /* }}} */
2238:
2239:
2240: /* {{{ mysqlnd_stmt::alloc_param_bind */
2241: static MYSQLND_PARAM_BIND *
2242: MYSQLND_METHOD(mysqlnd_stmt, alloc_param_bind)(MYSQLND_STMT * const s TSRMLS_DC)
2243: {
2244: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
2245: DBG_ENTER("mysqlnd_stmt::alloc_param_bind");
2246: if (!stmt) {
2247: DBG_RETURN(NULL);
2248: }
2249: DBG_RETURN(mnd_pecalloc(stmt->param_count, sizeof(MYSQLND_PARAM_BIND), stmt->persistent));
2250: }
2251: /* }}} */
2252:
2253:
2254: /* {{{ mysqlnd_stmt::alloc_result_bind */
2255: static MYSQLND_RESULT_BIND *
2256: MYSQLND_METHOD(mysqlnd_stmt, alloc_result_bind)(MYSQLND_STMT * const s TSRMLS_DC)
2257: {
2258: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
2259: DBG_ENTER("mysqlnd_stmt::alloc_result_bind");
2260: if (!stmt) {
2261: DBG_RETURN(NULL);
2262: }
2263: DBG_RETURN(mnd_pecalloc(stmt->field_count, sizeof(MYSQLND_RESULT_BIND), stmt->persistent));
2264: }
2265: /* }}} */
2266:
2267:
2268: /* {{{ param_bind::free_parameter_bind */
2269: PHPAPI void
2270: MYSQLND_METHOD(mysqlnd_stmt, free_parameter_bind)(MYSQLND_STMT * const s, MYSQLND_PARAM_BIND * param_bind TSRMLS_DC)
2271: {
2272: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
2273: if (stmt) {
2274: mnd_pefree(param_bind, stmt->persistent);
2275: }
2276: }
2277: /* }}} */
2278:
2279:
2280: /* {{{ mysqlnd_stmt::free_result_bind */
2281: PHPAPI void
2282: MYSQLND_METHOD(mysqlnd_stmt, free_result_bind)(MYSQLND_STMT * const s, MYSQLND_RESULT_BIND * result_bind TSRMLS_DC)
2283: {
2284: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
2285: if (stmt) {
2286: mnd_pefree(result_bind, stmt->persistent);
2287: }
2288: }
2289: /* }}} */
2290:
2291:
2292:
2293: MYSQLND_CLASS_METHODS_START(mysqlnd_stmt)
2294: MYSQLND_METHOD(mysqlnd_stmt, prepare),
2295: MYSQLND_METHOD(mysqlnd_stmt, execute),
2296: MYSQLND_METHOD(mysqlnd_stmt, use_result),
2297: MYSQLND_METHOD(mysqlnd_stmt, store_result),
2298: MYSQLND_METHOD(mysqlnd_stmt, get_result),
2299: MYSQLND_METHOD(mysqlnd_stmt, more_results),
2300: MYSQLND_METHOD(mysqlnd_stmt, next_result),
2301: MYSQLND_METHOD(mysqlnd_stmt, free_result),
2302: MYSQLND_METHOD(mysqlnd_stmt, data_seek),
2303: MYSQLND_METHOD(mysqlnd_stmt, reset),
2304: MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close),
2305: MYSQLND_METHOD(mysqlnd_stmt, dtor),
2306:
2307: MYSQLND_METHOD(mysqlnd_stmt, fetch),
2308:
2309: MYSQLND_METHOD(mysqlnd_stmt, bind_parameters),
2310: MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter),
2311: MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param),
2312: MYSQLND_METHOD(mysqlnd_stmt, bind_result),
2313: MYSQLND_METHOD(mysqlnd_stmt, bind_one_result),
2314: MYSQLND_METHOD(mysqlnd_stmt, send_long_data),
2315: MYSQLND_METHOD(mysqlnd_stmt, param_metadata),
2316: MYSQLND_METHOD(mysqlnd_stmt, result_metadata),
2317:
2318: MYSQLND_METHOD(mysqlnd_stmt, insert_id),
2319: MYSQLND_METHOD(mysqlnd_stmt, affected_rows),
2320: MYSQLND_METHOD(mysqlnd_stmt, num_rows),
2321:
2322: MYSQLND_METHOD(mysqlnd_stmt, param_count),
2323: MYSQLND_METHOD(mysqlnd_stmt, field_count),
2324: MYSQLND_METHOD(mysqlnd_stmt, warning_count),
2325:
2326: MYSQLND_METHOD(mysqlnd_stmt, errno),
2327: MYSQLND_METHOD(mysqlnd_stmt, error),
2328: MYSQLND_METHOD(mysqlnd_stmt, sqlstate),
2329:
2330: MYSQLND_METHOD(mysqlnd_stmt, attr_get),
2331: MYSQLND_METHOD(mysqlnd_stmt, attr_set),
2332:
2333:
2334: MYSQLND_METHOD(mysqlnd_stmt, alloc_param_bind),
2335: MYSQLND_METHOD(mysqlnd_stmt, alloc_result_bind),
2336: MYSQLND_METHOD(mysqlnd_stmt, free_parameter_bind),
2337: MYSQLND_METHOD(mysqlnd_stmt, free_result_bind),
2338: MYSQLND_METHOD(mysqlnd_stmt, server_status),
2339: mysqlnd_stmt_execute_generate_request,
2340: mysqlnd_stmt_execute_parse_response,
1.1.1.2 misho 2341: MYSQLND_METHOD(mysqlnd_stmt, free_stmt_content),
2342: MYSQLND_METHOD(mysqlnd_stmt, flush)
1.1 misho 2343: MYSQLND_CLASS_METHODS_END;
2344:
2345:
2346: /* {{{ _mysqlnd_stmt_init */
1.1.1.2 misho 2347: MYSQLND_STMT *
2348: _mysqlnd_stmt_init(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
1.1 misho 2349: {
1.1.1.2 misho 2350: MYSQLND_STMT * ret;
1.1 misho 2351: DBG_ENTER("_mysqlnd_stmt_init");
1.1.1.2 misho 2352: ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_prepared_statement(conn TSRMLS_CC);
2353: DBG_RETURN(ret);
1.1 misho 2354: }
2355: /* }}} */
2356:
2357:
2358: /* {{{ _mysqlnd_init_ps_subsystem */
2359: void _mysqlnd_init_ps_subsystem()
2360: {
1.1.1.2 misho 2361: mysqlnd_stmt_set_methods(&MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_stmt));
1.1 misho 2362: _mysqlnd_init_ps_fetch_subsystem();
2363: }
2364: /* }}} */
2365:
2366:
2367: /*
2368: * Local variables:
2369: * tab-width: 4
2370: * c-basic-offset: 4
2371: * End:
2372: * vim600: noet sw=4 ts=4 fdm=marker
2373: * vim<600: noet sw=4 ts=4
2374: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>