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