Cleaned up whitespace

Fixed bug #44372 (compilation with Oracle 10gR1 libraries)
http://bugs.php.net/bug.php?id=44372

Updated updated error number list to improve re-connection
behavior after a database restart.

Guard against potential internal list corruption after ping
removes old oci_pconnect() information.

Fix ini_set("oci8.connection_class", "abc") to get an appropriate
persistent connection.

Ping at oci8.ping_interval for oci_connect() and oci_new_connect()
when DRCP connections are used. This improves non-persistent
connection reliability if a database gets restarted.
This commit is contained in:
Christopher Jones 2008-03-12 01:25:13 +00:00
parent a25b1f6d6d
commit 7d41cd902b
4 changed files with 869 additions and 629 deletions

View file

@ -166,7 +166,7 @@ directory will contain logs of any failures.
6. DRCP and FAN Support
-----------------------
The PHP OCI8 Beta extension has support for the Oracle Database
The PHP 6 OCI8 Beta extension has support for the Oracle Database
Resident Connection Pool (DRCP) and Fast Application Notification
(FAN).
@ -205,6 +205,25 @@ contains background information on DRCP.
After building PHP with the OCI8 extension and 11g libraries, follow
these steps:
6.2.0 Important: if Oracle Database 11.1.0.6 with DRCP connections is
used, then the Oracle database patch for bug 6474441 must be
applied (see section 6.5) or a workaround below used. Without
this patch, "ORA-01000: maximum open cursors exceeded", "ORA-01001
invalid cursor" or "ORA-01002 fetch out of sequence" errors may
occur.
If the Oracle 11.1.0.6 database patch cannot be applied, one of
the following three workarounds can be used to disable statement
caching instead:
(i) Connect using Oracle dedicated or shared servers instead of DRCP.
(ii) Set PHP's oci8.statement_cache_size to 0.
(iii) Set an event in the database initialization parameter file:
event="56699 trace name context forever, level 128".
6.2.1. As a privileged database administrator, use a program like
SQL*Plus to start the connection pool in the database:
@ -298,55 +317,15 @@ To enable FAN support in PHP, after building PHP with Oracle 10gR2 or
6.3.4. Run your application, connecting to a 10gR2 or 11g database.
6.4. Changes in this release from PECL OCI8 1.3.0 Beta
6.4. Recommendations and Known Limitations
The initial release of OCI8 with DRCP and FAN support was PECL OCI8
1.3.0 Beta. This section documents differences from that release.
6.4.1 Changing Password for DRCP connections
6.4.1 Statement caching has been re-enabled.
Changing a password over DRCP connections will fail with the error
"ORA-56609: Usage not supported with DRCP". This is an documented
restriction of Oracle Database 11g.
Important: if Oracle Database 11.1.0.6 with DRCP connections is used,
then the Oracle database patch for bug 6474441 must be applied (see
section 6.5) or a workaround below used. Without this patch,
"ORA-01000: maximum open cursors exceeded", "ORA-01001 invalid cursor"
or "ORA-01002 fetch out of sequence" errors may occur.
If the Oracle 11.1.0.6 database patch cannot be applied, one of the
following three workarounds can be used to disable statement caching
instead:
(i) Connect using Oracle dedicated or shared servers instead of DRCP.
(ii) Set PHP's oci8.statement_cache_size to 0.
(iii) Set an event in the database initialization parameter file:
event="56699 trace name context forever, level 128".
6.4.2 Changing Password for non-DRCP connections has been re-enabled.
When oci_password_change() is successfully performed for non-DRCP
connections in a PHP script, subsequent connections with the new
password from this PHP instance no longer fail with "ORA-1017 invalid
username/password".
Changing a password over DRCP connections will continue to fail with
the error "ORA-56609: Usage not supported with DRCP". This is an
documented restriction of Oracle Database 11g.
6.4.3 Oci8.max_persistent setting is re-enabled.
The php.ini parameter oci8.max_persistent will limit the number of
persistent connections that each PHP process will keep open between
HTTP requests. Any further oci_pconnect() calls once this limit is
reached will be treated as oci_connect() calls.
It is still recommended that DRCP users modify the connection pool
settings of the database to control resource usage. Non-DRCP users
should consider setting oci8.persistent_timeout to close idle
connections.
6.4.4 LOGON Triggers can be used to set session properties
6.4.2 LOGON Triggers can be used to set session properties
The patch for Oracle Database 11.1.0.6 bug 6474441 (see section 6.5)
allows PHP applications with DRCP connection to use a database LOGON
@ -364,9 +343,9 @@ application code.
With DRCP there is an connection management relationship between (i)
DRCP's automatic pool expansion and reduction, (ii) PHP's persistent
connection caching, (iii) with the way LOGON triggers fire with DRCP
authentication. Because of this interplay, LOGON triggers in PHP when
DRCP is used are only recommended for setting session attributes and
not for per-PHP connection events.
authentication. Because of this interplay, LOGON triggers in PHP
(when DRCP is used) are only recommended for setting session
attributes and not for per-PHP connection events.
6.5. Patching Oracle Database 11g

View file

