xref: /PHP-8.2/ext/pdo_mysql/mysql_statement.c (revision d4accd8b)
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 "pdo/php_pdo.h"
27 #include "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 = &param->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 = &param->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 = &param->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 = &param->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