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