]> granicus.if.org Git - php/commitdiff
Implement persistent connections
authorWez Furlong <wez@php.net>
Thu, 23 Sep 2004 20:07:02 +0000 (20:07 +0000)
committerWez Furlong <wez@php.net>
Thu, 23 Sep 2004 20:07:02 +0000 (20:07 +0000)
$dbh->exec --> $dbh->query

ext/pdo/TODO
ext/pdo/pdo.c
ext/pdo/pdo_dbh.c
ext/pdo/php_pdo_driver.h
ext/pdo/php_pdo_int.h

index b30eab16c2f579cdf140a65729d020bce14125fe..f522d154dd361f5bc02740d0926e604c07a5898c 100755 (executable)
@@ -4,19 +4,14 @@ In no particular order:
 
 Low-level:
 
-- Scanner for :named placeholders and prepare()/execute() emulation (George)
 - $dbh->quote()
-- Exceptions and unified error API
 - Scrollable cursors
 - meta data from driver, such as server version and supported features
 - field meta data from statement handles
-- $dbh->exec() for one-shot SQL
 - LOB support via Streams API
-- persistent handles
 - iterator support
 
 Not-so-low-level:
-- hash key case folding for portabilitiy, ala sqlite.assoc_case, but not using an ini option.
 - fetchAll(), single row, single column fetches etc.
 
 Could be more...
index 295e8f3c823687b886ba5b87fc93397f0023bca3..1f0f1b07847944ecebb59b10e8b626d0b2aa2ca9 100755 (executable)
@@ -43,6 +43,11 @@ static HashTable pdo_driver_hash;
 /* we use persistent resources for the driver connection stuff */
 static int le_ppdo;
 
+int php_pdo_list_entry(void)
+{
+       return le_ppdo;
+}
+
 /* for exceptional circumstances */
 zend_class_entry *pdo_exception_ce;
 
