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