1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2018 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Author: Wez Furlong <wez@php.net>                                    |
16   |         Marcus Boerger <helly@php.net>                               |
17   |         Sterling Hughes <sterling@php.net>                           |
18   +----------------------------------------------------------------------+
19 */
20 
21 /* The PDO Statement Handle Class */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include "php.h"
28 #include "php_ini.h"
29 #include "ext/standard/info.h"
30 #include "ext/standard/php_var.h"
31 #include "php_pdo.h"
32 #include "php_pdo_driver.h"
33 #include "php_pdo_int.h"
34 #include "zend_exceptions.h"
35 #include "zend_interfaces.h"
36 #include "php_memory_streams.h"
37 
38 /* {{{ arginfo */
39 ZEND_BEGIN_ARG_INFO(arginfo_pdostatement__void, 0)
ZEND_END_ARG_INFO()40 ZEND_END_ARG_INFO()
41 
42 ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_execute, 0, 0, 0)
43 	ZEND_ARG_INFO(0, bound_input_params) /* array */
44 ZEND_END_ARG_INFO()
45 
46 ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_fetch, 0, 0, 0)
47 	ZEND_ARG_INFO(0, how)
48 	ZEND_ARG_INFO(0, orientation)
49 	ZEND_ARG_INFO(0, offset)
50 ZEND_END_ARG_INFO()
51 
52 ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_fetchobject, 0, 0, 0)
53 	ZEND_ARG_INFO(0, class_name)
54 	ZEND_ARG_INFO(0, ctor_args) /* array */
55 ZEND_END_ARG_INFO()
56 
57 ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_fetchcolumn, 0, 0, 0)
58 	ZEND_ARG_INFO(0, column_number)
59 ZEND_END_ARG_INFO()
60 
61 ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_fetchall, 0, 0, 0)
62 	ZEND_ARG_INFO(0, how)
63 	ZEND_ARG_INFO(0, class_name)
64 	ZEND_ARG_INFO(0, ctor_args) /* array */
65 ZEND_END_ARG_INFO()
66 
67 ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_bindvalue, 0, 0, 2)
68 	ZEND_ARG_INFO(0, paramno)
69 	ZEND_ARG_INFO(0, param)
70 	ZEND_ARG_INFO(0, type)
71 ZEND_END_ARG_INFO()
72 
73 ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_bindparam, 0, 0, 2)
74 	ZEND_ARG_INFO(0, paramno)
75 	ZEND_ARG_INFO(1, param)
76 	ZEND_ARG_INFO(0, type)
77 	ZEND_ARG_INFO(0, maxlen)
78 	ZEND_ARG_INFO(0, driverdata)
79 ZEND_END_ARG_INFO()
80 
81 ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_bindcolumn, 0, 0, 2)
82 	ZEND_ARG_INFO(0, column)
83 	ZEND_ARG_INFO(1, param)
84 	ZEND_ARG_INFO(0, type)
85 	ZEND_ARG_INFO(0, maxlen)
86 	ZEND_ARG_INFO(0, driverdata)
87 ZEND_END_ARG_INFO()
88 
89 ZEND_BEGIN_ARG_INFO(arginfo_pdostatement_setattribute, 0)
90 	ZEND_ARG_INFO(0, attribute)
91 	ZEND_ARG_INFO(0, value)
92 ZEND_END_ARG_INFO()
93 
94 ZEND_BEGIN_ARG_INFO(arginfo_pdostatement_getattribute, 0)
95 	ZEND_ARG_INFO(0, attribute)
96 ZEND_END_ARG_INFO()
97 
98 ZEND_BEGIN_ARG_INFO(arginfo_pdostatement_getcolumnmeta, 0)
99 	ZEND_ARG_INFO(0, column)
100 ZEND_END_ARG_INFO()
101 
102 ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_setfetchmode, 0, 0, 1)
103 	ZEND_ARG_INFO(0, mode)
104 	ZEND_ARG_INFO(0, params)
105 ZEND_END_ARG_INFO()
106 /* }}} */
107 
108 #define PHP_STMT_GET_OBJ	\
109   pdo_stmt_t *stmt = Z_PDO_STMT_P(getThis());	\
110   if (!stmt->dbh) {	\
111 	  RETURN_FALSE;	\
112   }	\
113 
114 static inline int rewrite_name_to_position(pdo_stmt_t *stmt, struct pdo_bound_param_data *param) /* {{{ */
115 {
116 	if (stmt->bound_param_map) {
117 		/* rewriting :name to ? style.
118 		 * We need to fixup the parameter numbers on the parameters.
119 		 * If we find that a given named parameter has been used twice,
120 		 * we will raise an error, as we can't be sure that it is safe
121 		 * to bind multiple parameters onto the same zval in the underlying
122 		 * driver */
123 		char *name;
124 		int position = 0;
125 
126 		if (stmt->named_rewrite_template) {
127 			/* this is not an error here */
128 			return 1;
129 		}
130 		if (!param->name) {
131 			/* do the reverse; map the parameter number to the name */
132 			if ((name = zend_hash_index_find_ptr(stmt->bound_param_map, param->paramno)) != NULL) {
133 				param->name = zend_string_init(name, strlen(name), 0);
134 				return 1;
135 			}
136 			pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined");
137 			return 0;
138 		}
139 
140 		ZEND_HASH_FOREACH_PTR(stmt->bound_param_map, name) {
141 			if (strncmp(name, ZSTR_VAL(param->name), ZSTR_LEN(param->name) + 1)) {
142 				position++;
143 				continue;
144 			}
145 			if (param->paramno >= 0) {
146 				pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "PDO refuses to handle repeating the same :named parameter for multiple positions with this driver, as it might be unsafe to do so.  Consider using a separate name for each parameter instead");
147 				return -1;
148 			}
149 			param->paramno = position;
150 			return 1;
151 		} ZEND_HASH_FOREACH_END();
152 		pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined");
153 		return 0;
154 	}
155 	return 1;
156 }
157 /* }}} */
158 
159 /* trigger callback hook for parameters */
dispatch_param_event(pdo_stmt_t * stmt,enum pdo_param_event event_type)160 static int dispatch_param_event(pdo_stmt_t *stmt, enum pdo_param_event event_type) /* {{{ */
161 {
162 	int ret = 1, is_param = 1;
163 	struct pdo_bound_param_data *param;
164 	HashTable *ht;
165 
166 	if (stmt->dbh->skip_param_evt & (1 << event_type)) {
167 		return 1;
168 	}
169 
170 	if (!stmt->methods->param_hook) {
171 		return 1;
172 	}
173 
174 	ht = stmt->bound_params;
175 
176 iterate:
177 	if (ht) {
178 		ZEND_HASH_FOREACH_PTR(ht, param) {
179 			if (!stmt->methods->param_hook(stmt, param, event_type)) {
180 				ret = 0;
181 				break;
182 			}
183 		} ZEND_HASH_FOREACH_END();
184 	}
185 	if (ret && is_param) {
186 		ht = stmt->bound_columns;
187 		is_param = 0;
188 		goto iterate;
189 	}
190 
191 	return ret;
192 }
193 /* }}} */
194 
pdo_stmt_describe_columns(pdo_stmt_t * stmt)195 int pdo_stmt_describe_columns(pdo_stmt_t *stmt) /* {{{ */
196 {
197 	int col;
198 
199 	stmt->columns = ecalloc(stmt->column_count, sizeof(struct pdo_column_data));
200 
201 	for (col = 0; col < stmt->column_count; col++) {
202 		if (!stmt->methods->describer(stmt, col)) {
203 			return 0;
204 		}
205 
206 		/* if we are applying case conversions on column names, do so now */
207 		if (stmt->dbh->native_case != stmt->dbh->desired_case && stmt->dbh->desired_case != PDO_CASE_NATURAL) {
208 			char *s = ZSTR_VAL(stmt->columns[col].name);
209 
210 			switch (stmt->dbh->desired_case) {
211 				case PDO_CASE_UPPER:
212 					while (*s != '\0') {
213 						*s = toupper(*s);
214 						s++;
215 					}
216 					break;
217 				case PDO_CASE_LOWER:
218 					while (*s != '\0') {
219 						*s = tolower(*s);
220 						s++;
221 					}
222 					break;
223 				default:
224 					;
225 			}
226 		}
227 
228 		/* update the column index on named bound parameters */
229 		if (stmt->bound_columns) {
230 			struct pdo_bound_param_data *param;
231 
232 			if ((param = zend_hash_find_ptr(stmt->bound_columns,
233 					stmt->columns[col].name)) != NULL) {
234 				param->paramno = col;
235 			}
236 		}
237 
238 	}
239 	return 1;
240 }
241 /* }}} */
242 
get_lazy_object(pdo_stmt_t * stmt,zval * return_value)243 static void get_lazy_object(pdo_stmt_t *stmt, zval *return_value) /* {{{ */
244 {
245 	if (Z_ISUNDEF(stmt->lazy_object_ref)) {
246 		pdo_row_t *row = ecalloc(1, sizeof(pdo_row_t));
247 		row->stmt = stmt;
248 		zend_object_std_init(&row->std, pdo_row_ce);
249 		ZVAL_OBJ(&stmt->lazy_object_ref, &row->std);
250 		row->std.handlers = &pdo_row_object_handlers;
251 		GC_ADDREF(&stmt->std);
252 		GC_DELREF(&row->std);
253 	}
254 	ZVAL_COPY(return_value, &stmt->lazy_object_ref);
255 }
256 /* }}} */
257 
param_dtor(zval * el)258 static void param_dtor(zval *el) /* {{{ */
259 {
260 	struct pdo_bound_param_data *param = (struct pdo_bound_param_data *)Z_PTR_P(el);
261 
262 	/* tell the driver that it is going away */
263 	if (param->stmt->methods->param_hook) {
264 		param->stmt->methods->param_hook(param->stmt, param, PDO_PARAM_EVT_FREE);
265 	}
266 
267 	if (param->name) {
268 		zend_string_release_ex(param->name, 0);
269 	}
270 
271 	if (!Z_ISUNDEF(param->parameter)) {
272 		zval_ptr_dtor(&param->parameter);
273 		ZVAL_UNDEF(&param->parameter);
274 	}
275 	if (!Z_ISUNDEF(param->driver_params)) {
276 		zval_ptr_dtor(&param->driver_params);
277 	}
278 	efree(param);
279 }
280 /* }}} */
281 
really_register_bound_param(struct pdo_bound_param_data * param,pdo_stmt_t * stmt,int is_param)282 static int really_register_bound_param(struct pdo_bound_param_data *param, pdo_stmt_t *stmt, int is_param) /* {{{ */
283 {
284 	HashTable *hash;
285 	zval *parameter;
286 	struct pdo_bound_param_data *pparam = NULL;
287 
288 	hash = is_param ? stmt->bound_params : stmt->bound_columns;
289 
290 	if (!hash) {
291 		ALLOC_HASHTABLE(hash);
292 		zend_hash_init(hash, 13, NULL, param_dtor, 0);
293 
294 		if (is_param) {
295 			stmt->bound_params = hash;
296 		} else {
297 			stmt->bound_columns = hash;
298 		}
299 	}
300 
301 	if (!Z_ISREF(param->parameter)) {
302 		parameter = &param->parameter;
303 	} else {
304 		parameter = Z_REFVAL(param->parameter);
305 	}
306 
307 	if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_STR && param->max_value_len <= 0 && !Z_ISNULL_P(parameter)) {
308 		if (Z_TYPE_P(parameter) == IS_DOUBLE) {
309 			char *p;
310 			int len = zend_spprintf_unchecked(&p, 0, "%.*H", (int) EG(precision), Z_DVAL_P(parameter));
311 			ZVAL_STRINGL(parameter, p, len);
312 			efree(p);
313 		} else {
314 			convert_to_string(parameter);
315 		}
316 	} else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_INT && (Z_TYPE_P(parameter) == IS_FALSE || Z_TYPE_P(parameter) == IS_TRUE)) {
317 		convert_to_long(parameter);
318 	} else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL && Z_TYPE_P(parameter) == IS_LONG) {
319 		convert_to_boolean(parameter);
320 	}
321 
322 	param->stmt = stmt;
323 	param->is_param = is_param;
324 
325 	if (Z_REFCOUNTED(param->driver_params)) {
326 		Z_ADDREF(param->driver_params);
327 	}
328 
329 	if (!is_param && param->name && stmt->columns) {
330 		/* try to map the name to the column */
331 		int i;
332 
333 		for (i = 0; i < stmt->column_count; i++) {
334 			if (ZSTR_LEN(stmt->columns[i].name) == ZSTR_LEN(param->name) &&
335 			    strncmp(ZSTR_VAL(stmt->columns[i].name), ZSTR_VAL(param->name), ZSTR_LEN(param->name) + 1) == 0) {
336 				param->paramno = i;
337 				break;
338 			}
339 		}
340 
341 		/* if you prepare and then execute passing an array of params keyed by names,
342 		 * then this will trigger, and we don't want that */
343 		if (param->paramno == -1) {
344 			char *tmp;
345 			spprintf(&tmp, 0, "Did not find column name '%s' in the defined columns; it will not be bound", ZSTR_VAL(param->name));
346 			pdo_raise_impl_error(stmt->dbh, stmt, "HY000", tmp);
347 			efree(tmp);
348 		}
349 	}
350 
351 	if (param->name) {
352 		if (is_param && ZSTR_VAL(param->name)[0] != ':') {
353 			zend_string *temp = zend_string_alloc(ZSTR_LEN(param->name) + 1, 0);
354 			ZSTR_VAL(temp)[0] = ':';
355 			memmove(ZSTR_VAL(temp) + 1, ZSTR_VAL(param->name), ZSTR_LEN(param->name) + 1);
356 			param->name = temp;
357 		} else {
358 			param->name = zend_string_init(ZSTR_VAL(param->name), ZSTR_LEN(param->name), 0);
359 		}
360 	}
361 
362 	if (is_param && !rewrite_name_to_position(stmt, param)) {
363 		if (param->name) {
364 			zend_string_release_ex(param->name, 0);
365 			param->name = NULL;
366 		}
367 		return 0;
368 	}
369 
370 	/* ask the driver to perform any normalization it needs on the
371 	 * parameter name.  Note that it is illegal for the driver to take
372 	 * a reference to param, as it resides in transient storage only
373 	 * at this time. */
374 	if (stmt->methods->param_hook) {
375 		if (!stmt->methods->param_hook(stmt, param, PDO_PARAM_EVT_NORMALIZE
376 				)) {
377 			if (param->name) {
378 				zend_string_release_ex(param->name, 0);
379 				param->name = NULL;
380 			}
381 			return 0;
382 		}
383 	}
384 
385 	/* delete any other parameter registered with this number.
386 	 * If the parameter is named, it will be removed and correctly
387 	 * disposed of by the hash_update call that follows */
388 	if (param->paramno >= 0) {
389 		zend_hash_index_del(hash, param->paramno);
390 	}
391 
392 	/* allocate storage for the parameter, keyed by its "canonical" name */
393 	if (param->name) {
394 		pparam = zend_hash_update_mem(hash, param->name, param, sizeof(struct pdo_bound_param_data));
395 	} else {
396 		pparam = zend_hash_index_update_mem(hash, param->paramno, param, sizeof(struct pdo_bound_param_data));
397 	}
398 
399 	/* tell the driver we just created a parameter */
400 	if (stmt->methods->param_hook) {
401 		if (!stmt->methods->param_hook(stmt, pparam, PDO_PARAM_EVT_ALLOC
402 					)) {
403 			/* undo storage allocation; the hash will free the parameter
404 			 * name if required */
405 			if (pparam->name) {
406 				zend_hash_del(hash, pparam->name);
407 			} else {
408 				zend_hash_index_del(hash, pparam->paramno);
409 			}
410 			/* param->parameter is freed by hash dtor */
411 			ZVAL_UNDEF(&param->parameter);
412 			return 0;
413 		}
414 	}
415 	return 1;
416 }
417 /* }}} */
418 
419 /* {{{ proto bool PDOStatement::execute([array $bound_input_params])
420    Execute a prepared statement, optionally binding parameters */
PHP_METHOD(PDOStatement,execute)421 static PHP_METHOD(PDOStatement, execute)
422 {
423 	zval *input_params = NULL;
424 	int ret = 1;
425 	PHP_STMT_GET_OBJ;
426 
427 	ZEND_PARSE_PARAMETERS_START(0, 1)
428 		Z_PARAM_OPTIONAL
429 		Z_PARAM_ARRAY_EX(input_params, 1, 0)
430 	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
431 
432 	PDO_STMT_CLEAR_ERR();
433 
434 	if (input_params) {
435 		struct pdo_bound_param_data param;
436 		zval *tmp;
437 		zend_string *key = NULL;
438 		zend_ulong num_index;
439 
440 		if (stmt->bound_params) {
441 			zend_hash_destroy(stmt->bound_params);
442 			FREE_HASHTABLE(stmt->bound_params);
443 			stmt->bound_params = NULL;
444 		}
445 
446 		ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input_params), num_index, key, tmp) {
447 			memset(&param, 0, sizeof(param));
448 
449 			if (key) {
450 				/* yes this is correct.  we don't want to count the null byte.  ask wez */
451 				param.name = key;
452 				param.paramno = -1;
453 			} else {
454 				/* we're okay to be zero based here */
455 				/* num_index is unsignend
456 				if (num_index < 0) {
457 					pdo_raise_impl_error(stmt->dbh, stmt, "HY093", NULL);
458 					RETURN_FALSE;
459 				}
460 				*/
461 				param.paramno = num_index;
462 			}
463 
464 			param.param_type = PDO_PARAM_STR;
465 			ZVAL_COPY(&param.parameter, tmp);
466 
467 			if (!really_register_bound_param(&param, stmt, 1)) {
468 				if (!Z_ISUNDEF(param.parameter)) {
469 					zval_ptr_dtor(&param.parameter);
470 				}
471 				RETURN_FALSE;
472 			}
473 		} ZEND_HASH_FOREACH_END();
474 	}
475 
476 	if (PDO_PLACEHOLDER_NONE == stmt->supports_placeholders) {
477 		/* handle the emulated parameter binding,
478          * stmt->active_query_string holds the query with binds expanded and
479 		 * quoted.
480          */
481 
482 		/* string is leftover from previous calls so PDOStatement::debugDumpParams() can access */
483 		if (stmt->active_query_string && stmt->active_query_string != stmt->query_string) {
484 			efree(stmt->active_query_string);
485 		}
486 		stmt->active_query_string = NULL;
487 
488 		ret = pdo_parse_params(stmt, stmt->query_string, stmt->query_stringlen,
489 			&stmt->active_query_string, &stmt->active_query_stringlen);
490 
491 		if (ret == 0) {
492 			/* no changes were made */
493 			stmt->active_query_string = stmt->query_string;
494 			stmt->active_query_stringlen = stmt->query_stringlen;
495 			ret = 1;
496 		} else if (ret == -1) {
497 			/* something broke */
498 			PDO_HANDLE_STMT_ERR();
499 			RETURN_FALSE;
500 		}
501 	} else if (!dispatch_param_event(stmt, PDO_PARAM_EVT_EXEC_PRE)) {
502 		PDO_HANDLE_STMT_ERR();
503 		RETURN_FALSE;
504 	}
505 	if (stmt->methods->executer(stmt)) {
506 		if (!stmt->executed) {
507 			/* this is the first execute */
508 
509 			if (stmt->dbh->alloc_own_columns && !stmt->columns) {
510 				/* for "big boy" drivers, we need to allocate memory to fetch
511 				 * the results into, so lets do that now */
512 				ret = pdo_stmt_describe_columns(stmt);
513 			}
514 
515 			stmt->executed = 1;
516 		}
517 
518 		if (ret && !dispatch_param_event(stmt, PDO_PARAM_EVT_EXEC_POST)) {
519 			RETURN_FALSE;
520 		}
521 
522 		RETURN_BOOL(ret);
523 	}
524 	PDO_HANDLE_STMT_ERR();
525 	RETURN_FALSE;
526 }
527 /* }}} */
528 
fetch_value(pdo_stmt_t * stmt,zval * dest,int colno,int * type_override)529 static inline void fetch_value(pdo_stmt_t *stmt, zval *dest, int colno, int *type_override) /* {{{ */
530 {
531 	struct pdo_column_data *col;
532 	char *value = NULL;
533 	size_t value_len = 0;
534 	int caller_frees = 0;
535 	int type, new_type;
536 
537 	if (colno < 0 || colno >= stmt->column_count) {
538 		pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Invalid column index");
539 		ZVAL_FALSE(dest);
540 
541 		return;
542 	}
543 
544 	col = &stmt->columns[colno];
545 	type = PDO_PARAM_TYPE(col->param_type);
546 	new_type =  type_override ? (int)PDO_PARAM_TYPE(*type_override) : type;
547 
548 	value = NULL;
549 	value_len = 0;
550 
551 	stmt->methods->get_col(stmt, colno, &value, &value_len, &caller_frees);
552 
553 	switch (type) {
554 		case PDO_PARAM_ZVAL:
555 			if (value && value_len == sizeof(zval)) {
556 				ZVAL_COPY_VALUE(dest, (zval *)value);
557 			} else {
558 				ZVAL_NULL(dest);
559 			}
560 
561 			if (Z_TYPE_P(dest) == IS_NULL) {
562 				type = new_type;
563 			}
564 			break;
565 
566 		case PDO_PARAM_INT:
567 			if (value && value_len == sizeof(zend_long)) {
568 				ZVAL_LONG(dest, *(zend_long*)value);
569 				break;
570 			}
571 			ZVAL_NULL(dest);
572 			break;
573 
574 		case PDO_PARAM_BOOL:
575 			if (value && value_len == sizeof(zend_bool)) {
576 				ZVAL_BOOL(dest, *(zend_bool*)value);
577 				break;
578 			}
579 			ZVAL_NULL(dest);
580 			break;
581 
582 		case PDO_PARAM_LOB:
583 			if (value == NULL) {
584 				ZVAL_NULL(dest);
585 			} else if (value_len == 0) {
586 				/* Warning, empty strings need to be passed as stream */
587 				if (stmt->dbh->stringify || new_type == PDO_PARAM_STR) {
588 					zend_string *buf;
589 					buf = php_stream_copy_to_mem((php_stream*)value, PHP_STREAM_COPY_ALL, 0);
590 					if (buf == NULL) {
591 						ZVAL_EMPTY_STRING(dest);
592 					} else {
593 						ZVAL_STR(dest, buf);
594 					}
595 					php_stream_close((php_stream*)value);
596 				} else {
597 					php_stream_to_zval((php_stream*)value, dest);
598 				}
599 			} else if (!stmt->dbh->stringify && new_type != PDO_PARAM_STR) {
600 				/* they gave us a string, but LOBs are represented as streams in PDO */
601 				php_stream *stm;
602 #ifdef TEMP_STREAM_TAKE_BUFFER
603 				if (caller_frees) {
604 					stm = php_stream_memory_open(TEMP_STREAM_TAKE_BUFFER, value, value_len);
605 					if (stm) {
606 						caller_frees = 0;
607 					}
608 				} else
609 #endif
610 				{
611 					stm = php_stream_memory_open(TEMP_STREAM_READONLY, value, value_len);
612 				}
613 				if (stm) {
614 					php_stream_to_zval(stm, dest);
615 				} else {
616 					ZVAL_NULL(dest);
617 				}
618 			} else {
619 				ZVAL_STRINGL(dest, value, value_len);
620 			}
621 			break;
622 
623 		case PDO_PARAM_STR:
624 			if (value && !(value_len == 0 && stmt->dbh->oracle_nulls == PDO_NULL_EMPTY_STRING)) {
625 				ZVAL_STRINGL(dest, value, value_len);
626 				break;
627 			}
628 		default:
629 			ZVAL_NULL(dest);
630 	}
631 
632 	if (type != new_type) {
633 		switch (new_type) {
634 			case PDO_PARAM_INT:
635 				convert_to_long_ex(dest);
636 				break;
637 			case PDO_PARAM_BOOL:
638 				convert_to_boolean_ex(dest);
639 				break;
640 			case PDO_PARAM_STR:
641 				convert_to_string_ex(dest);
642 				break;
643 			case PDO_PARAM_NULL:
644 				convert_to_null_ex(dest);
645 				break;
646 			default:
647 				;
648 		}
649 	}
650 
651 	if (caller_frees && value) {
652 		efree(value);
653 	}
654 
655 	if (stmt->dbh->stringify) {
656 		switch (Z_TYPE_P(dest)) {
657 			case IS_LONG:
658 			case IS_DOUBLE:
659 				convert_to_string(dest);
660 				break;
661 		}
662 	}
663 
664 	if (Z_TYPE_P(dest) == IS_NULL && stmt->dbh->oracle_nulls == PDO_NULL_TO_STRING) {
665 		ZVAL_EMPTY_STRING(dest);
666 	}
667 }
668 /* }}} */
669 
do_fetch_common(pdo_stmt_t * stmt,enum pdo_fetch_orientation ori,zend_long offset,int do_bind)670 static int do_fetch_common(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset, int do_bind) /* {{{ */
671 {
672 	if (!stmt->executed) {
673 		return 0;
674 	}
675 
676 	if (!dispatch_param_event(stmt, PDO_PARAM_EVT_FETCH_PRE)) {
677 		return 0;
678 	}
679 
680 	if (!stmt->methods->fetcher(stmt, ori, offset)) {
681 		return 0;
682 	}
683 
684 	/* some drivers might need to describe the columns now */
685 	if (!stmt->columns && !pdo_stmt_describe_columns(stmt)) {
686 		return 0;
687 	}
688 
689 	if (!dispatch_param_event(stmt, PDO_PARAM_EVT_FETCH_POST)) {
690 		return 0;
691 	}
692 
693 	if (do_bind && stmt->bound_columns) {
694 		/* update those bound column variables now */
695 		struct pdo_bound_param_data *param;
696 
697 		ZEND_HASH_FOREACH_PTR(stmt->bound_columns, param) {
698 			if (param->paramno >= 0) {
699 				if (!Z_ISREF(param->parameter)) {
700 					continue;
701 				}
702 
703 				/* delete old value */
704 				zval_ptr_dtor(Z_REFVAL(param->parameter));
705 
706 				/* set new value */
707 				fetch_value(stmt, Z_REFVAL(param->parameter), param->paramno, (int *)&param->param_type);
708 
709 				/* TODO: some smart thing that avoids duplicating the value in the
710 				 * general loop below.  For now, if you're binding output columns,
711 				 * it's better to use LAZY or BOUND fetches if you want to shave
712 				 * off those cycles */
713 			}
714 		} ZEND_HASH_FOREACH_END();
715 	}
716 
717 	return 1;
718 }
719 /* }}} */
720 
do_fetch_class_prepare(pdo_stmt_t * stmt)721 static int do_fetch_class_prepare(pdo_stmt_t *stmt) /* {{{ */
722 {
723 	zend_class_entry *ce = stmt->fetch.cls.ce;
724 	zend_fcall_info *fci = &stmt->fetch.cls.fci;
725 	zend_fcall_info_cache *fcc = &stmt->fetch.cls.fcc;
726 
727 	fci->size = sizeof(zend_fcall_info);
728 
729 	if (!ce) {
730 		stmt->fetch.cls.ce = ZEND_STANDARD_CLASS_DEF_PTR;
731 		ce = ZEND_STANDARD_CLASS_DEF_PTR;
732 	}
733 
734 	if (ce->constructor) {
735 		ZVAL_UNDEF(&fci->function_name);
736 		fci->retval = &stmt->fetch.cls.retval;
737 		fci->param_count = 0;
738 		fci->params = NULL;
739 		fci->no_separation = 1;
740 
741 		zend_fcall_info_args_ex(fci, ce->constructor, &stmt->fetch.cls.ctor_args);
742 
743 		fcc->function_handler = ce->constructor;
744 		fcc->called_scope = ce;
745 		return 1;
746 	} else if (!Z_ISUNDEF(stmt->fetch.cls.ctor_args)) {
747 		pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "user-supplied class does not have a constructor, use NULL for the ctor_params parameter, or simply omit it");
748 		return 0;
749 	} else {
750 		return 1; /* no ctor no args is also ok */
751 	}
752 }
753 /* }}} */
754 
make_callable_ex(pdo_stmt_t * stmt,zval * callable,zend_fcall_info * fci,zend_fcall_info_cache * fcc,int num_args)755 static int make_callable_ex(pdo_stmt_t *stmt, zval *callable, zend_fcall_info * fci, zend_fcall_info_cache * fcc, int num_args) /* {{{ */
756 {
757 	char *is_callable_error = NULL;
758 
759 	if (zend_fcall_info_init(callable, 0, fci, fcc, NULL, &is_callable_error) == FAILURE) {
760 		if (is_callable_error) {
761 			pdo_raise_impl_error(stmt->dbh, stmt, "HY000", is_callable_error);
762 			efree(is_callable_error);
763 		} else {
764 			pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "user-supplied function must be a valid callback");
765 		}
766 		return 0;
767 	}
768 	if (is_callable_error) {
769 		/* Possible E_STRICT error message */
770 		efree(is_callable_error);
771 	}
772 
773 	fci->param_count = num_args; /* probably less */
774 	fci->params = safe_emalloc(sizeof(zval), num_args, 0);
775 
776 	return 1;
777 }
778 /* }}} */
779 
do_fetch_func_prepare(pdo_stmt_t * stmt)780 static int do_fetch_func_prepare(pdo_stmt_t *stmt) /* {{{ */
781 {
782 	zend_fcall_info *fci = &stmt->fetch.cls.fci;
783 	zend_fcall_info_cache *fcc = &stmt->fetch.cls.fcc;
784 
785 	if (!make_callable_ex(stmt, &stmt->fetch.func.function, fci, fcc, stmt->column_count)) {
786 		return 0;
787 	} else {
788 		stmt->fetch.func.values = safe_emalloc(sizeof(zval), stmt->column_count, 0);
789 		return 1;
790 	}
791 }
792 /* }}} */
793 
do_fetch_opt_finish(pdo_stmt_t * stmt,int free_ctor_agrs)794 static void do_fetch_opt_finish(pdo_stmt_t *stmt, int free_ctor_agrs) /* {{{ */
795 {
796 	/* fci.size is used to check if it is valid */
797 	if (stmt->fetch.cls.fci.size && stmt->fetch.cls.fci.params) {
798 		if (!Z_ISUNDEF(stmt->fetch.cls.ctor_args)) {
799 		    /* Added to free constructor arguments */
800 			zend_fcall_info_args_clear(&stmt->fetch.cls.fci, 1);
801 		} else {
802 			efree(stmt->fetch.cls.fci.params);
803 		}
804 		stmt->fetch.cls.fci.params = NULL;
805 	}
806 
807 	stmt->fetch.cls.fci.size = 0;
808 	if (!Z_ISUNDEF(stmt->fetch.cls.ctor_args) && free_ctor_agrs) {
809 		zval_ptr_dtor(&stmt->fetch.cls.ctor_args);
810 		ZVAL_UNDEF(&stmt->fetch.cls.ctor_args);
811 		stmt->fetch.cls.fci.param_count = 0;
812 	}
813 	if (stmt->fetch.func.values) {
814 		efree(stmt->fetch.func.values);
815 		stmt->fetch.func.values = NULL;
816 	}
817 }
818 /* }}} */
819 
820 /* perform a fetch.  If do_bind is true, update any bound columns.
821  * If return_value is not null, store values into it according to HOW. */
do_fetch(pdo_stmt_t * stmt,int do_bind,zval * return_value,enum pdo_fetch_type how,enum pdo_fetch_orientation ori,zend_long offset,zval * return_all)822 static int do_fetch(pdo_stmt_t *stmt, int do_bind, zval *return_value, enum pdo_fetch_type how, enum pdo_fetch_orientation ori, zend_long offset, zval *return_all) /* {{{ */
823 {
824 	int flags, idx, old_arg_count = 0;
825 	zend_class_entry *ce = NULL, *old_ce = NULL;
826 	zval grp_val, *pgrp, retval, old_ctor_args;
827 	int colno;
828 
829 	if (how == PDO_FETCH_USE_DEFAULT) {
830 		how = stmt->default_fetch_type;
831 	}
832 	flags = how & PDO_FETCH_FLAGS;
833 	how = how & ~PDO_FETCH_FLAGS;
834 
835 	if (!do_fetch_common(stmt, ori, offset, do_bind)) {
836 		return 0;
837 	}
838 
839 	if (how == PDO_FETCH_BOUND) {
840 		RETVAL_TRUE;
841 		return 1;
842 	}
843 
844 	if (flags & PDO_FETCH_GROUP && stmt->fetch.column == -1) {
845 		colno = 1;
846 	} else {
847 		colno = stmt->fetch.column;
848 	}
849 
850 	if (return_value) {
851 		int i = 0;
852 
853 		if (how == PDO_FETCH_LAZY) {
854 			get_lazy_object(stmt, return_value);
855 			return 1;
856 		}
857 
858 		RETVAL_FALSE;
859 
860 		switch (how) {
861 			case PDO_FETCH_USE_DEFAULT:
862 			case PDO_FETCH_ASSOC:
863 			case PDO_FETCH_BOTH:
864 			case PDO_FETCH_NUM:
865 			case PDO_FETCH_NAMED:
866 				if (!return_all) {
867 					array_init_size(return_value, stmt->column_count);
868 				} else {
869 					array_init(return_value);
870 				}
871 				break;
872 
873 			case PDO_FETCH_KEY_PAIR:
874 				if (stmt->column_count != 2) {
875 					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_KEY_PAIR fetch mode requires the result set to contain extactly 2 columns.");
876 					return 0;
877 				}
878 				if (!return_all) {
879 					array_init(return_value);
880 				}
881 				break;
882 
883 			case PDO_FETCH_COLUMN:
884 				if (colno >= 0 && colno < stmt->column_count) {
885 					if (flags == PDO_FETCH_GROUP && stmt->fetch.column == -1) {
886 						fetch_value(stmt, return_value, 1, NULL);
887 					} else if (flags == PDO_FETCH_GROUP && colno) {
888 						fetch_value(stmt, return_value, 0, NULL);
889 					} else {
890 						fetch_value(stmt, return_value, colno, NULL);
891 					}
892 					if (!return_all) {
893 						return 1;
894 					} else {
895 						break;
896 					}
897 				} else {
898 					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Invalid column index");
899 				}
900 				return 0;
901 
902 			case PDO_FETCH_OBJ:
903 				object_init_ex(return_value, ZEND_STANDARD_CLASS_DEF_PTR);
904 				break;
905 
906 			case PDO_FETCH_CLASS:
907 				if (flags & PDO_FETCH_CLASSTYPE) {
908 					zval val;
909 					zend_class_entry *cep;
910 
911 					old_ce = stmt->fetch.cls.ce;
912 					ZVAL_COPY_VALUE(&old_ctor_args, &stmt->fetch.cls.ctor_args);
913 					old_arg_count = stmt->fetch.cls.fci.param_count;
914 					do_fetch_opt_finish(stmt, 0);
915 
916 					fetch_value(stmt, &val, i++, NULL);
917 					if (Z_TYPE(val) != IS_NULL) {
918 						convert_to_string(&val);
919 						if ((cep = zend_lookup_class(Z_STR(val))) == NULL) {
920 							stmt->fetch.cls.ce = ZEND_STANDARD_CLASS_DEF_PTR;
921 						} else {
922 							stmt->fetch.cls.ce = cep;
923 						}
924 					}
925 
926 					do_fetch_class_prepare(stmt);
927 					zval_ptr_dtor_str(&val);
928 				}
929 				ce = stmt->fetch.cls.ce;
930 				if (!ce) {
931 					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "No fetch class specified");
932 					return 0;
933 				}
934 				if ((flags & PDO_FETCH_SERIALIZE) == 0) {
935 					if (UNEXPECTED(object_init_ex(return_value, ce) != SUCCESS)) {
936 						return 0;
937 					}
938 					if (!stmt->fetch.cls.fci.size) {
939 						if (!do_fetch_class_prepare(stmt))
940 						{
941 							return 0;
942 						}
943 					}
944 					if (ce->constructor && (flags & PDO_FETCH_PROPS_LATE)) {
945 						stmt->fetch.cls.fci.object = Z_OBJ_P(return_value);
946 						stmt->fetch.cls.fcc.object = Z_OBJ_P(return_value);
947 						if (zend_call_function(&stmt->fetch.cls.fci, &stmt->fetch.cls.fcc) == FAILURE) {
948 							pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call class constructor");
949 							return 0;
950 						} else {
951 							if (!Z_ISUNDEF(stmt->fetch.cls.retval)) {
952 								zval_ptr_dtor(&stmt->fetch.cls.retval);
953 								ZVAL_UNDEF(&stmt->fetch.cls.retval);
954 							}
955 						}
956 					}
957 				}
958 				break;
959 
960 			case PDO_FETCH_INTO:
961 				if (Z_ISUNDEF(stmt->fetch.into)) {
962 					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "No fetch-into object specified.");
963 					return 0;
964 					break;
965 				}
966 
967 				ZVAL_COPY(return_value, &stmt->fetch.into);
968 
969 				if (Z_OBJ_P(return_value)->ce == ZEND_STANDARD_CLASS_DEF_PTR) {
970 					how = PDO_FETCH_OBJ;
971 				}
972 				break;
973 
974 			case PDO_FETCH_FUNC:
975 				if (Z_ISUNDEF(stmt->fetch.func.function)) {
976 					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "No fetch function specified");
977 					return 0;
978 				}
979 				if (!stmt->fetch.func.fci.size) {
980 					if (!do_fetch_func_prepare(stmt))
981 					{
982 						return 0;
983 					}
984 				}
985 				break;
986 
987 
988 			default:
989 				/* shouldn't happen */
990 				return 0;
991 		}
992 
993 		if (return_all && how != PDO_FETCH_KEY_PAIR) {
994 			if (flags == PDO_FETCH_GROUP && how == PDO_FETCH_COLUMN && stmt->fetch.column > 0) {
995 				fetch_value(stmt, &grp_val, colno, NULL);
996 			} else {
997 				fetch_value(stmt, &grp_val, i, NULL);
998 			}
999 			convert_to_string(&grp_val);
1000 			if (how == PDO_FETCH_COLUMN) {
1001 				i = stmt->column_count; /* no more data to fetch */
1002 			} else {
1003 				i++;
1004 			}
1005 		}
1006 
1007 		for (idx = 0; i < stmt->column_count; i++, idx++) {
1008 			zval val;
1009 			fetch_value(stmt, &val, i, NULL);
1010 
1011 			switch (how) {
1012 				case PDO_FETCH_ASSOC:
1013 					zend_symtable_update(Z_ARRVAL_P(return_value), stmt->columns[i].name, &val);
1014 					break;
1015 
1016 				case PDO_FETCH_KEY_PAIR:
1017 					{
1018 						zval tmp;
1019 						fetch_value(stmt, &tmp, ++i, NULL);
1020 
1021 						if (Z_TYPE(val) == IS_LONG) {
1022 							zend_hash_index_update((return_all ? Z_ARRVAL_P(return_all) : Z_ARRVAL_P(return_value)), Z_LVAL(val), &tmp);
1023 						} else {
1024 							convert_to_string(&val);
1025 							zend_symtable_update((return_all ? Z_ARRVAL_P(return_all) : Z_ARRVAL_P(return_value)), Z_STR(val), &tmp);
1026 						}
1027 						zval_ptr_dtor(&val);
1028 						return 1;
1029 					}
1030 					break;
1031 
1032 				case PDO_FETCH_USE_DEFAULT:
1033 				case PDO_FETCH_BOTH:
1034 					zend_symtable_update(Z_ARRVAL_P(return_value), stmt->columns[i].name, &val);
1035 					if (Z_REFCOUNTED(val)) {
1036 						Z_ADDREF(val);
1037 					}
1038 					zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &val);
1039 					break;
1040 
1041 				case PDO_FETCH_NAMED:
1042 					/* already have an item with this name? */
1043 					{
1044 						zval *curr_val;
1045 						if ((curr_val = zend_hash_find(Z_ARRVAL_P(return_value), stmt->columns[i].name))) {
1046 							zval arr;
1047 							if (Z_TYPE_P(curr_val) != IS_ARRAY) {
1048 								/* a little bit of black magic here:
1049 								 * we're creating a new array and swapping it for the
1050 								 * zval that's already stored in the hash under the name
1051 								 * we want.  We then add that zval to the array.
1052 								 * This is effectively the same thing as:
1053 								 * if (!is_array($hash[$name])) {
1054 								 *   $hash[$name] = array($hash[$name]);
1055 								 * }
1056 								 * */
1057 								zval cur;
1058 
1059 								array_init(&arr);
1060 
1061 								ZVAL_COPY_VALUE(&cur, curr_val);
1062 								ZVAL_COPY_VALUE(curr_val, &arr);
1063 
1064 								zend_hash_next_index_insert_new(Z_ARRVAL(arr), &cur);
1065 							} else {
1066 								ZVAL_COPY_VALUE(&arr, curr_val);
1067 							}
1068 							zend_hash_next_index_insert_new(Z_ARRVAL(arr), &val);
1069 						} else {
1070 							zend_hash_update(Z_ARRVAL_P(return_value), stmt->columns[i].name, &val);
1071 						}
1072 					}
1073 					break;
1074 
1075 				case PDO_FETCH_NUM:
1076 					zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &val);
1077 					break;
1078 
1079 				case PDO_FETCH_OBJ:
1080 				case PDO_FETCH_INTO:
1081 					zend_update_property_ex(NULL, return_value,
1082 						stmt->columns[i].name,
1083 						&val);
1084 					zval_ptr_dtor(&val);
1085 					break;
1086 
1087 				case PDO_FETCH_CLASS:
1088 					if ((flags & PDO_FETCH_SERIALIZE) == 0 || idx) {
1089 						zend_update_property_ex(ce, return_value,
1090 							stmt->columns[i].name,
1091 							&val);
1092 						zval_ptr_dtor(&val);
1093 					} else {
1094 #ifdef MBO_0
1095 						php_unserialize_data_t var_hash;
1096 
1097 						PHP_VAR_UNSERIALIZE_INIT(var_hash);
1098 						if (php_var_unserialize(return_value, (const unsigned char**)&Z_STRVAL(val), Z_STRVAL(val)+Z_STRLEN(val), NULL) == FAILURE) {
1099 							pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "cannot unserialize data");
1100 							PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1101 							return 0;
1102 						}
1103 						PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1104 #endif
1105 						if (!ce->unserialize) {
1106 							zval_ptr_dtor(&val);
1107 							pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "cannot unserialize class");
1108 							return 0;
1109 						} else if (ce->unserialize(return_value, ce, (unsigned char *)(Z_TYPE(val) == IS_STRING ? Z_STRVAL(val) : ""), Z_TYPE(val) == IS_STRING ? Z_STRLEN(val) : 0, NULL) == FAILURE) {
1110 							zval_ptr_dtor(&val);
1111 							pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "cannot unserialize class");
1112 							zval_ptr_dtor(return_value);
1113 							ZVAL_NULL(return_value);
1114 							return 0;
1115 						} else {
1116 							zval_ptr_dtor(&val);
1117 						}
1118 					}
1119 					break;
1120 
1121 				case PDO_FETCH_FUNC:
1122 					ZVAL_COPY_VALUE(&stmt->fetch.func.values[idx], &val);
1123 					ZVAL_COPY_VALUE(&stmt->fetch.cls.fci.params[idx], &stmt->fetch.func.values[idx]);
1124 					break;
1125 
1126 				default:
1127 					zval_ptr_dtor(&val);
1128 					pdo_raise_impl_error(stmt->dbh, stmt, "22003", "mode is out of range");
1129 					return 0;
1130 					break;
1131 			}
1132 		}
1133 
1134 		switch (how) {
1135 			case PDO_FETCH_CLASS:
1136 				if (ce->constructor && !(flags & (PDO_FETCH_PROPS_LATE | PDO_FETCH_SERIALIZE))) {
1137 					stmt->fetch.cls.fci.object = Z_OBJ_P(return_value);
1138 					stmt->fetch.cls.fcc.object = Z_OBJ_P(return_value);
1139 					if (zend_call_function(&stmt->fetch.cls.fci, &stmt->fetch.cls.fcc) == FAILURE) {
1140 						pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call class constructor");
1141 						return 0;
1142 					} else {
1143 						if (!Z_ISUNDEF(stmt->fetch.cls.retval)) {
1144 							zval_ptr_dtor(&stmt->fetch.cls.retval);
1145 						}
1146 					}
1147 				}
1148 				if (flags & PDO_FETCH_CLASSTYPE) {
1149 					do_fetch_opt_finish(stmt, 0);
1150 					stmt->fetch.cls.ce = old_ce;
1151 					ZVAL_COPY_VALUE(&stmt->fetch.cls.ctor_args, &old_ctor_args);
1152 					stmt->fetch.cls.fci.param_count = old_arg_count;
1153 				}
1154 				break;
1155 
1156 			case PDO_FETCH_FUNC:
1157 				stmt->fetch.func.fci.param_count = idx;
1158 				stmt->fetch.func.fci.retval = &retval;
1159 				if (zend_call_function(&stmt->fetch.func.fci, &stmt->fetch.func.fcc) == FAILURE) {
1160 					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call user-supplied function");
1161 					return 0;
1162 				} else {
1163 					if (return_all) {
1164 						zval_ptr_dtor(return_value); /* we don't need that */
1165 						ZVAL_COPY_VALUE(return_value, &retval);
1166 					} else if (!Z_ISUNDEF(retval)) {
1167 						ZVAL_COPY_VALUE(return_value, &retval);
1168 					}
1169 				}
1170 				while (idx--) {
1171 					zval_ptr_dtor(&stmt->fetch.func.values[idx]);
1172 				}
1173 				break;
1174 
1175 			default:
1176 				break;
1177 		}
1178 
1179 		if (return_all) {
1180 			if ((flags & PDO_FETCH_UNIQUE) == PDO_FETCH_UNIQUE) {
1181 				zend_symtable_update(Z_ARRVAL_P(return_all), Z_STR(grp_val), return_value);
1182 			} else {
1183 				zval grp;
1184 				if ((pgrp = zend_symtable_find(Z_ARRVAL_P(return_all), Z_STR(grp_val))) == NULL) {
1185 					array_init(&grp);
1186 					zend_symtable_update(Z_ARRVAL_P(return_all), Z_STR(grp_val), &grp);
1187 				} else {
1188 					ZVAL_COPY_VALUE(&grp, pgrp);
1189 				}
1190 				zend_hash_next_index_insert(Z_ARRVAL(grp), return_value);
1191 			}
1192 			zval_ptr_dtor_str(&grp_val);
1193 		}
1194 
1195 	}
1196 
1197 	return 1;
1198 }
1199 /* }}} */
1200 
pdo_stmt_verify_mode(pdo_stmt_t * stmt,zend_long mode,int fetch_all)1201 static int pdo_stmt_verify_mode(pdo_stmt_t *stmt, zend_long mode, int fetch_all) /* {{{ */
1202 {
1203 	int flags = mode & PDO_FETCH_FLAGS;
1204 
1205 	mode = mode & ~PDO_FETCH_FLAGS;
1206 
1207 	if (mode < 0 || mode > PDO_FETCH__MAX) {
1208 		pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "invalid fetch mode");
1209 		return 0;
1210 	}
1211 
1212 	if (mode == PDO_FETCH_USE_DEFAULT) {
1213 		flags = stmt->default_fetch_type & PDO_FETCH_FLAGS;
1214 		mode = stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
1215 	}
1216 
1217 	switch(mode) {
1218 		case PDO_FETCH_FUNC:
1219 			if (!fetch_all) {
1220 				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_FUNC is only allowed in PDOStatement::fetchAll()");
1221 				return 0;
1222 			}
1223 			return 1;
1224 
1225 		case PDO_FETCH_LAZY:
1226 			if (fetch_all) {
1227 				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_LAZY can't be used with PDOStatement::fetchAll()");
1228 				return 0;
1229 			}
1230 			/* fall through */
1231 		default:
1232 			if ((flags & PDO_FETCH_SERIALIZE) == PDO_FETCH_SERIALIZE) {
1233 				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_SERIALIZE can only be used together with PDO::FETCH_CLASS");
1234 				return 0;
1235 			}
1236 			if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
1237 				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_CLASSTYPE can only be used together with PDO::FETCH_CLASS");
1238 				return 0;
1239 			}
1240 			if (mode >= PDO_FETCH__MAX) {
1241 				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "invalid fetch mode");
1242 				return 0;
1243 			}
1244 			/* no break; */
1245 
1246 		case PDO_FETCH_CLASS:
1247 			return 1;
1248 	}
1249 }
1250 /* }}} */
1251 
1252 /* {{{ proto mixed PDOStatement::fetch([int $how = PDO_FETCH_BOTH [, int $orientation [, int $offset]]])
1253    Fetches the next row and returns it, or false if there are no more rows */
PHP_METHOD(PDOStatement,fetch)1254 static PHP_METHOD(PDOStatement, fetch)
1255 {
1256 	zend_long how = PDO_FETCH_USE_DEFAULT;
1257 	zend_long ori = PDO_FETCH_ORI_NEXT;
1258 	zend_long off = 0;
1259     PHP_STMT_GET_OBJ;
1260 
1261 	ZEND_PARSE_PARAMETERS_START(0, 3)
1262 		Z_PARAM_OPTIONAL
1263 		Z_PARAM_LONG(how)
1264 		Z_PARAM_LONG(ori)
1265 		Z_PARAM_LONG(off)
1266 	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1267 
1268 	PDO_STMT_CLEAR_ERR();
1269 
1270 	if (!pdo_stmt_verify_mode(stmt, how, 0)) {
1271 		RETURN_FALSE;
1272 	}
1273 
1274 	if (!do_fetch(stmt, TRUE, return_value, how, ori, off, 0)) {
1275 		PDO_HANDLE_STMT_ERR();
1276 		RETURN_FALSE;
1277 	}
1278 }
1279 /* }}} */
1280 
1281 /* {{{ proto mixed PDOStatement::fetchObject([string class_name [, NULL|array ctor_args]])
1282    Fetches the next row and returns it as an object. */
PHP_METHOD(PDOStatement,fetchObject)1283 static PHP_METHOD(PDOStatement, fetchObject)
1284 {
1285 	zend_long how = PDO_FETCH_CLASS;
1286 	zend_long ori = PDO_FETCH_ORI_NEXT;
1287 	zend_long off = 0;
1288 	zend_string *class_name = NULL;
1289 	zend_class_entry *old_ce;
1290 	zval old_ctor_args, *ctor_args = NULL;
1291 	int error = 0, old_arg_count;
1292 
1293 	PHP_STMT_GET_OBJ;
1294 
1295 	ZEND_PARSE_PARAMETERS_START(0, 2)
1296 		Z_PARAM_OPTIONAL
1297 		Z_PARAM_STR_EX(class_name, 1, 0)
1298 		Z_PARAM_ARRAY(ctor_args)
1299 	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1300 
1301 	PDO_STMT_CLEAR_ERR();
1302 
1303 	if (!pdo_stmt_verify_mode(stmt, how, 0)) {
1304 		RETURN_FALSE;
1305 	}
1306 
1307 	old_ce = stmt->fetch.cls.ce;
1308 	ZVAL_COPY_VALUE(&old_ctor_args, &stmt->fetch.cls.ctor_args);
1309 	old_arg_count = stmt->fetch.cls.fci.param_count;
1310 
1311 	do_fetch_opt_finish(stmt, 0);
1312 
1313 	if (ctor_args) {
1314 		if (Z_TYPE_P(ctor_args) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(ctor_args))) {
1315 			ZVAL_ARR(&stmt->fetch.cls.ctor_args, zend_array_dup(Z_ARRVAL_P(ctor_args)));
1316 		} else {
1317 			ZVAL_UNDEF(&stmt->fetch.cls.ctor_args);
1318 		}
1319 	}
1320 	if (class_name && !error) {
1321 		stmt->fetch.cls.ce = zend_fetch_class(class_name, ZEND_FETCH_CLASS_AUTO);
1322 
1323 		if (!stmt->fetch.cls.ce) {
1324 			pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Could not find user-supplied class");
1325 			error = 1;
1326 		}
1327 	} else if (!error) {
1328 		stmt->fetch.cls.ce = zend_standard_class_def;
1329 	}
1330 
1331 	if (!error && !do_fetch(stmt, TRUE, return_value, how, ori, off, 0)) {
1332 		error = 1;
1333 	}
1334 	if (error) {
1335 		PDO_HANDLE_STMT_ERR();
1336 	}
1337 	do_fetch_opt_finish(stmt, 1);
1338 
1339 	stmt->fetch.cls.ce = old_ce;
1340 	ZVAL_COPY_VALUE(&stmt->fetch.cls.ctor_args, &old_ctor_args);
1341 	stmt->fetch.cls.fci.param_count = old_arg_count;
1342 	if (error) {
1343 		RETURN_FALSE;
1344 	}
1345 }
1346 /* }}} */
1347 
1348 /* {{{ proto string PDOStatement::fetchColumn([int column_number])
1349    Returns a data of the specified column in the result set. */
PHP_METHOD(PDOStatement,fetchColumn)1350 static PHP_METHOD(PDOStatement, fetchColumn)
1351 {
1352 	zend_long col_n = 0;
1353 	PHP_STMT_GET_OBJ;
1354 
1355 	ZEND_PARSE_PARAMETERS_START(0, 1)
1356 		Z_PARAM_OPTIONAL
1357 		Z_PARAM_LONG(col_n)
1358 	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1359 
1360 	PDO_STMT_CLEAR_ERR();
1361 
1362 	if (!do_fetch_common(stmt, PDO_FETCH_ORI_NEXT, 0, TRUE)) {
1363 		PDO_HANDLE_STMT_ERR();
1364 		RETURN_FALSE;
1365 	}
1366 
1367 	fetch_value(stmt, return_value, col_n, NULL);
1368 }
1369 /* }}} */
1370 
1371 /* {{{ proto array PDOStatement::fetchAll([int $how = PDO_FETCH_BOTH [, string class_name [, NULL|array ctor_args]]])
1372    Returns an array of all of the results. */
PHP_METHOD(PDOStatement,fetchAll)1373 static PHP_METHOD(PDOStatement, fetchAll)
1374 {
1375 	zend_long how = PDO_FETCH_USE_DEFAULT;
1376 	zval data, *return_all;
1377 	zval *arg2;
1378 	zend_class_entry *old_ce;
1379 	zval old_ctor_args, *ctor_args = NULL;
1380 	int error = 0, flags, old_arg_count;
1381 	PHP_STMT_GET_OBJ;
1382 
1383 	ZEND_PARSE_PARAMETERS_START(0, 3)
1384 		Z_PARAM_OPTIONAL
1385 		Z_PARAM_LONG(how)
1386 		Z_PARAM_ZVAL(arg2)
1387 		Z_PARAM_ZVAL(ctor_args)
1388 	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1389 
1390 	if (!pdo_stmt_verify_mode(stmt, how, 1)) {
1391 		RETURN_FALSE;
1392 	}
1393 
1394 	old_ce = stmt->fetch.cls.ce;
1395 	ZVAL_COPY_VALUE(&old_ctor_args, &stmt->fetch.cls.ctor_args);
1396 	old_arg_count = stmt->fetch.cls.fci.param_count;
1397 
1398 	do_fetch_opt_finish(stmt, 0);
1399 
1400 	switch(how & ~PDO_FETCH_FLAGS) {
1401 	case PDO_FETCH_CLASS:
1402 		switch(ZEND_NUM_ARGS()) {
1403 		case 0:
1404 		case 1:
1405 			stmt->fetch.cls.ce = zend_standard_class_def;
1406 			break;
1407 		case 3:
1408 			if (Z_TYPE_P(ctor_args) != IS_NULL && Z_TYPE_P(ctor_args) != IS_ARRAY) {
1409 				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "ctor_args must be either NULL or an array");
1410 				error = 1;
1411 				break;
1412 			}
1413 			if (Z_TYPE_P(ctor_args) != IS_ARRAY || !zend_hash_num_elements(Z_ARRVAL_P(ctor_args))) {
1414 				ctor_args = NULL;
1415 			}
1416 			/* no break */
1417 		case 2:
1418 			if (ctor_args) {
1419 				ZVAL_COPY_VALUE(&stmt->fetch.cls.ctor_args, ctor_args); /* we're not going to free these */
1420 			} else {
1421 				ZVAL_UNDEF(&stmt->fetch.cls.ctor_args);
1422 			}
1423 			if (Z_TYPE_P(arg2) != IS_STRING) {
1424 				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Invalid class name (should be a string)");
1425 				error = 1;
1426 				break;
1427 			} else {
1428 				stmt->fetch.cls.ce = zend_fetch_class(Z_STR_P(arg2), ZEND_FETCH_CLASS_AUTO);
1429 				if (!stmt->fetch.cls.ce) {
1430 					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not find user-specified class");
1431 					error = 1;
1432 					break;
1433 				}
1434 			}
1435 		}
1436 		if (!error) {
1437 			do_fetch_class_prepare(stmt);
1438 		}
1439 		break;
1440 
1441 	case PDO_FETCH_FUNC:
1442 		switch (ZEND_NUM_ARGS()) {
1443 			case 0:
1444 			case 1:
1445 				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "no fetch function specified");
1446 				error = 1;
1447 				break;
1448 			case 3:
1449 			case 2:
1450 				ZVAL_COPY_VALUE(&stmt->fetch.func.function, arg2);
1451 				if (do_fetch_func_prepare(stmt) == 0) {
1452 					error = 1;
1453 				}
1454 				break;
1455 		}
1456 		break;
1457 
1458 	case PDO_FETCH_COLUMN:
1459 		switch(ZEND_NUM_ARGS()) {
1460 		case 0:
1461 		case 1:
1462 			stmt->fetch.column = how & PDO_FETCH_GROUP ? -1 : 0;
1463 			break;
1464 		case 2:
1465 			convert_to_long(arg2);
1466 			stmt->fetch.column = Z_LVAL_P(arg2);
1467 			break;
1468 		case 3:
1469 			pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Third parameter not allowed for PDO::FETCH_COLUMN");
1470 			error = 1;
1471 		}
1472 		break;
1473 
1474 	default:
1475 		if (ZEND_NUM_ARGS() > 1) {
1476 			pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Extraneous additional parameters");
1477 			error = 1;
1478 		}
1479 	}
1480 
1481 	flags = how & PDO_FETCH_FLAGS;
1482 
1483 	if ((how & ~PDO_FETCH_FLAGS) == PDO_FETCH_USE_DEFAULT) {
1484 		flags |= stmt->default_fetch_type & PDO_FETCH_FLAGS;
1485 		how |= stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
1486 	}
1487 
1488 	if (!error)	{
1489 		PDO_STMT_CLEAR_ERR();
1490 		if ((how & PDO_FETCH_GROUP) || how == PDO_FETCH_KEY_PAIR ||
1491 			(how == PDO_FETCH_USE_DEFAULT && stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)
1492 		) {
1493 			array_init(return_value);
1494 			return_all = return_value;
1495 		} else {
1496 			return_all = 0;
1497 		}
1498 		if (!do_fetch(stmt, 1, &data, how | flags, PDO_FETCH_ORI_NEXT, 0, return_all)) {
1499 			error = 2;
1500 		}
1501 	}
1502 	if (!error) {
1503 		if ((how & PDO_FETCH_GROUP)) {
1504 			while (do_fetch(stmt, 1, &data, how | flags, PDO_FETCH_ORI_NEXT, 0, return_all));
1505 		} else if (how == PDO_FETCH_KEY_PAIR || (how == PDO_FETCH_USE_DEFAULT && stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)) {
1506 			while (do_fetch(stmt, 1, &data, how | flags, PDO_FETCH_ORI_NEXT, 0, return_all));
1507 		} else {
1508 			array_init(return_value);
1509 			do {
1510 				zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &data);
1511 			} while (do_fetch(stmt, 1, &data, how | flags, PDO_FETCH_ORI_NEXT, 0, 0));
1512 		}
1513 	}
1514 
1515 	do_fetch_opt_finish(stmt, 0);
1516 
1517 	stmt->fetch.cls.ce = old_ce;
1518 	ZVAL_COPY_VALUE(&stmt->fetch.cls.ctor_args, &old_ctor_args);
1519 	stmt->fetch.cls.fci.param_count = old_arg_count;
1520 
1521 	if (error) {
1522 		PDO_HANDLE_STMT_ERR();
1523 		if (error != 2) {
1524 			RETURN_FALSE;
1525 		} else { /* on no results, return an empty array */
1526 			if (Z_TYPE_P(return_value) != IS_ARRAY) {
1527 				array_init(return_value);
1528 			}
1529 			return;
1530 		}
1531 	}
1532 }
1533 /* }}} */
1534 
register_bound_param(INTERNAL_FUNCTION_PARAMETERS,pdo_stmt_t * stmt,int is_param)1535 static int register_bound_param(INTERNAL_FUNCTION_PARAMETERS, pdo_stmt_t *stmt, int is_param) /* {{{ */
1536 {
1537 	struct pdo_bound_param_data param;
1538 	zend_long param_type = PDO_PARAM_STR;
1539 	zval *parameter, *driver_params = NULL;
1540 
1541 	memset(&param, 0, sizeof(param));
1542 	param.paramno = -1;
1543 
1544 	if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(),
1545 			"lz|llz!", &param.paramno, &parameter, &param_type, &param.max_value_len,
1546 			&driver_params)) {
1547 		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "Sz|llz!", &param.name,
1548 				&parameter, &param_type, &param.max_value_len,
1549 				&driver_params)) {
1550 			return 0;
1551 		}
1552 	}
1553 
1554 	param.param_type = (int) param_type;
1555 
1556 	if (param.paramno > 0) {
1557 		--param.paramno; /* make it zero-based internally */
1558 	} else if (!param.name) {
1559 		pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "Columns/Parameters are 1-based");
1560 		return 0;
1561 	}
1562 
1563 	if (driver_params) {
1564 		ZVAL_COPY(&param.driver_params, driver_params);
1565 	}
1566 
1567 	ZVAL_COPY(&param.parameter, parameter);
1568 	if (!really_register_bound_param(&param, stmt, is_param)) {
1569 		if (!Z_ISUNDEF(param.parameter)) {
1570 			zval_ptr_dtor(&(param.parameter));
1571 		}
1572 		return 0;
1573 	}
1574 	return 1;
1575 } /* }}} */
1576 
1577 /* {{{ proto bool PDOStatement::bindValue(mixed $paramno, mixed $param [, int $type ])
1578    bind an input parameter to the value of a PHP variable.  $paramno is the 1-based position of the placeholder in the SQL statement (but can be the parameter name for drivers that support named placeholders).  It should be called prior to execute(). */
PHP_METHOD(PDOStatement,bindValue)1579 static PHP_METHOD(PDOStatement, bindValue)
1580 {
1581 	struct pdo_bound_param_data param;
1582 	zend_long param_type = PDO_PARAM_STR;
1583 	zval *parameter;
1584 	PHP_STMT_GET_OBJ;
1585 
1586 	memset(&param, 0, sizeof(param));
1587 	param.paramno = -1;
1588 
1589 	if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(),
1590 			"lz|l", &param.paramno, &parameter, &param_type)) {
1591 		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "Sz|l", &param.name,
1592 				&parameter, &param_type)) {
1593 			RETURN_FALSE;
1594 		}
1595 	}
1596 
1597 	param.param_type = (int) param_type;
1598 
1599 	if (param.paramno > 0) {
1600 		--param.paramno; /* make it zero-based internally */
1601 	} else if (!param.name) {
1602 		pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "Columns/Parameters are 1-based");
1603 		RETURN_FALSE;
1604 	}
1605 
1606 	ZVAL_COPY(&param.parameter, parameter);
1607 	if (!really_register_bound_param(&param, stmt, TRUE)) {
1608 		if (!Z_ISUNDEF(param.parameter)) {
1609 			zval_ptr_dtor(&(param.parameter));
1610 			ZVAL_UNDEF(&param.parameter);
1611 		}
1612 		RETURN_FALSE;
1613 	}
1614 	RETURN_TRUE;
1615 }
1616 /* }}} */
1617 
1618 /* {{{ proto bool PDOStatement::bindParam(mixed $paramno, mixed &$param [, int $type [, int $maxlen [, mixed $driverdata]]])
1619    bind a parameter to a PHP variable.  $paramno is the 1-based position of the placeholder in the SQL statement (but can be the parameter name for drivers that support named placeholders).  This isn't supported by all drivers.  It should be called prior to execute(). */
PHP_METHOD(PDOStatement,bindParam)1620 static PHP_METHOD(PDOStatement, bindParam)
1621 {
1622 	PHP_STMT_GET_OBJ;
1623 	RETURN_BOOL(register_bound_param(INTERNAL_FUNCTION_PARAM_PASSTHRU, stmt, TRUE));
1624 }
1625 /* }}} */
1626 
1627 /* {{{ proto bool PDOStatement::bindColumn(mixed $column, mixed &$param [, int $type [, int $maxlen [, mixed $driverdata]]])
1628    bind a column to a PHP variable.  On each row fetch $param will contain the value of the corresponding column.  $column is the 1-based offset of the column, or the column name.  For portability, don't call this before execute(). */
PHP_METHOD(PDOStatement,bindColumn)1629 static PHP_METHOD(PDOStatement, bindColumn)
1630 {
1631 	PHP_STMT_GET_OBJ;
1632 	RETURN_BOOL(register_bound_param(INTERNAL_FUNCTION_PARAM_PASSTHRU, stmt, 0));
1633 }
1634 /* }}} */
1635 
1636 /* {{{ proto int PDOStatement::rowCount()
1637    Returns the number of rows in a result set, or the number of rows affected by the last execute().  It is not always meaningful. */
PHP_METHOD(PDOStatement,rowCount)1638 static PHP_METHOD(PDOStatement, rowCount)
1639 {
1640 	PHP_STMT_GET_OBJ;
1641 
1642 	RETURN_LONG(stmt->row_count);
1643 }
1644 /* }}} */
1645 
1646 /* {{{ proto string PDOStatement::errorCode()
1647    Fetch the error code associated with the last operation on the statement handle */
PHP_METHOD(PDOStatement,errorCode)1648 static PHP_METHOD(PDOStatement, errorCode)
1649 {
1650 	PHP_STMT_GET_OBJ;
1651 
1652 	if (zend_parse_parameters_none() == FAILURE) {
1653 		return;
1654 	}
1655 
1656 	if (stmt->error_code[0] == '\0') {
1657 		RETURN_NULL();
1658 	}
1659 
1660 	RETURN_STRING(stmt->error_code);
1661 }
1662 /* }}} */
1663 
1664 /* {{{ proto array PDOStatement::errorInfo()
1665    Fetch extended error information associated with the last operation on the statement handle */
PHP_METHOD(PDOStatement,errorInfo)1666 static PHP_METHOD(PDOStatement, errorInfo)
1667 {
1668 	int error_count;
1669 	int error_count_diff     = 0;
1670 	int error_expected_count = 3;
1671 
1672 	PHP_STMT_GET_OBJ;
1673 
1674 	if (zend_parse_parameters_none() == FAILURE) {
1675 		return;
1676 	}
1677 
1678 	array_init(return_value);
1679 	add_next_index_string(return_value, stmt->error_code);
1680 
1681 	if (stmt->dbh->methods->fetch_err) {
1682 		stmt->dbh->methods->fetch_err(stmt->dbh, stmt, return_value);
1683 	}
1684 
1685 	error_count = zend_hash_num_elements(Z_ARRVAL_P(return_value));
1686 
1687 	if (error_expected_count > error_count) {
1688 		int current_index;
1689 
1690 		error_count_diff = error_expected_count - error_count;
1691 		for (current_index = 0; current_index < error_count_diff; current_index++) {
1692 			add_next_index_null(return_value);
1693 		}
1694 	}
1695 }
1696 /* }}} */
1697 
1698 /* {{{ proto bool PDOStatement::setAttribute(long attribute, mixed value)
1699    Set an attribute */
PHP_METHOD(PDOStatement,setAttribute)1700 static PHP_METHOD(PDOStatement, setAttribute)
1701 {
1702 	zend_long attr;
1703 	zval *value = NULL;
1704 	PHP_STMT_GET_OBJ;
1705 
1706 	ZEND_PARSE_PARAMETERS_START(2, 2)
1707 		Z_PARAM_LONG(attr)
1708 		Z_PARAM_ZVAL_EX(value, 1, 0)
1709 	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1710 
1711 	if (!stmt->methods->set_attribute) {
1712 		goto fail;
1713 	}
1714 
1715 	PDO_STMT_CLEAR_ERR();
1716 	if (stmt->methods->set_attribute(stmt, attr, value)) {
1717 		RETURN_TRUE;
1718 	}
1719 
1720 fail:
1721 	if (!stmt->methods->set_attribute) {
1722 		pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "This driver doesn't support setting attributes");
1723 	} else {
1724 		PDO_HANDLE_STMT_ERR();
1725 	}
1726 	RETURN_FALSE;
1727 }
1728 /* }}} */
1729 
1730 /* {{{ proto mixed PDOStatement::getAttribute(long attribute)
1731    Get an attribute */
1732 
generic_stmt_attr_get(pdo_stmt_t * stmt,zval * return_value,zend_long attr)1733 static int generic_stmt_attr_get(pdo_stmt_t *stmt, zval *return_value, zend_long attr)
1734 {
1735 	switch (attr) {
1736 		case PDO_ATTR_EMULATE_PREPARES:
1737 			RETVAL_BOOL(stmt->supports_placeholders == PDO_PLACEHOLDER_NONE);
1738 			return 1;
1739 	}
1740 	return 0;
1741 }
1742 
PHP_METHOD(PDOStatement,getAttribute)1743 static PHP_METHOD(PDOStatement, getAttribute)
1744 {
1745 	zend_long attr;
1746 	PHP_STMT_GET_OBJ;
1747 
1748 	ZEND_PARSE_PARAMETERS_START(1, 1)
1749 		Z_PARAM_LONG(attr)
1750 	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1751 
1752 	if (!stmt->methods->get_attribute) {
1753 		if (!generic_stmt_attr_get(stmt, return_value, attr)) {
1754 			pdo_raise_impl_error(stmt->dbh, stmt, "IM001",
1755 				"This driver doesn't support getting attributes");
1756 			RETURN_FALSE;
1757 		}
1758 		return;
1759 	}
1760 
1761 	PDO_STMT_CLEAR_ERR();
1762 	switch (stmt->methods->get_attribute(stmt, attr, return_value)) {
1763 		case -1:
1764 			PDO_HANDLE_STMT_ERR();
1765 			RETURN_FALSE;
1766 
1767 		case 0:
1768 			if (!generic_stmt_attr_get(stmt, return_value, attr)) {
1769 				/* XXX: should do something better here */
1770 				pdo_raise_impl_error(stmt->dbh, stmt, "IM001",
1771 					"driver doesn't support getting that attribute");
1772 				RETURN_FALSE;
1773 			}
1774 			return;
1775 
1776 		default:
1777 			return;
1778 	}
1779 }
1780 /* }}} */
1781 
1782 /* {{{ proto int PDOStatement::columnCount()
1783    Returns the number of columns in the result set */
PHP_METHOD(PDOStatement,columnCount)1784 static PHP_METHOD(PDOStatement, columnCount)
1785 {
1786 	PHP_STMT_GET_OBJ;
1787 	if (zend_parse_parameters_none() == FAILURE) {
1788 		return;
1789 	}
1790 	RETURN_LONG(stmt->column_count);
1791 }
1792 /* }}} */
1793 
1794 /* {{{ proto array PDOStatement::getColumnMeta(int $column)
1795    Returns meta data for a numbered column */
PHP_METHOD(PDOStatement,getColumnMeta)1796 static PHP_METHOD(PDOStatement, getColumnMeta)
1797 {
1798 	zend_long colno;
1799 	struct pdo_column_data *col;
1800 	PHP_STMT_GET_OBJ;
1801 
1802 	ZEND_PARSE_PARAMETERS_START(1, 1)
1803 		Z_PARAM_LONG(colno)
1804 	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1805 
1806 	if(colno < 0) {
1807 		pdo_raise_impl_error(stmt->dbh, stmt, "42P10", "column number must be non-negative");
1808 		RETURN_FALSE;
1809 	}
1810 
1811 	if (!stmt->methods->get_column_meta) {
1812 		pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "driver doesn't support meta data");
1813 		RETURN_FALSE;
1814 	}
1815 
1816 	PDO_STMT_CLEAR_ERR();
1817 	if (FAILURE == stmt->methods->get_column_meta(stmt, colno, return_value)) {
1818 		PDO_HANDLE_STMT_ERR();
1819 		RETURN_FALSE;
1820 	}
1821 
1822 	/* add stock items */
1823 	col = &stmt->columns[colno];
1824 	add_assoc_str(return_value, "name", zend_string_copy(col->name));
1825 	add_assoc_long(return_value, "len", col->maxlen); /* FIXME: unsigned ? */
1826 	add_assoc_long(return_value, "precision", col->precision);
1827 	if (col->param_type != PDO_PARAM_ZVAL) {
1828 		/* if param_type is PDO_PARAM_ZVAL the driver has to provide correct data */
1829 		add_assoc_long(return_value, "pdo_type", col->param_type);
1830 	}
1831 }
1832 /* }}} */
1833 
1834 /* {{{ proto bool PDOStatement::setFetchMode(int mode [mixed* params])
1835    Changes the default fetch mode for subsequent fetches (params have different meaning for different fetch modes) */
1836 
pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAMETERS,pdo_stmt_t * stmt,int skip)1837 int pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAMETERS, pdo_stmt_t *stmt, int skip)
1838 {
1839 	zend_long mode = PDO_FETCH_BOTH;
1840 	int flags = 0, argc = ZEND_NUM_ARGS() - skip;
1841 	zval *args;
1842 	zend_class_entry *cep;
1843 	int retval;
1844 
1845 	do_fetch_opt_finish(stmt, 1);
1846 
1847 	switch (stmt->default_fetch_type) {
1848 		case PDO_FETCH_INTO:
1849 			if (!Z_ISUNDEF(stmt->fetch.into)) {
1850 				zval_ptr_dtor(&stmt->fetch.into);
1851 				ZVAL_UNDEF(&stmt->fetch.into);
1852 			}
1853 			break;
1854 		default:
1855 			;
1856 	}
1857 
1858 	stmt->default_fetch_type = PDO_FETCH_BOTH;
1859 
1860 	if (argc == 0) {
1861 		return SUCCESS;
1862 	}
1863 
1864 	args = safe_emalloc(ZEND_NUM_ARGS(), sizeof(zval), 0);
1865 
1866 	retval = zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args);
1867 
1868 	if (SUCCESS == retval) {
1869 		if (Z_TYPE(args[skip]) != IS_LONG) {
1870 			pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "mode must be an integer");
1871 			retval = FAILURE;
1872 		} else {
1873 			mode = Z_LVAL(args[skip]);
1874 			flags = mode & PDO_FETCH_FLAGS;
1875 
1876 			/* pdo_stmt_verify_mode() returns a boolean value */
1877 			retval = pdo_stmt_verify_mode(stmt, mode, 0) ? SUCCESS : FAILURE;
1878 		}
1879 	}
1880 
1881 	if (FAILURE == retval) {
1882 		PDO_STMT_CLEAR_ERR();
1883 		efree(args);
1884 		return FAILURE;
1885 	}
1886 
1887 	retval = FAILURE;
1888 	switch (mode & ~PDO_FETCH_FLAGS) {
1889 		case PDO_FETCH_USE_DEFAULT:
1890 		case PDO_FETCH_LAZY:
1891 		case PDO_FETCH_ASSOC:
1892 		case PDO_FETCH_NUM:
1893 		case PDO_FETCH_BOTH:
1894 		case PDO_FETCH_OBJ:
1895 		case PDO_FETCH_BOUND:
1896 		case PDO_FETCH_NAMED:
1897 		case PDO_FETCH_KEY_PAIR:
1898 			if (argc != 1) {
1899 				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode doesn't allow any extra arguments");
1900 			} else {
1901 				retval = SUCCESS;
1902 			}
1903 			break;
1904 
1905 		case PDO_FETCH_COLUMN:
1906 			if (argc != 2) {
1907 				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode requires the colno argument");
1908 			} else	if (Z_TYPE(args[skip+1]) != IS_LONG) {
1909 				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "colno must be an integer");
1910 			} else {
1911 				stmt->fetch.column = Z_LVAL(args[skip+1]);
1912 				retval = SUCCESS;
1913 			}
1914 			break;
1915 
1916 		case PDO_FETCH_CLASS:
1917 			/* Gets its class name from 1st column */
1918 			if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
1919 				if (argc != 1) {
1920 					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode doesn't allow any extra arguments");
1921 				} else {
1922 					stmt->fetch.cls.ce = NULL;
1923 					retval = SUCCESS;
1924 				}
1925 			} else {
1926 				if (argc < 2) {
1927 					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode requires the classname argument");
1928 				} else if (argc > 3) {
1929 					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "too many arguments");
1930 				} else if (Z_TYPE(args[skip+1]) != IS_STRING) {
1931 					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "classname must be a string");
1932 				} else {
1933 					cep = zend_lookup_class(Z_STR(args[skip+1]));
1934 					if (cep) {
1935 						retval = SUCCESS;
1936 						stmt->fetch.cls.ce = cep;
1937 					}
1938 				}
1939 			}
1940 
1941 			if (SUCCESS == retval) {
1942 				ZVAL_UNDEF(&stmt->fetch.cls.ctor_args);
1943 #ifdef ilia_0 /* we'll only need this when we have persistent statements, if ever */
1944 				if (stmt->dbh->is_persistent) {
1945 					php_error_docref(NULL, E_WARNING, "PHP might crash if you don't call $stmt->setFetchMode() to reset to defaults on this persistent statement.  This will be fixed in a later release");
1946 				}
1947 #endif
1948 				if (argc == 3) {
1949 					if (Z_TYPE(args[skip+2]) != IS_NULL && Z_TYPE(args[skip+2]) != IS_ARRAY) {
1950 						pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "ctor_args must be either NULL or an array");
1951 						retval = FAILURE;
1952 					} else if (Z_TYPE(args[skip+2]) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL(args[skip+2]))) {
1953 						ZVAL_ARR(&stmt->fetch.cls.ctor_args, zend_array_dup(Z_ARRVAL(args[skip+2])));
1954 					}
1955 				}
1956 
1957 				if (SUCCESS == retval) {
1958 					do_fetch_class_prepare(stmt);
1959 				}
1960 			}
1961 
1962 			break;
1963 
1964 		case PDO_FETCH_INTO:
1965 			if (argc != 2) {
1966 				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode requires the object parameter");
1967 			} else if (Z_TYPE(args[skip+1]) != IS_OBJECT) {
1968 				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "object must be an object");
1969 			} else {
1970 				retval = SUCCESS;
1971 			}
1972 
1973 			if (SUCCESS == retval) {
1974 #ifdef ilia_0 /* we'll only need this when we have persistent statements, if ever */
1975 				if (stmt->dbh->is_persistent) {
1976 					php_error_docref(NULL, E_WARNING, "PHP might crash if you don't call $stmt->setFetchMode() to reset to defaults on this persistent statement.  This will be fixed in a later release");
1977 				}
1978 #endif
1979 				ZVAL_COPY(&stmt->fetch.into, &args[skip+1]);
1980 			}
1981 
1982 			break;
1983 
1984 		default:
1985 			pdo_raise_impl_error(stmt->dbh, stmt, "22003", "Invalid fetch mode specified");
1986 	}
1987 
1988 	if (SUCCESS == retval) {
1989 		stmt->default_fetch_type = mode;
1990 	}
1991 
1992 	/*
1993 	 * PDO error (if any) has already been raised at this point.
1994 	 *
1995 	 * The error_code is cleared, otherwise the caller will read the
1996 	 * last error message from the driver.
1997 	 *
1998 	 */
1999 	PDO_STMT_CLEAR_ERR();
2000 
2001 	efree(args);
2002 
2003 	return retval;
2004 }
2005 
PHP_METHOD(PDOStatement,setFetchMode)2006 static PHP_METHOD(PDOStatement, setFetchMode)
2007 {
2008 	PHP_STMT_GET_OBJ;
2009 
2010 	RETVAL_BOOL(
2011 		pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAM_PASSTHRU,
2012 			stmt, 0) == SUCCESS ? 1 : 0
2013 		);
2014 }
2015 /* }}} */
2016 
2017 /* {{{ proto bool PDOStatement::nextRowset()
2018    Advances to the next rowset in a multi-rowset statement handle. Returns true if it succeeded, false otherwise */
2019 
pdo_stmt_do_next_rowset(pdo_stmt_t * stmt)2020 static int pdo_stmt_do_next_rowset(pdo_stmt_t *stmt)
2021 {
2022 	/* un-describe */
2023 	if (stmt->columns) {
2024 		int i;
2025 		struct pdo_column_data *cols = stmt->columns;
2026 
2027 		for (i = 0; i < stmt->column_count; i++) {
2028 			if (cols[i].name) {
2029 				zend_string_release_ex(cols[i].name, 0);
2030 			}
2031 		}
2032 		efree(stmt->columns);
2033 		stmt->columns = NULL;
2034 		stmt->column_count = 0;
2035 	}
2036 
2037 	if (!stmt->methods->next_rowset(stmt)) {
2038 		/* Set the executed flag to 0 to reallocate columns on next execute */
2039 		stmt->executed = 0;
2040 		return 0;
2041 	}
2042 
2043 	pdo_stmt_describe_columns(stmt);
2044 
2045 	return 1;
2046 }
2047 
PHP_METHOD(PDOStatement,nextRowset)2048 static PHP_METHOD(PDOStatement, nextRowset)
2049 {
2050 	PHP_STMT_GET_OBJ;
2051 
2052 	if (!stmt->methods->next_rowset) {
2053 		pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "driver does not support multiple rowsets");
2054 		RETURN_FALSE;
2055 	}
2056 
2057 	PDO_STMT_CLEAR_ERR();
2058 
2059 	if (!pdo_stmt_do_next_rowset(stmt)) {
2060 		PDO_HANDLE_STMT_ERR();
2061 		RETURN_FALSE;
2062 	}
2063 
2064 	RETURN_TRUE;
2065 }
2066 /* }}} */
2067 
2068 /* {{{ proto bool PDOStatement::closeCursor()
2069    Closes the cursor, leaving the statement ready for re-execution. */
PHP_METHOD(PDOStatement,closeCursor)2070 static PHP_METHOD(PDOStatement, closeCursor)
2071 {
2072 	PHP_STMT_GET_OBJ;
2073 
2074 	if (!stmt->methods->cursor_closer) {
2075 		/* emulate it by fetching and discarding rows */
2076 		do {
2077 			while (stmt->methods->fetcher(stmt, PDO_FETCH_ORI_NEXT, 0))
2078 				;
2079 			if (!stmt->methods->next_rowset) {
2080 				break;
2081 			}
2082 
2083 			if (!pdo_stmt_do_next_rowset(stmt)) {
2084 				break;
2085 			}
2086 
2087 		} while (1);
2088 		stmt->executed = 0;
2089 		RETURN_TRUE;
2090 	}
2091 
2092 	PDO_STMT_CLEAR_ERR();
2093 
2094 	if (!stmt->methods->cursor_closer(stmt)) {
2095 		PDO_HANDLE_STMT_ERR();
2096 		RETURN_FALSE;
2097 	}
2098 	stmt->executed = 0;
2099 	RETURN_TRUE;
2100 }
2101 /* }}} */
2102 
2103 /* {{{ proto void PDOStatement::debugDumpParams()
2104    A utility for internals hackers to debug parameter internals */
PHP_METHOD(PDOStatement,debugDumpParams)2105 static PHP_METHOD(PDOStatement, debugDumpParams)
2106 {
2107 	php_stream *out = php_stream_open_wrapper("php://output", "w", 0, NULL);
2108 	struct pdo_bound_param_data *param;
2109 	PHP_STMT_GET_OBJ;
2110 
2111 	if (out == NULL) {
2112 		RETURN_FALSE;
2113 	}
2114 
2115 	/* break into multiple operations so query string won't be truncated at FORMAT_CONV_MAX_PRECISION */
2116 	php_stream_printf(out, "SQL: [%zd] ", stmt->query_stringlen);
2117 	php_stream_write(out, stmt->query_string, stmt->query_stringlen);
2118 	php_stream_write(out, "\n", 1);
2119 
2120 	/* show parsed SQL if emulated prepares enabled */
2121 	/* pointers will be equal if PDO::query() was invoked */
2122 	if (stmt->active_query_string != NULL && stmt->active_query_string != stmt->query_string) {
2123 		/* break into multiple operations so query string won't be truncated at FORMAT_CONV_MAX_PRECISION */
2124 		php_stream_printf(out, "Sent SQL: [%zd] ", stmt->active_query_stringlen);
2125 		php_stream_write(out, stmt->active_query_string, stmt->active_query_stringlen);
2126 		php_stream_write(out, "\n", 1);
2127 	}
2128 
2129 	php_stream_printf(out, "Params:  %d\n",
2130 		stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0);
2131 
2132 	if (stmt->bound_params) {
2133 		zend_ulong num;
2134 		zend_string *key = NULL;
2135 		ZEND_HASH_FOREACH_KEY_PTR(stmt->bound_params, num, key, param) {
2136 			if (key) {
2137 				php_stream_printf(out, "Key: Name: [%zd] %.*s\n",
2138 					ZSTR_LEN(key), (int) ZSTR_LEN(key), ZSTR_VAL(key));
2139 			} else {
2140 				php_stream_printf(out, "Key: Position #" ZEND_ULONG_FMT ":\n", num);
2141 			}
2142 
2143 			php_stream_printf(out,
2144 				"paramno=" ZEND_LONG_FMT "\n"
2145 				"name=[%zd] \"%.*s\"\n"
2146 				"is_param=%d\n"
2147 				"param_type=%d\n",
2148 				param->paramno, param->name ? ZSTR_LEN(param->name) : 0, param->name ? (int) ZSTR_LEN(param->name) : 0,
2149 				param->name ? ZSTR_VAL(param->name) : "",
2150 				param->is_param,
2151 				param->param_type);
2152 
2153 		} ZEND_HASH_FOREACH_END();
2154 	}
2155 
2156 	php_stream_close(out);
2157 }
2158 /* }}} */
2159 
2160 /* {{{ proto PDOStatement::__wakeup()
2161    Prevents use of a PDOStatement instance that has been unserialized */
PHP_METHOD(PDOStatement,__wakeup)2162 static PHP_METHOD(PDOStatement, __wakeup)
2163 {
2164 	zend_throw_exception_ex(php_pdo_get_exception(), 0, "You cannot serialize or unserialize PDOStatement instances");
2165 }
2166 /* }}} */
2167 
2168 /* {{{ proto int PDOStatement::__sleep()
2169    Prevents serialization of a PDOStatement instance */
PHP_METHOD(PDOStatement,__sleep)2170 static PHP_METHOD(PDOStatement, __sleep)
2171 {
2172 	zend_throw_exception_ex(php_pdo_get_exception(), 0, "You cannot serialize or unserialize PDOStatement instances");
2173 }
2174 /* }}} */
2175 
2176 const zend_function_entry pdo_dbstmt_functions[] = {
2177 	PHP_ME(PDOStatement, execute,		arginfo_pdostatement_execute,		ZEND_ACC_PUBLIC)
2178 	PHP_ME(PDOStatement, fetch,			arginfo_pdostatement_fetch,			ZEND_ACC_PUBLIC)
2179 	PHP_ME(PDOStatement, bindParam,		arginfo_pdostatement_bindparam,		ZEND_ACC_PUBLIC)
2180 	PHP_ME(PDOStatement, bindColumn,	arginfo_pdostatement_bindcolumn,	ZEND_ACC_PUBLIC)
2181 	PHP_ME(PDOStatement, bindValue,		arginfo_pdostatement_bindvalue,		ZEND_ACC_PUBLIC)
2182 	PHP_ME(PDOStatement, rowCount,		arginfo_pdostatement__void,			ZEND_ACC_PUBLIC)
2183 	PHP_ME(PDOStatement, fetchColumn,	arginfo_pdostatement_fetchcolumn,	ZEND_ACC_PUBLIC)
2184 	PHP_ME(PDOStatement, fetchAll,		arginfo_pdostatement_fetchall,		ZEND_ACC_PUBLIC)
2185 	PHP_ME(PDOStatement, fetchObject,	arginfo_pdostatement_fetchobject,	ZEND_ACC_PUBLIC)
2186 	PHP_ME(PDOStatement, errorCode,		arginfo_pdostatement__void,			ZEND_ACC_PUBLIC)
2187 	PHP_ME(PDOStatement, errorInfo,		arginfo_pdostatement__void,			ZEND_ACC_PUBLIC)
2188 	PHP_ME(PDOStatement, setAttribute,	arginfo_pdostatement_setattribute,	ZEND_ACC_PUBLIC)
2189 	PHP_ME(PDOStatement, getAttribute,	arginfo_pdostatement_getattribute,	ZEND_ACC_PUBLIC)
2190 	PHP_ME(PDOStatement, columnCount,	arginfo_pdostatement__void,			ZEND_ACC_PUBLIC)
2191 	PHP_ME(PDOStatement, getColumnMeta,	arginfo_pdostatement_getcolumnmeta,	ZEND_ACC_PUBLIC)
2192 	PHP_ME(PDOStatement, setFetchMode,	arginfo_pdostatement_setfetchmode,	ZEND_ACC_PUBLIC)
2193 	PHP_ME(PDOStatement, nextRowset,	arginfo_pdostatement__void,			ZEND_ACC_PUBLIC)
2194 	PHP_ME(PDOStatement, closeCursor,	arginfo_pdostatement__void,			ZEND_ACC_PUBLIC)
2195 	PHP_ME(PDOStatement, debugDumpParams, arginfo_pdostatement__void,		ZEND_ACC_PUBLIC)
2196 	PHP_ME(PDOStatement, __wakeup,		arginfo_pdostatement__void,			ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
2197 	PHP_ME(PDOStatement, __sleep,		arginfo_pdostatement__void,			ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
2198 	PHP_FE_END
2199 };
2200 
2201 /* {{{ overloaded handlers for PDOStatement class */
dbstmt_prop_write(zval * object,zval * member,zval * value,void ** cache_slot)2202 static void dbstmt_prop_write(zval *object, zval *member, zval *value, void **cache_slot)
2203 {
2204 	pdo_stmt_t *stmt = Z_PDO_STMT_P(object);
2205 
2206 	convert_to_string(member);
2207 
2208 	if (strcmp(Z_STRVAL_P(member), "queryString") == 0) {
2209 		pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "property queryString is read only");
2210 	} else {
2211 		zend_std_write_property(object, member, value, cache_slot);
2212 	}
2213 }
2214 
dbstmt_prop_delete(zval * object,zval * member,void ** cache_slot)2215 static void dbstmt_prop_delete(zval *object, zval *member, void **cache_slot)
2216 {
2217 	pdo_stmt_t *stmt = Z_PDO_STMT_P(object);
2218 
2219 	convert_to_string(member);
2220 
2221 	if (strcmp(Z_STRVAL_P(member), "queryString") == 0) {
2222 		pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "property queryString is read only");
2223 	} else {
2224 		zend_std_unset_property(object, member, cache_slot);
2225 	}
2226 }
2227 
dbstmt_method_get(zend_object ** object_pp,zend_string * method_name,const zval * key)2228 static union _zend_function *dbstmt_method_get(zend_object **object_pp, zend_string *method_name, const zval *key)
2229 {
2230 	zend_function *fbc = NULL;
2231 	zend_string *lc_method_name;
2232 	zend_object *object = *object_pp;
2233 
2234 	lc_method_name = zend_string_tolower(method_name);
2235 
2236 	if ((fbc = zend_hash_find_ptr(&object->ce->function_table, lc_method_name)) == NULL) {
2237 		pdo_stmt_t *stmt = php_pdo_stmt_fetch_object(object);
2238 		/* instance not created by PDO object */
2239 		if (!stmt->dbh) {
2240 			goto out;
2241 		}
2242 		/* not a pre-defined method, nor a user-defined method; check
2243 		 * the driver specific methods */
2244 		if (!stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT]) {
2245 			if (!pdo_hash_methods(Z_PDO_OBJECT_P(&stmt->database_object_handle),
2246 				PDO_DBH_DRIVER_METHOD_KIND_STMT)
2247 				|| !stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT]) {
2248 				goto out;
2249 			}
2250 		}
2251 
2252 		if ((fbc = zend_hash_find_ptr(stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT], lc_method_name)) == NULL) {
2253 			goto out;
2254 		}
2255 		/* got it */
2256 	}
2257 
2258 out:
2259 	zend_string_release_ex(lc_method_name, 0);
2260 	if (!fbc) {
2261 		fbc = zend_std_get_method(object_pp, method_name, key);
2262 	}
2263 	return fbc;
2264 }
2265 
dbstmt_compare(zval * object1,zval * object2)2266 static int dbstmt_compare(zval *object1, zval *object2)
2267 {
2268 	return -1;
2269 }
2270 
2271 zend_object_handlers pdo_dbstmt_object_handlers;
2272 static int pdo_row_serialize(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data);
2273 
pdo_stmt_init(void)2274 void pdo_stmt_init(void)
2275 {
2276 	zend_class_entry ce;
2277 
2278 	INIT_CLASS_ENTRY(ce, "PDOStatement", pdo_dbstmt_functions);
2279 	pdo_dbstmt_ce = zend_register_internal_class(&ce);
2280 	pdo_dbstmt_ce->get_iterator = pdo_stmt_iter_get;
2281 	pdo_dbstmt_ce->create_object = pdo_dbstmt_new;
2282 	zend_class_implements(pdo_dbstmt_ce, 1, zend_ce_traversable);
2283 	zend_declare_property_null(pdo_dbstmt_ce, "queryString", sizeof("queryString")-1, ZEND_ACC_PUBLIC);
2284 
2285 	memcpy(&pdo_dbstmt_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
2286 	pdo_dbstmt_object_handlers.offset = XtOffsetOf(pdo_stmt_t, std);
2287 	pdo_dbstmt_object_handlers.dtor_obj = zend_objects_destroy_object;
2288 	pdo_dbstmt_object_handlers.free_obj = pdo_dbstmt_free_storage;
2289 	pdo_dbstmt_object_handlers.write_property = dbstmt_prop_write;
2290 	pdo_dbstmt_object_handlers.unset_property = dbstmt_prop_delete;
2291 	pdo_dbstmt_object_handlers.get_method = dbstmt_method_get;
2292 	pdo_dbstmt_object_handlers.compare_objects = dbstmt_compare;
2293 	pdo_dbstmt_object_handlers.clone_obj = NULL;
2294 
2295 	INIT_CLASS_ENTRY(ce, "PDORow", pdo_row_functions);
2296 	pdo_row_ce = zend_register_internal_class(&ce);
2297 	pdo_row_ce->ce_flags |= ZEND_ACC_FINAL; /* when removing this a lot of handlers need to be redone */
2298 	pdo_row_ce->create_object = pdo_row_new;
2299 	pdo_row_ce->serialize = pdo_row_serialize;
2300 	pdo_row_ce->unserialize = zend_class_unserialize_deny;
2301 }
2302 
php_pdo_free_statement(pdo_stmt_t * stmt)2303 PDO_API void php_pdo_free_statement(pdo_stmt_t *stmt)
2304 {
2305 	if (stmt->bound_params) {
2306 		zend_hash_destroy(stmt->bound_params);
2307 		FREE_HASHTABLE(stmt->bound_params);
2308 		stmt->bound_params = NULL;
2309 	}
2310 	if (stmt->bound_param_map) {
2311 		zend_hash_destroy(stmt->bound_param_map);
2312 		FREE_HASHTABLE(stmt->bound_param_map);
2313 		stmt->bound_param_map = NULL;
2314 	}
2315 	if (stmt->bound_columns) {
2316 		zend_hash_destroy(stmt->bound_columns);
2317 		FREE_HASHTABLE(stmt->bound_columns);
2318 		stmt->bound_columns = NULL;
2319 	}
2320 
2321 	if (stmt->methods && stmt->methods->dtor) {
2322 		stmt->methods->dtor(stmt);
2323 	}
2324 	if (stmt->active_query_string && stmt->active_query_string != stmt->query_string) {
2325 		efree(stmt->active_query_string);
2326 	}
2327 	if (stmt->query_string) {
2328 		efree(stmt->query_string);
2329 	}
2330 
2331 	if (stmt->columns) {
2332 		int i;
2333 		struct pdo_column_data *cols = stmt->columns;
2334 
2335 		for (i = 0; i < stmt->column_count; i++) {
2336 			if (cols[i].name) {
2337 				zend_string_release_ex(cols[i].name, 0);
2338 				cols[i].name = NULL;
2339 			}
2340 		}
2341 		efree(stmt->columns);
2342 		stmt->columns = NULL;
2343 	}
2344 
2345 	if (!Z_ISUNDEF(stmt->fetch.into) && stmt->default_fetch_type == PDO_FETCH_INTO) {
2346 		zval_ptr_dtor(&stmt->fetch.into);
2347 		ZVAL_UNDEF(&stmt->fetch.into);
2348 	}
2349 
2350 	do_fetch_opt_finish(stmt, 1);
2351 
2352 	if (!Z_ISUNDEF(stmt->database_object_handle)) {
2353 		zval_ptr_dtor(&stmt->database_object_handle);
2354 	}
2355 	zend_object_std_dtor(&stmt->std);
2356 }
2357 
pdo_dbstmt_free_storage(zend_object * std)2358 void pdo_dbstmt_free_storage(zend_object *std)
2359 {
2360 	pdo_stmt_t *stmt = php_pdo_stmt_fetch_object(std);
2361 	php_pdo_free_statement(stmt);
2362 }
2363 
pdo_dbstmt_new(zend_class_entry * ce)2364 zend_object *pdo_dbstmt_new(zend_class_entry *ce)
2365 {
2366 	pdo_stmt_t *stmt;
2367 
2368 	stmt = zend_object_alloc(sizeof(pdo_stmt_t), ce);
2369 	zend_object_std_init(&stmt->std, ce);
2370 	object_properties_init(&stmt->std, ce);
2371 
2372 	stmt->std.handlers = &pdo_dbstmt_object_handlers;
2373 
2374 	return &stmt->std;
2375 }
2376 /* }}} */
2377 
2378 /* {{{ statement iterator */
2379 
2380 struct php_pdo_iterator {
2381 	zend_object_iterator iter;
2382 	zend_ulong key;
2383 	zval fetch_ahead;
2384 };
2385 
pdo_stmt_iter_dtor(zend_object_iterator * iter)2386 static void pdo_stmt_iter_dtor(zend_object_iterator *iter)
2387 {
2388 	struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2389 
2390 	zval_ptr_dtor(&I->iter.data);
2391 
2392 	if (!Z_ISUNDEF(I->fetch_ahead)) {
2393 		zval_ptr_dtor(&I->fetch_ahead);
2394 	}
2395 }
2396 
pdo_stmt_iter_valid(zend_object_iterator * iter)2397 static int pdo_stmt_iter_valid(zend_object_iterator *iter)
2398 {
2399 	struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2400 
2401 	return Z_ISUNDEF(I->fetch_ahead) ? FAILURE : SUCCESS;
2402 }
2403 
pdo_stmt_iter_get_data(zend_object_iterator * iter)2404 static zval *pdo_stmt_iter_get_data(zend_object_iterator *iter)
2405 {
2406 	struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2407 
2408 	/* sanity */
2409 	if (Z_ISUNDEF(I->fetch_ahead)) {
2410 		return NULL;
2411 	}
2412 
2413 	return &I->fetch_ahead;
2414 }
2415 
pdo_stmt_iter_get_key(zend_object_iterator * iter,zval * key)2416 static void pdo_stmt_iter_get_key(zend_object_iterator *iter, zval *key)
2417 {
2418 	struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2419 
2420 	if (I->key == (ulong)-1) {
2421 		ZVAL_NULL(key);
2422 	} else {
2423 		ZVAL_LONG(key, I->key);
2424 	}
2425 }
2426 
pdo_stmt_iter_move_forwards(zend_object_iterator * iter)2427 static void pdo_stmt_iter_move_forwards(zend_object_iterator *iter)
2428 {
2429 	struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2430 	pdo_stmt_t *stmt = Z_PDO_STMT_P(&I->iter.data); /* for PDO_HANDLE_STMT_ERR() */
2431 
2432 	if (!Z_ISUNDEF(I->fetch_ahead)) {
2433 		zval_ptr_dtor(&I->fetch_ahead);
2434 	}
2435 
2436 	if (!do_fetch(stmt, TRUE, &I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2437 			PDO_FETCH_ORI_NEXT, 0, 0)) {
2438 
2439 		PDO_HANDLE_STMT_ERR();
2440 		I->key = (ulong)-1;
2441 		ZVAL_UNDEF(&I->fetch_ahead);
2442 
2443 		return;
2444 	}
2445 
2446 	I->key++;
2447 }
2448 
2449 static const zend_object_iterator_funcs pdo_stmt_iter_funcs = {
2450 	pdo_stmt_iter_dtor,
2451 	pdo_stmt_iter_valid,
2452 	pdo_stmt_iter_get_data,
2453 	pdo_stmt_iter_get_key,
2454 	pdo_stmt_iter_move_forwards,
2455 	NULL,
2456 	NULL
2457 };
2458 
pdo_stmt_iter_get(zend_class_entry * ce,zval * object,int by_ref)2459 zend_object_iterator *pdo_stmt_iter_get(zend_class_entry *ce, zval *object, int by_ref)
2460 {
2461 	pdo_stmt_t *stmt = Z_PDO_STMT_P(object);
2462 	struct php_pdo_iterator *I;
2463 
2464 	if (by_ref) {
2465 		zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
2466 		return NULL;
2467 	}
2468 
2469 	I = ecalloc(1, sizeof(struct php_pdo_iterator));
2470 	zend_iterator_init(&I->iter);
2471 	I->iter.funcs = &pdo_stmt_iter_funcs;
2472 	ZVAL_COPY(&I->iter.data, object);
2473 
2474 	if (!do_fetch(stmt, 1, &I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2475 			PDO_FETCH_ORI_NEXT, 0, 0)) {
2476 		PDO_HANDLE_STMT_ERR();
2477 		I->key = (ulong)-1;
2478 		ZVAL_UNDEF(&I->fetch_ahead);
2479 	}
2480 
2481 	return &I->iter;
2482 }
2483 
2484 /* }}} */
2485 
2486 /* {{{ overloaded handlers for PDORow class (used by PDO_FETCH_LAZY) */
2487 
2488 const zend_function_entry pdo_row_functions[] = {
2489 	PHP_FE_END
2490 };
2491 
row_prop_read(zval * object,zval * member,int type,void ** cache_slot,zval * rv)2492 static zval *row_prop_read(zval *object, zval *member, int type, void **cache_slot, zval *rv)
2493 {
2494 	pdo_row_t *row = (pdo_row_t *)Z_OBJ_P(object);
2495 	pdo_stmt_t *stmt = row->stmt;
2496 	int colno = -1;
2497 	zval zobj;
2498 	zend_long lval;
2499 
2500 	ZVAL_NULL(rv);
2501 	if (stmt) {
2502 		if (Z_TYPE_P(member) == IS_LONG) {
2503 			if (Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count) {
2504 				fetch_value(stmt, rv, Z_LVAL_P(member), NULL);
2505 			}
2506 		} else if (Z_TYPE_P(member) == IS_STRING
2507 			   && is_numeric_string_ex(Z_STRVAL_P(member), Z_STRLEN_P(member), &lval, NULL, 0, NULL) == IS_LONG)	{
2508 			if (lval >= 0 && lval < stmt->column_count) {
2509 				fetch_value(stmt, rv, lval, NULL);
2510 			}
2511 		} else {
2512 			convert_to_string(member);
2513 			/* TODO: replace this with a hash of available column names to column
2514 			 * numbers */
2515 			for (colno = 0; colno < stmt->column_count; colno++) {
2516 				if (ZSTR_LEN(stmt->columns[colno].name) == Z_STRLEN_P(member) &&
2517 				    strncmp(ZSTR_VAL(stmt->columns[colno].name), Z_STRVAL_P(member), Z_STRLEN_P(member)) == 0) {
2518 					fetch_value(stmt, rv, colno, NULL);
2519 					return rv;
2520 				}
2521 			}
2522 			if (strcmp(Z_STRVAL_P(member), "queryString") == 0) {
2523 				ZVAL_OBJ(&zobj, &stmt->std);
2524 				//zval_ptr_dtor(rv);
2525 				return zend_std_read_property(&zobj, member, type, cache_slot, rv);
2526 			}
2527 		}
2528 	}
2529 
2530 	return rv;
2531 }
2532 
row_dim_read(zval * object,zval * member,int type,zval * rv)2533 static zval *row_dim_read(zval *object, zval *member, int type, zval *rv)
2534 {
2535 	return row_prop_read(object, member, type, NULL, rv);
2536 }
2537 
row_prop_write(zval * object,zval * member,zval * value,void ** cache_slot)2538 static void row_prop_write(zval *object, zval *member, zval *value, void **cache_slot)
2539 {
2540 	php_error_docref(NULL, E_WARNING, "This PDORow is not from a writable result set");
2541 }
2542 
row_dim_write(zval * object,zval * member,zval * value)2543 static void row_dim_write(zval *object, zval *member, zval *value)
2544 {
2545 	php_error_docref(NULL, E_WARNING, "This PDORow is not from a writable result set");
2546 }
2547 
row_prop_exists(zval * object,zval * member,int check_empty,void ** cache_slot)2548 static int row_prop_exists(zval *object, zval *member, int check_empty, void **cache_slot)
2549 {
2550 	pdo_row_t *row = (pdo_row_t *)Z_OBJ_P(object);
2551 	pdo_stmt_t *stmt = row->stmt;
2552 	int colno = -1;
2553 	zend_long lval;
2554 
2555 	if (stmt) {
2556 		if (Z_TYPE_P(member) == IS_LONG) {
2557 			return Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count;
2558 		} else if (Z_TYPE_P(member) == IS_STRING) {
2559 			if (is_numeric_string_ex(Z_STRVAL_P(member), Z_STRLEN_P(member), &lval, NULL, 0, NULL) == IS_LONG)	{
2560 				return lval >=0 && lval < stmt->column_count;
2561 			}
2562 		} else {
2563 			convert_to_string(member);
2564 		}
2565 
2566 		/* TODO: replace this with a hash of available column names to column
2567 		 * numbers */
2568 		for (colno = 0; colno < stmt->column_count; colno++) {
2569 			if (ZSTR_LEN(stmt->columns[colno].name) == Z_STRLEN_P(member) &&
2570 			    strncmp(ZSTR_VAL(stmt->columns[colno].name), Z_STRVAL_P(member), Z_STRLEN_P(member)) == 0) {
2571 					int res;
2572 					zval val;
2573 
2574 					fetch_value(stmt, &val, colno, NULL);
2575 					res = check_empty ? i_zend_is_true(&val) : Z_TYPE(val) != IS_NULL;
2576 					zval_ptr_dtor_nogc(&val);
2577 
2578 					return res;
2579 			}
2580 		}
2581 	}
2582 
2583 	return 0;
2584 }
2585 
row_dim_exists(zval * object,zval * member,int check_empty)2586 static int row_dim_exists(zval *object, zval *member, int check_empty)
2587 {
2588 	return row_prop_exists(object, member, check_empty, NULL);
2589 }
2590 
row_prop_delete(zval * object,zval * offset,void ** cache_slot)2591 static void row_prop_delete(zval *object, zval *offset, void **cache_slot)
2592 {
2593 	php_error_docref(NULL, E_WARNING, "Cannot delete properties from a PDORow");
2594 }
2595 
row_dim_delete(zval * object,zval * offset)2596 static void row_dim_delete(zval *object, zval *offset)
2597 {
2598 	php_error_docref(NULL, E_WARNING, "Cannot delete properties from a PDORow");
2599 }
2600 
row_get_properties(zval * object)2601 static HashTable *row_get_properties(zval *object)
2602 {
2603 	pdo_row_t *row = (pdo_row_t *)Z_OBJ_P(object);
2604 	pdo_stmt_t *stmt = row->stmt;
2605 	int i;
2606 
2607 	if (stmt == NULL) {
2608 		return NULL;
2609 	}
2610 
2611 	if (!stmt->std.properties) {
2612 		rebuild_object_properties(&stmt->std);
2613 	}
2614 	for (i = 0; i < stmt->column_count; i++) {
2615 		zval val;
2616 		fetch_value(stmt, &val, i, NULL);
2617 
2618 		zend_hash_update(stmt->std.properties, stmt->columns[i].name, &val);
2619 	}
2620 
2621 	return stmt->std.properties;
2622 }
2623 
row_method_get(zend_object ** object_pp,zend_string * method_name,const zval * key)2624 static union _zend_function *row_method_get(
2625 	zend_object **object_pp,
2626 	zend_string *method_name, const zval *key)
2627 {
2628 	zend_function *fbc;
2629 	zend_string *lc_method_name;
2630 
2631 	lc_method_name = zend_string_tolower(method_name);
2632 
2633 	if ((fbc = zend_hash_find_ptr(&pdo_row_ce->function_table, lc_method_name)) == NULL) {
2634 		zend_string_release_ex(lc_method_name, 0);
2635 		return NULL;
2636 	}
2637 
2638 	zend_string_release_ex(lc_method_name, 0);
2639 
2640 	return fbc;
2641 }
2642 
row_call_method(zend_string * method,zend_object * object,INTERNAL_FUNCTION_PARAMETERS)2643 static int row_call_method(zend_string *method, zend_object *object, INTERNAL_FUNCTION_PARAMETERS)
2644 {
2645 	return FAILURE;
2646 }
2647 
row_get_ctor(zend_object * object)2648 static union _zend_function *row_get_ctor(zend_object *object)
2649 {
2650 	zend_throw_exception_ex(php_pdo_get_exception(), 0, "You may not create a PDORow manually");
2651 	return NULL;
2652 }
2653 
row_get_classname(const zend_object * object)2654 static zend_string *row_get_classname(const zend_object *object)
2655 {
2656 	return zend_string_init("PDORow", sizeof("PDORow") - 1, 0);
2657 }
2658 
row_compare(zval * object1,zval * object2)2659 static int row_compare(zval *object1, zval *object2)
2660 {
2661 	return -1;
2662 }
2663 
2664 const zend_object_handlers pdo_row_object_handlers = {
2665 	0,
2666 	zend_objects_destroy_object,
2667 	pdo_row_free_storage,
2668 	NULL,
2669 	row_prop_read,
2670 	row_prop_write,
2671 	row_dim_read,
2672 	row_dim_write,
2673 	NULL,
2674 	NULL,
2675 	NULL,
2676 	row_prop_exists,
2677 	row_prop_delete,
2678 	row_dim_exists,
2679 	row_dim_delete,
2680 	row_get_properties,
2681 	row_method_get,
2682 	row_call_method,
2683 	row_get_ctor,
2684 	row_get_classname,
2685 	row_compare,
2686 	NULL, /* cast */
2687 	NULL,
2688 	NULL,
2689 	NULL,
2690 	NULL,
2691 	NULL,
2692 	NULL
2693 };
2694 
pdo_row_free_storage(zend_object * std)2695 void pdo_row_free_storage(zend_object *std)
2696 {
2697 	pdo_row_t *row = (pdo_row_t *)std;
2698 	if (row->stmt) {
2699 		ZVAL_UNDEF(&row->stmt->lazy_object_ref);
2700 		OBJ_RELEASE(&row->stmt->std);
2701 	}
2702 }
2703 
pdo_row_new(zend_class_entry * ce)2704 zend_object *pdo_row_new(zend_class_entry *ce)
2705 {
2706 	pdo_row_t *row = ecalloc(1, sizeof(pdo_row_t));
2707 	zend_object_std_init(&row->std, ce);
2708 	row->std.handlers = &pdo_row_object_handlers;
2709 
2710 	return &row->std;
2711 }
2712 
pdo_row_serialize(zval * object,unsigned char ** buffer,size_t * buf_len,zend_serialize_data * data)2713 static int pdo_row_serialize(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data)
2714 {
2715 	php_error_docref(NULL, E_WARNING, "PDORow instances may not be serialized");
2716 	return FAILURE;
2717 }
2718 /* }}} */
2719 
2720 /*
2721  * Local variables:
2722  * tab-width: 4
2723  * c-basic-offset: 4
2724  * End:
2725  * vim600: noet sw=4 ts=4 fdm=marker
2726  * vim<600: noet sw=4 ts=4
2727  */
2728