@@ -94,7 +99,8 @@ static void php_pdo_init_globals(zend_pdo_globals *pdo_globals)
 PDO_API int php_pdo_register_driver(pdo_driver_t *driver)
 {
        if (driver->api_version != PDO_DRIVER_API) {
-               zend_error(E_ERROR, "failed api version check");
+               zend_error(E_ERROR, "PDO: driver %s requires PDO API version %d; this is PDO version %d",
+                       driver->driver_name, driver->api_version, PDO_DRIVER_API);
                return FAILURE;
        }
        if (!zend_hash_exists(&module_registry, "pdo", sizeof("pdo"))) {
@@ -204,6 +210,9 @@ PHP_MINIT_FUNCTION(pdo)
 
        zend_hash_init(&pdo_driver_hash, 0, NULL, NULL, 1);
 
+       le_ppdo = zend_register_list_destructors_ex(NULL, php_pdo_pdbh_dtor,
+               "PDO persistent database", module_number);
+
        REGISTER_LONG_CONSTANT("PDO_PARAM_NULL", (long)PDO_PARAM_NULL,  CONST_CS|CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("PDO_PARAM_INT",  (long)PDO_PARAM_INT,   CONST_CS|CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("PDO_PARAM_STR",  (long)PDO_PARAM_STR,   CONST_CS|CONST_PERSISTENT);
@@ -230,6 +239,7 @@ PHP_MINIT_FUNCTION(pdo)
        REGISTER_LONG_CONSTANT("PDO_ATTR_CURSOR_NAME",  (long)PDO_ATTR_CURSOR_NAME,             CONST_CS|CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("PDO_ATTR_CURSOR",               (long)PDO_ATTR_CURSOR,          CONST_CS|CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("PDO_ATTR_ORACLE_NULLS", (long)PDO_ATTR_ORACLE_NULLS,    CONST_CS|CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("PDO_ATTR_PERSISTENT",   (long)PDO_ATTR_PERSISTENT,              CONST_CS|CONST_PERSISTENT);
        
        REGISTER_LONG_CONSTANT("PDO_ERRMODE_SILENT",    (long)PDO_ERRMODE_SILENT,               CONST_CS|CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("PDO_ERRMODE_WARNING",   (long)PDO_ERRMODE_WARNING,              CONST_CS|CONST_PERSISTENT);
@@ -249,6 +259,7 @@ PHP_MINIT_FUNCTION(pdo)
        REGISTER_LONG_CONSTANT("PDO_ERR_MISMATCH",                      (long)PDO_ERR_MISMATCH,         CONST_CS|CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("PDO_ERR_TRUNCATED",             (long)PDO_ERR_TRUNCATED,                CONST_CS|CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("PDO_ERR_DISCONNECTED",          (long)PDO_ERR_DISCONNECTED,             CONST_CS|CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("PDO_ERR_NO_PERM",                       (long)PDO_ERR_NO_PERM,                  CONST_CS|CONST_PERSISTENT);
 
        INIT_CLASS_ENTRY(ce, "PDOException", NULL);
        pdo_exception_ce = zend_register_internal_class_ex(&ce, zend_exception_get_default(), NULL TSRMLS_CC);
index 18041c39dbd311dedb6f58be91a9e36ccbb92fe0..97c6ce45b395e0495fecdac95582230f3f3d93b6 100755 (executable)
@@ -214,28 +214,107 @@ static PHP_FUNCTION(dbh_constructor)
        
        dbh = (pdo_dbh_t *) zend_object_store_get_object(object TSRMLS_CC);
 
-       if (dbh == NULL) {
-               /* need this check for persistent allocations */
-               zend_throw_exception_ex(php_pdo_get_exception(), PDO_ERR_NONE TSRMLS_CC, "out of memory!?");
+       /* is this supposed to be a persistent connection ? */
+       if (driver_options) {
+               zval **v;
+               int plen;
+               char *hashkey = NULL;
+               list_entry *le;
+               pdo_dbh_t *pdbh = NULL;
+
+               if (SUCCESS == zend_hash_index_find(Z_ARRVAL_P(driver_options), PDO_ATTR_PERSISTENT, (void**)&v)) {
+                       if (Z_TYPE_PP(v) == IS_STRING) {
+                               /* user specified key */
+                               plen = spprintf(&hashkey, 0, "PDO:DBH:DSN=%s:%s:%s:%s", data_source,
+                                               username ? username : "",
+                                               password ? password : "",
+                                               Z_STRVAL_PP(v));
+                               is_persistent = 1;
+                       } else {
+                               convert_to_long_ex(v);
+                               is_persistent = Z_LVAL_PP(v) ? 1 : 0;
+                               plen = spprintf(&hashkey, 0, "PDO:DBH:DSN=%s:%s:%s", data_source,
+                                               username ? username : "",
+                                               password ? password : "");
+                       }
+               }
+
+               /* let's see if we have one cached.... */
+               if (is_persistent && SUCCESS == zend_hash_find(&EG(persistent_list), hashkey, plen+1, (void*)&le)) {
+                       if (Z_TYPE_P(le) == php_pdo_list_entry()) {
+                               pdbh = (pdo_dbh_t*)le->ptr;
+
+                               /* is the connection still alive ? */
+                               if (pdbh->methods->check_liveness && FAILURE == (pdbh->methods->check_liveness)(pdbh TSRMLS_CC)) {
+                                       /* nope... need to kill it */
+                                       pdbh = NULL;
+                               }
+
+                       }
+               }
+
+               if (is_persistent && !pdbh) {
+                       /* need a brand new pdbh */
+                       pdbh = pecalloc(1, sizeof(*pdbh), 1);
+
+                       if (!pdbh) {
+                               php_error_docref(NULL TSRMLS_CC, E_ERROR, "out of memory while allocating PDO handle");
+                               /* NOTREACHED */
+                       }
+                       
+                       pdbh->is_persistent = 1;
+                       pdbh->persistent_id = pemalloc(plen + 1, 1);
+                       memcpy((char *)pdbh->persistent_id, hashkey, plen+1);
+                       pdbh->persistent_id_len = plen+1;
+                       pdbh->refcount = 1;
+               }
+
+               if (pdbh) {
+                       /* let's copy the emalloc bits over from the other handle */
+                       pdbh->ce = dbh->ce;
+                       pdbh->properties = dbh->properties;
+                       /* kill the non-persistent thingamy */
+                       efree(dbh);
+                       /* switch over to the persistent one */
+                       dbh = pdbh;
+                       zend_object_store_set_object(object, dbh TSRMLS_CC);
+                       dbh->refcount++;
+               }
+
+               if (hashkey) {
+                       efree(hashkey);
+               }
        }
-       dbh->is_persistent = is_persistent;
+       
        dbh->data_source_len = strlen(colon + 1);
-       /* when persistent stuff is done, we should check the return values here
-        * too */
        dbh->data_source = (const char*)pestrdup(colon + 1, is_persistent);
        dbh->username = username ? pestrdup(username, is_persistent) : NULL;
        dbh->password = password ? pestrdup(password, is_persistent) : NULL;
 
        dbh->auto_commit = pdo_attr_lval(driver_options, PDO_ATTR_AUTOCOMMIT, 1 TSRMLS_CC);
+
+       if (!dbh->data_source || !dbh->username || !dbh->password) {
+               php_error_docref(NULL TSRMLS_CC, E_ERROR, "out of memory");
+       }
        
        if (driver->db_handle_factory(dbh, driver_options TSRMLS_CC)) {
                /* all set */
 
                if (is_persistent) {
+                       list_entry le;
+
                        /* register in the persistent list etc. */
                        /* we should also need to replace the object store entry,
                           since it was created with emalloc */
-                       ;
+
+                       le.type = php_pdo_list_entry();
+                       le.ptr = dbh;
+
+                       if (FAILURE == zend_hash_update(&EG(persistent_list),
+                                       (char*)dbh->persistent_id, dbh->persistent_id_len, (void*)&le,
+                                       sizeof(le), NULL)) {
+                               php_error_docref(NULL TSRMLS_CC, E_ERROR, "Failed to register persistent entry");
+                       }
                }
                return; 
        }
@@ -465,9 +544,9 @@ static PHP_METHOD(PDO, getAttribute)
 }
 /* }}} */
 
-/* {{{ proto long PDO::exec(string query)
+/* {{{ proto long PDO::query(string query)
    Execute a query that does not return a row set, returning the number of affected rows */
-static PHP_METHOD(PDO, exec)
+static PHP_METHOD(PDO, query)
 {
        pdo_dbh_t *dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
        char *statement;
@@ -553,7 +632,7 @@ function_entry pdo_dbh_functions[] = {
        PHP_ME(PDO, commit,                     NULL,                                   ZEND_ACC_PUBLIC)
        PHP_ME(PDO, rollBack,           NULL,                                   ZEND_ACC_PUBLIC)
        PHP_ME(PDO, setAttribute,       NULL,                                   ZEND_ACC_PUBLIC)
-       PHP_ME(PDO, exec,                       NULL,                                   ZEND_ACC_PUBLIC)
+       PHP_ME(PDO, query,                      NULL,                                   ZEND_ACC_PUBLIC)
        PHP_ME(PDO, lastInsertId,       NULL,                                   ZEND_ACC_PUBLIC)
        PHP_ME(PDO, errorCode,          NULL,                                   ZEND_ACC_PUBLIC)
        PHP_ME(PDO, errorInfo,          NULL,                                   ZEND_ACC_PUBLIC)
@@ -697,26 +776,11 @@ static zend_object_handlers pdo_dbh_object_handlers = {
        NULL
 };
 
-
-static void pdo_dbh_free_storage(zend_object *object TSRMLS_DC)
+static void dbh_free(pdo_dbh_t *dbh TSRMLS_DC)
 {
-       pdo_dbh_t *dbh = (pdo_dbh_t*)object;
-
-       if (!dbh) {
+       if (--dbh->refcount)
                return;
-       }
 
-       if (dbh->is_persistent) {
-               /* XXX: don't really free it, just delete the rsrc id */
-               return;
-       }
-       if(dbh->properties) {
-               zend_hash_destroy(dbh->properties);
-               /* XXX: this should be probably changed to pefree() when persistent
-                * connections will be properly implemented
-                * */
-               efree(dbh->properties); 
-       }
        if (dbh->methods) {
                dbh->methods->closer(dbh TSRMLS_CC);
        }
@@ -734,14 +798,32 @@ static void pdo_dbh_free_storage(zend_object *object TSRMLS_DC)
        pefree(dbh, dbh->is_persistent);
 }
 
+static void pdo_dbh_free_storage(zend_object *object TSRMLS_DC)
+{
+       pdo_dbh_t *dbh = (pdo_dbh_t*)object;
+       if (!dbh) {
+               return;
+       }
+
+       if (dbh->properties) {
+               zend_hash_destroy(dbh->properties);
+               efree(dbh->properties);
+               dbh->properties = NULL;
+       }
+
+       if (!dbh->is_persistent) {
+               dbh_free(dbh TSRMLS_CC);
+       }
+}
+
 zend_object_value pdo_dbh_new(zend_class_entry *ce TSRMLS_DC)
 {
        zend_object_value retval;
        pdo_dbh_t *dbh;
-
        dbh = emalloc(sizeof(*dbh));
        memset(dbh, 0, sizeof(*dbh));
        dbh->ce = ce;
+       dbh->refcount = 1;
        ALLOC_HASHTABLE(dbh->properties);
        zend_hash_init(dbh->properties, 0, NULL, ZVAL_PTR_DTOR, 0);
        
@@ -757,6 +839,15 @@ zend_object_value pdo_dbh_new(zend_class_entry *ce TSRMLS_DC)
 
 /* }}} */
 
+ZEND_RSRC_DTOR_FUNC(php_pdo_pdbh_dtor)
+{
+       if (rsrc->ptr) {
+               pdo_dbh_t *dbh = (pdo_dbh_t*)rsrc->ptr;
+               dbh_free(dbh TSRMLS_CC);
+               rsrc->ptr = NULL;
+       }
+}
+
 /*
  * Local variables:
  * tab-width: 4
index f80efd2d8fff161d7c387df4828a7148703ebb51..00d7d5ee7ed2bcebed2781bc76607ae76821e9d3 100755 (executable)
@@ -35,7 +35,7 @@ struct pdo_bound_param_data;
 # define FALSE 0
 #endif
 
-#define PDO_DRIVER_API 20040513
+#define PDO_DRIVER_API 20040923
 
 enum pdo_param_type {
        PDO_PARAM_NULL,
@@ -69,6 +69,7 @@ enum pdo_attribute_type {
        PDO_ATTR_CURSOR_NAME,           /* name a cursor for use in "WHERE CURRENT OF <name>" */
        PDO_ATTR_CURSOR,                        /* cursor type */
        PDO_ATTR_ORACLE_NULLS,          /* convert empty strings to NULL */
+       PDO_ATTR_PERSISTENT,            /* pconnect style connection */
 
        /* this defines the start of the range for driver specific options.
         * Drivers should define their own attribute constants beginning with this
@@ -175,6 +176,9 @@ typedef     int (*pdo_dbh_fetch_error_func)(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *
 /* fetching of attributes */
 typedef int (*pdo_dbh_get_attr_func)(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC);
 
+/* checking/pinging persistent connections */
+typedef int (*pdo_dbh_check_liveness_func)(pdo_dbh_t *dbh TSRMLS_DC);
+
 struct pdo_dbh_methods {
        pdo_dbh_close_func              closer;
        pdo_dbh_prepare_func    preparer;
@@ -187,6 +191,7 @@ struct pdo_dbh_methods {
        pdo_dbh_last_id_func            last_id;
        pdo_dbh_fetch_error_func        fetch_err;
        pdo_dbh_get_attr_func           get_attribute;
+       pdo_dbh_check_liveness_func     check_liveness;
 };
 
 /* }}} */
@@ -313,12 +318,10 @@ struct _pdo_dbh_t {
 
        enum pdo_case_conversion native_case, desired_case;
 
-#if 0
        /* persistent hash key associated with this handle */
        const char *persistent_id;
-       /* and the list id associated with it */
-       int persistent_rsrc_id;
-#endif
+       int persistent_id_len;
+       unsigned int refcount;
 };
 
 /* describes a column */
index 882ee904e1c382b7bb4bd0f5b606e9bf68cd1562..8d2be6845098b705bc7781a8562bf50053be28c1 100755 (executable)
 /* Stuff private to the PDO extension and not for consumption by PDO drivers
  * */
 extern zend_class_entry *pdo_exception_ce;
+int php_pdo_list_entry(void);
 
 extern zend_object_value pdo_dbh_new(zend_class_entry *ce TSRMLS_DC);
 extern function_entry pdo_dbh_functions[];
 extern zend_class_entry *pdo_dbh_ce;
+extern ZEND_RSRC_DTOR_FUNC(php_pdo_pdbh_dtor);
 
 extern zend_object_value pdo_dbstmt_new(zend_class_entry *ce TSRMLS_DC);
 extern function_entry pdo_dbstmt_functions[];