Annotation of embedaddon/php/ext/mysqlnd/mysqlnd_result.c, revision 1.1.1.2
1.1 misho 1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
5: | Copyright (c) 2006-2012 The PHP Group |
6: +----------------------------------------------------------------------+
7: | This source file is subject to version 3.01 of the PHP license, |
8: | that is bundled with this package in the file LICENSE, and is |
9: | available through the world-wide-web at the following url: |
10: | http://www.php.net/license/3_01.txt |
11: | If you did not receive a copy of the PHP license and are unable to |
12: | obtain it through the world-wide-web, please send a note to |
13: | license@php.net so we can mail you a copy immediately. |
14: +----------------------------------------------------------------------+
1.1.1.2 ! misho 15: | Authors: Andrey Hristov <andrey@mysql.com> |
1.1 misho 16: | Ulf Wendel <uwendel@mysql.com> |
1.1.1.2 ! misho 17: | Georg Richter <georg@mysql.com> |
1.1 misho 18: +----------------------------------------------------------------------+
19: */
20:
1.1.1.2 ! misho 21: /* $Id$ */
1.1 misho 22: #include "php.h"
23: #include "mysqlnd.h"
24: #include "mysqlnd_wireprotocol.h"
25: #include "mysqlnd_block_alloc.h"
26: #include "mysqlnd_priv.h"
27: #include "mysqlnd_result.h"
28: #include "mysqlnd_result_meta.h"
29: #include "mysqlnd_statistics.h"
30: #include "mysqlnd_debug.h"
1.1.1.2 ! misho 31: #include "mysqlnd_ext_plugin.h"
1.1 misho 32:
33: #define MYSQLND_SILENT
34:
35:
36: /* {{{ mysqlnd_res::initialize_result_set_rest */
37: static enum_func_status
38: MYSQLND_METHOD(mysqlnd_res, initialize_result_set_rest)(MYSQLND_RES * const result TSRMLS_DC)
39: {
40: unsigned int i;
41: zval **data_cursor = result->stored_data? result->stored_data->data:NULL;
42: zval **data_begin = result->stored_data? result->stored_data->data:NULL;
43: unsigned int field_count = result->meta? result->meta->field_count : 0;
44: uint64_t row_count = result->stored_data? result->stored_data->row_count:0;
45: enum_func_status ret = PASS;
46: DBG_ENTER("mysqlnd_res::initialize_result_set_rest");
47:
48: if (!data_cursor || row_count == result->stored_data->initialized_rows) {
49: DBG_RETURN(ret);
50: }
51: while ((data_cursor - data_begin) < (int)(row_count * field_count)) {
52: if (NULL == data_cursor[0]) {
53: enum_func_status rc = result->m.row_decoder(
54: result->stored_data->row_buffers[(data_cursor - data_begin) / field_count],
55: data_cursor,
56: result->meta->field_count,
57: result->meta->fields,
1.1.1.2 ! misho 58: result->conn->options->numeric_and_datetime_as_unicode,
! 59: result->conn->options->int_and_float_native,
1.1 misho 60: result->conn->stats TSRMLS_CC);
61: if (rc != PASS) {
62: ret = FAIL;
63: break;
64: }
65: result->stored_data->initialized_rows++;
66: for (i = 0; i < result->field_count; i++) {
67: /*
68: NULL fields are 0 length, 0 is not more than 0
69: String of zero size, definitely can't be the next max_length.
70: Thus for NULL and zero-length we are quite efficient.
71: */
72: if (Z_TYPE_P(data_cursor[i]) >= IS_STRING) {
73: unsigned long len = Z_STRLEN_P(data_cursor[i]);
74: if (result->meta->fields[i].max_length < len) {
75: result->meta->fields[i].max_length = len;
76: }
77: }
78: }
79: }
80: data_cursor += field_count;
81: }
82: DBG_RETURN(ret);
83: }
84: /* }}} */
85:
86:
87: /* {{{ mysqlnd_rset_zval_ptr_dtor */
88: static void
89: mysqlnd_rset_zval_ptr_dtor(zval **zv, enum_mysqlnd_res_type type, zend_bool * copy_ctor_called TSRMLS_DC)
90: {
91: DBG_ENTER("mysqlnd_rset_zval_ptr_dtor");
92: if (!zv || !*zv) {
93: *copy_ctor_called = FALSE;
94: DBG_ERR_FMT("zv was NULL");
95: DBG_VOID_RETURN;
96: }
97: /*
98: This zval is not from the cache block.
99: Thus the refcount is -1 than of a zval from the cache,
100: because the zvals from the cache are owned by it.
101: */
102: if (type == MYSQLND_RES_PS_BUF || type == MYSQLND_RES_PS_UNBUF) {
103: *copy_ctor_called = FALSE;
104: ; /* do nothing, zval_ptr_dtor will do the job*/
105: } else if (Z_REFCOUNT_PP(zv) > 1) {
106: /*
107: Not a prepared statement, then we have to
108: call copy_ctor and then zval_ptr_dtor()
109:
110: In Unicode mode the destruction of the zvals should not call
111: zval_copy_ctor() because then we will leak.
112: I suppose we can use UG(unicode) in mysqlnd.c when freeing a result set
113: to check if we need to call copy_ctor().
114:
115: If the type is IS_UNICODE, which can happen with PHP6, then we don't
116: need to copy_ctor, as the data doesn't point to our internal buffers.
117: If it's string (in PHP5 always) and in PHP6 if data is binary, then
118: it still points to internal buffers and has to be copied.
119: */
120: if (Z_TYPE_PP(zv) == IS_STRING) {
121: zval_copy_ctor(*zv);
122: }
123: *copy_ctor_called = TRUE;
124: } else {
125: /*
126: noone but us point to this, so we can safely ZVAL_NULL the zval,
127: so Zend does not try to free what the zval points to - which is
128: in result set buffers
129: */
130: *copy_ctor_called = FALSE;
131: if (Z_TYPE_PP(zv) == IS_STRING) {
132: ZVAL_NULL(*zv);
133: }
134: }
135: zval_ptr_dtor(zv);
136: DBG_VOID_RETURN;
137: }
138: /* }}} */
139:
140:
141: /* {{{ mysqlnd_res::unbuffered_free_last_data */
142: static void
143: MYSQLND_METHOD(mysqlnd_res, unbuffered_free_last_data)(MYSQLND_RES * result TSRMLS_DC)
144: {
145: MYSQLND_RES_UNBUFFERED *unbuf = result->unbuf;
146:
147: DBG_ENTER("mysqlnd_res::unbuffered_free_last_data");
148:
149: if (!unbuf) {
150: DBG_VOID_RETURN;
151: }
152:
153: if (unbuf->last_row_data) {
154: unsigned int i, ctor_called_count = 0;
155: zend_bool copy_ctor_called;
156: MYSQLND_STATS *global_stats = result->conn? result->conn->stats:NULL;
157:
158: for (i = 0; i < result->field_count; i++) {
159: mysqlnd_rset_zval_ptr_dtor(&(unbuf->last_row_data[i]), result->type, ©_ctor_called TSRMLS_CC);
160: if (copy_ctor_called) {
161: ++ctor_called_count;
162: }
163: }
164: DBG_INF_FMT("copy_ctor_called_count=%u", ctor_called_count);
165: /* By using value3 macros we hold a mutex only once, there is no value2 */
166: MYSQLND_INC_CONN_STATISTIC_W_VALUE2(global_stats,
167: STAT_COPY_ON_WRITE_PERFORMED,
168: ctor_called_count,
169: STAT_COPY_ON_WRITE_SAVED,
170: result->field_count - ctor_called_count);
171: /* Free last row's zvals */
172: mnd_efree(unbuf->last_row_data);
173: unbuf->last_row_data = NULL;
174: }
175: if (unbuf->last_row_buffer) {
176: DBG_INF("Freeing last row buffer");
177: /* Nothing points to this buffer now, free it */
178: unbuf->last_row_buffer->free_chunk(unbuf->last_row_buffer TSRMLS_CC);
179: unbuf->last_row_buffer = NULL;
180: }
181:
182: DBG_VOID_RETURN;
183: }
184: /* }}} */
185:
186:
187: /* {{{ mysqlnd_res::free_buffered_data */
188: static void
189: MYSQLND_METHOD(mysqlnd_res, free_buffered_data)(MYSQLND_RES * result TSRMLS_DC)
190: {
191: MYSQLND_RES_BUFFERED *set = result->stored_data;
192: unsigned int field_count = result->field_count;
193: int64_t row;
194:
195: DBG_ENTER("mysqlnd_res::free_buffered_data");
196: DBG_INF_FMT("Freeing "MYSQLND_LLU_SPEC" row(s)", set->row_count);
197:
198: if (set->data) {
199: unsigned int copy_on_write_performed = 0;
200: unsigned int copy_on_write_saved = 0;
201:
202: for (row = set->row_count - 1; row >= 0; row--) {
203: zval **current_row = set->data + row * field_count;
204: MYSQLND_MEMORY_POOL_CHUNK *current_buffer = set->row_buffers[row];
205: int64_t col;
206:
207: if (current_row != NULL) {
208: for (col = field_count - 1; col >= 0; --col) {
209: if (current_row[col]) {
210: zend_bool copy_ctor_called;
211: mysqlnd_rset_zval_ptr_dtor(&(current_row[col]), result->type, ©_ctor_called TSRMLS_CC);
212: if (copy_ctor_called) {
213: ++copy_on_write_performed;
214: } else {
215: ++copy_on_write_saved;
216: }
217: }
218: }
219: }
220: current_buffer->free_chunk(current_buffer TSRMLS_CC);
221: }
222:
223: MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_COPY_ON_WRITE_PERFORMED, copy_on_write_performed,
224: STAT_COPY_ON_WRITE_SAVED, copy_on_write_saved);
1.1.1.2 ! misho 225: mnd_efree(set->data);
1.1 misho 226: set->data = NULL;
227: }
228:
229: if (set->row_buffers) {
1.1.1.2 ! misho 230: mnd_efree(set->row_buffers);
1.1 misho 231: set->row_buffers = NULL;
232: }
233: set->data_cursor = NULL;
234: set->row_count = 0;
235:
1.1.1.2 ! misho 236: mnd_efree(set);
1.1 misho 237:
238: DBG_VOID_RETURN;
239: }
240: /* }}} */
241:
242:
243: /* {{{ mysqlnd_res::free_result_buffers */
244: static void
245: MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES * result TSRMLS_DC)
246: {
247: DBG_ENTER("mysqlnd_res::free_result_buffers");
248: DBG_INF_FMT("%s", result->unbuf? "unbuffered":(result->stored_data? "buffered":"unknown"));
249:
250: if (result->unbuf) {
251: result->m.unbuffered_free_last_data(result TSRMLS_CC);
252: mnd_efree(result->unbuf);
253: result->unbuf = NULL;
254: } else if (result->stored_data) {
255: result->m.free_buffered_data(result TSRMLS_CC);
256: result->stored_data = NULL;
257: }
258:
259: if (result->lengths) {
260: mnd_efree(result->lengths);
261: result->lengths = NULL;
262: }
263:
264: if (result->row_packet) {
265: PACKET_FREE(result->row_packet);
266: result->row_packet = NULL;
267: }
268:
269: if (result->result_set_memory_pool) {
270: mysqlnd_mempool_destroy(result->result_set_memory_pool TSRMLS_CC);
271: result->result_set_memory_pool = NULL;
272: }
273:
274: DBG_VOID_RETURN;
275: }
276: /* }}} */
277:
278:
279: /* {{{ mysqlnd_internal_free_result_contents */
280: static
281: void mysqlnd_internal_free_result_contents(MYSQLND_RES * result TSRMLS_DC)
282: {
283: DBG_ENTER("mysqlnd_internal_free_result_contents");
284:
285: result->m.free_result_buffers(result TSRMLS_CC);
286:
287: if (result->meta) {
288: result->meta->m->free_metadata(result->meta TSRMLS_CC);
289: result->meta = NULL;
290: }
291:
292: DBG_VOID_RETURN;
293: }
294: /* }}} */
295:
296:
297: /* {{{ mysqlnd_internal_free_result */
298: static
299: void mysqlnd_internal_free_result(MYSQLND_RES * result TSRMLS_DC)
300: {
301: DBG_ENTER("mysqlnd_internal_free_result");
302: result->m.free_result_contents(result TSRMLS_CC);
303:
304: if (result->conn) {
305: result->conn->m->free_reference(result->conn TSRMLS_CC);
306: result->conn = NULL;
307: }
308:
309: mnd_pefree(result, result->persistent);
310:
311: DBG_VOID_RETURN;
312: }
313: /* }}} */
314:
315:
316: /* {{{ mysqlnd_res::read_result_metadata */
317: static enum_func_status
1.1.1.2 ! misho 318: MYSQLND_METHOD(mysqlnd_res, read_result_metadata)(MYSQLND_RES * result, MYSQLND_CONN_DATA * conn TSRMLS_DC)
1.1 misho 319: {
320: DBG_ENTER("mysqlnd_res::read_result_metadata");
321:
322: /*
323: Make it safe to call it repeatedly for PS -
324: better free and allocate a new because the number of field might change
325: (select *) with altered table. Also for statements which skip the PS
326: infrastructure!
327: */
328: if (result->meta) {
329: result->meta->m->free_metadata(result->meta TSRMLS_CC);
330: result->meta = NULL;
331: }
332:
333: result->meta = result->m.result_meta_init(result->field_count, result->persistent TSRMLS_CC);
334: if (!result->meta) {
1.1.1.2 ! misho 335: SET_OOM_ERROR(*conn->error_info);
1.1 misho 336: DBG_RETURN(FAIL);
337: }
338:
339: /* 1. Read all fields metadata */
340:
341: /* It's safe to reread without freeing */
342: if (FAIL == result->meta->m->read_metadata(result->meta, conn TSRMLS_CC)) {
343: result->m.free_result_contents(result TSRMLS_CC);
344: DBG_RETURN(FAIL);
345: }
346: /* COM_FIELD_LIST is broken and has premature EOF, thus we need to hack here and in mysqlnd_res_meta.c */
347: result->field_count = result->meta->field_count;
348:
349: /*
350: 2. Follows an EOF packet, which the client of mysqlnd_read_result_metadata()
351: should consume.
352: 3. If there is a result set, it follows. The last packet will have 'eof' set
353: If PS, then no result set follows.
354: */
355:
356: DBG_RETURN(PASS);
357: }
358: /* }}} */
359:
360:
361: /* {{{ mysqlnd_query_read_result_set_header */
362: enum_func_status
1.1.1.2 ! misho 363: mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s TSRMLS_DC)
1.1 misho 364: {
365: MYSQLND_STMT_DATA * stmt = s ? s->data:NULL;
366: enum_func_status ret;
367: MYSQLND_PACKET_RSET_HEADER * rset_header = NULL;
368: MYSQLND_PACKET_EOF * fields_eof = NULL;
369:
370: DBG_ENTER("mysqlnd_query_read_result_set_header");
371: DBG_INF_FMT("stmt=%lu", stmt? stmt->stmt_id:0);
372:
373: ret = FAIL;
374: do {
375: rset_header = conn->protocol->m.get_rset_header_packet(conn->protocol, FALSE TSRMLS_CC);
376: if (!rset_header) {
1.1.1.2 ! misho 377: SET_OOM_ERROR(*conn->error_info);
1.1 misho 378: ret = FAIL;
379: break;
380: }
381:
382: SET_ERROR_AFF_ROWS(conn);
383:
384: if (FAIL == (ret = PACKET_READ(rset_header, conn))) {
385: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error reading result set's header");
386: break;
387: }
388:
389: if (rset_header->error_info.error_no) {
390: /*
391: Cover a protocol design error: error packet does not
392: contain the server status. Therefore, the client has no way
393: to find out whether there are more result sets of
394: a multiple-result-set statement pending. Luckily, in 5.0 an
395: error always aborts execution of a statement, wherever it is
396: a multi-statement or a stored procedure, so it should be
397: safe to unconditionally turn off the flag here.
398: */
1.1.1.2 ! misho 399: conn->upsert_status->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
1.1 misho 400: /*
401: This will copy the error code and the messages, as they
402: are buffers in the struct
403: */
1.1.1.2 ! misho 404: COPY_CLIENT_ERROR(*conn->error_info, rset_header->error_info);
1.1 misho 405: ret = FAIL;
406: DBG_ERR_FMT("error=%s", rset_header->error_info.error);
407: /* Return back from CONN_QUERY_SENT */
408: CONN_SET_STATE(conn, CONN_READY);
409: break;
410: }
1.1.1.2 ! misho 411: conn->error_info->error_no = 0;
1.1 misho 412:
413: switch (rset_header->field_count) {
414: case MYSQLND_NULL_LENGTH: { /* LOAD DATA LOCAL INFILE */
415: zend_bool is_warning;
416: DBG_INF("LOAD DATA");
417: conn->last_query_type = QUERY_LOAD_LOCAL;
418: conn->field_count = 0; /* overwrite previous value, or the last value could be used and lead to bug#53503 */
419: CONN_SET_STATE(conn, CONN_SENDING_LOAD_DATA);
420: ret = mysqlnd_handle_local_infile(conn, rset_header->info_or_local_file, &is_warning TSRMLS_CC);
421: CONN_SET_STATE(conn, (ret == PASS || is_warning == TRUE)? CONN_READY:CONN_QUIT_SENT);
422: MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_NON_RSET_QUERY);
423: break;
424: }
425: case 0: /* UPSERT */
426: DBG_INF("UPSERT");
427: conn->last_query_type = QUERY_UPSERT;
428: conn->field_count = rset_header->field_count;
1.1.1.2 ! misho 429: conn->upsert_status->warning_count = rset_header->warning_count;
! 430: conn->upsert_status->server_status = rset_header->server_status;
! 431: conn->upsert_status->affected_rows = rset_header->affected_rows;
! 432: conn->upsert_status->last_insert_id = rset_header->last_insert_id;
1.1 misho 433: SET_NEW_MESSAGE(conn->last_message, conn->last_message_len,
434: rset_header->info_or_local_file, rset_header->info_or_local_file_len,
435: conn->persistent);
436: /* Result set can follow UPSERT statement, check server_status */
1.1.1.2 ! misho 437: if (conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
1.1 misho 438: CONN_SET_STATE(conn, CONN_NEXT_RESULT_PENDING);
439: } else {
440: CONN_SET_STATE(conn, CONN_READY);
441: }
442: ret = PASS;
443: MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_NON_RSET_QUERY);
444: break;
445: default: do { /* Result set */
446: MYSQLND_RES * result;
447: enum_mysqlnd_collected_stats statistic = STAT_LAST;
448:
449: DBG_INF("Result set pending");
450: SET_EMPTY_MESSAGE(conn->last_message, conn->last_message_len, conn->persistent);
451:
452: MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_RSET_QUERY);
1.1.1.2 ! misho 453: memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
1.1 misho 454: /* restore after zeroing */
455: SET_ERROR_AFF_ROWS(conn);
456:
457: conn->last_query_type = QUERY_SELECT;
458: CONN_SET_STATE(conn, CONN_FETCHING_DATA);
459: /* PS has already allocated it */
460: conn->field_count = rset_header->field_count;
461: if (!stmt) {
462: result = conn->current_result = conn->m->result_init(rset_header->field_count, conn->persistent TSRMLS_CC);
463: } else {
464: if (!stmt->result) {
465: DBG_INF("This is 'SHOW'/'EXPLAIN'-like query.");
466: /*
467: This is 'SHOW'/'EXPLAIN'-like query. Current implementation of
468: prepared statements can't send result set metadata for these queries
469: on prepare stage. Read it now.
470: */
471: result = stmt->result = conn->m->result_init(rset_header->field_count, stmt->persistent TSRMLS_CC);
472: } else {
473: /*
474: Update result set metadata if it for some reason changed between
475: prepare and execute, i.e.:
476: - in case of 'SELECT ?' we don't know column type unless data was
477: supplied to mysql_stmt_execute, so updated column type is sent
478: now.
479: - if data dictionary changed between prepare and execute, for
480: example a table used in the query was altered.
481: Note, that now (4.1.3) we always send metadata in reply to
482: COM_STMT_EXECUTE (even if it is not necessary), so either this or
483: previous branch always works.
484: */
485: }
486: result = stmt->result;
487: }
488: if (!result) {
1.1.1.2 ! misho 489: SET_OOM_ERROR(*conn->error_info);
1.1 misho 490: ret = FAIL;
491: break;
492: }
493:
494: if (FAIL == (ret = result->m.read_result_metadata(result, conn TSRMLS_CC))) {
495: /* For PS, we leave them in Prepared state */
496: if (!stmt && conn->current_result) {
497: mnd_efree(conn->current_result);
498: conn->current_result = NULL;
499: }
500: DBG_ERR("Error ocurred while reading metadata");
501: break;
502: }
503:
504: /* Check for SERVER_STATUS_MORE_RESULTS if needed */
505: fields_eof = conn->protocol->m.get_eof_packet(conn->protocol, FALSE TSRMLS_CC);
506: if (!fields_eof) {
1.1.1.2 ! misho 507: SET_OOM_ERROR(*conn->error_info);
1.1 misho 508: ret = FAIL;
509: break;
510: }
511: if (FAIL == (ret = PACKET_READ(fields_eof, conn))) {
512: DBG_ERR("Error ocurred while reading the EOF packet");
513: result->m.free_result_contents(result TSRMLS_CC);
514: mnd_efree(result);
515: if (!stmt) {
516: conn->current_result = NULL;
517: } else {
518: stmt->result = NULL;
519: memset(stmt, 0, sizeof(MYSQLND_STMT));
520: stmt->state = MYSQLND_STMT_INITTED;
521: }
522: } else {
523: unsigned int to_log = MYSQLND_G(log_mask);
524: to_log &= fields_eof->server_status;
525: DBG_INF_FMT("warnings=%u server_status=%u", fields_eof->warning_count, fields_eof->server_status);
1.1.1.2 ! misho 526: conn->upsert_status->warning_count = fields_eof->warning_count;
1.1 misho 527: /*
528: If SERVER_MORE_RESULTS_EXISTS is set then this is either MULTI_QUERY or a CALL()
529: The first packet after sending the query/com_execute has the bit set only
530: in this cases. Not sure why it's a needed but it marks that the whole stream
531: will include many result sets. What actually matters are the bits set at the end
532: of every result set (the EOF packet).
533: */
1.1.1.2 ! misho 534: conn->upsert_status->server_status = fields_eof->server_status;
1.1 misho 535: if (fields_eof->server_status & SERVER_QUERY_NO_GOOD_INDEX_USED) {
536: statistic = STAT_BAD_INDEX_USED;
537: } else if (fields_eof->server_status & SERVER_QUERY_NO_INDEX_USED) {
538: statistic = STAT_NO_INDEX_USED;
539: } else if (fields_eof->server_status & SERVER_QUERY_WAS_SLOW) {
540: statistic = STAT_QUERY_WAS_SLOW;
541: }
542: if (to_log) {
543: #if A0
544: char *backtrace = mysqlnd_get_backtrace(TSRMLS_C);
545: php_log_err(backtrace TSRMLS_CC);
546: efree(backtrace);
547: #endif
548: }
549: MYSQLND_INC_CONN_STATISTIC(conn->stats, statistic);
550: }
551: } while (0);
552: PACKET_FREE(fields_eof);
553: break; /* switch break */
554: }
555: } while (0);
556: PACKET_FREE(rset_header);
557:
558: DBG_INF(ret == PASS? "PASS":"FAIL");
559: DBG_RETURN(ret);
560: }
561: /* }}} */
562:
563:
564: /* {{{ mysqlnd_fetch_lengths_buffered */
565: /*
566: Do lazy initialization for buffered results. As PHP strings have
567: length inside, this function makes not much sense in the context
568: of PHP, to be called as separate function. But let's have it for
569: completeness.
570: */
571: static unsigned long *
572: mysqlnd_fetch_lengths_buffered(MYSQLND_RES * const result TSRMLS_DC)
573: {
574: unsigned int i;
575: zval **previous_row;
576: MYSQLND_RES_BUFFERED *set = result->stored_data;
577:
578: /*
579: If:
580: - unbuffered result
581: - first row has not been read
582: - last_row has been read
583: */
584: if (set->data_cursor == NULL ||
585: set->data_cursor == set->data ||
586: ((set->data_cursor - set->data) > (set->row_count * result->meta->field_count) ))
587: {
588: return NULL;/* No rows or no more rows */
589: }
590:
591: previous_row = set->data_cursor - result->meta->field_count;
592: for (i = 0; i < result->meta->field_count; i++) {
593: result->lengths[i] = (Z_TYPE_P(previous_row[i]) == IS_NULL)? 0:Z_STRLEN_P(previous_row[i]);
594: }
595:
596: return result->lengths;
597: }
598: /* }}} */
599:
600:
601: /* {{{ mysqlnd_fetch_lengths_unbuffered */
602: static unsigned long *
603: mysqlnd_fetch_lengths_unbuffered(MYSQLND_RES * const result TSRMLS_DC)
604: {
605: /* simulate output of libmysql */
606: return (!result->unbuf || result->unbuf->last_row_data || result->unbuf->eof_reached)? result->lengths:NULL;
607: }
608: /* }}} */
609:
610:
611: /* {{{ mysqlnd_res::fetch_lengths */
612: PHPAPI unsigned long * _mysqlnd_fetch_lengths(MYSQLND_RES * const result TSRMLS_DC)
613: {
614: return result->m.fetch_lengths? result->m.fetch_lengths(result TSRMLS_CC) : NULL;
615: }
616: /* }}} */
617:
618:
619: /* {{{ mysqlnd_fetch_row_unbuffered_c */
620: static MYSQLND_ROW_C
621: mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES * result TSRMLS_DC)
622: {
623: enum_func_status ret;
624: MYSQLND_ROW_C retrow = NULL;
625: unsigned int i,
626: field_count = result->field_count;
627: MYSQLND_PACKET_ROW *row_packet = result->row_packet;
628: unsigned long *lengths = result->lengths;
629:
630: DBG_ENTER("mysqlnd_fetch_row_unbuffered_c");
631:
632: if (result->unbuf->eof_reached) {
633: /* No more rows obviously */
634: DBG_RETURN(retrow);
635: }
636: if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) {
1.1.1.2 ! misho 637: SET_CLIENT_ERROR(*result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
1.1 misho 638: UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
639: DBG_RETURN(retrow);
640: }
641: if (!row_packet) {
642: /* Not fully initialized object that is being cleaned up */
643: DBG_RETURN(retrow);
644: }
645: /* Let the row packet fill our buffer and skip additional mnd_malloc + memcpy */
646: row_packet->skip_extraction = FALSE;
647:
648: /*
649: If we skip rows (row == NULL) we have to
650: result->m.unbuffered_free_last_data() before it. The function returns always true.
651: */
652: if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
653: result->unbuf->row_count++;
654:
655: result->m.unbuffered_free_last_data(result TSRMLS_CC);
656:
657: result->unbuf->last_row_data = row_packet->fields;
658: result->unbuf->last_row_buffer = row_packet->row_buffer;
659: row_packet->fields = NULL;
660: row_packet->row_buffer = NULL;
661:
662: MYSQLND_INC_CONN_STATISTIC(result->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF);
663:
664: if (!row_packet->skip_extraction) {
665: MYSQLND_FIELD *field = result->meta->fields;
666: struct mysqlnd_field_hash_key * hash_key = result->meta->zend_hash_keys;
667:
668: enum_func_status rc = result->m.row_decoder(result->unbuf->last_row_buffer,
669: result->unbuf->last_row_data,
670: row_packet->field_count,
671: row_packet->fields_metadata,
1.1.1.2 ! misho 672: result->conn->options->numeric_and_datetime_as_unicode,
! 673: result->conn->options->int_and_float_native,
1.1 misho 674: result->conn->stats TSRMLS_CC);
675: if (PASS != rc) {
676: DBG_RETURN(retrow);
677: }
678:
679: retrow = mnd_malloc(result->field_count * sizeof(char *));
680: if (retrow) {
681: for (i = 0; i < field_count; i++, field++, hash_key++) {
682: zval *data = result->unbuf->last_row_data[i];
683: unsigned int len;
684:
685: if (Z_TYPE_P(data) != IS_NULL) {
686: convert_to_string(data);
687: retrow[i] = Z_STRVAL_P(data);
688: len = Z_STRLEN_P(data);
689: } else {
690: retrow[i] = NULL;
691: len = 0;
692: }
693:
694: if (lengths) {
695: lengths[i] = len;
696: }
697:
698: if (field->max_length < len) {
699: field->max_length = len;
700: }
701: }
702: } else {
1.1.1.2 ! misho 703: SET_OOM_ERROR(*result->conn->error_info);
1.1 misho 704: }
705: }
706: } else if (ret == FAIL) {
707: if (row_packet->error_info.error_no) {
1.1.1.2 ! misho 708: COPY_CLIENT_ERROR(*result->conn->error_info, row_packet->error_info);
1.1 misho 709: DBG_ERR_FMT("errorno=%u error=%s", row_packet->error_info.error_no, row_packet->error_info.error);
710: }
711: CONN_SET_STATE(result->conn, CONN_READY);
712: result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
713: } else if (row_packet->eof) {
714: /* Mark the connection as usable again */
1.1.1.2 ! misho 715: DBG_INF_FMT("warnings=%u server_status=%u", row_packet->warning_count, row_packet->server_status);
1.1 misho 716: result->unbuf->eof_reached = TRUE;
1.1.1.2 ! misho 717: result->conn->upsert_status->warning_count = row_packet->warning_count;
! 718: result->conn->upsert_status->server_status = row_packet->server_status;
1.1 misho 719: /*
720: result->row_packet will be cleaned when
721: destroying the result object
722: */
1.1.1.2 ! misho 723: if (result->conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
1.1 misho 724: CONN_SET_STATE(result->conn, CONN_NEXT_RESULT_PENDING);
725: } else {
726: CONN_SET_STATE(result->conn, CONN_READY);
727: }
728: result->m.unbuffered_free_last_data(result TSRMLS_CC);
729: }
730:
731: DBG_RETURN(retrow);
732: }
733: /* }}} */
734:
735:
736: /* {{{ mysqlnd_fetch_row_unbuffered */
737: static enum_func_status
738: mysqlnd_fetch_row_unbuffered(MYSQLND_RES * result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
739: {
740: enum_func_status ret;
741: zval *row = (zval *) param;
742: MYSQLND_PACKET_ROW *row_packet = result->row_packet;
743:
744: DBG_ENTER("mysqlnd_fetch_row_unbuffered");
745:
746: *fetched_anything = FALSE;
747: if (result->unbuf->eof_reached) {
748: /* No more rows obviously */
749: DBG_RETURN(PASS);
750: }
751: if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) {
1.1.1.2 ! misho 752: SET_CLIENT_ERROR(*result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
1.1 misho 753: DBG_RETURN(FAIL);
754: }
755: if (!row_packet) {
756: /* Not fully initialized object that is being cleaned up */
757: DBG_RETURN(FAIL);
758: }
759: /* Let the row packet fill our buffer and skip additional mnd_malloc + memcpy */
760: row_packet->skip_extraction = row? FALSE:TRUE;
761:
762: /*
763: If we skip rows (row == NULL) we have to
764: result->m.unbuffered_free_last_data() before it. The function returns always true.
765: */
766: if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
767: result->m.unbuffered_free_last_data(result TSRMLS_CC);
768:
769: result->unbuf->last_row_data = row_packet->fields;
770: result->unbuf->last_row_buffer = row_packet->row_buffer;
771: row_packet->fields = NULL;
772: row_packet->row_buffer = NULL;
773:
774: MYSQLND_INC_CONN_STATISTIC(result->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF);
775:
776: if (!row_packet->skip_extraction) {
777: HashTable *row_ht = Z_ARRVAL_P(row);
778: MYSQLND_FIELD *field = result->meta->fields;
779: struct mysqlnd_field_hash_key * hash_key = result->meta->zend_hash_keys;
780: unsigned int i, field_count = result->field_count;
781: unsigned long *lengths = result->lengths;
782:
783: enum_func_status rc = result->m.row_decoder(result->unbuf->last_row_buffer,
784: result->unbuf->last_row_data,
785: field_count,
786: row_packet->fields_metadata,
1.1.1.2 ! misho 787: result->conn->options->numeric_and_datetime_as_unicode,
! 788: result->conn->options->int_and_float_native,
1.1 misho 789: result->conn->stats TSRMLS_CC);
790: if (PASS != rc) {
791: DBG_RETURN(FAIL);
792: }
793: for (i = 0; i < field_count; i++, field++, hash_key++) {
794: zval *data = result->unbuf->last_row_data[i];
795: unsigned int len = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data);
796:
797: if (lengths) {
798: lengths[i] = len;
799: }
800:
801: if (flags & MYSQLND_FETCH_NUM) {
802: Z_ADDREF_P(data);
803: zend_hash_next_index_insert(row_ht, &data, sizeof(zval *), NULL);
804: }
805: if (flags & MYSQLND_FETCH_ASSOC) {
806: /* zend_hash_quick_update needs length + trailing zero */
807: /* QQ: Error handling ? */
808: /*
809: zend_hash_quick_update does not check, as add_assoc_zval_ex do, whether
810: the index is a numeric and convert it to it. This however means constant
811: hashing of the column name, which is not needed as it can be precomputed.
812: */
813: Z_ADDREF_P(data);
814: if (hash_key->is_numeric == FALSE) {
815: #if MYSQLND_UNICODE
816: zend_u_hash_quick_update(Z_ARRVAL_P(row), IS_UNICODE,
817: hash_key->ustr,
818: hash_key->ulen + 1,
819: hash_key->key,
820: (void *) &data, sizeof(zval *), NULL);
821: #else
822: zend_hash_quick_update(Z_ARRVAL_P(row),
823: field->name,
824: field->name_length + 1,
825: hash_key->key,
826: (void *) &data, sizeof(zval *), NULL);
827: #endif
828: } else {
829: zend_hash_index_update(Z_ARRVAL_P(row),
830: hash_key->key,
831: (void *) &data, sizeof(zval *), NULL);
832: }
833: }
834: if (field->max_length < len) {
835: field->max_length = len;
836: }
837: }
838: }
839: *fetched_anything = TRUE;
840: result->unbuf->row_count++;
841: } else if (ret == FAIL) {
842: if (row_packet->error_info.error_no) {
1.1.1.2 ! misho 843: COPY_CLIENT_ERROR(*result->conn->error_info, row_packet->error_info);
1.1 misho 844: DBG_ERR_FMT("errorno=%u error=%s", row_packet->error_info.error_no, row_packet->error_info.error);
845: }
846: CONN_SET_STATE(result->conn, CONN_READY);
847: result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
848: } else if (row_packet->eof) {
849: /* Mark the connection as usable again */
850: DBG_INF_FMT("warnings=%u server_status=%u", row_packet->warning_count, row_packet->server_status);
851: result->unbuf->eof_reached = TRUE;
1.1.1.2 ! misho 852: result->conn->upsert_status->warning_count = row_packet->warning_count;
! 853: result->conn->upsert_status->server_status = row_packet->server_status;
1.1 misho 854: /*
855: result->row_packet will be cleaned when
856: destroying the result object
857: */
1.1.1.2 ! misho 858: if (result->conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
1.1 misho 859: CONN_SET_STATE(result->conn, CONN_NEXT_RESULT_PENDING);
860: } else {
861: CONN_SET_STATE(result->conn, CONN_READY);
862: }
863: result->m.unbuffered_free_last_data(result TSRMLS_CC);
864: }
865:
866: DBG_INF_FMT("ret=%s fetched=%u", ret == PASS? "PASS":"FAIL", *fetched_anything);
867: DBG_RETURN(PASS);
868: }
869: /* }}} */
870:
871:
872: /* {{{ mysqlnd_res::use_result */
873: static MYSQLND_RES *
874: MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps TSRMLS_DC)
875: {
876: DBG_ENTER("mysqlnd_res::use_result");
877:
1.1.1.2 ! misho 878: SET_EMPTY_ERROR(*result->conn->error_info);
1.1 misho 879:
880: if (ps == FALSE) {
881: result->type = MYSQLND_RES_NORMAL;
882: result->m.fetch_row = result->m.fetch_row_normal_unbuffered;
883: result->m.fetch_lengths = mysqlnd_fetch_lengths_unbuffered;
884: result->m.row_decoder = php_mysqlnd_rowp_read_text_protocol;
885: result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long));
886: if (!result->lengths) {
887: goto oom;
888: }
889: } else {
890: result->type = MYSQLND_RES_PS_UNBUF;
891: result->m.fetch_row = NULL;
892: /* result->m.fetch_row() will be set in mysqlnd_ps.c */
893: result->m.fetch_lengths = NULL; /* makes no sense */
894: result->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol;
895: result->lengths = NULL;
896: }
897:
898: result->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC);
899: result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));
900: if (!result->result_set_memory_pool || !result->unbuf) {
901: goto oom;
902: }
903:
904: /*
905: Will be freed in the mysqlnd_internal_free_result_contents() called
906: by the resource destructor. mysqlnd_fetch_row_unbuffered() expects
907: this to be not NULL.
908: */
909: /* FALSE = non-persistent */
910: result->row_packet = result->conn->protocol->m.get_row_packet(result->conn->protocol, FALSE TSRMLS_CC);
911: if (!result->row_packet) {
912: goto oom;
913: }
914: result->row_packet->result_set_memory_pool = result->result_set_memory_pool;
915: result->row_packet->field_count = result->field_count;
916: result->row_packet->binary_protocol = ps;
917: result->row_packet->fields_metadata = result->meta->fields;
918: result->row_packet->bit_fields_count = result->meta->bit_fields_count;
919: result->row_packet->bit_fields_total_len = result->meta->bit_fields_total_len;
920:
921: DBG_RETURN(result);
922: oom:
1.1.1.2 ! misho 923: SET_OOM_ERROR(*result->conn->error_info);
1.1 misho 924: DBG_RETURN(NULL);
925: }
926: /* }}} */
927:
928:
929: /* {{{ mysqlnd_fetch_row_buffered_c */
930: static MYSQLND_ROW_C
931: mysqlnd_fetch_row_buffered_c(MYSQLND_RES * result TSRMLS_DC)
932: {
933: MYSQLND_ROW_C ret = NULL;
934: MYSQLND_RES_BUFFERED *set = result->stored_data;
935:
936: DBG_ENTER("mysqlnd_fetch_row_buffered_c");
937:
938: /* If we haven't read everything */
939: if (set->data_cursor &&
940: (set->data_cursor - set->data) < (set->row_count * result->meta->field_count))
941: {
942: zval **current_row = set->data_cursor;
943: MYSQLND_FIELD *field = result->meta->fields;
944: struct mysqlnd_field_hash_key * hash_key = result->meta->zend_hash_keys;
945: unsigned int i;
946:
947: if (NULL == current_row[0]) {
948: uint64_t row_num = (set->data_cursor - set->data) / result->meta->field_count;
949: enum_func_status rc = result->m.row_decoder(set->row_buffers[row_num],
950: current_row,
951: result->meta->field_count,
952: result->meta->fields,
1.1.1.2 ! misho 953: result->conn->options->numeric_and_datetime_as_unicode,
! 954: result->conn->options->int_and_float_native,
1.1 misho 955: result->conn->stats TSRMLS_CC);
956: if (rc != PASS) {
957: DBG_RETURN(ret);
958: }
959: set->initialized_rows++;
960: for (i = 0; i < result->field_count; i++) {
961: /*
962: NULL fields are 0 length, 0 is not more than 0
963: String of zero size, definitely can't be the next max_length.
964: Thus for NULL and zero-length we are quite efficient.
965: */
966: if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
967: unsigned long len = Z_STRLEN_P(current_row[i]);
968: if (field->max_length < len) {
969: field->max_length = len;
970: }
971: }
972: }
973: }
974:
975: set->data_cursor += result->meta->field_count;
976: MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);
977:
978: ret = mnd_malloc(result->field_count * sizeof(char *));
979: if (ret) {
980: for (i = 0; i < result->field_count; i++, field++, hash_key++) {
981: zval *data = current_row[i];
982:
983: if (Z_TYPE_P(data) != IS_NULL) {
984: convert_to_string(data);
985: ret[i] = Z_STRVAL_P(data);
986: } else {
987: ret[i] = NULL;
988: }
989: }
990: }
991: /* there is no conn handle in this function thus we can't set OOM in error_info */
992: } else {
993: set->data_cursor = NULL;
994: DBG_INF("EOF reached");
995: }
996: DBG_RETURN(ret);
997: }
998: /* }}} */
999:
1000:
1001: /* {{{ mysqlnd_fetch_row_buffered */
1002: static enum_func_status
1003: mysqlnd_fetch_row_buffered(MYSQLND_RES * result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
1004: {
1005: unsigned int i;
1006: zval *row = (zval *) param;
1007: MYSQLND_RES_BUFFERED *set = result->stored_data;
1008: enum_func_status ret = FAIL;
1009:
1010: DBG_ENTER("mysqlnd_fetch_row_buffered");
1011:
1012: /* If we haven't read everything */
1013: if (set->data_cursor &&
1014: (set->data_cursor - set->data) < (set->row_count * result->meta->field_count))
1015: {
1016: zval **current_row = set->data_cursor;
1017: MYSQLND_FIELD *field = result->meta->fields;
1018: struct mysqlnd_field_hash_key * hash_key = result->meta->zend_hash_keys;
1019:
1020: if (NULL == current_row[0]) {
1021: uint64_t row_num = (set->data_cursor - set->data) / result->meta->field_count;
1022: enum_func_status rc = result->m.row_decoder(set->row_buffers[row_num],
1023: current_row,
1024: result->meta->field_count,
1025: result->meta->fields,
1.1.1.2 ! misho 1026: result->conn->options->numeric_and_datetime_as_unicode,
! 1027: result->conn->options->int_and_float_native,
1.1 misho 1028: result->conn->stats TSRMLS_CC);
1029: if (rc != PASS) {
1030: DBG_RETURN(FAIL);
1031: }
1032: set->initialized_rows++;
1033: for (i = 0; i < result->field_count; i++) {
1034: /*
1035: NULL fields are 0 length, 0 is not more than 0
1036: String of zero size, definitely can't be the next max_length.
1037: Thus for NULL and zero-length we are quite efficient.
1038: */
1039: if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
1040: unsigned long len = Z_STRLEN_P(current_row[i]);
1041: if (field->max_length < len) {
1042: field->max_length = len;
1043: }
1044: }
1045: }
1046: }
1047:
1048: for (i = 0; i < result->field_count; i++, field++, hash_key++) {
1049: zval *data = current_row[i];
1050:
1051: if (flags & MYSQLND_FETCH_NUM) {
1052: Z_ADDREF_P(data);
1053: zend_hash_next_index_insert(Z_ARRVAL_P(row), &data, sizeof(zval *), NULL);
1054: }
1055: if (flags & MYSQLND_FETCH_ASSOC) {
1056: /* zend_hash_quick_update needs length + trailing zero */
1057: /* QQ: Error handling ? */
1058: /*
1059: zend_hash_quick_update does not check, as add_assoc_zval_ex do, whether
1060: the index is a numeric and convert it to it. This however means constant
1061: hashing of the column name, which is not needed as it can be precomputed.
1062: */
1063: Z_ADDREF_P(data);
1064: if (hash_key->is_numeric == FALSE) {
1065: #if MYSQLND_UNICODE
1066: zend_u_hash_quick_update(Z_ARRVAL_P(row), IS_UNICODE,
1067: hash_key->ustr,
1068: hash_key->ulen + 1,
1069: hash_key->key,
1070: (void *) &data, sizeof(zval *), NULL);
1071: #else
1072: zend_hash_quick_update(Z_ARRVAL_P(row),
1073: field->name,
1074: field->name_length + 1,
1075: hash_key->key,
1076: (void *) &data, sizeof(zval *), NULL);
1077: #endif
1078: } else {
1079: zend_hash_index_update(Z_ARRVAL_P(row),
1080: hash_key->key,
1081: (void *) &data, sizeof(zval *), NULL);
1082: }
1083: }
1084: }
1085: set->data_cursor += result->meta->field_count;
1086: *fetched_anything = TRUE;
1087: MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);
1088: ret = PASS;
1089: } else {
1090: set->data_cursor = NULL;
1091: *fetched_anything = FALSE;
1092: ret = PASS;
1093: DBG_INF("EOF reached");
1094: }
1095: DBG_INF_FMT("ret=PASS fetched=%u", *fetched_anything);
1096: DBG_RETURN(ret);
1097: }
1098: /* }}} */
1099:
1100:
1101: #define STORE_RESULT_PREALLOCATED_SET_IF_NOT_EMPTY 2
1102:
1103: /* {{{ mysqlnd_res::store_result_fetch_data */
1104: enum_func_status
1.1.1.2 ! misho 1105: MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const conn, MYSQLND_RES * result,
1.1 misho 1106: MYSQLND_RES_METADATA *meta,
1.1.1.2 ! misho 1107: zend_bool binary_protocol TSRMLS_DC)
1.1 misho 1108: {
1109: enum_func_status ret;
1110: MYSQLND_PACKET_ROW *row_packet = NULL;
1111: unsigned int next_extend = STORE_RESULT_PREALLOCATED_SET_IF_NOT_EMPTY, free_rows = 1;
1112: MYSQLND_RES_BUFFERED *set;
1113:
1114: DBG_ENTER("mysqlnd_res::store_result_fetch_data");
1115:
1.1.1.2 ! misho 1116: result->stored_data = set = mnd_ecalloc(1, sizeof(MYSQLND_RES_BUFFERED));
1.1 misho 1117: if (!set) {
1.1.1.2 ! misho 1118: SET_OOM_ERROR(*conn->error_info);
1.1 misho 1119: ret = FAIL;
1120: goto end;
1121: }
1122: if (free_rows) {
1.1.1.2 ! misho 1123: set->row_buffers = mnd_emalloc((size_t)(free_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)));
1.1 misho 1124: if (!set->row_buffers) {
1.1.1.2 ! misho 1125: SET_OOM_ERROR(*conn->error_info);
1.1 misho 1126: ret = FAIL;
1127: goto end;
1128: }
1129: }
1130: set->references = 1;
1131:
1132: /* non-persistent */
1133: row_packet = conn->protocol->m.get_row_packet(conn->protocol, FALSE TSRMLS_CC);
1134: if (!row_packet) {
1.1.1.2 ! misho 1135: SET_OOM_ERROR(*conn->error_info);
1.1 misho 1136: ret = FAIL;
1137: goto end;
1138: }
1139: row_packet->result_set_memory_pool = result->result_set_memory_pool;
1140: row_packet->field_count = meta->field_count;
1141: row_packet->binary_protocol = binary_protocol;
1142: row_packet->fields_metadata = meta->fields;
1143: row_packet->bit_fields_count = meta->bit_fields_count;
1144: row_packet->bit_fields_total_len = meta->bit_fields_total_len;
1145:
1146: row_packet->skip_extraction = TRUE; /* let php_mysqlnd_rowp_read() not allocate row_packet->fields, we will do it */
1147:
1148: while (FAIL != (ret = PACKET_READ(row_packet, conn)) && !row_packet->eof) {
1149: if (!free_rows) {
1150: uint64_t total_allocated_rows = free_rows = next_extend = next_extend * 11 / 10; /* extend with 10% */
1151: MYSQLND_MEMORY_POOL_CHUNK ** new_row_buffers;
1152: total_allocated_rows += set->row_count;
1153:
1154: /* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
1155: if (total_allocated_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *) > SIZE_MAX) {
1.1.1.2 ! misho 1156: SET_OOM_ERROR(*conn->error_info);
1.1 misho 1157: ret = FAIL;
1158: goto end;
1159: }
1.1.1.2 ! misho 1160: new_row_buffers = mnd_erealloc(set->row_buffers, (size_t)(total_allocated_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)));
1.1 misho 1161: if (!new_row_buffers) {
1.1.1.2 ! misho 1162: SET_OOM_ERROR(*conn->error_info);
1.1 misho 1163: ret = FAIL;
1164: goto end;
1165: }
1166: set->row_buffers = new_row_buffers;
1167: }
1168: free_rows--;
1169: set->row_buffers[set->row_count] = row_packet->row_buffer;
1170:
1171: set->row_count++;
1172:
1173: /* So row_packet's destructor function won't efree() it */
1174: row_packet->fields = NULL;
1175: row_packet->row_buffer = NULL;
1176:
1177: /*
1178: No need to FREE_ALLOCA as we can reuse the
1179: 'lengths' and 'fields' arrays. For lengths its absolutely safe.
1180: 'fields' is reused because the ownership of the strings has been
1181: transfered above.
1182: */
1183: }
1184: /* Overflow ? */
1185: if (set->row_count) {
1186: /* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
1187: if (set->row_count * meta->field_count * sizeof(zval *) > SIZE_MAX) {
1.1.1.2 ! misho 1188: SET_OOM_ERROR(*conn->error_info);
1.1 misho 1189: ret = FAIL;
1190: goto end;
1191: }
1192: /* if pecalloc is used valgrind barks gcc version 4.3.1 20080507 (prerelease) [gcc-4_3-branch revision 135036] (SUSE Linux) */
1.1.1.2 ! misho 1193: set->data = mnd_emalloc((size_t)(set->row_count * meta->field_count * sizeof(zval *)));
1.1 misho 1194: if (!set->data) {
1.1.1.2 ! misho 1195: SET_OOM_ERROR(*conn->error_info);
1.1 misho 1196: ret = FAIL;
1197: goto end;
1198: }
1199: memset(set->data, 0, (size_t)(set->row_count * meta->field_count * sizeof(zval *)));
1200: }
1201:
1202: MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats,
1203: binary_protocol? STAT_ROWS_BUFFERED_FROM_CLIENT_PS:
1204: STAT_ROWS_BUFFERED_FROM_CLIENT_NORMAL,
1205: set->row_count);
1206:
1207: /* Finally clean */
1208: if (row_packet->eof) {
1.1.1.2 ! misho 1209: conn->upsert_status->warning_count = row_packet->warning_count;
! 1210: conn->upsert_status->server_status = row_packet->server_status;
1.1 misho 1211: }
1212: /* save some memory */
1213: if (free_rows) {
1214: /* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
1215: if (set->row_count * sizeof(MYSQLND_MEMORY_POOL_CHUNK *) > SIZE_MAX) {
1.1.1.2 ! misho 1216: SET_OOM_ERROR(*conn->error_info);
1.1 misho 1217: ret = FAIL;
1218: goto end;
1219: }
1.1.1.2 ! misho 1220: set->row_buffers = mnd_erealloc(set->row_buffers, (size_t) (set->row_count * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)));
1.1 misho 1221: }
1222:
1.1.1.2 ! misho 1223: if (conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
1.1 misho 1224: CONN_SET_STATE(conn, CONN_NEXT_RESULT_PENDING);
1225: } else {
1226: CONN_SET_STATE(conn, CONN_READY);
1227: }
1228:
1229: if (ret == FAIL) {
1.1.1.2 ! misho 1230: COPY_CLIENT_ERROR(set->error_info, row_packet->error_info);
1.1 misho 1231: } else {
1232: /* Position at the first row */
1233: set->data_cursor = set->data;
1234:
1235: /* libmysql's documentation says it should be so for SELECT statements */
1.1.1.2 ! misho 1236: conn->upsert_status->affected_rows = set->row_count;
1.1 misho 1237: }
1238: DBG_INF_FMT("ret=%s row_count=%u warnings=%u server_status=%u",
1.1.1.2 ! misho 1239: ret == PASS? "PASS":"FAIL", (uint) set->row_count, conn->upsert_status->warning_count, conn->upsert_status->server_status);
1.1 misho 1240: end:
1241: PACKET_FREE(row_packet);
1242:
1243: DBG_RETURN(ret);
1244: }
1245: /* }}} */
1246:
1247:
1248: /* {{{ mysqlnd_res::store_result */
1249: static MYSQLND_RES *
1250: MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result,
1.1.1.2 ! misho 1251: MYSQLND_CONN_DATA * const conn,
1.1 misho 1252: zend_bool ps_protocol TSRMLS_DC)
1253: {
1254: enum_func_status ret;
1255:
1256: DBG_ENTER("mysqlnd_res::store_result");
1257:
1258: /* We need the conn because we are doing lazy zval initialization in buffered_fetch_row */
1259: result->conn = conn->m->get_reference(conn TSRMLS_CC);
1260: result->type = MYSQLND_RES_NORMAL;
1261: result->m.fetch_row = result->m.fetch_row_normal_buffered;
1262: result->m.fetch_lengths = mysqlnd_fetch_lengths_buffered;
1.1.1.2 ! misho 1263: result->m.row_decoder = ps_protocol? php_mysqlnd_rowp_read_binary_protocol:
! 1264: php_mysqlnd_rowp_read_text_protocol;
1.1 misho 1265:
1266: result->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC);
1267: result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long));
1.1.1.2 ! misho 1268:
1.1 misho 1269: if (!result->result_set_memory_pool || !result->lengths) {
1.1.1.2 ! misho 1270: SET_OOM_ERROR(*conn->error_info);
1.1 misho 1271: DBG_RETURN(NULL);
1272: }
1273:
1274: CONN_SET_STATE(conn, CONN_FETCHING_DATA);
1275:
1.1.1.2 ! misho 1276: ret = result->m.store_result_fetch_data(conn, result, result->meta, ps_protocol TSRMLS_CC);
1.1 misho 1277: if (FAIL == ret) {
1278: if (result->stored_data) {
1.1.1.2 ! misho 1279: COPY_CLIENT_ERROR(*conn->error_info, result->stored_data->error_info);
1.1 misho 1280: } else {
1.1.1.2 ! misho 1281: SET_OOM_ERROR(*conn->error_info);
1.1 misho 1282: }
1283: DBG_RETURN(NULL);
1284: }
1285: /* libmysql's documentation says it should be so for SELECT statements */
1.1.1.2 ! misho 1286: conn->upsert_status->affected_rows = result->stored_data->row_count;
1.1 misho 1287:
1288: DBG_RETURN(result);
1289: }
1290: /* }}} */
1291:
1292:
1293: /* {{{ mysqlnd_res::skip_result */
1294: static enum_func_status
1295: MYSQLND_METHOD(mysqlnd_res, skip_result)(MYSQLND_RES * const result TSRMLS_DC)
1296: {
1297: zend_bool fetched_anything;
1298:
1299: DBG_ENTER("mysqlnd_res::skip_result");
1300: /*
1301: Unbuffered sets
1302: A PS could be prepared - there is metadata and thus a stmt->result but the
1303: fetch_row function isn't actually set (NULL), thus we have to skip these.
1304: */
1305: if (!result->stored_data && result->unbuf &&
1306: !result->unbuf->eof_reached && result->m.fetch_row)
1307: {
1308: DBG_INF("skipping result");
1309: /* We have to fetch all data to clean the line */
1310: MYSQLND_INC_CONN_STATISTIC(result->conn->stats,
1311: result->type == MYSQLND_RES_NORMAL? STAT_FLUSHED_NORMAL_SETS:
1312: STAT_FLUSHED_PS_SETS);
1313:
1314: while ((PASS == result->m.fetch_row(result, NULL, 0, &fetched_anything TSRMLS_CC)) && fetched_anything == TRUE) {
1315: /* do nothing */;
1316: }
1317: }
1318: DBG_RETURN(PASS);
1319: }
1320: /* }}} */
1321:
1322:
1323: /* {{{ mysqlnd_res::free_result */
1324: static enum_func_status
1325: MYSQLND_METHOD(mysqlnd_res, free_result)(MYSQLND_RES * result, zend_bool implicit TSRMLS_DC)
1326: {
1327: DBG_ENTER("mysqlnd_res::free_result");
1328:
1329: result->m.skip_result(result TSRMLS_CC);
1330: MYSQLND_INC_CONN_STATISTIC(result->conn? result->conn->stats : NULL,
1331: implicit == TRUE? STAT_FREE_RESULT_IMPLICIT:
1332: STAT_FREE_RESULT_EXPLICIT);
1333:
1334: result->m.free_result_internal(result TSRMLS_CC);
1335: DBG_RETURN(PASS);
1336: }
1337: /* }}} */
1338:
1339:
1340: /* {{{ mysqlnd_res::data_seek */
1341: static enum_func_status
1342: MYSQLND_METHOD(mysqlnd_res, data_seek)(MYSQLND_RES * result, uint64_t row TSRMLS_DC)
1343: {
1344: DBG_ENTER("mysqlnd_res::data_seek");
1345: DBG_INF_FMT("row=%lu", row);
1346:
1347: if (!result->stored_data) {
1348: return FAIL;
1349: }
1350:
1351: /* libmysql just moves to the end, it does traversing of a linked list */
1352: if (row >= result->stored_data->row_count) {
1353: result->stored_data->data_cursor = NULL;
1354: } else {
1355: result->stored_data->data_cursor = result->stored_data->data + row * result->meta->field_count;
1356: }
1357:
1358: DBG_RETURN(PASS);
1359: }
1360: /* }}} */
1361:
1362:
1363: /* {{{ mysqlnd_res::num_rows */
1364: static uint64_t
1365: MYSQLND_METHOD(mysqlnd_res, num_rows)(const MYSQLND_RES * const result TSRMLS_DC)
1366: {
1367: /* Be compatible with libmysql. We count row_count, but will return 0 */
1368: return result->stored_data? result->stored_data->row_count:(result->unbuf && result->unbuf->eof_reached? result->unbuf->row_count:0);
1369: }
1370: /* }}} */
1371:
1372:
1373: /* {{{ mysqlnd_res::num_fields */
1374: static unsigned int
1375: MYSQLND_METHOD(mysqlnd_res, num_fields)(const MYSQLND_RES * const result TSRMLS_DC)
1376: {
1377: return result->field_count;
1378: }
1379: /* }}} */
1380:
1381:
1382: /* {{{ mysqlnd_res::fetch_field */
1383: static const MYSQLND_FIELD *
1384: MYSQLND_METHOD(mysqlnd_res, fetch_field)(MYSQLND_RES * const result TSRMLS_DC)
1385: {
1386: DBG_ENTER("mysqlnd_res::fetch_field");
1387: do {
1388: if (result->meta) {
1389: /*
1390: We optimize the result set, so we don't convert all the data from raw buffer format to
1391: zval arrays during store. In the case someone doesn't read all the lines this will
1392: save time. However, when a metadata call is done, we need to calculate max_length.
1393: We don't have control whether max_length will be used, unfortunately. Otherwise we
1394: could have been able to skip that step.
1395: Well, if the mysqli API switches from returning stdClass to class like mysqli_field_metadata,
1396: then we can have max_length as dynamic property, which will be calculated during runtime and
1397: not during mysqli_fetch_field() time.
1398: */
1399: if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
1400: DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request");
1401: /* we have to initialize the rest to get the updated max length */
1402: if (PASS != result->m.initialize_result_set_rest(result TSRMLS_CC)) {
1403: break;
1404: }
1405: }
1406: DBG_RETURN(result->meta->m->fetch_field(result->meta TSRMLS_CC));
1407: }
1408: } while (0);
1409: DBG_RETURN(NULL);
1410: }
1411: /* }}} */
1412:
1413:
1414: /* {{{ mysqlnd_res::fetch_field_direct */
1415: static const MYSQLND_FIELD *
1416: MYSQLND_METHOD(mysqlnd_res, fetch_field_direct)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC)
1417: {
1418: DBG_ENTER("mysqlnd_res::fetch_field_direct");
1419: do {
1420: if (result->meta) {
1421: /*
1422: We optimize the result set, so we don't convert all the data from raw buffer format to
1423: zval arrays during store. In the case someone doesn't read all the lines this will
1424: save time. However, when a metadata call is done, we need to calculate max_length.
1425: We don't have control whether max_length will be used, unfortunately. Otherwise we
1426: could have been able to skip that step.
1427: Well, if the mysqli API switches from returning stdClass to class like mysqli_field_metadata,
1428: then we can have max_length as dynamic property, which will be calculated during runtime and
1429: not during mysqli_fetch_field_direct() time.
1430: */
1431: if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
1432: DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request");
1433: /* we have to initialized the rest to get the updated max length */
1434: if (PASS != result->m.initialize_result_set_rest(result TSRMLS_CC)) {
1435: break;
1436: }
1437: }
1438: DBG_RETURN(result->meta->m->fetch_field_direct(result->meta, fieldnr TSRMLS_CC));
1439: }
1440: } while (0);
1441:
1442: DBG_RETURN(NULL);
1443: }
1444: /* }}} */
1445:
1446:
1447: /* {{{ mysqlnd_res::fetch_field */
1448: static const MYSQLND_FIELD *
1449: MYSQLND_METHOD(mysqlnd_res, fetch_fields)(MYSQLND_RES * const result TSRMLS_DC)
1450: {
1451: DBG_ENTER("mysqlnd_res::fetch_fields");
1452: do {
1453: if (result->meta) {
1454: if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
1455: /* we have to initialize the rest to get the updated max length */
1456: if (PASS != result->m.initialize_result_set_rest(result TSRMLS_CC)) {
1457: break;
1458: }
1459: }
1460: DBG_RETURN(result->meta->m->fetch_fields(result->meta TSRMLS_CC));
1461: }
1462: } while (0);
1463: DBG_RETURN(NULL);
1464: }
1465: /* }}} */
1466:
1467:
1468:
1469: /* {{{ mysqlnd_res::field_seek */
1470: static MYSQLND_FIELD_OFFSET
1471: MYSQLND_METHOD(mysqlnd_res, field_seek)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET field_offset TSRMLS_DC)
1472: {
1473: MYSQLND_FIELD_OFFSET return_value = 0;
1474: if (result->meta) {
1475: return_value = result->meta->current_field;
1476: result->meta->current_field = field_offset;
1477: }
1478: return return_value;
1479: }
1480: /* }}} */
1481:
1482:
1483: /* {{{ mysqlnd_res::field_tell */
1484: static MYSQLND_FIELD_OFFSET
1485: MYSQLND_METHOD(mysqlnd_res, field_tell)(const MYSQLND_RES * const result TSRMLS_DC)
1486: {
1487: return result->meta? result->meta->m->field_tell(result->meta TSRMLS_CC) : 0;
1488: }
1489: /* }}} */
1490:
1491:
1492: /* {{{ mysqlnd_res::fetch_into */
1493: static void
1494: MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES * result, unsigned int flags,
1495: zval *return_value,
1496: enum_mysqlnd_extension extension TSRMLS_DC ZEND_FILE_LINE_DC)
1497: {
1498: zend_bool fetched_anything;
1499:
1500: DBG_ENTER("mysqlnd_res::fetch_into");
1501:
1502: if (!result->m.fetch_row) {
1503: RETVAL_NULL();
1504: DBG_VOID_RETURN;
1505: }
1506: /*
1507: Hint Zend how many elements we will have in the hash. Thus it won't
1508: extend and rehash the hash constantly.
1509: */
1510: mysqlnd_array_init(return_value, mysqlnd_num_fields(result) * 2);
1511: if (FAIL == result->m.fetch_row(result, (void *)return_value, flags, &fetched_anything TSRMLS_CC)) {
1512: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading a row");
1513: RETVAL_FALSE;
1514: } else if (fetched_anything == FALSE) {
1515: zval_dtor(return_value);
1516: switch (extension) {
1517: case MYSQLND_MYSQLI:
1518: RETVAL_NULL();
1519: break;
1520: case MYSQLND_MYSQL:
1521: RETVAL_FALSE;
1522: break;
1523: default:exit(0);
1524: }
1.1.1.2 ! misho 1525: }
1.1 misho 1526: /*
1527: return_value is IS_NULL for no more data and an array for data. Thus it's ok
1528: to return here.
1529: */
1530: DBG_VOID_RETURN;
1531: }
1532: /* }}} */
1533:
1534:
1535: /* {{{ mysqlnd_res::fetch_row_c */
1536: static MYSQLND_ROW_C
1537: MYSQLND_METHOD(mysqlnd_res, fetch_row_c)(MYSQLND_RES * result TSRMLS_DC)
1538: {
1539: MYSQLND_ROW_C ret = NULL;
1540: DBG_ENTER("mysqlnd_res::fetch_row_c");
1541:
1542: if (result->m.fetch_row) {
1543: if (result->m.fetch_row == result->m.fetch_row_normal_buffered) {
1544: DBG_RETURN(mysqlnd_fetch_row_buffered_c(result TSRMLS_CC));
1545: } else if (result->m.fetch_row == result->m.fetch_row_normal_unbuffered) {
1546: DBG_RETURN(mysqlnd_fetch_row_unbuffered_c(result TSRMLS_CC));
1547: } else {
1548: php_error_docref(NULL TSRMLS_CC, E_ERROR, "result->m.fetch_row has invalid value. Report to the developers");
1549: }
1550: }
1551: DBG_RETURN(ret);
1552: }
1553: /* }}} */
1554:
1555:
1556: /* {{{ mysqlnd_res::fetch_all */
1557: static void
1558: MYSQLND_METHOD(mysqlnd_res, fetch_all)(MYSQLND_RES * result, unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC)
1559: {
1560: zval *row;
1561: ulong i = 0;
1562: MYSQLND_RES_BUFFERED *set = result->stored_data;
1563:
1564: DBG_ENTER("mysqlnd_res::fetch_all");
1565:
1566: if ((!result->unbuf && !set)) {
1567: php_error_docref(NULL TSRMLS_CC, E_WARNING, "fetch_all can be used only with buffered sets");
1568: if (result->conn) {
1.1.1.2 ! misho 1569: SET_CLIENT_ERROR(*result->conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "fetch_all can be used only with buffered sets");
1.1 misho 1570: }
1571: RETVAL_NULL();
1572: DBG_VOID_RETURN;
1573: }
1574:
1575: /* 4 is a magic value. The cast is safe, if larger then the array will be later extended - no big deal :) */
1576: mysqlnd_array_init(return_value, set? (unsigned int) set->row_count : 4);
1577:
1578: do {
1579: MAKE_STD_ZVAL(row);
1580: mysqlnd_fetch_into(result, flags, row, MYSQLND_MYSQLI);
1581: if (Z_TYPE_P(row) != IS_ARRAY) {
1582: zval_ptr_dtor(&row);
1583: break;
1584: }
1585: add_index_zval(return_value, i++, row);
1586: } while (1);
1587:
1588: DBG_VOID_RETURN;
1589: }
1590: /* }}} */
1591:
1592:
1593: /* {{{ mysqlnd_res::fetch_field_data */
1594: static void
1595: MYSQLND_METHOD(mysqlnd_res, fetch_field_data)(MYSQLND_RES * result, unsigned int offset, zval *return_value TSRMLS_DC)
1596: {
1597: zval row;
1598: zval **entry;
1599: unsigned int i = 0;
1600:
1601: DBG_ENTER("mysqlnd_res::fetch_field_data");
1602: DBG_INF_FMT("offset=%u", offset);
1603:
1604: if (!result->m.fetch_row) {
1605: RETVAL_NULL();
1606: DBG_VOID_RETURN;
1607: }
1608: /*
1609: Hint Zend how many elements we will have in the hash. Thus it won't
1610: extend and rehash the hash constantly.
1611: */
1612: INIT_PZVAL(&row);
1613: mysqlnd_fetch_into(result, MYSQLND_FETCH_NUM, &row, MYSQLND_MYSQL);
1614: if (Z_TYPE(row) != IS_ARRAY) {
1615: zval_dtor(&row);
1616: RETVAL_NULL();
1617: DBG_VOID_RETURN;
1618: }
1619: zend_hash_internal_pointer_reset(Z_ARRVAL(row));
1620: while (i++ < offset) {
1621: zend_hash_move_forward(Z_ARRVAL(row));
1622: zend_hash_get_current_data(Z_ARRVAL(row), (void **)&entry);
1623: }
1624:
1625: zend_hash_get_current_data(Z_ARRVAL(row), (void **)&entry);
1626:
1627: *return_value = **entry;
1628: zval_copy_ctor(return_value);
1629: Z_SET_REFCOUNT_P(return_value, 1);
1630: zval_dtor(&row);
1631:
1632: DBG_VOID_RETURN;
1633: }
1634: /* }}} */
1635:
1636:
1637: MYSQLND_CLASS_METHODS_START(mysqlnd_res)
1638: NULL, /* fetch_row */
1639: mysqlnd_fetch_row_buffered,
1640: mysqlnd_fetch_row_unbuffered,
1641: MYSQLND_METHOD(mysqlnd_res, use_result),
1642: MYSQLND_METHOD(mysqlnd_res, store_result),
1643: MYSQLND_METHOD(mysqlnd_res, fetch_into),
1644: MYSQLND_METHOD(mysqlnd_res, fetch_row_c),
1645: MYSQLND_METHOD(mysqlnd_res, fetch_all),
1646: MYSQLND_METHOD(mysqlnd_res, fetch_field_data),
1647: MYSQLND_METHOD(mysqlnd_res, num_rows),
1648: MYSQLND_METHOD(mysqlnd_res, num_fields),
1649: MYSQLND_METHOD(mysqlnd_res, skip_result),
1650: MYSQLND_METHOD(mysqlnd_res, data_seek),
1651: MYSQLND_METHOD(mysqlnd_res, field_seek),
1652: MYSQLND_METHOD(mysqlnd_res, field_tell),
1653: MYSQLND_METHOD(mysqlnd_res, fetch_field),
1654: MYSQLND_METHOD(mysqlnd_res, fetch_field_direct),
1655: MYSQLND_METHOD(mysqlnd_res, fetch_fields),
1656: MYSQLND_METHOD(mysqlnd_res, read_result_metadata),
1657: NULL, /* fetch_lengths */
1658: MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data),
1659: MYSQLND_METHOD(mysqlnd_res, initialize_result_set_rest),
1660: MYSQLND_METHOD(mysqlnd_res, free_result_buffers),
1661: MYSQLND_METHOD(mysqlnd_res, free_result),
1662:
1663: mysqlnd_internal_free_result, /* free_result_internal */
1664: mysqlnd_internal_free_result_contents, /* free_result_contents */
1665: MYSQLND_METHOD(mysqlnd_res, free_buffered_data),
1666: MYSQLND_METHOD(mysqlnd_res, unbuffered_free_last_data),
1667:
1668: NULL /* row_decoder */,
1669: mysqlnd_result_meta_init
1670: MYSQLND_CLASS_METHODS_END;
1671:
1672:
1673: /* {{{ mysqlnd_result_init */
1674: PHPAPI MYSQLND_RES *
1675: mysqlnd_result_init(unsigned int field_count, zend_bool persistent TSRMLS_DC)
1676: {
1677: size_t alloc_size = sizeof(MYSQLND_RES) + mysqlnd_plugin_count() * sizeof(void *);
1678: MYSQLND_RES *ret = mnd_pecalloc(1, alloc_size, persistent);
1679:
1680: DBG_ENTER("mysqlnd_result_init");
1681:
1682: if (!ret) {
1683: DBG_RETURN(NULL);
1684: }
1685:
1686: ret->persistent = persistent;
1687: ret->field_count = field_count;
1.1.1.2 ! misho 1688: ret->m = *mysqlnd_result_get_methods();
1.1 misho 1689:
1690: DBG_RETURN(ret);
1691: }
1692: /* }}} */
1693:
1694:
1695: /*
1696: * Local variables:
1697: * tab-width: 4
1698: * c-basic-offset: 4
1699: * End:
1700: * vim600: noet sw=4 ts=4 fdm=marker
1701: * vim<600: noet sw=4 ts=4
1702: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>