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