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 (!ce->unserialize) {
1145                            zval_ptr_dtor(&val);
1146                            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "cannot unserialize class" TSRMLS_CC);
1147                            return 0;
1148                        } 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) {
1149                            zval_ptr_dtor(&val);
1150                            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "cannot unserialize class" TSRMLS_CC);
1151                            zval_dtor(return_value);
1152                            ZVAL_NULL(return_value);
1153                            return 0;
1154                        } else {
1155                            zval_ptr_dtor(&val);
1156                        }
1157                    }
1158                    break;
1159
1160                case PDO_FETCH_FUNC:
1161                    stmt->fetch.func.values[idx] = val;
1162                    stmt->fetch.cls.fci.params[idx] = &stmt->fetch.func.values[idx];
1163                    break;
1164
1165                default:
1166                    zval_ptr_dtor(&val);
1167                    pdo_raise_impl_error(stmt->dbh, stmt, "22003", "mode is out of range" TSRMLS_CC);
1168                    return 0;
1169                    break;
1170            }
1171        }
1172
1173        switch (how) {
1174            case PDO_FETCH_CLASS:
1175                if (ce->constructor && !(flags & (PDO_FETCH_PROPS_LATE | PDO_FETCH_SERIALIZE))) {
1176                    stmt->fetch.cls.fci.object_ptr = return_value;
1177                    stmt->fetch.cls.fcc.object_ptr = return_value;
1178                    if (zend_call_function(&stmt->fetch.cls.fci, &stmt->fetch.cls.fcc TSRMLS_CC) == FAILURE) {
1179                        pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call class constructor" TSRMLS_CC);
1180                        return 0;
1181                    } else {
1182                        if (stmt->fetch.cls.retval_ptr) {
1183                            zval_ptr_dtor(&stmt->fetch.cls.retval_ptr);
1184                        }
1185                    }
1186                }
1187                if (flags & PDO_FETCH_CLASSTYPE) {
1188                    do_fetch_opt_finish(stmt, 0 TSRMLS_CC);
1189                    stmt->fetch.cls.ce = old_ce;
1190                    stmt->fetch.cls.ctor_args = old_ctor_args;
1191                    stmt->fetch.cls.fci.param_count = old_arg_count;
1192                }
1193                break;
1194
1195            case PDO_FETCH_FUNC:
1196                stmt->fetch.func.fci.param_count = idx;
1197                stmt->fetch.func.fci.retval_ptr_ptr = &retval;
1198                if (zend_call_function(&stmt->fetch.func.fci, &stmt->fetch.func.fcc TSRMLS_CC) == FAILURE) {
1199                    pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call user-supplied function" TSRMLS_CC);
1200                    return 0;
1201                } else {
1202                    if (return_all) {
1203                        zval_ptr_dtor(&return_value); /* we don't need that */
1204                        return_value = retval;
1205                    } else if (retval) {
1206                        MAKE_COPY_ZVAL(&retval, return_value);
1207                        zval_ptr_dtor(&retval);
1208                    }
1209                }
1210                while(idx--) {
1211                    zval_ptr_dtor(&stmt->fetch.func.values[idx]);
1212                }
1213                break;
1214
1215            default:
1216                break;
1217        }
1218
1219        if (return_all) {
1220            if ((flags & PDO_FETCH_UNIQUE) == PDO_FETCH_UNIQUE) {
1221                add_assoc_zval(return_all, Z_STRVAL(grp_val), return_value);
1222            } else {
1223                if (zend_symtable_find(Z_ARRVAL_P(return_all), Z_STRVAL(grp_val), Z_STRLEN(grp_val)+1, (void**)&pgrp) == FAILURE) {
1224                    MAKE_STD_ZVAL(grp);
1225                    array_init(grp);
1226                    add_assoc_zval(return_all, Z_STRVAL(grp_val), grp);
1227                } else {
1228                    grp = *pgrp;
1229                }
1230                add_next_index_zval(grp, return_value);
1231            }
1232            zval_dtor(&grp_val);
1233        }
1234
1235    }
1236
1237    return 1;
1238}
1239/* }}} */
1240
1241static int pdo_stmt_verify_mode(pdo_stmt_t *stmt, long mode, int fetch_all TSRMLS_DC) /* {{{ */
1242{
1243    int flags = mode & PDO_FETCH_FLAGS;
1244
1245    mode = mode & ~PDO_FETCH_FLAGS;
1246
1247    if (mode < 0 || mode > PDO_FETCH__MAX) {
1248        pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "invalid fetch mode" TSRMLS_CC);
1249        return 0;
1250    }
1251
1252    if (mode == PDO_FETCH_USE_DEFAULT) {
1253        flags = stmt->default_fetch_type & PDO_FETCH_FLAGS;
1254        mode = stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
1255    }
1256
1257    switch(mode) {
1258    case PDO_FETCH_FUNC:
1259        if (!fetch_all) {
1260            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_FUNC is only allowed in PDOStatement::fetchAll()" TSRMLS_CC);
1261            return 0;
1262        }
1263        return 1;
1264
1265    case PDO_FETCH_LAZY:
1266        if (fetch_all) {
1267            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_LAZY can't be used with PDOStatement::fetchAll()" TSRMLS_CC);
1268            return 0;
1269        }
1270        /* fall through */
1271
1272    default:
1273        if ((flags & PDO_FETCH_SERIALIZE) == PDO_FETCH_SERIALIZE) {
1274            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_SERIALIZE can only be used together with PDO::FETCH_CLASS" TSRMLS_CC);
1275            return 0;
1276        }
1277        if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
1278            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_CLASSTYPE can only be used together with PDO::FETCH_CLASS" TSRMLS_CC);
1279            return 0;
1280        }
1281        if (mode >= PDO_FETCH__MAX) {
1282            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "invalid fetch mode" TSRMLS_CC);
1283            return 0;
1284        }
1285        /* no break; */
1286
1287    case PDO_FETCH_CLASS:
1288        return 1;
1289    }
1290}
1291/* }}} */
1292
1293/* {{{ proto mixed PDOStatement::fetch([int $how = PDO_FETCH_BOTH [, int $orientation [, int $offset]]])
1294   Fetches the next row and returns it, or false if there are no more rows */
1295static PHP_METHOD(PDOStatement, fetch)
1296{
1297    long how = PDO_FETCH_USE_DEFAULT;
1298    long ori = PDO_FETCH_ORI_NEXT;
1299    long off = 0;
1300        PHP_STMT_GET_OBJ;
1301
1302    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lll", &how,
1303            &ori, &off)) {
1304        RETURN_FALSE;
1305    }
1306
1307    PDO_STMT_CLEAR_ERR();
1308
1309    if (!pdo_stmt_verify_mode(stmt, how, 0 TSRMLS_CC)) {
1310        RETURN_FALSE;
1311    }
1312
1313    if (!do_fetch(stmt, TRUE, return_value, how, ori, off, 0 TSRMLS_CC)) {
1314        PDO_HANDLE_STMT_ERR();
1315        RETURN_FALSE;
1316    }
1317}
1318/* }}} */
1319
1320/* {{{ proto mixed PDOStatement::fetchObject([string class_name [, NULL|array ctor_args]])
1321   Fetches the next row and returns it as an object. */
1322static PHP_METHOD(PDOStatement, fetchObject)
1323{
1324    long how = PDO_FETCH_CLASS;
1325    long ori = PDO_FETCH_ORI_NEXT;
1326    long off = 0;
1327    char *class_name = NULL;
1328    int class_name_len;
1329    zend_class_entry *old_ce;
1330    zval *old_ctor_args, *ctor_args = NULL;
1331    int error = 0, old_arg_count;
1332
1333    PHP_STMT_GET_OBJ;
1334
1335    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!a", &class_name, &class_name_len, &ctor_args)) {
1336        RETURN_FALSE;
1337    }
1338
1339    PDO_STMT_CLEAR_ERR();
1340
1341    if (!pdo_stmt_verify_mode(stmt, how, 0 TSRMLS_CC)) {
1342        RETURN_FALSE;
1343    }
1344
1345    old_ce = stmt->fetch.cls.ce;
1346    old_ctor_args = stmt->fetch.cls.ctor_args;
1347    old_arg_count = stmt->fetch.cls.fci.param_count;
1348
1349    do_fetch_opt_finish(stmt, 0 TSRMLS_CC);
1350
1351    if (ctor_args) {
1352        if (Z_TYPE_P(ctor_args) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(ctor_args))) {
1353            ALLOC_ZVAL(stmt->fetch.cls.ctor_args);
1354            *stmt->fetch.cls.ctor_args = *ctor_args;
1355            zval_copy_ctor(stmt->fetch.cls.ctor_args);
1356        } else {
1357            stmt->fetch.cls.ctor_args = NULL;
1358        }
1359    }
1360    if (class_name && !error) {
1361        stmt->fetch.cls.ce = zend_fetch_class(class_name, class_name_len, ZEND_FETCH_CLASS_AUTO TSRMLS_CC);
1362
1363        if (!stmt->fetch.cls.ce) {
1364            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Could not find user-supplied class" TSRMLS_CC);
1365            error = 1;
1366        }
1367    } else if (!error) {
1368        stmt->fetch.cls.ce = zend_standard_class_def;
1369    }
1370
1371    if (!error && !do_fetch(stmt, TRUE, return_value, how, ori, off, 0 TSRMLS_CC)) {
1372        error = 1;
1373    }
1374    if (error) {
1375        PDO_HANDLE_STMT_ERR();
1376    }
1377    do_fetch_opt_finish(stmt, 1 TSRMLS_CC);
1378
1379    stmt->fetch.cls.ce = old_ce;
1380    stmt->fetch.cls.ctor_args = old_ctor_args;
1381    stmt->fetch.cls.fci.param_count = old_arg_count;
1382    if (error) {
1383        RETURN_FALSE;
1384    }
1385}
1386/* }}} */
1387
1388/* {{{ proto string PDOStatement::fetchColumn([int column_number])
1389   Returns a data of the specified column in the result set. */
1390static PHP_METHOD(PDOStatement, fetchColumn)
1391{
1392    long col_n = 0;
1393    PHP_STMT_GET_OBJ;
1394
1395    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &col_n)) {
1396        RETURN_FALSE;
1397    }
1398
1399    PDO_STMT_CLEAR_ERR();
1400
1401    if (!do_fetch_common(stmt, PDO_FETCH_ORI_NEXT, 0, TRUE TSRMLS_CC)) {
1402        PDO_HANDLE_STMT_ERR();
1403        RETURN_FALSE;
1404    }
1405
1406    fetch_value(stmt, return_value, col_n, NULL TSRMLS_CC);
1407}
1408/* }}} */
1409
1410/* {{{ proto array PDOStatement::fetchAll([int $how = PDO_FETCH_BOTH [, string class_name [, NULL|array ctor_args]]])
1411   Returns an array of all of the results. */
1412static PHP_METHOD(PDOStatement, fetchAll)
1413{
1414    long how = PDO_FETCH_USE_DEFAULT;
1415    zval *data, *return_all;
1416    zval *arg2;
1417    zend_class_entry *old_ce;
1418    zval *old_ctor_args, *ctor_args = NULL;
1419    int error = 0, flags, old_arg_count;
1420    PHP_STMT_GET_OBJ;
1421
1422    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lzz", &how, &arg2, &ctor_args)) {
1423        RETURN_FALSE;
1424    }
1425
1426    if (!pdo_stmt_verify_mode(stmt, how, 1 TSRMLS_CC)) {
1427        RETURN_FALSE;
1428    }
1429
1430    old_ce = stmt->fetch.cls.ce;
1431    old_ctor_args = stmt->fetch.cls.ctor_args;
1432    old_arg_count = stmt->fetch.cls.fci.param_count;
1433
1434    do_fetch_opt_finish(stmt, 0 TSRMLS_CC);
1435
1436    switch(how & ~PDO_FETCH_FLAGS) {
1437    case PDO_FETCH_CLASS:
1438        switch(ZEND_NUM_ARGS()) {
1439        case 0:
1440        case 1:
1441            stmt->fetch.cls.ce = zend_standard_class_def;
1442            break;
1443        case 3:
1444            if (Z_TYPE_P(ctor_args) != IS_NULL && Z_TYPE_P(ctor_args) != IS_ARRAY) {
1445                pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "ctor_args must be either NULL or an array" TSRMLS_CC);
1446                error = 1;
1447                break;
1448            }
1449            if (Z_TYPE_P(ctor_args) != IS_ARRAY || !zend_hash_num_elements(Z_ARRVAL_P(ctor_args))) {
1450                ctor_args = NULL;
1451            }
1452            /* no break */
1453        case 2:
1454            stmt->fetch.cls.ctor_args = ctor_args; /* we're not going to free these */
1455            if (Z_TYPE_P(arg2) != IS_STRING) {
1456                pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Invalid class name (should be a string)" TSRMLS_CC);
1457                error = 1;
1458                break;
1459            } else {
1460                stmt->fetch.cls.ce = zend_fetch_class(Z_STRVAL_P(arg2), Z_STRLEN_P(arg2), ZEND_FETCH_CLASS_AUTO TSRMLS_CC);
1461                if (!stmt->fetch.cls.ce) {
1462                    pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not find user-specified class" TSRMLS_CC);
1463                    error = 1;
1464                    break;
1465                }
1466            }
1467        }
1468        if (!error) {
1469            do_fetch_class_prepare(stmt TSRMLS_CC);
1470        }
1471        break;
1472
1473    case PDO_FETCH_FUNC:
1474        switch(ZEND_NUM_ARGS()) {
1475        case 0:
1476        case 1:
1477            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "no fetch function specified" TSRMLS_CC);
1478            error = 1;
1479            break;
1480        case 3:
1481        case 2:
1482            stmt->fetch.func.function = arg2;
1483            if (do_fetch_func_prepare(stmt TSRMLS_CC) == 0) {
1484                error = 1;
1485            }
1486            break;
1487        }
1488        break;
1489
1490    case PDO_FETCH_COLUMN:
1491        switch(ZEND_NUM_ARGS()) {
1492        case 0:
1493        case 1:
1494            stmt->fetch.column = how & PDO_FETCH_GROUP ? -1 : 0;
1495            break;
1496        case 2:
1497            convert_to_long(arg2);
1498            stmt->fetch.column = Z_LVAL_P(arg2);
1499            break;
1500        case 3:
1501            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Third parameter not allowed for PDO::FETCH_COLUMN" TSRMLS_CC);
1502            error = 1;
1503        }
1504        break;
1505
1506    default:
1507        if (ZEND_NUM_ARGS() > 1) {
1508            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Extraneous additional parameters" TSRMLS_CC);
1509            error = 1;
1510        }
1511    }
1512
1513    flags = how & PDO_FETCH_FLAGS;
1514
1515    if ((how & ~PDO_FETCH_FLAGS) == PDO_FETCH_USE_DEFAULT) {
1516        flags |= stmt->default_fetch_type & PDO_FETCH_FLAGS;
1517        how |= stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
1518    }
1519
1520    if (!error) {
1521        PDO_STMT_CLEAR_ERR();
1522        MAKE_STD_ZVAL(data);
1523        if (    (how & PDO_FETCH_GROUP) || how == PDO_FETCH_KEY_PAIR ||
1524            (how == PDO_FETCH_USE_DEFAULT && stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)
1525        ) {
1526            array_init(return_value);
1527            return_all = return_value;
1528        } else {
1529            return_all = 0;
1530        }
1531        if (!do_fetch(stmt, TRUE, data, how | flags, PDO_FETCH_ORI_NEXT, 0, return_all TSRMLS_CC)) {
1532            FREE_ZVAL(data);
1533            error = 2;
1534        }
1535    }
1536    if (!error) {
1537        if ((how & PDO_FETCH_GROUP)) {
1538            do {
1539                MAKE_STD_ZVAL(data);
1540            } while (do_fetch(stmt, TRUE, data, how | flags, PDO_FETCH_ORI_NEXT, 0, return_all TSRMLS_CC));
1541        } else if (how == PDO_FETCH_KEY_PAIR || (how == PDO_FETCH_USE_DEFAULT && stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)) {
1542            while (do_fetch(stmt, TRUE, data, how | flags, PDO_FETCH_ORI_NEXT, 0, return_all TSRMLS_CC));
1543        } else {
1544            array_init(return_value);
1545            do {
1546                add_next_index_zval(return_value, data);
1547                MAKE_STD_ZVAL(data);
1548            } while (do_fetch(stmt, TRUE, data, how | flags, PDO_FETCH_ORI_NEXT, 0, 0 TSRMLS_CC));
1549        }
1550        FREE_ZVAL(data);
1551    }
1552
1553    do_fetch_opt_finish(stmt, 0 TSRMLS_CC);
1554
1555    stmt->fetch.cls.ce = old_ce;
1556    stmt->fetch.cls.ctor_args = old_ctor_args;
1557    stmt->fetch.cls.fci.param_count = old_arg_count;
1558
1559    if (error) {
1560        PDO_HANDLE_STMT_ERR();
1561        if (error != 2) {
1562            RETURN_FALSE;
1563        } else { /* on no results, return an empty array */
1564            if (Z_TYPE_P(return_value) != IS_ARRAY) {
1565                array_init(return_value);
1566            }
1567            return;
1568        }
1569    }
1570}
1571/* }}} */
1572
1573static int register_bound_param(INTERNAL_FUNCTION_PARAMETERS, pdo_stmt_t *stmt, int is_param) /* {{{ */
1574{
1575    struct pdo_bound_param_data param = {0};
1576    long param_type = PDO_PARAM_STR;
1577
1578    param.paramno = -1;
1579
1580    if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC,
1581            "lz|llz!", &param.paramno, &param.parameter, &param_type, &param.max_value_len,
1582            &param.driver_params)) {
1583        if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|llz!", &param.name,
1584                &param.namelen, &param.parameter, &param_type, &param.max_value_len,
1585                &param.driver_params)) {
1586            return 0;
1587        }
1588    }
1589
1590    param.param_type = (int) param_type;
1591
1592    if (param.paramno > 0) {
1593        --param.paramno; /* make it zero-based internally */
1594    } else if (!param.name) {
1595        pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "Columns/Parameters are 1-based" TSRMLS_CC);
1596        return 0;
1597    }
1598
1599    Z_ADDREF_P(param.parameter);
1600    if (!really_register_bound_param(&param, stmt, is_param TSRMLS_CC)) {
1601        if (param.parameter) {
1602            zval_ptr_dtor(&(param.parameter));
1603            param.parameter = NULL;
1604        }
1605        return 0;
1606    }
1607    return 1;
1608} /* }}} */
1609
1610/* {{{ proto bool PDOStatement::bindValue(mixed $paramno, mixed $param [, int $type ])
1611   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(). */
1612static PHP_METHOD(PDOStatement, bindValue)
1613{
1614    struct pdo_bound_param_data param = {0};
1615    long param_type = PDO_PARAM_STR;
1616    PHP_STMT_GET_OBJ;
1617
1618    param.paramno = -1;
1619
1620    if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC,
1621            "lz/|l", &param.paramno, &param.parameter, &param_type)) {
1622        if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz/|l", &param.name,
1623                &param.namelen, &param.parameter, &param_type)) {
1624            RETURN_FALSE;
1625        }
1626    }
1627
1628    param.param_type = (int) param_type;
1629
1630    if (param.paramno > 0) {
1631        --param.paramno; /* make it zero-based internally */
1632    } else if (!param.name) {
1633        pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "Columns/Parameters are 1-based" TSRMLS_CC);
1634        RETURN_FALSE;
1635    }
1636
1637    Z_ADDREF_P(param.parameter);
1638    if (!really_register_bound_param(&param, stmt, TRUE TSRMLS_CC)) {
1639        if (param.parameter) {
1640            zval_ptr_dtor(&(param.parameter));
1641            param.parameter = NULL;
1642        }
1643        RETURN_FALSE;
1644    }
1645    RETURN_TRUE;
1646}
1647/* }}} */
1648
1649
1650/* {{{ proto bool PDOStatement::bindParam(mixed $paramno, mixed &$param [, int $type [, int $maxlen [, mixed $driverdata]]])
1651   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(). */
1652static PHP_METHOD(PDOStatement, bindParam)
1653{
1654    PHP_STMT_GET_OBJ;
1655    RETURN_BOOL(register_bound_param(INTERNAL_FUNCTION_PARAM_PASSTHRU, stmt, TRUE));
1656}
1657/* }}} */
1658
1659/* {{{ proto bool PDOStatement::bindColumn(mixed $column, mixed &$param [, int $type [, int $maxlen [, mixed $driverdata]]])
1660   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(). */
1661static PHP_METHOD(PDOStatement, bindColumn)
1662{
1663    PHP_STMT_GET_OBJ;
1664    RETURN_BOOL(register_bound_param(INTERNAL_FUNCTION_PARAM_PASSTHRU, stmt, FALSE));
1665}
1666/* }}} */
1667
1668/* {{{ proto int PDOStatement::rowCount()
1669   Returns the number of rows in a result set, or the number of rows affected by the last execute().  It is not always meaningful. */
1670static PHP_METHOD(PDOStatement, rowCount)
1671{
1672    PHP_STMT_GET_OBJ;
1673
1674    RETURN_LONG(stmt->row_count);
1675}
1676/* }}} */
1677
1678/* {{{ proto string PDOStatement::errorCode()
1679   Fetch the error code associated with the last operation on the statement handle */
1680static PHP_METHOD(PDOStatement, errorCode)
1681{
1682    PHP_STMT_GET_OBJ;
1683
1684    if (zend_parse_parameters_none() == FAILURE) {
1685        return;
1686    }
1687
1688    if (stmt->error_code[0] == '\0') {
1689        RETURN_NULL();
1690    }
1691
1692    RETURN_STRING(stmt->error_code, 1);
1693}
1694/* }}} */
1695
1696/* {{{ proto array PDOStatement::errorInfo()
1697   Fetch extended error information associated with the last operation on the statement handle */
1698static PHP_METHOD(PDOStatement, errorInfo)
1699{
1700    int error_count;
1701    int error_count_diff     = 0;
1702    int error_expected_count = 3;
1703
1704    PHP_STMT_GET_OBJ;
1705
1706    if (zend_parse_parameters_none() == FAILURE) {
1707        return;
1708    }
1709
1710    array_init(return_value);
1711    add_next_index_string(return_value, stmt->error_code, 1);
1712
1713    if (stmt->dbh->methods->fetch_err) {
1714        stmt->dbh->methods->fetch_err(stmt->dbh, stmt, return_value TSRMLS_CC);
1715    }
1716
1717    error_count = zend_hash_num_elements(Z_ARRVAL_P(return_value));
1718
1719    if (error_expected_count > error_count) {
1720        int current_index;
1721
1722        error_count_diff = error_expected_count - error_count;
1723        for (current_index = 0; current_index < error_count_diff; current_index++) {
1724            add_next_index_null(return_value);
1725        }
1726    }
1727}
1728/* }}} */
1729
1730/* {{{ proto bool PDOStatement::setAttribute(long attribute, mixed value)
1731   Set an attribute */
1732static PHP_METHOD(PDOStatement, setAttribute)
1733{
1734    long attr;
1735    zval *value = NULL;
1736    PHP_STMT_GET_OBJ;
1737
1738    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lz!", &attr, &value)) {
1739        RETURN_FALSE;
1740    }
1741
1742    if (!stmt->methods->set_attribute) {
1743        goto fail;
1744    }
1745
1746    PDO_STMT_CLEAR_ERR();
1747    if (stmt->methods->set_attribute(stmt, attr, value TSRMLS_CC)) {
1748        RETURN_TRUE;
1749    }
1750
1751fail:
1752    if (!stmt->methods->set_attribute) {
1753        pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "This driver doesn't support setting attributes" TSRMLS_CC);
1754    } else {
1755        PDO_HANDLE_STMT_ERR();
1756    }
1757    RETURN_FALSE;
1758}
1759/* }}} */
1760
1761/* {{{ proto mixed PDOStatement::getAttribute(long attribute)
1762   Get an attribute */
1763
1764static int generic_stmt_attr_get(pdo_stmt_t *stmt, zval *return_value, long attr)
1765{
1766    switch (attr) {
1767        case PDO_ATTR_EMULATE_PREPARES:
1768            RETVAL_BOOL(stmt->supports_placeholders == PDO_PLACEHOLDER_NONE);
1769            return 1;
1770    }
1771    return 0;
1772}
1773
1774static PHP_METHOD(PDOStatement, getAttribute)
1775{
1776    long attr;
1777    PHP_STMT_GET_OBJ;
1778
1779    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &attr)) {
1780        RETURN_FALSE;
1781    }
1782
1783    if (!stmt->methods->get_attribute) {
1784        if (!generic_stmt_attr_get(stmt, return_value, attr)) {
1785            pdo_raise_impl_error(stmt->dbh, stmt, "IM001",
1786                "This driver doesn't support getting attributes" TSRMLS_CC);
1787            RETURN_FALSE;
1788        }
1789        return;
1790    }
1791
1792    PDO_STMT_CLEAR_ERR();
1793    switch (stmt->methods->get_attribute(stmt, attr, return_value TSRMLS_CC)) {
1794        case -1:
1795            PDO_HANDLE_STMT_ERR();
1796            RETURN_FALSE;
1797
1798        case 0:
1799            if (!generic_stmt_attr_get(stmt, return_value, attr)) {
1800                /* XXX: should do something better here */
1801                pdo_raise_impl_error(stmt->dbh, stmt, "IM001",
1802                    "driver doesn't support getting that attribute" TSRMLS_CC);
1803                RETURN_FALSE;
1804            }
1805            return;
1806
1807        default:
1808            return;
1809    }
1810}
1811/* }}} */
1812
1813/* {{{ proto int PDOStatement::columnCount()
1814   Returns the number of columns in the result set */
1815static PHP_METHOD(PDOStatement, columnCount)
1816{
1817    PHP_STMT_GET_OBJ;
1818    if (zend_parse_parameters_none() == FAILURE) {
1819        return;
1820    }
1821    RETURN_LONG(stmt->column_count);
1822}
1823/* }}} */
1824
1825/* {{{ proto array PDOStatement::getColumnMeta(int $column)
1826   Returns meta data for a numbered column */
1827static PHP_METHOD(PDOStatement, getColumnMeta)
1828{
1829    long colno;
1830    struct pdo_column_data *col;
1831    PHP_STMT_GET_OBJ;
1832
1833    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &colno)) {
1834        RETURN_FALSE;
1835    }
1836    if(colno < 0) {
1837        pdo_raise_impl_error(stmt->dbh, stmt, "42P10", "column number must be non-negative" TSRMLS_CC);
1838        RETURN_FALSE;
1839    }
1840
1841    if (!stmt->methods->get_column_meta) {
1842        pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "driver doesn't support meta data" TSRMLS_CC);
1843        RETURN_FALSE;
1844    }
1845
1846    PDO_STMT_CLEAR_ERR();
1847    if (FAILURE == stmt->methods->get_column_meta(stmt, colno, return_value TSRMLS_CC)) {
1848        PDO_HANDLE_STMT_ERR();
1849        RETURN_FALSE;
1850    }
1851
1852    /* add stock items */
1853    col = &stmt->columns[colno];
1854    add_assoc_string(return_value, "name", col->name, 1);
1855    add_assoc_long(return_value, "len", col->maxlen); /* FIXME: unsigned ? */
1856    add_assoc_long(return_value, "precision", col->precision);
1857    if (col->param_type != PDO_PARAM_ZVAL) {
1858        /* if param_type is PDO_PARAM_ZVAL the driver has to provide correct data */
1859        add_assoc_long(return_value, "pdo_type", col->param_type);
1860    }
1861}
1862/* }}} */
1863
1864/* {{{ proto bool PDOStatement::setFetchMode(int mode [mixed* params])
1865   Changes the default fetch mode for subsequent fetches (params have different meaning for different fetch modes) */
1866
1867int pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAMETERS, pdo_stmt_t *stmt, int skip)
1868{
1869    long mode = PDO_FETCH_BOTH;
1870    int flags = 0, argc = ZEND_NUM_ARGS() - skip;
1871    zval ***args;
1872    zend_class_entry **cep;
1873    int retval;
1874
1875    do_fetch_opt_finish(stmt, 1 TSRMLS_CC);
1876
1877    switch (stmt->default_fetch_type) {
1878        case PDO_FETCH_INTO:
1879            if (stmt->fetch.into) {
1880                zval_ptr_dtor(&stmt->fetch.into);
1881                stmt->fetch.into = NULL;
1882            }
1883            break;
1884        default:
1885            ;
1886    }
1887
1888    stmt->default_fetch_type = PDO_FETCH_BOTH;
1889
1890    if (argc == 0) {
1891        return SUCCESS;
1892    }
1893
1894    args = safe_emalloc(ZEND_NUM_ARGS(), sizeof(zval*), 0);
1895
1896    retval = zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args);
1897
1898    if (SUCCESS == retval) {
1899        if (Z_TYPE_PP(args[skip]) != IS_LONG) {
1900            pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "mode must be an integer" TSRMLS_CC);
1901            retval = FAILURE;
1902        } else {
1903            mode = Z_LVAL_PP(args[skip]);
1904            flags = mode & PDO_FETCH_FLAGS;
1905
1906            retval = pdo_stmt_verify_mode(stmt, mode, 0 TSRMLS_CC);
1907        }
1908    }
1909
1910    if (FAILURE == retval) {
1911        PDO_STMT_CLEAR_ERR();
1912        efree(args);
1913        return FAILURE;
1914    }
1915
1916    retval = FAILURE;
1917    switch (mode & ~PDO_FETCH_FLAGS) {
1918        case PDO_FETCH_USE_DEFAULT:
1919        case PDO_FETCH_LAZY:
1920        case PDO_FETCH_ASSOC:
1921        case PDO_FETCH_NUM:
1922        case PDO_FETCH_BOTH:
1923        case PDO_FETCH_OBJ:
1924        case PDO_FETCH_BOUND:
1925        case PDO_FETCH_NAMED:
1926        case PDO_FETCH_KEY_PAIR:
1927            if (argc != 1) {
1928                pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode doesn't allow any extra arguments" TSRMLS_CC);
1929            } else {
1930                retval = SUCCESS;
1931            }
1932            break;
1933
1934        case PDO_FETCH_COLUMN:
1935            if (argc != 2) {
1936                pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode requires the colno argument" TSRMLS_CC);
1937            } else  if (Z_TYPE_PP(args[skip+1]) != IS_LONG) {
1938                pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "colno must be an integer" TSRMLS_CC);
1939            } else {
1940                stmt->fetch.column = Z_LVAL_PP(args[skip+1]);
1941                retval = SUCCESS;
1942            }
1943            break;
1944
1945        case PDO_FETCH_CLASS:
1946            /* Gets its class name from 1st column */
1947            if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
1948                if (argc != 1) {
1949                    pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode doesn't allow any extra arguments" TSRMLS_CC);
1950                } else {
1951                    stmt->fetch.cls.ce = NULL;
1952                    retval = SUCCESS;
1953                }
1954            } else {
1955                if (argc < 2) {
1956                    pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode requires the classname argument" TSRMLS_CC);
1957                } else if (argc > 3) {
1958                    pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "too many arguments" TSRMLS_CC);
1959                } else if (Z_TYPE_PP(args[skip+1]) != IS_STRING) {
1960                    pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "classname must be a string" TSRMLS_CC);
1961                } else {
1962                    retval = zend_lookup_class(Z_STRVAL_PP(args[skip+1]),
1963                        Z_STRLEN_PP(args[skip+1]), &cep TSRMLS_CC);
1964
1965                    if (SUCCESS == retval && cep && *cep) {
1966                        stmt->fetch.cls.ce = *cep;
1967                    }
1968                }
1969            }
1970
1971            if (SUCCESS == retval) {
1972                stmt->fetch.cls.ctor_args = NULL;
1973#ifdef ilia_0 /* we'll only need this when we have persistent statements, if ever */
1974                if (stmt->dbh->is_persistent) {
1975                    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");
1976                }
1977#endif
1978                if (argc == 3) {
1979                    if (Z_TYPE_PP(args[skip+2]) != IS_NULL && Z_TYPE_PP(args[skip+2]) != IS_ARRAY) {
1980                        pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "ctor_args must be either NULL or an array" TSRMLS_CC);
1981                        retval = FAILURE;
1982                    } else if (Z_TYPE_PP(args[skip+2]) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_PP(args[skip+2]))) {
1983                        ALLOC_ZVAL(stmt->fetch.cls.ctor_args);
1984                        *stmt->fetch.cls.ctor_args = **args[skip+2];
1985                        zval_copy_ctor(stmt->fetch.cls.ctor_args);
1986                    }
1987                }
1988
1989                if (SUCCESS == retval) {
1990                    do_fetch_class_prepare(stmt TSRMLS_CC);
1991                }
1992            }
1993
1994            break;
1995
1996        case PDO_FETCH_INTO:
1997            if (argc != 2) {
1998                pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "fetch mode requires the object parameter" TSRMLS_CC);
1999            } else if (Z_TYPE_PP(args[skip+1]) != IS_OBJECT) {
2000                pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "object must be an object" TSRMLS_CC);
2001            } else {
2002                retval = SUCCESS;
2003            }
2004
2005            if (SUCCESS == retval) {
2006#ifdef ilia_0 /* we'll only need this when we have persistent statements, if ever */
2007                if (stmt->dbh->is_persistent) {
2008                    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");
2009                }
2010#endif
2011                MAKE_STD_ZVAL(stmt->fetch.into);
2012
2013                Z_TYPE_P(stmt->fetch.into) = IS_OBJECT;
2014                Z_OBJ_HANDLE_P(stmt->fetch.into) = Z_OBJ_HANDLE_PP(args[skip+1]);
2015                Z_OBJ_HT_P(stmt->fetch.into) = Z_OBJ_HT_PP(args[skip+1]);
2016                zend_objects_store_add_ref(stmt->fetch.into TSRMLS_CC);
2017            }
2018
2019            break;
2020
2021        default:
2022            pdo_raise_impl_error(stmt->dbh, stmt, "22003", "Invalid fetch mode specified" TSRMLS_CC);
2023    }
2024
2025    if (SUCCESS == retval) {
2026        stmt->default_fetch_type = mode;
2027    }
2028
2029    /*
2030     * PDO error (if any) has already been raised at this point.
2031     *
2032     * The error_code is cleared, otherwise the caller will read the
2033     * last error message from the driver.
2034     *
2035     */
2036    PDO_STMT_CLEAR_ERR();
2037
2038    efree(args);
2039
2040    return retval;
2041}
2042
2043static PHP_METHOD(PDOStatement, setFetchMode)
2044{
2045    PHP_STMT_GET_OBJ;
2046
2047    RETVAL_BOOL(
2048        pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAM_PASSTHRU,
2049            stmt, 0) == SUCCESS ? 1 : 0
2050        );
2051}
2052/* }}} */
2053
2054/* {{{ proto bool PDOStatement::nextRowset()
2055   Advances to the next rowset in a multi-rowset statement handle. Returns true if it succeded, false otherwise */
2056
2057static int pdo_stmt_do_next_rowset(pdo_stmt_t *stmt TSRMLS_DC)
2058{
2059    /* un-describe */
2060    if (stmt->columns) {
2061        int i;
2062        struct pdo_column_data *cols = stmt->columns;
2063
2064        for (i = 0; i < stmt->column_count; i++) {
2065            efree(cols[i].name);
2066        }
2067        efree(stmt->columns);
2068        stmt->columns = NULL;
2069        stmt->column_count = 0;
2070    }
2071
2072    if (!stmt->methods->next_rowset(stmt TSRMLS_CC)) {
2073        /* Set the executed flag to 0 to reallocate columns on next execute */
2074        stmt->executed = 0;
2075        return 0;
2076    }
2077
2078    pdo_stmt_describe_columns(stmt TSRMLS_CC);
2079
2080    return 1;
2081}
2082
2083static PHP_METHOD(PDOStatement, nextRowset)
2084{
2085    PHP_STMT_GET_OBJ;
2086
2087    if (!stmt->methods->next_rowset) {
2088        pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "driver does not support multiple rowsets" TSRMLS_CC);
2089        RETURN_FALSE;
2090    }
2091
2092    PDO_STMT_CLEAR_ERR();
2093
2094    if (!pdo_stmt_do_next_rowset(stmt TSRMLS_CC)) {
2095        PDO_HANDLE_STMT_ERR();
2096        RETURN_FALSE;
2097    }
2098
2099    RETURN_TRUE;
2100}
2101/* }}} */
2102
2103/* {{{ proto bool PDOStatement::closeCursor()
2104   Closes the cursor, leaving the statement ready for re-execution. */
2105static PHP_METHOD(PDOStatement, closeCursor)
2106{
2107    PHP_STMT_GET_OBJ;
2108
2109    if (!stmt->methods->cursor_closer) {
2110        /* emulate it by fetching and discarding rows */
2111        do {
2112            while (stmt->methods->fetcher(stmt, PDO_FETCH_ORI_NEXT, 0 TSRMLS_CC))
2113                ;
2114            if (!stmt->methods->next_rowset) {
2115                break;
2116            }
2117
2118            if (!pdo_stmt_do_next_rowset(stmt TSRMLS_CC)) {
2119                break;
2120            }
2121
2122        } while (1);
2123        stmt->executed = 0;
2124        RETURN_TRUE;
2125    }
2126
2127    PDO_STMT_CLEAR_ERR();
2128
2129    if (!stmt->methods->cursor_closer(stmt TSRMLS_CC)) {
2130        PDO_HANDLE_STMT_ERR();
2131        RETURN_FALSE;
2132    }
2133    stmt->executed = 0;
2134    RETURN_TRUE;
2135}
2136/* }}} */
2137
2138/* {{{ proto void PDOStatement::debugDumpParams()
2139   A utility for internals hackers to debug parameter internals */
2140static PHP_METHOD(PDOStatement, debugDumpParams)
2141{
2142    php_stream *out = php_stream_open_wrapper("php://output", "w", 0, NULL);
2143    HashPosition pos;
2144    struct pdo_bound_param_data *param;
2145    PHP_STMT_GET_OBJ;
2146
2147    if (out == NULL) {
2148        RETURN_FALSE;
2149    }
2150
2151    php_stream_printf(out TSRMLS_CC, "SQL: [%d] %.*s\n",
2152        stmt->query_stringlen,
2153        stmt->query_stringlen, stmt->query_string);
2154
2155    php_stream_printf(out TSRMLS_CC, "Params:  %d\n",
2156        stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0);
2157
2158    if (stmt->bound_params) {
2159        zend_hash_internal_pointer_reset_ex(stmt->bound_params, &pos);
2160        while (SUCCESS == zend_hash_get_current_data_ex(stmt->bound_params,
2161                (void**)&param, &pos)) {
2162            char *str;
2163            uint len;
2164            ulong num;
2165            int res;
2166
2167            res = zend_hash_get_current_key_ex(stmt->bound_params, &str, &len, &num, 0, &pos);
2168            if (res == HASH_KEY_IS_LONG) {
2169                php_stream_printf(out TSRMLS_CC, "Key: Position #%ld:\n", num);
2170            } else if (res == HASH_KEY_IS_STRING) {
2171                php_stream_printf(out TSRMLS_CC, "Key: Name: [%d] %.*s\n", len, len, str);
2172            }
2173
2174            php_stream_printf(out TSRMLS_CC, "paramno=%ld\nname=[%d] \"%.*s\"\nis_param=%d\nparam_type=%d\n",
2175                param->paramno, param->namelen, param->namelen, param->name ? param->name : "",
2176                param->is_param,
2177                param->param_type);
2178
2179            zend_hash_move_forward_ex(stmt->bound_params, &pos);
2180        }
2181    }
2182
2183    php_stream_close(out);
2184}
2185/* }}} */
2186
2187/* {{{ proto int PDOStatement::__wakeup()
2188   Prevents use of a PDOStatement instance that has been unserialized */
2189static PHP_METHOD(PDOStatement, __wakeup)
2190{
2191    zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "You cannot serialize or unserialize PDOStatement instances");
2192}
2193/* }}} */
2194
2195/* {{{ proto int PDOStatement::__sleep()
2196   Prevents serialization of a PDOStatement instance */
2197static PHP_METHOD(PDOStatement, __sleep)
2198{
2199    zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "You cannot serialize or unserialize PDOStatement instances");
2200}
2201/* }}} */
2202
2203const zend_function_entry pdo_dbstmt_functions[] = {
2204    PHP_ME(PDOStatement, execute,       arginfo_pdostatement_execute,       ZEND_ACC_PUBLIC)
2205    PHP_ME(PDOStatement, fetch,         arginfo_pdostatement_fetch,         ZEND_ACC_PUBLIC)
2206    PHP_ME(PDOStatement, bindParam,     arginfo_pdostatement_bindparam,     ZEND_ACC_PUBLIC)
2207    PHP_ME(PDOStatement, bindColumn,    arginfo_pdostatement_bindcolumn,    ZEND_ACC_PUBLIC)
2208    PHP_ME(PDOStatement, bindValue,     arginfo_pdostatement_bindvalue,     ZEND_ACC_PUBLIC)
2209    PHP_ME(PDOStatement, rowCount,      arginfo_pdostatement__void,         ZEND_ACC_PUBLIC)
2210    PHP_ME(PDOStatement, fetchColumn,   arginfo_pdostatement_fetchcolumn,   ZEND_ACC_PUBLIC)
2211    PHP_ME(PDOStatement, fetchAll,      arginfo_pdostatement_fetchall,      ZEND_ACC_PUBLIC)
2212    PHP_ME(PDOStatement, fetchObject,   arginfo_pdostatement_fetchobject,   ZEND_ACC_PUBLIC)
2213    PHP_ME(PDOStatement, errorCode,     arginfo_pdostatement__void,         ZEND_ACC_PUBLIC)
2214    PHP_ME(PDOStatement, errorInfo,     arginfo_pdostatement__void,         ZEND_ACC_PUBLIC)
2215    PHP_ME(PDOStatement, setAttribute,  arginfo_pdostatement_setattribute,  ZEND_ACC_PUBLIC)
2216    PHP_ME(PDOStatement, getAttribute,  arginfo_pdostatement_getattribute,  ZEND_ACC_PUBLIC)
2217    PHP_ME(PDOStatement, columnCount,   arginfo_pdostatement__void,         ZEND_ACC_PUBLIC)
2218    PHP_ME(PDOStatement, getColumnMeta, arginfo_pdostatement_getcolumnmeta, ZEND_ACC_PUBLIC)
2219    PHP_ME(PDOStatement, setFetchMode,  arginfo_pdostatement_setfetchmode,  ZEND_ACC_PUBLIC)
2220    PHP_ME(PDOStatement, nextRowset,    arginfo_pdostatement__void,         ZEND_ACC_PUBLIC)
2221    PHP_ME(PDOStatement, closeCursor,   arginfo_pdostatement__void,         ZEND_ACC_PUBLIC)
2222    PHP_ME(PDOStatement, debugDumpParams, arginfo_pdostatement__void,       ZEND_ACC_PUBLIC)
2223    PHP_ME(PDOStatement, __wakeup,      arginfo_pdostatement__void,         ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
2224    PHP_ME(PDOStatement, __sleep,       arginfo_pdostatement__void,         ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
2225    {NULL, NULL, NULL}
2226};
2227
2228/* {{{ overloaded handlers for PDOStatement class */
2229static void dbstmt_prop_write(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC)
2230{
2231    pdo_stmt_t * stmt = (pdo_stmt_t *) zend_object_store_get_object(object TSRMLS_CC);
2232
2233    convert_to_string(member);
2234
2235    if(strcmp(Z_STRVAL_P(member), "queryString") == 0) {
2236        pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "property queryString is read only" TSRMLS_CC);
2237    } else {
2238        std_object_handlers.write_property(object, member, value, key TSRMLS_CC);
2239    }
2240}
2241
2242static void dbstmt_prop_delete(zval *object, zval *member, const zend_literal *key TSRMLS_DC)
2243{
2244    pdo_stmt_t * stmt = (pdo_stmt_t *) zend_object_store_get_object(object TSRMLS_CC);
2245
2246    convert_to_string(member);
2247
2248    if(strcmp(Z_STRVAL_P(member), "queryString") == 0) {
2249        pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "property queryString is read only" TSRMLS_CC);
2250    } else {
2251        std_object_handlers.unset_property(object, member, key TSRMLS_CC);
2252    }
2253}
2254
2255static union _zend_function *dbstmt_method_get(
2256#if PHP_API_VERSION >= 20041225
2257    zval **object_pp,
2258#else
2259    zval *object,
2260#endif
2261    char *method_name, int method_len, const zend_literal *key TSRMLS_DC)
2262{
2263    zend_function *fbc = NULL;
2264    char *lc_method_name;
2265#if PHP_API_VERSION >= 20041225
2266    zval *object = *object_pp;
2267#endif
2268
2269    lc_method_name = emalloc(method_len + 1);
2270    zend_str_tolower_copy(lc_method_name, method_name, method_len);
2271
2272    if (zend_hash_find(&Z_OBJCE_P(object)->function_table, lc_method_name,
2273            method_len+1, (void**)&fbc) == FAILURE) {
2274        pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object(object TSRMLS_CC);
2275        /* instance not created by PDO object */
2276        if (!stmt->dbh) {
2277            goto out;
2278        }
2279        /* not a pre-defined method, nor a user-defined method; check
2280         * the driver specific methods */
2281        if (!stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT]) {
2282            if (!pdo_hash_methods(stmt->dbh,
2283                PDO_DBH_DRIVER_METHOD_KIND_STMT TSRMLS_CC)
2284                || !stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT]) {
2285                goto out;
2286            }
2287        }
2288
2289        if (zend_hash_find(stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT],
2290                lc_method_name, method_len+1, (void**)&fbc) == FAILURE) {
2291            fbc = NULL;
2292            goto out;
2293        }
2294        /* got it */
2295    }
2296
2297out:
2298    efree(lc_method_name);
2299    return fbc;
2300}
2301
2302static int dbstmt_compare(zval *object1, zval *object2 TSRMLS_DC)
2303{
2304    return -1;
2305}
2306
2307static zend_object_value dbstmt_clone_obj(zval *zobject TSRMLS_DC)
2308{
2309    zend_object_value retval;
2310    pdo_stmt_t *stmt;
2311    pdo_stmt_t *old_stmt;
2312    zend_object_handle handle = Z_OBJ_HANDLE_P(zobject);
2313
2314    stmt = ecalloc(1, sizeof(*stmt));
2315    zend_object_std_init(&stmt->std, Z_OBJCE_P(zobject) TSRMLS_CC);
2316    object_properties_init(&stmt->std, Z_OBJCE_P(zobject));
2317    stmt->refcount = 1;
2318
2319    old_stmt = (pdo_stmt_t *)zend_object_store_get_object(zobject TSRMLS_CC);
2320
2321    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);
2322    retval.handlers = Z_OBJ_HT_P(zobject);
2323
2324    zend_objects_clone_members((zend_object *)stmt, retval, (zend_object *)old_stmt, handle TSRMLS_CC);
2325
2326    zend_objects_store_add_ref(&old_stmt->database_object_handle TSRMLS_CC);
2327    stmt->database_object_handle = old_stmt->database_object_handle;
2328
2329    return retval;
2330}
2331
2332zend_object_handlers pdo_dbstmt_object_handlers;
2333static int pdo_row_serialize(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC);
2334
2335void pdo_stmt_init(TSRMLS_D)
2336{
2337    zend_class_entry ce;
2338
2339    INIT_CLASS_ENTRY(ce, "PDOStatement", pdo_dbstmt_functions);
2340    pdo_dbstmt_ce = zend_register_internal_class(&ce TSRMLS_CC);
2341    pdo_dbstmt_ce->get_iterator = pdo_stmt_iter_get;
2342    pdo_dbstmt_ce->create_object = pdo_dbstmt_new;
2343    zend_class_implements(pdo_dbstmt_ce TSRMLS_CC, 1, zend_ce_traversable);
2344    zend_declare_property_null(pdo_dbstmt_ce, "queryString", sizeof("queryString")-1, ZEND_ACC_PUBLIC TSRMLS_CC);
2345
2346    memcpy(&pdo_dbstmt_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
2347    pdo_dbstmt_object_handlers.write_property = dbstmt_prop_write;
2348    pdo_dbstmt_object_handlers.unset_property = dbstmt_prop_delete;
2349    pdo_dbstmt_object_handlers.get_method = dbstmt_method_get;
2350    pdo_dbstmt_object_handlers.compare_objects = dbstmt_compare;
2351    pdo_dbstmt_object_handlers.clone_obj = dbstmt_clone_obj;
2352
2353    INIT_CLASS_ENTRY(ce, "PDORow", pdo_row_functions);
2354    pdo_row_ce = zend_register_internal_class(&ce TSRMLS_CC);
2355    pdo_row_ce->ce_flags |= ZEND_ACC_FINAL_CLASS; /* when removing this a lot of handlers need to be redone */
2356    pdo_row_ce->create_object = pdo_row_new;
2357    pdo_row_ce->serialize = pdo_row_serialize;
2358}
2359
2360static void free_statement(pdo_stmt_t *stmt TSRMLS_DC)
2361{
2362    if (stmt->bound_params) {
2363        zend_hash_destroy(stmt->bound_params);
2364        FREE_HASHTABLE(stmt->bound_params);
2365        stmt->bound_params = NULL;
2366    }
2367    if (stmt->bound_param_map) {
2368        zend_hash_destroy(stmt->bound_param_map);
2369        FREE_HASHTABLE(stmt->bound_param_map);
2370        stmt->bound_param_map = NULL;
2371    }
2372    if (stmt->bound_columns) {
2373        zend_hash_destroy(stmt->bound_columns);
2374        FREE_HASHTABLE(stmt->bound_columns);
2375        stmt->bound_columns = NULL;
2376    }
2377
2378    if (stmt->methods && stmt->methods->dtor) {
2379        stmt->methods->dtor(stmt TSRMLS_CC);
2380    }
2381    if (stmt->query_string) {
2382        efree(stmt->query_string);
2383    }
2384
2385    if (stmt->columns) {
2386        int i;
2387        struct pdo_column_data *cols = stmt->columns;
2388
2389        for (i = 0; i < stmt->column_count; i++) {
2390            if (cols[i].name) {
2391                efree(cols[i].name);
2392                cols[i].name = NULL;
2393            }
2394        }
2395        efree(stmt->columns);
2396        stmt->columns = NULL;
2397    }
2398
2399    if (stmt->fetch.into && stmt->default_fetch_type == PDO_FETCH_INTO) {
2400        FREE_ZVAL(stmt->fetch.into);
2401        stmt->fetch.into = NULL;
2402    }
2403
2404    do_fetch_opt_finish(stmt, 1 TSRMLS_CC);
2405
2406    zend_objects_store_del_ref(&stmt->database_object_handle TSRMLS_CC);
2407    if (stmt->dbh) {
2408        php_pdo_dbh_delref(stmt->dbh TSRMLS_CC);
2409    }
2410    zend_object_std_dtor(&stmt->std TSRMLS_CC);
2411    efree(stmt);
2412}
2413
2414PDO_API void php_pdo_stmt_addref(pdo_stmt_t *stmt TSRMLS_DC)
2415{
2416    stmt->refcount++;
2417}
2418
2419PDO_API void php_pdo_stmt_delref(pdo_stmt_t *stmt TSRMLS_DC)
2420{
2421    if (--stmt->refcount == 0) {
2422        free_statement(stmt TSRMLS_CC);
2423    }
2424}
2425
2426void pdo_dbstmt_free_storage(pdo_stmt_t *stmt TSRMLS_DC)
2427{
2428    php_pdo_stmt_delref(stmt TSRMLS_CC);
2429}
2430
2431zend_object_value pdo_dbstmt_new(zend_class_entry *ce TSRMLS_DC)
2432{
2433    zend_object_value retval;
2434
2435    pdo_stmt_t *stmt;
2436    stmt = emalloc(sizeof(*stmt));
2437    memset(stmt, 0, sizeof(*stmt));
2438    zend_object_std_init(&stmt->std, ce TSRMLS_CC);
2439    object_properties_init(&stmt->std, ce);
2440    stmt->refcount = 1;
2441
2442    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);
2443    retval.handlers = &pdo_dbstmt_object_handlers;
2444
2445    return retval;
2446}
2447/* }}} */
2448
2449/* {{{ statement iterator */
2450
2451struct php_pdo_iterator {
2452    zend_object_iterator iter;
2453    pdo_stmt_t *stmt;
2454    ulong key;
2455    zval *fetch_ahead;
2456};
2457
2458static void pdo_stmt_iter_dtor(zend_object_iterator *iter TSRMLS_DC)
2459{
2460    struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter->data;
2461
2462    if (--I->stmt->refcount == 0) {
2463        free_statement(I->stmt TSRMLS_CC);
2464    }
2465
2466    if (I->fetch_ahead) {
2467        zval_ptr_dtor(&I->fetch_ahead);
2468    }
2469
2470    efree(I);
2471}
2472
2473static int pdo_stmt_iter_valid(zend_object_iterator *iter TSRMLS_DC)
2474{
2475    struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter->data;
2476
2477    return I->fetch_ahead ? SUCCESS : FAILURE;
2478}
2479
2480static void pdo_stmt_iter_get_data(zend_object_iterator *iter, zval ***data TSRMLS_DC)
2481{
2482    struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter->data;
2483
2484    /* sanity */
2485    if (!I->fetch_ahead) {
2486        *data = NULL;
2487        return;
2488    }
2489
2490    *data = &I->fetch_ahead;
2491}
2492
2493static void pdo_stmt_iter_get_key(zend_object_iterator *iter, zval *key TSRMLS_DC)
2494{
2495    struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter->data;
2496
2497    if (I->key == (ulong)-1) {
2498        ZVAL_NULL(key);
2499    } else {
2500        ZVAL_LONG(key, I->key);
2501    }
2502}
2503
2504static void pdo_stmt_iter_move_forwards(zend_object_iterator *iter TSRMLS_DC)
2505{
2506    struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter->data;
2507
2508    if (I->fetch_ahead) {
2509        zval_ptr_dtor(&I->fetch_ahead);
2510        I->fetch_ahead = NULL;
2511    }
2512
2513    MAKE_STD_ZVAL(I->fetch_ahead);
2514
2515    if (!do_fetch(I->stmt, TRUE, I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2516            PDO_FETCH_ORI_NEXT, 0, 0 TSRMLS_CC)) {
2517        pdo_stmt_t *stmt = I->stmt; /* for PDO_HANDLE_STMT_ERR() */
2518
2519        PDO_HANDLE_STMT_ERR();
2520        I->key = (ulong)-1;
2521        FREE_ZVAL(I->fetch_ahead);
2522        I->fetch_ahead = NULL;
2523
2524        return;
2525    }
2526
2527    I->key++;
2528}
2529
2530static zend_object_iterator_funcs pdo_stmt_iter_funcs = {
2531    pdo_stmt_iter_dtor,
2532    pdo_stmt_iter_valid,
2533    pdo_stmt_iter_get_data,
2534    pdo_stmt_iter_get_key,
2535    pdo_stmt_iter_move_forwards,
2536    NULL
2537};
2538
2539zend_object_iterator *pdo_stmt_iter_get(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC)
2540{
2541    pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object(object TSRMLS_CC);
2542    struct php_pdo_iterator *I;
2543
2544    if (by_ref) {
2545        zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
2546    }
2547
2548    I = ecalloc(1, sizeof(*I));
2549    I->iter.funcs = &pdo_stmt_iter_funcs;
2550    I->iter.data = I;
2551    I->stmt = stmt;
2552    stmt->refcount++;
2553
2554    MAKE_STD_ZVAL(I->fetch_ahead);
2555    if (!do_fetch(I->stmt, TRUE, I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2556            PDO_FETCH_ORI_NEXT, 0, 0 TSRMLS_CC)) {
2557        PDO_HANDLE_STMT_ERR();
2558        I->key = (ulong)-1;
2559        FREE_ZVAL(I->fetch_ahead);
2560        I->fetch_ahead = NULL;
2561    }
2562
2563    return &I->iter;
2564}
2565
2566/* }}} */
2567
2568/* {{{ overloaded handlers for PDORow class (used by PDO_FETCH_LAZY) */
2569
2570const zend_function_entry pdo_row_functions[] = {
2571    {NULL, NULL, NULL}
2572};
2573
2574static zval *row_prop_read(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC)
2575{
2576    zval *return_value;
2577    pdo_stmt_t * stmt = (pdo_stmt_t *) zend_object_store_get_object(object TSRMLS_CC);
2578    int colno = -1;
2579
2580    MAKE_STD_ZVAL(return_value);
2581    RETVAL_NULL();
2582
2583    if (stmt) {
2584        if (Z_TYPE_P(member) == IS_LONG) {
2585            if (Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count) {
2586                fetch_value(stmt, return_value, Z_LVAL_P(member), NULL TSRMLS_CC);
2587            }
2588        } else {
2589            convert_to_string(member);
2590            /* TODO: replace this with a hash of available column names to column
2591             * numbers */
2592            for (colno = 0; colno < stmt->column_count; colno++) {
2593                if (strcmp(stmt->columns[colno].name, Z_STRVAL_P(member)) == 0) {
2594                    fetch_value(stmt, return_value, colno, NULL TSRMLS_CC);
2595                    Z_SET_REFCOUNT_P(return_value, 0);
2596                    Z_UNSET_ISREF_P(return_value);
2597                    return return_value;
2598                }
2599            }
2600            if (strcmp(Z_STRVAL_P(member), "queryString") == 0) {
2601                zval_ptr_dtor(&return_value);
2602                return std_object_handlers.read_property(object, member, type, key TSRMLS_CC);
2603            }
2604        }
2605    }
2606
2607    Z_SET_REFCOUNT_P(return_value, 0);
2608    Z_UNSET_ISREF_P(return_value);
2609
2610    return return_value;
2611}
2612
2613static zval *row_dim_read(zval *object, zval *member, int type TSRMLS_DC)
2614{
2615    return row_prop_read(object, member, type, NULL TSRMLS_CC);
2616}
2617
2618static void row_prop_write(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC)
2619{
2620    php_error_docref(NULL TSRMLS_CC, E_WARNING, "This PDORow is not from a writable result set");
2621}
2622
2623static void row_dim_write(zval *object, zval *member, zval *value TSRMLS_DC)
2624{
2625    php_error_docref(NULL TSRMLS_CC, E_WARNING, "This PDORow is not from a writable result set");
2626}
2627
2628static int row_prop_exists(zval *object, zval *member, int check_empty, const zend_literal *key TSRMLS_DC)
2629{
2630    pdo_stmt_t * stmt = (pdo_stmt_t *) zend_object_store_get_object(object TSRMLS_CC);
2631    int colno = -1;
2632
2633    if (stmt) {
2634        if (Z_TYPE_P(member) == IS_LONG) {
2635            return Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count;
2636        } else {
2637            convert_to_string(member);
2638
2639            /* TODO: replace this with a hash of available column names to column
2640             * numbers */
2641            for (colno = 0; colno < stmt->column_count; colno++) {
2642                if (strcmp(stmt->columns[colno].name, Z_STRVAL_P(member)) == 0) {
2643                    return 1;
2644                }
2645            }
2646        }
2647    }
2648
2649    return 0;
2650}
2651
2652static int row_dim_exists(zval *object, zval *member, int check_empty TSRMLS_DC)
2653{
2654    return row_prop_exists(object, member, check_empty, NULL TSRMLS_CC);
2655}
2656
2657static void row_prop_delete(zval *object, zval *offset, const zend_literal *key TSRMLS_DC)
2658{
2659    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot delete properties from a PDORow");
2660}
2661
2662static void row_dim_delete(zval *object, zval *offset TSRMLS_DC)
2663{
2664    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot delete properties from a PDORow");
2665}
2666
2667static HashTable *row_get_properties(zval *object TSRMLS_DC)
2668{
2669    pdo_stmt_t * stmt = (pdo_stmt_t *) zend_object_store_get_object(object TSRMLS_CC);
2670    int i;
2671
2672    if (stmt == NULL) {
2673        return NULL;
2674    }
2675
2676    if (!stmt->std.properties) {
2677        rebuild_object_properties(&stmt->std);
2678    }
2679    for (i = 0; i < stmt->column_count; i++) {
2680        zval *val;
2681        MAKE_STD_ZVAL(val);
2682        fetch_value(stmt, val, i, NULL TSRMLS_CC);
2683
2684        zend_hash_update(stmt->std.properties, stmt->columns[i].name, stmt->columns[i].namelen + 1, (void *)&val, sizeof(zval *), NULL);
2685    }
2686
2687    return stmt->std.properties;
2688}
2689
2690static union _zend_function *row_method_get(
2691#if PHP_API_VERSION >= 20041225
2692    zval **object_pp,
2693#else
2694    zval *object,
2695#endif
2696    char *method_name, int method_len, const zend_literal *key TSRMLS_DC)
2697{
2698    zend_function *fbc;
2699    char *lc_method_name;
2700
2701    lc_method_name = emalloc(method_len + 1);
2702    zend_str_tolower_copy(lc_method_name, method_name, method_len);
2703
2704    if (zend_hash_find(&pdo_row_ce->function_table, lc_method_name, method_len+1, (void**)&fbc) == FAILURE) {
2705        efree(lc_method_name);
2706        return NULL;
2707    }
2708
2709    efree(lc_method_name);
2710    return fbc;
2711}
2712
2713static int row_call_method(const char *method, INTERNAL_FUNCTION_PARAMETERS)
2714{
2715    return FAILURE;
2716}
2717
2718static union _zend_function *row_get_ctor(zval *object TSRMLS_DC)
2719{
2720    static zend_internal_function ctor = {0};
2721
2722    ctor.type = ZEND_INTERNAL_FUNCTION;
2723    ctor.function_name = "__construct";
2724    ctor.scope = pdo_row_ce;
2725    ctor.handler = ZEND_FN(dbstmt_constructor);
2726    ctor.fn_flags = ZEND_ACC_PUBLIC;
2727
2728    return (union _zend_function*)&ctor;
2729}
2730
2731static zend_class_entry *row_get_ce(const zval *object TSRMLS_DC)
2732{
2733    return pdo_row_ce;
2734}
2735
2736static int row_get_classname(const zval *object, const char **class_name, zend_uint *class_name_len, int parent TSRMLS_DC)
2737{
2738    if (parent) {
2739        return FAILURE;
2740    } else {
2741        *class_name = estrndup("PDORow", sizeof("PDORow")-1);
2742        *class_name_len = sizeof("PDORow")-1;
2743        return SUCCESS;
2744    }
2745}
2746
2747static int row_compare(zval *object1, zval *object2 TSRMLS_DC)
2748{
2749    return -1;
2750}
2751
2752zend_object_handlers pdo_row_object_handlers = {
2753    zend_objects_store_add_ref,
2754    zend_objects_store_del_ref,
2755    NULL,
2756    row_prop_read,
2757    row_prop_write,
2758    row_dim_read,
2759    row_dim_write,
2760    NULL,
2761    NULL,
2762    NULL,
2763    row_prop_exists,
2764    row_prop_delete,
2765    row_dim_exists,
2766    row_dim_delete,
2767    row_get_properties,
2768    row_method_get,
2769    row_call_method,
2770    row_get_ctor,
2771    row_get_ce,
2772    row_get_classname,
2773    row_compare,
2774    NULL, /* cast */
2775    NULL
2776};
2777
2778void pdo_row_free_storage(pdo_stmt_t *stmt TSRMLS_DC)
2779{
2780    if (stmt) {
2781        ZVAL_NULL(&stmt->lazy_object_ref);
2782
2783        if (--stmt->refcount == 0) {
2784            free_statement(stmt TSRMLS_CC);
2785        }
2786    }
2787}
2788
2789zend_object_value pdo_row_new(zend_class_entry *ce TSRMLS_DC)
2790{
2791    zend_object_value retval;
2792
2793    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);
2794    retval.handlers = &pdo_row_object_handlers;
2795
2796    return retval;
2797}
2798
2799static int pdo_row_serialize(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC)
2800{
2801    php_error_docref(NULL TSRMLS_CC, E_WARNING, "PDORow instances may not be serialized");
2802    return FAILURE;
2803}
2804/* }}} */
2805
2806/*
2807 * Local variables:
2808 * tab-width: 4
2809 * c-basic-offset: 4
2810 * End:
2811 * vim600: noet sw=4 ts=4 fdm=marker
2812 * vim<600: noet sw=4 ts=4
2813 */
2814