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 */
39ZEND_BEGIN_ARG_INFO(arginfo_pdostatement__void, 0)
40ZEND_END_ARG_INFO()
41
42ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_execute, 0, 0, 0)
43	ZEND_ARG_INFO(0, bound_input_params) /* array */
44ZEND_END_ARG_INFO()
45
46ZEND_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)
50ZEND_END_ARG_INFO()
51
52ZEND_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 */
55ZEND_END_ARG_INFO()
56
57ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_fetchcolumn, 0, 0, 0)
58	ZEND_ARG_INFO(0, column_number)
59ZEND_END_ARG_INFO()
60
61ZEND_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 */
65ZEND_END_ARG_INFO()
66
67ZEND_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)
71ZEND_END_ARG_INFO()
72
73ZEND_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)
79ZEND_END_ARG_INFO()
80
81ZEND_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)
87ZEND_END_ARG_INFO()
88
89ZEND_BEGIN_ARG_INFO(arginfo_pdostatement_setattribute, 0)
90	ZEND_ARG_INFO(0, attribute)
91	ZEND_ARG_INFO(0, value)
92ZEND_END_ARG_INFO()
93
94ZEND_BEGIN_ARG_INFO(arginfo_pdostatement_getattribute, 0)
95	ZEND_ARG_INFO(0, attribute)
96ZEND_END_ARG_INFO()
97
98ZEND_BEGIN_ARG_INFO(arginfo_pdostatement_getcolumnmeta, 0)
99	ZEND_ARG_INFO(0, column)
100ZEND_END_ARG_INFO()
101
102ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_setfetchmode, 0, 0, 1)
103	ZEND_ARG_INFO(0, mode)
104	ZEND_ARG_INFO(0, params)
105ZEND_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
114static 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 */
160static 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
176iterate:
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
195int 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
243static 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
258static 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
282static 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 */
421static 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
529static 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
670static 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
721static 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
755static 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
780static 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
794static 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. */
822static 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
1201static 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 */
1254static 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. */
1283static 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. */
1350static 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. */
1373static 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
1535static 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(). */
1579static 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(). */
1620static 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(). */
1629static 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. */
1638static 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 */
1648static 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 */
1666static 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 */
1700static 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
1720fail:
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
1733static 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
1743static 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 */
1784static 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 */
1796static 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
1837int 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
2006static 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
2020static 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
2048static 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. */
2070static 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 */
2105static 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 */
2162static 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 */
2170static 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
2176const 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 */
2202static 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
2215static 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
2228static 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
2258out:
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
2266static int dbstmt_compare(zval *object1, zval *object2)
2267{
2268	return -1;
2269}
2270
2271zend_object_handlers pdo_dbstmt_object_handlers;
2272static int pdo_row_serialize(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data);
2273
2274void 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
2303PDO_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
2358void 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
2364zend_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
2380struct php_pdo_iterator {
2381	zend_object_iterator iter;
2382	zend_ulong key;
2383	zval fetch_ahead;
2384};
2385
2386static 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
2397static 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
2404static 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
2416static 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
2427static 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
2449static 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
2459zend_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
2488const zend_function_entry pdo_row_functions[] = {
2489	PHP_FE_END
2490};
2491
2492static 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
2533static 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
2538static 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
2543static 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
2548static 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
2586static int row_dim_exists(zval *object, zval *member, int check_empty)
2587{
2588	return row_prop_exists(object, member, check_empty, NULL);
2589}
2590
2591static 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
2596static void row_dim_delete(zval *object, zval *offset)
2597{
2598	php_error_docref(NULL, E_WARNING, "Cannot delete properties from a PDORow");
2599}
2600
2601static 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
2624static 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
2643static int row_call_method(zend_string *method, zend_object *object, INTERNAL_FUNCTION_PARAMETERS)
2644{
2645	return FAILURE;
2646}
2647
2648static 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
2654static zend_string *row_get_classname(const zend_object *object)
2655{
2656	return zend_string_init("PDORow", sizeof("PDORow") - 1, 0);
2657}
2658
2659static int row_compare(zval *object1, zval *object2)
2660{
2661	return -1;
2662}
2663
2664const 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
2695void 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
2704zend_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
2713static 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