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