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		fci->function_table = &ce->function_table;
744		ZVAL_UNDEF(&fci->function_name);
745		fci->symbol_table = NULL;
746		fci->retval = &stmt->fetch.cls.retval;
747		fci->param_count = 0;
748		fci->params = NULL;
749		fci->no_separation = 1;
750
751		zend_fcall_info_args_ex(fci, ce->constructor, &stmt->fetch.cls.ctor_args);
752
753		fcc->initialized = 1;
754		fcc->function_handler = ce->constructor;
755		fcc->calling_scope = EG(scope);
756		fcc->called_scope = ce;
757		return 1;
758	} else if (!Z_ISUNDEF(stmt->fetch.cls.ctor_args)) {
759		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");
760		return 0;
761	} else {
762		return 1; /* no ctor no args is also ok */
763	}
764}
765/* }}} */
766
767static int make_callable_ex(pdo_stmt_t *stmt, zval *callable, zend_fcall_info * fci, zend_fcall_info_cache * fcc, int num_args) /* {{{ */
768{
769	char *is_callable_error = NULL;
770
771	if (zend_fcall_info_init(callable, 0, fci, fcc, NULL, &is_callable_error) == FAILURE) {
772		if (is_callable_error) {
773			pdo_raise_impl_error(stmt->dbh, stmt, "HY000", is_callable_error);
774			efree(is_callable_error);
775		} else {
776			pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "user-supplied function must be a valid callback");
777		}
778		return 0;
779	}
780	if (is_callable_error) {
781		/* Possible E_STRICT error message */
782		efree(is_callable_error);
783	}
784
785	fci->param_count = num_args; /* probably less */
786	fci->params = safe_emalloc(sizeof(zval), num_args, 0);
787
788	return 1;
789}
790/* }}} */
791
792static int do_fetch_func_prepare(pdo_stmt_t *stmt) /* {{{ */
793{
794	zend_fcall_info *fci = &stmt->fetch.cls.fci;
795	zend_fcall_info_cache *fcc = &stmt->fetch.cls.fcc;
796
797	if (!make_callable_ex(stmt, &stmt->fetch.func.function, fci, fcc, stmt->column_count)) {
798		return 0;
799	} else {
800		stmt->fetch.func.values = safe_emalloc(sizeof(zval), stmt->column_count, 0);
801		return 1;
802	}
803}
804/* }}} */
805
806static void do_fetch_opt_finish(pdo_stmt_t *stmt, int free_ctor_agrs) /* {{{ */
807{
808	/* fci.size is used to check if it is valid */
809	if (stmt->fetch.cls.fci.size && stmt->fetch.cls.fci.params) {
810		if (!Z_ISUNDEF(stmt->fetch.cls.ctor_args)) {
811		    /* Added to free constructor arguments */
812			zend_fcall_info_args_clear(&stmt->fetch.cls.fci, 1);
813		} else {
814			efree(stmt->fetch.cls.fci.params);
815		}
816		stmt->fetch.cls.fci.params = NULL;
817	}
818
819	stmt->fetch.cls.fci.size = 0;
820	if (!Z_ISUNDEF(stmt->fetch.cls.ctor_args) && free_ctor_agrs) {
821		zval_ptr_dtor(&stmt->fetch.cls.ctor_args);
822		ZVAL_UNDEF(&stmt->fetch.cls.ctor_args);
823		stmt->fetch.cls.fci.param_count = 0;
824	}
825	if (stmt->fetch.func.values) {
826		efree(stmt->fetch.func.values);
827		stmt->fetch.func.values = NULL;
828	}
829}
830/* }}} */
831
832/* perform a fetch.  If do_bind is true, update any bound columns.
833 * If return_value is not null, store values into it according to HOW. */
834static 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) /* {{{ */
835{
836	int flags, idx, old_arg_count = 0;
837	zend_class_entry *ce = NULL, *old_ce = NULL;
838	zval grp_val, *pgrp, retval, old_ctor_args;
839	int colno;
840
841	if (how == PDO_FETCH_USE_DEFAULT) {
842		how = stmt->default_fetch_type;
843	}
844	flags = how & PDO_FETCH_FLAGS;
845	how = how & ~PDO_FETCH_FLAGS;
846
847	if (!do_fetch_common(stmt, ori, offset, do_bind)) {
848		return 0;
849	}
850
851	if (how == PDO_FETCH_BOUND) {
852		RETVAL_TRUE;
853		return 1;
854	}
855
856	if (flags & PDO_FETCH_GROUP && stmt->fetch.column == -1) {
857		colno = 1;
858	} else {
859		colno = stmt->fetch.column;
860	}
861
862	if (return_value) {
863		int i = 0;
864
865		if (how == PDO_FETCH_LAZY) {
866			get_lazy_object(stmt, return_value);
867			return 1;
868		}
869
870		RETVAL_FALSE;
871
872		switch (how) {
873			case PDO_FETCH_USE_DEFAULT:
874			case PDO_FETCH_ASSOC:
875			case PDO_FETCH_BOTH:
876			case PDO_FETCH_NUM:
877			case PDO_FETCH_NAMED:
878				if (!return_all) {
879					ZVAL_NEW_ARR(return_value);
880					zend_hash_init(Z_ARRVAL_P(return_value), stmt->column_count, NULL, ZVAL_PTR_DTOR, 0);;
881				} else {
882					array_init(return_value);
883				}
884				break;
885
886			case PDO_FETCH_KEY_PAIR:
887				if (stmt->column_count != 2) {
888					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_KEY_PAIR fetch mode requires the result set to contain extactly 2 columns.");
889					return 0;
890				}
891				if (!return_all) {
892					array_init(return_value);
893				}
894				break;
895
896			case PDO_FETCH_COLUMN:
897				if (colno >= 0 && colno < stmt->column_count) {
898					if (flags == PDO_FETCH_GROUP && stmt->fetch.column == -1) {
899						fetch_value(stmt, return_value, 1, NULL);
900					} else if (flags == PDO_FETCH_GROUP && colno) {
901						fetch_value(stmt, return_value, 0, NULL);
902					} else {
903						fetch_value(stmt, return_value, colno, NULL);
904					}
905					if (!return_all) {
906						return 1;
907					} else {
908						break;
909					}
910				} else {
911					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Invalid column index");
912				}
913				return 0;
914
915			case PDO_FETCH_OBJ:
916				object_init_ex(return_value, ZEND_STANDARD_CLASS_DEF_PTR);
917				break;
918
919			case PDO_FETCH_CLASS:
920				if (flags & PDO_FETCH_CLASSTYPE) {
921					zval val;
922					zend_class_entry *cep;
923
924					old_ce = stmt->fetch.cls.ce;
925					ZVAL_COPY_VALUE(&old_ctor_args, &stmt->fetch.cls.ctor_args);
926					old_arg_count = stmt->fetch.cls.fci.param_count;
927					do_fetch_opt_finish(stmt, 0);
928
929					fetch_value(stmt, &val, i++, NULL);
930					if (Z_TYPE(val) != IS_NULL) {
931						convert_to_string(&val);
932						if ((cep = zend_lookup_class(Z_STR(val))) == NULL) {
933							stmt->fetch.cls.ce = ZEND_STANDARD_CLASS_DEF_PTR;
934						} else {
935							stmt->fetch.cls.ce = cep;
936						}
937					}
938
939					do_fetch_class_prepare(stmt);
940					zval_dtor(&val);
941				}
942				ce = stmt->fetch.cls.ce;
943				if (!ce) {
944					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "No fetch class specified");
945					return 0;
946				}
947				if ((flags & PDO_FETCH_SERIALIZE) == 0) {
948					if (UNEXPECTED(object_init_ex(return_value, ce) != SUCCESS)) {
949						return 0;
950					}
951					if (!stmt->fetch.cls.fci.size) {
952						if (!do_fetch_class_prepare(stmt))
953						{
954							return 0;
955						}
956					}
957					if (ce->constructor && (flags & PDO_FETCH_PROPS_LATE)) {
958						stmt->fetch.cls.fci.object = Z_OBJ_P(return_value);
959						stmt->fetch.cls.fcc.object = Z_OBJ_P(return_value);
960						if (zend_call_function(&stmt->fetch.cls.fci, &stmt->fetch.cls.fcc) == FAILURE) {
961							pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call class constructor");
962							return 0;
963						} else {
964							if (!Z_ISUNDEF(stmt->fetch.cls.retval)) {
965								zval_ptr_dtor(&stmt->fetch.cls.retval);
966								ZVAL_UNDEF(&stmt->fetch.cls.retval);
967							}
968						}
969					}
970				}
971				break;
972
973			case PDO_FETCH_INTO:
974				if (Z_ISUNDEF(stmt->fetch.into)) {
975					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "No fetch-into object specified.");
976					return 0;
977					break;
978				}
979
980				ZVAL_COPY(return_value, &stmt->fetch.into);
981
982				if (Z_OBJ_P(return_value)->ce == ZEND_STANDARD_CLASS_DEF_PTR) {
983					how = PDO_FETCH_OBJ;
984				}
985				break;
986
987			case PDO_FETCH_FUNC:
988				if (Z_ISUNDEF(stmt->fetch.func.function)) {
989					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "No fetch function specified");
990					return 0;
991				}
992				if (!stmt->fetch.func.fci.size) {
993					if (!do_fetch_func_prepare(stmt))
994					{
995						return 0;
996					}
997				}
998				break;
999
1000
1001			default:
1002				/* shouldn't happen */
1003				return 0;
1004		}
1005
1006		if (return_all && how != PDO_FETCH_KEY_PAIR) {
1007			if (flags == PDO_FETCH_GROUP && how == PDO_FETCH_COLUMN && stmt->fetch.column > 0) {
1008				fetch_value(stmt, &grp_val, colno, NULL);
1009			} else {
1010				fetch_value(stmt, &grp_val, i, NULL);
1011			}
1012			convert_to_string(&grp_val);
1013			if (how == PDO_FETCH_COLUMN) {
1014				i = stmt->column_count; /* no more data to fetch */
1015			} else {
1016				i++;
1017			}
1018		}
1019
1020		for (idx = 0; i < stmt->column_count; i++, idx++) {
1021			zval val;
1022			fetch_value(stmt, &val, i, NULL);
1023
1024			switch (how) {
1025				case PDO_FETCH_ASSOC:
1026					zend_symtable_update(Z_ARRVAL_P(return_value), stmt->columns[i].name, &val);
1027					break;
1028
1029				case PDO_FETCH_KEY_PAIR:
1030					{
1031						zval tmp;
1032						fetch_value(stmt, &tmp, ++i, NULL);
1033
1034						if (Z_TYPE(val) == IS_LONG) {
1035							zend_hash_index_update((return_all ? Z_ARRVAL_P(return_all) : Z_ARRVAL_P(return_value)), Z_LVAL(val), &tmp);
1036						} else {
1037							convert_to_string(&val);
1038							zend_symtable_update((return_all ? Z_ARRVAL_P(return_all) : Z_ARRVAL_P(return_value)), Z_STR(val), &tmp);
1039						}
1040						zval_ptr_dtor(&val);
1041						return 1;
1042					}
1043					break;
1044
1045				case PDO_FETCH_USE_DEFAULT:
1046				case PDO_FETCH_BOTH:
1047					zend_symtable_update(Z_ARRVAL_P(return_value), stmt->columns[i].name, &val);
1048					if (Z_REFCOUNTED(val)) {
1049						Z_ADDREF(val);
1050					}
1051					zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &val);
1052					break;
1053
1054				case PDO_FETCH_NAMED:
1055					/* already have an item with this name? */
1056					{
1057						zval *curr_val;
1058						if ((curr_val = zend_hash_find(Z_ARRVAL_P(return_value), stmt->columns[i].name))) {
1059							zval arr;
1060							if (Z_TYPE_P(curr_val) != IS_ARRAY) {
1061								/* a little bit of black magic here:
1062								 * we're creating a new array and swapping it for the
1063								 * zval that's already stored in the hash under the name
1064								 * we want.  We then add that zval to the array.
1065								 * This is effectively the same thing as:
1066								 * if (!is_array($hash[$name])) {
1067								 *   $hash[$name] = array($hash[$name]);
1068								 * }
1069								 * */
1070								zval cur;
1071
1072								array_init(&arr);
1073
1074								ZVAL_COPY_VALUE(&cur, curr_val);
1075								ZVAL_COPY_VALUE(curr_val, &arr);
1076
1077								zend_hash_next_index_insert_new(Z_ARRVAL(arr), &cur);
1078							} else {
1079								ZVAL_COPY_VALUE(&arr, curr_val);
1080							}
1081							zend_hash_next_index_insert_new(Z_ARRVAL(arr), &val);
1082						} else {
1083							zend_hash_update(Z_ARRVAL_P(return_value), stmt->columns[i].name, &val);
1084						}
1085					}
1086					break;
1087
1088				case PDO_FETCH_NUM:
1089					zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &val);
1090					break;
1091
1092				case PDO_FETCH_OBJ:
1093				case PDO_FETCH_INTO:
1094					zend_update_property_ex(NULL, return_value,
1095						stmt->columns[i].name,
1096						&val);
1097					zval_ptr_dtor(&val);
1098					break;
1099
1100				case PDO_FETCH_CLASS:
1101					if ((flags & PDO_FETCH_SERIALIZE) == 0 || idx) {
1102						zend_update_property_ex(ce, return_value,
1103							stmt->columns[i].name,
1104							&val);
1105						zval_ptr_dtor(&val);
1106					} else {
1107#ifdef MBO_0
1108						php_unserialize_data_t var_hash;
1109
1110						PHP_VAR_UNSERIALIZE_INIT(var_hash);
1111						if (php_var_unserialize(return_value, (const unsigned char**)&Z_STRVAL(val), Z_STRVAL(val)+Z_STRLEN(val), NULL) == FAILURE) {
1112							pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "cannot unserialize data");
1113							PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1114							return 0;
1115						}
1116						PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1117#endif
1118						if (!ce->unserialize) {
1119							zval_ptr_dtor(&val);
1120							pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "cannot unserialize class");
1121							return 0;
1122						} 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) {
1123							zval_ptr_dtor(&val);
1124							pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "cannot unserialize class");
1125							zval_dtor(return_value);
1126							ZVAL_NULL(return_value);
1127							return 0;
1128						} else {
1129							zval_ptr_dtor(&val);
1130						}
1131					}
1132					break;
1133
1134				case PDO_FETCH_FUNC:
1135					ZVAL_COPY_VALUE(&stmt->fetch.func.values[idx], &val);
1136					ZVAL_COPY_VALUE(&stmt->fetch.cls.fci.params[idx], &stmt->fetch.func.values[idx]);
1137					break;
1138
1139				default:
1140					zval_ptr_dtor(&val);
1141					pdo_raise_impl_error(stmt->dbh, stmt, "22003", "mode is out of range");
1142					return 0;
1143					break;
1144			}
1145		}
1146
1147		switch (how) {
1148			case PDO_FETCH_CLASS:
1149				if (ce->constructor && !(flags & (PDO_FETCH_PROPS_LATE | PDO_FETCH_SERIALIZE))) {
1150					stmt->fetch.cls.fci.object = Z_OBJ_P(return_value);
1151					stmt->fetch.cls.fcc.object = Z_OBJ_P(return_value);
1152					if (zend_call_function(&stmt->fetch.cls.fci, &stmt->fetch.cls.fcc) == FAILURE) {
1153						pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call class constructor");
1154						return 0;
1155					} else {
1156						if (!Z_ISUNDEF(stmt->fetch.cls.retval)) {
1157							zval_ptr_dtor(&stmt->fetch.cls.retval);
1158						}
1159					}
1160				}
1161				if (flags & PDO_FETCH_CLASSTYPE) {
1162					do_fetch_opt_finish(stmt, 0);
1163					stmt->fetch.cls.ce = old_ce;
1164					ZVAL_COPY_VALUE(&stmt->fetch.cls.ctor_args, &old_ctor_args);
1165					stmt->fetch.cls.fci.param_count = old_arg_count;
1166				}
1167				break;
1168
1169			case PDO_FETCH_FUNC:
1170				stmt->fetch.func.fci.param_count = idx;
1171				stmt->fetch.func.fci.retval = &retval;
1172				if (zend_call_function(&stmt->fetch.func.fci, &stmt->fetch.func.fcc) == FAILURE) {
1173					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call user-supplied function");
1174					return 0;
1175				} else {
1176					if (return_all) {
1177						zval_ptr_dtor(return_value); /* we don't need that */
1178						ZVAL_COPY_VALUE(return_value, &retval);
1179					} else if (!Z_ISUNDEF(retval)) {
1180						ZVAL_COPY_VALUE(return_value, &retval);
1181					}
1182				}
1183				while (idx--) {
1184					zval_ptr_dtor(&stmt->fetch.func.values[idx]);
1185				}
1186				break;
1187
1188			default:
1189				break;
1190		}
1191
1192		if (return_all) {
1193			if ((flags & PDO_FETCH_UNIQUE) == PDO_FETCH_UNIQUE) {
1194				zend_symtable_update(Z_ARRVAL_P(return_all), Z_STR(grp_val), return_value);
1195			} else {
1196				zval grp;
1197				if ((pgrp = zend_symtable_find(Z_ARRVAL_P(return_all), Z_STR(grp_val))) == NULL) {
1198					array_init(&grp);
1199					zend_symtable_update(Z_ARRVAL_P(return_all), Z_STR(grp_val), &grp);
1200				} else {
1201					ZVAL_COPY_VALUE(&grp, pgrp);
1202				}
1203				zend_hash_next_index_insert(Z_ARRVAL(grp), return_value);
1204			}
1205			zval_dtor(&grp_val);
1206		}
1207
1208	}
1209
1210	return 1;
1211}
1212/* }}} */
1213
1214static int pdo_stmt_verify_mode(pdo_stmt_t *stmt, zend_long mode, int fetch_all) /* {{{ */
1215{
1216	int flags = mode & PDO_FETCH_FLAGS;
1217
1218	mode = mode & ~PDO_FETCH_FLAGS;
1219
1220	if (mode < 0 || mode > PDO_FETCH__MAX) {
1221		pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "invalid fetch mode");
1222		return 0;
1223	}
1224
1225	if (mode == PDO_FETCH_USE_DEFAULT) {
1226		flags = stmt->default_fetch_type & PDO_FETCH_FLAGS;
1227		mode = stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
1228	}
1229
1230	switch(mode) {
1231		case PDO_FETCH_FUNC:
1232			if (!fetch_all) {
1233				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_FUNC is only allowed in PDOStatement::fetchAll()");
1234				return 0;
1235			}
1236			return 1;
1237
1238		case PDO_FETCH_LAZY:
1239			if (fetch_all) {
1240				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_LAZY can't be used with PDOStatement::fetchAll()");
1241				return 0;
1242			}
1243			/* fall through */
1244
1245		default:
1246			if ((flags & PDO_FETCH_SERIALIZE) == PDO_FETCH_SERIALIZE) {
1247				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_SERIALIZE can only be used together with PDO::FETCH_CLASS");
1248				return 0;
1249			}
1250			if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
1251				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_CLASSTYPE can only be used together with PDO::FETCH_CLASS");
1252				return 0;
1253			}
1254			if (mode >= PDO_FETCH__MAX) {
1255				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "invalid fetch mode");
1256				return 0;
1257			}
1258			/* no break; */
1259
1260		case PDO_FETCH_CLASS:
1261			return 1;
1262	}
1263}
1264/* }}} */
1265
1266/* {{{ proto mixed PDOStatement::fetch([int $how = PDO_FETCH_BOTH [, int $orientation [, int $offset]]])
1267   Fetches the next row and returns it, or false if there are no more rows */
1268static PHP_METHOD(PDOStatement, fetch)
1269{
1270	zend_long how = PDO_FETCH_USE_DEFAULT;
1271	zend_long ori = PDO_FETCH_ORI_NEXT;
1272	zend_long off = 0;
1273    PHP_STMT_GET_OBJ;
1274
1275	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|lll", &how,
1276			&ori, &off)) {
1277		RETURN_FALSE;
1278	}
1279
1280	PDO_STMT_CLEAR_ERR();
1281
1282	if (!pdo_stmt_verify_mode(stmt, how, 0)) {
1283		RETURN_FALSE;
1284	}
1285
1286	if (!do_fetch(stmt, TRUE, return_value, how, ori, off, 0)) {
1287		PDO_HANDLE_STMT_ERR();
1288		RETURN_FALSE;
1289	}
1290}
1291/* }}} */
1292
1293/* {{{ proto mixed PDOStatement::fetchObject([string class_name [, NULL|array ctor_args]])
1294   Fetches the next row and returns it as an object. */
1295static PHP_METHOD(PDOStatement, fetchObject)
1296{
1297	zend_long how = PDO_FETCH_CLASS;
1298	zend_long ori = PDO_FETCH_ORI_NEXT;
1299	zend_long off = 0;
1300	zend_string *class_name = NULL;
1301	zend_class_entry *old_ce;
1302	zval old_ctor_args, *ctor_args = NULL;
1303	int error = 0, old_arg_count;
1304
1305	PHP_STMT_GET_OBJ;
1306
1307	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|S!a", &class_name, &ctor_args)) {
1308		RETURN_FALSE;
1309	}
1310
1311	PDO_STMT_CLEAR_ERR();
1312
1313	if (!pdo_stmt_verify_mode(stmt, how, 0)) {
1314		RETURN_FALSE;
1315	}
1316
1317	old_ce = stmt->fetch.cls.ce;
1318	ZVAL_COPY_VALUE(&old_ctor_args, &stmt->fetch.cls.ctor_args);
1319	old_arg_count = stmt->fetch.cls.fci.param_count;
1320
1321	do_fetch_opt_finish(stmt, 0);
1322
1323	if (ctor_args) {
1324		if (Z_TYPE_P(ctor_args) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(ctor_args))) {
1325			ZVAL_DUP(&stmt->fetch.cls.ctor_args, ctor_args);
1326		} else {
1327			ZVAL_UNDEF(&stmt->fetch.cls.ctor_args);
1328		}
1329	}
1330	if (class_name && !error) {
1331		stmt->fetch.cls.ce = zend_fetch_class(class_name, ZEND_FETCH_CLASS_AUTO);
1332
1333		if (!stmt->fetch.cls.ce) {
1334			pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Could not find user-supplied class");
1335			error = 1;
1336		}
1337	} else if (!error) {
1338		stmt->fetch.cls.ce = zend_standard_class_def;
1339	}
1340
1341	if (!error && !do_fetch(stmt, TRUE, return_value, how, ori, off, 0)) {
1342		error = 1;
1343	}
1344	if (error) {
1345		PDO_HANDLE_STMT_ERR();
1346	}
1347	do_fetch_opt_finish(stmt, 1);
1348
1349	stmt->fetch.cls.ce = old_ce;
1350	ZVAL_COPY_VALUE(&stmt->fetch.cls.ctor_args, &old_ctor_args);
1351	stmt->fetch.cls.fci.param_count = old_arg_count;
1352	if (error) {
1353		RETURN_FALSE;
1354	}
1355}
1356/* }}} */
1357
1358/* {{{ proto string PDOStatement::fetchColumn([int column_number])
1359   Returns a data of the specified column in the result set. */
1360static PHP_METHOD(PDOStatement, fetchColumn)
1361{
1362	zend_long col_n = 0;
1363	PHP_STMT_GET_OBJ;
1364
1365	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &col_n)) {
1366		RETURN_FALSE;
1367	}
1368
1369	PDO_STMT_CLEAR_ERR();
1370
1371	if (!do_fetch_common(stmt, PDO_FETCH_ORI_NEXT, 0, TRUE)) {
1372		PDO_HANDLE_STMT_ERR();
1373		RETURN_FALSE;
1374	}
1375
1376	fetch_value(stmt, return_value, col_n, NULL);
1377}
1378/* }}} */
1379
1380/* {{{ proto array PDOStatement::fetchAll([int $how = PDO_FETCH_BOTH [, string class_name [, NULL|array ctor_args]]])
1381   Returns an array of all of the results. */
1382static PHP_METHOD(PDOStatement, fetchAll)
1383{
1384	zend_long how = PDO_FETCH_USE_DEFAULT;
1385	zval data, *return_all;
1386	zval *arg2;
1387	zend_class_entry *old_ce;
1388	zval old_ctor_args, *ctor_args = NULL;
1389	int error = 0, flags, old_arg_count;
1390	PHP_STMT_GET_OBJ;
1391
1392	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|lzz", &how, &arg2, &ctor_args)) {
1393		RETURN_FALSE;
1394	}
1395
1396	if (!pdo_stmt_verify_mode(stmt, how, 1)) {
1397		RETURN_FALSE;
1398	}
1399
1400	old_ce = stmt->fetch.cls.ce;
1401	ZVAL_COPY_VALUE(&old_ctor_args, &stmt->fetch.cls.ctor_args);
1402	old_arg_count = stmt->fetch.cls.fci.param_count;
1403
1404	do_fetch_opt_finish(stmt, 0);
1405
1406	switch(how & ~PDO_FETCH_FLAGS) {
1407	case PDO_FETCH_CLASS:
1408		switch(ZEND_NUM_ARGS()) {
1409		case 0:
1410		case 1:
1411			stmt->fetch.cls.ce = zend_standard_class_def;
1412			break;
1413		case 3:
1414			if (Z_TYPE_P(ctor_args) != IS_NULL && Z_TYPE_P(ctor_args) != IS_ARRAY) {
1415				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "ctor_args must be either NULL or an array");
1416				error = 1;
1417				break;
1418			}
1419			if (Z_TYPE_P(ctor_args) != IS_ARRAY || !zend_hash_num_elements(Z_ARRVAL_P(ctor_args))) {
1420				ctor_args = NULL;
1421			}
1422			/* no break */
1423		case 2:
1424			if (ctor_args) {
1425				ZVAL_COPY_VALUE(&stmt->fetch.cls.ctor_args, ctor_args); /* we're not going to free these */
1426			} else {
1427				ZVAL_UNDEF(&stmt->fetch.cls.ctor_args);
1428			}
1429			if (Z_TYPE_P(arg2) != IS_STRING) {
1430				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Invalid class name (should be a string)");
1431				error = 1;
1432				break;
1433			} else {
1434				stmt->fetch.cls.ce = zend_fetch_class(Z_STR_P(arg2), ZEND_FETCH_CLASS_AUTO);
1435				if (!stmt->fetch.cls.ce) {
1436					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not find user-specified class");
1437					error = 1;
1438					break;
1439				}
1440			}
1441		}
1442		if (!error) {
1443			do_fetch_class_prepare(stmt);
1444		}
1445		break;
1446
1447	case PDO_FETCH_FUNC:
1448		switch (ZEND_NUM_ARGS()) {
1449			case 0:
1450			case 1:
1451				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "no fetch function specified");
1452				error = 1;
1453				break;
1454			case 3:
1455			case 2:
1456				ZVAL_COPY_VALUE(&stmt->fetch.func.function, arg2);
1457				if (do_fetch_func_prepare(stmt) == 0) {
1458					error = 1;
1459				}
1460				break;
1461		}
1462		break;
1463
1464	case PDO_FETCH_COLUMN:
1465		switch(ZEND_NUM_ARGS()) {
1466		case 0:
1467		case 1:
1468			stmt->fetch.column = how & PDO_FETCH_GROUP ? -1 : 0;
1469			break;
1470		case 2:
1471			convert_to_long(arg2);
1472			stmt->fetch.column = Z_LVAL_P(arg2);
1473			break;
1474		case 3:
1475			pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Third parameter not allowed for PDO::FETCH_COLUMN");
1476			error = 1;
1477		}
1478		break;
1479
1480	default:
1481		if (ZEND_NUM_ARGS() > 1) {
1482			pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Extraneous additional parameters");
1483			error = 1;
1484		}
1485	}
1486
1487	flags = how & PDO_FETCH_FLAGS;
1488
1489	if ((how & ~PDO_FETCH_FLAGS) == PDO_FETCH_USE_DEFAULT) {
1490		flags |= stmt->default_fetch_type & PDO_FETCH_FLAGS;
1491		how |= stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
1492	}
1493
1494	if (!error)	{
1495		PDO_STMT_CLEAR_ERR();
1496		if ((how & PDO_FETCH_GROUP) || how == PDO_FETCH_KEY_PAIR ||
1497			(how == PDO_FETCH_USE_DEFAULT && stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)
1498		) {
1499			array_init(return_value);
1500			return_all = return_value;
1501		} else {
1502			return_all = 0;
1503		}
1504		if (!do_fetch(stmt, 1, &data, how | flags, PDO_FETCH_ORI_NEXT, 0, return_all)) {
1505			error = 2;
1506		}
1507	}
1508	if (!error) {
1509		if ((how & PDO_FETCH_GROUP)) {
1510			while (do_fetch(stmt, 1, &data, how | flags, PDO_FETCH_ORI_NEXT, 0, return_all));
1511		} else if (how == PDO_FETCH_KEY_PAIR || (how == PDO_FETCH_USE_DEFAULT && stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)) {
1512			while (do_fetch(stmt, 1, &data, how | flags, PDO_FETCH_ORI_NEXT, 0, return_all));
1513		} else {
1514			array_init(return_value);
1515			do {
1516				zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &data);
1517			} while (do_fetch(stmt, 1, &data, how | flags, PDO_FETCH_ORI_NEXT, 0, 0));
1518		}
1519	}
1520
1521	do_fetch_opt_finish(stmt, 0);
1522
1523	stmt->fetch.cls.ce = old_ce;
1524	ZVAL_COPY_VALUE(&stmt->fetch.cls.ctor_args, &old_ctor_args);
1525	stmt->fetch.cls.fci.param_count = old_arg_count;
1526
1527	if (error) {
1528		PDO_HANDLE_STMT_ERR();
1529		if (error != 2) {
1530			RETURN_FALSE;
1531		} else { /* on no results, return an empty array */
1532			if (Z_TYPE_P(return_value) != IS_ARRAY) {
1533				array_init(return_value);
1534			}
1535			return;
1536		}
1537	}
1538}
1539/* }}} */
1540
1541static int register_bound_param(INTERNAL_FUNCTION_PARAMETERS, pdo_stmt_t *stmt, int is_param) /* {{{ */
1542{
1543	struct pdo_bound_param_data param = {{{0}}};
1544	zend_long param_type = PDO_PARAM_STR;
1545	zval *parameter;
1546
1547	param.paramno = -1;
1548
1549	if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(),
1550			"lz|llz!", &param.paramno, &parameter, &param_type, &param.max_value_len,
1551			&param.driver_params)) {
1552		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "Sz|llz!", &param.name,
1553				&parameter, &param_type, &param.max_value_len,
1554				&param.driver_params)) {
1555			return 0;
1556		}
1557	}
1558
1559	param.param_type = (int) param_type;
1560
1561	if (param.paramno > 0) {
1562		--param.paramno; /* make it zero-based internally */
1563	} else if (!param.name) {
1564		pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "Columns/Parameters are 1-based");
1565		return 0;
1566	}
1567
1568	ZVAL_COPY(&param.parameter, parameter);
1569	if (!really_register_bound_param(&param, stmt, is_param)) {
1570		if (!Z_ISUNDEF(param.parameter)) {
1571			zval_ptr_dtor(&(param.parameter));
1572		}
1573		return 0;
1574	}
1575	return 1;
1576} /* }}} */
1577
1578/* {{{ proto bool PDOStatement::bindValue(mixed $paramno, mixed $param [, int $type ])
1579   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(). */
1580static PHP_METHOD(PDOStatement, bindValue)
1581{
1582	struct pdo_bound_param_data param = {{{0}}};
1583	zend_long param_type = PDO_PARAM_STR;
1584	zval *parameter;
1585	PHP_STMT_GET_OBJ;
1586
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	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "lz!", &attr, &value)) {
1707		RETURN_FALSE;
1708	}
1709
1710	if (!stmt->methods->set_attribute) {
1711		goto fail;
1712	}
1713
1714	PDO_STMT_CLEAR_ERR();
1715	if (stmt->methods->set_attribute(stmt, attr, value)) {
1716		RETURN_TRUE;
1717	}
1718
1719fail:
1720	if (!stmt->methods->set_attribute) {
1721		pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "This driver doesn't support setting attributes");
1722	} else {
1723		PDO_HANDLE_STMT_ERR();
1724	}
1725	RETURN_FALSE;
1726}
1727/* }}} */
1728
1729/* {{{ proto mixed PDOStatement::getAttribute(long attribute)
1730   Get an attribute */
1731
1732static int generic_stmt_attr_get(pdo_stmt_t *stmt, zval *return_value, zend_long attr)
1733{
1734	switch (attr) {
1735		case PDO_ATTR_EMULATE_PREPARES:
1736			RETVAL_BOOL(stmt->supports_placeholders == PDO_PLACEHOLDER_NONE);
1737			return 1;
1738	}
1739	return 0;
1740}
1741
1742static PHP_METHOD(PDOStatement, getAttribute)
1743{
1744	zend_long attr;
1745	PHP_STMT_GET_OBJ;
1746
1747	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "l", &attr)) {
1748		RETURN_FALSE;
1749	}
1750
1751	if (!stmt->methods->get_attribute) {
1752		if (!generic_stmt_attr_get(stmt, return_value, attr)) {
1753			pdo_raise_impl_error(stmt->dbh, stmt, "IM001",
1754				"This driver doesn't support getting attributes");
1755			RETURN_FALSE;
1756		}
1757		return;
1758	}
1759
1760	PDO_STMT_CLEAR_ERR();
1761	switch (stmt->methods->get_attribute(stmt, attr, return_value)) {
1762		case -1:
1763			PDO_HANDLE_STMT_ERR();
1764			RETURN_FALSE;
1765
1766		case 0:
1767			if (!generic_stmt_attr_get(stmt, return_value, attr)) {
1768				/* XXX: should do something better here */
1769				pdo_raise_impl_error(stmt->dbh, stmt, "IM001",
1770					"driver doesn't support getting that attribute");
1771				RETURN_FALSE;
1772			}
1773			return;
1774
1775		default:
1776			return;
1777	}
1778}
1779/* }}} */
1780
1781/* {{{ proto int PDOStatement::columnCount()
1782   Returns the number of columns in the result set */
1783static PHP_METHOD(PDOStatement, columnCount)
1784{
1785	PHP_STMT_GET_OBJ;
1786	if (zend_parse_parameters_none() == FAILURE) {
1787		return;
1788	}
1789	RETURN_LONG(stmt->column_count);
1790}
1791/* }}} */
1792
1793/* {{{ proto array PDOStatement::getColumnMeta(int $column)
1794   Returns meta data for a numbered column */
1795static PHP_METHOD(PDOStatement, getColumnMeta)
1796{
1797	zend_long colno;
1798	struct pdo_column_data *col;
1799	PHP_STMT_GET_OBJ;
1800
1801	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "l", &colno)) {
1802		RETURN_FALSE;
1803	}
1804	if(colno < 0) {
1805		pdo_raise_impl_error(stmt->dbh, stmt, "42P10", "column number must be non-negative");
1806		RETURN_FALSE;
1807	}
1808
1809	if (!stmt->methods->get_column_meta) {
1810		pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "driver doesn't support meta data");
1811		RETURN_FALSE;
1812	}
1813
1814	PDO_STMT_CLEAR_ERR();
1815	if (FAILURE == stmt->methods->get_column_meta(stmt, colno, return_value)) {
1816		PDO_HANDLE_STMT_ERR();
1817		RETURN_FALSE;
1818	}
1819
1820	/* add stock items */
1821	col = &stmt->columns[colno];
1822	add_assoc_str(return_value, "name", zend_string_copy(col->name));
1823	add_assoc_long(return_value, "len", col->maxlen); /* FIXME: unsigned ? */
1824	add_assoc_long(return_value, "precision", col->precision);
1825	if (col->param_type != PDO_PARAM_ZVAL) {
1826		/* if param_type is PDO_PARAM_ZVAL the driver has to provide correct data */
1827		add_assoc_long(return_value, "pdo_type", col->param_type);
1828	}
1829}
1830/* }}} */
1831
1832/* {{{ proto bool PDOStatement::setFetchMode(int mode [mixed* params])
1833   Changes the default fetch mode for subsequent fetches (params have different meaning for different fetch modes) */
1834
1835int pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAMETERS, pdo_stmt_t *stmt, int skip)
1836{
1837	zend_long mode = PDO_FETCH_BOTH;
1838	int flags = 0, argc = ZEND_NUM_ARGS() - skip;
1839	zval *args;
1840	zend_class_entry *cep;
1841	int retval;
1842
1843	do_fetch_opt_finish(stmt, 1);
1844
1845	switch (stmt->default_fetch_type) {
1846		case PDO_FETCH_INTO:
1847			if (!Z_ISUNDEF(stmt->fetch.into)) {
1848				zval_ptr_dtor(&stmt->fetch.into);
1849				ZVAL_UNDEF(&stmt->fetch.into);
1850			}
1851			break;
1852		default:
1853			;
1854	}
1855
1856	stmt->default_fetch_type = PDO_FETCH_BOTH;
1857
1858	if (argc == 0) {
1859		return SUCCESS;
1860	}
1861
1862	args = safe_emalloc(ZEND_NUM_ARGS(), sizeof(zval), 0);
1863
1864	retval = zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args);
1865
1866	if (SUCCESS == retval) {
1867		if (Z_TYPE(args[skip]) != IS_LONG) {
1868			pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "mode must be an integer");
1869			retval = FAILURE;
1870		} else {
1871			mode = Z_LVAL(args[skip]);
1872			flags = mode & PDO_FETCH_FLAGS;
1873
1874			retval = pdo_stmt_verify_mode(stmt, mode, 0);
1875		}
1876	}
1877
1878	if (FAILURE == retval) {
1879		PDO_STMT_CLEAR_ERR();
1880		efree(args);
1881		return FAILURE;
1882	}
1883
1884	retval = FAILURE;
1885	switch (mode & ~PDO_FETCH_FLAGS) {
1886		case PDO_FETCH_USE_DEFAULT:
1887		case PDO_FETCH_LAZY:
1888		case PDO_FETCH_ASSOC:
1889		case PDO_FETCH_NUM:
1890		case PDO_FETCH_BOTH:
1891		case PDO_FETCH_OBJ:
1892		case PDO_FETCH_BOUND:
1893		case PDO_FETCH_NAMED:
1894		case PDO_FETCH_KEY_PAIR:
1895			if (argc != 1) {
1896				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode doesn't allow any extra arguments");
1897			} else {
1898				retval = SUCCESS;
1899			}
1900			break;
1901
1902		case PDO_FETCH_COLUMN:
1903			if (argc != 2) {
1904				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode requires the colno argument");
1905			} else	if (Z_TYPE(args[skip+1]) != IS_LONG) {
1906				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "colno must be an integer");
1907			} else {
1908				stmt->fetch.column = Z_LVAL(args[skip+1]);
1909				retval = SUCCESS;
1910			}
1911			break;
1912
1913		case PDO_FETCH_CLASS:
1914			/* Gets its class name from 1st column */
1915			if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
1916				if (argc != 1) {
1917					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode doesn't allow any extra arguments");
1918				} else {
1919					stmt->fetch.cls.ce = NULL;
1920					retval = SUCCESS;
1921				}
1922			} else {
1923				if (argc < 2) {
1924					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode requires the classname argument");
1925				} else if (argc > 3) {
1926					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "too many arguments");
1927				} else if (Z_TYPE(args[skip+1]) != IS_STRING) {
1928					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "classname must be a string");
1929				} else {
1930					cep = zend_lookup_class(Z_STR(args[skip+1]));
1931					if (cep) {
1932						retval = SUCCESS;
1933						stmt->fetch.cls.ce = cep;
1934					}
1935				}
1936			}
1937
1938			if (SUCCESS == retval) {
1939				ZVAL_UNDEF(&stmt->fetch.cls.ctor_args);
1940#ifdef ilia_0 /* we'll only need this when we have persistent statements, if ever */
1941				if (stmt->dbh->is_persistent) {
1942					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");
1943				}
1944#endif
1945				if (argc == 3) {
1946					if (Z_TYPE(args[skip+2]) != IS_NULL && Z_TYPE(args[skip+2]) != IS_ARRAY) {
1947						pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "ctor_args must be either NULL or an array");
1948						retval = FAILURE;
1949					} else if (Z_TYPE(args[skip+2]) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL(args[skip+2]))) {
1950						ZVAL_DUP(&stmt->fetch.cls.ctor_args, &args[skip+2]);
1951					}
1952				}
1953
1954				if (SUCCESS == retval) {
1955					do_fetch_class_prepare(stmt);
1956				}
1957			}
1958
1959			break;
1960
1961		case PDO_FETCH_INTO:
1962			if (argc != 2) {
1963				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode requires the object parameter");
1964			} else if (Z_TYPE(args[skip+1]) != IS_OBJECT) {
1965				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "object must be an object");
1966			} else {
1967				retval = SUCCESS;
1968			}
1969
1970			if (SUCCESS == retval) {
1971#ifdef ilia_0 /* we'll only need this when we have persistent statements, if ever */
1972				if (stmt->dbh->is_persistent) {
1973					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");
1974				}
1975#endif
1976				ZVAL_COPY(&stmt->fetch.into, &args[skip+1]);
1977			}
1978
1979			break;
1980
1981		default:
1982			pdo_raise_impl_error(stmt->dbh, stmt, "22003", "Invalid fetch mode specified");
1983	}
1984
1985	if (SUCCESS == retval) {
1986		stmt->default_fetch_type = mode;
1987	}
1988
1989	/*
1990	 * PDO error (if any) has already been raised at this point.
1991	 *
1992	 * The error_code is cleared, otherwise the caller will read the
1993	 * last error message from the driver.
1994	 *
1995	 */
1996	PDO_STMT_CLEAR_ERR();
1997
1998	efree(args);
1999
2000	return retval;
2001}
2002
2003static PHP_METHOD(PDOStatement, setFetchMode)
2004{
2005	PHP_STMT_GET_OBJ;
2006
2007	RETVAL_BOOL(
2008		pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAM_PASSTHRU,
2009			stmt, 0) == SUCCESS ? 1 : 0
2010		);
2011}
2012/* }}} */
2013
2014/* {{{ proto bool PDOStatement::nextRowset()
2015   Advances to the next rowset in a multi-rowset statement handle. Returns true if it succeeded, false otherwise */
2016
2017static int pdo_stmt_do_next_rowset(pdo_stmt_t *stmt)
2018{
2019	/* un-describe */
2020	if (stmt->columns) {
2021		int i;
2022		struct pdo_column_data *cols = stmt->columns;
2023
2024		for (i = 0; i < stmt->column_count; i++) {
2025			zend_string_release(cols[i].name);
2026		}
2027		efree(stmt->columns);
2028		stmt->columns = NULL;
2029		stmt->column_count = 0;
2030	}
2031
2032	if (!stmt->methods->next_rowset(stmt)) {
2033		/* Set the executed flag to 0 to reallocate columns on next execute */
2034		stmt->executed = 0;
2035		return 0;
2036	}
2037
2038	pdo_stmt_describe_columns(stmt);
2039
2040	return 1;
2041}
2042
2043static PHP_METHOD(PDOStatement, nextRowset)
2044{
2045	PHP_STMT_GET_OBJ;
2046
2047	if (!stmt->methods->next_rowset) {
2048		pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "driver does not support multiple rowsets");
2049		RETURN_FALSE;
2050	}
2051
2052	PDO_STMT_CLEAR_ERR();
2053
2054	if (!pdo_stmt_do_next_rowset(stmt)) {
2055		PDO_HANDLE_STMT_ERR();
2056		RETURN_FALSE;
2057	}
2058
2059	RETURN_TRUE;
2060}
2061/* }}} */
2062
2063/* {{{ proto bool PDOStatement::closeCursor()
2064   Closes the cursor, leaving the statement ready for re-execution. */
2065static PHP_METHOD(PDOStatement, closeCursor)
2066{
2067	PHP_STMT_GET_OBJ;
2068
2069	if (!stmt->methods->cursor_closer) {
2070		/* emulate it by fetching and discarding rows */
2071		do {
2072			while (stmt->methods->fetcher(stmt, PDO_FETCH_ORI_NEXT, 0))
2073				;
2074			if (!stmt->methods->next_rowset) {
2075				break;
2076			}
2077
2078			if (!pdo_stmt_do_next_rowset(stmt)) {
2079				break;
2080			}
2081
2082		} while (1);
2083		stmt->executed = 0;
2084		RETURN_TRUE;
2085	}
2086
2087	PDO_STMT_CLEAR_ERR();
2088
2089	if (!stmt->methods->cursor_closer(stmt)) {
2090		PDO_HANDLE_STMT_ERR();
2091		RETURN_FALSE;
2092	}
2093	stmt->executed = 0;
2094	RETURN_TRUE;
2095}
2096/* }}} */
2097
2098/* {{{ proto void PDOStatement::debugDumpParams()
2099   A utility for internals hackers to debug parameter internals */
2100static PHP_METHOD(PDOStatement, debugDumpParams)
2101{
2102	php_stream *out = php_stream_open_wrapper("php://output", "w", 0, NULL);
2103	struct pdo_bound_param_data *param;
2104	PHP_STMT_GET_OBJ;
2105
2106	if (out == NULL) {
2107		RETURN_FALSE;
2108	}
2109
2110	php_stream_printf(out, "SQL: [%d] %.*s\n",
2111		stmt->query_stringlen,
2112		stmt->query_stringlen, stmt->query_string);
2113
2114	php_stream_printf(out, "Params:  %d\n",
2115		stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0);
2116
2117	if (stmt->bound_params) {
2118		zend_ulong num;
2119		zend_string *key = NULL;
2120		ZEND_HASH_FOREACH_KEY_PTR(stmt->bound_params, num, key, param) {
2121			if (key) {
2122				php_stream_printf(out, "Key: Name: [%d] %.*s\n", ZSTR_LEN(key), ZSTR_LEN(key), ZSTR_VAL(key));
2123			} else {
2124				php_stream_printf(out, "Key: Position #%pd:\n", num);
2125			}
2126
2127			php_stream_printf(out, "paramno=%pd\nname=[%d] \"%.*s\"\nis_param=%d\nparam_type=%d\n",
2128					param->paramno, param->name? ZSTR_LEN(param->name) : 0, param->name? ZSTR_LEN(param->name) : 0,
2129					param->name ? ZSTR_VAL(param->name) : "",
2130					param->is_param,
2131					param->param_type);
2132
2133		} ZEND_HASH_FOREACH_END();
2134	}
2135
2136	php_stream_close(out);
2137}
2138/* }}} */
2139
2140/* {{{ proto int PDOStatement::__wakeup()
2141   Prevents use of a PDOStatement instance that has been unserialized */
2142static PHP_METHOD(PDOStatement, __wakeup)
2143{
2144	zend_throw_exception_ex(php_pdo_get_exception(), 0, "You cannot serialize or unserialize PDOStatement instances");
2145}
2146/* }}} */
2147
2148/* {{{ proto int PDOStatement::__sleep()
2149   Prevents serialization of a PDOStatement instance */
2150static PHP_METHOD(PDOStatement, __sleep)
2151{
2152	zend_throw_exception_ex(php_pdo_get_exception(), 0, "You cannot serialize or unserialize PDOStatement instances");
2153}
2154/* }}} */
2155
2156const zend_function_entry pdo_dbstmt_functions[] = {
2157	PHP_ME(PDOStatement, execute,		arginfo_pdostatement_execute,		ZEND_ACC_PUBLIC)
2158	PHP_ME(PDOStatement, fetch,			arginfo_pdostatement_fetch,			ZEND_ACC_PUBLIC)
2159	PHP_ME(PDOStatement, bindParam,		arginfo_pdostatement_bindparam,		ZEND_ACC_PUBLIC)
2160	PHP_ME(PDOStatement, bindColumn,	arginfo_pdostatement_bindcolumn,	ZEND_ACC_PUBLIC)
2161	PHP_ME(PDOStatement, bindValue,		arginfo_pdostatement_bindvalue,		ZEND_ACC_PUBLIC)
2162	PHP_ME(PDOStatement, rowCount,		arginfo_pdostatement__void,			ZEND_ACC_PUBLIC)
2163	PHP_ME(PDOStatement, fetchColumn,	arginfo_pdostatement_fetchcolumn,	ZEND_ACC_PUBLIC)
2164	PHP_ME(PDOStatement, fetchAll,		arginfo_pdostatement_fetchall,		ZEND_ACC_PUBLIC)
2165	PHP_ME(PDOStatement, fetchObject,	arginfo_pdostatement_fetchobject,	ZEND_ACC_PUBLIC)
2166	PHP_ME(PDOStatement, errorCode,		arginfo_pdostatement__void,			ZEND_ACC_PUBLIC)
2167	PHP_ME(PDOStatement, errorInfo,		arginfo_pdostatement__void,			ZEND_ACC_PUBLIC)
2168	PHP_ME(PDOStatement, setAttribute,	arginfo_pdostatement_setattribute,	ZEND_ACC_PUBLIC)
2169	PHP_ME(PDOStatement, getAttribute,	arginfo_pdostatement_getattribute,	ZEND_ACC_PUBLIC)
2170	PHP_ME(PDOStatement, columnCount,	arginfo_pdostatement__void,			ZEND_ACC_PUBLIC)
2171	PHP_ME(PDOStatement, getColumnMeta,	arginfo_pdostatement_getcolumnmeta,	ZEND_ACC_PUBLIC)
2172	PHP_ME(PDOStatement, setFetchMode,	arginfo_pdostatement_setfetchmode,	ZEND_ACC_PUBLIC)
2173	PHP_ME(PDOStatement, nextRowset,	arginfo_pdostatement__void,			ZEND_ACC_PUBLIC)
2174	PHP_ME(PDOStatement, closeCursor,	arginfo_pdostatement__void,			ZEND_ACC_PUBLIC)
2175	PHP_ME(PDOStatement, debugDumpParams, arginfo_pdostatement__void,		ZEND_ACC_PUBLIC)
2176	PHP_ME(PDOStatement, __wakeup,		arginfo_pdostatement__void,			ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
2177	PHP_ME(PDOStatement, __sleep,		arginfo_pdostatement__void,			ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
2178	{NULL, NULL, NULL}
2179};
2180
2181/* {{{ overloaded handlers for PDOStatement class */
2182static void dbstmt_prop_write(zval *object, zval *member, zval *value, void **cache_slot)
2183{
2184	pdo_stmt_t *stmt = Z_PDO_STMT_P(object);
2185
2186	convert_to_string(member);
2187
2188	if (strcmp(Z_STRVAL_P(member), "queryString") == 0) {
2189		pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "property queryString is read only");
2190	} else {
2191		std_object_handlers.write_property(object, member, value, cache_slot);
2192	}
2193}
2194
2195static void dbstmt_prop_delete(zval *object, zval *member, void **cache_slot)
2196{
2197	pdo_stmt_t *stmt = Z_PDO_STMT_P(object);
2198
2199	convert_to_string(member);
2200
2201	if (strcmp(Z_STRVAL_P(member), "queryString") == 0) {
2202		pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "property queryString is read only");
2203	} else {
2204		std_object_handlers.unset_property(object, member, cache_slot);
2205	}
2206}
2207
2208static union _zend_function *dbstmt_method_get(zend_object **object_pp, zend_string *method_name, const zval *key)
2209{
2210	zend_function *fbc = NULL;
2211	zend_string *lc_method_name;
2212	zend_object *object = *object_pp;
2213
2214	lc_method_name = zend_string_alloc(ZSTR_LEN(method_name), 0);
2215	zend_str_tolower_copy(ZSTR_VAL(lc_method_name), ZSTR_VAL(method_name), ZSTR_LEN(method_name));
2216
2217	if ((fbc = zend_hash_find_ptr(&object->ce->function_table, lc_method_name)) == NULL) {
2218		pdo_stmt_t *stmt = php_pdo_stmt_fetch_object(object);
2219		/* instance not created by PDO object */
2220		if (!stmt->dbh) {
2221			goto out;
2222		}
2223		/* not a pre-defined method, nor a user-defined method; check
2224		 * the driver specific methods */
2225		if (!stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT]) {
2226			if (!pdo_hash_methods(Z_PDO_OBJECT_P(&stmt->database_object_handle),
2227				PDO_DBH_DRIVER_METHOD_KIND_STMT)
2228				|| !stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT]) {
2229				goto out;
2230			}
2231		}
2232
2233		if ((fbc = zend_hash_find_ptr(stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT], lc_method_name)) == NULL) {
2234			goto out;
2235		}
2236		/* got it */
2237	}
2238
2239out:
2240	zend_string_release(lc_method_name);
2241	return fbc;
2242}
2243
2244static int dbstmt_compare(zval *object1, zval *object2)
2245{
2246	return -1;
2247}
2248
2249static zend_object *dbstmt_clone_obj(zval *zobject)
2250{
2251	pdo_stmt_t *stmt;
2252	pdo_stmt_t *old_stmt;
2253
2254	stmt = ecalloc(1, sizeof(pdo_stmt_t) + zend_object_properties_size(Z_OBJCE_P(zobject)));
2255	zend_object_std_init(&stmt->std, Z_OBJCE_P(zobject));
2256	object_properties_init(&stmt->std, Z_OBJCE_P(zobject));
2257
2258	old_stmt = Z_PDO_STMT_P(zobject);
2259
2260	zend_objects_clone_members(&stmt->std, &old_stmt->std);
2261
2262	return &stmt->std;
2263}
2264
2265zend_object_handlers pdo_dbstmt_object_handlers;
2266static int pdo_row_serialize(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data);
2267
2268void pdo_stmt_init(void)
2269{
2270	zend_class_entry ce;
2271
2272	INIT_CLASS_ENTRY(ce, "PDOStatement", pdo_dbstmt_functions);
2273	pdo_dbstmt_ce = zend_register_internal_class(&ce);
2274	pdo_dbstmt_ce->get_iterator = pdo_stmt_iter_get;
2275	pdo_dbstmt_ce->create_object = pdo_dbstmt_new;
2276	zend_class_implements(pdo_dbstmt_ce, 1, zend_ce_traversable);
2277	zend_declare_property_null(pdo_dbstmt_ce, "queryString", sizeof("queryString")-1, ZEND_ACC_PUBLIC);
2278
2279	memcpy(&pdo_dbstmt_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
2280	pdo_dbstmt_object_handlers.offset = XtOffsetOf(pdo_stmt_t, std);
2281	pdo_dbstmt_object_handlers.dtor_obj = zend_objects_destroy_object;
2282	pdo_dbstmt_object_handlers.free_obj = pdo_dbstmt_free_storage;
2283	pdo_dbstmt_object_handlers.write_property = dbstmt_prop_write;
2284	pdo_dbstmt_object_handlers.unset_property = dbstmt_prop_delete;
2285	pdo_dbstmt_object_handlers.get_method = dbstmt_method_get;
2286	pdo_dbstmt_object_handlers.compare_objects = dbstmt_compare;
2287	pdo_dbstmt_object_handlers.clone_obj = dbstmt_clone_obj;
2288
2289	INIT_CLASS_ENTRY(ce, "PDORow", pdo_row_functions);
2290	pdo_row_ce = zend_register_internal_class(&ce);
2291	pdo_row_ce->ce_flags |= ZEND_ACC_FINAL; /* when removing this a lot of handlers need to be redone */
2292	pdo_row_ce->create_object = pdo_row_new;
2293	pdo_row_ce->serialize = pdo_row_serialize;
2294}
2295
2296PDO_API void php_pdo_free_statement(pdo_stmt_t *stmt)
2297{
2298	if (stmt->bound_params) {
2299		zend_hash_destroy(stmt->bound_params);
2300		FREE_HASHTABLE(stmt->bound_params);
2301		stmt->bound_params = NULL;
2302	}
2303	if (stmt->bound_param_map) {
2304		zend_hash_destroy(stmt->bound_param_map);
2305		FREE_HASHTABLE(stmt->bound_param_map);
2306		stmt->bound_param_map = NULL;
2307	}
2308	if (stmt->bound_columns) {
2309		zend_hash_destroy(stmt->bound_columns);
2310		FREE_HASHTABLE(stmt->bound_columns);
2311		stmt->bound_columns = NULL;
2312	}
2313
2314	if (stmt->methods && stmt->methods->dtor) {
2315		stmt->methods->dtor(stmt);
2316	}
2317	if (stmt->query_string) {
2318		efree(stmt->query_string);
2319	}
2320
2321	if (stmt->columns) {
2322		int i;
2323		struct pdo_column_data *cols = stmt->columns;
2324
2325		for (i = 0; i < stmt->column_count; i++) {
2326			if (cols[i].name) {
2327				zend_string_release(cols[i].name);
2328				cols[i].name = NULL;
2329			}
2330		}
2331		efree(stmt->columns);
2332		stmt->columns = NULL;
2333	}
2334
2335	if (!Z_ISUNDEF(stmt->fetch.into) && stmt->default_fetch_type == PDO_FETCH_INTO) {
2336		zval_ptr_dtor(&stmt->fetch.into);
2337		ZVAL_UNDEF(&stmt->fetch.into);
2338	}
2339
2340	do_fetch_opt_finish(stmt, 1);
2341
2342	if (!Z_ISUNDEF(stmt->database_object_handle)) {
2343		zval_ptr_dtor(&stmt->database_object_handle);
2344	}
2345	zend_object_std_dtor(&stmt->std);
2346}
2347
2348void pdo_dbstmt_free_storage(zend_object *std)
2349{
2350	pdo_stmt_t *stmt = php_pdo_stmt_fetch_object(std);
2351	php_pdo_free_statement(stmt);
2352}
2353
2354zend_object *pdo_dbstmt_new(zend_class_entry *ce)
2355{
2356	pdo_stmt_t *stmt;
2357
2358	stmt = ecalloc(1, sizeof(pdo_stmt_t) + zend_object_properties_size(ce));
2359	zend_object_std_init(&stmt->std, ce);
2360	object_properties_init(&stmt->std, ce);
2361
2362	stmt->std.handlers = &pdo_dbstmt_object_handlers;
2363
2364	return &stmt->std;
2365}
2366/* }}} */
2367
2368/* {{{ statement iterator */
2369
2370struct php_pdo_iterator {
2371	zend_object_iterator iter;
2372	zend_ulong key;
2373	zval fetch_ahead;
2374};
2375
2376static void pdo_stmt_iter_dtor(zend_object_iterator *iter)
2377{
2378	struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2379
2380	zval_ptr_dtor(&I->iter.data);
2381
2382	if (!Z_ISUNDEF(I->fetch_ahead)) {
2383		zval_ptr_dtor(&I->fetch_ahead);
2384	}
2385}
2386
2387static int pdo_stmt_iter_valid(zend_object_iterator *iter)
2388{
2389	struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2390
2391	return Z_ISUNDEF(I->fetch_ahead) ? FAILURE : SUCCESS;
2392}
2393
2394static zval *pdo_stmt_iter_get_data(zend_object_iterator *iter)
2395{
2396	struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2397
2398	/* sanity */
2399	if (Z_ISUNDEF(I->fetch_ahead)) {
2400		return NULL;
2401	}
2402
2403	return &I->fetch_ahead;
2404}
2405
2406static void pdo_stmt_iter_get_key(zend_object_iterator *iter, zval *key)
2407{
2408	struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2409
2410	if (I->key == (ulong)-1) {
2411		ZVAL_NULL(key);
2412	} else {
2413		ZVAL_LONG(key, I->key);
2414	}
2415}
2416
2417static void pdo_stmt_iter_move_forwards(zend_object_iterator *iter)
2418{
2419	struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2420	pdo_stmt_t *stmt = Z_PDO_STMT_P(&I->iter.data); /* for PDO_HANDLE_STMT_ERR() */
2421
2422	if (!Z_ISUNDEF(I->fetch_ahead)) {
2423		zval_ptr_dtor(&I->fetch_ahead);
2424	}
2425
2426	if (!do_fetch(stmt, TRUE, &I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2427			PDO_FETCH_ORI_NEXT, 0, 0)) {
2428
2429		PDO_HANDLE_STMT_ERR();
2430		I->key = (ulong)-1;
2431		ZVAL_UNDEF(&I->fetch_ahead);
2432
2433		return;
2434	}
2435
2436	I->key++;
2437}
2438
2439static zend_object_iterator_funcs pdo_stmt_iter_funcs = {
2440	pdo_stmt_iter_dtor,
2441	pdo_stmt_iter_valid,
2442	pdo_stmt_iter_get_data,
2443	pdo_stmt_iter_get_key,
2444	pdo_stmt_iter_move_forwards,
2445	NULL
2446};
2447
2448zend_object_iterator *pdo_stmt_iter_get(zend_class_entry *ce, zval *object, int by_ref)
2449{
2450	pdo_stmt_t *stmt = Z_PDO_STMT_P(object);
2451	struct php_pdo_iterator *I;
2452
2453	if (by_ref) {
2454		zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
2455	}
2456
2457	I = ecalloc(1, sizeof(struct php_pdo_iterator));
2458	zend_iterator_init(&I->iter);
2459	I->iter.funcs = &pdo_stmt_iter_funcs;
2460	ZVAL_COPY(&I->iter.data, object);
2461
2462	if (!do_fetch(stmt, 1, &I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2463			PDO_FETCH_ORI_NEXT, 0, 0)) {
2464		PDO_HANDLE_STMT_ERR();
2465		I->key = (ulong)-1;
2466		ZVAL_UNDEF(&I->fetch_ahead);
2467	}
2468
2469	return &I->iter;
2470}
2471
2472/* }}} */
2473
2474/* {{{ overloaded handlers for PDORow class (used by PDO_FETCH_LAZY) */
2475
2476const zend_function_entry pdo_row_functions[] = {
2477	{NULL, NULL, NULL}
2478};
2479
2480static zval *row_prop_read(zval *object, zval *member, int type, void **cache_slot, zval *rv)
2481{
2482	pdo_row_t *row = (pdo_row_t *)Z_OBJ_P(object);
2483	pdo_stmt_t *stmt = row->stmt;
2484	int colno = -1;
2485	zval zobj;
2486	zend_long lval;
2487
2488	ZVAL_NULL(rv);
2489	if (stmt) {
2490		if (Z_TYPE_P(member) == IS_LONG) {
2491			if (Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count) {
2492				fetch_value(stmt, rv, Z_LVAL_P(member), NULL);
2493			}
2494		} else if (Z_TYPE_P(member) == IS_STRING
2495			   && is_numeric_string_ex(Z_STRVAL_P(member), Z_STRLEN_P(member), &lval, NULL, 0, NULL) == IS_LONG)	{
2496			if (lval >= 0 && lval < stmt->column_count) {
2497				fetch_value(stmt, rv, lval, NULL);
2498			}
2499		} else {
2500			convert_to_string(member);
2501			/* TODO: replace this with a hash of available column names to column
2502			 * numbers */
2503			for (colno = 0; colno < stmt->column_count; colno++) {
2504				if (ZSTR_LEN(stmt->columns[colno].name) == Z_STRLEN_P(member) &&
2505				    strncmp(ZSTR_VAL(stmt->columns[colno].name), Z_STRVAL_P(member), Z_STRLEN_P(member)) == 0) {
2506					fetch_value(stmt, rv, colno, NULL);
2507					return rv;
2508				}
2509			}
2510			if (strcmp(Z_STRVAL_P(member), "queryString") == 0) {
2511				ZVAL_OBJ(&zobj, &stmt->std);
2512				//zval_ptr_dtor(rv);
2513				return std_object_handlers.read_property(&zobj, member, type, cache_slot, rv);
2514			}
2515		}
2516	}
2517
2518	return rv;
2519}
2520
2521static zval *row_dim_read(zval *object, zval *member, int type, zval *rv)
2522{
2523	return row_prop_read(object, member, type, NULL, rv);
2524}
2525
2526static void row_prop_write(zval *object, zval *member, zval *value, void **cache_slot)
2527{
2528	php_error_docref(NULL, E_WARNING, "This PDORow is not from a writable result set");
2529}
2530
2531static void row_dim_write(zval *object, zval *member, zval *value)
2532{
2533	php_error_docref(NULL, E_WARNING, "This PDORow is not from a writable result set");
2534}
2535
2536static int row_prop_exists(zval *object, zval *member, int check_empty, void **cache_slot)
2537{
2538	pdo_row_t *row = (pdo_row_t *)Z_OBJ_P(object);
2539	pdo_stmt_t *stmt = row->stmt;
2540	int colno = -1;
2541	zend_long lval;
2542
2543	if (stmt) {
2544		if (Z_TYPE_P(member) == IS_LONG) {
2545			return Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count;
2546		} else if (Z_TYPE_P(member) == IS_STRING) {
2547			if (is_numeric_string_ex(Z_STRVAL_P(member), Z_STRLEN_P(member), &lval, NULL, 0, NULL) == IS_LONG)	{
2548				return lval >=0 && lval < stmt->column_count;
2549			}
2550		} else {
2551			convert_to_string(member);
2552		}
2553
2554		/* TODO: replace this with a hash of available column names to column
2555		 * numbers */
2556		for (colno = 0; colno < stmt->column_count; colno++) {
2557			if (ZSTR_LEN(stmt->columns[colno].name) == Z_STRLEN_P(member) &&
2558			    strncmp(ZSTR_VAL(stmt->columns[colno].name), Z_STRVAL_P(member), Z_STRLEN_P(member)) == 0) {
2559				return 1;
2560			}
2561		}
2562	}
2563
2564	return 0;
2565}
2566
2567static int row_dim_exists(zval *object, zval *member, int check_empty)
2568{
2569	return row_prop_exists(object, member, check_empty, NULL);
2570}
2571
2572static void row_prop_delete(zval *object, zval *offset, void **cache_slot)
2573{
2574	php_error_docref(NULL, E_WARNING, "Cannot delete properties from a PDORow");
2575}
2576
2577static void row_dim_delete(zval *object, zval *offset)
2578{
2579	php_error_docref(NULL, E_WARNING, "Cannot delete properties from a PDORow");
2580}
2581
2582static HashTable *row_get_properties(zval *object)
2583{
2584	pdo_row_t *row = (pdo_row_t *)Z_OBJ_P(object);
2585	pdo_stmt_t *stmt = row->stmt;
2586	int i;
2587
2588	if (stmt == NULL) {
2589		return NULL;
2590	}
2591
2592	if (!stmt->std.properties) {
2593		rebuild_object_properties(&stmt->std);
2594	}
2595	for (i = 0; i < stmt->column_count; i++) {
2596		zval val;
2597		fetch_value(stmt, &val, i, NULL);
2598
2599		zend_hash_update(stmt->std.properties, stmt->columns[i].name, &val);
2600	}
2601
2602	return stmt->std.properties;
2603}
2604
2605static union _zend_function *row_method_get(
2606	zend_object **object_pp,
2607	zend_string *method_name, const zval *key)
2608{
2609	zend_function *fbc;
2610	zend_string *lc_method_name;
2611
2612	lc_method_name = zend_string_alloc(ZSTR_LEN(method_name), 0);
2613	zend_str_tolower_copy(ZSTR_VAL(lc_method_name), ZSTR_VAL(method_name), ZSTR_LEN(method_name));
2614
2615	if ((fbc = zend_hash_find_ptr(&pdo_row_ce->function_table, lc_method_name)) == NULL) {
2616		zend_string_release(lc_method_name);
2617		return NULL;
2618	}
2619
2620	zend_string_release(lc_method_name);
2621	return fbc;
2622}
2623
2624static int row_call_method(zend_string *method, zend_object *object, INTERNAL_FUNCTION_PARAMETERS)
2625{
2626	return FAILURE;
2627}
2628
2629static union _zend_function *row_get_ctor(zend_object *object)
2630{
2631	static zend_internal_function ctor = {0};
2632
2633	ctor.type = ZEND_INTERNAL_FUNCTION;
2634	ctor.function_name = zend_string_init("__construct", sizeof("__construct") - 1, 0);
2635	ctor.scope = pdo_row_ce;
2636	ctor.handler = ZEND_FN(dbrow_constructor);
2637	ctor.fn_flags = ZEND_ACC_PUBLIC;
2638
2639	return (union _zend_function*)&ctor;
2640}
2641
2642static zend_string *row_get_classname(const zend_object *object)
2643{
2644	return zend_string_init("PDORow", sizeof("PDORow") - 1, 0);
2645}
2646
2647static int row_compare(zval *object1, zval *object2)
2648{
2649	return -1;
2650}
2651
2652zend_object_handlers pdo_row_object_handlers = {
2653	0,
2654	zend_objects_destroy_object,
2655	pdo_row_free_storage,
2656	NULL,
2657	row_prop_read,
2658	row_prop_write,
2659	row_dim_read,
2660	row_dim_write,
2661	NULL,
2662	NULL,
2663	NULL,
2664	row_prop_exists,
2665	row_prop_delete,
2666	row_dim_exists,
2667	row_dim_delete,
2668	row_get_properties,
2669	row_method_get,
2670	row_call_method,
2671	row_get_ctor,
2672	row_get_classname,
2673	row_compare,
2674	NULL, /* cast */
2675	NULL
2676};
2677
2678void pdo_row_free_storage(zend_object *std)
2679{
2680	pdo_row_t *row = (pdo_row_t *)std;
2681	if (row->stmt) {
2682		ZVAL_UNDEF(&row->stmt->lazy_object_ref);
2683		OBJ_RELEASE(&row->stmt->std);
2684	}
2685}
2686
2687zend_object *pdo_row_new(zend_class_entry *ce)
2688{
2689	pdo_row_t *row = ecalloc(1, sizeof(pdo_row_t));
2690	zend_object_std_init(&row->std, ce);
2691	row->std.handlers = &pdo_row_object_handlers;
2692
2693	return &row->std;
2694}
2695
2696static int pdo_row_serialize(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data)
2697{
2698	php_error_docref(NULL, E_WARNING, "PDORow instances may not be serialized");
2699	return FAILURE;
2700}
2701/* }}} */
2702
2703/*
2704 * Local variables:
2705 * tab-width: 4
2706 * c-basic-offset: 4
2707 * End:
2708 * vim600: noet sw=4 ts=4 fdm=marker
2709 * vim<600: noet sw=4 ts=4
2710 */
2711