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 | Author: George Schlossnagle <george@omniti.com> |
14 | Wez Furlong <wez@php.net> |
15 | Johannes Schlueter <johannes@mysql.com> |
16 +----------------------------------------------------------------------+
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include "php.h"
24 #include "php_ini.h"
25 #include "ext/standard/info.h"
26 #include "ext/pdo/php_pdo.h"
27 #include "ext/pdo/php_pdo_driver.h"
28 #include "php_pdo_mysql.h"
29 #include "php_pdo_mysql_int.h"
30
31 #ifdef PDO_USE_MYSQLND
32 # define pdo_mysql_stmt_execute_prepared(stmt) pdo_mysql_stmt_execute_prepared_mysqlnd(stmt)
33 #else
34 # define pdo_mysql_stmt_execute_prepared(stmt) pdo_mysql_stmt_execute_prepared_libmysql(stmt)
35 #endif
36
pdo_mysql_free_result(pdo_mysql_stmt * S)37 static void pdo_mysql_free_result(pdo_mysql_stmt *S)
38 {
39 if (S->result) {
40 #ifndef PDO_USE_MYSQLND
41 if (S->bound_result) {
42 /* We can't use stmt->column_count here, because it gets reset before the
43 * next_rowset handler is called. */
44 unsigned column_count = mysql_num_fields(S->result);
45 for (unsigned i = 0; i < column_count; i++) {
46 efree(S->bound_result[i].buffer);
47 }
48
49 efree(S->bound_result);
50 efree(S->out_null);
51 efree(S->out_length);
52 S->bound_result = NULL;
53 }
54 #else
55 if (S->current_row) {
56 unsigned column_count = mysql_num_fields(S->result);
57 for (unsigned i = 0; i < column_count; i++) {
58 zval_ptr_dtor_nogc(&S->current_row[i]);
59 }
60 efree(S->current_row);
61 S->current_row = NULL;
62 }
63 #endif
64 mysql_free_result(S->result);
65 S->result = NULL;
66 }
67 }
68
pdo_mysql_stmt_dtor(pdo_stmt_t * stmt)69 static int pdo_mysql_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */
70 {
71 pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data;
72
73 PDO_DBG_ENTER("pdo_mysql_stmt_dtor");
74 PDO_DBG_INF_FMT("stmt=%p", S->stmt);
75
76 pdo_mysql_free_result(S);
77 if (S->einfo.errmsg) {
78 pefree(S->einfo.errmsg, stmt->dbh->is_persistent);
79 S->einfo.errmsg = NULL;
80 }
81 if (S->stmt) {
82 mysql_stmt_close(S->stmt);
83 S->stmt = NULL;
84 }
85
86 #ifndef PDO_USE_MYSQLND
87 if (S->params) {
88 efree(S->params);
89 }
90 if (S->in_null) {
91 efree(S->in_null);
92 }
93 if (S->in_length) {
94 efree(S->in_length);
95 }
96 #endif
97
98 if (!S->done && !Z_ISUNDEF(stmt->database_object_handle)
99 && IS_OBJ_VALID(EG(objects_store).object_buckets[Z_OBJ_HANDLE(stmt->database_object_handle)])
100 && (!(OBJ_FLAGS(Z_OBJ(stmt->database_object_handle)) & IS_OBJ_FREE_CALLED))) {
101 while (mysql_more_results(S->H->server)) {
102 MYSQL_RES *res;
103 if (mysql_next_result(S->H->server) != 0) {
104 break;
105 }
106
107 res = mysql_store_result(S->H->server);
108 if (res) {
109 mysql_free_result(res);
110 }
111 }
112 }
113
114 efree(S);
115 PDO_DBG_RETURN(1);
116 }
117 /* }}} */
118
pdo_mysql_stmt_set_row_count(pdo_stmt_t * stmt)119 static void pdo_mysql_stmt_set_row_count(pdo_stmt_t *stmt) /* {{{ */
120 {
121 pdo_mysql_stmt *S = stmt->driver_data;
122 zend_long row_count = (zend_long) mysql_stmt_affected_rows(S->stmt);
123 if (row_count != (zend_long)-1) {
124 stmt->row_count = row_count;
125 }
126 }
127 /* }}} */
128
pdo_mysql_fill_stmt_from_result(pdo_stmt_t * stmt)129 static int pdo_mysql_fill_stmt_from_result(pdo_stmt_t *stmt) /* {{{ */
130 {
131 pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data;
132 pdo_mysql_db_handle *H = S->H;
133 my_ulonglong row_count;
134 PDO_DBG_ENTER("pdo_mysql_fill_stmt_from_result");
135
136 row_count = mysql_affected_rows(H->server);
137 if (row_count == (my_ulonglong)-1) {
138 /* we either have a query that returned a result set or an error occurred
139 lets see if we have access to a result set */
140 if (!H->buffered) {
141 S->result = mysql_use_result(H->server);
142 } else {
143 S->result = mysql_store_result(H->server);
144 }
145 if (NULL == S->result) {
146 pdo_mysql_error_stmt(stmt);
147 PDO_DBG_RETURN(0);
148 }
149
150 stmt->row_count = (zend_long) mysql_num_rows(S->result);
151 php_pdo_stmt_set_column_count(stmt, (int) mysql_num_fields(S->result));
152 S->fields = mysql_fetch_fields(S->result);
153 } else {
154 /* this was a DML or DDL query (INSERT, UPDATE, DELETE, ... */
155 stmt->row_count = (zend_long) row_count;
156 }
157
158 PDO_DBG_RETURN(1);
159 }
160 /* }}} */
161
pdo_mysql_stmt_after_execute_prepared(pdo_stmt_t * stmt)162 static bool pdo_mysql_stmt_after_execute_prepared(pdo_stmt_t *stmt) {
163 pdo_mysql_stmt *S = stmt->driver_data;
164 pdo_mysql_db_handle *H = S->H;
165
166 #ifdef PDO_USE_MYSQLND
167 /* For SHOW/DESCRIBE and others the column/field count is not available before execute. */
168 php_pdo_stmt_set_column_count(stmt, mysql_stmt_field_count(S->stmt));
169 for (int i = 0; i < stmt->column_count; i++) {
170 mysqlnd_stmt_bind_one_result(S->stmt, i);
171 }
172
173 S->result = mysqlnd_stmt_result_metadata(S->stmt);
174 if (S->result) {
175 S->fields = mysql_fetch_fields(S->result);
176 /* If buffered, pre-fetch all the data */
177 if (H->buffered) {
178 if (mysql_stmt_store_result(S->stmt)) {
179 pdo_mysql_error_stmt(stmt);
180 return false;
181 }
182 }
183 }
184 #else
185 /* figure out the result set format, if any */
186 S->result = mysql_stmt_result_metadata(S->stmt);
187 if (S->result) {
188 int calc_max_length = H->buffered && S->max_length == 1;
189 S->fields = mysql_fetch_fields(S->result);
190
191 php_pdo_stmt_set_column_count(stmt, (int)mysql_num_fields(S->result));
192 S->bound_result = ecalloc(stmt->column_count, sizeof(MYSQL_BIND));
193 S->out_null = ecalloc(stmt->column_count, sizeof(my_bool));
194 S->out_length = ecalloc(stmt->column_count, sizeof(zend_ulong));
195
196 /* summon memory to hold the row */
197 for (int i = 0; i < stmt->column_count; i++) {
198 if (calc_max_length && S->fields[i].type == FIELD_TYPE_BLOB) {
199 my_bool on = 1;
200 mysql_stmt_attr_set(S->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &on);
201 calc_max_length = 0;
202 }
203 switch (S->fields[i].type) {
204 case FIELD_TYPE_INT24:
205 S->bound_result[i].buffer_length = MAX_MEDIUMINT_WIDTH + 1;
206 break;
207 case FIELD_TYPE_LONG:
208 S->bound_result[i].buffer_length = MAX_INT_WIDTH + 1;
209 break;
210 case FIELD_TYPE_LONGLONG:
211 S->bound_result[i].buffer_length = MAX_BIGINT_WIDTH + 1;
212 break;
213 case FIELD_TYPE_TINY:
214 S->bound_result[i].buffer_length = MAX_TINYINT_WIDTH + 1;
215 break;
216 case FIELD_TYPE_SHORT:
217 S->bound_result[i].buffer_length = MAX_SMALLINT_WIDTH + 1;
218 break;
219 default:
220 S->bound_result[i].buffer_length =
221 S->fields[i].max_length? S->fields[i].max_length:
222 S->fields[i].length;
223 /* work-around for longtext and alike */
224 if (S->bound_result[i].buffer_length > H->max_buffer_size) {
225 S->bound_result[i].buffer_length = H->max_buffer_size;
226 }
227 }
228
229 /* there are cases where the length reported by mysql is too short.
230 * eg: when describing a table that contains an enum column. Since
231 * we have no way of knowing the true length either, we'll bump up
232 * our buffer size to a reasonable size, just in case */
233 if (S->fields[i].max_length == 0 && S->bound_result[i].buffer_length < 128 && MYSQL_TYPE_VAR_STRING) {
234 S->bound_result[i].buffer_length = 128;
235 }
236
237 S->out_length[i] = 0;
238
239 S->bound_result[i].buffer = emalloc(S->bound_result[i].buffer_length);
240 S->bound_result[i].is_null = &S->out_null[i];
241 S->bound_result[i].length = &S->out_length[i];
242 S->bound_result[i].buffer_type = MYSQL_TYPE_STRING;
243 }
244
245 if (mysql_stmt_bind_result(S->stmt, S->bound_result)) {
246 pdo_mysql_error_stmt(stmt);
247 PDO_DBG_RETURN(0);
248 }
249
250 /* if buffered, pre-fetch all the data */
251 if (H->buffered) {
252 if (mysql_stmt_store_result(S->stmt)) {
253 pdo_mysql_error_stmt(stmt);
254 PDO_DBG_RETURN(0);
255 }
256 }
257 }
258 #endif
259
260 pdo_mysql_stmt_set_row_count(stmt);
261 return true;
262 }
263
264 #ifndef PDO_USE_MYSQLND
pdo_mysql_stmt_execute_prepared_libmysql(pdo_stmt_t * stmt)265 static int pdo_mysql_stmt_execute_prepared_libmysql(pdo_stmt_t *stmt) /* {{{ */
266 {
267 pdo_mysql_stmt *S = stmt->driver_data;
268
269 PDO_DBG_ENTER("pdo_mysql_stmt_execute_prepared_libmysql");
270
271 /* (re)bind the parameters */
272 if (mysql_stmt_bind_param(S->stmt, S->params) || mysql_stmt_execute(S->stmt)) {
273 if (S->params) {
274 memset(S->params, 0, S->num_params * sizeof(MYSQL_BIND));
275 }
276 pdo_mysql_error_stmt(stmt);
277 if (mysql_stmt_errno(S->stmt) == 2057) {
278 /* CR_NEW_STMT_METADATA makes the statement unusable */
279 S->stmt = NULL;
280 }
281 PDO_DBG_RETURN(0);
282 }
283
284 PDO_DBG_RETURN(pdo_mysql_stmt_after_execute_prepared(stmt));
285 }
286 /* }}} */
287 #endif
288
289 #ifdef PDO_USE_MYSQLND
pdo_mysql_stmt_execute_prepared_mysqlnd(pdo_stmt_t * stmt)290 static int pdo_mysql_stmt_execute_prepared_mysqlnd(pdo_stmt_t *stmt) /* {{{ */
291 {
292 pdo_mysql_stmt *S = stmt->driver_data;
293
294 PDO_DBG_ENTER("pdo_mysql_stmt_execute_prepared_mysqlnd");
295
296 if (mysql_stmt_execute(S->stmt)) {
297 pdo_mysql_error_stmt(stmt);
298 PDO_DBG_RETURN(0);
299 }
300
301 PDO_DBG_RETURN(pdo_mysql_stmt_after_execute_prepared(stmt));
302 }
303 /* }}} */
304 #endif
305
pdo_mysql_stmt_execute(pdo_stmt_t * stmt)306 static int pdo_mysql_stmt_execute(pdo_stmt_t *stmt) /* {{{ */
307 {
308 pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data;
309 pdo_mysql_db_handle *H = S->H;
310 PDO_DBG_ENTER("pdo_mysql_stmt_execute");
311 PDO_DBG_INF_FMT("stmt=%p", S->stmt);
312
313 /* ensure that we free any previous unfetched results */
314 pdo_mysql_free_result(S);
315 S->done = 0;
316
317 if (S->stmt) {
318 uint32_t num_bound_params =
319 stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0;
320 if (num_bound_params < (uint32_t) S->num_params) {
321 /* too few parameter bound */
322 PDO_DBG_ERR("too few parameters bound");
323 strcpy(stmt->error_code, "HY093");
324 PDO_DBG_RETURN(0);
325 }
326
327 PDO_DBG_RETURN(pdo_mysql_stmt_execute_prepared(stmt));
328 }
329
330 if (mysql_real_query(H->server, ZSTR_VAL(stmt->active_query_string), ZSTR_LEN(stmt->active_query_string)) != 0) {
331 pdo_mysql_error_stmt(stmt);
332 PDO_DBG_RETURN(0);
333 }
334
335 PDO_DBG_RETURN(pdo_mysql_fill_stmt_from_result(stmt));
336 }
337 /* }}} */
338
pdo_mysql_stmt_next_rowset(pdo_stmt_t * stmt)339 static int pdo_mysql_stmt_next_rowset(pdo_stmt_t *stmt) /* {{{ */
340 {
341 pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data;
342 pdo_mysql_db_handle *H = S->H;
343 PDO_DBG_ENTER("pdo_mysql_stmt_next_rowset");
344 PDO_DBG_INF_FMT("stmt=%p", S->stmt);
345
346 /* ensure that we free any previous unfetched results */
347 pdo_mysql_free_result(S);
348
349 if (S->stmt) {
350 mysql_stmt_free_result(S->stmt);
351 if (mysql_stmt_next_result(S->stmt)) {
352 pdo_mysql_error_stmt(stmt);
353 S->done = 1;
354 PDO_DBG_RETURN(0);
355 }
356 PDO_DBG_RETURN(pdo_mysql_stmt_after_execute_prepared(stmt));
357 } else {
358 if (mysql_next_result(H->server)) {
359 pdo_mysql_error_stmt(stmt);
360 S->done = 1;
361 PDO_DBG_RETURN(0);
362 }
363 PDO_DBG_RETURN(pdo_mysql_fill_stmt_from_result(stmt));
364 }
365 }
366 /* }}} */
367
368
369 static const char * const pdo_param_event_names[] =
370 {
371 "PDO_PARAM_EVT_ALLOC",
372 "PDO_PARAM_EVT_FREE",
373 "PDO_PARAM_EVT_EXEC_PRE",
374 "PDO_PARAM_EVT_EXEC_POST",
375 "PDO_PARAM_EVT_FETCH_PRE",
376 "PDO_PARAM_EVT_FETCH_POST",
377 "PDO_PARAM_EVT_NORMALIZE",
378 };
379
380 #ifndef PDO_USE_MYSQLND
381 static unsigned char libmysql_false_buffer = 0;
382 static unsigned char libmysql_true_buffer = 1;
383 #endif
384
pdo_mysql_stmt_param_hook(pdo_stmt_t * stmt,struct pdo_bound_param_data * param,enum pdo_param_event event_type)385 static int pdo_mysql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, enum pdo_param_event event_type) /* {{{ */
386 {
387 zval *parameter;
388 #ifndef PDO_USE_MYSQLND
389 PDO_MYSQL_PARAM_BIND *b;
390 #endif
391 pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data;
392
393 PDO_DBG_ENTER("pdo_mysql_stmt_param_hook");
394 PDO_DBG_INF_FMT("stmt=%p", S->stmt);
395 PDO_DBG_INF_FMT("event = %s", pdo_param_event_names[event_type]);
396 if (S->stmt && param->is_param) {
397 switch (event_type) {
398 case PDO_PARAM_EVT_ALLOC:
399 /* sanity check parameter number range */
400 if (param->paramno < 0 || param->paramno >= S->num_params) {
401 strcpy(stmt->error_code, "HY093");
402 PDO_DBG_RETURN(0);
403 }
404
405 #ifndef PDO_USE_MYSQLND
406 b = &S->params[param->paramno];
407 param->driver_data = b;
408 b->is_null = &S->in_null[param->paramno];
409 b->length = &S->in_length[param->paramno];
410 /* recall how many parameters have been provided */
411 #endif
412 PDO_DBG_RETURN(1);
413
414 case PDO_PARAM_EVT_EXEC_PRE:
415 if (!Z_ISREF(param->parameter)) {
416 parameter = ¶m->parameter;
417 } else {
418 parameter = Z_REFVAL(param->parameter);
419 }
420
421 #ifdef PDO_USE_MYSQLND
422 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL || (Z_TYPE_P(parameter) == IS_NULL)) {
423 mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, parameter, MYSQL_TYPE_NULL);
424 PDO_DBG_RETURN(1);
425 }
426 #else
427 b = (PDO_MYSQL_PARAM_BIND*)param->driver_data;
428 *b->is_null = 0;
429 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL || Z_TYPE_P(parameter) == IS_NULL) {
430 *b->is_null = 1;
431 b->buffer_type = MYSQL_TYPE_STRING;
432 b->buffer = NULL;
433 b->buffer_length = 0;
434 *b->length = 0;
435 PDO_DBG_RETURN(1);
436 }
437 #endif /* PDO_USE_MYSQLND */
438
439 switch (PDO_PARAM_TYPE(param->param_type)) {
440 case PDO_PARAM_STMT:
441 PDO_DBG_RETURN(0);
442 case PDO_PARAM_LOB:
443 PDO_DBG_INF("PDO_PARAM_LOB");
444 if (!Z_ISREF(param->parameter)) {
445 parameter = ¶m->parameter;
446 } else {
447 parameter = Z_REFVAL(param->parameter);
448 }
449 if (Z_TYPE_P(parameter) == IS_RESOURCE) {
450 php_stream *stm = NULL;
451 php_stream_from_zval_no_verify(stm, parameter);
452 if (stm) {
453 zend_string *mem = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0);
454 zval_ptr_dtor(parameter);
455 ZVAL_STR(parameter, mem ? mem : ZSTR_EMPTY_ALLOC());
456 } else {
457 pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource");
458 return 0;
459 }
460 }
461 /* fall through */
462
463 default:
464 ;
465 }
466
467 #ifdef PDO_USE_MYSQLND
468 /* Is it really correct to check the zval's type? - But well, that's what the old code below does, too */
469 PDO_DBG_INF_FMT("param->parameter->type=%d", Z_TYPE(param->parameter));
470 if (!Z_ISREF(param->parameter)) {
471 parameter = ¶m->parameter;
472 } else {
473 parameter = Z_REFVAL(param->parameter);
474 }
475 switch (Z_TYPE_P(parameter)) {
476 case IS_STRING:
477 mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, parameter, MYSQL_TYPE_VAR_STRING);
478 break;
479 case IS_LONG:
480 #if SIZEOF_ZEND_LONG==8
481 mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, parameter, MYSQL_TYPE_LONGLONG);
482 #elif SIZEOF_ZEND_LONG==4
483 mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, parameter, MYSQL_TYPE_LONG);
484 #endif /* SIZEOF_LONG */
485 break;
486 case IS_TRUE:
487 case IS_FALSE:
488 mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, parameter, MYSQL_TYPE_TINY);
489 break;
490 case IS_DOUBLE:
491 mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, parameter, MYSQL_TYPE_DOUBLE);
492 break;
493 default:
494 PDO_DBG_RETURN(0);
495 }
496
497 PDO_DBG_RETURN(1);
498 #else
499 PDO_DBG_INF_FMT("param->parameter->type=%d", Z_TYPE(param->parameter));
500 if (!Z_ISREF(param->parameter)) {
501 parameter = ¶m->parameter;
502 } else {
503 parameter = Z_REFVAL(param->parameter);
504 }
505 switch (Z_TYPE_P(parameter)) {
506 case IS_STRING:
507 b->buffer_type = MYSQL_TYPE_STRING;
508 b->buffer = Z_STRVAL_P(parameter);
509 b->buffer_length = Z_STRLEN_P(parameter);
510 *b->length = Z_STRLEN_P(parameter);
511 PDO_DBG_RETURN(1);
512
513 case IS_FALSE:
514 b->buffer_type = MYSQL_TYPE_TINY;
515 b->buffer = &libmysql_false_buffer;
516 PDO_DBG_RETURN(1);
517
518 case IS_TRUE:
519 b->buffer_type = MYSQL_TYPE_TINY;
520 b->buffer = &libmysql_true_buffer;
521 PDO_DBG_RETURN(1);
522
523 case IS_LONG:
524 b->buffer_type = MYSQL_TYPE_LONG;
525 b->buffer = &Z_LVAL_P(parameter);
526 PDO_DBG_RETURN(1);
527
528 case IS_DOUBLE:
529 b->buffer_type = MYSQL_TYPE_DOUBLE;
530 b->buffer = &Z_DVAL_P(parameter);
531 PDO_DBG_RETURN(1);
532
533 default:
534 PDO_DBG_RETURN(0);
535 }
536 #endif /* PDO_USE_MYSQLND */
537 case PDO_PARAM_EVT_FREE:
538 case PDO_PARAM_EVT_EXEC_POST:
539 case PDO_PARAM_EVT_FETCH_PRE:
540 case PDO_PARAM_EVT_FETCH_POST:
541 case PDO_PARAM_EVT_NORMALIZE:
542 /* do nothing */
543 break;
544 }
545 }
546
547 PDO_DBG_RETURN(1);
548 }
549 /* }}} */
550
pdo_mysql_stmt_fetch(pdo_stmt_t * stmt,enum pdo_fetch_orientation ori,zend_long offset)551 static int pdo_mysql_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) /* {{{ */
552 {
553 pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data;
554
555 if (!S->result) {
556 PDO_DBG_RETURN(0);
557 }
558
559 #ifdef PDO_USE_MYSQLND
560 bool fetched_anything;
561
562 PDO_DBG_ENTER("pdo_mysql_stmt_fetch");
563 PDO_DBG_INF_FMT("stmt=%p", S->stmt);
564 if (S->stmt) {
565 if (FAIL == mysqlnd_stmt_fetch(S->stmt, &fetched_anything) || !fetched_anything) {
566 pdo_mysql_error_stmt(stmt);
567 PDO_DBG_RETURN(0);
568 }
569
570 PDO_DBG_RETURN(1);
571 }
572
573 zval *row_data;
574 if (mysqlnd_fetch_row_zval(S->result, &row_data, &fetched_anything) == FAIL) {
575 pdo_mysql_error_stmt(stmt);
576 PDO_DBG_RETURN(0);
577 }
578
579 if (!fetched_anything) {
580 PDO_DBG_RETURN(0);
581 }
582
583 if (!S->current_row) {
584 S->current_row = ecalloc(stmt->column_count, sizeof(zval));
585 }
586 for (unsigned i = 0; i < stmt->column_count; i++) {
587 zval_ptr_dtor_nogc(&S->current_row[i]);
588 ZVAL_COPY_VALUE(&S->current_row[i], &row_data[i]);
589 }
590 PDO_DBG_RETURN(1);
591 #else
592 int ret;
593
594 if (S->stmt) {
595 ret = mysql_stmt_fetch(S->stmt);
596
597 # ifdef MYSQL_DATA_TRUNCATED
598 if (ret == MYSQL_DATA_TRUNCATED) {
599 ret = 0;
600 }
601 # endif
602
603 if (ret) {
604 if (ret != MYSQL_NO_DATA) {
605 pdo_mysql_error_stmt(stmt);
606 }
607 PDO_DBG_RETURN(0);
608 }
609
610 PDO_DBG_RETURN(1);
611 }
612
613 if ((S->current_data = mysql_fetch_row(S->result)) == NULL) {
614 if (!S->H->buffered && mysql_errno(S->H->server)) {
615 pdo_mysql_error_stmt(stmt);
616 }
617 PDO_DBG_RETURN(0);
618 }
619
620 S->current_lengths = mysql_fetch_lengths(S->result);
621 PDO_DBG_RETURN(1);
622 #endif /* PDO_USE_MYSQLND */
623 }
624 /* }}} */
625
pdo_mysql_stmt_describe(pdo_stmt_t * stmt,int colno)626 static int pdo_mysql_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */
627 {
628 pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data;
629 struct pdo_column_data *cols = stmt->columns;
630 int i;
631
632 PDO_DBG_ENTER("pdo_mysql_stmt_describe");
633 PDO_DBG_INF_FMT("stmt=%p", S->stmt);
634 if (!S->result) {
635 PDO_DBG_RETURN(0);
636 }
637
638 if (colno >= stmt->column_count) {
639 /* error invalid column */
640 PDO_DBG_RETURN(0);
641 }
642
643 /* fetch all on demand, this seems easiest
644 ** if we've been here before bail out
645 */
646 if (cols[0].name) {
647 PDO_DBG_RETURN(1);
648 }
649 for (i = 0; i < stmt->column_count; i++) {
650
651 if (S->H->fetch_table_names) {
652 cols[i].name = strpprintf(0, "%s.%s", S->fields[i].table, S->fields[i].name);
653 } else {
654 #ifdef PDO_USE_MYSQLND
655 cols[i].name = zend_string_copy(S->fields[i].sname);
656 #else
657 cols[i].name = zend_string_init(S->fields[i].name, S->fields[i].name_length, 0);
658 #endif
659 }
660
661 cols[i].precision = S->fields[i].decimals;
662 cols[i].maxlen = S->fields[i].length;
663 }
664 PDO_DBG_RETURN(1);
665 }
666 /* }}} */
667
pdo_mysql_stmt_get_col(pdo_stmt_t * stmt,int colno,zval * result,enum pdo_param_type * type)668 static int pdo_mysql_stmt_get_col(
669 pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) /* {{{ */
670 {
671 pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data;
672
673 PDO_DBG_ENTER("pdo_mysql_stmt_get_col");
674 PDO_DBG_INF_FMT("stmt=%p", S->stmt);
675 if (!S->result) {
676 PDO_DBG_RETURN(0);
677 }
678
679 if (colno >= stmt->column_count) {
680 /* error invalid column */
681 PDO_DBG_RETURN(0);
682 }
683 #ifdef PDO_USE_MYSQLND
684 if (S->stmt) {
685 ZVAL_COPY(result, &S->stmt->data->result_bind[colno].zv);
686 } else {
687 ZVAL_COPY(result, &S->current_row[colno]);
688 }
689 PDO_DBG_RETURN(1);
690 #else
691 if (S->stmt) {
692 if (S->out_null[colno]) {
693 PDO_DBG_RETURN(1);
694 }
695
696 size_t length = S->out_length[colno];
697 if (length > S->bound_result[colno].buffer_length) {
698 /* mysql lied about the column width */
699 strcpy(stmt->error_code, "01004"); /* truncated */
700 length = S->out_length[colno] = S->bound_result[colno].buffer_length;
701 }
702 ZVAL_STRINGL_FAST(result, S->bound_result[colno].buffer, length);
703 PDO_DBG_RETURN(1);
704 }
705
706 if (S->current_data == NULL) {
707 PDO_DBG_RETURN(0);
708 }
709 if (S->current_data[colno]) {
710 ZVAL_STRINGL_FAST(result, S->current_data[colno], S->current_lengths[colno]);
711 }
712 PDO_DBG_RETURN(1);
713 #endif
714 } /* }}} */
715
type_to_name_native(int type)716 static char *type_to_name_native(int type) /* {{{ */
717 {
718 #define PDO_MYSQL_NATIVE_TYPE_NAME(x) case FIELD_TYPE_##x: return #x;
719
720 switch (type) {
721 PDO_MYSQL_NATIVE_TYPE_NAME(STRING)
722 PDO_MYSQL_NATIVE_TYPE_NAME(VAR_STRING)
723 #ifdef FIELD_TYPE_TINY
724 PDO_MYSQL_NATIVE_TYPE_NAME(TINY)
725 #endif
726 #ifdef FIELD_TYPE_BIT
727 PDO_MYSQL_NATIVE_TYPE_NAME(BIT)
728 #endif
729 PDO_MYSQL_NATIVE_TYPE_NAME(SHORT)
730 PDO_MYSQL_NATIVE_TYPE_NAME(LONG)
731 PDO_MYSQL_NATIVE_TYPE_NAME(LONGLONG)
732 PDO_MYSQL_NATIVE_TYPE_NAME(INT24)
733 PDO_MYSQL_NATIVE_TYPE_NAME(FLOAT)
734 PDO_MYSQL_NATIVE_TYPE_NAME(DOUBLE)
735 PDO_MYSQL_NATIVE_TYPE_NAME(DECIMAL)
736 #ifdef FIELD_TYPE_NEWDECIMAL
737 PDO_MYSQL_NATIVE_TYPE_NAME(NEWDECIMAL)
738 #endif
739 #ifdef FIELD_TYPE_GEOMETRY
740 PDO_MYSQL_NATIVE_TYPE_NAME(GEOMETRY)
741 #endif
742 PDO_MYSQL_NATIVE_TYPE_NAME(TIMESTAMP)
743 #ifdef FIELD_TYPE_YEAR
744 PDO_MYSQL_NATIVE_TYPE_NAME(YEAR)
745 #endif
746 PDO_MYSQL_NATIVE_TYPE_NAME(SET)
747 PDO_MYSQL_NATIVE_TYPE_NAME(ENUM)
748 PDO_MYSQL_NATIVE_TYPE_NAME(DATE)
749 #ifdef FIELD_TYPE_NEWDATE
750 PDO_MYSQL_NATIVE_TYPE_NAME(NEWDATE)
751 #endif
752 PDO_MYSQL_NATIVE_TYPE_NAME(TIME)
753 PDO_MYSQL_NATIVE_TYPE_NAME(DATETIME)
754 PDO_MYSQL_NATIVE_TYPE_NAME(TINY_BLOB)
755 PDO_MYSQL_NATIVE_TYPE_NAME(MEDIUM_BLOB)
756 PDO_MYSQL_NATIVE_TYPE_NAME(LONG_BLOB)
757 PDO_MYSQL_NATIVE_TYPE_NAME(BLOB)
758 PDO_MYSQL_NATIVE_TYPE_NAME(NULL)
759 default:
760 return NULL;
761 }
762 #undef PDO_MYSQL_NATIVE_TYPE_NAME
763 } /* }}} */
764
pdo_mysql_stmt_col_meta(pdo_stmt_t * stmt,zend_long colno,zval * return_value)765 static int pdo_mysql_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) /* {{{ */
766 {
767 pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data;
768 const MYSQL_FIELD *F;
769 zval flags;
770 char *str;
771
772 PDO_DBG_ENTER("pdo_mysql_stmt_col_meta");
773 PDO_DBG_INF_FMT("stmt=%p", S->stmt);
774 if (!S->result) {
775 PDO_DBG_RETURN(FAILURE);
776 }
777 if (colno >= stmt->column_count) {
778 /* error invalid column */
779 PDO_DBG_RETURN(FAILURE);
780 }
781
782 array_init(return_value);
783 array_init(&flags);
784
785 F = S->fields + colno;
786
787 if (F->def) {
788 add_assoc_string(return_value, "mysql:def", F->def);
789 }
790 if (IS_NOT_NULL(F->flags)) {
791 add_next_index_string(&flags, "not_null");
792 }
793 if (IS_PRI_KEY(F->flags)) {
794 add_next_index_string(&flags, "primary_key");
795 }
796 if (F->flags & MULTIPLE_KEY_FLAG) {
797 add_next_index_string(&flags, "multiple_key");
798 }
799 if (F->flags & UNIQUE_KEY_FLAG) {
800 add_next_index_string(&flags, "unique_key");
801 }
802 if (IS_BLOB(F->flags)) {
803 add_next_index_string(&flags, "blob");
804 }
805 str = type_to_name_native(F->type);
806 if (str) {
807 add_assoc_string(return_value, "native_type", str);
808 }
809
810 enum pdo_param_type param_type;
811 switch (F->type) {
812 case MYSQL_TYPE_BIT:
813 case MYSQL_TYPE_YEAR:
814 case MYSQL_TYPE_TINY:
815 case MYSQL_TYPE_SHORT:
816 case MYSQL_TYPE_INT24:
817 case MYSQL_TYPE_LONG:
818 #if SIZEOF_ZEND_LONG==8
819 case MYSQL_TYPE_LONGLONG:
820 #endif
821 param_type = PDO_PARAM_INT;
822 break;
823 default:
824 param_type = PDO_PARAM_STR;
825 break;
826 }
827 add_assoc_long(return_value, "pdo_type", param_type);
828
829 add_assoc_zval(return_value, "flags", &flags);
830 add_assoc_string(return_value, "table", (char *) (F->table?F->table : ""));
831
832 PDO_DBG_RETURN(SUCCESS);
833 } /* }}} */
834
pdo_mysql_stmt_cursor_closer(pdo_stmt_t * stmt)835 static int pdo_mysql_stmt_cursor_closer(pdo_stmt_t *stmt) /* {{{ */
836 {
837 pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data;
838
839 PDO_DBG_ENTER("pdo_mysql_stmt_cursor_closer");
840 PDO_DBG_INF_FMT("stmt=%p", S->stmt);
841
842 S->done = 1;
843 pdo_mysql_free_result(S);
844 if (S->stmt) {
845 mysql_stmt_free_result(S->stmt);
846 }
847
848 while (mysql_more_results(S->H->server)) {
849 MYSQL_RES *res;
850 if (mysql_next_result(S->H->server) != 0) {
851 pdo_mysql_error_stmt(stmt);
852 PDO_DBG_RETURN(0);
853 }
854 res = mysql_store_result(S->H->server);
855 if (res) {
856 mysql_free_result(res);
857 }
858 }
859 PDO_DBG_RETURN(1);
860 }
861 /* }}} */
862
863 const struct pdo_stmt_methods mysql_stmt_methods = {
864 pdo_mysql_stmt_dtor,
865 pdo_mysql_stmt_execute,
866 pdo_mysql_stmt_fetch,
867 pdo_mysql_stmt_describe,
868 pdo_mysql_stmt_get_col,
869 pdo_mysql_stmt_param_hook,
870 NULL, /* set_attr */
871 NULL, /* get_attr */
872 pdo_mysql_stmt_col_meta,
873 pdo_mysql_stmt_next_rowset,
874 pdo_mysql_stmt_cursor_closer
875 };
876