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