/* * 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.lcs.mit.edu/. * * 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. */ #include "nsd.h" #define CONFIG_USER "user" /* Database login user */ #define CONFIG_PASS "password" /* Database login passowrd */ #define CONFIG_SOURCE "datasource" /* Database location string */ #define CONFIG_VERBOSE "verbose" /* Log SQL statements and errors */ #define CONFIG_VERBOSE_ERROR "logsqlerrors" /* Log SQL errors only */ #define CONFIG_CONNS "connections" /* Number of database connections. */ /* * 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_Tls ngotTls; #ifdef NOTDEF Ns_Mutex lock; Ns_Cond waitCond; Ns_Cond getCond; #endif char *driver; struct DbDriver *driverPtr; int waiting; int nhandles; struct Handle *firstPtr; struct Handle *lastPtr; int fVerbose; int fVerboseError; time_t tMaxIdle; time_t tMaxOpen; 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 tOpen; time_t tAccess; int stale; int stale_on_close; } Handle; /* * Local functions defined in this file */ static Pool *GetPool(char *pool); static void ReturnHandle(Handle * handle); static void CheckPool(Pool *poolPtr); /*static Ns_Callback CheckPools;*/ static int IsStale(Handle *); static int Connect(Handle *); static Pool *CreatePool(char *pool, char *path, char *driver); /* * Static variables defined in this file */ static Tcl_HashTable poolsTable; static char *defaultPool; static char *allowedPools; /* * Global variables defined in this file */ Ns_ModLogHandle nsDBModLogHandle; /* *========================================================================== * API functions *========================================================================== */ /* *---------------------------------------------------------------------- * * 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 *hServer) { return defaultPool; } /* *---------------------------------------------------------------------- * * Ns_DbPoolList -- * * Return the list of all pools. * * Results: * Double-null terminated list of pool names. * * Side effects: * None. * *---------------------------------------------------------------------- */ char * Ns_DbPoolList(char *hServer) { return allowedPools; } /* *---------------------------------------------------------------------- * * 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 *hServer, char *pool) { #ifdef NOTDEF register char *p; p = allowedPools; if (p != NULL) { while (*p != '\0') { if (STREQ(pool, p)) { return NS_TRUE; } p = p + strlen(p) + 1; } } return NS_FALSE; #endif return NS_TRUE; } /* *---------------------------------------------------------------------- * * 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; int ngot; 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. */ if (IsStale(handlePtr)) { NsDbDisconnect(handle); } else { time(&handlePtr->tAccess); } #ifdef NOTDEF Ns_MutexLock(&poolPtr->lock); ReturnHandle(handlePtr); if (poolPtr->waiting) { Ns_CondSignal(&poolPtr->getCond); } Ns_MutexUnlock(&poolPtr->lock); #else ReturnHandle(handlePtr); #endif ngot = (int) Ns_TlsGet(&poolPtr->ngotTls); --ngot; Ns_TlsSet(&poolPtr->ngotTls, (void *) ngot); } /* *---------------------------------------------------------------------- * * 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; #ifdef NOTDEF Ns_Time timeout, *timePtr; #endif 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_ModLog(Error, nsDBModLogHandle, "no such pool: %s", pool); return NS_ERROR; } if (poolPtr->nhandles < nwant) { Ns_ModLog(Error, nsDBModLogHandle, "attempt to get %d handles from pool of %d: %s", nwant, poolPtr->nhandles, pool); return NS_ERROR; } ngot = (int) Ns_TlsGet(&poolPtr->ngotTls); if (ngot > 0) { Ns_ModLog(Error, nsDBModLogHandle, "thread already owns %d handle%s from pool: %s", ngot, ngot > 1 ? "s" : "", pool); return NS_ERROR; } #ifdef NOTDEF /* * 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; #else status = NS_OK; #endif while (status == NS_OK && ngot < nwant) { #ifdef NOTDEF while (status == NS_OK && poolPtr->firstPtr == NULL) { status = Ns_CondTimedWait(&poolPtr->getCond, &poolPtr->lock, timePtr); } #endif if (poolPtr->firstPtr != NULL) { handlePtr = poolPtr->firstPtr; poolPtr->firstPtr = handlePtr->nextPtr; handlePtr->nextPtr = NULL; if (poolPtr->lastPtr == handlePtr) { poolPtr->lastPtr = NULL; } handlesPtrPtr[ngot++] = handlePtr; } } #ifdef NOTDEF 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; } #endif /* * 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) { #ifdef NOTDEF Ns_MutexLock(&poolPtr->lock); #endif while (ngot > 0) { ReturnHandle(handlesPtrPtr[--ngot]); } #ifdef NOTDEF if (poolPtr->waiting) { Ns_CondSignal(&poolPtr->getCond); } Ns_MutexUnlock(&poolPtr->lock); #endif } Ns_TlsSet(&poolPtr->ngotTls, (void *) ngot); 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; } #ifdef NOTDEF Ns_MutexLock(&poolPtr->lock); #endif 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; } #ifdef NOTDEF Ns_MutexUnlock(&poolPtr->lock); #endif CheckPool(poolPtr); return NS_OK; } /* *========================================================================== * Exported functions *========================================================================== */ /* *---------------------------------------------------------------------- * * NsDbInit -- * * Initialize the database pools at startup. * * Results: * None. * * Side effects: * Pools may be created as configured. * *---------------------------------------------------------------------- */ void NsDbInit(void) { Tcl_HashEntry *hPtr; Tcl_HashSearch search; Pool *poolPtr; #ifdef NOTDEF Ns_Set *pools; #endif Ns_DString ds; char *path, *allowed, *pool, *driver; register char *p; int new, /* i, */ tcheck; /* * register the sub-realm */ NsModLogRegSubRealm("db", &nsDBModLogHandle); Ns_DStringInit(&ds); Tcl_InitHashTable(&poolsTable, TCL_STRING_KEYS); /* * Add the allowed pools to the poolsTable. */ path = Ns_ConfigGetPath(nsServer, NULL, "db", NULL); allowed = Ns_ConfigGet(path, "pools"); defaultPool = Ns_ConfigGet(path, "defaultpool"); #ifdef NOTDEF pools = Ns_ConfigSection("ns/db/pools"); if (pools != NULL && allowed != NULL) { if (STREQ(allowed, "*")) { for (i = 0; i < Ns_SetSize(pools); ++i) { pool = Ns_SetKey(pools, i); Tcl_CreateHashEntry(&poolsTable, pool, &new); } } else { #endif /* Tcl code should eventually set "allowed" to the list of * all defined pools if the config file itself doesn't provide * a value... */ p = allowed; while (p != NULL && *p != '\0') { p = strchr(allowed, ','); if (p != NULL) { *p = '\0'; } Tcl_CreateHashEntry(&poolsTable, allowed, &new); if (p != NULL) { *p++ = ','; } allowed = p; } #ifdef NOTDEF } } #endif /* * Attempt to create a database pool for each entry in the poolsTable. */ hPtr = Tcl_FirstHashEntry(&poolsTable, &search); while (hPtr != NULL) { pool = Tcl_GetHashKey(&poolsTable, hPtr); path = Ns_ConfigGetPath(NULL, NULL, "db", "pool", pool, NULL); driver = Ns_ConfigGet(path, "driver"); poolPtr = NULL; if (driver == NULL) { Ns_ModLog(Error, nsDBModLogHandle, "no driver defined for pool: %s", pool); } else { poolPtr = CreatePool(pool, path, driver); } if (poolPtr != NULL) { Tcl_SetHashValue(hPtr, poolPtr); } else { Tcl_DeleteHashEntry(hPtr); } hPtr = Tcl_NextHashEntry(&search); } /* * Verify the default pool exists, if any. */ if (defaultPool != NULL) { hPtr = Tcl_FindHashEntry(&poolsTable, defaultPool); if (hPtr == NULL) { Ns_ModLog(Error, nsDBModLogHandle, "no such default pool: %s", defaultPool); defaultPool = NULL; } } /* * Construct the allowedPools list and initialize the nsdb Tcl * commands if any pools were actually created. */ if (poolsTable.numEntries == 0) { Ns_ModLog(Debug, nsDBModLogHandle, "no configured pools"); allowedPools = ""; } else { tcheck = INT_MAX; Ns_DStringInit(&ds); hPtr = Tcl_FirstHashEntry(&poolsTable, &search); while (hPtr != NULL) { poolPtr = Tcl_GetHashValue(hPtr); if (tcheck > poolPtr->tMaxIdle) { tcheck = poolPtr->tMaxIdle; } NsDbServerInit(poolPtr->driverPtr); Ns_DStringAppendArg(&ds, poolPtr->name); hPtr = Tcl_NextHashEntry(&search); } allowedPools = ns_malloc(ds.length + 1); memcpy(allowedPools, ds.string, ds.length + 1); Ns_DStringFree(&ds); NsDbTclInit(); #ifdef YYYY_TO_FIX Ns_ScheduleProc(CheckPools, NULL, 1, tcheck); #endif } } /* *---------------------------------------------------------------------- * * 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->tAccess = handlePtr->tOpen = 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_ModLog(Error, nsDBModLogHandle, "error(%s, %s): %s", handle->datasource, handle->dsExceptionMsg.string, sql); } } else if (handle->verbose) { Ns_ModLog(Notice, nsDBModLogHandle, "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; } /* *========================================================================== * Static functions *========================================================================== */ /* *---------------------------------------------------------------------- * * 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, minAccess, minOpen; if (handlePtr->connected) { time(&now); minAccess = now - handlePtr->poolPtr->tMaxIdle; minOpen = now - handlePtr->poolPtr->tMaxOpen; if ((handlePtr->poolPtr->tMaxIdle && handlePtr->tAccess < minAccess) || (handlePtr->poolPtr->tMaxOpen && (handlePtr->tOpen < minOpen)) || (handlePtr->stale == NS_TRUE) || (handlePtr->poolPtr->stale_on_close > handlePtr->stale_on_close)) { if (handlePtr->poolPtr->fVerbose) { Ns_ModLog(Notice, nsDBModLogHandle, "closing %s handle in pool: %s", handlePtr->tAccess < minAccess ? "idle" : "old", handlePtr->poolname); } return NS_TRUE; } } return NS_FALSE; } /* *---------------------------------------------------------------------- * * CheckPools -- * * Schedule procedure to check all pools. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ #if 0 static void CheckPools(void *ignored) { Tcl_HashEntry *hPtr; Tcl_HashSearch search; Pool *poolPtr; hPtr = Tcl_FirstHashEntry(&poolsTable, &search); while (hPtr != NULL) { poolPtr = Tcl_GetHashValue(hPtr); CheckPool(poolPtr); hPtr = Tcl_NextHashEntry(&search); } } #endif /* *---------------------------------------------------------------------- * * CheckPool -- * * Verify all handles in a pool are not stale. * * Results: * None. * * Side effects: * Stale handles, if any, are closed. * *---------------------------------------------------------------------- */ static void CheckPool(Pool *poolPtr) { Handle *handlePtr, *nextPtr; Handle *checkedPtr; checkedPtr = NULL; /* * Grab the entire list of handles from the pool. */ #ifdef NOTDEF Ns_MutexLock(&poolPtr->lock); #endif handlePtr = poolPtr->firstPtr; poolPtr->firstPtr = poolPtr->lastPtr = NULL; #ifdef NOTDEF Ns_MutexUnlock(&poolPtr->lock); #endif /* * 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)) { NsDbDisconnect((Ns_DbHandle *) handlePtr); } handlePtr->nextPtr = checkedPtr; checkedPtr = handlePtr; handlePtr = nextPtr; } #ifdef NOTDEF Ns_MutexLock(&poolPtr->lock); #endif handlePtr = checkedPtr; while (handlePtr != NULL) { nextPtr = handlePtr->nextPtr; ReturnHandle(handlePtr); handlePtr = nextPtr; } #ifdef NOTDEF if (poolPtr->waiting) { Ns_CondSignal(&poolPtr->getCond); } Ns_MutexUnlock(&poolPtr->lock); #endif } } /* XXX --- must modify to take params as arguments, rather * than calling ConfigGet. And make it a tcl command. */ /* *---------------------------------------------------------------------- * * 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; driverPtr = NsDbLoadDriver(driver); if (driverPtr == NULL) { return NULL; } poolPtr = ns_malloc(sizeof(Pool)); poolPtr->driver = driver; poolPtr->driverPtr = driverPtr; #ifdef NOTDEF Ns_MutexInit(&poolPtr->lock); Ns_CondInit(&poolPtr->waitCond); Ns_CondInit(&poolPtr->getCond); #endif Ns_TlsAlloc(&poolPtr->ngotTls, NULL); poolPtr->name = pool; poolPtr->waiting = 0; poolPtr->user = Ns_ConfigGet(path, CONFIG_USER); poolPtr->pass = Ns_ConfigGet(path, CONFIG_PASS); poolPtr->source = Ns_ConfigGet(path, CONFIG_SOURCE); poolPtr->desc = Ns_ConfigGet("ns/db/pools", pool); poolPtr->stale_on_close = 0; if (Ns_ConfigGetBool(path, CONFIG_VERBOSE, &poolPtr->fVerbose) == NS_FALSE) { poolPtr->fVerbose = 0; } if (Ns_ConfigGetBool(path, CONFIG_VERBOSE_ERROR, &poolPtr->fVerboseError) == NS_FALSE) { poolPtr->fVerboseError = 0; } if (Ns_ConfigGetInt(path, CONFIG_CONNS, &poolPtr->nhandles) == NS_FALSE || poolPtr->nhandles <= 0) { poolPtr->nhandles = 2; } if (Ns_ConfigGetInt(path, "MaxIdle", &i) == NS_FALSE || i <= 0) { i = 600; /* 10 minutes */ } poolPtr->tMaxIdle = i; if (Ns_ConfigGetInt(path, "MaxOpen", &i) == NS_FALSE || i <= 0) { i = 3600; /* 1 hour */ } poolPtr->tMaxOpen = 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->tOpen = handlePtr->tAccess = 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); } 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->tAccess = handlePtr->tOpen = 0; handlePtr->stale = NS_FALSE; } else { handlePtr->connected = NS_TRUE; handlePtr->tAccess = handlePtr->tOpen = time(NULL); } return status; }