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 Database 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 "php_pdo.h"
33#include "php_pdo_driver.h"
34#include "php_pdo_int.h"
35#include "zend_exceptions.h"
36#include "zend_object_handlers.h"
37#include "zend_hash.h"
38
39static int pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value TSRMLS_DC);
40
41void pdo_raise_impl_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *sqlstate, const char *supp TSRMLS_DC) /* {{{ */
42{
43    pdo_error_type *pdo_err = &dbh->error_code;
44    char *message = NULL;
45    const char *msg;
46
47    if (dbh && dbh->error_mode == PDO_ERRMODE_SILENT) {
48#if 0
49        /* BUG: if user is running in silent mode and hits an error at the driver level
50         * when they use the PDO methods to call up the error information, they may
51         * get bogus information */
52        return;
53#endif
54    }
55
56    if (stmt) {
57        pdo_err = &stmt->error_code;
58    }
59
60    strncpy(*pdo_err, sqlstate, 6);
61
62    /* hash sqlstate to error messages */
63    msg = pdo_sqlstate_state_to_description(*pdo_err);
64    if (!msg) {
65        msg = "<<Unknown error>>";
66    }
67
68    if (supp) {
69        spprintf(&message, 0, "SQLSTATE[%s]: %s: %s", *pdo_err, msg, supp);
70    } else {
71        spprintf(&message, 0, "SQLSTATE[%s]: %s", *pdo_err, msg);
72    }
73
74    if (dbh && dbh->error_mode != PDO_ERRMODE_EXCEPTION) {
75        php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", message);
76    } else {
77        zval ex, info;
78        zend_class_entry *def_ex = php_pdo_get_exception_base(1 TSRMLS_CC), *pdo_ex = php_pdo_get_exception();
79
80        object_init_ex(&ex, pdo_ex);
81
82        zend_update_property_string(def_ex, &ex, "message", sizeof("message")-1, message TSRMLS_CC);
83        zend_update_property_string(def_ex, &ex, "code", sizeof("code")-1, *pdo_err TSRMLS_CC);
84
85        array_init(&info);
86
87        add_next_index_string(&info, *pdo_err);
88        add_next_index_long(&info, 0);
89        zend_update_property(pdo_ex, &ex, "errorInfo", sizeof("errorInfo")-1, &info TSRMLS_CC);
90        zval_ptr_dtor(&info);
91
92        zend_throw_exception_object(&ex TSRMLS_CC);
93    }
94
95    if (message) {
96        efree(message);
97    }
98}
99/* }}} */
100
101PDO_API void pdo_handle_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */
102{
103    pdo_error_type *pdo_err = &dbh->error_code;
104    const char *msg = "<<Unknown>>";
105    char *supp = NULL;
106    zend_long native_code = 0;
107    zend_string *message = NULL;
108    zval info;
109
110    if (dbh == NULL || dbh->error_mode == PDO_ERRMODE_SILENT) {
111        return;
112    }
113
114    if (stmt) {
115        pdo_err = &stmt->error_code;
116    }
117
118    /* hash sqlstate to error messages */
119    msg = pdo_sqlstate_state_to_description(*pdo_err);
120    if (!msg) {
121        msg = "<<Unknown error>>";
122    }
123
124    ZVAL_UNDEF(&info);
125    if (dbh->methods->fetch_err) {
126        array_init(&info);
127
128        add_next_index_string(&info, *pdo_err);
129
130        if (dbh->methods->fetch_err(dbh, stmt, &info TSRMLS_CC)) {
131            zval *item;
132
133            if ((item = zend_hash_index_find(Z_ARRVAL(info), 1)) != NULL) {
134                native_code = Z_LVAL_P(item);
135            }
136
137            if ((item = zend_hash_index_find(Z_ARRVAL(info), 2)) != NULL) {
138                supp = estrndup(Z_STRVAL_P(item), Z_STRLEN_P(item));
139            }
140        }
141    }
142
143    if (supp) {
144        message = strpprintf(0, "SQLSTATE[%s]: %s: %ld %s", *pdo_err, msg, native_code, supp);
145    } else {
146        message = strpprintf(0, "SQLSTATE[%s]: %s", *pdo_err, msg);
147    }
148
149    if (dbh->error_mode == PDO_ERRMODE_WARNING) {
150        php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", message->val);
151    } else if (EG(exception) == NULL) {
152        zval ex;
153        zend_class_entry *def_ex = php_pdo_get_exception_base(1 TSRMLS_CC), *pdo_ex = php_pdo_get_exception();
154
155        object_init_ex(&ex, pdo_ex);
156
157        zend_update_property_str(def_ex, &ex, "message", sizeof("message") - 1, message TSRMLS_CC);
158        zend_update_property_string(def_ex, &ex, "code", sizeof("code") - 1, *pdo_err TSRMLS_CC);
159
160        if (!Z_ISUNDEF(info)) {
161            zend_update_property(pdo_ex, &ex, "errorInfo", sizeof("errorInfo") - 1, &info TSRMLS_CC);
162        }
163
164        zend_throw_exception_object(&ex TSRMLS_CC);
165    }
166
167    if (!Z_ISUNDEF(info)) {
168        zval_ptr_dtor(&info);
169    }
170
171    if (message) {
172        zend_string_release(message);
173    }
174
175    if (supp) {
176        efree(supp);
177    }
178}
179/* }}} */
180
181static char *dsn_from_uri(char *uri, char *buf, size_t buflen TSRMLS_DC) /* {{{ */
182{
183    php_stream *stream;
184    char *dsn = NULL;
185
186    stream = php_stream_open_wrapper(uri, "rb", REPORT_ERRORS, NULL);
187    if (stream) {
188        dsn = php_stream_get_line(stream, buf, buflen, NULL);
189        php_stream_close(stream);
190    }
191    return dsn;
192}
193/* }}} */
194
195/* {{{ proto void PDO::__construct(string dsn[, string username[, string passwd [, array options]]])
196   */
197static PHP_METHOD(PDO, dbh_constructor)
198{
199    zval *object = getThis();
200    pdo_dbh_t *dbh = NULL;
201    zend_bool is_persistent = 0;
202    char *data_source;
203    size_t data_source_len;
204    char *colon;
205    char *username=NULL, *password=NULL;
206    size_t usernamelen, passwordlen;
207    pdo_driver_t *driver = NULL;
208    zval *options = NULL;
209    char alt_dsn[512];
210    int call_factory = 1;
211
212    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s!s!a!", &data_source, &data_source_len,
213                &username, &usernamelen, &password, &passwordlen, &options)) {
214        ZEND_CTOR_MAKE_NULL();
215        return;
216    }
217
218    /* parse the data source name */
219    colon = strchr(data_source, ':');
220
221    if (!colon) {
222        /* let's see if this string has a matching dsn in the php.ini */
223        char *ini_dsn = NULL;
224
225        snprintf(alt_dsn, sizeof(alt_dsn), "pdo.dsn.%s", data_source);
226        if (FAILURE == cfg_get_string(alt_dsn, &ini_dsn)) {
227            zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "invalid data source name");
228            ZEND_CTOR_MAKE_NULL();
229            return;
230        }
231
232        data_source = ini_dsn;
233        colon = strchr(data_source, ':');
234
235        if (!colon) {
236            zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "invalid data source name (via INI: %s)", alt_dsn);
237            ZEND_CTOR_MAKE_NULL();
238            return;
239        }
240    }
241
242    if (!strncmp(data_source, "uri:", sizeof("uri:")-1)) {
243        /* the specified URI holds connection details */
244        data_source = dsn_from_uri(data_source + sizeof("uri:")-1, alt_dsn, sizeof(alt_dsn) TSRMLS_CC);
245        if (!data_source) {
246            zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "invalid data source URI");
247            ZEND_CTOR_MAKE_NULL();
248            return;
249        }
250        colon = strchr(data_source, ':');
251        if (!colon) {
252            zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "invalid data source name (via URI)");
253            ZEND_CTOR_MAKE_NULL();
254            return;
255        }
256    }
257
258    driver = pdo_find_driver(data_source, colon - data_source);
259
260    if (!driver) {
261        /* NB: don't want to include the data_source in the error message as
262         * it might contain a password */
263        zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "could not find driver");
264        ZEND_CTOR_MAKE_NULL();
265        return;
266    }
267
268    dbh = Z_PDO_DBH_P(object);
269
270    /* is this supposed to be a persistent connection ? */
271    if (options) {
272        int plen = 0;
273        char *hashkey = NULL;
274        zend_resource *le;
275        pdo_dbh_t *pdbh = NULL;
276        zval *v;
277
278        if ((v = zend_hash_index_find(Z_ARRVAL_P(options), PDO_ATTR_PERSISTENT)) != NULL) {
279            if (Z_TYPE_P(v) == IS_STRING &&
280                !is_numeric_string(Z_STRVAL_P(v), Z_STRLEN_P(v), NULL, NULL, 0) && Z_STRLEN_P(v) > 0) {
281                /* user specified key */
282                plen = spprintf(&hashkey, 0, "PDO:DBH:DSN=%s:%s:%s:%s", data_source,
283                        username ? username : "",
284                        password ? password : "",
285                        Z_STRVAL_P(v));
286                is_persistent = 1;
287            } else {
288                convert_to_long_ex(v);
289                is_persistent = Z_LVAL_P(v) ? 1 : 0;
290                plen = spprintf(&hashkey, 0, "PDO:DBH:DSN=%s:%s:%s", data_source,
291                        username ? username : "",
292                        password ? password : "");
293            }
294        }
295
296        if (is_persistent) {
297            /* let's see if we have one cached.... */
298            if ((le = zend_hash_str_find_ptr(&EG(persistent_list), hashkey, plen)) != NULL) {
299                if (le->type == php_pdo_list_entry()) {
300                    pdbh = (pdo_dbh_t*)le->ptr;
301
302                    /* is the connection still alive ? */
303                    if (pdbh->methods->check_liveness && FAILURE == (pdbh->methods->check_liveness)(pdbh TSRMLS_CC)) {
304                        /* nope... need to kill it */
305                        /*??? memory leak */
306                        zend_list_close(le);
307                        pdbh = NULL;
308                    }
309                }
310            }
311
312            if (pdbh) {
313                call_factory = 0;
314            } else {
315                /* need a brand new pdbh */
316                pdbh = pecalloc(1, sizeof(*pdbh), 1);
317
318                if (!pdbh) {
319                    php_error_docref(NULL TSRMLS_CC, E_ERROR, "out of memory while allocating PDO handle");
320                    /* NOTREACHED */
321                }
322
323                pdbh->is_persistent = 1;
324                if (!(pdbh->persistent_id = pemalloc(plen + 1, 1))) {
325                    php_error_docref(NULL TSRMLS_CC, E_ERROR, "out of memory while allocating PDO handle");
326                }
327                memcpy((char *)pdbh->persistent_id, hashkey, plen+1);
328                pdbh->persistent_id_len = plen;
329                pdbh->def_stmt_ce = dbh->def_stmt_ce;
330            }
331        }
332
333        if (pdbh) {
334            efree(dbh);
335            /* switch over to the persistent one */
336            Z_PDO_OBJECT_P(object)->inner = pdbh;
337            dbh = pdbh;
338        }
339
340        if (hashkey) {
341            efree(hashkey);
342        }
343    }
344
345    if (call_factory) {
346        dbh->data_source_len = strlen(colon + 1);
347        dbh->data_source = (const char*)pestrdup(colon + 1, is_persistent);
348        dbh->username = username ? pestrdup(username, is_persistent) : NULL;
349        dbh->password = password ? pestrdup(password, is_persistent) : NULL;
350        dbh->default_fetch_type = PDO_FETCH_BOTH;
351    }
352
353    dbh->auto_commit = pdo_attr_lval(options, PDO_ATTR_AUTOCOMMIT, 1 TSRMLS_CC);
354
355    if (!dbh->data_source || (username && !dbh->username) || (password && !dbh->password)) {
356        php_error_docref(NULL TSRMLS_CC, E_ERROR, "out of memory");
357    }
358
359    if (!call_factory) {
360        /* we got a persistent guy from our cache */
361        goto options;
362    }
363
364    if (driver->db_handle_factory(dbh, options TSRMLS_CC)) {
365        /* all set */
366
367        if (is_persistent) {
368            zend_resource le;
369
370            /* register in the persistent list etc. */
371            /* we should also need to replace the object store entry,
372               since it was created with emalloc */
373
374            le.type = php_pdo_list_entry();
375            le.ptr = dbh;
376            GC_REFCOUNT(&le) = 1;
377
378            if ((zend_hash_str_update_mem(&EG(persistent_list),
379                        (char*)dbh->persistent_id, dbh->persistent_id_len, &le, sizeof(le))) == NULL) {
380                php_error_docref(NULL TSRMLS_CC, E_ERROR, "Failed to register persistent entry");
381            }
382        }
383
384        dbh->driver = driver;
385options:
386        if (options) {
387            zval *attr_value;
388            zend_ulong long_key;
389            zend_string *str_key = NULL;
390
391            ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(options), long_key, str_key, attr_value) {
392                if (str_key) {
393                    continue;
394                }
395                pdo_dbh_attribute_set(dbh, long_key, attr_value TSRMLS_CC);
396            } ZEND_HASH_FOREACH_END();
397        }
398
399        return;
400    }
401
402    /* the connection failed; things will tidy up in free_storage */
403    /* XXX raise exception */
404    ZEND_CTOR_MAKE_NULL();
405}
406/* }}} */
407
408static zval *pdo_stmt_instantiate(pdo_dbh_t *dbh, zval *object, zend_class_entry *dbstmt_ce, zval *ctor_args TSRMLS_DC) /* {{{ */
409{
410    if (!Z_ISUNDEF_P(ctor_args)) {
411        if (Z_TYPE_P(ctor_args) != IS_ARRAY) {
412            pdo_raise_impl_error(dbh, NULL, "HY000", "constructor arguments must be passed as an array" TSRMLS_CC);
413            return NULL;
414        }
415        if (!dbstmt_ce->constructor) {
416            pdo_raise_impl_error(dbh, NULL, "HY000", "user-supplied statement does not accept constructor arguments" TSRMLS_CC);
417            return NULL;
418        }
419    }
420
421    object_init_ex(object, dbstmt_ce);
422    // ??? Z_SET_REFCOUNT_P(object, 1);
423    //Z_SET_ISREF_P(object);
424
425    return object;
426} /* }}} */
427
428static void pdo_stmt_construct(pdo_stmt_t *stmt, zval *object, zend_class_entry *dbstmt_ce, zval *ctor_args TSRMLS_DC) /* {{{ */
429{
430    zval query_string;
431    zval z_key;
432
433    ZVAL_STRINGL(&query_string, stmt->query_string, stmt->query_stringlen);
434    ZVAL_STRINGL(&z_key, "queryString", sizeof("queryString") - 1);
435    std_object_handlers.write_property(object, &z_key, &query_string, NULL TSRMLS_CC);
436    zval_ptr_dtor(&query_string);
437    zval_ptr_dtor(&z_key);
438
439    if (dbstmt_ce->constructor) {
440        zend_fcall_info fci;
441        zend_fcall_info_cache fcc;
442        zval retval;
443
444        fci.size = sizeof(zend_fcall_info);
445        fci.function_table = &dbstmt_ce->function_table;
446        ZVAL_UNDEF(&fci.function_name);
447        fci.object = Z_OBJ_P(object);
448        fci.symbol_table = NULL;
449        fci.retval = &retval;
450        fci.param_count = 0;
451        fci.params = NULL;
452        fci.no_separation = 1;
453
454        zend_fcall_info_args(&fci, ctor_args TSRMLS_CC);
455
456        fcc.initialized = 1;
457        fcc.function_handler = dbstmt_ce->constructor;
458        fcc.calling_scope = EG(scope);
459        fcc.called_scope = Z_OBJCE_P(object);
460        fcc.object = Z_OBJ_P(object);
461
462        if (zend_call_function(&fci, &fcc TSRMLS_CC) == FAILURE) {
463            Z_OBJ_P(object) = NULL;
464            ZEND_CTOR_MAKE_NULL();
465            object = NULL; /* marks failure */
466        } else if (!Z_ISUNDEF(retval)) {
467            zval_ptr_dtor(&retval);
468        }
469
470        if (fci.params) {
471            efree(fci.params);
472        }
473    }
474}
475/* }}} */
476
477/* {{{ proto object PDO::prepare(string statment [, array options])
478   Prepares a statement for execution and returns a statement object */
479static PHP_METHOD(PDO, prepare)
480{
481    pdo_stmt_t *stmt;
482    char *statement;
483    size_t statement_len;
484    zval *options = NULL, *opt, *item, ctor_args;
485    zend_class_entry *dbstmt_ce, *pce;
486    pdo_dbh_object_t *dbh_obj = Z_PDO_OBJECT_P(getThis());
487    pdo_dbh_t *dbh = dbh_obj->inner;
488
489    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a", &statement,
490            &statement_len, &options)) {
491        RETURN_FALSE;
492    }
493
494    PDO_DBH_CLEAR_ERR();
495    PDO_CONSTRUCT_CHECK;
496
497    if (ZEND_NUM_ARGS() > 1 && (opt = zend_hash_index_find(Z_ARRVAL_P(options), PDO_ATTR_STATEMENT_CLASS)) != NULL) {
498        if (Z_TYPE_P(opt) != IS_ARRAY || (item = zend_hash_index_find(Z_ARRVAL_P(opt), 0)) == NULL
499            || Z_TYPE_P(item) != IS_STRING
500            || (pce = zend_lookup_class(Z_STR_P(item) TSRMLS_CC)) == NULL
501        ) {
502            pdo_raise_impl_error(dbh, NULL, "HY000",
503                "PDO::ATTR_STATEMENT_CLASS requires format array(classname, array(ctor_args)); "
504                "the classname must be a string specifying an existing class"
505                TSRMLS_CC);
506            PDO_HANDLE_DBH_ERR();
507            RETURN_FALSE;
508        }
509        dbstmt_ce = pce;
510        if (!instanceof_function(dbstmt_ce, pdo_dbstmt_ce TSRMLS_CC)) {
511            pdo_raise_impl_error(dbh, NULL, "HY000",
512                "user-supplied statement class must be derived from PDOStatement" TSRMLS_CC);
513            PDO_HANDLE_DBH_ERR();
514            RETURN_FALSE;
515        }
516        if (dbstmt_ce->constructor && !(dbstmt_ce->constructor->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED))) {
517            pdo_raise_impl_error(dbh, NULL, "HY000",
518                "user-supplied statement class cannot have a public constructor" TSRMLS_CC);
519            PDO_HANDLE_DBH_ERR();
520            RETURN_FALSE;
521        }
522        if ((item = zend_hash_index_find(Z_ARRVAL_P(opt), 1)) != NULL) {
523            if (Z_TYPE_P(item) != IS_ARRAY) {
524                pdo_raise_impl_error(dbh, NULL, "HY000",
525                    "PDO::ATTR_STATEMENT_CLASS requires format array(classname, ctor_args); "
526                    "ctor_args must be an array"
527                TSRMLS_CC);
528                PDO_HANDLE_DBH_ERR();
529                RETURN_FALSE;
530            }
531            ZVAL_COPY_VALUE(&ctor_args, item);
532        } else {
533            ZVAL_UNDEF(&ctor_args);
534        }
535    } else {
536        dbstmt_ce = dbh->def_stmt_ce;
537        ZVAL_COPY_VALUE(&ctor_args, &dbh->def_stmt_ctor_args);
538    }
539
540    if (!pdo_stmt_instantiate(dbh, return_value, dbstmt_ce, &ctor_args TSRMLS_CC)) {
541        pdo_raise_impl_error(dbh, NULL, "HY000",
542            "failed to instantiate user-supplied statement class"
543            TSRMLS_CC);
544        PDO_HANDLE_DBH_ERR();
545        RETURN_FALSE;
546    }
547    stmt = Z_PDO_STMT_P(return_value);
548
549    /* unconditionally keep this for later reference */
550    stmt->query_string = estrndup(statement, statement_len);
551    stmt->query_stringlen = statement_len;
552    stmt->default_fetch_type = dbh->default_fetch_type;
553    stmt->dbh = dbh;
554    /* give it a reference to me */
555    ZVAL_OBJ(&stmt->database_object_handle, &dbh_obj->std);
556    Z_ADDREF(stmt->database_object_handle);
557    /* we haven't created a lazy object yet */
558    ZVAL_UNDEF(&stmt->lazy_object_ref);
559
560    if (dbh->methods->preparer(dbh, statement, statement_len, stmt, options TSRMLS_CC)) {
561        pdo_stmt_construct(stmt, return_value, dbstmt_ce, &ctor_args TSRMLS_CC);
562        return;
563    }
564
565    PDO_HANDLE_DBH_ERR();
566
567    /* kill the object handle for the stmt here */
568    zval_dtor(return_value);
569
570    RETURN_FALSE;
571}
572/* }}} */
573
574/* {{{ proto bool PDO::beginTransaction()
575   Initiates a transaction */
576static PHP_METHOD(PDO, beginTransaction)
577{
578    pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
579
580    if (zend_parse_parameters_none() == FAILURE) {
581        return;
582    }
583    PDO_CONSTRUCT_CHECK;
584
585    if (dbh->in_txn) {
586        zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "There is already an active transaction");
587        RETURN_FALSE;
588    }
589
590    if (!dbh->methods->begin) {
591        /* TODO: this should be an exception; see the auto-commit mode
592         * comments below */
593        zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "This driver doesn't support transactions");
594        RETURN_FALSE;
595    }
596
597    if (dbh->methods->begin(dbh TSRMLS_CC)) {
598        dbh->in_txn = 1;
599        RETURN_TRUE;
600    }
601
602    PDO_HANDLE_DBH_ERR();
603    RETURN_FALSE;
604}
605/* }}} */
606
607/* {{{ proto bool PDO::commit()
608   Commit a transaction */
609static PHP_METHOD(PDO, commit)
610{
611    pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
612
613    if (zend_parse_parameters_none() == FAILURE) {
614        return;
615    }
616    PDO_CONSTRUCT_CHECK;
617
618    if (!dbh->in_txn) {
619        zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "There is no active transaction");
620        RETURN_FALSE;
621    }
622
623    if (dbh->methods->commit(dbh TSRMLS_CC)) {
624        dbh->in_txn = 0;
625        RETURN_TRUE;
626    }
627
628    PDO_HANDLE_DBH_ERR();
629    RETURN_FALSE;
630}
631/* }}} */
632
633/* {{{ proto bool PDO::rollBack()
634   roll back a transaction */
635static PHP_METHOD(PDO, rollBack)
636{
637    pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
638
639    if (zend_parse_parameters_none() == FAILURE) {
640        return;
641    }
642    PDO_CONSTRUCT_CHECK;
643
644    if (!dbh->in_txn) {
645        zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "There is no active transaction");
646        RETURN_FALSE;
647    }
648
649    if (dbh->methods->rollback(dbh TSRMLS_CC)) {
650        dbh->in_txn = 0;
651        RETURN_TRUE;
652    }
653
654    PDO_HANDLE_DBH_ERR();
655    RETURN_FALSE;
656}
657/* }}} */
658
659/* {{{ proto bool PDO::inTransaction()
660   determine if inside a transaction */
661static PHP_METHOD(PDO, inTransaction)
662{
663    pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
664
665    if (zend_parse_parameters_none() == FAILURE) {
666        return;
667    }
668    PDO_CONSTRUCT_CHECK;
669
670    if (!dbh->methods->in_transaction) {
671        RETURN_BOOL(dbh->in_txn);
672    }
673
674    RETURN_BOOL(dbh->methods->in_transaction(dbh TSRMLS_CC));
675}
676/* }}} */
677
678static int pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value TSRMLS_DC) /* {{{ */
679{
680
681#define PDO_LONG_PARAM_CHECK \
682    if (Z_TYPE_P(value) != IS_LONG && Z_TYPE_P(value) != IS_STRING && Z_TYPE_P(value) != IS_FALSE && Z_TYPE_P(value) != IS_TRUE) { \
683        pdo_raise_impl_error(dbh, NULL, "HY000", "attribute value must be an integer" TSRMLS_CC); \
684        PDO_HANDLE_DBH_ERR(); \
685        return FAILURE; \
686    } \
687
688    switch (attr) {
689        case PDO_ATTR_ERRMODE:
690            PDO_LONG_PARAM_CHECK;
691            convert_to_long(value);
692            switch (Z_LVAL_P(value)) {
693                case PDO_ERRMODE_SILENT:
694                case PDO_ERRMODE_WARNING:
695                case PDO_ERRMODE_EXCEPTION:
696                    dbh->error_mode = Z_LVAL_P(value);
697                    return SUCCESS;
698                default:
699                    pdo_raise_impl_error(dbh, NULL, "HY000", "invalid error mode" TSRMLS_CC);
700                    PDO_HANDLE_DBH_ERR();
701                    return FAILURE;
702            }
703            return FAILURE;
704
705        case PDO_ATTR_CASE:
706            PDO_LONG_PARAM_CHECK;
707            convert_to_long(value);
708            switch (Z_LVAL_P(value)) {
709                case PDO_CASE_NATURAL:
710                case PDO_CASE_UPPER:
711                case PDO_CASE_LOWER:
712                    dbh->desired_case = Z_LVAL_P(value);
713                    return SUCCESS;
714                default:
715                    pdo_raise_impl_error(dbh, NULL, "HY000", "invalid case folding mode" TSRMLS_CC);
716                    PDO_HANDLE_DBH_ERR();
717                    return FAILURE;
718            }
719            return FAILURE;
720
721        case PDO_ATTR_ORACLE_NULLS:
722            PDO_LONG_PARAM_CHECK;
723            convert_to_long(value);
724            dbh->oracle_nulls = Z_LVAL_P(value);
725            return SUCCESS;
726
727        case PDO_ATTR_DEFAULT_FETCH_MODE:
728            if (Z_TYPE_P(value) == IS_ARRAY) {
729                zval *tmp;
730                if ((tmp = zend_hash_index_find(Z_ARRVAL_P(value), 0)) != NULL && Z_TYPE_P(tmp) == IS_LONG) {
731                    if (Z_LVAL_P(tmp) == PDO_FETCH_INTO || Z_LVAL_P(tmp) == PDO_FETCH_CLASS) {
732                        pdo_raise_impl_error(dbh, NULL, "HY000", "FETCH_INTO and FETCH_CLASS are not yet supported as default fetch modes" TSRMLS_CC);
733                        return FAILURE;
734                    }
735                }
736            } else {
737                PDO_LONG_PARAM_CHECK;
738            }
739            convert_to_long(value);
740            if (Z_LVAL_P(value) == PDO_FETCH_USE_DEFAULT) {
741                pdo_raise_impl_error(dbh, NULL, "HY000", "invalid fetch mode type" TSRMLS_CC);
742                return FAILURE;
743            }
744            dbh->default_fetch_type = Z_LVAL_P(value);
745            return SUCCESS;
746
747        case PDO_ATTR_STRINGIFY_FETCHES:
748            PDO_LONG_PARAM_CHECK;
749            convert_to_long(value);
750            dbh->stringify = Z_LVAL_P(value) ? 1 : 0;
751            return SUCCESS;
752
753        case PDO_ATTR_STATEMENT_CLASS: {
754            /* array(string classname, array(mixed ctor_args)) */
755            zend_class_entry *pce;
756            zval *item;
757
758            if (dbh->is_persistent) {
759                pdo_raise_impl_error(dbh, NULL, "HY000",
760                    "PDO::ATTR_STATEMENT_CLASS cannot be used with persistent PDO instances"
761                    TSRMLS_CC);
762                PDO_HANDLE_DBH_ERR();
763                return FAILURE;
764            }
765            if (Z_TYPE_P(value) != IS_ARRAY
766                || (item = zend_hash_index_find(Z_ARRVAL_P(value), 0)) == NULL
767                || Z_TYPE_P(item) != IS_STRING
768                || (pce = zend_lookup_class(Z_STR_P(item) TSRMLS_CC)) == NULL
769            ) {
770                pdo_raise_impl_error(dbh, NULL, "HY000",
771                    "PDO::ATTR_STATEMENT_CLASS requires format array(classname, array(ctor_args)); "
772                    "the classname must be a string specifying an existing class"
773                    TSRMLS_CC);
774                PDO_HANDLE_DBH_ERR();
775                return FAILURE;
776            }
777            if (!instanceof_function(pce, pdo_dbstmt_ce TSRMLS_CC)) {
778                pdo_raise_impl_error(dbh, NULL, "HY000",
779                    "user-supplied statement class must be derived from PDOStatement" TSRMLS_CC);
780                PDO_HANDLE_DBH_ERR();
781                return FAILURE;
782            }
783            if (pce->constructor && !(pce->constructor->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED))) {
784                pdo_raise_impl_error(dbh, NULL, "HY000",
785                    "user-supplied statement class cannot have a public constructor" TSRMLS_CC);
786                PDO_HANDLE_DBH_ERR();
787                return FAILURE;
788            }
789            dbh->def_stmt_ce = pce;
790            if (!Z_ISUNDEF(dbh->def_stmt_ctor_args)) {
791                zval_ptr_dtor(&dbh->def_stmt_ctor_args);
792                ZVAL_UNDEF(&dbh->def_stmt_ctor_args);
793            }
794            if ((item = zend_hash_index_find(Z_ARRVAL_P(value), 1)) != NULL) {
795                if (Z_TYPE_P(item) != IS_ARRAY) {
796                    pdo_raise_impl_error(dbh, NULL, "HY000",
797                        "PDO::ATTR_STATEMENT_CLASS requires format array(classname, array(ctor_args)); "
798                        "ctor_args must be an array"
799                    TSRMLS_CC);
800                    PDO_HANDLE_DBH_ERR();
801                    return FAILURE;
802                }
803                ZVAL_COPY(&dbh->def_stmt_ctor_args, item);
804            }
805            return SUCCESS;
806        }
807
808        default:
809            ;
810    }
811
812    if (!dbh->methods->set_attribute) {
813        goto fail;
814    }
815
816    PDO_DBH_CLEAR_ERR();
817    if (dbh->methods->set_attribute(dbh, attr, value TSRMLS_CC)) {
818        return SUCCESS;
819    }
820
821fail:
822    if (attr == PDO_ATTR_AUTOCOMMIT) {
823        zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "The auto-commit mode cannot be changed for this driver");
824    } else if (!dbh->methods->set_attribute) {
825        pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support setting attributes" TSRMLS_CC);
826    } else {
827        PDO_HANDLE_DBH_ERR();
828    }
829    return FAILURE;
830}
831/* }}} */
832
833/* {{{ proto bool PDO::setAttribute(long attribute, mixed value)
834   Set an attribute */
835static PHP_METHOD(PDO, setAttribute)
836{
837    pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
838    zend_long attr;
839    zval *value;
840
841    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lz", &attr, &value)) {
842        RETURN_FALSE;
843    }
844
845    PDO_DBH_CLEAR_ERR();
846    PDO_CONSTRUCT_CHECK;
847
848    if (pdo_dbh_attribute_set(dbh, attr, value TSRMLS_CC) != FAILURE) {
849        RETURN_TRUE;
850    }
851    RETURN_FALSE;
852}
853/* }}} */
854
855/* {{{ proto mixed PDO::getAttribute(long attribute)
856   Get an attribute */
857static PHP_METHOD(PDO, getAttribute)
858{
859    pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
860    zend_long attr;
861
862    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &attr)) {
863        RETURN_FALSE;
864    }
865
866    PDO_DBH_CLEAR_ERR();
867    PDO_CONSTRUCT_CHECK;
868
869    /* handle generic PDO-level attributes */
870    switch (attr) {
871        case PDO_ATTR_PERSISTENT:
872            RETURN_BOOL(dbh->is_persistent);
873
874        case PDO_ATTR_CASE:
875            RETURN_LONG(dbh->desired_case);
876
877        case PDO_ATTR_ORACLE_NULLS:
878            RETURN_LONG(dbh->oracle_nulls);
879
880        case PDO_ATTR_ERRMODE:
881            RETURN_LONG(dbh->error_mode);
882
883        case PDO_ATTR_DRIVER_NAME:
884            RETURN_STRINGL((char*)dbh->driver->driver_name, dbh->driver->driver_name_len);
885
886        case PDO_ATTR_STATEMENT_CLASS:
887            array_init(return_value);
888            add_next_index_str(return_value, zend_string_copy(dbh->def_stmt_ce->name));
889            if (!Z_ISUNDEF(dbh->def_stmt_ctor_args)) {
890                if (Z_REFCOUNTED(dbh->def_stmt_ctor_args)) Z_ADDREF(dbh->def_stmt_ctor_args);
891                add_next_index_zval(return_value, &dbh->def_stmt_ctor_args);
892            }
893            return;
894        case PDO_ATTR_DEFAULT_FETCH_MODE:
895            RETURN_LONG(dbh->default_fetch_type);
896        default:
897            break;
898    }
899
900    if (!dbh->methods->get_attribute) {
901        pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support getting attributes" TSRMLS_CC);
902        RETURN_FALSE;
903    }
904
905    switch (dbh->methods->get_attribute(dbh, attr, return_value TSRMLS_CC)) {
906        case -1:
907            PDO_HANDLE_DBH_ERR();
908            RETURN_FALSE;
909
910        case 0:
911            pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support that attribute" TSRMLS_CC);
912            RETURN_FALSE;
913
914        default:
915            return;
916    }
917}
918/* }}} */
919
920/* {{{ proto long PDO::exec(string query)
921   Execute a query that does not return a row set, returning the number of affected rows */
922static PHP_METHOD(PDO, exec)
923{
924    pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
925    char *statement;
926    size_t statement_len;
927    zend_long ret;
928
929    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &statement, &statement_len)) {
930        RETURN_FALSE;
931    }
932
933    if (!statement_len) {
934        pdo_raise_impl_error(dbh, NULL, "HY000",  "trying to execute an empty query" TSRMLS_CC);
935        RETURN_FALSE;
936    }
937    PDO_DBH_CLEAR_ERR();
938    PDO_CONSTRUCT_CHECK;
939    ret = dbh->methods->doer(dbh, statement, statement_len TSRMLS_CC);
940    if(ret == -1) {
941        PDO_HANDLE_DBH_ERR();
942        RETURN_FALSE;
943    } else {
944        RETURN_LONG(ret);
945    }
946}
947/* }}} */
948
949/* {{{ proto string PDO::lastInsertId([string seqname])
950   Returns the id of the last row that we affected on this connection.  Some databases require a sequence or table name to be passed in.  Not always meaningful. */
951static PHP_METHOD(PDO, lastInsertId)
952{
953    pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
954    char *name = NULL;
955    size_t namelen;
956
957    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &name, &namelen)) {
958        RETURN_FALSE;
959    }
960
961    PDO_DBH_CLEAR_ERR();
962    PDO_CONSTRUCT_CHECK;
963    if (!dbh->methods->last_id) {
964        pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support lastInsertId()" TSRMLS_CC);
965        RETURN_FALSE;
966    } else {
967        int id_len;
968        char *id;
969        id = dbh->methods->last_id(dbh, name, (unsigned int *)&id_len TSRMLS_CC);
970        if (!id) {
971            PDO_HANDLE_DBH_ERR();
972            RETURN_FALSE;
973        } else {
974            //??? use zend_string ?
975            RETVAL_STRINGL(id, id_len);
976            efree(id);
977        }
978    }
979}
980/* }}} */
981
982/* {{{ proto string PDO::errorCode()
983   Fetch the error code associated with the last operation on the database handle */
984static PHP_METHOD(PDO, errorCode)
985{
986    pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
987
988    if (zend_parse_parameters_none() == FAILURE) {
989        return;
990    }
991    PDO_CONSTRUCT_CHECK;
992
993    if (dbh->query_stmt) {
994        RETURN_STRING(dbh->query_stmt->error_code);
995    }
996
997    if (dbh->error_code[0] == '\0') {
998        RETURN_NULL();
999    }
1000
1001    /**
1002     * Making sure that we fallback to the default implementation
1003     * if the dbh->error_code is not null.
1004     */
1005    RETURN_STRING(dbh->error_code);
1006}
1007/* }}} */
1008
1009/* {{{ proto int PDO::errorInfo()
1010   Fetch extended error information associated with the last operation on the database handle */
1011static PHP_METHOD(PDO, errorInfo)
1012{
1013    int error_count;
1014    int error_count_diff     = 0;
1015    int error_expected_count = 3;
1016
1017    pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
1018
1019    if (zend_parse_parameters_none() == FAILURE) {
1020        return;
1021    }
1022
1023    PDO_CONSTRUCT_CHECK;
1024
1025    array_init(return_value);
1026
1027    if (dbh->query_stmt) {
1028        add_next_index_string(return_value, dbh->query_stmt->error_code);
1029    } else {
1030        add_next_index_string(return_value, dbh->error_code);
1031    }
1032
1033    if (dbh->methods->fetch_err) {
1034        dbh->methods->fetch_err(dbh, dbh->query_stmt, return_value TSRMLS_CC);
1035    }
1036
1037    /**
1038     * In order to be consistent, we have to make sure we add the good amount
1039     * of nulls depending on the current number of elements. We make a simple
1040     * difference and add the needed elements
1041     */
1042    error_count = zend_hash_num_elements(Z_ARRVAL_P(return_value));
1043
1044    if (error_expected_count > error_count) {
1045        int current_index;
1046
1047        error_count_diff = error_expected_count - error_count;
1048        for (current_index = 0; current_index < error_count_diff; current_index++) {
1049            add_next_index_null(return_value);
1050        }
1051    }
1052}
1053/* }}} */
1054
1055/* {{{ proto object PDO::query(string sql [, PDOStatement::setFetchMode() args])
1056   Prepare and execute $sql; returns the statement object for iteration */
1057static PHP_METHOD(PDO, query)
1058{
1059    pdo_stmt_t *stmt;
1060    char *statement;
1061    size_t statement_len;
1062    pdo_dbh_object_t *dbh_obj = Z_PDO_OBJECT_P(getThis());
1063    pdo_dbh_t *dbh = dbh_obj->inner;
1064
1065    /* Return a meaningful error when no parameters were passed */
1066    if (!ZEND_NUM_ARGS()) {
1067        zend_parse_parameters(0 TSRMLS_CC, "z|z", NULL, NULL);
1068        RETURN_FALSE;
1069    }
1070
1071    if (FAILURE == zend_parse_parameters(1 TSRMLS_CC, "s", &statement,
1072            &statement_len)) {
1073        RETURN_FALSE;
1074    }
1075
1076    PDO_DBH_CLEAR_ERR();
1077    PDO_CONSTRUCT_CHECK;
1078
1079    if (!pdo_stmt_instantiate(dbh, return_value, dbh->def_stmt_ce, &dbh->def_stmt_ctor_args TSRMLS_CC)) {
1080        pdo_raise_impl_error(dbh, NULL, "HY000", "failed to instantiate user supplied statement class" TSRMLS_CC);
1081        return;
1082    }
1083    stmt = Z_PDO_STMT_P(return_value);
1084
1085    /* unconditionally keep this for later reference */
1086    stmt->query_string = estrndup(statement, statement_len);
1087    stmt->query_stringlen = statement_len;
1088
1089    stmt->default_fetch_type = dbh->default_fetch_type;
1090    stmt->active_query_string = stmt->query_string;
1091    stmt->active_query_stringlen = statement_len;
1092    stmt->dbh = dbh;
1093    /* give it a reference to me */
1094    ZVAL_OBJ(&stmt->database_object_handle, &dbh_obj->std);
1095    Z_ADDREF(stmt->database_object_handle);
1096    /* we haven't created a lazy object yet */
1097    ZVAL_UNDEF(&stmt->lazy_object_ref);
1098
1099    if (dbh->methods->preparer(dbh, statement, statement_len, stmt, NULL TSRMLS_CC)) {
1100        PDO_STMT_CLEAR_ERR();
1101        if (ZEND_NUM_ARGS() == 1 || SUCCESS == pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAM_PASSTHRU, stmt, 1)) {
1102
1103            /* now execute the statement */
1104            PDO_STMT_CLEAR_ERR();
1105            if (stmt->methods->executer(stmt TSRMLS_CC)) {
1106                int ret = 1;
1107                if (!stmt->executed) {
1108                    if (stmt->dbh->alloc_own_columns) {
1109                        ret = pdo_stmt_describe_columns(stmt TSRMLS_CC);
1110                    }
1111                    stmt->executed = 1;
1112                }
1113                if (ret) {
1114                    pdo_stmt_construct(stmt, return_value, dbh->def_stmt_ce, &dbh->def_stmt_ctor_args TSRMLS_CC);
1115                    return;
1116                }
1117            }
1118        }
1119        /* something broke */
1120        dbh->query_stmt = stmt;
1121        ZVAL_COPY_VALUE(&dbh->query_stmt_zval, return_value);
1122        PDO_HANDLE_STMT_ERR();
1123    } else {
1124        PDO_HANDLE_DBH_ERR();
1125        zval_ptr_dtor(return_value);
1126    }
1127
1128    RETURN_FALSE;
1129}
1130/* }}} */
1131
1132/* {{{ proto string PDO::quote(string string [, int paramtype])
1133   quotes string for use in a query.  The optional paramtype acts as a hint for drivers that have alternate quoting styles.  The default value is PDO_PARAM_STR */
1134static PHP_METHOD(PDO, quote)
1135{
1136    pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
1137    char *str;
1138    size_t str_len;
1139    zend_long paramtype = PDO_PARAM_STR;
1140    char *qstr;
1141    int qlen;
1142
1143    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str, &str_len, &paramtype)) {
1144        RETURN_FALSE;
1145    }
1146
1147    PDO_DBH_CLEAR_ERR();
1148    PDO_CONSTRUCT_CHECK;
1149    if (!dbh->methods->quoter) {
1150        pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support quoting" TSRMLS_CC);
1151        RETURN_FALSE;
1152    }
1153
1154    if (dbh->methods->quoter(dbh, str, str_len, &qstr, &qlen, paramtype TSRMLS_CC)) {
1155        RETVAL_STRINGL(qstr, qlen);
1156        efree(qstr);
1157        return;
1158    }
1159    PDO_HANDLE_DBH_ERR();
1160    RETURN_FALSE;
1161}
1162/* }}} */
1163
1164/* {{{ proto int PDO::__wakeup()
1165   Prevents use of a PDO instance that has been unserialized */
1166static PHP_METHOD(PDO, __wakeup)
1167{
1168    zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "You cannot serialize or unserialize PDO instances");
1169}
1170/* }}} */
1171
1172/* {{{ proto int PDO::__sleep()
1173   Prevents serialization of a PDO instance */
1174static PHP_METHOD(PDO, __sleep)
1175{
1176    zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "You cannot serialize or unserialize PDO instances");
1177}
1178/* }}} */
1179
1180/* {{{ proto array PDO::getAvailableDrivers()
1181   Return array of available PDO drivers */
1182static PHP_METHOD(PDO, getAvailableDrivers)
1183{
1184    pdo_driver_t *pdriver;
1185
1186    if (zend_parse_parameters_none() == FAILURE) {
1187        return;
1188    }
1189
1190    array_init(return_value);
1191
1192    ZEND_HASH_FOREACH_PTR(&pdo_driver_hash, pdriver) {
1193        add_next_index_stringl(return_value, (char*)pdriver->driver_name, pdriver->driver_name_len);
1194    } ZEND_HASH_FOREACH_END();
1195}
1196/* }}} */
1197
1198/* {{{ arginfo */
1199ZEND_BEGIN_ARG_INFO_EX(arginfo_pdo___construct, 0, 0, 1)
1200    ZEND_ARG_INFO(0, dsn)
1201    ZEND_ARG_INFO(0, username)
1202    ZEND_ARG_INFO(0, passwd)
1203    ZEND_ARG_INFO(0, options) /* array */
1204ZEND_END_ARG_INFO()
1205
1206ZEND_BEGIN_ARG_INFO_EX(arginfo_pdo_prepare, 0, 0, 1)
1207    ZEND_ARG_INFO(0, statement)
1208    ZEND_ARG_INFO(0, options) /* array */
1209ZEND_END_ARG_INFO()
1210
1211ZEND_BEGIN_ARG_INFO(arginfo_pdo_setattribute, 0)
1212    ZEND_ARG_INFO(0, attribute)
1213    ZEND_ARG_INFO(0, value)
1214ZEND_END_ARG_INFO()
1215
1216ZEND_BEGIN_ARG_INFO(arginfo_pdo_getattribute, 0)
1217    ZEND_ARG_INFO(0, attribute)
1218ZEND_END_ARG_INFO()
1219
1220ZEND_BEGIN_ARG_INFO(arginfo_pdo_exec, 0)
1221    ZEND_ARG_INFO(0, query)
1222ZEND_END_ARG_INFO()
1223
1224ZEND_BEGIN_ARG_INFO_EX(arginfo_pdo_lastinsertid, 0, 0, 0)
1225    ZEND_ARG_INFO(0, seqname)
1226ZEND_END_ARG_INFO()
1227
1228ZEND_BEGIN_ARG_INFO_EX(arginfo_pdo_quote, 0, 0, 1)
1229    ZEND_ARG_INFO(0, string)
1230    ZEND_ARG_INFO(0, paramtype)
1231ZEND_END_ARG_INFO()
1232
1233ZEND_BEGIN_ARG_INFO(arginfo_pdo__void, 0)
1234ZEND_END_ARG_INFO()
1235/* }}} */
1236
1237const zend_function_entry pdo_dbh_functions[] = /* {{{ */ {
1238    ZEND_MALIAS(PDO, __construct, dbh_constructor,  arginfo_pdo___construct,    ZEND_ACC_PUBLIC)
1239    PHP_ME(PDO, prepare,                arginfo_pdo_prepare,        ZEND_ACC_PUBLIC)
1240    PHP_ME(PDO, beginTransaction,       arginfo_pdo__void,         ZEND_ACC_PUBLIC)
1241    PHP_ME(PDO, commit,                 arginfo_pdo__void,         ZEND_ACC_PUBLIC)
1242    PHP_ME(PDO, rollBack,               arginfo_pdo__void,         ZEND_ACC_PUBLIC)
1243    PHP_ME(PDO, inTransaction,          arginfo_pdo__void,         ZEND_ACC_PUBLIC)
1244    PHP_ME(PDO, setAttribute,   arginfo_pdo_setattribute,   ZEND_ACC_PUBLIC)
1245    PHP_ME(PDO, exec,           arginfo_pdo_exec,       ZEND_ACC_PUBLIC)
1246    PHP_ME(PDO, query,          NULL,                   ZEND_ACC_PUBLIC)
1247    PHP_ME(PDO, lastInsertId,   arginfo_pdo_lastinsertid,   ZEND_ACC_PUBLIC)
1248    PHP_ME(PDO, errorCode,              arginfo_pdo__void,         ZEND_ACC_PUBLIC)
1249    PHP_ME(PDO, errorInfo,              arginfo_pdo__void,         ZEND_ACC_PUBLIC)
1250    PHP_ME(PDO, getAttribute,   arginfo_pdo_getattribute,   ZEND_ACC_PUBLIC)
1251    PHP_ME(PDO, quote,          arginfo_pdo_quote,      ZEND_ACC_PUBLIC)
1252    PHP_ME(PDO, __wakeup,               arginfo_pdo__void,         ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
1253    PHP_ME(PDO, __sleep,                arginfo_pdo__void,         ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
1254    PHP_ME(PDO, getAvailableDrivers,    arginfo_pdo__void,         ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
1255    {NULL, NULL, NULL}
1256};
1257/* }}} */
1258
1259static void cls_method_dtor(zval *el) /* {{{ */ {
1260    zend_function *func = (zend_function*)Z_PTR_P(el);
1261    if (func->common.function_name) {
1262        zend_string_release(func->common.function_name);
1263    }
1264    efree(func);
1265}
1266/* }}} */
1267
1268/* {{{ overloaded object handlers for PDO class */
1269int pdo_hash_methods(pdo_dbh_object_t *dbh_obj, int kind TSRMLS_DC)
1270{
1271    const zend_function_entry *funcs;
1272    zend_function func;
1273    zend_internal_function *ifunc = (zend_internal_function*)&func;
1274    size_t namelen;
1275    char *lc_name;
1276    pdo_dbh_t *dbh = dbh_obj->inner;
1277
1278    if (!dbh || !dbh->methods || !dbh->methods->get_driver_methods) {
1279        return 0;
1280    }
1281    funcs = dbh->methods->get_driver_methods(dbh, kind TSRMLS_CC);
1282    if (!funcs) {
1283        return 0;
1284    }
1285
1286    if (!(dbh->cls_methods[kind] = pemalloc(sizeof(HashTable), dbh->is_persistent))) {
1287        php_error_docref(NULL TSRMLS_CC, E_ERROR, "out of memory while allocating PDO methods.");
1288    }
1289    zend_hash_init_ex(dbh->cls_methods[kind], 8, NULL, cls_method_dtor, dbh->is_persistent, 0);
1290
1291    while (funcs->fname) {
1292        ifunc->type = ZEND_INTERNAL_FUNCTION;
1293        ifunc->handler = funcs->handler;
1294        ifunc->function_name = zend_string_init(funcs->fname, strlen(funcs->fname), 0);
1295        ifunc->scope = dbh_obj->std.ce;
1296        ifunc->prototype = NULL;
1297        if (funcs->flags) {
1298            ifunc->fn_flags = funcs->flags | ZEND_ACC_NEVER_CACHE;
1299        } else {
1300            ifunc->fn_flags = ZEND_ACC_PUBLIC | ZEND_ACC_NEVER_CACHE;
1301        }
1302        if (funcs->arg_info) {
1303            zend_internal_function_info *info = (zend_internal_function_info*)funcs->arg_info;
1304
1305            ifunc->arg_info = (zend_arg_info*)funcs->arg_info + 1;
1306            ifunc->num_args = funcs->num_args;
1307            if (info->required_num_args == -1) {
1308                ifunc->required_num_args = funcs->num_args;
1309            } else {
1310                ifunc->required_num_args = info->required_num_args;
1311            }
1312            if (info->return_reference) {
1313                ifunc->fn_flags |= ZEND_ACC_RETURN_REFERENCE;
1314            }
1315            if (funcs->arg_info[funcs->num_args].is_variadic) {
1316                ifunc->fn_flags |= ZEND_ACC_VARIADIC;
1317            }
1318        } else {
1319            ifunc->arg_info = NULL;
1320            ifunc->num_args = 0;
1321            ifunc->required_num_args = 0;
1322        }
1323        namelen = strlen(funcs->fname);
1324        lc_name = emalloc(namelen+1);
1325        zend_str_tolower_copy(lc_name, funcs->fname, namelen);
1326        zend_hash_str_add_mem(dbh->cls_methods[kind], lc_name, namelen, &func, sizeof(func));
1327        efree(lc_name);
1328        funcs++;
1329    }
1330
1331    return 1;
1332}
1333
1334static union _zend_function *dbh_method_get(zend_object **object, zend_string *method_name, const zval *key TSRMLS_DC)
1335{
1336    zend_function *fbc = NULL;
1337    pdo_dbh_object_t *dbh_obj = php_pdo_dbh_fetch_object(*object);
1338    zend_string *lc_method_name;
1339
1340    lc_method_name = zend_string_init(method_name->val, method_name->len, 0);
1341    zend_str_tolower_copy(lc_method_name->val, method_name->val, method_name->len);
1342
1343    if ((fbc = std_object_handlers.get_method(object, method_name, key TSRMLS_CC)) == NULL) {
1344        /* not a pre-defined method, nor a user-defined method; check
1345         * the driver specific methods */
1346        if (!dbh_obj->inner->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_DBH]) {
1347            if (!pdo_hash_methods(dbh_obj,
1348                PDO_DBH_DRIVER_METHOD_KIND_DBH TSRMLS_CC)
1349                || !dbh_obj->inner->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_DBH]) {
1350                goto out;
1351            }
1352        }
1353
1354        fbc = zend_hash_find_ptr(dbh_obj->inner->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_DBH], lc_method_name);
1355    }
1356
1357out:
1358    zend_string_release(lc_method_name);
1359    return fbc;
1360}
1361
1362static int dbh_compare(zval *object1, zval *object2 TSRMLS_DC)
1363{
1364    return -1;
1365}
1366
1367static zend_object_handlers pdo_dbh_object_handlers;
1368static void pdo_dbh_free_storage(zend_object *std TSRMLS_DC);
1369
1370void pdo_dbh_init(TSRMLS_D)
1371{
1372    zend_class_entry ce;
1373
1374    INIT_CLASS_ENTRY(ce, "PDO", pdo_dbh_functions);
1375    pdo_dbh_ce = zend_register_internal_class(&ce TSRMLS_CC);
1376    pdo_dbh_ce->create_object = pdo_dbh_new;
1377
1378    memcpy(&pdo_dbh_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
1379    pdo_dbh_object_handlers.offset = XtOffsetOf(pdo_dbh_object_t, std);
1380    pdo_dbh_object_handlers.dtor_obj = zend_objects_destroy_object;
1381    pdo_dbh_object_handlers.free_obj = pdo_dbh_free_storage;
1382    pdo_dbh_object_handlers.get_method = dbh_method_get;
1383    pdo_dbh_object_handlers.compare_objects = dbh_compare;
1384
1385    REGISTER_PDO_CLASS_CONST_LONG("PARAM_BOOL", (zend_long)PDO_PARAM_BOOL);
1386    REGISTER_PDO_CLASS_CONST_LONG("PARAM_NULL", (zend_long)PDO_PARAM_NULL);
1387    REGISTER_PDO_CLASS_CONST_LONG("PARAM_INT",  (zend_long)PDO_PARAM_INT);
1388    REGISTER_PDO_CLASS_CONST_LONG("PARAM_STR",  (zend_long)PDO_PARAM_STR);
1389    REGISTER_PDO_CLASS_CONST_LONG("PARAM_LOB",  (zend_long)PDO_PARAM_LOB);
1390    REGISTER_PDO_CLASS_CONST_LONG("PARAM_STMT", (zend_long)PDO_PARAM_STMT);
1391    REGISTER_PDO_CLASS_CONST_LONG("PARAM_INPUT_OUTPUT", (zend_long)PDO_PARAM_INPUT_OUTPUT);
1392
1393    REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_ALLOC",        (zend_long)PDO_PARAM_EVT_ALLOC);
1394    REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_FREE",         (zend_long)PDO_PARAM_EVT_FREE);
1395    REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_EXEC_PRE",     (zend_long)PDO_PARAM_EVT_EXEC_PRE);
1396    REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_EXEC_POST",    (zend_long)PDO_PARAM_EVT_EXEC_POST);
1397    REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_FETCH_PRE",    (zend_long)PDO_PARAM_EVT_FETCH_PRE);
1398    REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_FETCH_POST",   (zend_long)PDO_PARAM_EVT_FETCH_POST);
1399    REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_NORMALIZE",    (zend_long)PDO_PARAM_EVT_NORMALIZE);
1400
1401    REGISTER_PDO_CLASS_CONST_LONG("FETCH_LAZY", (zend_long)PDO_FETCH_LAZY);
1402    REGISTER_PDO_CLASS_CONST_LONG("FETCH_ASSOC", (zend_long)PDO_FETCH_ASSOC);
1403    REGISTER_PDO_CLASS_CONST_LONG("FETCH_NUM",  (zend_long)PDO_FETCH_NUM);
1404    REGISTER_PDO_CLASS_CONST_LONG("FETCH_BOTH", (zend_long)PDO_FETCH_BOTH);
1405    REGISTER_PDO_CLASS_CONST_LONG("FETCH_OBJ",  (zend_long)PDO_FETCH_OBJ);
1406    REGISTER_PDO_CLASS_CONST_LONG("FETCH_BOUND", (zend_long)PDO_FETCH_BOUND);
1407    REGISTER_PDO_CLASS_CONST_LONG("FETCH_COLUMN", (zend_long)PDO_FETCH_COLUMN);
1408    REGISTER_PDO_CLASS_CONST_LONG("FETCH_CLASS", (zend_long)PDO_FETCH_CLASS);
1409    REGISTER_PDO_CLASS_CONST_LONG("FETCH_INTO", (zend_long)PDO_FETCH_INTO);
1410    REGISTER_PDO_CLASS_CONST_LONG("FETCH_FUNC", (zend_long)PDO_FETCH_FUNC);
1411    REGISTER_PDO_CLASS_CONST_LONG("FETCH_GROUP", (zend_long)PDO_FETCH_GROUP);
1412    REGISTER_PDO_CLASS_CONST_LONG("FETCH_UNIQUE", (zend_long)PDO_FETCH_UNIQUE);
1413    REGISTER_PDO_CLASS_CONST_LONG("FETCH_KEY_PAIR", (zend_long)PDO_FETCH_KEY_PAIR);
1414    REGISTER_PDO_CLASS_CONST_LONG("FETCH_CLASSTYPE", (zend_long)PDO_FETCH_CLASSTYPE);
1415
1416#if PHP_VERSION_ID >= 50100
1417    REGISTER_PDO_CLASS_CONST_LONG("FETCH_SERIALIZE",(zend_long)PDO_FETCH_SERIALIZE);
1418#endif
1419    REGISTER_PDO_CLASS_CONST_LONG("FETCH_PROPS_LATE", (zend_long)PDO_FETCH_PROPS_LATE);
1420    REGISTER_PDO_CLASS_CONST_LONG("FETCH_NAMED", (zend_long)PDO_FETCH_NAMED);
1421
1422    REGISTER_PDO_CLASS_CONST_LONG("ATTR_AUTOCOMMIT",    (zend_long)PDO_ATTR_AUTOCOMMIT);
1423    REGISTER_PDO_CLASS_CONST_LONG("ATTR_PREFETCH",      (zend_long)PDO_ATTR_PREFETCH);
1424    REGISTER_PDO_CLASS_CONST_LONG("ATTR_TIMEOUT",       (zend_long)PDO_ATTR_TIMEOUT);
1425    REGISTER_PDO_CLASS_CONST_LONG("ATTR_ERRMODE",       (zend_long)PDO_ATTR_ERRMODE);
1426    REGISTER_PDO_CLASS_CONST_LONG("ATTR_SERVER_VERSION",    (zend_long)PDO_ATTR_SERVER_VERSION);
1427    REGISTER_PDO_CLASS_CONST_LONG("ATTR_CLIENT_VERSION",    (zend_long)PDO_ATTR_CLIENT_VERSION);
1428    REGISTER_PDO_CLASS_CONST_LONG("ATTR_SERVER_INFO",       (zend_long)PDO_ATTR_SERVER_INFO);
1429    REGISTER_PDO_CLASS_CONST_LONG("ATTR_CONNECTION_STATUS",     (zend_long)PDO_ATTR_CONNECTION_STATUS);
1430    REGISTER_PDO_CLASS_CONST_LONG("ATTR_CASE",          (zend_long)PDO_ATTR_CASE);
1431    REGISTER_PDO_CLASS_CONST_LONG("ATTR_CURSOR_NAME",   (zend_long)PDO_ATTR_CURSOR_NAME);
1432    REGISTER_PDO_CLASS_CONST_LONG("ATTR_CURSOR",        (zend_long)PDO_ATTR_CURSOR);
1433    REGISTER_PDO_CLASS_CONST_LONG("ATTR_ORACLE_NULLS",  (zend_long)PDO_ATTR_ORACLE_NULLS);
1434    REGISTER_PDO_CLASS_CONST_LONG("ATTR_PERSISTENT",    (zend_long)PDO_ATTR_PERSISTENT);
1435    REGISTER_PDO_CLASS_CONST_LONG("ATTR_STATEMENT_CLASS",       (zend_long)PDO_ATTR_STATEMENT_CLASS);
1436    REGISTER_PDO_CLASS_CONST_LONG("ATTR_FETCH_TABLE_NAMES",     (zend_long)PDO_ATTR_FETCH_TABLE_NAMES);
1437    REGISTER_PDO_CLASS_CONST_LONG("ATTR_FETCH_CATALOG_NAMES",       (zend_long)PDO_ATTR_FETCH_CATALOG_NAMES);
1438    REGISTER_PDO_CLASS_CONST_LONG("ATTR_DRIVER_NAME",       (zend_long)PDO_ATTR_DRIVER_NAME);
1439    REGISTER_PDO_CLASS_CONST_LONG("ATTR_STRINGIFY_FETCHES", (zend_long)PDO_ATTR_STRINGIFY_FETCHES);
1440    REGISTER_PDO_CLASS_CONST_LONG("ATTR_MAX_COLUMN_LEN", (zend_long)PDO_ATTR_MAX_COLUMN_LEN);
1441    REGISTER_PDO_CLASS_CONST_LONG("ATTR_EMULATE_PREPARES", (zend_long)PDO_ATTR_EMULATE_PREPARES);
1442    REGISTER_PDO_CLASS_CONST_LONG("ATTR_DEFAULT_FETCH_MODE", (zend_long)PDO_ATTR_DEFAULT_FETCH_MODE);
1443
1444    REGISTER_PDO_CLASS_CONST_LONG("ERRMODE_SILENT", (zend_long)PDO_ERRMODE_SILENT);
1445    REGISTER_PDO_CLASS_CONST_LONG("ERRMODE_WARNING",    (zend_long)PDO_ERRMODE_WARNING);
1446    REGISTER_PDO_CLASS_CONST_LONG("ERRMODE_EXCEPTION",  (zend_long)PDO_ERRMODE_EXCEPTION);
1447
1448    REGISTER_PDO_CLASS_CONST_LONG("CASE_NATURAL",   (zend_long)PDO_CASE_NATURAL);
1449    REGISTER_PDO_CLASS_CONST_LONG("CASE_LOWER", (zend_long)PDO_CASE_LOWER);
1450    REGISTER_PDO_CLASS_CONST_LONG("CASE_UPPER", (zend_long)PDO_CASE_UPPER);
1451
1452    REGISTER_PDO_CLASS_CONST_LONG("NULL_NATURAL",   (zend_long)PDO_NULL_NATURAL);
1453    REGISTER_PDO_CLASS_CONST_LONG("NULL_EMPTY_STRING",  (zend_long)PDO_NULL_EMPTY_STRING);
1454    REGISTER_PDO_CLASS_CONST_LONG("NULL_TO_STRING", (zend_long)PDO_NULL_TO_STRING);
1455
1456    REGISTER_PDO_CLASS_CONST_STRING("ERR_NONE", PDO_ERR_NONE);
1457
1458    REGISTER_PDO_CLASS_CONST_LONG("FETCH_ORI_NEXT", (zend_long)PDO_FETCH_ORI_NEXT);
1459    REGISTER_PDO_CLASS_CONST_LONG("FETCH_ORI_PRIOR", (zend_long)PDO_FETCH_ORI_PRIOR);
1460    REGISTER_PDO_CLASS_CONST_LONG("FETCH_ORI_FIRST", (zend_long)PDO_FETCH_ORI_FIRST);
1461    REGISTER_PDO_CLASS_CONST_LONG("FETCH_ORI_LAST", (zend_long)PDO_FETCH_ORI_LAST);
1462    REGISTER_PDO_CLASS_CONST_LONG("FETCH_ORI_ABS", (zend_long)PDO_FETCH_ORI_ABS);
1463    REGISTER_PDO_CLASS_CONST_LONG("FETCH_ORI_REL", (zend_long)PDO_FETCH_ORI_REL);
1464
1465    REGISTER_PDO_CLASS_CONST_LONG("CURSOR_FWDONLY", (zend_long)PDO_CURSOR_FWDONLY);
1466    REGISTER_PDO_CLASS_CONST_LONG("CURSOR_SCROLL", (zend_long)PDO_CURSOR_SCROLL);
1467
1468#if 0
1469    REGISTER_PDO_CLASS_CONST_LONG("ERR_CANT_MAP",       (zend_long)PDO_ERR_CANT_MAP);
1470    REGISTER_PDO_CLASS_CONST_LONG("ERR_SYNTAX",         (zend_long)PDO_ERR_SYNTAX);
1471    REGISTER_PDO_CLASS_CONST_LONG("ERR_CONSTRAINT",     (zend_long)PDO_ERR_CONSTRAINT);
1472    REGISTER_PDO_CLASS_CONST_LONG("ERR_NOT_FOUND",      (zend_long)PDO_ERR_NOT_FOUND);
1473    REGISTER_PDO_CLASS_CONST_LONG("ERR_ALREADY_EXISTS",     (zend_long)PDO_ERR_ALREADY_EXISTS);
1474    REGISTER_PDO_CLASS_CONST_LONG("ERR_NOT_IMPLEMENTED",    (zend_long)PDO_ERR_NOT_IMPLEMENTED);
1475    REGISTER_PDO_CLASS_CONST_LONG("ERR_MISMATCH",       (zend_long)PDO_ERR_MISMATCH);
1476    REGISTER_PDO_CLASS_CONST_LONG("ERR_TRUNCATED",      (zend_long)PDO_ERR_TRUNCATED);
1477    REGISTER_PDO_CLASS_CONST_LONG("ERR_DISCONNECTED",   (zend_long)PDO_ERR_DISCONNECTED);
1478    REGISTER_PDO_CLASS_CONST_LONG("ERR_NO_PERM",        (zend_long)PDO_ERR_NO_PERM);
1479#endif
1480
1481}
1482
1483static void dbh_free(pdo_dbh_t *dbh, zend_bool free_persistent TSRMLS_DC)
1484{
1485    int i;
1486
1487    if (dbh->is_persistent && !free_persistent) {
1488        return;
1489    }
1490
1491    if (dbh->query_stmt) {
1492        zval_ptr_dtor(&dbh->query_stmt_zval);
1493        dbh->query_stmt = NULL;
1494    }
1495
1496    if (dbh->methods) {
1497        dbh->methods->closer(dbh TSRMLS_CC);
1498    }
1499
1500    if (dbh->data_source) {
1501        pefree((char *)dbh->data_source, dbh->is_persistent);
1502    }
1503    if (dbh->username) {
1504        pefree(dbh->username, dbh->is_persistent);
1505    }
1506    if (dbh->password) {
1507        pefree(dbh->password, dbh->is_persistent);
1508    }
1509
1510    if (dbh->persistent_id) {
1511        pefree((char *)dbh->persistent_id, dbh->is_persistent);
1512    }
1513
1514    if (!Z_ISUNDEF(dbh->def_stmt_ctor_args)) {
1515        zval_ptr_dtor(&dbh->def_stmt_ctor_args);
1516    }
1517
1518    for (i = 0; i < PDO_DBH_DRIVER_METHOD_KIND__MAX; i++) {
1519        if (dbh->cls_methods[i]) {
1520            zend_hash_destroy(dbh->cls_methods[i]);
1521            pefree(dbh->cls_methods[i], dbh->is_persistent);
1522        }
1523    }
1524
1525    pefree(dbh, dbh->is_persistent);
1526}
1527
1528static void pdo_dbh_free_storage(zend_object *std TSRMLS_DC)
1529{
1530    pdo_dbh_t *dbh = php_pdo_dbh_fetch_inner(std);
1531    if (dbh->in_txn && dbh->methods && dbh->methods->rollback) {
1532        dbh->methods->rollback(dbh TSRMLS_CC);
1533        dbh->in_txn = 0;
1534    }
1535
1536    if (dbh->is_persistent && dbh->methods && dbh->methods->persistent_shutdown) {
1537        dbh->methods->persistent_shutdown(dbh TSRMLS_CC);
1538    }
1539    zend_object_std_dtor(std TSRMLS_CC);
1540    dbh_free(dbh, 0 TSRMLS_CC);
1541}
1542
1543zend_object *pdo_dbh_new(zend_class_entry *ce TSRMLS_DC)
1544{
1545    pdo_dbh_object_t *dbh;
1546
1547    dbh = ecalloc(1, sizeof(pdo_dbh_object_t) + sizeof(zval) * (ce->default_properties_count - 1));
1548    zend_object_std_init(&dbh->std, ce TSRMLS_CC);
1549    object_properties_init(&dbh->std, ce);
1550    rebuild_object_properties(&dbh->std);
1551    dbh->inner = ecalloc(1, sizeof(pdo_dbh_t));
1552    dbh->inner->def_stmt_ce = pdo_dbstmt_ce;
1553
1554    dbh->std.handlers = &pdo_dbh_object_handlers;
1555
1556    return &dbh->std;
1557}
1558
1559/* }}} */
1560
1561ZEND_RSRC_DTOR_FUNC(php_pdo_pdbh_dtor) /* {{{ */
1562{
1563    if (res->ptr) {
1564        pdo_dbh_t *dbh = (pdo_dbh_t*)res->ptr;
1565        dbh_free(dbh, 1 TSRMLS_CC);
1566        res->ptr = NULL;
1567    }
1568}
1569/* }}} */
1570
1571/*
1572 * Local variables:
1573 * tab-width: 4
1574 * c-basic-offset: 4
1575 * End:
1576 * vim600: noet sw=4 ts=4 fdm=marker
1577 * vim<600: noet sw=4 ts=4
1578 */
1579