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