1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 5                                                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 1997-2013 The PHP Group                                |
6  +----------------------------------------------------------------------+
7  | This source file is subject to version 3.01 of the PHP license,      |
8  | that is bundled with this package in the file LICENSE, and is        |
9  | available through the world-wide-web at the following url:           |
10  | http://www.php.net/license/3_01.txt                                  |
11  | If you did not receive a copy of the PHP license and are unable to   |
12  | obtain it through the world-wide-web, please send a note to          |
13  | license@php.net so we can mail you a copy immediately.               |
14  +----------------------------------------------------------------------+
15  | Author: Wez Furlong <wez@php.net>                                    |
16  |         Marcus Boerger <helly@php.net>                               |
17  |         Sterling Hughes <sterling@php.net>                           |
18  +----------------------------------------------------------------------+
19*/
20
21/* $Id$ */
22
23/* The PDO Statement Handle Class */
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#include "php.h"
30#include "php_ini.h"
31#include "ext/standard/info.h"
32#include "ext/standard/php_var.h"
33#include "php_pdo.h"
34#include "php_pdo_driver.h"
35#include "php_pdo_int.h"
36#include "zend_exceptions.h"
37#include "zend_interfaces.h"
38#include "php_memory_streams.h"
39
40/* {{{ arginfo */
41ZEND_BEGIN_ARG_INFO(arginfo_pdostatement__void, 0)
42ZEND_END_ARG_INFO()
43
44ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_execute, 0, 0, 0)
45    ZEND_ARG_INFO(0, bound_input_params) /* array */
46ZEND_END_ARG_INFO()
47
48ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_fetch, 0, 0, 0)
49    ZEND_ARG_INFO(0, how)
50    ZEND_ARG_INFO(0, orientation)
51    ZEND_ARG_INFO(0, offset)
52ZEND_END_ARG_INFO()
53
54ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_fetchobject, 0, 0, 0)
55    ZEND_ARG_INFO(0, class_name)
56    ZEND_ARG_INFO(0, ctor_args) /* array */
57ZEND_END_ARG_INFO()
58
59ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_fetchcolumn, 0, 0, 0)
60    ZEND_ARG_INFO(0, column_number)
61ZEND_END_ARG_INFO()
62
63ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_fetchall, 0, 0, 0)
64    ZEND_ARG_INFO(0, how)
65    ZEND_ARG_INFO(0, class_name)
66    ZEND_ARG_INFO(0, ctor_args) /* array */
67ZEND_END_ARG_INFO()
68
69ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_bindvalue, 0, 0, 2)
70    ZEND_ARG_INFO(0, paramno)
71    ZEND_ARG_INFO(0, param)
72    ZEND_ARG_INFO(0, type)
73ZEND_END_ARG_INFO()
74
75ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_bindparam, 0, 0, 2)
76    ZEND_ARG_INFO(0, paramno)
77    ZEND_ARG_INFO(1, param)
78    ZEND_ARG_INFO(0, type)
79    ZEND_ARG_INFO(0, maxlen)
80    ZEND_ARG_INFO(0, driverdata)
81ZEND_END_ARG_INFO()
82
83ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_bindcolumn, 0, 0, 2)
84    ZEND_ARG_INFO(0, column)
85    ZEND_ARG_INFO(1, param)
86    ZEND_ARG_INFO(0, type)
87    ZEND_ARG_INFO(0, maxlen)
88    ZEND_ARG_INFO(0, driverdata)
89ZEND_END_ARG_INFO()
90
91ZEND_BEGIN_ARG_INFO(arginfo_pdostatement_setattribute, 0)
92    ZEND_ARG_INFO(0, attribute)
93    ZEND_ARG_INFO(0, value)
94ZEND_END_ARG_INFO()
95
96ZEND_BEGIN_ARG_INFO(arginfo_pdostatement_getattribute, 0)
97    ZEND_ARG_INFO(0, attribute)
98ZEND_END_ARG_INFO()
99
100ZEND_BEGIN_ARG_INFO(arginfo_pdostatement_getcolumnmeta, 0)
101    ZEND_ARG_INFO(0, column)
102ZEND_END_ARG_INFO()
103
104ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_setfetchmode, 0, 0, 1)
105    ZEND_ARG_INFO(0, mode)
106    ZEND_ARG_INFO(0, params)
107ZEND_END_ARG_INFO()
108/* }}} */
109
110#define PHP_STMT_GET_OBJ    \
111  pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object(getThis() TSRMLS_CC);    \
112  if (!stmt->dbh) { \
113    RETURN_FALSE;   \
114  } \
115
116static PHP_FUNCTION(dbstmt_constructor) /* {{{ */
117{
118    php_error_docref(NULL TSRMLS_CC, E_ERROR, "You should not create a PDOStatement manually");
119}
120/* }}} */
121
122static 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 */
171static 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
183iterate:
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
205int 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
263static 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
278static 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
302static 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 */
434static 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
546static 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
689static 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
742static 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
791static 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
816static 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
830static 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. */
853static 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 PHP_MAJOR_VERSION > 5 || PHP_MINOR_VERSION >= 1
1145                        if (!ce->unserialize) {
1146                            zval_ptr_dtor(&val);
1147                            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "cannot unserialize class" TSRMLS_CC);
1148                            return 0;
1149                        } else if (ce->unserialize(&return_value, ce, 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) {
1150                            zval_ptr_dtor(&val);
1151                            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "cannot unserialize class" TSRMLS_CC);
1152                            zval_dtor(return_value);
1153                            ZVAL_NULL(return_value);
1154                            return 0;
1155                        } else {
1156                            zval_ptr_dtor(&val);
1157                        }
1158#endif
1159                    }
1160                    break;
1161
1162                case PDO_FETCH_FUNC:
1163                    stmt->fetch.func.values[idx] = val;
1164                    stmt->fetch.cls.fci.params[idx] = &stmt->fetch.func.values[idx];
1165                    break;
1166
1167                default:
1168                    zval_ptr_dtor(&val);
1169                    pdo_raise_impl_error(stmt->dbh, stmt, "22003", "mode is out of range" TSRMLS_CC);
1170                    return 0;
1171                    break;
1172            }
1173        }
1174
1175        switch (how) {
1176            case PDO_FETCH_CLASS:
1177                if (ce->constructor && !(flags & (PDO_FETCH_PROPS_LATE | PDO_FETCH_SERIALIZE))) {
1178                    stmt->fetch.cls.fci.object_ptr = return_value;
1179                    stmt->fetch.cls.fcc.object_ptr = return_value;
1180                    if (zend_call_function(&stmt->fetch.cls.fci, &stmt->fetch.cls.fcc TSRMLS_CC) == FAILURE) {
1181                        pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call class constructor" TSRMLS_CC);
1182                        return 0;
1183                    } else {
1184                        if (stmt->fetch.cls.retval_ptr) {
1185                            zval_ptr_dtor(&stmt->fetch.cls.retval_ptr);
1186                        }
1187                    }
1188                }
1189                if (flags & PDO_FETCH_CLASSTYPE) {
1190                    do_fetch_opt_finish(stmt, 0 TSRMLS_CC);
1191                    stmt->fetch.cls.ce = old_ce;
1192                    stmt->fetch.cls.ctor_args = old_ctor_args;
1193                    stmt->fetch.cls.fci.param_count = old_arg_count;
1194                }
1195                break;
1196
1197            case PDO_FETCH_FUNC:
1198                stmt->fetch.func.fci.param_count = idx;
1199                stmt->fetch.func.fci.retval_ptr_ptr = &retval;
1200                if (zend_call_function(&stmt->fetch.func.fci, &stmt->fetch.func.fcc TSRMLS_CC) == FAILURE) {
1201                    pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call user-supplied function" TSRMLS_CC);
1202                    return 0;
1203                } else {
1204                    if (return_all) {
1205                        zval_ptr_dtor(&return_value); /* we don't need that */
1206                        return_value = retval;
1207                    } else if (retval) {
1208                        MAKE_COPY_ZVAL(&retval, return_value);
1209                        zval_ptr_dtor(&retval);
1210                    }
1211                }
1212                while(idx--) {
1213                    zval_ptr_dtor(&stmt->fetch.func.values[idx]);
1214                }
1215                break;
1216
1217            default:
1218                break;
1219        }
1220
1221        if (return_all) {
1222            if ((flags & PDO_FETCH_UNIQUE) == PDO_FETCH_UNIQUE) {
1223                add_assoc_zval(return_all, Z_STRVAL(grp_val), return_value);
1224            } else {
1225                if (zend_symtable_find(Z_ARRVAL_P(return_all), Z_STRVAL(grp_val), Z_STRLEN(grp_val)+1, (void**)&pgrp) == FAILURE) {
1226                    MAKE_STD_ZVAL(grp);
1227                    array_init(grp);
1228                    add_assoc_zval(return_all, Z_STRVAL(grp_val), grp);
1229                } else {
1230                    grp = *pgrp;
1231                }
1232                add_next_index_zval(grp, return_value);
1233            }
1234            zval_dtor(&grp_val);
1235        }
1236
1237    }
1238
1239    return 1;
1240}
1241/* }}} */
1242
1243static int pdo_stmt_verify_mode(pdo_stmt_t *stmt, long mode, int fetch_all TSRMLS_DC) /* {{{ */
1244{
1245    int flags = mode & PDO_FETCH_FLAGS;
1246
1247    mode = mode & ~PDO_FETCH_FLAGS;
1248
1249    if (mode < 0 || mode > PDO_FETCH__MAX) {
1250        pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "invalid fetch mode" TSRMLS_CC);
1251        return 0;
1252    }
1253
1254    if (mode == PDO_FETCH_USE_DEFAULT) {
1255        flags = stmt->default_fetch_type & PDO_FETCH_FLAGS;
1256        mode = stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
1257    }
1258
1259#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 1
1260    if ((flags & PDO_FETCH_SERIALIZE) == PDO_FETCH_SERIALIZE) {
1261        pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "PDO::FETCH_SERIALIZE is not supported in this PHP version" TSRMLS_CC);
1262        return 0;
1263    }
1264#endif
1265
1266    switch(mode) {
1267    case PDO_FETCH_FUNC:
1268        if (!fetch_all) {
1269            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_FUNC is only allowed in PDOStatement::fetchAll()" TSRMLS_CC);
1270            return 0;
1271        }
1272        return 1;
1273
1274    case PDO_FETCH_LAZY:
1275        if (fetch_all) {
1276            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_LAZY can't be used with PDOStatement::fetchAll()" TSRMLS_CC);
1277            return 0;
1278        }
1279        /* fall through */
1280
1281    default:
1282        if ((flags & PDO_FETCH_SERIALIZE) == PDO_FETCH_SERIALIZE) {
1283            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_SERIALIZE can only be used together with PDO::FETCH_CLASS" TSRMLS_CC);
1284            return 0;
1285        }
1286        if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
1287            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_CLASSTYPE can only be used together with PDO::FETCH_CLASS" TSRMLS_CC);
1288            return 0;
1289        }
1290        if (mode >= PDO_FETCH__MAX) {
1291            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "invalid fetch mode" TSRMLS_CC);
1292            return 0;
1293        }
1294        /* no break; */
1295
1296    case PDO_FETCH_CLASS:
1297        return 1;
1298    }
1299}
1300/* }}} */
1301
1302/* {{{ proto mixed PDOStatement::fetch([int $how = PDO_FETCH_BOTH [, int $orientation [, int $offset]]])
1303   Fetches the next row and returns it, or false if there are no more rows */
1304static PHP_METHOD(PDOStatement, fetch)
1305{
1306    long how = PDO_FETCH_USE_DEFAULT;
1307    long ori = PDO_FETCH_ORI_NEXT;
1308    long off = 0;
1309        PHP_STMT_GET_OBJ;
1310
1311    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lll", &how,
1312            &ori, &off)) {
1313        RETURN_FALSE;
1314    }
1315
1316    PDO_STMT_CLEAR_ERR();
1317
1318    if (!pdo_stmt_verify_mode(stmt, how, 0 TSRMLS_CC)) {
1319        RETURN_FALSE;
1320    }
1321
1322    if (!do_fetch(stmt, TRUE, return_value, how, ori, off, 0 TSRMLS_CC)) {
1323        PDO_HANDLE_STMT_ERR();
1324        RETURN_FALSE;
1325    }
1326}
1327/* }}} */
1328
1329/* {{{ proto mixed PDOStatement::fetchObject([string class_name [, NULL|array ctor_args]])
1330   Fetches the next row and returns it as an object. */
1331static PHP_METHOD(PDOStatement, fetchObject)
1332{
1333    long how = PDO_FETCH_CLASS;
1334    long ori = PDO_FETCH_ORI_NEXT;
1335    long off = 0;
1336    char *class_name;
1337    int class_name_len;
1338    zend_class_entry *old_ce;
1339    zval *old_ctor_args, *ctor_args;
1340    int error = 0, old_arg_count;
1341
1342    PHP_STMT_GET_OBJ;
1343
1344    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sz",
1345        &class_name, &class_name_len, &ctor_args)) {
1346        RETURN_FALSE;
1347    }
1348
1349    PDO_STMT_CLEAR_ERR();
1350
1351    if (!pdo_stmt_verify_mode(stmt, how, 0 TSRMLS_CC)) {
1352        RETURN_FALSE;
1353    }
1354
1355    old_ce = stmt->fetch.cls.ce;
1356    old_ctor_args = stmt->fetch.cls.ctor_args;
1357    old_arg_count = stmt->fetch.cls.fci.param_count;
1358
1359    do_fetch_opt_finish(stmt, 0 TSRMLS_CC);
1360
1361    switch(ZEND_NUM_ARGS()) {
1362    case 0:
1363        stmt->fetch.cls.ce = zend_standard_class_def;
1364        break;
1365    case 2:
1366        if (Z_TYPE_P(ctor_args) != IS_NULL && Z_TYPE_P(ctor_args) != IS_ARRAY) {
1367            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "ctor_args must be either NULL or an array" TSRMLS_CC);
1368            error = 1;
1369            break;
1370        }
1371        if (Z_TYPE_P(ctor_args) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(ctor_args))) {
1372            ALLOC_ZVAL(stmt->fetch.cls.ctor_args);
1373            *stmt->fetch.cls.ctor_args = *ctor_args;
1374            zval_copy_ctor(stmt->fetch.cls.ctor_args);
1375        } else {
1376            stmt->fetch.cls.ctor_args = NULL;
1377        }
1378        /* no break */
1379    case 1:
1380        stmt->fetch.cls.ce = zend_fetch_class(class_name, class_name_len, ZEND_FETCH_CLASS_AUTO TSRMLS_CC);
1381
1382        if (!stmt->fetch.cls.ce) {
1383            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Could not find user-supplied class" TSRMLS_CC);
1384            error = 1;
1385            break;
1386        }
1387    }
1388
1389    if (!error && !do_fetch(stmt, TRUE, return_value, how, ori, off, 0 TSRMLS_CC)) {
1390        error = 1;
1391    }
1392    if (error) {
1393        PDO_HANDLE_STMT_ERR();
1394    }
1395    do_fetch_opt_finish(stmt, 1 TSRMLS_CC);
1396
1397    stmt->fetch.cls.ce = old_ce;
1398    stmt->fetch.cls.ctor_args = old_ctor_args;
1399    stmt->fetch.cls.fci.param_count = old_arg_count;
1400    if (error) {
1401        RETURN_FALSE;
1402    }
1403}
1404/* }}} */
1405
1406/* {{{ proto string PDOStatement::fetchColumn([int column_number])
1407   Returns a data of the specified column in the result set. */
1408static PHP_METHOD(PDOStatement, fetchColumn)
1409{
1410    long col_n = 0;
1411    PHP_STMT_GET_OBJ;
1412
1413    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &col_n)) {
1414        RETURN_FALSE;
1415    }
1416
1417    PDO_STMT_CLEAR_ERR();
1418
1419    if (!do_fetch_common(stmt, PDO_FETCH_ORI_NEXT, 0, TRUE TSRMLS_CC)) {
1420        PDO_HANDLE_STMT_ERR();
1421        RETURN_FALSE;
1422    }
1423
1424    fetch_value(stmt, return_value, col_n, NULL TSRMLS_CC);
1425}
1426/* }}} */
1427
1428/* {{{ proto array PDOStatement::fetchAll([int $how = PDO_FETCH_BOTH [, string class_name [, NULL|array ctor_args]]])
1429   Returns an array of all of the results. */
1430static PHP_METHOD(PDOStatement, fetchAll)
1431{
1432    long how = PDO_FETCH_USE_DEFAULT;
1433    zval *data, *return_all;
1434    zval *arg2;
1435    zend_class_entry *old_ce;
1436    zval *old_ctor_args, *ctor_args = NULL;
1437    int error = 0, flags, old_arg_count;
1438    PHP_STMT_GET_OBJ;
1439
1440    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lzz", &how, &arg2, &ctor_args)) {
1441        RETURN_FALSE;
1442    }
1443
1444    if (!pdo_stmt_verify_mode(stmt, how, 1 TSRMLS_CC)) {
1445        RETURN_FALSE;
1446    }
1447
1448    old_ce = stmt->fetch.cls.ce;
1449    old_ctor_args = stmt->fetch.cls.ctor_args;
1450    old_arg_count = stmt->fetch.cls.fci.param_count;
1451
1452    do_fetch_opt_finish(stmt, 0 TSRMLS_CC);
1453
1454    switch(how & ~PDO_FETCH_FLAGS) {
1455    case PDO_FETCH_CLASS:
1456        switch(ZEND_NUM_ARGS()) {
1457        case 0:
1458        case 1:
1459            stmt->fetch.cls.ce = zend_standard_class_def;
1460            break;
1461        case 3:
1462            if (Z_TYPE_P(ctor_args) != IS_NULL && Z_TYPE_P(ctor_args) != IS_ARRAY) {
1463                pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "ctor_args must be either NULL or an array" TSRMLS_CC);
1464                error = 1;
1465                break;
1466            }
1467            if (Z_TYPE_P(ctor_args) != IS_ARRAY || !zend_hash_num_elements(Z_ARRVAL_P(ctor_args))) {
1468                ctor_args = NULL;
1469            }
1470            /* no break */
1471        case 2:
1472            stmt->fetch.cls.ctor_args = ctor_args; /* we're not going to free these */
1473            if (Z_TYPE_P(arg2) != IS_STRING) {
1474                pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Invalid class name (should be a string)" TSRMLS_CC);
1475                error = 1;
1476                break;
1477            } else {
1478                stmt->fetch.cls.ce = zend_fetch_class(Z_STRVAL_P(arg2), Z_STRLEN_P(arg2), ZEND_FETCH_CLASS_AUTO TSRMLS_CC);
1479                if (!stmt->fetch.cls.ce) {
1480                    pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not find user-specified class" TSRMLS_CC);
1481                    error = 1;
1482                    break;
1483                }
1484            }
1485        }
1486        if (!error) {
1487            do_fetch_class_prepare(stmt TSRMLS_CC);
1488        }
1489        break;
1490
1491    case PDO_FETCH_FUNC:
1492        switch(ZEND_NUM_ARGS()) {
1493        case 0:
1494        case 1:
1495            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "no fetch function specified" TSRMLS_CC);
1496            error = 1;
1497            break;
1498        case 3:
1499        case 2:
1500            stmt->fetch.func.function = arg2;
1501            if (do_fetch_func_prepare(stmt TSRMLS_CC) == 0) {
1502                error = 1;
1503            }
1504            break;
1505        }
1506        break;
1507
1508    case PDO_FETCH_COLUMN:
1509        switch(ZEND_NUM_ARGS()) {
1510        case 0:
1511        case 1:
1512            stmt->fetch.column = how & PDO_FETCH_GROUP ? -1 : 0;
1513            break;
1514        case 2:
1515            convert_to_long(arg2);
1516            stmt->fetch.column = Z_LVAL_P(arg2);
1517            break;
1518        case 3:
1519            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Third parameter not allowed for PDO::FETCH_COLUMN" TSRMLS_CC);
1520            error = 1;
1521        }
1522        break;
1523
1524    default:
1525        if (ZEND_NUM_ARGS() > 1) {
1526            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Extraneous additional parameters" TSRMLS_CC);
1527            error = 1;
1528        }
1529    }
1530
1531    flags = how & PDO_FETCH_FLAGS;
1532
1533    if ((how & ~PDO_FETCH_FLAGS) == PDO_FETCH_USE_DEFAULT) {
1534        flags |= stmt->default_fetch_type & PDO_FETCH_FLAGS;
1535        how |= stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
1536    }
1537
1538    if (!error) {
1539        PDO_STMT_CLEAR_ERR();
1540        MAKE_STD_ZVAL(data);
1541        if (    (how & PDO_FETCH_GROUP) || how == PDO_FETCH_KEY_PAIR ||
1542            (how == PDO_FETCH_USE_DEFAULT && stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)
1543        ) {
1544            array_init(return_value);
1545            return_all = return_value;
1546        } else {
1547            return_all = 0;
1548        }
1549        if (!do_fetch(stmt, TRUE, data, how | flags, PDO_FETCH_ORI_NEXT, 0, return_all TSRMLS_CC)) {
1550            FREE_ZVAL(data);
1551            error = 2;
1552        }
1553    }
1554    if (!error) {
1555        if ((how & PDO_FETCH_GROUP)) {
1556            do {
1557                MAKE_STD_ZVAL(data);
1558            } while (do_fetch(stmt, TRUE, data, how | flags, PDO_FETCH_ORI_NEXT, 0, return_all TSRMLS_CC));
1559        } else if (how == PDO_FETCH_KEY_PAIR || (how == PDO_FETCH_USE_DEFAULT && stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)) {
1560            while (do_fetch(stmt, TRUE, data, how | flags, PDO_FETCH_ORI_NEXT, 0, return_all TSRMLS_CC));
1561        } else {
1562            array_init(return_value);
1563            do {
1564                add_next_index_zval(return_value, data);
1565                MAKE_STD_ZVAL(data);
1566            } while (do_fetch(stmt, TRUE, data, how | flags, PDO_FETCH_ORI_NEXT, 0, 0 TSRMLS_CC));
1567        }
1568        FREE_ZVAL(data);
1569    }
1570
1571    do_fetch_opt_finish(stmt, 0 TSRMLS_CC);
1572
1573    stmt->fetch.cls.ce = old_ce;
1574    stmt->fetch.cls.ctor_args = old_ctor_args;
1575    stmt->fetch.cls.fci.param_count = old_arg_count;
1576
1577    if (error) {
1578        PDO_HANDLE_STMT_ERR();
1579        if (error != 2) {
1580            RETURN_FALSE;
1581        } else { /* on no results, return an empty array */
1582            if (Z_TYPE_P(return_value) != IS_ARRAY) {
1583                array_init(return_value);
1584            }
1585            return;
1586        }
1587    }
1588}
1589/* }}} */
1590
1591static int register_bound_param(INTERNAL_FUNCTION_PARAMETERS, pdo_stmt_t *stmt, int is_param) /* {{{ */
1592{
1593    struct pdo_bound_param_data param = {0};
1594    long param_type = PDO_PARAM_STR;
1595
1596    param.paramno = -1;
1597
1598    if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC,
1599            "lz|llz!", &param.paramno, &param.parameter, &param_type, &param.max_value_len,
1600            &param.driver_params)) {
1601        if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|llz!", &param.name,
1602                &param.namelen, &param.parameter, &param_type, &param.max_value_len,
1603                &param.driver_params)) {
1604            return 0;
1605        }
1606    }
1607
1608    param.param_type = (int) param_type;
1609
1610    if (param.paramno > 0) {
1611        --param.paramno; /* make it zero-based internally */
1612    } else if (!param.name) {
1613        pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "Columns/Parameters are 1-based" TSRMLS_CC);
1614        return 0;
1615    }
1616
1617    Z_ADDREF_P(param.parameter);
1618    if (!really_register_bound_param(&param, stmt, is_param TSRMLS_CC)) {
1619        if (param.parameter) {
1620            zval_ptr_dtor(&(param.parameter));
1621            param.parameter = NULL;
1622        }
1623        return 0;
1624    }
1625    return 1;
1626} /* }}} */
1627
1628/* {{{ proto bool PDOStatement::bindValue(mixed $paramno, mixed $param [, int $type ])
1629   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(). */
1630static PHP_METHOD(PDOStatement, bindValue)
1631{
1632    struct pdo_bound_param_data param = {0};
1633    long param_type = PDO_PARAM_STR;
1634    PHP_STMT_GET_OBJ;
1635
1636    param.paramno = -1;
1637
1638    if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC,
1639            "lz/|l", &param.paramno, &param.parameter, &param_type)) {
1640        if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz/|l", &param.name,
1641                &param.namelen, &param.parameter, &param_type)) {
1642            RETURN_FALSE;
1643        }
1644    }
1645
1646    param.param_type = (int) param_type;
1647
1648    if (param.paramno > 0) {
1649        --param.paramno; /* make it zero-based internally */
1650    } else if (!param.name) {
1651        pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "Columns/Parameters are 1-based" TSRMLS_CC);
1652        RETURN_FALSE;
1653    }
1654
1655    Z_ADDREF_P(param.parameter);
1656    if (!really_register_bound_param(&param, stmt, TRUE TSRMLS_CC)) {
1657        if (param.parameter) {
1658            zval_ptr_dtor(&(param.parameter));
1659            param.parameter = NULL;
1660        }
1661        RETURN_FALSE;
1662    }
1663    RETURN_TRUE;
1664}
1665/* }}} */
1666
1667
1668/* {{{ proto bool PDOStatement::bindParam(mixed $paramno, mixed &$param [, int $type [, int $maxlen [, mixed $driverdata]]])
1669   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(). */
1670static PHP_METHOD(PDOStatement, bindParam)
1671{
1672    PHP_STMT_GET_OBJ;
1673    RETURN_BOOL(register_bound_param(INTERNAL_FUNCTION_PARAM_PASSTHRU, stmt, TRUE));
1674}
1675/* }}} */
1676
1677/* {{{ proto bool PDOStatement::bindColumn(mixed $column, mixed &$param [, int $type [, int $maxlen [, mixed $driverdata]]])
1678   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(). */
1679static PHP_METHOD(PDOStatement, bindColumn)
1680{
1681    PHP_STMT_GET_OBJ;
1682    RETURN_BOOL(register_bound_param(INTERNAL_FUNCTION_PARAM_PASSTHRU, stmt, FALSE));
1683}
1684/* }}} */
1685
1686/* {{{ proto int PDOStatement::rowCount()
1687   Returns the number of rows in a result set, or the number of rows affected by the last execute().  It is not always meaningful. */
1688static PHP_METHOD(PDOStatement, rowCount)
1689{
1690    PHP_STMT_GET_OBJ;
1691
1692    RETURN_LONG(stmt->row_count);
1693}
1694/* }}} */
1695
1696/* {{{ proto string PDOStatement::errorCode()
1697   Fetch the error code associated with the last operation on the statement handle */
1698static PHP_METHOD(PDOStatement, errorCode)
1699{
1700    PHP_STMT_GET_OBJ;
1701
1702    if (zend_parse_parameters_none() == FAILURE) {
1703        return;
1704    }
1705
1706    if (stmt->error_code[0] == '\0') {
1707        RETURN_NULL();
1708    }
1709
1710    RETURN_STRING(stmt->error_code, 1);
1711}
1712/* }}} */
1713
1714/* {{{ proto array PDOStatement::errorInfo()
1715   Fetch extended error information associated with the last operation on the statement handle */
1716static PHP_METHOD(PDOStatement, errorInfo)
1717{
1718    int error_count;
1719    int error_count_diff     = 0;
1720    int error_expected_count = 3;
1721
1722    PHP_STMT_GET_OBJ;
1723
1724    if (zend_parse_parameters_none() == FAILURE) {
1725        return;
1726    }
1727
1728    array_init(return_value);
1729    add_next_index_string(return_value, stmt->error_code, 1);
1730
1731    if (stmt->dbh->methods->fetch_err) {
1732        stmt->dbh->methods->fetch_err(stmt->dbh, stmt, return_value TSRMLS_CC);
1733    }
1734
1735    error_count = zend_hash_num_elements(Z_ARRVAL_P(return_value));
1736
1737    if (error_expected_count > error_count) {
1738        int current_index;
1739
1740        error_count_diff = error_expected_count - error_count;
1741        for (current_index = 0; current_index < error_count_diff; current_index++) {
1742            add_next_index_null(return_value);
1743        }
1744    }
1745}
1746/* }}} */
1747
1748/* {{{ proto bool PDOStatement::setAttribute(long attribute, mixed value)
1749   Set an attribute */
1750static PHP_METHOD(PDOStatement, setAttribute)
1751{
1752    long attr;
1753    zval *value = NULL;
1754    PHP_STMT_GET_OBJ;
1755
1756    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lz!", &attr, &value)) {
1757        RETURN_FALSE;
1758    }
1759
1760    if (!stmt->methods->set_attribute) {
1761        goto fail;
1762    }
1763
1764    PDO_STMT_CLEAR_ERR();
1765    if (stmt->methods->set_attribute(stmt, attr, value TSRMLS_CC)) {
1766        RETURN_TRUE;
1767    }
1768
1769fail:
1770    if (!stmt->methods->set_attribute) {
1771        pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "This driver doesn't support setting attributes" TSRMLS_CC);
1772    } else {
1773        PDO_HANDLE_STMT_ERR();
1774    }
1775    RETURN_FALSE;
1776}
1777/* }}} */
1778
1779/* {{{ proto mixed PDOStatement::getAttribute(long attribute)
1780   Get an attribute */
1781
1782static int generic_stmt_attr_get(pdo_stmt_t *stmt, zval *return_value, long attr)
1783{
1784    switch (attr) {
1785        case PDO_ATTR_EMULATE_PREPARES:
1786            RETVAL_BOOL(stmt->supports_placeholders == PDO_PLACEHOLDER_NONE);
1787            return 1;
1788    }
1789    return 0;
1790}
1791
1792static PHP_METHOD(PDOStatement, getAttribute)
1793{
1794    long attr;
1795    PHP_STMT_GET_OBJ;
1796
1797    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &attr)) {
1798        RETURN_FALSE;
1799    }
1800
1801    if (!stmt->methods->get_attribute) {
1802        if (!generic_stmt_attr_get(stmt, return_value, attr)) {
1803            pdo_raise_impl_error(stmt->dbh, stmt, "IM001",
1804                "This driver doesn't support getting attributes" TSRMLS_CC);
1805            RETURN_FALSE;
1806        }
1807        return;
1808    }
1809
1810    PDO_STMT_CLEAR_ERR();
1811    switch (stmt->methods->get_attribute(stmt, attr, return_value TSRMLS_CC)) {
1812        case -1:
1813            PDO_HANDLE_STMT_ERR();
1814            RETURN_FALSE;
1815
1816        case 0:
1817            if (!generic_stmt_attr_get(stmt, return_value, attr)) {
1818                /* XXX: should do something better here */
1819                pdo_raise_impl_error(stmt->dbh, stmt, "IM001",
1820                    "driver doesn't support getting that attribute" TSRMLS_CC);
1821                RETURN_FALSE;
1822            }
1823            return;
1824
1825        default:
1826            return;
1827    }
1828}
1829/* }}} */
1830
1831/* {{{ proto int PDOStatement::columnCount()
1832   Returns the number of columns in the result set */
1833static PHP_METHOD(PDOStatement, columnCount)
1834{
1835    PHP_STMT_GET_OBJ;
1836    if (zend_parse_parameters_none() == FAILURE) {
1837        return;
1838    }
1839    RETURN_LONG(stmt->column_count);
1840}
1841/* }}} */
1842
1843/* {{{ proto array PDOStatement::getColumnMeta(int $column)
1844   Returns meta data for a numbered column */
1845static PHP_METHOD(PDOStatement, getColumnMeta)
1846{
1847    long colno;
1848    struct pdo_column_data *col;
1849    PHP_STMT_GET_OBJ;
1850
1851    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &colno)) {
1852        RETURN_FALSE;
1853    }
1854    if(colno < 0) {
1855        pdo_raise_impl_error(stmt->dbh, stmt, "42P10", "column number must be non-negative" TSRMLS_CC);
1856        RETURN_FALSE;
1857    }
1858
1859    if (!stmt->methods->get_column_meta) {
1860        pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "driver doesn't support meta data" TSRMLS_CC);
1861        RETURN_FALSE;
1862    }
1863
1864    PDO_STMT_CLEAR_ERR();
1865    if (FAILURE == stmt->methods->get_column_meta(stmt, colno, return_value TSRMLS_CC)) {
1866        PDO_HANDLE_STMT_ERR();
1867        RETURN_FALSE;
1868    }
1869
1870    /* add stock items */
1871    col = &stmt->columns[colno];
1872    add_assoc_string(return_value, "name", col->name, 1);
1873    add_assoc_long(return_value, "len", col->maxlen); /* FIXME: unsigned ? */
1874    add_assoc_long(return_value, "precision", col->precision);
1875    if (col->param_type != PDO_PARAM_ZVAL) {
1876        /* if param_type is PDO_PARAM_ZVAL the driver has to provide correct data */
1877        add_assoc_long(return_value, "pdo_type", col->param_type);
1878    }
1879}
1880/* }}} */
1881
1882/* {{{ proto bool PDOStatement::setFetchMode(int mode [mixed* params])
1883   Changes the default fetch mode for subsequent fetches (params have different meaning for different fetch modes) */
1884
1885int pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAMETERS, pdo_stmt_t *stmt, int skip)
1886{
1887    long mode = PDO_FETCH_BOTH;
1888    int flags, argc = ZEND_NUM_ARGS() - skip;
1889    zval ***args;
1890    zend_class_entry **cep;
1891    int retval;
1892
1893    do_fetch_opt_finish(stmt, 1 TSRMLS_CC);
1894
1895    switch (stmt->default_fetch_type) {
1896        case PDO_FETCH_INTO:
1897            if (stmt->fetch.into) {
1898                zval_ptr_dtor(&stmt->fetch.into);
1899                stmt->fetch.into = NULL;
1900            }
1901            break;
1902        default:
1903            ;
1904    }
1905
1906    stmt->default_fetch_type = PDO_FETCH_BOTH;
1907
1908    if (argc == 0) {
1909        return SUCCESS;
1910    }
1911
1912    args = safe_emalloc(ZEND_NUM_ARGS(), sizeof(zval*), 0);
1913
1914    retval = zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args);
1915
1916    if (SUCCESS == retval) {
1917        if (Z_TYPE_PP(args[skip]) != IS_LONG) {
1918            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "mode must be an integer" TSRMLS_CC);
1919            retval = FAILURE;
1920        } else {
1921            mode = Z_LVAL_PP(args[skip]);
1922            flags = mode & PDO_FETCH_FLAGS;
1923
1924            retval = pdo_stmt_verify_mode(stmt, mode, 0 TSRMLS_CC);
1925        }
1926    }
1927
1928    if (FAILURE == retval) {
1929        PDO_STMT_CLEAR_ERR();
1930        efree(args);
1931        return FAILURE;
1932    }
1933
1934    retval = FAILURE;
1935    switch (mode & ~PDO_FETCH_FLAGS) {
1936        case PDO_FETCH_USE_DEFAULT:
1937        case PDO_FETCH_LAZY:
1938        case PDO_FETCH_ASSOC:
1939        case PDO_FETCH_NUM:
1940        case PDO_FETCH_BOTH:
1941        case PDO_FETCH_OBJ:
1942        case PDO_FETCH_BOUND:
1943        case PDO_FETCH_NAMED:
1944        case PDO_FETCH_KEY_PAIR:
1945            if (argc != 1) {
1946                pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode doesn't allow any extra arguments" TSRMLS_CC);
1947            } else {
1948                retval = SUCCESS;
1949            }
1950            break;
1951
1952        case PDO_FETCH_COLUMN:
1953            if (argc != 2) {
1954                pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode requires the colno argument" TSRMLS_CC);
1955            } else  if (Z_TYPE_PP(args[skip+1]) != IS_LONG) {
1956                pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "colno must be an integer" TSRMLS_CC);
1957            } else {
1958                stmt->fetch.column = Z_LVAL_PP(args[skip+1]);
1959                retval = SUCCESS;
1960            }
1961            break;
1962
1963        case PDO_FETCH_CLASS:
1964            /* Gets its class name from 1st column */
1965            if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
1966                if (argc != 1) {
1967                    pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode doesn't allow any extra arguments" TSRMLS_CC);
1968                } else {
1969                    stmt->fetch.cls.ce = NULL;
1970                    retval = SUCCESS;
1971                }
1972            } else {
1973                if (argc < 2) {
1974                    pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode requires the classname argument" TSRMLS_CC);
1975                } else if (argc > 3) {
1976                    pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "too many arguments" TSRMLS_CC);
1977                } else if (Z_TYPE_PP(args[skip+1]) != IS_STRING) {
1978                    pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "classname must be a string" TSRMLS_CC);
1979                } else {
1980                    retval = zend_lookup_class(Z_STRVAL_PP(args[skip+1]),
1981                        Z_STRLEN_PP(args[skip+1]), &cep TSRMLS_CC);
1982
1983                    if (SUCCESS == retval && cep && *cep) {
1984                        stmt->fetch.cls.ce = *cep;
1985                    }
1986                }
1987            }
1988
1989            if (SUCCESS == retval) {
1990                stmt->fetch.cls.ctor_args = NULL;
1991#ifdef ilia_0 /* we'll only need this when we have persistent statements, if ever */
1992                if (stmt->dbh->is_persistent) {
1993                    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");
1994                }
1995#endif
1996                if (argc == 3) {
1997                    if (Z_TYPE_PP(args[skip+2]) != IS_NULL && Z_TYPE_PP(args[skip+2]) != IS_ARRAY) {
1998                        pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "ctor_args must be either NULL or an array" TSRMLS_CC);
1999                        retval = FAILURE;
2000                    } else if (Z_TYPE_PP(args[skip+2]) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_PP(args[skip+2]))) {
2001                        ALLOC_ZVAL(stmt->fetch.cls.ctor_args);
2002                        *stmt->fetch.cls.ctor_args = **args[skip+2];
2003                        zval_copy_ctor(stmt->fetch.cls.ctor_args);
2004                    }
2005                }
2006
2007                if (SUCCESS == retval) {
2008                    do_fetch_class_prepare(stmt TSRMLS_CC);
2009                }
2010            }
2011
2012            break;
2013
2014        case PDO_FETCH_INTO:
2015            if (argc != 2) {
2016                pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode requires the object parameter" TSRMLS_CC);
2017            } else if (Z_TYPE_PP(args[skip+1]) != IS_OBJECT) {
2018                pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "object must be an object" TSRMLS_CC);
2019            } else {
2020                retval = SUCCESS;
2021            }
2022
2023            if (SUCCESS == retval) {
2024#ifdef ilia_0 /* we'll only need this when we have persistent statements, if ever */
2025                if (stmt->dbh->is_persistent) {
2026                    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");
2027                }
2028#endif
2029                MAKE_STD_ZVAL(stmt->fetch.into);
2030
2031                Z_TYPE_P(stmt->fetch.into) = IS_OBJECT;
2032                Z_OBJ_HANDLE_P(stmt->fetch.into) = Z_OBJ_HANDLE_PP(args[skip+1]);
2033                Z_OBJ_HT_P(stmt->fetch.into) = Z_OBJ_HT_PP(args[skip+1]);
2034                zend_objects_store_add_ref(stmt->fetch.into TSRMLS_CC);
2035            }
2036
2037            break;
2038
2039        default:
2040            pdo_raise_impl_error(stmt->dbh, stmt, "22003", "Invalid fetch mode specified" TSRMLS_CC);
2041    }
2042
2043    if (SUCCESS == retval) {
2044        stmt->default_fetch_type = mode;
2045    }
2046
2047    /*
2048     * PDO error (if any) has already been raised at this point.
2049     *
2050     * The error_code is cleared, otherwise the caller will read the
2051     * last error message from the driver.
2052     *
2053     */
2054    PDO_STMT_CLEAR_ERR();
2055
2056    efree(args);
2057
2058    return retval;
2059}
2060
2061static PHP_METHOD(PDOStatement, setFetchMode)
2062{
2063    PHP_STMT_GET_OBJ;
2064
2065    RETVAL_BOOL(
2066        pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAM_PASSTHRU,
2067            stmt, 0) == SUCCESS ? 1 : 0
2068        );
2069}
2070/* }}} */
2071
2072/* {{{ proto bool PDOStatement::nextRowset()
2073   Advances to the next rowset in a multi-rowset statement handle. Returns true if it succeded, false otherwise */
2074
2075static int pdo_stmt_do_next_rowset(pdo_stmt_t *stmt TSRMLS_DC)
2076{
2077    /* un-describe */
2078    if (stmt->columns) {
2079        int i;
2080        struct pdo_column_data *cols = stmt->columns;
2081
2082        for (i = 0; i < stmt->column_count; i++) {
2083            efree(cols[i].name);
2084        }
2085        efree(stmt->columns);
2086        stmt->columns = NULL;
2087        stmt->column_count = 0;
2088    }
2089
2090    if (!stmt->methods->next_rowset(stmt TSRMLS_CC)) {
2091        /* Set the executed flag to 0 to reallocate columns on next execute */
2092        stmt->executed = 0;
2093        return 0;
2094    }
2095
2096    pdo_stmt_describe_columns(stmt TSRMLS_CC);
2097
2098    return 1;
2099}
2100
2101static PHP_METHOD(PDOStatement, nextRowset)
2102{
2103    PHP_STMT_GET_OBJ;
2104
2105    if (!stmt->methods->next_rowset) {
2106        pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "driver does not support multiple rowsets" TSRMLS_CC);
2107        RETURN_FALSE;
2108    }
2109
2110    PDO_STMT_CLEAR_ERR();
2111
2112    if (!pdo_stmt_do_next_rowset(stmt TSRMLS_CC)) {
2113        PDO_HANDLE_STMT_ERR();
2114        RETURN_FALSE;
2115    }
2116
2117    RETURN_TRUE;
2118}
2119/* }}} */
2120
2121/* {{{ proto bool PDOStatement::closeCursor()
2122   Closes the cursor, leaving the statement ready for re-execution. */
2123static PHP_METHOD(PDOStatement, closeCursor)
2124{
2125    PHP_STMT_GET_OBJ;
2126
2127    if (!stmt->methods->cursor_closer) {
2128        /* emulate it by fetching and discarding rows */
2129        do {
2130            while (stmt->methods->fetcher(stmt, PDO_FETCH_ORI_NEXT, 0 TSRMLS_CC))
2131                ;
2132            if (!stmt->methods->next_rowset) {
2133                break;
2134            }
2135
2136            if (!pdo_stmt_do_next_rowset(stmt TSRMLS_CC)) {
2137                break;
2138            }
2139
2140        } while (1);
2141        stmt->executed = 0;
2142        RETURN_TRUE;
2143    }
2144
2145    PDO_STMT_CLEAR_ERR();
2146
2147    if (!stmt->methods->cursor_closer(stmt TSRMLS_CC)) {
2148        PDO_HANDLE_STMT_ERR();
2149        RETURN_FALSE;
2150    }
2151    stmt->executed = 0;
2152    RETURN_TRUE;
2153}
2154/* }}} */
2155
2156/* {{{ proto void PDOStatement::debugDumpParams()
2157   A utility for internals hackers to debug parameter internals */
2158static PHP_METHOD(PDOStatement, debugDumpParams)
2159{
2160    php_stream *out = php_stream_open_wrapper("php://output", "w", 0, NULL);
2161    HashPosition pos;
2162    struct pdo_bound_param_data *param;
2163    PHP_STMT_GET_OBJ;
2164
2165    if (out == NULL) {
2166        RETURN_FALSE;
2167    }
2168
2169    php_stream_printf(out TSRMLS_CC, "SQL: [%d] %.*s\n",
2170        stmt->query_stringlen,
2171        stmt->query_stringlen, stmt->query_string);
2172
2173    php_stream_printf(out TSRMLS_CC, "Params:  %d\n",
2174        stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0);
2175
2176    if (stmt->bound_params) {
2177        zend_hash_internal_pointer_reset_ex(stmt->bound_params, &pos);
2178        while (SUCCESS == zend_hash_get_current_data_ex(stmt->bound_params,
2179                (void**)&param, &pos)) {
2180            char *str;
2181            uint len;
2182            ulong num;
2183            int res;
2184
2185            res = zend_hash_get_current_key_ex(stmt->bound_params, &str, &len, &num, 0, &pos);
2186            if (res == HASH_KEY_IS_LONG) {
2187                php_stream_printf(out TSRMLS_CC, "Key: Position #%ld:\n", num);
2188            } else if (res == HASH_KEY_IS_STRING) {
2189                php_stream_printf(out TSRMLS_CC, "Key: Name: [%d] %.*s\n", len, len, str);
2190            }
2191
2192            php_stream_printf(out TSRMLS_CC, "paramno=%ld\nname=[%d] \"%.*s\"\nis_param=%d\nparam_type=%d\n",
2193                param->paramno, param->namelen, param->namelen, param->name ? param->name : "",
2194                param->is_param,
2195                param->param_type);
2196
2197            zend_hash_move_forward_ex(stmt->bound_params, &pos);
2198        }
2199    }
2200
2201    php_stream_close(out);
2202}
2203/* }}} */
2204
2205/* {{{ proto int PDOStatement::__wakeup()
2206   Prevents use of a PDOStatement instance that has been unserialized */
2207static PHP_METHOD(PDOStatement, __wakeup)
2208{
2209    zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "You cannot serialize or unserialize PDOStatement instances");
2210}
2211/* }}} */
2212
2213/* {{{ proto int PDOStatement::__sleep()
2214   Prevents serialization of a PDOStatement instance */
2215static PHP_METHOD(PDOStatement, __sleep)
2216{
2217    zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "You cannot serialize or unserialize PDOStatement instances");
2218}
2219/* }}} */
2220
2221const zend_function_entry pdo_dbstmt_functions[] = {
2222    PHP_ME(PDOStatement, execute,       arginfo_pdostatement_execute,       ZEND_ACC_PUBLIC)
2223    PHP_ME(PDOStatement, fetch,         arginfo_pdostatement_fetch,         ZEND_ACC_PUBLIC)
2224    PHP_ME(PDOStatement, bindParam,     arginfo_pdostatement_bindparam,     ZEND_ACC_PUBLIC)
2225    PHP_ME(PDOStatement, bindColumn,    arginfo_pdostatement_bindcolumn,    ZEND_ACC_PUBLIC)
2226    PHP_ME(PDOStatement, bindValue,     arginfo_pdostatement_bindvalue,     ZEND_ACC_PUBLIC)
2227    PHP_ME(PDOStatement, rowCount,      arginfo_pdostatement__void,         ZEND_ACC_PUBLIC)
2228    PHP_ME(PDOStatement, fetchColumn,   arginfo_pdostatement_fetchcolumn,   ZEND_ACC_PUBLIC)
2229    PHP_ME(PDOStatement, fetchAll,      arginfo_pdostatement_fetchall,      ZEND_ACC_PUBLIC)
2230    PHP_ME(PDOStatement, fetchObject,   arginfo_pdostatement_fetchobject,   ZEND_ACC_PUBLIC)
2231    PHP_ME(PDOStatement, errorCode,     arginfo_pdostatement__void,         ZEND_ACC_PUBLIC)
2232    PHP_ME(PDOStatement, errorInfo,     arginfo_pdostatement__void,         ZEND_ACC_PUBLIC)
2233    PHP_ME(PDOStatement, setAttribute,  arginfo_pdostatement_setattribute,  ZEND_ACC_PUBLIC)
2234    PHP_ME(PDOStatement, getAttribute,  arginfo_pdostatement_getattribute,  ZEND_ACC_PUBLIC)
2235    PHP_ME(PDOStatement, columnCount,   arginfo_pdostatement__void,         ZEND_ACC_PUBLIC)
2236    PHP_ME(PDOStatement, getColumnMeta, arginfo_pdostatement_getcolumnmeta, ZEND_ACC_PUBLIC)
2237    PHP_ME(PDOStatement, setFetchMode,  arginfo_pdostatement_setfetchmode,  ZEND_ACC_PUBLIC)
2238    PHP_ME(PDOStatement, nextRowset,    arginfo_pdostatement__void,         ZEND_ACC_PUBLIC)
2239    PHP_ME(PDOStatement, closeCursor,   arginfo_pdostatement__void,         ZEND_ACC_PUBLIC)
2240    PHP_ME(PDOStatement, debugDumpParams, arginfo_pdostatement__void,       ZEND_ACC_PUBLIC)
2241    PHP_ME(PDOStatement, __wakeup,      arginfo_pdostatement__void,         ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
2242    PHP_ME(PDOStatement, __sleep,       arginfo_pdostatement__void,         ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
2243    {NULL, NULL, NULL}
2244};
2245
2246/* {{{ overloaded handlers for PDOStatement class */
2247static void dbstmt_prop_write(zval *object, zval *member, zval *value TSRMLS_DC)
2248{
2249    pdo_stmt_t * stmt = (pdo_stmt_t *) zend_object_store_get_object(object TSRMLS_CC);
2250
2251    convert_to_string(member);
2252
2253    if(strcmp(Z_STRVAL_P(member), "queryString") == 0) {
2254        pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "property queryString is read only" TSRMLS_CC);
2255    } else {
2256        std_object_handlers.write_property(object, member, value TSRMLS_CC);
2257    }
2258}
2259
2260static void dbstmt_prop_delete(zval *object, zval *member TSRMLS_DC)
2261{
2262    pdo_stmt_t * stmt = (pdo_stmt_t *) zend_object_store_get_object(object TSRMLS_CC);
2263
2264    convert_to_string(member);
2265
2266    if(strcmp(Z_STRVAL_P(member), "queryString") == 0) {
2267        pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "property queryString is read only" TSRMLS_CC);
2268    } else {
2269        std_object_handlers.unset_property(object, member TSRMLS_CC);
2270    }
2271}
2272
2273static union _zend_function *dbstmt_method_get(
2274#if PHP_API_VERSION >= 20041225
2275    zval **object_pp,
2276#else
2277    zval *object,
2278#endif
2279    char *method_name, int method_len TSRMLS_DC)
2280{
2281    zend_function *fbc = NULL;
2282    char *lc_method_name;
2283#if PHP_API_VERSION >= 20041225
2284    zval *object = *object_pp;
2285#endif
2286
2287    lc_method_name = emalloc(method_len + 1);
2288    zend_str_tolower_copy(lc_method_name, method_name, method_len);
2289
2290    if (zend_hash_find(&Z_OBJCE_P(object)->function_table, lc_method_name,
2291            method_len+1, (void**)&fbc) == FAILURE) {
2292        pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object(object TSRMLS_CC);
2293        /* instance not created by PDO object */
2294        if (!stmt->dbh) {
2295            goto out;
2296        }
2297        /* not a pre-defined method, nor a user-defined method; check
2298         * the driver specific methods */
2299        if (!stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT]) {
2300            if (!pdo_hash_methods(stmt->dbh,
2301                PDO_DBH_DRIVER_METHOD_KIND_STMT TSRMLS_CC)
2302                || !stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT]) {
2303                goto out;
2304            }
2305        }
2306
2307        if (zend_hash_find(stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT],
2308                lc_method_name, method_len+1, (void**)&fbc) == FAILURE) {
2309            fbc = NULL;
2310            goto out;
2311        }
2312        /* got it */
2313    }
2314
2315out:
2316    efree(lc_method_name);
2317    return fbc;
2318}
2319
2320static int dbstmt_compare(zval *object1, zval *object2 TSRMLS_DC)
2321{
2322    return -1;
2323}
2324
2325static zend_object_value dbstmt_clone_obj(zval *zobject TSRMLS_DC)
2326{
2327    zend_object_value retval;
2328    zval *tmp;
2329    pdo_stmt_t *stmt;
2330    pdo_stmt_t *old_stmt;
2331    zend_object_handle handle = Z_OBJ_HANDLE_P(zobject);
2332
2333    stmt = ecalloc(1, sizeof(*stmt));
2334    stmt->ce = Z_OBJCE_P(zobject);
2335    stmt->refcount = 1;
2336    ALLOC_HASHTABLE(stmt->properties);
2337    zend_hash_init(stmt->properties, 0, NULL, ZVAL_PTR_DTOR, 0);
2338    zend_hash_copy(stmt->properties, &stmt->ce->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
2339
2340    old_stmt = (pdo_stmt_t *)zend_object_store_get_object(zobject TSRMLS_CC);
2341
2342    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);
2343    retval.handlers = Z_OBJ_HT_P(zobject);
2344
2345    zend_objects_clone_members((zend_object *)stmt, retval, (zend_object *)old_stmt, handle TSRMLS_CC);
2346
2347    zend_objects_store_add_ref(&old_stmt->database_object_handle TSRMLS_CC);
2348    stmt->database_object_handle = old_stmt->database_object_handle;
2349
2350    return retval;
2351}
2352
2353zend_object_handlers pdo_dbstmt_object_handlers;
2354static int pdo_row_serialize(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC);
2355
2356void pdo_stmt_init(TSRMLS_D)
2357{
2358    zend_class_entry ce;
2359
2360    INIT_CLASS_ENTRY(ce, "PDOStatement", pdo_dbstmt_functions);
2361    pdo_dbstmt_ce = zend_register_internal_class(&ce TSRMLS_CC);
2362    pdo_dbstmt_ce->get_iterator = pdo_stmt_iter_get;
2363    pdo_dbstmt_ce->create_object = pdo_dbstmt_new;
2364    zend_class_implements(pdo_dbstmt_ce TSRMLS_CC, 1, zend_ce_traversable);
2365    zend_declare_property_null(pdo_dbstmt_ce, "queryString", sizeof("queryString")-1, ZEND_ACC_PUBLIC TSRMLS_CC);
2366
2367    memcpy(&pdo_dbstmt_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
2368    pdo_dbstmt_object_handlers.write_property = dbstmt_prop_write;
2369    pdo_dbstmt_object_handlers.unset_property = dbstmt_prop_delete;
2370    pdo_dbstmt_object_handlers.get_method = dbstmt_method_get;
2371    pdo_dbstmt_object_handlers.compare_objects = dbstmt_compare;
2372    pdo_dbstmt_object_handlers.clone_obj = dbstmt_clone_obj;
2373
2374    INIT_CLASS_ENTRY(ce, "PDORow", pdo_row_functions);
2375    pdo_row_ce = zend_register_internal_class(&ce TSRMLS_CC);
2376    pdo_row_ce->ce_flags |= ZEND_ACC_FINAL_CLASS; /* when removing this a lot of handlers need to be redone */
2377    pdo_row_ce->create_object = pdo_row_new;
2378    pdo_row_ce->serialize = pdo_row_serialize;
2379}
2380
2381static void free_statement(pdo_stmt_t *stmt TSRMLS_DC)
2382{
2383    if (stmt->properties) {
2384        zend_hash_destroy(stmt->properties);
2385        efree(stmt->properties);
2386        stmt->properties = NULL;
2387    }
2388
2389    if (stmt->bound_params) {
2390        zend_hash_destroy(stmt->bound_params);
2391        FREE_HASHTABLE(stmt->bound_params);
2392        stmt->bound_params = NULL;
2393    }
2394    if (stmt->bound_param_map) {
2395        zend_hash_destroy(stmt->bound_param_map);
2396        FREE_HASHTABLE(stmt->bound_param_map);
2397        stmt->bound_param_map = NULL;
2398    }
2399    if (stmt->bound_columns) {
2400        zend_hash_destroy(stmt->bound_columns);
2401        FREE_HASHTABLE(stmt->bound_columns);
2402        stmt->bound_columns = NULL;
2403    }
2404
2405    if (stmt->methods && stmt->methods->dtor) {
2406        stmt->methods->dtor(stmt TSRMLS_CC);
2407    }
2408    if (stmt->query_string) {
2409        efree(stmt->query_string);
2410    }
2411
2412    if (stmt->columns) {
2413        int i;
2414        struct pdo_column_data *cols = stmt->columns;
2415
2416        for (i = 0; i < stmt->column_count; i++) {
2417            if (cols[i].name) {
2418                efree(cols[i].name);
2419                cols[i].name = NULL;
2420            }
2421        }
2422        efree(stmt->columns);
2423        stmt->columns = NULL;
2424    }
2425
2426    if (stmt->fetch.into && stmt->default_fetch_type == PDO_FETCH_INTO) {
2427        FREE_ZVAL(stmt->fetch.into);
2428        stmt->fetch.into = NULL;
2429    }
2430
2431    do_fetch_opt_finish(stmt, 1 TSRMLS_CC);
2432
2433    zend_objects_store_del_ref(&stmt->database_object_handle TSRMLS_CC);
2434    if (stmt->dbh) {
2435        php_pdo_dbh_delref(stmt->dbh TSRMLS_CC);
2436    }
2437    efree(stmt);
2438}
2439
2440PDO_API void php_pdo_stmt_addref(pdo_stmt_t *stmt TSRMLS_DC)
2441{
2442    stmt->refcount++;
2443}
2444
2445PDO_API void php_pdo_stmt_delref(pdo_stmt_t *stmt TSRMLS_DC)
2446{
2447    if (--stmt->refcount == 0) {
2448        free_statement(stmt TSRMLS_CC);
2449    }
2450}
2451
2452void pdo_dbstmt_free_storage(pdo_stmt_t *stmt TSRMLS_DC)
2453{
2454    php_pdo_stmt_delref(stmt TSRMLS_CC);
2455}
2456
2457zend_object_value pdo_dbstmt_new(zend_class_entry *ce TSRMLS_DC)
2458{
2459    zend_object_value retval;
2460    zval *tmp;
2461
2462    pdo_stmt_t *stmt;
2463    stmt = emalloc(sizeof(*stmt));
2464    memset(stmt, 0, sizeof(*stmt));
2465    stmt->ce = ce;
2466    stmt->refcount = 1;
2467    ALLOC_HASHTABLE(stmt->properties);
2468    zend_hash_init(stmt->properties, 0, NULL, ZVAL_PTR_DTOR, 0);
2469    zend_hash_copy(stmt->properties, &ce->default_properties, (copy_ctor_func_t) zval_property_ctor, (void *) &tmp, sizeof(zval *));
2470
2471    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);
2472    retval.handlers = &pdo_dbstmt_object_handlers;
2473
2474    return retval;
2475}
2476/* }}} */
2477
2478/* {{{ statement iterator */
2479
2480struct php_pdo_iterator {
2481    zend_object_iterator iter;
2482    pdo_stmt_t *stmt;
2483    ulong key;
2484    zval *fetch_ahead;
2485};
2486
2487static void pdo_stmt_iter_dtor(zend_object_iterator *iter TSRMLS_DC)
2488{
2489    struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter->data;
2490
2491    if (--I->stmt->refcount == 0) {
2492        free_statement(I->stmt TSRMLS_CC);
2493    }
2494
2495    if (I->fetch_ahead) {
2496        zval_ptr_dtor(&I->fetch_ahead);
2497    }
2498
2499    efree(I);
2500}
2501
2502static int pdo_stmt_iter_valid(zend_object_iterator *iter TSRMLS_DC)
2503{
2504    struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter->data;
2505
2506    return I->fetch_ahead ? SUCCESS : FAILURE;
2507}
2508
2509static void pdo_stmt_iter_get_data(zend_object_iterator *iter, zval ***data TSRMLS_DC)
2510{
2511    struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter->data;
2512
2513    /* sanity */
2514    if (!I->fetch_ahead) {
2515        *data = NULL;
2516        return;
2517    }
2518
2519    *data = &I->fetch_ahead;
2520}
2521
2522static int pdo_stmt_iter_get_key(zend_object_iterator *iter, char **str_key, uint *str_key_len,
2523    ulong *int_key TSRMLS_DC)
2524{
2525    struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter->data;
2526
2527    if (I->key == (ulong)-1) {
2528        return HASH_KEY_NON_EXISTANT;
2529    }
2530    *int_key = I->key;
2531    return HASH_KEY_IS_LONG;
2532}
2533
2534static void pdo_stmt_iter_move_forwards(zend_object_iterator *iter TSRMLS_DC)
2535{
2536    struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter->data;
2537
2538    if (I->fetch_ahead) {
2539        zval_ptr_dtor(&I->fetch_ahead);
2540        I->fetch_ahead = NULL;
2541    }
2542
2543    MAKE_STD_ZVAL(I->fetch_ahead);
2544
2545    if (!do_fetch(I->stmt, TRUE, I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2546            PDO_FETCH_ORI_NEXT, 0, 0 TSRMLS_CC)) {
2547        pdo_stmt_t *stmt = I->stmt; /* for PDO_HANDLE_STMT_ERR() */
2548
2549        PDO_HANDLE_STMT_ERR();
2550        I->key = (ulong)-1;
2551        FREE_ZVAL(I->fetch_ahead);
2552        I->fetch_ahead = NULL;
2553
2554        return;
2555    }
2556
2557    I->key++;
2558}
2559
2560static zend_object_iterator_funcs pdo_stmt_iter_funcs = {
2561    pdo_stmt_iter_dtor,
2562    pdo_stmt_iter_valid,
2563    pdo_stmt_iter_get_data,
2564    pdo_stmt_iter_get_key,
2565    pdo_stmt_iter_move_forwards,
2566    NULL
2567};
2568
2569zend_object_iterator *pdo_stmt_iter_get(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC)
2570{
2571    pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object(object TSRMLS_CC);
2572    struct php_pdo_iterator *I;
2573
2574    if (by_ref) {
2575        zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
2576    }
2577
2578    I = ecalloc(1, sizeof(*I));
2579    I->iter.funcs = &pdo_stmt_iter_funcs;
2580    I->iter.data = I;
2581    I->stmt = stmt;
2582    stmt->refcount++;
2583
2584    MAKE_STD_ZVAL(I->fetch_ahead);
2585    if (!do_fetch(I->stmt, TRUE, I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2586            PDO_FETCH_ORI_NEXT, 0, 0 TSRMLS_CC)) {
2587        PDO_HANDLE_STMT_ERR();
2588        I->key = (ulong)-1;
2589        FREE_ZVAL(I->fetch_ahead);
2590        I->fetch_ahead = NULL;
2591    }
2592
2593    return &I->iter;
2594}
2595
2596/* }}} */
2597
2598/* {{{ overloaded handlers for PDORow class (used by PDO_FETCH_LAZY) */
2599
2600const zend_function_entry pdo_row_functions[] = {
2601    {NULL, NULL, NULL}
2602};
2603
2604static zval *row_prop_or_dim_read(zval *object, zval *member, int type TSRMLS_DC)
2605{
2606    zval *return_value;
2607    pdo_stmt_t * stmt = (pdo_stmt_t *) zend_object_store_get_object(object TSRMLS_CC);
2608    int colno = -1;
2609
2610    MAKE_STD_ZVAL(return_value);
2611    RETVAL_NULL();
2612
2613    if (stmt) {
2614        if (Z_TYPE_P(member) == IS_LONG) {
2615            if (Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count) {
2616                fetch_value(stmt, return_value, Z_LVAL_P(member), NULL TSRMLS_CC);
2617            }
2618        } else {
2619            convert_to_string(member);
2620            /* TODO: replace this with a hash of available column names to column
2621             * numbers */
2622            for (colno = 0; colno < stmt->column_count; colno++) {
2623                if (strcmp(stmt->columns[colno].name, Z_STRVAL_P(member)) == 0) {
2624                    fetch_value(stmt, return_value, colno, NULL TSRMLS_CC);
2625                    Z_SET_REFCOUNT_P(return_value, 0);
2626                    Z_UNSET_ISREF_P(return_value);
2627                    return return_value;
2628                }
2629            }
2630            if (strcmp(Z_STRVAL_P(member), "queryString") == 0) {
2631                zval_ptr_dtor(&return_value);
2632                return std_object_handlers.read_property(object, member, type TSRMLS_CC);
2633            }
2634        }
2635    }
2636
2637    Z_SET_REFCOUNT_P(return_value, 0);
2638    Z_UNSET_ISREF_P(return_value);
2639
2640    return return_value;
2641}
2642
2643static void row_prop_or_dim_write(zval *object, zval *member, zval *value TSRMLS_DC)
2644{
2645    php_error_docref(NULL TSRMLS_CC, E_WARNING, "This PDORow is not from a writable result set");
2646}
2647
2648static int row_prop_or_dim_exists(zval *object, zval *member, int check_empty TSRMLS_DC)
2649{
2650    pdo_stmt_t * stmt = (pdo_stmt_t *) zend_object_store_get_object(object TSRMLS_CC);
2651    int colno = -1;
2652
2653    if (stmt) {
2654        if (Z_TYPE_P(member) == IS_LONG) {
2655            return Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count;
2656        } else {
2657            convert_to_string(member);
2658
2659            /* TODO: replace this with a hash of available column names to column
2660             * numbers */
2661            for (colno = 0; colno < stmt->column_count; colno++) {
2662                if (strcmp(stmt->columns[colno].name, Z_STRVAL_P(member)) == 0) {
2663                    return 1;
2664                }
2665            }
2666        }
2667    }
2668
2669    return 0;
2670}
2671
2672static void row_prop_or_dim_delete(zval *object, zval *offset TSRMLS_DC)
2673{
2674    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot delete properties from a PDORow");
2675}
2676
2677static HashTable *row_get_properties(zval *object TSRMLS_DC)
2678{
2679    pdo_stmt_t * stmt = (pdo_stmt_t *) zend_object_store_get_object(object TSRMLS_CC);
2680    int i;
2681
2682    if (stmt == NULL) {
2683        return NULL;
2684    }
2685
2686    for (i = 0; i < stmt->column_count; i++) {
2687        zval *val;
2688        MAKE_STD_ZVAL(val);
2689        fetch_value(stmt, val, i, NULL TSRMLS_CC);
2690
2691        zend_hash_update(stmt->properties, stmt->columns[i].name, stmt->columns[i].namelen + 1, (void *)&val, sizeof(zval *), NULL);
2692    }
2693
2694    return stmt->properties;
2695}
2696
2697static union _zend_function *row_method_get(
2698#if PHP_API_VERSION >= 20041225
2699    zval **object_pp,
2700#else
2701    zval *object,
2702#endif
2703    char *method_name, int method_len TSRMLS_DC)
2704{
2705    zend_function *fbc;
2706    char *lc_method_name;
2707
2708    lc_method_name = emalloc(method_len + 1);
2709    zend_str_tolower_copy(lc_method_name, method_name, method_len);
2710
2711    if (zend_hash_find(&pdo_row_ce->function_table, lc_method_name, method_len+1, (void**)&fbc) == FAILURE) {
2712        efree(lc_method_name);
2713        return NULL;
2714    }
2715
2716    efree(lc_method_name);
2717    return fbc;
2718}
2719
2720static int row_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS)
2721{
2722    return FAILURE;
2723}
2724
2725static union _zend_function *row_get_ctor(zval *object TSRMLS_DC)
2726{
2727    static zend_internal_function ctor = {0};
2728
2729    ctor.type = ZEND_INTERNAL_FUNCTION;
2730    ctor.function_name = "__construct";
2731    ctor.scope = pdo_row_ce;
2732    ctor.handler = ZEND_FN(dbstmt_constructor);
2733
2734    return (union _zend_function*)&ctor;
2735}
2736
2737static zend_class_entry *row_get_ce(const zval *object TSRMLS_DC)
2738{
2739    return pdo_row_ce;
2740}
2741
2742static int row_get_classname(const zval *object, char **class_name, zend_uint *class_name_len, int parent TSRMLS_DC)
2743{
2744    if (parent) {
2745        return FAILURE;
2746    } else {
2747        *class_name = estrndup("PDORow", sizeof("PDORow")-1);
2748        *class_name_len = sizeof("PDORow")-1;
2749        return SUCCESS;
2750    }
2751}
2752
2753static int row_compare(zval *object1, zval *object2 TSRMLS_DC)
2754{
2755    return -1;
2756}
2757
2758zend_object_handlers pdo_row_object_handlers = {
2759    ZEND_OBJECTS_STORE_HANDLERS,
2760    row_prop_or_dim_read,
2761    row_prop_or_dim_write,
2762    row_prop_or_dim_read,
2763    row_prop_or_dim_write,
2764    NULL,
2765    NULL,
2766    NULL,
2767    row_prop_or_dim_exists,
2768    row_prop_or_dim_delete,
2769    row_prop_or_dim_exists,
2770    row_prop_or_dim_delete,
2771    row_get_properties,
2772    row_method_get,
2773    row_call_method,
2774    row_get_ctor,
2775    row_get_ce,
2776    row_get_classname,
2777    row_compare,
2778    NULL, /* cast */
2779    NULL
2780};
2781
2782void pdo_row_free_storage(pdo_stmt_t *stmt TSRMLS_DC)
2783{
2784    if (stmt) {
2785        ZVAL_NULL(&stmt->lazy_object_ref);
2786
2787        if (--stmt->refcount == 0) {
2788            free_statement(stmt TSRMLS_CC);
2789        }
2790    }
2791}
2792
2793zend_object_value pdo_row_new(zend_class_entry *ce TSRMLS_DC)
2794{
2795    zend_object_value retval;
2796
2797    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);
2798    retval.handlers = &pdo_row_object_handlers;
2799
2800    return retval;
2801}
2802
2803static int pdo_row_serialize(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC)
2804{
2805    php_error_docref(NULL TSRMLS_CC, E_WARNING, "PDORow instances may not be serialized");
2806    return FAILURE;
2807}
2808/* }}} */
2809
2810/*
2811 * Local variables:
2812 * tab-width: 4
2813 * c-basic-offset: 4
2814 * End:
2815 * vim600: noet sw=4 ts=4 fdm=marker
2816 * vim<600: noet sw=4 ts=4
2817 */
2818