@ -33,7 +33,7 @@
*
* get OCI_ATTR_CHARSET_ID attr of column to detect UTF string and multiply buffer in 4 times
*
* */
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
@ -86,6 +86,7 @@ zend_class_entry *oci_coll_class_entry_ptr;
/* static protos {{{ */
static void php_oci_connection_list_dtor (zend_rsrc_list_entry * TSRMLS_DC);
static void php_oci_pconnection_list_dtor (zend_rsrc_list_entry * TSRMLS_DC);
static void php_oci_pconnection_list_np_dtor (zend_rsrc_list_entry * TSRMLS_DC);
static void php_oci_statement_list_dtor (zend_rsrc_list_entry * TSRMLS_DC);
static void php_oci_descriptor_list_dtor (zend_rsrc_list_entry * TSRMLS_DC);
static void php_oci_spool_list_dtor(zend_rsrc_list_entry *entry TSRMLS_DC);
@ -107,6 +108,7 @@ static int php_oci_create_session(php_oci_connection *connection, php_oci_spool
static int php_oci_old_create_session(php_oci_connection *connection, zstr dbname, int dbname_len, zstr username, int username_len, zstr password, int password_len, zstr new_password, int new_password_len, int session_mode, zend_uchar type TSRMLS_DC);
static php_oci_spool *php_oci_get_spool(zstr username, int username_len, zstr password, int password_len, zstr dbname, int dbname_len, int charsetid, zend_uchar type TSRMLS_DC);
static php_oci_spool *php_oci_create_spool(zstr username, int username_len, zstr password, int password_len, zstr dbname, int dbname_len, char *hash_key, int hash_key_len, int charsetid, zend_uchar type TSRMLS_DC);
static sword php_oci_ping_init(php_oci_connection *connection, OCIError *errh TSRMLS_DC);
/* }}} */
/* {{{ dynamically loadable module stuff */
@ -509,7 +511,7 @@ PHP_MINIT_FUNCTION(oci)
le_statement = zend_register_list_destructors_ex(php_oci_statement_list_dtor, NULL, "oci8 statement", module_number);
le_connection = zend_register_list_destructors_ex(php_oci_connection_list_dtor, NULL, "oci8 connection", module_number);
le_pconnection = zend_register_list_destructors_ex(NULL, php_oci_pconnection_list_dtor, "oci8 persistent connection", module_number);
le_pconnection = zend_register_list_destructors_ex(php_oci_pconnection_list_np_dtor, php_oci_pconnection_list_dtor, "oci8 persistent connection", module_number);
le_psessionpool = zend_register_list_destructors_ex(NULL, php_oci_spool_list_dtor, "oci8 persistent session pool", module_number);
le_descriptor = zend_register_list_destructors_ex(php_oci_descriptor_list_dtor, NULL, "oci8 descriptor", module_number);
#ifdef PHP_OCI8_HAVE_COLLECTIONS
@ -714,8 +716,11 @@ PHP_MINFO_FUNCTION(oci)
static void php_oci_connection_list_dtor(zend_rsrc_list_entry *entry TSRMLS_DC)
{
php_oci_connection *connection = (php_oci_connection *)entry->ptr;
if (connection) {
php_oci_connection_close(connection TSRMLS_CC);
OCI_G(num_links)--;
}
} /* }}} */
/* {{{ php_oci_pconnection_list_dtor()
@ -723,8 +728,25 @@ static void php_oci_connection_list_dtor(zend_rsrc_list_entry *entry TSRMLS_DC)
static void php_oci_pconnection_list_dtor(zend_rsrc_list_entry *entry TSRMLS_DC)
{
php_oci_connection *connection = (php_oci_connection *)entry->ptr;
if (connection) {
php_oci_connection_close(connection TSRMLS_CC);
OCI_G(num_persistent)--;
}
} /* }}} */
/* {{{ php_oci_pconnection_list_np_dtor()
Non-Persistent destructor for persistent connection - This gets invoked when
the refcount of this goes to zero in the regular list */
static void php_oci_pconnection_list_np_dtor(zend_rsrc_list_entry *entry TSRMLS_DC)
{
php_oci_connection *connection = (php_oci_connection *)entry->ptr;
/* If it is a bad connection, clean it up. This is the sole purpose of this dtor. We should ideally do a hash_del also, but this scenario is currently not possible. */
if (connection && !connection->is_open && !connection->is_stub) {
php_oci_connection_close(connection TSRMLS_CC);
OCI_G(num_persistent)--;
}
} /* }}} */
/* {{{ php_oci_statement_list_dtor()
@ -790,12 +812,6 @@ void php_oci_bind_hash_dtor(void *data)
efree(bind->array.indicators);
}
/*
if (bind->array.retcodes) {
efree(bind->array.retcodes);
}
*/
zval_ptr_dtor(&bind->zval);
}
/* }}} */
@ -1028,6 +1044,7 @@ php_oci_connection *php_oci_do_connect_ex(zstr username, int username_len, zstr
time_t timestamp;
php_oci_spool *session_pool = NULL;
zend_bool use_spool = 1; /* Default is to use client-side session pool */
zend_bool ping_done = 0;
#if HAVE_OCI_ENV_NLS_CREATE
ub2 charsetid = 0;
@ -1062,30 +1079,36 @@ php_oci_connection *php_oci_do_connect_ex(zstr username, int username_len, zstr
*/
if ((session_mode == OCI_SYSOPER) || (session_mode == OCI_SYSDBA) || (new_password_len)) {
use_spool = 0;
}
else if (UG(unicode)) {
} else if (UG(unicode)) {
/* Pre 10.1 session pool does not support unicode - bypass pool */
#ifndef HAVE_OCI_LOB_READ2 /* For finding 10.1+ client */
use_spool = 0;
#endif
}
smart_str_appendl_ex(&hashed_details, "oci8___", sizeof("oci8___") - 1, 0);
smart_str_appendl_ex(&hashed_details, "oci8***", sizeof("oci8***") - 1, 0);
smart_str_appendl_ex(&hashed_details, username.s, USTR_BYTES(type, username_len), 0);
smart_str_appendl_ex(&hashed_details, "__", sizeof("__") - 1, 0);
smart_str_appendl_ex(&hashed_details, "**", sizeof("**") - 1, 0);
/* DRCP: connection_class is an attribute of a connection */
if (OCI_G(connection_class)){
smart_str_appendl_ex(&hashed_details, OCI_G(connection_class), (ub4)UG(unicode) ? USTR_BYTES(type, u_strlen((UChar *)OCI_G(connection_class))) : strlen(OCI_G(connection_class)), 0);
}
smart_str_appendl_ex(&hashed_details, "**", sizeof("**") - 1, 0);
if (password_len) {
ulong password_hash;
password_hash = zend_u_inline_hash_func(type, password, password_len);
smart_str_append_unsigned_ex(&hashed_details, password_hash, 0);
}
smart_str_appendl_ex(&hashed_details, "__", sizeof("__") - 1, 0);
smart_str_appendl_ex(&hashed_details, "**", sizeof("**") - 1, 0);
if (dbname_len) {
smart_str_appendl_ex(&hashed_details, dbname.s, USTR_BYTES(type, dbname_len), 0);
}
smart_str_appendl_ex(&hashed_details, "__", sizeof("__") - 1, 0);
smart_str_appendl_ex(&hashed_details, "**", sizeof("**") - 1, 0);
/* Initialize global handles if the weren't initialized before */
/* Initialize global handles if they weren't initialized before */
if (OCI_G(env) == NULL) {
php_oci_init_global_handles(TSRMLS_C);
}
@ -1112,8 +1135,7 @@ php_oci_connection *php_oci_do_connect_ex(zstr username, int username_len, zstr
}
smart_str_append_unsigned_ex(&hashed_details, charsetid_nls_lang, 0);
}
}
else {
} else {
charsetid = OCI_UTF16ID;
smart_str_append_unsigned_ex(&hashed_details, charsetid, 0);
}
@ -1159,11 +1181,9 @@ php_oci_connection *php_oci_do_connect_ex(zstr username, int username_len, zstr
if (OCI_G(debug_mode)) {
if (connection && connection->is_stub) {
php_printf ("OCI8 DEBUG L1: Got a cached stub: (%p) at (%s:%d) \n", connection, __FILE__, __LINE__);
}
else if (connection) {
} else if (connection) {
php_printf ("OCI8 DEBUG L1: Got a cached connection: (%p) at (%s:%d) \n", connection, __FILE__, __LINE__);
}
else {
} else {
php_printf ("OCI8 DEBUG L1: Got NO cached connection at (%s:%d) \n", __FILE__, __LINE__);
}
} /* }}} */
@ -1183,26 +1203,29 @@ php_oci_connection *php_oci_do_connect_ex(zstr username, int username_len, zstr
return NULL;
}
/* We do the ping in php_oci_create_session, no need to ping again below */
ping_done = 1;
}
if (connection) {
if (connection->is_open) {
/* found an open connection. now ping it */
if (connection->is_persistent) {
int rsrc_type;
/* check connection liveness in the following order:
* 1) always check OCI_ATTR_SERVER_STATUS
* 2) see if it's time to ping it
* 3) ping it if needed
* */
*/
if (php_oci_connection_status(connection TSRMLS_CC)) {
/* only ping if:
* 1) next_ping > 0, which means that ping_interval is not -1 (aka "Off")
* 2) current_timestamp > next_ping, which means "it's time to check if it's still alive"
* */
if ( (connection->next_ping > 0) && (timestamp >= connection->next_ping) && !php_oci_connection_ping(connection TSRMLS_CC)) {
*/
if ( !ping_done && (*(connection->next_pingp) > 0) && (timestamp >= *(connection->next_pingp)) && !php_oci_connection_ping(connection TSRMLS_CC) ) {
/* server died */
} else {
int rsrc_type;
php_oci_connection *tmp;
/* okay, the connection is open and the server is still alive */
@ -1221,6 +1244,18 @@ php_oci_connection *php_oci_do_connect_ex(zstr username, int username_len, zstr
}
/* server died */
connection->is_open = 0;
connection->used_this_request = 1;
/* Connection is no more part of the persistent list */
free(connection->hash_key);
connection->hash_key = NULL;
connection->hash_key_len = 0;
/* We have to do a hash_del but need to preserve the resource if there is a positive refcount. Set the data pointer in the list entry to NULL */
if (zend_list_find(connection->rsrc_id, &rsrc_type)) {
le->ptr = NULL;
}
zend_hash_del(&EG(persistent_list), hashed_details.c, hashed_details.len+1);
connection = NULL;
goto open;
@ -1247,7 +1282,7 @@ php_oci_connection *php_oci_do_connect_ex(zstr username, int username_len, zstr
open:
/* Check if we have reached max_persistent. If so, try to remove a few
* timeout out connections. As last resort, return a non-persistent conn
* timed-out connections. As a last resort, return a non-persistent connection.
*/
if (persistent) {
zend_bool alloc_non_persistent = 0;
@ -1298,12 +1333,6 @@ open:
} /* }}} */
connection->idle_expiry = (OCI_G(persistent_timeout) > 0) ? (timestamp + OCI_G(persistent_timeout)) : 0;
if (OCI_G(ping_interval) >= 0) {
connection->next_ping = timestamp + OCI_G(ping_interval);
} else {
/* -1 means "Off" */
connection->next_ping = 0;
}
/* mark password as unchanged by PHP during the duration of the database session */
connection->passwd_changed = 0;
@ -1318,14 +1347,13 @@ open:
}
#endif
/* Old session creation semantics when session pool cannot be used Eg: privileged connect/password change {{{*/
/* Old session creation semantics when session pool cannot be used Eg: privileged connect/password change */
if ( !use_spool) {
if (php_oci_old_create_session(connection, dbname, dbname_len, username, username_len, password, password_len, new_password, new_password_len, session_mode, type TSRMLS_CC)) {
php_oci_connection_close(connection TSRMLS_CC);
return NULL;
}
} /* }}} */
else {
} else {
/* create using the client-side session pool */
if (php_oci_create_session(connection, session_pool, dbname, dbname_len, username, username_len, password, password_len, new_password, new_password_len, session_mode, type TSRMLS_CC)) {
php_oci_connection_close(connection TSRMLS_CC);
@ -1359,8 +1387,7 @@ open:
if (OCI_G(debug_mode)) {
if (connection->is_persistent) {
php_printf ("OCI8 DEBUG L1: New Persistent Connection address:(%p) at (%s:%d) \n", connection, __FILE__, __LINE__);
}
else {
} else {
php_printf ("OCI8 DEBUG L1: New Non-Persistent Connection address: (%p) at (%s:%d) \n", connection, __FILE__, __LINE__);
}
php_printf ("OCI8 DEBUG L1: num_persistent=(%ld), num_links=(%ld) at (%s:%d) \n", OCI_G(num_persistent), OCI_G(num_links), __FILE__, __LINE__);
@ -1378,9 +1405,9 @@ static int php_oci_connection_ping(php_oci_connection *connection TSRMLS_DC)
* ORA-1010 (invalid OCI operation) such as from Pre-10.1 servers,
* the error is still from the server and we would have
* successfully performed a roundtrip and validated the
* connection. Use OCIServerVersion for Pre-10.1 clients
* connection. Use OCIServerVersion for Pre-10.2 clients
*/
#if HAVE_OCI_LOB_READ2 /* 10.1 and greater client - OCIPing was first available in 10.1 */
#if ( (OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION > 2)) ) /* OCIPing available 10.2 onwards */
PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIPing, (connection->svc, OCI_G(err), OCI_DEFAULT));
#else
char version[256];
@ -1390,8 +1417,7 @@ static int php_oci_connection_ping(php_oci_connection *connection TSRMLS_DC)
if (OCI_G(errcode) == OCI_SUCCESS) {
return 1;
}
else {
} else {
sb4 error_code = 0;
text tmp_buf[PHP_OCI_ERRBUF_LEN];
@ -1480,11 +1506,18 @@ static int php_oci_connection_close(php_oci_connection *connection TSRMLS_DC)
}
if (!connection->is_stub && connection->svc && connection->is_open) {
/* Update the next_ping in the connection. Needed also for non-peristent because non-persistent DRCP caches connection underneath */
if (OCI_G(ping_interval) >= 0) {
*(connection->next_pingp) = time(NULL) + OCI_G(ping_interval);
} else {
/* ping_interval is -1 */
*(connection->next_pingp) = 0;
}
/* Use OCISessionRelease for session pool connections */
if (connection->using_spool) {
PHP_OCI_CALL(OCISessionRelease, (connection->svc, connection->err, NULL,0, (ub4) 0));
}
else {
} else {
PHP_OCI_CALL(OCISessionEnd, (connection->svc, connection->err, connection->session, (ub4) 0));
}
}
@ -1496,7 +1529,7 @@ static int php_oci_connection_close(php_oci_connection *connection TSRMLS_DC)
PHP_OCI_CALL(OCIHandleFree, ((dvoid *) connection->authinfo, (ub4) OCI_HTYPE_AUTHINFO));
}
/* No Handlefrees for session pool connections {{{ */
/* No Handlefrees for session pool connections */
if (!connection->using_spool) {
if (connection->session) {
PHP_OCI_CALL(OCIHandleFree, ((dvoid *) connection->session, OCI_HTYPE_SESSION));
@ -1517,8 +1550,7 @@ static int php_oci_connection_close(php_oci_connection *connection TSRMLS_DC)
if (connection->env) {
PHP_OCI_CALL(OCIHandleFree, ((dvoid *) connection->env, OCI_HTYPE_ENV));
}
} /* }}} */
else if (connection->private_spool) {
} else if (connection->private_spool) {
/* Keep this as the last member to be freed, as there are dependencies
* (like env) on the session pool
*/
@ -1570,8 +1602,15 @@ int php_oci_connection_release(php_oci_connection *connection TSRMLS_DC)
/* Release the session */
if (connection->svc) {
if (OCI_G(ping_interval) >= 0) {
*(connection->next_pingp) = time(NULL) + OCI_G(ping_interval);
} else {
/* ping_interval is -1 */
*(connection->next_pingp) = 0;
}
PHP_OCI_CALL(OCISessionRelease, (connection->svc, connection->err, NULL,
0, OCI_DEFAULT));
0, result ? OCI_SESSRLS_DROPSESS : OCI_DEFAULT));
}
/* It no longer has relation with the database session. However authinfo and env are cached */
@ -1582,7 +1621,9 @@ int php_oci_connection_release(php_oci_connection *connection TSRMLS_DC)
connection->is_attached = connection->is_open = connection->needs_commit = 0;
connection->is_stub = 1;
/* now a stub, so don't count it in the number of connnections */
/* Cut the link between the connection structure and the time_t structure allocated within the OCI session */
connection->next_pingp = NULL;
if (!connection->is_persistent) {
OCI_G(num_links)--; /* Support for "connection" stubs - future use */
}
@ -1849,6 +1890,11 @@ static int php_oci_persistent_helper(zend_rsrc_list_entry *le TSRMLS_DC)
return ZEND_HASH_APPLY_REMOVE;
}
/* Helps remove bad connections from the persistent list */
if (!connection->is_open && !connection->is_stub) {
return ZEND_HASH_APPLY_REMOVE;
}
if (connection->descriptors) {
zend_hash_destroy(connection->descriptors);
efree(connection->descriptors);
@ -1875,17 +1921,19 @@ static int php_oci_persistent_helper(zend_rsrc_list_entry *le TSRMLS_DC)
connection->idle_expiry = timestamp + OCI_G(persistent_timeout);
}
if (!connection->is_stub) {
if (OCI_G(ping_interval) >= 0) {
connection->next_ping = timestamp + OCI_G(ping_interval);
*(connection->next_pingp) = timestamp + OCI_G(ping_interval);
} else {
/* ping_interval is -1 */
connection->next_ping = 0;
*(connection->next_pingp) = 0;
}
/* Release all persistent connections at the end of the request */
if (connection->using_spool && !connection->is_stub && php_oci_connection_release(connection TSRMLS_CC)) {
/* Release all session pool-using persistent connections at the end of the request */
if (connection->using_spool && php_oci_connection_release(connection TSRMLS_CC)) {
return ZEND_HASH_APPLY_REMOVE;
}
}
connection->used_this_request = 0;
} else if (OCI_G(persistent_timeout) != -1) {
@ -1904,6 +1952,7 @@ static php_oci_spool *php_oci_create_spool(zstr username, int username_len, zstr
{
php_oci_spool *session_pool = NULL;
zend_bool iserror = 0;
ub4 poolmode = OCI_DEFAULT; /* Mode to be passed to OCISessionPoolCreate */
/*Allocate sessionpool out of persistent memory */
session_pool = (php_oci_spool *) calloc(1, sizeof(php_oci_spool));
@ -1942,10 +1991,17 @@ static php_oci_spool *php_oci_create_spool(zstr username, int username_len, zstr
goto exit_create_spool;
}
/* Disable RLB as we'd mostly have single-connection pools */
#if (OCI_MAJOR_VERSION > 10 )
poolmode = OCI_SPC_NO_RLB | OCI_SPC_HOMOGENEOUS;
#else
poolmode = OCI_SPC_HOMOGENEOUS;
#endif
/* Create the homogeneous session pool - We have different session pools
* for every different username, password, charset and dbname.
*/
PHP_OCI_CALL_RETURN(OCI_G(errcode), OCISessionPoolCreate,(session_pool->env, OCI_G(err), session_pool->poolh, (OraText **)&session_pool->poolname, &session_pool->poolname_len, (OraText *)dbname.s, (ub4)USTR_BYTES(type, dbname_len), 1, UB4MAXVAL, 1,(OraText *)username.s, (ub4)USTR_BYTES(type, username_len), (OraText *)password.s,(ub4)USTR_BYTES(type, password_len), OCI_SPC_HOMOGENEOUS));
PHP_OCI_CALL_RETURN(OCI_G(errcode), OCISessionPoolCreate,(session_pool->env, OCI_G(err), session_pool->poolh, (OraText **)&session_pool->poolname, &session_pool->poolname_len, (OraText *)dbname.s, (ub4)USTR_BYTES(type, dbname_len), 0, UB4MAXVAL, 1,(OraText *)username.s, (ub4)USTR_BYTES(type, username_len), (OraText *)password.s,(ub4)USTR_BYTES(type, password_len), poolmode));
if (OCI_G(errcode) != OCI_SUCCESS) {
php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
@ -1965,6 +2021,7 @@ static php_oci_spool *php_oci_create_spool(zstr username, int username_len, zstr
goto exit_create_spool;
}
}
exit_create_spool:
if (iserror && session_pool) {
php_oci_spool_close(session_pool TSRMLS_CC);
@ -1991,24 +2048,24 @@ static php_oci_spool *php_oci_get_spool(zstr username, int username_len, zstr pa
zend_bool iserror = 0;
/* Create the spool hash key {{{ */
smart_str_appendl_ex(&spool_hashed_details, "oci8__spool__", sizeof("oci8__spool__") - 1, 0);
smart_str_appendl_ex(&spool_hashed_details, "oci8spool***", sizeof("oci8spool***") - 1, 0);
smart_str_appendl_ex(&spool_hashed_details, username.s, USTR_BYTES(type, username_len), 0);
smart_str_appendl_ex(&spool_hashed_details, "__", sizeof("__") - 1, 0);
smart_str_appendl_ex(&spool_hashed_details, "**", sizeof("**") - 1, 0);
if (password_len) {
ulong password_hash;
password_hash = zend_u_inline_hash_func(type, password, password_len);
smart_str_append_unsigned_ex(&spool_hashed_details, password_hash, 0);
}
smart_str_appendl_ex(&spool_hashed_details, "__", sizeof("__") - 1, 0);
smart_str_appendl_ex(&spool_hashed_details, "**", sizeof("**") - 1, 0);
if (dbname_len) {
smart_str_appendl_ex(&spool_hashed_details, dbname.s, USTR_BYTES(type, dbname_len), 0);
}
smart_str_appendl_ex(&spool_hashed_details, "__", sizeof("__") - 1, 0);
smart_str_appendl_ex(&spool_hashed_details, "**", sizeof("**") - 1, 0);
smart_str_append_unsigned_ex(&spool_hashed_details, charsetid, 0);
/* Session Pool Hash Key : oci8__spool__dbname__charset */
/* Session Pool Hash Key : oci8spool***username**hashedpassword**dbname**charset */
smart_str_0(&spool_hashed_details);
php_strtolower(spool_hashed_details.c, spool_hashed_details.len);
@ -2026,8 +2083,7 @@ static php_oci_spool *php_oci_get_spool(zstr username, int username_len, zstr pa
spool_le.type = le_psessionpool;
zend_list_insert(session_pool, le_psessionpool);
zend_hash_update(&EG(persistent_list), session_pool->spool_hash_key, session_pool->spool_hash_key_len + 1, (void *)&spool_le, sizeof(zend_rsrc_list_entry),NULL);
}
else if (spool_out_le->type == le_psessionpool &&
} else if (spool_out_le->type == le_psessionpool &&
(((php_oci_spool *)(spool_out_le->ptr))->spool_hash_key_len) == spool_hashed_details.len &&
memcmp(((php_oci_spool *)(spool_out_le->ptr))->spool_hash_key, spool_hashed_details.c, spool_hashed_details.len) == 0 ) {
/* retrieve the cached session pool */
@ -2217,13 +2273,19 @@ static int php_oci_old_create_session(php_oci_connection *connection, zstr dbnam
php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
/* OCISessionBegin returns OCI_SUCCESS_WITH_INFO when
* user's password has expired, but is still usable.
* */
*/
if (OCI_G(errcode) != OCI_SUCCESS_WITH_INFO) {
return 1;
}
} /* }}} */
}
/* Brand new connection: Init and update the next_ping in the connection */
if (php_oci_ping_init(connection, OCI_G(err) TSRMLS_CC) != OCI_SUCCESS) {
php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
return 1;
}
#if HAVE_OCI_STMT_PREPARE2
{
ub4 statement_cache_size = (OCI_G(statement_cache_size) > 0) ? OCI_G(statement_cache_size) : 0;
@ -2249,6 +2311,7 @@ static int php_oci_create_session(php_oci_connection *connection, php_oci_spool
#if (OCI_MAJOR_VERSION > 10 )
ub4 purity = -2; /* Illegal value to initialize */
#endif
time_t timestamp = time(NULL);
/* Persistent connections have private session pools */
if (connection->is_persistent && !connection->private_spool &&
@ -2269,13 +2332,12 @@ static int php_oci_create_session(php_oci_connection *connection, php_oci_spool
if (OCI_G(debug_mode)) {
if (session_pool) {
php_printf ("OCI8 DEBUG L1: using shared pool: (%p) at (%s:%d) \n", session_pool, __FILE__, __LINE__);
}
else {
} else {
php_printf ("OCI8 DEBUG L1: using private pool: (%p) at (%s:%d) \n", connection->private_spool, __FILE__, __LINE__);
}
}
/* The passed in "connection" can be a cached stub from plist or a
/* The passed in "connection" can be a cached stub from plist or
* freshly created. In the former case, we do not have to allocate
* any handles */
@ -2311,7 +2373,6 @@ static int php_oci_create_session(php_oci_connection *connection, php_oci_spool
else
purity = OCI_ATTR_PURITY_NEW;
PHP_OCI_CALL_RETURN(OCI_G(errcode),OCIAttrSet, ((dvoid *) connection->authinfo,(ub4) OCI_HTYPE_AUTHINFO, (dvoid *) &purity, (ub4)0, (ub4)OCI_ATTR_PURITY, OCI_G(err)));
if (OCI_G(errcode) != OCI_SUCCESS) {
@ -2321,6 +2382,25 @@ static int php_oci_create_session(php_oci_connection *connection, php_oci_spool
#endif
} /* }}} */
/* Debug statements {{{ */
if (OCI_G(debug_mode)) {
ub4 numfree = 0, numbusy = 0, numopen = 0;
PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrGet, ((dvoid *)actual_spool->poolh, OCI_HTYPE_SPOOL, (dvoid *)&numopen, (ub4 *)0, OCI_ATTR_SPOOL_OPEN_COUNT, OCI_G(err)));
PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrGet, ((dvoid *)actual_spool->poolh, OCI_HTYPE_SPOOL, (dvoid *)&numbusy, (ub4 *)0, OCI_ATTR_SPOOL_BUSY_COUNT, OCI_G(err)));
numfree = numopen - numbusy; /* number of free connections in the pool */
php_printf ("OCI8 DEBUG L1: (numopen=%d)(numbusy=%d) at (%s:%d) \n", numopen, numbusy, __FILE__, __LINE__);
} /* }}} */
/* Ping loop: Ping and loop till we get a good
* connection. When a database instance goes down, it can
* leave several bad connections that need to be flushed out
* before getting a good one. In non-RAC, we always get a
* brand new connection at the end of the loop and in RAC, we
* can get a good connection from a different instance before
* flushing out all bad ones. We do not need to ping brand new
* connections.
*/
do {
/* Continue to use the global error handle as the connection is closed when an error occurs */
PHP_OCI_CALL_RETURN(OCI_G(errcode),OCISessionGet, (connection->env, OCI_G(err), &(connection->svc), (OCIAuthInfo *)connection->authinfo, (OraText *)actual_spool->poolname, (ub4)actual_spool->poolname_len, NULL, 0, NULL, NULL, NULL, OCI_SESSGET_SPOOL));
@ -2329,13 +2409,44 @@ static int php_oci_create_session(php_oci_connection *connection, php_oci_spool
/* Session creation returns OCI_SUCCESS_WITH_INFO when
* user's password has expired, but is still usable.
* */
*/
if (OCI_G(errcode) != OCI_SUCCESS_WITH_INFO) {
return 1;
}
}
/* {{{ Populate the session and server fields of the connection */
PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrGet, ((dvoid *)connection->svc, OCI_HTYPE_SVCCTX, (dvoid *)&(connection->server), (ub4 *)0, OCI_ATTR_SERVER, OCI_G(err)));
PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrGet, ((dvoid *)connection->svc, OCI_HTYPE_SVCCTX, (dvoid *)&(connection->session), (ub4 *)0, OCI_ATTR_SESSION, OCI_G(err))); /* }}} */
PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIContextGetValue, (connection->session, OCI_G(err), (ub1 *)"NEXT_PING", (ub1)sizeof("NEXT_PING"), (void **)&(connection->next_pingp)));
if (OCI_G(errcode) != OCI_SUCCESS) {
php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
return 1;
}
if (!(connection->next_pingp)){
/* This is a brand new connection, we need not ping, but have to initialize ping */
if (php_oci_ping_init(connection, OCI_G(err) TSRMLS_CC) != OCI_SUCCESS) {
php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
return 1;
}
} else if ((*(connection->next_pingp) > 0) && (timestamp >= *(connection->next_pingp))) {
if (php_oci_connection_ping(connection TSRMLS_CC)) {
/* Got a good connection - update next_ping and get out of ping loop */
*(connection->next_pingp) = timestamp + OCI_G(ping_interval);
} else {
/* Bad connection - remove from pool */
PHP_OCI_CALL(OCISessionRelease, (connection->svc, connection->err, NULL,0, (ub4) OCI_SESSRLS_DROPSESS));
connection->svc = NULL;
connection->server = NULL;
connection->session = NULL;
}
} /* If ping applicable */
} while (!(connection->svc));
#if HAVE_OCI_STMT_PREPARE2
{
ub4 statement_cache_size = (OCI_G(statement_cache_size) > 0) ? OCI_G(statement_cache_size) : 0;
@ -2349,11 +2460,6 @@ static int php_oci_create_session(php_oci_connection *connection, php_oci_spool
}
#endif
/* {{{ Populate the session and server fields of the connection */
PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrGet, ((dvoid *)connection->svc, OCI_HTYPE_SVCCTX, (dvoid *)&(connection->server), (ub4 *)0, OCI_ATTR_SERVER, OCI_G(err)));
PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrGet, ((dvoid *)connection->svc, OCI_HTYPE_SVCCTX, (dvoid *)&(connection->session), (ub4 *)0, OCI_ATTR_SESSION, OCI_G(err))); /* }}} */
/* Session is now taken from the session pool and attached and open */
connection->is_stub = 0;
connection->is_attached = connection->is_open = 1;
@ -2402,6 +2508,48 @@ static void php_oci_spool_close(php_oci_spool *session_pool TSRMLS_DC)
free(session_pool);
} /* }}} */
/* {{{ php_oci_ping_init()
Initializes the next_ping time as a context value in the
connection. We now use OCIContext{Get,Set}Value to store the
next_ping because we need to support ping for non-persistent DRCP
connections */
static sword php_oci_ping_init(php_oci_connection *connection, OCIError *errh TSRMLS_DC)
{
time_t *next_pingp = NULL;
PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIContextGetValue, (connection->session, errh, (ub1 *)"NEXT_PING", (ub1)sizeof("NEXT_PING"), (void **)&next_pingp));
if (OCI_G(errcode) != OCI_SUCCESS) {
return OCI_G(errcode);
}
/* This must be a brand-new connection. Allocate memory for the ping */
if (!next_pingp) {
PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIMemoryAlloc, (connection->session, errh, (void **)&next_pingp, OCI_DURATION_SESSION, sizeof(time_t), OCI_MEMORY_CLEARED));
if (OCI_G(errcode) != OCI_SUCCESS) {
return OCI_G(errcode);
}
}
if (OCI_G(ping_interval) >= 0) {
time_t timestamp = time(NULL);
*next_pingp = timestamp + OCI_G(ping_interval);
} else {
*next_pingp = 0;
}
/* Set the new ping value into the connection */
PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIContextSetValue, (connection->session, errh, OCI_DURATION_SESSION, (ub1 *)"NEXT_PING", (ub1)sizeof("NEXT_PING"), next_pingp));
if (OCI_G(errcode) != OCI_SUCCESS) {
OCIMemoryFree(connection->session, errh, next_pingp);
return OCI_G(errcode);
}
/* Cache the pointer so we do not have to do OCIContextGetValue repeatedly */
connection->next_pingp = next_pingp;
return OCI_SUCCESS;
} /* }}} */
#ifdef ZTS
/* {{{ php_oci_list_helper()
Helper function to destroy data on thread shutdown in ZTS mode */

View file

@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
| PHP Version 6 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2008 The PHP Group |
+----------------------------------------------------------------------+
@ -134,7 +134,7 @@ typedef struct { /* php_oci_connection {{{ */
unsigned using_spool:1; /* Is this connection from session pool? */
int rsrc_id; /* resource ID */
time_t idle_expiry; /* time when the connection will be considered as expired */
time_t next_ping; /* time of the next ping */
time_t *next_pingp; /* (pointer to) time of the next ping */
char *hash_key; /* hashed details of the connection */
int hash_key_len;
} php_oci_connection; /* }}} */
@ -207,14 +207,13 @@ typedef struct { /* php_oci_bind {{{ */
void *elements;
sb2 *indicators;
ub2 *element_lengths;
/* ub2 *retcodes; */
ub4 current_length;
ub4 old_length;
ub4 max_length;
long type;
} array;
sb2 indicator; /* -1 means NULL */
ub2 retcode; /* */
ub2 retcode;
zend_bool out; /* OUT bind or not */
ub4 dummy_len; /* a dummy var to store alenpp value in bind OUT callback */
} php_oci_bind; /* }}} */
@ -228,18 +227,18 @@ typedef struct { /* php_oci_out_column {{{ */
ub2 data_type; /* column data type */
ub2 data_size; /* data size */
ub4 storage_size4; /* size used when allocating buffers */
sb2 indicator; /* */
sb2 indicator;
ub2 retcode; /* code returned when fetching this particular column */
ub2 retlen; /* */
ub4 retlen4; /* */
ub2 retlen;
ub4 retlen4;
ub2 is_descr; /* column contains a descriptor */
ub2 is_cursor; /* column contains a cursor */
int stmtid; /* statement id for cursors */
int descid; /* descriptor id for descriptors */
void *data; /* */
void *data;
php_oci_define *define; /* define handle */
int piecewise; /* column is fetched piece-by-piece */
ub4 cb_retlen; /* */
ub4 cb_retlen;
sb1 scale; /* column scale */
sb2 precision; /* column precision */
ub1 charset_form; /* charset form, required for NCLOBs */
@ -275,11 +274,25 @@ typedef struct { /* php_oci_out_column {{{ */
zend_bailout(); \
break; \
case 22: \
case 1012: \
case 3113: \
case 378: \
case 602: \
case 603: \
case 604: \
case 609: \
case 1012: \
case 1033: \
case 1041: \
case 1043: \
case 1089: \
case 1090: \
case 1092: \
case 3113: \
case 3114: \
case 3122: \
case 3135: \
case 12153: \
case 27146: \
case 28511: \
connection->is_open = 0; \
break; \
} \
@ -437,12 +450,6 @@ ZEND_BEGIN_MODULE_GLOBALS(oci) /* {{{ */
sword errcode; /* global last error code (used when connect fails, for example) */
OCIError *err; /* global error handle */
/*
char *default_username;
char *default_password;
char *default_dbname;
*/
zend_bool debug_mode; /* debug mode flag */
long max_persistent; /* maximum number of persistent connections per process */
@ -486,5 +493,3 @@ ZEND_EXTERN_MODULE_GLOBALS(oci)
* c-basic-offset: 4
* End:
*/

View file

@ -0,0 +1,108 @@
--TEST--
DRCP: Test setting connection class inline
--SKIPIF--
<?php
if (!extension_loaded('oci8')) die ("skip no oci8 extension");
require(__DIR__."/details.inc");
if (!$test_drcp) die("skip testing DRCP connection class only works in DRCP mode");
if (strcasecmp($user, "system") && strcasecmp($user, "sys")) die("skip needs to be run as a DBA user");
?>
--FILE--
<?php
require(__DIR__."/details.inc");
// Initialization
$t = time();
$cc1 = 'cc1_'.$t;
$cc2 = 'cc2_'.$t;
// Run Test
echo "Test 1\n";
ini_set('oci8.connection_class', $cc1);
$c = oci_pconnect($user, $password, $dbase);
$s = oci_parse($c, "select * from dual");
oci_execute($s);
oci_fetch_all($s, $r);
var_dump($r);
echo "Test 2\n";
ini_set('oci8.connection_class', $cc2);
$c = oci_pconnect($user, $password, $dbase);
$s = oci_parse($c, "select * from dual");
oci_execute($s);
oci_fetch_all($s, $r);
var_dump($r);
echo "Test 3\n";
$s = oci_parse($c, "select cclass_name from v\$cpool_cc_stats where cclass_name like '%.cc__$t'");
oci_execute($s);
oci_fetch_all($s, $r);
var_dump($r);
// Cleanup
echo "Done\n";
?>
--EXPECTF--
Test 1
array(1) {
["DUMMY"]=>
array(1) {
[0]=>
string(1) "X"
}
}
Test 2
array(1) {
["DUMMY"]=>
array(1) {
[0]=>
string(1) "X"
}
}
Test 3
array(1) {
["CCLASS_NAME"]=>
array(2) {
[0]=>
string(21) "%s.cc1_%d"
[1]=>
string(21) "%s.cc2_%d"
}
}
Done
--UEXPECTF--
Test 1
array(1) {
[u"DUMMY"]=>
array(1) {
[0]=>
unicode(1) "X"
}
}
Test 2
array(1) {
[u"DUMMY"]=>
array(1) {
[0]=>
unicode(1) "X"
}
}
Test 3
array(1) {
[u"CCLASS_NAME"]=>
array(2) {
[0]=>
unicode(21) "%s.cc1_%d"
[1]=>
unicode(21) "%s.cc2_%d"
}
}
Done