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