php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
mysqlnd_result.c
Go to the documentation of this file.
1/*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Andrey Hristov <andrey@php.net> |
14 | Ulf Wendel <uw@php.net> |
15 +----------------------------------------------------------------------+
16*/
17
18#include "php.h"
19#include "mysqlnd.h"
21#include "mysqlnd_block_alloc.h"
22#include "mysqlnd_connection.h"
23#include "mysqlnd_priv.h"
24#include "mysqlnd_result.h"
25#include "mysqlnd_result_meta.h"
26#include "mysqlnd_statistics.h"
27#include "mysqlnd_debug.h"
28#include "mysqlnd_ext_plugin.h"
29
30/* {{{ mysqlnd_result_unbuffered::free_result */
31static void
32MYSQLND_METHOD(mysqlnd_result_unbuffered, free_result)(MYSQLND_RES_UNBUFFERED * const result, MYSQLND_STATS * const global_stats)
33{
34 DBG_ENTER("mysqlnd_result_unbuffered, free_result");
35
36 /* must be free before because references the memory pool */
37 if (result->row_packet) {
38 PACKET_FREE(result->row_packet);
39 mnd_efree(result->row_packet);
40 result->row_packet = NULL;
41 }
42
44}
45/* }}} */
46
47static void mysqlnd_result_free_prev_data(MYSQLND_RES *result)
48{
49 if (result->free_row_data) {
50 for (unsigned i = 0; i < result->field_count; ++i) {
51 zval_ptr_dtor_nogc(&result->row_data[i]);
52 }
53 result->free_row_data = 0;
54 }
55}
56
57/* {{{ mysqlnd_result_buffered::free_result */
58static void
59MYSQLND_METHOD(mysqlnd_result_buffered, free_result)(MYSQLND_RES_BUFFERED * const set)
60{
61
62 DBG_ENTER("mysqlnd_result_buffered::free_result");
63 DBG_INF_FMT("Freeing %" PRIu64 " row(s)", set->row_count);
64
65 mysqlnd_error_info_free_contents(&set->error_info);
66
67 if (set->row_buffers) {
68 mnd_efree(set->row_buffers);
69 set->row_buffers = NULL;
70 }
71
73}
74/* }}} */
75
76
77/* {{{ mysqlnd_res::free_result_buffers */
78static void
79MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES * result)
80{
81 DBG_ENTER("mysqlnd_res::free_result_buffers");
82 DBG_INF_FMT("%s", result->unbuf? "unbuffered":(result->stored_data? "buffered":"unknown"));
83
84 mysqlnd_result_free_prev_data(result);
85
86 if (result->meta) {
87 ZEND_ASSERT(zend_arena_contains(result->memory_pool->arena, result->meta));
88 result->meta->m->free_metadata(result->meta);
89 result->meta = NULL;
90 }
91
92 if (result->unbuf) {
93 result->unbuf->m.free_result(result->unbuf, result->conn? result->conn->stats : NULL);
94 result->unbuf = NULL;
95 } else if (result->stored_data) {
96 result->stored_data->m.free_result(result->stored_data);
97 result->stored_data = NULL;
98 }
99
102
104}
105/* }}} */
106
107
108/* {{{ mysqlnd_res::free_result_contents_internal */
109static
110void MYSQLND_METHOD(mysqlnd_res, free_result_contents_internal)(MYSQLND_RES * result)
111{
112 DBG_ENTER("mysqlnd_res::free_result_contents_internal");
113
114 result->m.free_result_buffers(result);
115
116 if (result->conn) {
117 result->conn->m->free_reference(result->conn);
118 result->conn = NULL;
119 }
120
121 mysqlnd_mempool_destroy(result->memory_pool);
122
124}
125/* }}} */
126
127
128/* {{{ mysqlnd_res::read_result_metadata */
129static enum_func_status
130MYSQLND_METHOD(mysqlnd_res, read_result_metadata)(MYSQLND_RES * result, MYSQLND_CONN_DATA * conn)
131{
132 DBG_ENTER("mysqlnd_res::read_result_metadata");
133
134 /*
135 Make it safe to call it repeatedly for PS -
136 better free and allocate a new because the number of field might change
137 (select *) with altered table. Also for statements which skip the PS
138 infrastructure!
139 */
140 if (result->meta) {
141 result->meta->m->free_metadata(result->meta);
142 result->meta = NULL;
143 }
144
145 result->meta = result->m.result_meta_init(result, result->field_count);
146
147 /* 1. Read all fields metadata */
148
149 /* It's safe to reread without freeing */
150 if (FAIL == result->meta->m->read_metadata(result->meta, conn, result)) {
151 result->meta->m->free_metadata(result->meta);
152 result->meta = NULL;
154 }
155
156 /*
157 2. Follows an EOF packet, which the client of mysqlnd_read_result_metadata()
158 should consume.
159 3. If there is a result set, it follows. The last packet will have 'eof' set
160 If PS, then no result set follows.
161 */
162
164}
165/* }}} */
166
167
168/* {{{ mysqlnd_query_read_result_set_header */
171{
173 MYSQLND_STMT_DATA * stmt = s ? s->data : NULL;
174 MYSQLND_PACKET_RSET_HEADER rset_header;
175 MYSQLND_PACKET_EOF fields_eof;
176
177 DBG_ENTER("mysqlnd_query_read_result_set_header");
178 DBG_INF_FMT("stmt=" ZEND_ULONG_FMT, stmt? stmt->stmt_id:0);
179
180 ret = FAIL;
181 do {
182 conn->payload_decoder_factory->m.init_rset_header_packet(&rset_header);
184
185 if (FAIL == (ret = PACKET_READ(conn, &rset_header))) {
187 php_error_docref(NULL, E_WARNING, "Error reading result set's header");
188 }
189 break;
190 }
191
192 if (rset_header.error_info.error_no) {
193 /*
194 Cover a protocol design error: error packet does not
195 contain the server status. Therefore, the client has no way
196 to find out whether there are more result sets of
197 a multiple-result-set statement pending. Luckily, in 5.0 an
198 error always aborts execution of a statement, wherever it is
199 a multi-statement or a stored procedure, so it should be
200 safe to unconditionally turn off the flag here.
201 */
203 /*
204 This will copy the error code and the messages, as they
205 are buffers in the struct
206 */
207 COPY_CLIENT_ERROR(conn->error_info, rset_header.error_info);
208 ret = FAIL;
209 DBG_ERR_FMT("error=%s", rset_header.error_info.error);
210 /* Return back from CONN_QUERY_SENT */
212 break;
213 }
214 conn->error_info->error_no = 0;
215
216 switch (rset_header.field_count) {
217 case MYSQLND_NULL_LENGTH: { /* LOAD DATA LOCAL INFILE */
218 bool is_warning;
219 DBG_INF("LOAD DATA");
221 conn->field_count = 0; /* overwrite previous value, or the last value could be used and lead to bug#53503 */
223 ret = mysqlnd_handle_local_infile(conn, rset_header.info_or_local_file.s, &is_warning);
224 SET_CONNECTION_STATE(&conn->state, (ret == PASS || is_warning == TRUE)? CONN_READY:CONN_QUIT_SENT);
226 break;
227 }
228 case 0: /* UPSERT */
229 DBG_INF("UPSERT");
231 conn->field_count = rset_header.field_count;
237 mysqlnd_set_string(&conn->last_message, rset_header.info_or_local_file.s, rset_header.info_or_local_file.l);
238 /* Result set can follow UPSERT statement, check server_status */
241 } else {
243 }
244 ret = PASS;
246 break;
247 default: do { /* Result set */
250
251 DBG_INF("Result set pending");
252 mysqlnd_set_string(&conn->last_message, NULL, 0);
253
256 /* restore after zeroing */
258
261 /* PS has already allocated it */
262 conn->field_count = rset_header.field_count;
263 if (!stmt) {
264 result = conn->current_result = conn->m->result_init(rset_header.field_count);
265 } else {
266 if (!stmt->result) {
267 DBG_INF("This is 'SHOW'/'EXPLAIN'-like query.");
268 /*
269 This is 'SHOW'/'EXPLAIN'-like query. Current implementation of
270 prepared statements can't send result set metadata for these queries
271 on prepare stage. Read it now.
272 */
273 result = stmt->result = conn->m->result_init(rset_header.field_count);
274 } else {
275 /*
276 Update result set metadata if it for some reason changed between
277 prepare and execute, i.e.:
278 - in case of 'SELECT ?' we don't know column type unless data was
279 supplied to mysql_stmt_execute, so updated column type is sent
280 now.
281 - if data dictionary changed between prepare and execute, for
282 example a table used in the query was altered.
283 Note, that now (4.1.3) we always send metadata in reply to
284 COM_STMT_EXECUTE (even if it is not necessary), so either this or
285 previous branch always works.
286 */
287 if (rset_header.field_count != stmt->result->field_count) {
288 stmt->result->m.free_result(stmt->result, TRUE);
289 stmt->result = conn->m->result_init(rset_header.field_count);
290 }
291 result = stmt->result;
292 }
293 }
294 if (!result) {
296 ret = FAIL;
297 break;
298 }
299
300 if (FAIL == (ret = result->m.read_result_metadata(result, conn))) {
301 /* For PS, we leave them in Prepared state */
302 if (!stmt && conn->current_result) {
303 conn->current_result->m.free_result(conn->current_result, TRUE);
304 conn->current_result = NULL;
305 }
306 DBG_ERR("Error occurred while reading metadata");
307 break;
308 }
309
310 /* Check for SERVER_STATUS_MORE_RESULTS if needed */
311 conn->payload_decoder_factory->m.init_eof_packet(&fields_eof);
312 if (FAIL == (ret = PACKET_READ(conn, &fields_eof))) {
313 DBG_ERR("Error occurred while reading the EOF packet");
314 result->m.free_result_contents(result);
315 if (!stmt) {
316 conn->current_result = NULL;
317 } else {
318 stmt->result = NULL;
319 /* XXX: This will crash, because we will null also the methods.
320 But seems it happens in extreme cases or doesn't. Should be fixed by exporting a function
321 (from mysqlnd_driver.c?) to do the reset.
322 This is done also in mysqlnd_ps.c
323 */
324 memset(stmt, 0, sizeof(*stmt));
326 }
327 } else {
328 DBG_INF_FMT("warnings=%u server_status=%u", fields_eof.warning_count, fields_eof.server_status);
330 /*
331 If SERVER_MORE_RESULTS_EXISTS is set then this is either MULTI_QUERY or a CALL()
332 The first packet after sending the query/com_execute has the bit set only
333 in these cases. Not sure why it's a needed but it marks that the whole stream
334 will include many result sets. What actually matters are the bits set at the end
335 of every result set (the EOF packet).
336 */
339 statistic = STAT_BAD_INDEX_USED;
340 } else if (fields_eof.server_status & SERVER_QUERY_NO_INDEX_USED) {
341 statistic = STAT_NO_INDEX_USED;
342 } else if (fields_eof.server_status & SERVER_QUERY_WAS_SLOW) {
343 statistic = STAT_QUERY_WAS_SLOW;
344 }
345 MYSQLND_INC_CONN_STATISTIC(conn->stats, statistic);
346 }
347 PACKET_FREE(&fields_eof);
348 } while (0);
349 break; /* switch break */
350 }
351 } while (0);
352 PACKET_FREE(&rset_header);
353
354 DBG_INF(ret == PASS? "PASS":"FAIL");
356}
357/* }}} */
358
359
360/* {{{ mysqlnd_result_buffered::fetch_lengths */
361/*
362 Do lazy initialization for buffered results. As PHP strings have
363 length inside, this function makes not much sense in the context
364 of PHP, to be called as separate function. But let's have it for
365 completeness.
366*/
367static const size_t *
368MYSQLND_METHOD(mysqlnd_result_buffered, fetch_lengths)(const MYSQLND_RES_BUFFERED * const result)
369{
370 DBG_ENTER("mysqlnd_result_buffered::fetch_lengths");
371
372 if (result->current_row > result->row_count || result->current_row == 0) {
373 DBG_INF("EOF");
374 DBG_RETURN(NULL); /* No more rows, or no fetched row */
375 }
376 DBG_INF("non NULL");
377 DBG_RETURN(result->lengths);
378}
379/* }}} */
380
381
382/* {{{ mysqlnd_result_unbuffered::fetch_lengths */
383static const size_t *
384MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_lengths)(const MYSQLND_RES_UNBUFFERED * const result)
385{
386 /* simulate output of libmysql */
387 return (result->last_row_buffer.ptr || result->eof_reached)? result->lengths : NULL;
388}
389/* }}} */
390
391
392/* {{{ mysqlnd_res::fetch_lengths */
393static const size_t *
394MYSQLND_METHOD(mysqlnd_res, fetch_lengths)(const MYSQLND_RES * const result)
395{
396 const size_t * ret;
397 DBG_ENTER("mysqlnd_res::fetch_lengths");
398 ret = result->stored_data && result->stored_data->m.fetch_lengths ?
399 result->stored_data->m.fetch_lengths(result->stored_data) :
400 (result->unbuf && result->unbuf->m.fetch_lengths ?
401 result->unbuf->m.fetch_lengths(result->unbuf) :
402 NULL
403 );
405}
406/* }}} */
407
408
409/* {{{ mysqlnd_result_unbuffered::fetch_row */
410static enum_func_status
411MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, zval **row_ptr, const unsigned int flags, bool * fetched_anything)
412{
414 MYSQLND_PACKET_ROW *row_packet = result->unbuf->row_packet;
415 const MYSQLND_RES_METADATA * const meta = result->meta;
416 MYSQLND_RES_UNBUFFERED *set = result->unbuf;
417 MYSQLND_CONN_DATA * const conn = result->conn;
418 void *checkpoint;
419
420 DBG_ENTER("mysqlnd_result_unbuffered::fetch_row");
421
422 *fetched_anything = FALSE;
423 if (set->eof_reached) {
424 /* No more rows obviously */
426 }
430 }
431 if (!row_packet) {
432 /* Not fully initialized object that is being cleaned up */
434 }
435
436 checkpoint = result->memory_pool->checkpoint;
438
439 if (PASS == (ret = PACKET_READ(conn, row_packet)) && !row_packet->eof) {
440 set->last_row_buffer = row_packet->row_buffer;
441 row_packet->row_buffer.ptr = NULL;
442
446
447 if (row_ptr) {
448 unsigned int field_count = meta->field_count;
449
450 *row_ptr = result->row_data;
451 enum_func_status rc = set->m.row_decoder(
452 &set->last_row_buffer, result->row_data, field_count,
453 row_packet->fields_metadata, conn->options->int_and_float_native, conn->stats);
454 if (PASS != rc) {
456 result->memory_pool->checkpoint = checkpoint;
458 }
459
460 size_t *lengths = set->lengths;
461 if (lengths) {
462 for (unsigned i = 0; i < field_count; i++) {
463 zval *data = &result->row_data[i];
464 lengths[i] = Z_TYPE_P(data) == IS_STRING ? Z_STRLEN_P(data) : 0;
465 }
466 }
467 }
468 set->row_count++;
469 *fetched_anything = TRUE;
470 } else if (ret == FAIL) {
471 if (row_packet->error_info.error_no) {
472 COPY_CLIENT_ERROR(conn->error_info, row_packet->error_info);
473 if (set->stmt) {
474 COPY_CLIENT_ERROR(set->stmt->error_info, row_packet->error_info);
475 }
476 DBG_ERR_FMT("errorno=%u error=%s", row_packet->error_info.error_no, row_packet->error_info.error);
477 }
480 }
481 set->eof_reached = TRUE; /* so next time we won't get an error */
482 } else if (row_packet->eof) {
483 /* Mark the connection as usable again */
484 DBG_INF_FMT("warnings=%u server_status=%u", row_packet->warning_count, row_packet->server_status);
485 set->eof_reached = TRUE;
486
490 /*
491 result->row_packet will be cleaned when
492 destroying the result object
493 */
496 } else {
498 }
499 }
500
502 result->memory_pool->checkpoint = checkpoint;
503
504 DBG_INF_FMT("ret=%s fetched=%u", ret == PASS? "PASS":"FAIL", *fetched_anything);
506}
507/* }}} */
508
509
510/* {{{ mysqlnd_res::use_result */
511static MYSQLND_RES *
512MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, MYSQLND_STMT_DATA *stmt)
513{
514 MYSQLND_CONN_DATA * const conn = result->conn;
515 DBG_ENTER("mysqlnd_res::use_result");
516
518
519 if (!stmt) {
521 } else {
523 }
524
525 result->unbuf = mysqlnd_result_unbuffered_init(result, result->field_count, stmt);
526
527 /*
528 Will be freed in the mysqlnd_internal_free_result_contents() called
529 by the resource destructor. mysqlnd_result_unbuffered::fetch_row() expects
530 this to be not NULL.
531 */
532 /* FALSE = non-persistent */
533 {
534 struct st_mysqlnd_packet_row *row_packet = mnd_emalloc(sizeof(struct st_mysqlnd_packet_row));
535
536 conn->payload_decoder_factory->m.init_row_packet(row_packet);
537 row_packet->result_set_memory_pool = result->unbuf->result_set_memory_pool;
538 row_packet->field_count = result->field_count;
539 row_packet->binary_protocol = stmt != NULL;
540 row_packet->fields_metadata = result->meta->fields;
541
542 result->unbuf->row_packet = row_packet;
543 }
544
546}
547/* }}} */
548
549
550/* {{{ mysqlnd_result_buffered::fetch_row */
551static enum_func_status
552MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row)(MYSQLND_RES * result, zval **row_ptr, const unsigned int flags, bool * fetched_anything)
553{
554 MYSQLND_RES_BUFFERED *set = result->stored_data;
555
556 DBG_ENTER("mysqlnd_result_buffered::fetch_row");
557
558 /* If we haven't read everything */
559 if (set->current_row < set->row_count) {
560 if (row_ptr) {
561 const MYSQLND_RES_METADATA * const meta = result->meta;
562 const unsigned int field_count = meta->field_count;
563 MYSQLND_CONN_DATA * const conn = result->conn;
565 zval *current_row = result->row_data;
566 *row_ptr = result->row_data;
567 rc = result->stored_data->m.row_decoder(&set->row_buffers[set->current_row],
568 current_row,
570 meta->fields,
572 conn->stats);
573 if (rc != PASS) {
575 }
576
577 if (set->lengths) {
578 for (unsigned i = 0; i < field_count; ++i) {
579 zval *data = &current_row[i];
580 set->lengths[i] = Z_TYPE_P(data) == IS_STRING ? Z_STRLEN_P(data) : 0;
581 }
582 }
583 }
584
585 ++set->current_row;
588 *fetched_anything = TRUE;
589 } else {
590 if (set->current_row == set->row_count) {
591 set->current_row = set->row_count + 1;
592 }
593 DBG_INF_FMT("EOF reached. current_row=%llu", (unsigned long long) set->current_row);
594 *fetched_anything = FALSE;
595 }
596
597 DBG_INF_FMT("ret=PASS fetched=%u", *fetched_anything);
599}
600/* }}} */
601
602
603/* {{{ mysqlnd_res::fetch_row */
604static enum_func_status
605MYSQLND_METHOD(mysqlnd_res, fetch_row)(MYSQLND_RES *result, zval **row_ptr, const unsigned int flags, bool *fetched_anything)
606{
607 const mysqlnd_fetch_row_func f =
608 result->stored_data ? result->stored_data->m.fetch_row :
609 result->unbuf ? result->unbuf->m.fetch_row : NULL;
610 if (f) {
611 return f(result, row_ptr, flags, fetched_anything);
612 }
613 *fetched_anything = FALSE;
614 return PASS;
615}
616/* }}} */
617
618
619/* {{{ mysqlnd_res::store_result_fetch_data */
621MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const conn, MYSQLND_RES * result,
623 MYSQLND_ROW_BUFFER **row_buffers,
624 bool binary_protocol)
625{
627 uint64_t total_allocated_rows = 0;
628 unsigned int free_rows = 0;
629 MYSQLND_RES_BUFFERED * set = result->stored_data;
630 MYSQLND_PACKET_ROW row_packet;
631
632 DBG_ENTER("mysqlnd_res::store_result_fetch_data");
633 if (!set || !row_buffers) {
634 ret = FAIL;
635 goto end;
636 }
637
638 *row_buffers = NULL;
639
640 conn->payload_decoder_factory->m.init_row_packet(&row_packet);
641 set->references = 1;
642
643 row_packet.result_set_memory_pool = result->stored_data->result_set_memory_pool;
644 row_packet.field_count = meta->field_count;
645 row_packet.binary_protocol = binary_protocol;
646 row_packet.fields_metadata = meta->fields;
647
648 while (FAIL != (ret = PACKET_READ(conn, &row_packet)) && !row_packet.eof) {
649 if (!free_rows) {
650 MYSQLND_ROW_BUFFER * new_row_buffers;
651
652 if (total_allocated_rows < 1024) {
653 if (total_allocated_rows == 0) {
654 free_rows = 1;
655 total_allocated_rows = 1;
656 } else {
657 free_rows = total_allocated_rows;
658 total_allocated_rows *= 2;
659 }
660 } else {
661 free_rows = 1024;
662 total_allocated_rows += 1024;
663 }
664
665 /* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
666 if (total_allocated_rows * sizeof(MYSQLND_ROW_BUFFER) > SIZE_MAX) {
668 ret = FAIL;
669 goto free_end;
670 }
671 if (*row_buffers) {
672 new_row_buffers = mnd_erealloc(*row_buffers, (size_t)(total_allocated_rows * sizeof(MYSQLND_ROW_BUFFER)));
673 } else {
674 new_row_buffers = mnd_emalloc((size_t)(total_allocated_rows * sizeof(MYSQLND_ROW_BUFFER)));
675 }
676 *row_buffers = new_row_buffers;
677 }
678 free_rows--;
679 (*row_buffers)[set->row_count] = row_packet.row_buffer;
680
681 set->row_count++;
682
683 /* So row_packet's destructor function won't efree() it */
684 row_packet.row_buffer.ptr = NULL;
685 }
686 /* Overflow ? */
690 set->row_count);
691
692 /* Finally clean */
693 if (row_packet.eof) {
697 }
698
699 if (ret == FAIL) {
700 /* Error packets do not contain server status information. However, we know that after
701 * an error there will be no further result sets. */
704 }
705
706 /* save some memory */
707 if (free_rows) {
708 /* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
709 if (set->row_count * sizeof(MYSQLND_ROW_BUFFER) > SIZE_MAX) {
711 ret = FAIL;
712 goto free_end;
713 }
714 *row_buffers = mnd_erealloc(*row_buffers, (size_t) (set->row_count * sizeof(MYSQLND_ROW_BUFFER)));
715 }
716
719 } else {
721 }
722
723 if (ret == FAIL) {
724 COPY_CLIENT_ERROR(&set->error_info, row_packet.error_info);
725 } else {
726 /* libmysql's documentation says it should be so for SELECT statements */
728 }
729 DBG_INF_FMT("ret=%s row_count=%u warnings=%u server_status=%u",
730 ret == PASS? "PASS":"FAIL",
731 (uint32_t) set->row_count,
734free_end:
735 PACKET_FREE(&row_packet);
736 DBG_INF_FMT("rows=%llu", (unsigned long long)set->row_count);
737end:
739}
740/* }}} */
741
742
743/* {{{ mysqlnd_res::store_result */
744static MYSQLND_RES *
745MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result,
746 MYSQLND_CONN_DATA * const conn,
747 MYSQLND_STMT_DATA *stmt)
748{
750 MYSQLND_ROW_BUFFER **row_buffers = NULL;
751
752 DBG_ENTER("mysqlnd_res::store_result");
753
754 /* We need the conn because we are doing lazy zval initialization in buffered_fetch_row */
755 /* In case of error the reference will be released in free_result() called indirectly by our caller */
756 result->conn = conn->m->get_reference(conn);
758
760
761 result->stored_data = (MYSQLND_RES_BUFFERED *) mysqlnd_result_buffered_init(result, result->field_count, stmt);
762 row_buffers = &result->stored_data->row_buffers;
763
764 ret = result->m.store_result_fetch_data(conn, result, result->meta, row_buffers, stmt != NULL);
765
766 if (FAIL == ret) {
767 if (result->stored_data) {
768 COPY_CLIENT_ERROR(conn->error_info, result->stored_data->error_info);
769 } else {
771 }
773 } else {
774 result->stored_data->current_row = 0;
775 }
776
777 /* libmysql's documentation says it should be so for SELECT statements */
778 UPSERT_STATUS_SET_AFFECTED_ROWS(conn->upsert_status, result->stored_data->row_count);
779
781}
782/* }}} */
783
784
785/* {{{ mysqlnd_res::skip_result */
786static void
787MYSQLND_METHOD(mysqlnd_res, skip_result)(MYSQLND_RES * const result)
788{
789 bool fetched_anything;
790
791 DBG_ENTER("mysqlnd_res::skip_result");
792 /*
793 Unbuffered sets
794 A PS could be prepared - there is metadata and thus a stmt->result but the
795 fetch_row function isn't actually set (NULL), thus we have to skip these.
796 */
797 if (result->unbuf && !result->unbuf->eof_reached) {
798 MYSQLND_CONN_DATA * const conn = result->conn;
799 DBG_INF("skipping result");
800 /* We have to fetch all data to clean the line */
804
805 while ((PASS == result->m.fetch_row(result, NULL, 0, &fetched_anything)) && fetched_anything == TRUE) {
809 }
810 }
812}
813/* }}} */
814
815
816/* {{{ mysqlnd_res::free_result */
817static enum_func_status
818MYSQLND_METHOD(mysqlnd_res, free_result)(MYSQLND_RES * result, const bool implicit)
819{
820 DBG_ENTER("mysqlnd_res::free_result");
821
822 MYSQLND_INC_CONN_STATISTIC(result->conn? result->conn->stats : NULL,
823 implicit == TRUE? STAT_FREE_RESULT_IMPLICIT:
825
826 result->m.skip_result(result);
827 result->m.free_result_contents(result);
829}
830/* }}} */
831
832
833/* {{{ mysqlnd_res::data_seek */
834static enum_func_status
835MYSQLND_METHOD(mysqlnd_res, data_seek)(MYSQLND_RES * const result, const uint64_t row)
836{
837 DBG_ENTER("mysqlnd_res::data_seek");
838 DBG_INF_FMT("row=%" PRIu64, row);
839
840 DBG_RETURN(result->stored_data? result->stored_data->m.data_seek(result->stored_data, row) : FAIL);
841}
842/* }}} */
843
844
845/* {{{ mysqlnd_result_buffered::data_seek */
846static enum_func_status
847MYSQLND_METHOD(mysqlnd_result_buffered, data_seek)(MYSQLND_RES_BUFFERED * const result, const uint64_t row)
848{
849 DBG_ENTER("mysqlnd_result_buffered::data_seek");
850
851 /* libmysql just moves to the end, it does traversing of a linked list */
852 if (row >= result->row_count) {
853 result->current_row = result->row_count;
854 } else {
855 result->current_row = row;
856 }
858}
859/* }}} */
860
861
862/* {{{ mysqlnd_result_unbuffered::num_rows */
863static uint64_t
864MYSQLND_METHOD(mysqlnd_result_unbuffered, num_rows)(const MYSQLND_RES_UNBUFFERED * const result)
865{
866 /* Be compatible with libmysql. We count row_count, but will return 0 */
867 return result->eof_reached? result->row_count : 0;
868}
869/* }}} */
870
871
872/* {{{ mysqlnd_result_buffered::num_rows */
873static uint64_t
874MYSQLND_METHOD(mysqlnd_result_buffered, num_rows)(const MYSQLND_RES_BUFFERED * const result)
875{
876 return result->row_count;
877}
878/* }}} */
879
880
881/* {{{ mysqlnd_res::num_rows */
882static uint64_t
883MYSQLND_METHOD(mysqlnd_res, num_rows)(const MYSQLND_RES * const result)
884{
885 return result->stored_data?
886 result->stored_data->m.num_rows(result->stored_data) :
887 (result->unbuf? result->unbuf->m.num_rows(result->unbuf) : 0);
888}
889/* }}} */
890
891
892/* {{{ mysqlnd_res::num_fields */
893static unsigned int
894MYSQLND_METHOD(mysqlnd_res, num_fields)(const MYSQLND_RES * const result)
895{
896 return result->field_count;
897}
898/* }}} */
899
900
901/* {{{ mysqlnd_res::fetch_field */
902static const MYSQLND_FIELD *
903MYSQLND_METHOD(mysqlnd_res, fetch_field)(MYSQLND_RES * const result)
904{
905 DBG_ENTER("mysqlnd_res::fetch_field");
906 do {
907 if (result->meta) {
908 DBG_RETURN(result->meta->m->fetch_field(result->meta));
909 }
910 } while (0);
912}
913/* }}} */
914
915
916/* {{{ mysqlnd_res::fetch_field_direct */
917static const MYSQLND_FIELD *
918MYSQLND_METHOD(mysqlnd_res, fetch_field_direct)(MYSQLND_RES * const result, const MYSQLND_FIELD_OFFSET fieldnr)
919{
920 DBG_ENTER("mysqlnd_res::fetch_field_direct");
921 do {
922 if (result->meta) {
923 DBG_RETURN(result->meta->m->fetch_field_direct(result->meta, fieldnr));
924 }
925 } while (0);
926
928}
929/* }}} */
930
931
932/* {{{ mysqlnd_res::fetch_field */
933static const MYSQLND_FIELD *
934MYSQLND_METHOD(mysqlnd_res, fetch_fields)(MYSQLND_RES * const result)
935{
936 DBG_ENTER("mysqlnd_res::fetch_fields");
937 do {
938 if (result->meta) {
939 DBG_RETURN(result->meta->m->fetch_fields(result->meta));
940 }
941 } while (0);
943}
944/* }}} */
945
946
947/* {{{ mysqlnd_res::field_seek */
949MYSQLND_METHOD(mysqlnd_res, field_seek)(MYSQLND_RES * const result, const MYSQLND_FIELD_OFFSET field_offset)
950{
951 return result->meta? result->meta->m->field_seek(result->meta, field_offset) : 0;
952}
953/* }}} */
954
955
956/* {{{ mysqlnd_res::field_tell */
958MYSQLND_METHOD(mysqlnd_res, field_tell)(const MYSQLND_RES * const result)
959{
960 return result->meta? result->meta->m->field_tell(result->meta) : 0;
961}
962/* }}} */
963
964
965/* {{{ mysqlnd_res::fetch_into */
966static void
967MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES * result, const unsigned int flags,
969{
970 bool fetched_anything;
971 zval *row_data;
972
973 // We clean the error here because in unbuffered mode we could receive a new error
974 // and therefore consumers of this method are checking for errors
975 MYSQLND_CONN_DATA *conn = result->conn;
976 if (conn) {
978 }
979
980 DBG_ENTER("mysqlnd_res::fetch_into");
981 if (FAIL == result->m.fetch_row(result, &row_data, flags, &fetched_anything)) {
984 } else if (fetched_anything == FALSE) {
985 RETVAL_NULL();
987 }
988
989 const MYSQLND_RES_METADATA * const meta = result->meta;
990 unsigned int array_size = meta->field_count;
992 array_size *= 2;
993 }
994 array_init_size(return_value, array_size);
995
997 MYSQLND_FIELD *field = meta->fields;
998 for (unsigned i = 0; i < meta->field_count; i++, field++) {
999 zval *data = &row_data[i];
1000
1001 if (flags & MYSQLND_FETCH_NUM) {
1002 if (zend_hash_index_add(row_ht, i, data) != NULL) {
1004 }
1005 }
1006 if (flags & MYSQLND_FETCH_ASSOC) {
1007 /* zend_hash_quick_update needs length + trailing zero */
1008 /* QQ: Error handling ? */
1009 /*
1010 zend_hash_quick_update does not check, as add_assoc_zval_ex do, whether
1011 the index is a numeric and convert it to it. This however means constant
1012 hashing of the column name, which is not needed as it can be precomputed.
1013 */
1015 if (meta->fields[i].is_numeric == FALSE) {
1016 zend_hash_update(row_ht, meta->fields[i].sname, data);
1017 } else {
1018 zend_hash_index_update(row_ht, meta->fields[i].num_key, data);
1019 }
1020 }
1021
1022 zval_ptr_dtor_nogc(data);
1023 }
1025}
1026/* }}} */
1027
1028
1029/* {{{ mysqlnd_res::fetch_row_c */
1030static MYSQLND_ROW_C
1031MYSQLND_METHOD(mysqlnd_res, fetch_row_c)(MYSQLND_RES * result)
1032{
1033 bool fetched_anything;
1034 zval *row_data;
1036 DBG_ENTER("mysqlnd_res::fetch_row_c");
1037
1038 mysqlnd_result_free_prev_data(result);
1039 if (result->m.fetch_row(result, &row_data, 0, &fetched_anything) == PASS && fetched_anything) {
1040 unsigned field_count = result->field_count;
1041 MYSQLND_FIELD *field = result->meta->fields;
1042
1043 ret = mnd_emalloc(field_count * sizeof(char *));
1044 for (unsigned i = 0; i < field_count; i++, field++) {
1045 zval *data = &row_data[i];
1046 if (Z_TYPE_P(data) != IS_NULL) {
1048 ret[i] = Z_STRVAL_P(data);
1049 } else {
1050 ret[i] = NULL;
1051 }
1052 }
1053 result->free_row_data = 1;
1054 }
1055 DBG_RETURN(ret);
1056}
1057/* }}} */
1058
1059
1060MYSQLND_CLASS_METHODS_START(mysqlnd_res)
1061 MYSQLND_METHOD(mysqlnd_res, fetch_row),
1062 MYSQLND_METHOD(mysqlnd_res, use_result),
1063 MYSQLND_METHOD(mysqlnd_res, store_result),
1064 MYSQLND_METHOD(mysqlnd_res, fetch_into),
1065 MYSQLND_METHOD(mysqlnd_res, fetch_row_c),
1066 MYSQLND_METHOD(mysqlnd_res, num_rows),
1067 MYSQLND_METHOD(mysqlnd_res, num_fields),
1068 MYSQLND_METHOD(mysqlnd_res, skip_result),
1069 MYSQLND_METHOD(mysqlnd_res, data_seek),
1070 MYSQLND_METHOD(mysqlnd_res, field_seek),
1071 MYSQLND_METHOD(mysqlnd_res, field_tell),
1072 MYSQLND_METHOD(mysqlnd_res, fetch_field),
1073 MYSQLND_METHOD(mysqlnd_res, fetch_field_direct),
1074 MYSQLND_METHOD(mysqlnd_res, fetch_fields),
1075 MYSQLND_METHOD(mysqlnd_res, read_result_metadata),
1076 MYSQLND_METHOD(mysqlnd_res, fetch_lengths),
1077 MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data),
1078 MYSQLND_METHOD(mysqlnd_res, free_result_buffers),
1079 MYSQLND_METHOD(mysqlnd_res, free_result),
1080 MYSQLND_METHOD(mysqlnd_res, free_result_contents_internal),
1082 NULL, /* unused1 */
1083 NULL, /* unused2 */
1084 NULL, /* unused3 */
1085 NULL, /* unused4 */
1086 NULL /* unused5 */
1088
1089
1090MYSQLND_CLASS_METHODS_START(mysqlnd_result_unbuffered)
1091 MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row),
1092 NULL, /* row_decoder */
1093 MYSQLND_METHOD(mysqlnd_result_unbuffered, num_rows),
1094 MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_lengths),
1095 MYSQLND_METHOD(mysqlnd_result_unbuffered, free_result)
1097
1098
1099MYSQLND_CLASS_METHODS_START(mysqlnd_result_buffered)
1100 MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row),
1101 NULL, /* row_decoder */
1102 MYSQLND_METHOD(mysqlnd_result_buffered, num_rows),
1103 MYSQLND_METHOD(mysqlnd_result_buffered, fetch_lengths),
1104 MYSQLND_METHOD(mysqlnd_result_buffered, data_seek),
1105 MYSQLND_METHOD(mysqlnd_result_buffered, free_result)
1107
1108
1109/* {{{ mysqlnd_result_init */
1112{
1113 const size_t alloc_size = sizeof(MYSQLND_RES) + mysqlnd_plugin_count() * sizeof(void *);
1114 MYSQLND_MEMORY_POOL * pool;
1115 MYSQLND_RES * ret;
1116
1117 DBG_ENTER("mysqlnd_result_init");
1118
1120 if (!pool) {
1122 }
1123
1124 ret = pool->get_chunk(pool, alloc_size);
1125 memset(ret, 0, alloc_size);
1126
1127 ret->row_data = pool->get_chunk(pool, field_count * sizeof(zval));
1128 ret->free_row_data = 0;
1129
1130 ret->memory_pool = pool;
1131 ret->field_count = field_count;
1133
1135
1136 DBG_RETURN(ret);
1137}
1138/* }}} */
1139
1140
1141/* {{{ mysqlnd_result_unbuffered_init */
1144{
1145 const size_t alloc_size = sizeof(MYSQLND_RES_UNBUFFERED) + mysqlnd_plugin_count() * sizeof(void *);
1146 MYSQLND_MEMORY_POOL * pool = result->memory_pool;
1148
1149 DBG_ENTER("mysqlnd_result_unbuffered_init");
1150
1151 ret = pool->get_chunk(pool, alloc_size);
1152 memset(ret, 0, alloc_size);
1153
1154 ret->result_set_memory_pool = pool;
1155 ret->field_count = field_count;
1156 ret->stmt = stmt;
1157
1159
1160 if (stmt) {
1162 ret->m.fetch_lengths = NULL; /* makes no sense */
1163 ret->lengths = NULL;
1164 } else {
1166
1167 ret->lengths = pool->get_chunk(pool, field_count * sizeof(size_t));
1168 memset(ret->lengths, 0, field_count * sizeof(size_t));
1169 }
1170
1171 DBG_RETURN(ret);
1172}
1173/* }}} */
1174
1175
1176/* {{{ mysqlnd_result_buffered_init */
1179{
1180 const size_t alloc_size = sizeof(MYSQLND_RES_BUFFERED) + mysqlnd_plugin_count() * sizeof(void *);
1181 MYSQLND_MEMORY_POOL * pool = result->memory_pool;
1183
1184 DBG_ENTER("mysqlnd_result_buffered_init");
1185
1186 ret = pool->get_chunk(pool, alloc_size);
1187 memset(ret, 0, alloc_size);
1188
1189 mysqlnd_error_info_init(&ret->error_info, /* persistent */ 0);
1190
1191 ret->result_set_memory_pool = pool;
1192 ret->field_count= field_count;
1193 ret->stmt = stmt;
1195
1196 if (stmt) {
1198 ret->m.fetch_lengths = NULL; /* makes no sense */
1199 ret->lengths = NULL;
1200 } else {
1202
1203 ret->lengths = pool->get_chunk(pool, field_count * sizeof(size_t));
1204 memset(ret->lengths, 0, field_count * sizeof(size_t));
1205 }
1206
1207 DBG_RETURN(ret);
1208}
1209/* }}} */
char s[4]
Definition cdf.c:77
memset(ptr, 0, type->size)
#define SIZE_MAX
Definition funcs.c:51
#define TRUE
Definition gd_gd.c:7
#define FALSE
Definition gd_gd.c:8
#define NULL
Definition gdcache.h:45
#define PASS(tables)
Definition hash_gost.c:193
PHPAPI ZEND_COLD void php_error_docref(const char *docref, int type, const char *format,...)
Definition main.c:1173
#define MYSQLND_INC_GLOBAL_STATISTIC(statistic)
Definition mysqlnd.h:255
PHPAPI unsigned int mysqlnd_plugin_count(void)
#define MYSQLND_INC_CONN_STATISTIC(conn_stats, statistic)
Definition mysqlnd.h:264
zend_long mempool_default_size
Definition mysqlnd.h:306
#define MYSQLND_G(v)
#define MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn_stats, statistic, value)
Definition mysqlnd.h:268
#define MYSQLND_METHOD(class, method)
Definition mysqlnd.h:294
#define mnd_efree(ptr)
#define mnd_erealloc(ptr, new_size)
#define mnd_emalloc(size)
PHPAPI void mysqlnd_mempool_destroy(MYSQLND_MEMORY_POOL *pool)
PHPAPI void mysqlnd_mempool_restore_state(MYSQLND_MEMORY_POOL *pool)
PHPAPI void mysqlnd_mempool_save_state(MYSQLND_MEMORY_POOL *pool)
PHPAPI MYSQLND_MEMORY_POOL * mysqlnd_mempool_create(size_t arena_size)
PHPAPI void mysqlnd_error_info_init(MYSQLND_ERROR_INFO *const info, const bool persistent)
PHPAPI void mysqlnd_error_info_free_contents(MYSQLND_ERROR_INFO *const info)
mysqlnd_result_init
PHPAPI const char *const mysqlnd_out_of_sync
mysqlnd_query_read_result_set_header
#define SET_CONNECTION_STATE(state_struct, s)
#define UPSERT_STATUS_GET_SERVER_STATUS(status)
#define UPSERT_STATUS_GET_WARNINGS(status)
#define UPSERT_STATUS_SET_WARNINGS(status, warnings)
#define UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(status)
#define GET_CONNECTION_STATE(state_struct)
#define UPSERT_STATUS_SET_LAST_INSERT_ID(status, id)
#define UPSERT_STATUS_RESET(status)
#define UPSERT_STATUS_SET_AFFECTED_ROWS(status, rows)
#define UPSERT_STATUS_SET_SERVER_STATUS(status, server_st)
#define DBG_VOID_RETURN
#define DBG_RETURN(value)
@ CONN_READY
@ CONN_QUIT_SENT
@ CONN_NEXT_RESULT_PENDING
@ CONN_SENDING_LOAD_DATA
@ CONN_FETCHING_DATA
@ QUERY_SELECT
@ QUERY_LOAD_LOCAL
@ QUERY_UPSERT
#define CR_SERVER_GONE_ERROR
#define SERVER_QUERY_NO_INDEX_USED
#define SERVER_QUERY_NO_GOOD_INDEX_USED
@ STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF
@ STAT_FREE_RESULT_EXPLICIT
@ STAT_NO_INDEX_USED
@ STAT_FLUSHED_NORMAL_SETS
@ STAT_NON_RSET_QUERY
@ STAT_QUERY_WAS_SLOW
@ STAT_FLUSHED_PS_SETS
@ STAT_ROWS_BUFFERED_FROM_CLIENT_NORMAL
@ STAT_ROWS_BUFFERED_FROM_CLIENT_PS
@ STAT_BAD_INDEX_USED
@ STAT_FREE_RESULT_IMPLICIT
@ STAT_ROWS_FETCHED_FROM_CLIENT_PS_UNBUF
@ STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF
@ STAT_ROWS_SKIPPED_NORMAL
@ STAT_RSET_QUERY
@ STAT_LAST
@ STAT_ROWS_FETCHED_FROM_CLIENT_PS_BUF
@ STAT_ROWS_SKIPPED_PS
#define SERVER_QUERY_WAS_SLOW
enum mysqlnd_collected_stats enum_mysqlnd_collected_stats
@ MYSQLND_RES_NORMAL
@ MYSQLND_RES_PS_UNBUF
@ MYSQLND_STMT_INITTED
#define CR_COMMANDS_OUT_OF_SYNC
@ MYSQLND_FETCH_ASSOC
@ MYSQLND_FETCH_NUM
#define SERVER_MORE_RESULTS_EXISTS
enum func_status enum_func_status
#define UNKNOWN_SQLSTATE
#define CR_CLIENT_INTERACTION_TIMEOUT
#define mysqlnd_result_buffered_get_methods()
#define mysqlnd_result_unbuffered_get_methods()
#define mysqlnd_result_get_methods()
enum_func_status mysqlnd_handle_local_infile(MYSQLND_CONN_DATA *conn, const char *const filename, bool *is_warning)
PHPAPI MYSQLND_RES_BUFFERED * mysqlnd_result_buffered_init(MYSQLND_RES *result, const unsigned int field_count, MYSQLND_STMT_DATA *stmt)
PHPAPI MYSQLND_RES_UNBUFFERED * mysqlnd_result_unbuffered_init(MYSQLND_RES *result, const unsigned int field_count, MYSQLND_STMT_DATA *stmt)
mysqlnd_result_meta_init
struct st_mysqlnd_result_metadata MYSQLND_RES_METADATA
char ** MYSQLND_ROW_C
struct st_mysqlnd_field MYSQLND_FIELD
#define MYSQLND_CLASS_METHODS_END
struct st_mysqlnd_connection_data MYSQLND_CONN_DATA
struct st_mysqlnd_memory_pool MYSQLND_MEMORY_POOL
struct st_mysqlnd_stats MYSQLND_STATS
unsigned int MYSQLND_FIELD_OFFSET
struct st_mysqlnd_buffered_result MYSQLND_RES_BUFFERED
struct st_mysqlnd_unbuffered_result MYSQLND_RES_UNBUFFERED
#define SET_EMPTY_ERROR(info)
struct st_mysqlnd_stmt_data MYSQLND_STMT_DATA
#define COPY_CLIENT_ERROR(dest, source)
struct st_mysqlnd_stmt MYSQLND_STMT
#define SET_OOM_ERROR(info)
#define SET_CLIENT_ERROR(info, err_no, sqlstate, error)
struct st_mysqlnd_row_buffer MYSQLND_ROW_BUFFER
enum_func_status(* mysqlnd_fetch_row_func)(MYSQLND_RES *result, zval **row, const unsigned int flags, bool *fetched_anything)
#define MYSQLND_CLASS_METHODS_START(class)
struct st_mysqlnd_res MYSQLND_RES
enum_func_status php_mysqlnd_rowp_read_text_protocol(MYSQLND_ROW_BUFFER *row_buffer, zval *fields, unsigned int field_count, const MYSQLND_FIELD *fields_metadata, bool as_int_or_float, MYSQLND_STATS *stats)
enum_func_status php_mysqlnd_rowp_read_binary_protocol(MYSQLND_ROW_BUFFER *row_buffer, zval *fields, const unsigned int field_count, const MYSQLND_FIELD *const fields_metadata, const bool as_int_or_float, MYSQLND_STATS *const stats)
struct st_mysqlnd_packet_eof MYSQLND_PACKET_EOF
#define PACKET_FREE(packet)
struct st_mysqlnd_packet_rset_header MYSQLND_PACKET_RSET_HEADER
#define MYSQLND_NULL_LENGTH
#define PACKET_READ(conn, packet)
struct st_mysqlnd_packet_row MYSQLND_PACKET_ROW
#define PHPAPI
Definition php.h:71
unsigned const char * end
Definition php_ffi.h:51
zend_constant * data
#define FAIL(...)
MYSQLND_ROW_BUFFER * row_buffers
MYSQLND_ERROR_INFO error_info
MYSQLND_SESSION_OPTIONS * options
MYSQLND_UPSERT_STATUS * upsert_status
enum_mysqlnd_query_type last_query_type
MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * payload_decoder_factory
MYSQLND_ERROR_INFO * error_info
MYSQLND_CONNECTION_STATE state
char error[MYSQLND_ERRMSG_SIZE+1]
zend_string * sname
void *(* get_chunk)(MYSQLND_MEMORY_POOL *pool, size_t size)
MYSQLND_ROW_BUFFER row_buffer
MYSQLND_ERROR_INFO error_info
MYSQLND_MEMORY_POOL * result_set_memory_pool
unsigned int field_count
enum_mysqlnd_stmt_state state
MYSQLND_ERROR_INFO * error_info
MYSQLND_ROW_BUFFER last_row_buffer
#define array_init_size(arg, size)
Definition zend_API.h:538
#define RETVAL_NULL()
Definition zend_API.h:1010
#define RETVAL_FALSE
Definition zend_API.h:1032
struct _zval_struct zval
#define E_WARNING
Definition zend_errors.h:24
ZEND_API zval *ZEND_FASTCALL zend_hash_index_add(HashTable *ht, zend_ulong h, zval *pData)
Definition zend_hash.c:1209
ZEND_API zval *ZEND_FASTCALL zend_hash_index_update(HashTable *ht, zend_ulong h, zval *pData)
Definition zend_hash.c:1219
ZEND_API zval *ZEND_FASTCALL zend_hash_update(HashTable *ht, zend_string *key, zval *pData)
Definition zend_hash.c:997
#define ZEND_ULONG_FMT
Definition zend_long.h:88
#define convert_to_string(op)
#define ZEND_FILE_LINE_DC
#define ZEND_ASSERT(c)
#define Z_TYPE_P(zval_p)
Definition zend_types.h:660
#define Z_TRY_ADDREF_P(pz)
#define Z_STRVAL_P(zval_p)
Definition zend_types.h:975
#define Z_ARRVAL_P(zval_p)
Definition zend_types.h:987
#define IS_STRING
Definition zend_types.h:606
struct _zend_array HashTable
Definition zend_types.h:386
#define Z_STRLEN_P(zval_p)
Definition zend_types.h:978
#define IS_NULL
Definition zend_types.h:601
zval * return_value
bool result
zval * ret