diff options
author | Wez Furlong <wez@php.net> | 2004-09-23 20:07:02 +0000 |
---|---|---|
committer | Wez Furlong <wez@php.net> | 2004-09-23 20:07:02 +0000 |
commit | 7937f0a2295c7daa89d23b80f221e17cf6ab01af (patch) | |
tree | c1eeacb431133d64960cbc048dbdb68aac1eb09d | |
parent | 2e49a2d960d98ebc272698e98e3a2d185887cc06 (diff) | |
download | php-git-7937f0a2295c7daa89d23b80f221e17cf6ab01af.tar.gz |
Implement persistent connections
$dbh->exec --> $dbh->query
-rwxr-xr-x | ext/pdo/TODO | 5 | ||||
-rwxr-xr-x | ext/pdo/pdo.c | 13 | ||||
-rwxr-xr-x | ext/pdo/pdo_dbh.c | 147 | ||||
-rwxr-xr-x | ext/pdo/php_pdo_driver.h | 13 | ||||
-rwxr-xr-x | ext/pdo/php_pdo_int.h | 2 |
5 files changed, 141 insertions, 39 deletions
diff --git a/ext/pdo/TODO b/ext/pdo/TODO index b30eab16c2..f522d154dd 100755 --- a/ext/pdo/TODO +++ b/ext/pdo/TODO @@ -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... diff --git a/ext/pdo/pdo.c b/ext/pdo/pdo.c index 295e8f3c82..1f0f1b0784 100755 --- a/ext/pdo/pdo.c +++ b/ext/pdo/pdo.c @@ -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); diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 18041c39db..97c6ce45b3 100755 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -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 diff --git a/ext/pdo/php_pdo_driver.h b/ext/pdo/php_pdo_driver.h index f80efd2d8f..00d7d5ee7e 100755 --- a/ext/pdo/php_pdo_driver.h +++ b/ext/pdo/php_pdo_driver.h @@ -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 */ diff --git a/ext/pdo/php_pdo_int.h b/ext/pdo/php_pdo_int.h index 882ee904e1..8d2be68450 100755 --- a/ext/pdo/php_pdo_int.h +++ b/ext/pdo/php_pdo_int.h @@ -23,10 +23,12 @@ /* 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[]; |