| |
| Collection support by Andy Sautins <asautins@veripost.net> |
| Temporary LOB support by David Benson <dbenson@mancala.com> |
+ | ZTS per process OCIPLogon by Harald Radi <harald.radi@nme.at> |
+----------------------------------------------------------------------+
*/
*
* - php.ini flags
* especialliy important for things like oci_ping
+ * allowpconns
+ * timeout
+ * maxlifetime
+ * maxpconns
* - Change return-value for OCIFetch*() (1-row read, 0-Normal end, false-error)
* - Error mode (print or shut up?)
* - binding of arrays
* - make OCIInternalDebug accept a mask of flags....
* - have one ocifree() call.
* - make it possible to have persistent statements?
- * - implement connection pooling in ZTS mode.
* - failover
* - change all the lob stuff to work without classes (optional)!
* - make sure that the callbacks terminate the strings with \0
#include "php.h"
#include "ext/standard/info.h"
+#include "php_ini.h"
/* #define HAVE_OCI8_TEMP_LOB 1 */
#define WITH_COLLECTIONS 1
#include "php_oci8.h"
+/* True globals, only used by thread safe functions */
+static TsHashTable *persistent_servers;
+static TsHashTable *persistent_sessions;
+
+static long num_persistent = 0;
+static long num_links = 0;
+
/* True globals, no need for thread safety */
static int le_conn;
static int le_stmt;
#define SAFE_STRING(s) ((s)?(s):"")
+#ifdef ZTS
+MUTEX_T mx_lock;
+
+#define mutex_alloc() tsrm_mutex_alloc()
+#define mutex_free(mutex) tsrm_mutex_free(mutex)
+#define mutex_lock(mutex) tsrm_mutex_lock(mutex)
+#define mutex_unlock(mutex) tsrm_mutex_unlock(mutex)
+#define thread_id() tsrm_thread_id()
+#else
+#define mutex_alloc()
+#define mutex_free(mutex)
+#define mutex_lock(mutex)
+#define mutex_unlock(mutex)
+#define thread_id() 1
+#endif
/* dirty marcos to make sure we _never_ call oracle-functions recursivly
*
STANDARD_MODULE_PROPERTIES
};
-/* }}} */
-/* {{{ debug malloc/realloc/free */
-
-#define OCI_USE_EMALLOC 0 /* set this to 1 if you want to use the php memory manager! */
-
-#if OCI_USE_EMALLOC
-CONST dvoid *ocimalloc(dvoid *ctx, size_t size)
-{
- dvoid *ret;
- ret = (dvoid *)malloc(size);
- oci_debug("ocimalloc(%d) = %08x", size,ret);
- return ret;
-}
-
-CONST dvoid *ocirealloc(dvoid *ctx, dvoid *ptr, size_t size)
-{
- dvoid *ret;
- oci_debug("ocirealloc(%08x, %d)", ptr, size);
- ret = (dvoid *)realloc(ptr, size);
- return ptr;
-}
-
-CONST void ocifree(dvoid *ctx, dvoid *ptr)
-{
- oci_debug("ocifree(%08x)", ptr);
- free(ptr);
-}
-#endif
-
/* }}} */
/* {{{ startup, shutdown and info functions */
OCI(shutdown) = 0;
OCI(in_call) = 0;
- OCI(user) = malloc(sizeof(HashTable));
- zend_hash_init(OCI(user), 13, NULL, NULL, 1);
-
- OCI(server) = malloc(sizeof(HashTable));
- zend_hash_init(OCI(server), 13, NULL, NULL, 1);
-
CALL_OCI(OCIEnvInit(
&OCI(pEnv),
OCI_DEFAULT,
NULL));
}
+static int _sessions_pcleanup(zend_llist *session_list TSRMLS_DC)
+{
+ zend_llist_destroy(session_list);
+
+ return 1;
+}
+
+static int _session_pcleanup(oci_session *session TSRMLS_DC)
+{
+ _oci_close_session(session);
+
+ return 1;
+}
+
+static int _server_pcleanup(oci_server *server TSRMLS_DC)
+{
+ _oci_close_server(server);
+
+ return 1;
+}
+
PHP_MINIT_FUNCTION(oci)
{
zend_class_entry oci_lob_class_entry;
#endif
-#if OCI_USE_EMALLOC
- OCIInitialize(PHP_OCI_INIT_MODE, NULL, ocimalloc, ocirealloc, ocifree);
-#else
+ mx_lock = mutex_alloc();
+
+ persistent_servers = malloc(sizeof(TsHashTable));
+ persistent_sessions = malloc(sizeof(TsHashTable));
+ zend_ts_hash_init(persistent_servers, 13, NULL, (dtor_func_t) _server_pcleanup, 1);
+ zend_ts_hash_init(persistent_sessions, 13, NULL, (dtor_func_t) _sessions_pcleanup, 1);
+
OCIInitialize(PHP_OCI_INIT_MODE, NULL, NULL, NULL, NULL);
-#endif
#ifdef ZTS
ts_allocate_id(&oci_globals_id, sizeof(php_oci_globals), (ts_allocate_ctor) php_oci_init_globals, NULL);
PHP_RINIT_FUNCTION(oci)
{
- /* XXX NYI
- OCI(num_links) =
- OCI(num_persistent);
- */
-
OCI(debug_mode) = 0; /* start "fresh" */
/* OCI(in_call) = 0; i don't think we want this! */
return SUCCESS;
}
-static int _session_pcleanup(oci_session *session TSRMLS_DC)
-{
- _oci_close_session(session);
-
- return 1;
-}
-
-static int _server_pcleanup(oci_server *server TSRMLS_DC)
-{
- _oci_close_server(server);
-
- return 1;
-}
-
PHP_MSHUTDOWN_FUNCTION(oci)
{
OCI(shutdown) = 1;
oci_debug("START php_mshutdown_oci");
- zend_hash_apply(OCI(user), (apply_func_t)_session_pcleanup TSRMLS_CC);
- zend_hash_apply(OCI(server), (apply_func_t)_server_pcleanup TSRMLS_CC);
+ zend_ts_hash_destroy(persistent_sessions);
+ zend_ts_hash_destroy(persistent_servers);
- zend_hash_destroy(OCI(user));
- zend_hash_destroy(OCI(server));
+ free(persistent_sessions);
+ free(persistent_servers);
- free(OCI(user));
- free(OCI(server));
+ mutex_free(mx_lock);
CALL_OCI(OCIHandleFree(
(dvoid *)OCI(pEnv),
#if 0
/* XXX free all statements, rollback all outstanding transactions */
- zend_hash_apply(OCI(user), (apply_func_t) _session_cleanup TSRMLS_CC);
- zend_hash_apply(OCI(server), (apply_func_t) _server_cleanup TSRMLS_CC);
+ zend_ts_hash_apply(persistent_sessions, (apply_func_t) _session_cleanup TSRMLS_CC);
+ zend_ts_hash_apply(persistent_servers, (apply_func_t) _server_cleanup TSRMLS_CC);
#endif
oci_debug("END php_rshutdown_oci");
PHP_MINFO_FUNCTION(oci)
{
+ char buf[32];
php_info_print_table_start();
php_info_print_table_row(2, "OCI8 Support", "enabled");
php_info_print_table_row(2, "Revision", "$Revision$");
+
+ sprintf(buf, "%ld", num_persistent);
+ php_info_print_table_row(2, "Active Persistent Links", buf);
+ sprintf(buf, "%ld", num_links);
+ php_info_print_table_row(2, "Active Links", buf);
+
#ifndef PHP_WIN32
php_info_print_table_row(2, "Oracle Version", PHP_OCI8_VERSION );
php_info_print_table_row(2, "Compile-time ORACLE_HOME", PHP_OCI8_DIR );
(ub4) OCI_HTYPE_SVCCTX));
}
- if (connection->session && connection->session->exclusive) {
- /* exclusive connection created via OCINLogon() close their
- associated session when destructed */
+ if (connection->session) {
+ /* close associated session when destructed */
zend_list_delete(connection->session->num);
}
_oci_session_list_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
{
oci_session *session = (oci_session *)rsrc->ptr;
- if (session->persistent)
+ if (session->persistent) {
+ /* clear thread assignment */
+ session->thread = 0;
return;
+ }
_oci_close_session(session);
}
ub4 siz = 0;
ub4 readlen = 0;
ub4 loblen = 0;
- int bytes = 0;
+ ub4 bytes = 0;
char *buf;
TSRMLS_FETCH();
return -1;
}
- while (readlen > 0 && bytes < *len && siz < loblen) { //paranoia
+ while (readlen > 0 && bytes < *len && siz < loblen) { /* paranoia */
CALL_OCI_RETURN(connection->error, OCILobRead(
connection->pServiceContext,
connection->pError,
static oci_session *_oci_open_session(oci_server* server,char *username,char *password,int persistent,int exclusive,char *charset)
{
- oci_session *session = 0, *psession = 0;
- OCISvcCtx *svchp = 0;
+ zend_llist *session_list;
+ oci_session *session = NULL;
+ OCISvcCtx *svchp = NULL;
smart_str hashed_details = {0};
#ifdef HAVE_OCI_9_2
ub2 charsetid = 0;
smart_str_0(&hashed_details);
if (! exclusive) {
- zend_hash_find(OCI(user), hashed_details.c, hashed_details.len+1, (void **) &session);
+ if (zend_ts_hash_find(persistent_sessions, hashed_details.c, hashed_details.len+1, &session_list) != SUCCESS) {
+ zend_llist tmp;
+ /* first session, set up a session list */
+ zend_llist_init(&tmp, sizeof(oci_session), (llist_dtor_func_t) _session_pcleanup, 1);
+ zend_ts_hash_update(persistent_sessions, hashed_details.c, hashed_details.len+1, &tmp, sizeof(zend_llist), &session_list);
+ } else {
+ mutex_lock(mx_lock);
+
+ /* session list found, search for an idle session or an already opened session by the current thread */
+ session = zend_llist_get_first(session_list);
+ while ((session != NULL) && session->thread && (session->thread != thread_id())) {
+ session = zend_llist_get_next(session_list);
+ }
+
+ if (session != NULL) {
+ /* mark session as busy */
+ session->thread = thread_id();
+ }
+
+ mutex_unlock(mx_lock);
+ }
if (session) {
if (session->is_open) {
}
}
- session = calloc(1,sizeof(oci_session));
+ session = ecalloc(1,sizeof(oci_session));
if (! session) {
goto CLEANUP;
}
session->persistent = persistent;
- session->hashed_details = hashed_details.c;
session->server = server;
session->exclusive = exclusive;
+ session->sessions_list = session_list;
+ session->thread = thread_id();
#ifdef HAVE_OCI_9_2
(dvoid *) svchp,
(ub4) OCI_HTYPE_SVCCTX));
- if (exclusive) {
- psession = session;
- } else {
- zend_hash_update(OCI(user),
- session->hashed_details,
- strlen(session->hashed_details)+1,
- (void *)session,
- sizeof(oci_session),
- (void**)&psession);
- }
-
- psession->num = zend_list_insert(psession,le_session);
- psession->is_open = 1;
+ session->num = zend_list_insert(session, le_session);
+ session->is_open = 1;
- oci_debug("_oci_open_session new sess=%d user=%s",psession->num,username);
+ mutex_lock(mx_lock);
+ num_links++;
+ if (! exclusive) {
+ zend_llist_add_element(session_list, session);
+ efree(session);
+ session = (oci_session*) session_list->tail->data;
+ num_persistent++;
+ }
+ mutex_unlock(mx_lock);
- if (! exclusive) free(session);
+ oci_debug("_oci_open_session new sess=%d user=%s",session->num,username);
- return psession;
+ return session;
CLEANUP:
oci_debug("_oci_open_session: FAILURE -> CLEANUP called");
/* {{{ _oci_close_session()
*/
+static int _session_compare(oci_session *sess1, oci_session *sess2)
+{
+ return sess1->num = sess2->num;
+}
+
static void
_oci_close_session(oci_session *session)
{
OCISvcCtx *svchp;
- char *hashed_details;
TSRMLS_FETCH();
if (! session) {
(ub4) OCI_HTYPE_SESSION));
}
- hashed_details = session->hashed_details;
+ mutex_lock(mx_lock);
+ num_links--;
+ if (! OCI(shutdown) && session->persistent) {
+ zend_llist_del_element(session->sessions_list, session, _session_compare);
+ num_persistent--;
+ }
+ mutex_unlock(mx_lock);
- if (! OCI(shutdown)) {
- zend_hash_del(OCI(user), hashed_details, strlen(hashed_details)+1);
+ if (session->exclusive) {
+ efree(session);
}
-
- free(hashed_details);
}
/* }}} */
static oci_server *_oci_open_server(char *dbname,int persistent)
{
- oci_server *server, *pserver = 0;
+ oci_server *server, *pserver = NULL;
TSRMLS_FETCH();
/*
but only as pesistent requested connections will be kept between requests!
*/
- zend_hash_find(OCI(server), dbname, strlen(dbname)+1, (void **) &pserver);
+ /* TODO either keep servers global or don't reuse them at all */
+ zend_ts_hash_find(persistent_servers, dbname, strlen(dbname)+1, (void **) &pserver);
if (pserver) {
/* XXX ini-flag */
}
}
- server = calloc(1,sizeof(oci_server));
+ server = ecalloc(1,sizeof(oci_server));
server->persistent = persistent;
server->dbname = strdup(SAFE_STRING(dbname));
goto CLEANUP;
}
- zend_hash_update(OCI(server),
+ zend_ts_hash_update(persistent_servers,
server->dbname,
strlen(server->dbname)+1,
(void *)server,
oci_debug("_oci_open_server new conn=%d dname=%s",server->num,server->dbname);
- free(server);
+ efree(server);
return pserver;
dbname = server->dbname;
if (! OCI(shutdown)) {
- zend_hash_del(OCI(server),dbname,strlen(dbname)+1);
+ zend_ts_hash_del(persistent_servers, dbname, strlen(dbname)+1);
}
free(dbname);
persistent = 0;
} else {
/* if our server-context is not persistent we can't */
- persistent = server->persistent;
+ persistent = (server->persistent) ? persistent : 0;
}
session = _oci_open_session(server,username,password,persistent,exclusive,charset);
zval *id;
oci_descriptor *descr;
int inx;
- int len;
if ((id = getThis()) != 0) {
if ((inx = _oci_get_ocidesc(id,&descr TSRMLS_CC)) == 0) {
zval *id;
oci_descriptor *descr;
int inx;
- int len;
if ((id = getThis()) != 0) {
if ((inx = _oci_get_ocidesc(id,&descr TSRMLS_CC)) == 0) {
}
}
else {
- //OCI_SEEK_SET by default
+ /* OCI_SEEK_SET by default */
descr->lob_current_position = Z_LVAL_PP(arg1);
}
RETURN_TRUE;
descr->lob_size = descr->lob_current_position;
}
- //marking buffer as used
+ /* marking buffer as used */
if (descr->buffering == 1) {
descr->buffering = 2;
}
}
if (trim_length < 0) {
- //negative length is not allowed
+ /* negative length is not allowed */
RETURN_FALSE;
}
}
if (descr->buffering == 0) {
- //buffering wasn't enabled, there is nothing to flush
+ /* buffering wasn't enabled, there is nothing to flush */
RETURN_FALSE;
}
else if (descr->buffering == 1) {
- //buffering is enabled, but not used yet
- //dunno why, but OCI returns error in this case, so I think we should suppress it
+ /* buffering is enabled, but not used yet
+ dunno why, but OCI returns error in this case, so I think we should suppress it */
RETURN_TRUE;
}
PHP_FUNCTION(ocigetbufferinglob)
{
- zval *id, **flag;
+ zval *id;
OCILobLocator *mylob;
oci_descriptor *descr;
int inx;
- ub4 curloblen;
if ((id = getThis()) != 0) {
if ((inx = _oci_get_ocidesc(id,&descr TSRMLS_CC)) == 0) {
oci_connection *connection;
oci_descriptor *first_descr,*second_descr;
int inx;
- ub4 first_curloblen,second_curloblen;
boolean is_equal;
if (zend_get_parameters_ex(2, &arg1, &arg2) == FAILURE) {