mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +02:00
Implement persistent connections
$dbh->exec --> $dbh->query
This commit is contained in:
parent
2e49a2d960
commit
7937f0a229
5 changed files with 141 additions and 39 deletions
|
@ -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...
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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[];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue