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