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