/* 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;
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"))) {
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);
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);
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);
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;
}
}
/* }}} */
-/* {{{ 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;
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)
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);
}
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);
/* }}} */
+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
# define FALSE 0
#endif
-#define PDO_DRIVER_API 20040513
+#define PDO_DRIVER_API 20040923
enum pdo_param_type {
PDO_PARAM_NULL,
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
/* 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;
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;
};
/* }}} */
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 */