* fix ns_db gethandle -timeout to do a non-blocking timeout where timeout=0.
/*
* The contents of this file are subject to the AOLserver Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://aolserver.com/.
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is AOLserver Code and related documentation
* distributed by AOL.
*
* The Initial Developer of the Original Code is America Online,
* Inc. Portions created by AOL are Copyright (C) 1999 America Online,
* Inc. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the terms
* of the GNU General Public License (the "GPL"), in which case the
* provisions of GPL are applicable instead of those above. If you wish
* to allow use of your version of this file only under the terms of the
* GPL and not to allow others to use your version of this file under the
* License, indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by the GPL.
* If you do not delete the provisions above, a recipient may use your
* version of this file under either the License or the GPL.
*/
/*
* dbinit.c --
*
* This file contains routines for creating and accessing
* pools of database handles.
*/
static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/aolserver/nsdb/dbinit.c,v 1.5 2004/02/05 17:50:53 dossy Exp $, compiled: " __DATE__ " " __TIME__;
#include "db.h"
/*
* The following structure defines a database pool.
*/
struct Handle;
typedef struct Pool {
char *name;
char *desc;
char *source;
char *user;
char *pass;
int type;
Ns_Mutex lock;
Ns_Cond waitCond;
Ns_Cond getCond;
char *driver;
struct DbDriver *driverPtr;
int waiting;
int nhandles;
struct Handle *firstPtr;
struct Handle *lastPtr;
int fVerbose;
int fVerboseError;
time_t maxidle;
time_t maxopen;
int stale_on_close;
} Pool;
/*
* The following structure defines the internal
* state of a database handle.
*/
typedef struct Handle {
char *driver;
char *datasource;
char *user;
char *password;
void *connection;
char *poolname;
int connected;
int verbose;
Ns_Set *row;
char cExceptionCode[6];
Ns_DString dsExceptionMsg;
void *context;
void *statement;
int fetchingRows;
/* Members above must match Ns_DbHandle */
struct Handle *nextPtr;
struct Pool *poolPtr;
time_t otime;
time_t atime;
int stale;
int stale_on_close;
} Handle;
/*
* The following structure maintains per-server data.
*/
typedef struct ServData {
char *defpool;
char *allowed;
} ServData;
/*
* Local functions defined in this file
*/
static Pool *GetPool(char *pool);
static void ReturnHandle(Handle * handle);
static int IsStale(Handle *, time_t now);
static int Connect(Handle *);
static Pool *CreatePool(char *pool, char *path, char *driver);
static int IncrCount(Pool *poolPtr, int incr);
static ServData *GetServer(char *server);
static Ns_TlsCleanup FreeTable;
static Ns_Callback CheckPool;
static Ns_ArgProc CheckArgProc;
/*
* Static variables defined in this file
*/
static Tcl_HashTable poolsTable;
static Tcl_HashTable serversTable;
static Ns_Tls tls;
/*
*----------------------------------------------------------------------
*
* Ns_DbPoolDescription --
*
* Return the pool's description string.
*
* Results:
* Configured description string or NULL.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
char *
Ns_DbPoolDescription(char *pool)
{
Pool *poolPtr;
poolPtr = GetPool(pool);
if (poolPtr == NULL) {
return NULL;
}
return poolPtr->desc;
}
/*
*----------------------------------------------------------------------
*
* Ns_DbPoolDefault --
*
* Return the default pool.
*
* Results:
* String name of default pool or NULL if no default is defined.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
char *
Ns_DbPoolDefault(char *server)
{
ServData *sdataPtr = GetServer(server);
return (sdataPtr ? sdataPtr->defpool : NULL);
}
/*
*----------------------------------------------------------------------
*
* Ns_DbPoolList --
*
* Return the list of all pools.
*
* Results:
* Double-null terminated list of pool names.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
char *
Ns_DbPoolList(char *server)
{
ServData *sdataPtr = GetServer(server);
return (sdataPtr ? sdataPtr->allowed : NULL);
}
/*
*----------------------------------------------------------------------
*
* Ns_DbPoolAllowable --
*
* Check that access is allowed to a pool.
*
* Results:
* NS_TRUE if allowed, NS_FALSE otherwise.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
Ns_DbPoolAllowable(char *server, char *pool)
{
register char *p;
p = Ns_DbPoolList(server);
if (p != NULL) {
while (*p != '\0') {
if (STREQ(pool, p)) {
return NS_TRUE;
}
p = p + strlen(p) + 1;
}
}
return NS_FALSE;
}
/*
*----------------------------------------------------------------------
*
* Ns_DbPoolPutHandle --
*
* Cleanup and then return a handle to its pool.
*
* Results:
* None.
*
* Side effects:
* Handle is flushed, reset, and possibly closed as required.
*
*----------------------------------------------------------------------
*/
void
Ns_DbPoolPutHandle(Ns_DbHandle *handle)
{
Handle *handlePtr;
Pool *poolPtr;
time_t now;
handlePtr = (Handle *) handle;
poolPtr = handlePtr->poolPtr;
/*
* Cleanup the handle.
*/
Ns_DbFlush(handle);
Ns_DbResetHandle(handle);
Ns_DStringFree(&handle->dsExceptionMsg);
handle->cExceptionCode[0] = '\0';
/*
* Close the handle if it's stale, otherwise update
* the last access time.
*/
time(&now);
if (IsStale(handlePtr, now)) {
NsDbDisconnect(handle);
} else {
handlePtr->atime = now;
}
IncrCount(poolPtr, -1);
Ns_MutexLock(&poolPtr->lock);
ReturnHandle(handlePtr);
if (poolPtr->waiting) {
Ns_CondSignal(&poolPtr->getCond);
}
Ns_MutexUnlock(&poolPtr->lock);
}
/*
*----------------------------------------------------------------------
*
* Ns_DbPoolTimedGetHandle --
*
* Return a single handle from a pool within the given number of
* seconds.
*
* Results:
* Pointer to Ns_DbHandle or NULL on error or timeout.
*
* Side effects:
* Database may be opened if needed.
*
*----------------------------------------------------------------------
*/
Ns_DbHandle *
Ns_DbPoolTimedGetHandle(char *pool, int wait)
{
Ns_DbHandle *handle;
if (Ns_DbPoolTimedGetMultipleHandles(&handle, pool, 1, wait) != NS_OK) {
return NULL;
}
return handle;
}
/*
*----------------------------------------------------------------------
*
* Ns_DbPoolGetHandle --
*
* Return a single handle from a pool.
*
* Results:
* Pointer to Ns_DbHandle or NULL on error.
*
* Side effects:
* Database may be opened if needed.
*
*----------------------------------------------------------------------
*/
Ns_DbHandle *
Ns_DbPoolGetHandle(char *pool)
{
return Ns_DbPoolTimedGetHandle(pool, 0);
}
/*
*----------------------------------------------------------------------
*
* Ns_DbPoolGetMultipleHandles --
*
* Return 1 or more handles from a pool.
*
* Results:
* NS_OK if handles were allocated, NS_ERROR otherwise.
*
* Side effects:
* Given array of handles is updated with pointers to allocated
* handles. Also, database may be opened if needed.
*
*----------------------------------------------------------------------
*/
int
Ns_DbPoolGetMultipleHandles(Ns_DbHandle **handles, char *pool, int nwant)
{
return Ns_DbPoolTimedGetMultipleHandles(handles, pool, nwant, 0);
}
/*
*----------------------------------------------------------------------
*
* Ns_DbPoolTimedGetMultipleHandles --
*
* Return 1 or more handles from a pool within the given number
* of seconds.
*
* Results:
* NS_OK if the handlers where allocated, NS_TIMEOUT if the
* thread could not wait long enough for the handles, NS_ERROR
* otherwise.
*
* Side effects:
* Given array of handles is updated with pointers to allocated
* handles. Also, database may be opened if needed.
*
*----------------------------------------------------------------------
*/
int
Ns_DbPoolTimedGetMultipleHandles(Ns_DbHandle **handles, char *pool,
int nwant, int wait)
{
Handle *handlePtr;
Handle **handlesPtrPtr = (Handle **) handles;
Pool *poolPtr;
Ns_Time timeout, *timePtr;
int i, ngot, status;
/*
* Verify the pool, the number of available handles in the pool,
* and that the calling thread does not already own handles from
* this pool.
*/
poolPtr = GetPool(pool);
if (poolPtr == NULL) {
Ns_Log(Error, "dbinit: no such pool '%s'", pool);
return NS_ERROR;
}
if (poolPtr->nhandles < nwant) {
Ns_Log(Error, "dbinit: "
"failed to get %d handles from a db pool of only %d handles: '%s'",
nwant, poolPtr->nhandles, pool);
return NS_ERROR;
}
ngot = IncrCount(poolPtr, nwant);
if (ngot > 0) {
Ns_Log(Error, "dbinit: db handle limit exceeded: "
"thread already owns %d handle%s from pool '%s'",
ngot, ngot == 1 ? "" : "s", pool);
IncrCount(poolPtr, -nwant);
return NS_ERROR;
}
/*
* Wait until this thread can be the exclusive thread aquireing
* handles and then wait until all requested handles are available,
* watching for timeout in either of these waits.
*/
if (wait < 0) {
timePtr = NULL;
} else {
Ns_GetTime(&timeout);
Ns_IncrTime(&timeout, wait, 0);
timePtr = &timeout;
}
status = NS_OK;
Ns_MutexLock(&poolPtr->lock);
while (status == NS_OK && poolPtr->waiting) {
status = Ns_CondTimedWait(&poolPtr->waitCond, &poolPtr->lock, timePtr);
}
if (status == NS_OK) {
poolPtr->waiting = 1;
while (status == NS_OK && ngot < nwant) {
while (status == NS_OK && poolPtr->firstPtr == NULL) {
status = Ns_CondTimedWait(&poolPtr->getCond, &poolPtr->lock,
timePtr);
}
if (poolPtr->firstPtr != NULL) {
handlePtr = poolPtr->firstPtr;
poolPtr->firstPtr = handlePtr->nextPtr;
handlePtr->nextPtr = NULL;
if (poolPtr->lastPtr == handlePtr) {
poolPtr->lastPtr = NULL;
}
handlesPtrPtr[ngot++] = handlePtr;
}
}
poolPtr->waiting = 0;
Ns_CondSignal(&poolPtr->waitCond);
}
Ns_MutexUnlock(&poolPtr->lock);
/*
* Handle special race condition where the final requested handle
* arrived just as the condition wait was timing out.
*/
if (status == NS_TIMEOUT && ngot == nwant) {
status = NS_OK;
}
/*
* If status is still ok, connect any handles not already connected,
* otherwise return any allocated handles back to the pool, then
* update the final number of handles owned by this thread.
*/
for (i = 0; status == NS_OK && i < ngot; ++i) {
handlePtr = handlesPtrPtr[i];
if (handlePtr->connected == NS_FALSE) {
status = Connect(handlePtr);
}
}
if (status != NS_OK) {
Ns_MutexLock(&poolPtr->lock);
while (ngot > 0) {
ReturnHandle(handlesPtrPtr[--ngot]);
}
if (poolPtr->waiting) {
Ns_CondSignal(&poolPtr->getCond);
}
Ns_MutexUnlock(&poolPtr->lock);
IncrCount(poolPtr, -nwant);
}
return status;
}
/*
*----------------------------------------------------------------------
*
* Ns_DbBouncePool --
*
* Close all handles in the pool.
*
* Results:
* NS_OK if pool was bounce, NS_ERROR otherwise.
*
* Side effects:
* Handles are all marked stale and then closed by CheckPool.
*
*----------------------------------------------------------------------
*/
int
Ns_DbBouncePool(char *pool)
{
Pool *poolPtr;
Handle *handlePtr;
poolPtr = GetPool(pool);
if (poolPtr == NULL) {
return NS_ERROR;
}
Ns_MutexLock(&poolPtr->lock);
poolPtr->stale_on_close++;
handlePtr = poolPtr->firstPtr;
while (handlePtr != NULL) {
if (handlePtr->connected) {
handlePtr->stale = 1;
}
handlePtr->stale_on_close = poolPtr->stale_on_close;
handlePtr = handlePtr->nextPtr;
}
Ns_MutexUnlock(&poolPtr->lock);
CheckPool(poolPtr);
return NS_OK;
}
/*
*----------------------------------------------------------------------
*
* NsDbInitPools --
*
* Initialize the database pools at startup.
*
* Results:
* None.
*
* Side effects:
* Pools may be created as configured.
*
*----------------------------------------------------------------------
*/
void
NsDbInitPools(void)
{
Tcl_HashEntry *hPtr;
Pool *poolPtr;
Ns_Set *pools;
char *path, *pool, *driver;
int new, i;
Ns_TlsAlloc(&tls, FreeTable);
/*
* Attempt to create each database pool.
*/
Tcl_InitHashTable(&serversTable, TCL_STRING_KEYS);
Tcl_InitHashTable(&poolsTable, TCL_STRING_KEYS);
pools = Ns_ConfigGetSection("ns/db/pools");
for (i = 0; pools != NULL && i < Ns_SetSize(pools); ++i) {
pool = Ns_SetKey(pools, i);
hPtr = Tcl_CreateHashEntry(&poolsTable, pool, &new);
if (!new) {
Ns_Log(Error, "dbinit: duplicate pool: %s", pool);
continue;
}
path = Ns_ConfigGetPath(NULL, NULL, "db", "pool", pool, NULL);
driver = Ns_ConfigGetValue(path, "driver");
poolPtr = CreatePool(pool, path, driver);
if (poolPtr == NULL) {
Tcl_DeleteHashEntry(hPtr);
} else {
Tcl_SetHashValue(hPtr, poolPtr);
}
}
Ns_RegisterProcInfo(CheckPool, "nsdb:check", CheckArgProc);
}
/*
*----------------------------------------------------------------------
*
* NsDbInitServer --
*
* Initialize a virtual server allowed and default options.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
void
NsDbInitServer(char *server)
{
Pool *poolPtr;
ServData *sdataPtr;
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
char *path, *pool, *p;
Ns_DString ds;
int new;
path = Ns_ConfigGetPath(server, NULL, "db", NULL);
/*
* Verify the default pool exists, if any.
*/
sdataPtr = ns_malloc(sizeof(ServData));
hPtr = Tcl_CreateHashEntry(&serversTable, server, &new);
Tcl_SetHashValue(hPtr, sdataPtr);
sdataPtr->defpool = Ns_ConfigGetValue(path, "defaultpool");
if (sdataPtr->defpool != NULL &&
(Tcl_FindHashEntry(&poolsTable, sdataPtr->defpool) == NULL)) {
Ns_Log(Error, "dbinit: no such default pool '%s'", sdataPtr->defpool);
sdataPtr->defpool = NULL;
}
/*
* Construct the allowed list and call the server-specific init.
*/
sdataPtr->allowed = "";
pool = Ns_ConfigGetValue(path, "pools");
if (pool != NULL && poolsTable.numEntries > 0) {
Ns_DStringInit(&ds);
if (STREQ(pool, "*")) {
hPtr = Tcl_FirstHashEntry(&poolsTable, &search);
while (hPtr != NULL) {
poolPtr = Tcl_GetHashValue(hPtr);
NsDbDriverInit(server, poolPtr->driverPtr);
Ns_DStringAppendArg(&ds, poolPtr->name);
hPtr = Tcl_NextHashEntry(&search);
}
} else {
p = pool;
while (p != NULL && *p != '\0') {
p = strchr(pool, ',');
if (p != NULL) {
*p = '\0';
}
hPtr = Tcl_FindHashEntry(&poolsTable, pool);
if (hPtr != NULL) {
poolPtr = Tcl_GetHashValue(hPtr);
NsDbDriverInit(server, poolPtr->driverPtr);
Ns_DStringAppendArg(&ds, poolPtr->name);
}
if (p != NULL) {
*p++ = ',';
}
pool = p;
}
}
sdataPtr->allowed = ns_malloc((size_t)(ds.length + 1));
memcpy(sdataPtr->allowed, ds.string, (size_t)(ds.length + 1));
Ns_DStringFree(&ds);
}
}
/*
*----------------------------------------------------------------------
*
* NsDbDisconnect --
*
* Disconnect a handle by closing the database if needed.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
void
NsDbDisconnect(Ns_DbHandle *handle)
{
Handle *handlePtr = (Handle *) handle;
NsDbClose(handle);
handlePtr->connected = NS_FALSE;
handlePtr->atime = handlePtr->otime = 0;
handlePtr->stale = NS_FALSE;
}
/*
*----------------------------------------------------------------------
*
* NsDbLogSql --
*
* Log a SQL statement depending on the verbose state of the
* handle.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
void
NsDbLogSql(Ns_DbHandle *handle, char *sql)
{
Handle *handlePtr = (Handle *) handle;
if (handle->dsExceptionMsg.length > 0) {
if (handlePtr->poolPtr->fVerboseError || handle->verbose) {
Ns_Log(Error, "dbinit: error(%s,%s): '%s'",
handle->datasource, handle->dsExceptionMsg.string, sql);
}
} else if (handle->verbose) {
Ns_Log(Notice, "dbinit: sql(%s): '%s'", handle->datasource, sql);
}
}
/*
*----------------------------------------------------------------------
*
* NsDbGetDriver --
*
* Return a pointer to the driver structure for a handle.
*
* Results:
* Pointer to driver or NULL on error.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
struct DbDriver *
NsDbGetDriver(Ns_DbHandle *handle)
{
Handle *handlePtr = (Handle *) handle;
if (handlePtr != NULL && handlePtr->poolPtr != NULL) {
return handlePtr->poolPtr->driverPtr;
}
return NULL;
}
/*
*----------------------------------------------------------------------
*
* GetPool --
*
* Return the Pool structure for the given pool name.
*
* Results:
* Pointer to Pool structure or NULL if pool does not exist.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static Pool *
GetPool(char *pool)
{
Tcl_HashEntry *hPtr;
hPtr = Tcl_FindHashEntry(&poolsTable, pool);
if (hPtr == NULL) {
return NULL;
}
return (Pool *) Tcl_GetHashValue(hPtr);
}
/*
*----------------------------------------------------------------------
*
* ReturnHandle --
*
* Return a handle to its pool. Connected handles are pushed on
* the front of the list, disconnected handles are appened to
* the end.
*
* Results:
* None.
*
* Side effects:
* Handle is returned to the pool. Note: The pool lock must be
* held by the caller and this function does not signal a thread
* waiting for handles.
*
*----------------------------------------------------------------------
*/
static void
ReturnHandle(Handle *handlePtr)
{
Pool *poolPtr;
poolPtr = handlePtr->poolPtr;
if (poolPtr->firstPtr == NULL) {
poolPtr->firstPtr = poolPtr->lastPtr = handlePtr;
handlePtr->nextPtr = NULL;
} else if (handlePtr->connected) {
handlePtr->nextPtr = poolPtr->firstPtr;
poolPtr->firstPtr = handlePtr;
} else {
poolPtr->lastPtr->nextPtr = handlePtr;
poolPtr->lastPtr = handlePtr;
handlePtr->nextPtr = NULL;
}
}
/*
*----------------------------------------------------------------------
*
* IsStale --
*
* Check to see if a handle is stale.
*
* Results:
* NS_TRUE if handle stale, NS_FALSE otherwise.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static int
IsStale(Handle *handlePtr, time_t now)
{
time_t minAccess, minOpen;
if (handlePtr->connected) {
minAccess = now - handlePtr->poolPtr->maxidle;
minOpen = now - handlePtr->poolPtr->maxopen;
if ((handlePtr->poolPtr->maxidle && handlePtr->atime < minAccess) ||
(handlePtr->poolPtr->maxopen && (handlePtr->otime < minOpen)) ||
(handlePtr->stale == NS_TRUE) ||
(handlePtr->poolPtr->stale_on_close > handlePtr->stale_on_close)) {
if (handlePtr->poolPtr->fVerbose) {
Ns_Log(Notice, "dbinit: closing %s handle in pool '%s'",
handlePtr->atime < minAccess ? "idle" : "old",
handlePtr->poolname);
}
return NS_TRUE;
}
}
return NS_FALSE;
}
/*
*----------------------------------------------------------------------
*
* CheckArgProc --
*
* Ns_ArgProc callback for the pool checker.
*
* Results:
* None.
*
* Side effects:
* Copies name of pool to given dstring.
*
*----------------------------------------------------------------------
*/
static void
CheckArgProc(Tcl_DString *dsPtr, void *arg)
{
Pool *poolPtr = arg;
Tcl_DStringAppendElement(dsPtr, poolPtr->name);
}
/*
*----------------------------------------------------------------------
*
* CheckPool --
*
* Verify all handles in a pool are not stale.
*
* Results:
* None.
*
* Side effects:
* Stale handles, if any, are closed.
*
*----------------------------------------------------------------------
*/
static void
CheckPool(void *arg)
{
Pool *poolPtr = arg;
Handle *handlePtr, *nextPtr;
Handle *checkedPtr;
time_t now;
time(&now);
checkedPtr = NULL;
/*
* Grab the entire list of handles from the pool.
*/
Ns_MutexLock(&poolPtr->lock);
handlePtr = poolPtr->firstPtr;
poolPtr->firstPtr = poolPtr->lastPtr = NULL;
Ns_MutexUnlock(&poolPtr->lock);
/*
* Run through the list of handles, closing any
* which have gone stale, and then return them
* all to the pool.
*/
if (handlePtr != NULL) {
while (handlePtr != NULL) {
nextPtr = handlePtr->nextPtr;
if (IsStale(handlePtr, now)) {
NsDbDisconnect((Ns_DbHandle *) handlePtr);
}
handlePtr->nextPtr = checkedPtr;
checkedPtr = handlePtr;
handlePtr = nextPtr;
}
Ns_MutexLock(&poolPtr->lock);
handlePtr = checkedPtr;
while (handlePtr != NULL) {
nextPtr = handlePtr->nextPtr;
ReturnHandle(handlePtr);
handlePtr = nextPtr;
}
if (poolPtr->waiting) {
Ns_CondSignal(&poolPtr->getCond);
}
Ns_MutexUnlock(&poolPtr->lock);
}
}
/*
*----------------------------------------------------------------------
*
* CreatePool --
*
* Create a new pool using the given driver.
*
* Results:
* Pointer to newly allocated Pool structure.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static Pool *
CreatePool(char *pool, char *path, char *driver)
{
Pool *poolPtr;
Handle *handlePtr;
struct DbDriver *driverPtr;
int i;
char *source;
if (driver == NULL) {
Ns_Log(Error, "dbinit: no driver for pool '%s'", pool);
return NULL;
}
driverPtr = NsDbLoadDriver(driver);
if (driverPtr == NULL) {
return NULL;
}
source = Ns_ConfigGetValue(path, "datasource");
if (source == NULL) {
Ns_Log(Error, "dbinit: missing datasource for pool '%s'", pool);
return NULL;
}
poolPtr = ns_malloc(sizeof(Pool));
poolPtr->driver = driver;
poolPtr->driverPtr = driverPtr;
Ns_MutexInit(&poolPtr->lock);
Ns_MutexSetName2(&poolPtr->lock, "nsdb", pool);
Ns_CondInit(&poolPtr->waitCond);
Ns_CondInit(&poolPtr->getCond);
poolPtr->source = source;
poolPtr->name = pool;
poolPtr->waiting = 0;
poolPtr->user = Ns_ConfigGetValue(path, "user");
poolPtr->pass = Ns_ConfigGetValue(path, "password");
poolPtr->desc = Ns_ConfigGetValue("ns/db/pools", pool);
poolPtr->stale_on_close = 0;
if (!Ns_ConfigGetBool(path, "verbose", &poolPtr->fVerbose)) {
poolPtr->fVerbose = 0;
}
if (!Ns_ConfigGetBool(path, "logsqlerrors", &poolPtr->fVerboseError)) {
poolPtr->fVerboseError = 0;
}
if (!Ns_ConfigGetInt(path, "connections", &poolPtr->nhandles)
|| poolPtr->nhandles <= 0) {
poolPtr->nhandles = 2;
}
if (Ns_ConfigGetInt(path, "MaxIdle", &i) == NS_FALSE || i < 0) {
i = 600; /* 10 minutes */
}
poolPtr->maxidle = i;
if (Ns_ConfigGetInt(path, "MaxOpen", &i) == NS_FALSE || i < 0) {
i = 3600; /* 1 hour */
}
poolPtr->maxopen = i;
poolPtr->firstPtr = poolPtr->lastPtr = NULL;
for (i = 0; i < poolPtr->nhandles; ++i) {
handlePtr = ns_malloc(sizeof(Handle));
Ns_DStringInit(&handlePtr->dsExceptionMsg);
handlePtr->poolPtr = poolPtr;
handlePtr->connection = NULL;
handlePtr->connected = NS_FALSE;
handlePtr->fetchingRows = 0;
handlePtr->row = Ns_SetCreate(NULL);
handlePtr->cExceptionCode[0] = '\0';
handlePtr->otime = handlePtr->atime = 0;
handlePtr->stale = NS_FALSE;
handlePtr->stale_on_close = 0;
/*
* The following elements of the Handle structure could
* be obtained by dereferencing the poolPtr. They're
* only needed to maintain the original Ns_DbHandle
* structure definition which was designed to allow
* handles outside of pools, a feature no longer supported.
*/
handlePtr->driver = driver;
handlePtr->datasource = poolPtr->source;
handlePtr->user = poolPtr->user;
handlePtr->password = poolPtr->pass;
handlePtr->verbose = poolPtr->fVerbose;
handlePtr->poolname = pool;
ReturnHandle(handlePtr);
}
if (!Ns_ConfigGetInt(path, "checkinterval", &i) || i < 0) {
i = 600; /* 10 minutes. */
}
Ns_ScheduleProc(CheckPool, poolPtr, 0, i);
return poolPtr;
}
/*
*----------------------------------------------------------------------
*
* Connect --
*
* Connect a handle by opening the database.
*
* Results:
* NS_OK if connect ok, NS_ERROR otherwise.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static int
Connect(Handle *handlePtr)
{
int status;
status = NsDbOpen((Ns_DbHandle *) handlePtr);
if (status != NS_OK) {
handlePtr->connected = NS_FALSE;
handlePtr->atime = handlePtr->otime = 0;
handlePtr->stale = NS_FALSE;
} else {
handlePtr->connected = NS_TRUE;
handlePtr->atime = handlePtr->otime = time(NULL);
}
return status;
}
/*
*----------------------------------------------------------------------
*
* IncrCount --
*
* Update per-thread count of allocated handles.
*
* Results:
* Previous count of allocated handles.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static int
IncrCount(Pool *poolPtr, int incr)
{
Tcl_HashTable *tablePtr;
Tcl_HashEntry *hPtr;
int prev, count, new;
tablePtr = Ns_TlsGet(&tls);
if (tablePtr == NULL) {
tablePtr = ns_malloc(sizeof(Tcl_HashTable));
Tcl_InitHashTable(tablePtr, TCL_ONE_WORD_KEYS);
Ns_TlsSet(&tls, tablePtr);
}
hPtr = Tcl_CreateHashEntry(tablePtr, (char *) poolPtr, &new);
if (new) {
prev = 0;
} else {
prev = (int) Tcl_GetHashValue(hPtr);
}
count = prev + incr;
if (count == 0) {
Tcl_DeleteHashEntry(hPtr);
} else {
Tcl_SetHashValue(hPtr, (ClientData) count);
}
return prev;
}
/*
*----------------------------------------------------------------------
*
* GetServer --
*
* Get per-server data.
*
* Results:
* Pointer to per-server data.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static ServData *
GetServer(char *server)
{
Tcl_HashEntry *hPtr;
hPtr = Tcl_FindHashEntry(&serversTable, server);
if (hPtr != NULL) {
return Tcl_GetHashValue(hPtr);
}
return NULL;
}
/*
*----------------------------------------------------------------------
*
* FreeTable --
*
* Free the per-thread count of allocated handles table.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
FreeTable(void *arg)
{
Tcl_HashTable *tablePtr = arg;
Tcl_DeleteHashTable(tablePtr);
ns_free(tablePtr);
}
|
Back to SourceForge.net Powered by ViewCVS 1.0-dev |