Fixed misnamed Ns_TclDbGetHandle function.
/*
* 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.
*/
/*
* dbtcl.c --
*
* Tcl database access routines.
*/
static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/aolserver/nsdb/dbtcl.c,v 1.6 2005/03/28 00:13:54 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__;
#include "db.h"
/*
* The following structure maintains per-interp data.
*/
typedef struct InterpData {
Tcl_Interp *interp;
char *server;
int cleanup;
Tcl_HashTable dbs;
} InterpData;
/*
* Local functions defined in this file
*/
static void EnterRow(Tcl_Interp *interp, Ns_Set *row, int flags,
int *statusPtr);
static void EnterHandle(InterpData *idataPtr, Tcl_Interp *interp,
Ns_DbHandle *handle);
static int GetHandle(InterpData *idataPtr, char *id,
Ns_DbHandle **handle, int clear, Tcl_HashEntry **hPtrPtr);
static int GetHandleObj(InterpData *idataPtr, Tcl_Obj *obj,
Ns_DbHandle **handle, int clear, Tcl_HashEntry **hPtrPtr);
static Tcl_InterpDeleteProc FreeData;
static Ns_TclDeferProc ReleaseDbs;
static Tcl_ObjCmdProc DbObjCmd;
static Tcl_CmdProc QuoteListToListCmd, GetCsvCmd, DbErrorCodeCmd,
DbErrorMsgCmd, GetCsvCmd, DbConfigPathCmd, PoolDescriptionCmd;
static char *datakey = "nsdb:data";
/*
*----------------------------------------------------------------------
* Ns_TclDbGetHandle --
*
* Get database handle from its handle id.
*
* Results:
* See GetHandle().
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
Ns_TclDbGetHandle(Tcl_Interp *interp, char *id, Ns_DbHandle **handle)
{
InterpData *idataPtr;
idataPtr = Tcl_GetAssocData(interp, datakey, NULL);
if (idataPtr == NULL) {
return TCL_ERROR;
}
return GetHandle(idataPtr, id, handle, 0, NULL);
}
/*
*----------------------------------------------------------------------
*
* NsDbAddCmds --
*
* Add the nsdb commands.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
NsDbAddCmds(Tcl_Interp *interp, void *arg)
{
InterpData *idataPtr;
/*
* Initialize the per-interp data.
*/
idataPtr = ns_malloc(sizeof(InterpData));
idataPtr->server = arg;
idataPtr->interp = interp;
idataPtr->cleanup = 0;
Tcl_InitHashTable(&idataPtr->dbs, TCL_STRING_KEYS);
Tcl_SetAssocData(interp, datakey, FreeData, idataPtr);
Tcl_CreateObjCommand(interp, "ns_db", DbObjCmd, idataPtr, NULL);
Tcl_CreateCommand(interp, "ns_quotelisttolist", QuoteListToListCmd, idataPtr, NULL);
Tcl_CreateCommand(interp, "ns_getcsv", GetCsvCmd, idataPtr, NULL);
Tcl_CreateCommand(interp, "ns_dberrorcode", DbErrorCodeCmd, idataPtr, NULL);
Tcl_CreateCommand(interp, "ns_dberrormsg", DbErrorMsgCmd, idataPtr, NULL);
Tcl_CreateCommand(interp, "ns_getcsv", GetCsvCmd, idataPtr, NULL);
Tcl_CreateCommand(interp, "ns_dbconfigpath", DbConfigPathCmd, idataPtr, NULL);
Tcl_CreateCommand(interp, "ns_pooldescription", PoolDescriptionCmd, idataPtr, NULL);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* DbCmd --
*
* Implement the AOLserver ns_db Tcl command.
*
* Results:
* Return TCL_OK upon success and TCL_ERROR otherwise.
*
* Side effects:
* Depends on the command.
*
*----------------------------------------------------------------------
*/
static int
DbObjCmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj **objv)
{
#define MAXHANDLES 4
InterpData *idataPtr = data;
Ns_DbHandle *handle, **handlesPtrPtr, *staticHandles[MAXHANDLES];
Ns_Set *row;
Tcl_HashEntry *hPtr;
Tcl_Obj *resultPtr;
char *arg, *pool, buf[32];
int timeout, nhandles, n, status;
static CONST char *opts[] = {
"getrow", "gethandle", "releasehandle", "select", "dml",
"1row", "0or1row", "bindrow", "exec", "sp_exec", "sp_getparams",
"sp_returncode", "sp_setparam", "sp_start", "exception",
"flush", "bouncepool", "cancel", "connected", "datasource",
"dbtype", "disconnect", "driver", "interpretsqlfile",
"password", "poolname", "pools", "resethandle", "setexception",
"user", "verbose", NULL
}; enum {
Db_getrowIdx, Db_gethandleIdx, Db_releasehandleIdx,
Db_selectIdx, Db_dmlIdx, Db_1rowIdx, Db_0or1rowIdx,
Db_bindrowIdx, Db_execIdx, Db_sp_execIdx, Db_sp_getparamsIdx,
Db_sp_returncodeIdx, Db_sp_setparamIdx, Db_sp_startIdx,
Db_exceptionIdx, Db_flushIdx, Db_bouncepoolIdx, Db_cancelIdx,
Db_connectedIdx, Db_datasourceIdx, Db_dbtypeIdx, Db_disconnectIdx,
Db_driverIdx, Db_interpretsqlfileIdx, Db_passwordIdx,
Db_poolnameIdx, Db_poolsIdx, Db_resethandleIdx, Db_setexceptionIdx,
Db_userIdx, Db_verboseIdx
} opt;
static CONST char *spopts[] = {
"in", "out", NULL
};
enum {
Sp_inIdx, Sp_outIdx
} spopt;
if (objc < 2) {
Tcl_WrongNumArgs(interp, 1, objv, "option ?args?");
return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(interp, objv[1], opts, "option", 1,
(int *) &opt) != TCL_OK) {
return TCL_ERROR;
}
resultPtr = Tcl_GetObjResult(interp);
handle = NULL;
status = NS_OK;
switch (opt) {
/*
* The following options require just a db arg and clears
* any exception when getting the handle.
*/
case Db_bindrowIdx:
case Db_cancelIdx:
case Db_connectedIdx:
case Db_datasourceIdx:
case Db_dbtypeIdx:
case Db_disconnectIdx:
case Db_driverIdx:
case Db_flushIdx:
case Db_passwordIdx:
case Db_poolnameIdx:
case Db_releasehandleIdx:
case Db_resethandleIdx:
case Db_sp_execIdx:
case Db_sp_getparamsIdx:
case Db_sp_returncodeIdx:
case Db_userIdx:
if (objc != 3) {
Tcl_WrongNumArgs(interp, 2, objv, "dbId");
return 0;
}
if (GetHandleObj(idataPtr, objv[2], &handle, 1, &hPtr) != TCL_OK) {
return TCL_ERROR;
}
switch ((int) opt) {
case Db_bindrowIdx:
row = Ns_DbBindRow(handle);
EnterRow(interp, row, NS_TCL_SET_STATIC, &status);
break;
case Db_cancelIdx:
status = Ns_DbCancel(handle);
break;
case Db_connectedIdx:
Tcl_SetBooleanObj(resultPtr, handle->connected);
break;
case Db_datasourceIdx:
Tcl_SetResult(interp, handle->datasource, TCL_STATIC);
break;
case Db_dbtypeIdx:
Tcl_SetResult(interp, Ns_DbDriverDbType(handle), TCL_STATIC);
break;
case Db_disconnectIdx:
NsDbDisconnect(handle);
break;
case Db_driverIdx:
Tcl_SetResult(interp, Ns_DbDriverName(handle), TCL_STATIC);
break;
case Db_flushIdx:
status = Ns_DbFlush(handle);
break;
case Db_passwordIdx:
Tcl_SetResult(interp, handle->password, TCL_VOLATILE);
break;
case Db_poolnameIdx:
Tcl_SetResult(interp, handle->poolname, TCL_VOLATILE);
break;
case Db_releasehandleIdx:
Tcl_DeleteHashEntry(hPtr);
Ns_DbPoolPutHandle(handle);
break;
case Db_resethandleIdx:
status = Ns_DbResetHandle(handle);
if (status == NS_OK) {
Tcl_SetIntObj(resultPtr, NS_OK);
}
break;
case Db_sp_execIdx:
status = Ns_DbSpExec(handle);
if (status == NS_DML) {
Tcl_SetResult(interp, "NS_DML", TCL_STATIC);
} else if (status == NS_ROWS) {
Tcl_SetResult(interp, "NS_ROWS", TCL_STATIC);
}
break;
case Db_sp_getparamsIdx:
row = Ns_DbSpGetParams(handle);
EnterRow(interp, row, NS_TCL_SET_DYNAMIC, &status);
break;
case Db_sp_returncodeIdx:
status = Ns_DbSpReturnCode(handle, buf, 32);
if (status == NS_OK) {
Tcl_SetResult(interp, buf, TCL_VOLATILE);
}
break;
case Db_userIdx:
Tcl_SetResult(interp, handle->user, TCL_VOLATILE);
break;
}
break;
/*
* The following also requires just a db argument and
* preserves any exception.
*/
case Db_exceptionIdx:
if (objc != 3) {
Tcl_WrongNumArgs(interp, 2, objv, "dbId");
return 0;
}
if (GetHandleObj(idataPtr, objv[2], &handle, 0, &hPtr) != TCL_OK) {
return TCL_ERROR;
}
Tcl_AppendElement(interp, handle->cExceptionCode);
Tcl_AppendElement(interp, handle->dsExceptionMsg.string);
break;
/*
* The following options require a db and extra string arg and
* clear any exception.
*/
case Db_0or1rowIdx:
case Db_1rowIdx:
case Db_dmlIdx:
case Db_execIdx:
case Db_getrowIdx:
case Db_interpretsqlfileIdx:
case Db_selectIdx:
case Db_sp_startIdx:
if (objc != 4) {
Tcl_WrongNumArgs(interp, 2, objv, "dbId arg");
return 0;
}
if (GetHandleObj(idataPtr, objv[2], &handle, 1, &hPtr) != TCL_OK) {
return TCL_ERROR;
}
arg = Tcl_GetString(objv[3]);
switch ((int) opt) {
case Db_0or1rowIdx:
row = Ns_Db0or1Row(handle, arg, &n);
if (row != NULL && n == 0) {
Ns_SetFree(row);
} else {
EnterRow(interp, row, NS_TCL_SET_DYNAMIC, &status);
}
break;
case Db_1rowIdx:
row = Ns_Db1Row(handle, arg);
EnterRow(interp, row, NS_TCL_SET_DYNAMIC, &status);
break;
case Db_dmlIdx:
status = Ns_DbDML(handle, arg);
break;
case Db_execIdx:
status = Ns_DbExec(handle, arg);
if (status == NS_DML) {
Tcl_SetResult(interp, "NS_DML", TCL_STATIC);
} else if (status == NS_ROWS) {
Tcl_SetResult(interp, "NS_ROWS", TCL_STATIC);
}
break;
case Db_getrowIdx:
if (Ns_TclGetSet2(interp, arg, &row) != TCL_OK) {
return TCL_ERROR;
}
status = Ns_DbGetRow(handle, row);
if (status == NS_OK) {
Tcl_SetBooleanObj(resultPtr, 1);
} else if (status == NS_END_DATA) {
Tcl_SetBooleanObj(resultPtr, 0);
}
break;
case Db_interpretsqlfileIdx:
status = Ns_DbInterpretSqlFile(handle, arg);
break;
case Db_selectIdx:
row = Ns_DbSelect(handle, arg);
EnterRow(interp, row, NS_TCL_SET_STATIC, &status);
break;
case Db_sp_startIdx:
status = Ns_DbSpStart(handle, arg);
if (status == NS_OK) {
Tcl_SetBooleanObj(resultPtr, 0);
}
break;
}
break;
/*
* The remaining options requiring specific args.
*/
case Db_sp_setparamIdx:
if (objc != 7) {
Tcl_WrongNumArgs(interp, 2, objv,
"dbId paramname type in|out value");
return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(interp, objv[5], spopts, "option", 0,
(int *) &spopt) != TCL_OK) {
return TCL_ERROR;
}
if (GetHandleObj(idataPtr, objv[2], &handle, 1, &hPtr) != TCL_OK) {
return TCL_ERROR;
}
status = Ns_DbSpSetParam(handle, Tcl_GetString(objv[3]),
Tcl_GetString(objv[4]),
spopts[spopt],
Tcl_GetString(objv[6]));
if (status == NS_OK) {
Tcl_SetBooleanObj(resultPtr, 1);
}
break;
case Db_gethandleIdx:
timeout = -1;
if (objc >= 4) {
arg = Tcl_GetString(objv[2]);
if (STREQ(arg, "-timeout")) {
if (Tcl_GetIntFromObj(interp, objv[3], &timeout) != TCL_OK) {
return TCL_ERROR;
}
objv += 2;
objc -= 2;
} else if (objc > 4) {
Tcl_WrongNumArgs(interp, 2, objv,
"?-timeout seconds? ?pool? ?nhandles?");
return TCL_ERROR;
}
}
objv += 2;
objc -= 2;
/*
* Determine the pool and requested number of handles
* from the remaining args.
*/
if (objc > 0) {
pool = Tcl_GetString(objv[0]);
} else {
pool = Ns_DbPoolDefault(idataPtr->server);
if (pool == NULL) {
Tcl_SetResult(interp, "no defaultpool configured", TCL_STATIC);
return TCL_ERROR;
}
}
if (Ns_DbPoolAllowable(idataPtr->server, pool) == NS_FALSE) {
Tcl_AppendResult(interp, "no access to pool: \"", pool, "\"",
NULL);
return TCL_ERROR;
}
if (objc < 2) {
nhandles = 1;
} else {
if (Tcl_GetIntFromObj(interp, objv[1], &nhandles) != TCL_OK) {
return TCL_ERROR;
}
if (nhandles <= 0) {
Tcl_AppendResult(interp, "invalid nhandles: \"",
Tcl_GetString(objv[1]),
"\": should be greater than 0.", NULL);
return TCL_ERROR;
}
}
/*
* Allocate handles and enter them into Tcl.
*/
if (nhandles > MAXHANDLES) {
handlesPtrPtr = ns_malloc(nhandles * sizeof(Ns_DbHandle *));
} else {
handlesPtrPtr = staticHandles;
}
status = Ns_DbPoolTimedGetMultipleHandles(handlesPtrPtr, pool,
nhandles, timeout);
if (status == NS_OK) {
while (--nhandles >= 0) {
EnterHandle(idataPtr, interp, handlesPtrPtr[nhandles]);
}
}
if (handlesPtrPtr != staticHandles) {
ns_free(handlesPtrPtr);
}
if (status != NS_TIMEOUT && status != NS_OK) {
sprintf(buf, "%d", nhandles);
Tcl_AppendResult(interp, "could not allocate ", buf,
" handle(s) from pool \"", pool, "\"", NULL);
return TCL_ERROR;
}
break;
case Db_bouncepoolIdx:
if (objc != 3) {
Tcl_WrongNumArgs(interp, 2, objv, "pool");
return TCL_ERROR;
}
status = Ns_DbBouncePool(Tcl_GetString(objv[2]));
break;
case Db_poolsIdx:
if (objc > 2) {
Tcl_WrongNumArgs(interp, 2, objv, NULL);
return TCL_ERROR;
}
pool = Ns_DbPoolList(idataPtr->server);
if (pool != NULL) {
while (*pool != '\0') {
Tcl_AppendElement(interp, pool);
pool = pool + strlen(pool) + 1;
}
}
break;
case Db_setexceptionIdx:
if (objc != 5) {
Tcl_WrongNumArgs(interp, 2, objv, "dbId code message");
return TCL_ERROR;
}
if (GetHandleObj(idataPtr, objv[2], &handle, 1, NULL) != TCL_OK) {
return TCL_ERROR;
}
arg = Tcl_GetStringFromObj(objv[3], &n);
if (n > 5) {
Tcl_AppendResult(interp, "code \"", arg,
"\" more than 5 characters", NULL);
return TCL_ERROR;
}
Ns_DbSetException(handle, arg, Tcl_GetString(objv[4]));
break;
case Db_verboseIdx:
if (objc != 3 && objc != 4) {
Tcl_WrongNumArgs(interp, 2, objv, "dbId ?bool?");
return TCL_ERROR;
}
if (GetHandleObj(idataPtr, objv[2], &handle, 1, NULL) != TCL_OK) {
return TCL_ERROR;
}
if (objc == 4) {
if (Tcl_GetBooleanFromObj(interp, objv[3], &n) != TCL_OK) {
return TCL_ERROR;
}
handle->verbose = n;
}
Tcl_SetIntObj(resultPtr, handle->verbose);
break;
}
if (status == NS_ERROR) {
Tcl_AppendResult(interp, "Database operation \"", opts[opt],
"\" failed", NULL);
if (handle != NULL && handle->cExceptionCode[0] != '\0') {
Tcl_AppendResult(interp, " (exception ",
handle->cExceptionCode, NULL);
if (handle->dsExceptionMsg.length > 0) {
Tcl_AppendResult(interp, ", \"",
handle->dsExceptionMsg.string, "\"", NULL);
}
Tcl_AppendResult(interp, ")", NULL);
}
return TCL_ERROR;
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
* DbErrorCodeCmd --
*
* Get database exception code for the database handle.
*
* Results:
* Returns TCL_OK and database exception code is set as Tcl result
* or TCL_ERROR if failure.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static int
ErrorCmd(ClientData arg, Tcl_Interp *interp, int argc, CONST char **argv, int cmd)
{
InterpData *idataPtr = arg;
Ns_DbHandle *handle;
if (argc != 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " dbId\"", NULL);
return TCL_ERROR;
}
if (GetHandle(idataPtr, (char*) argv[1], &handle, 0, NULL) != TCL_OK) {
return TCL_ERROR;
}
if (cmd == 'c') {
Tcl_SetResult(interp, handle->cExceptionCode, TCL_VOLATILE);
} else {
Tcl_SetResult(interp, handle->dsExceptionMsg.string, TCL_VOLATILE);
}
return TCL_OK;
}
static int
DbErrorCodeCmd(ClientData arg, Tcl_Interp *interp, int argc, CONST char **argv)
{
return ErrorCmd(arg, interp, argc, argv, 'c');
}
static int
DbErrorMsgCmd(ClientData arg, Tcl_Interp *interp, int argc, CONST char **argv)
{
return ErrorCmd(arg, interp, argc, argv, 'm');
}
/*
*----------------------------------------------------------------------
* DbConfigPathCmd --
*
* Get the database section name from the configuration file.
*
* Results:
* TCL_OK and the database section name is set as the Tcl result
* or TCL_ERROR if failure.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static int
DbConfigPathCmd(ClientData arg, Tcl_Interp *interp, int argc, CONST char **argv)
{
InterpData *idataPtr = arg;
char *section;
if (argc != 1) {
Tcl_AppendResult(interp, "wrong # of args: should be \"", argv[0],
"\"", NULL);
return TCL_ERROR;
}
section = Ns_ConfigGetPath(idataPtr->server, NULL, "db", NULL);
Tcl_SetResult(interp, section, TCL_STATIC);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
* PoolDescriptionCmd --
*
* Get the pool's description string.
*
* Results:
* Return TCL_OK and the pool's description string is set as the
* Tcl result string or TCL_ERROR if failure.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static int
PoolDescriptionCmd(ClientData arg, Tcl_Interp *interp, int argc,
CONST char **argv)
{
if (argc != 2) {
Tcl_AppendResult(interp, "wrong # of args: should be \"",
(char*)argv[0], " poolname\"", NULL);
return TCL_ERROR;
}
Tcl_SetResult(interp, Ns_DbPoolDescription((char*)argv[1]),TCL_STATIC);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
* QuoteListToListCmd --
*
* Remove space, \ and ' characters in a string.
*
* Results:
* TCL_OK and set the stripped string as the Tcl result or TCL_ERROR
* if failure.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static int
QuoteListToListCmd(ClientData arg, Tcl_Interp *interp, int argc,
CONST char **argv)
{
char *quotelist;
int inquotes;
Ns_DString ds;
if (argc != 2) {
Tcl_AppendResult(interp, "wrong # of args: should be \"",
argv[0], " quotelist\"", NULL);
return TCL_ERROR;
}
quotelist = (char*)argv[1];
inquotes = NS_FALSE;
Ns_DStringInit(&ds);
while (*quotelist != '\0') {
if (isspace(UCHAR(*quotelist)) && inquotes == NS_FALSE) {
if (ds.length != 0) {
Tcl_AppendElement(interp, ds.string);
Ns_DStringTrunc(&ds, 0);
}
while (isspace(UCHAR(*quotelist))) {
quotelist++;
}
} else if (*quotelist == '\\' && (*(quotelist + 1) != '\0')) {
Ns_DStringNAppend(&ds, quotelist + 1, 1);
quotelist += 2;
} else if (*quotelist == '\'') {
if (inquotes) {
/* Finish element */
Tcl_AppendElement(interp, ds.string);
Ns_DStringTrunc(&ds, 0);
inquotes = NS_FALSE;
} else {
/* Start element */
inquotes = NS_TRUE;
}
quotelist++;
} else {
Ns_DStringNAppend(&ds, quotelist, 1);
quotelist++;
}
}
if (ds.length != 0) {
Tcl_AppendElement(interp, ds.string);
}
Ns_DStringFree(&ds);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* GetCsvCmd --
*
* Implement the ns_getcvs command to read a line from a CSV file
* and parse the results into a Tcl list variable.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* One line is read for given open channel.
*
*----------------------------------------------------------------------
*/
static int
GetCsvCmd(ClientData arg, Tcl_Interp *interp, int argc,
CONST char **argv)
{
int ncols, inquote, quoted, blank;
char c, *p, buf[20];
const char *result;
Tcl_DString line, cols, elem;
Tcl_Channel chan;
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # of args: should be \"",
argv[0], " fileId varName\"", NULL);
return TCL_ERROR;
}
if (Ns_TclGetOpenChannel(interp, (char*)argv[1], 0, 0, &chan) == TCL_ERROR) {
return TCL_ERROR;
}
Tcl_DStringInit(&line);
if (Tcl_Gets(chan, &line) < 0) {
Tcl_DStringFree(&line);
if (!Tcl_Eof(chan)) {
Tcl_AppendResult(interp, "could not read from ", argv[1],
": ", Tcl_PosixError(interp), NULL);
return TCL_ERROR;
}
Tcl_SetResult(interp, "-1", TCL_STATIC);
return TCL_OK;
}
Tcl_DStringInit(&cols);
Tcl_DStringInit(&elem);
ncols = 0;
inquote = 0;
quoted = 0;
blank = 1;
p = line.string;
while (*p != '\0') {
c = *p++;
loopstart:
if (inquote) {
if (c == '"') {
c = *p++;
if (c == '\0') {
break;
}
if (c == '"') {
Tcl_DStringAppend(&elem, &c, 1);
} else {
inquote = 0;
goto loopstart;
}
} else {
Tcl_DStringAppend(&elem, &c, 1);
}
} else {
if ((c == '\n') || (c == '\r')) {
while ((c = *p++) != '\0') {
if ((c != '\n') && (c != '\r')) {
--p;
break;
}
}
break;
}
if (c == '"') {
inquote = 1;
quoted = 1;
blank = 0;
} else if ((c == '\r') || (elem.length == 0 && isspace(UCHAR(c)))) {
continue;
} else if (c == ',') {
if (!quoted) {
Ns_StrTrimRight(elem.string);
}
Tcl_DStringAppendElement(&cols, elem.string);
Tcl_DStringTrunc(&elem, 0);
ncols++;
quoted = 0;
} else {
blank = 0;
Tcl_DStringAppend(&elem, &c, 1);
}
}
}
if (!quoted) {
Ns_StrTrimRight(elem.string);
}
if (!blank) {
Tcl_DStringAppendElement(&cols, elem.string);
ncols++;
}
result = Tcl_SetVar(interp, argv[2], cols.string, TCL_LEAVE_ERR_MSG);
Tcl_DStringFree(&line);
Tcl_DStringFree(&cols);
Tcl_DStringFree(&elem);
if (result == NULL) {
return TCL_ERROR;
}
sprintf(buf, "%d", ncols);
Tcl_SetResult(interp, buf, TCL_VOLATILE);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
* GetHandle, GetHandleObj --
*
* Get database handle from its handle id.
*
* Results:
* Return TCL_OK if handle is found or TCL_ERROR otherwise.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static int
GetHandle(InterpData *idataPtr, char *id, Ns_DbHandle **handlePtr,
int clear, Tcl_HashEntry **hPtrPtr)
{
Ns_DbHandle *handle;
Tcl_HashEntry *hPtr;
hPtr = Tcl_FindHashEntry(&idataPtr->dbs, id);
if (hPtr == NULL) {
Tcl_AppendResult(idataPtr->interp, "invalid database id: \"", id,
"\"", NULL);
return TCL_ERROR;
}
handle = (Ns_DbHandle *) Tcl_GetHashValue(hPtr);
if (clear) {
Ns_DStringFree(&handle->dsExceptionMsg);
handle->cExceptionCode[0] = '\0';
}
if (hPtrPtr != NULL) {
*hPtrPtr = hPtr;
}
if (handlePtr != NULL) {
*handlePtr = handle;
}
return TCL_OK;
}
static int
GetHandleObj(InterpData *idataPtr, Tcl_Obj *obj, Ns_DbHandle **handlePtr,
int clear, Tcl_HashEntry **hPtrPtr)
{
return GetHandle(idataPtr, Tcl_GetString(obj), handlePtr, clear, hPtrPtr);
}
/*
*----------------------------------------------------------------------
* EnterHandle --
*
* Enter a database handle and create its handle id.
*
* Results:
* The database handle id is returned as a Tcl result.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
EnterHandle(InterpData *idataPtr, Tcl_Interp *interp, Ns_DbHandle *handle)
{
Tcl_HashEntry *hPtr;
int new, next;
char buf[100];
if (!idataPtr->cleanup) {
Ns_TclRegisterDeferred(interp, ReleaseDbs, idataPtr);
idataPtr->cleanup = 1;
}
next = idataPtr->dbs.numEntries;
do {
sprintf(buf, "nsdb%x", next++);
hPtr = Tcl_CreateHashEntry(&idataPtr->dbs, buf, &new);
} while (!new);
Tcl_AppendElement(interp, buf);
Tcl_SetHashValue(hPtr, handle);
}
/*
*----------------------------------------------------------------------
* FreeData --
*
* Free per-interp data at interp delete time.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
FreeData(ClientData arg, Tcl_Interp *interp)
{
InterpData *idataPtr = arg;
Tcl_DeleteHashTable(&idataPtr->dbs);
ns_free(idataPtr);
}
/*
*----------------------------------------------------------------------
* ReleaseDbs --
*
* Release any database handles still held.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
ReleaseDbs(Tcl_Interp *interp, void *arg)
{
Ns_DbHandle *handle;
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
InterpData *idataPtr = arg;
hPtr = Tcl_FirstHashEntry(&idataPtr->dbs, &search);
while (hPtr != NULL) {
handle = Tcl_GetHashValue(hPtr);
Ns_DbPoolPutHandle(handle);
hPtr = Tcl_NextHashEntry(&search);
}
Tcl_DeleteHashTable(&idataPtr->dbs);
Tcl_InitHashTable(&idataPtr->dbs, TCL_STRING_KEYS);
idataPtr->cleanup = 0;
}
/*
*----------------------------------------------------------------------
* EnterRow --
*
* Enter a db row set into interp.
*
* Results:
* None.
*
* Side effects:
* Will set *statusPtr to NS_ERROR if row is null.
*
*----------------------------------------------------------------------
*/
static void
EnterRow(Tcl_Interp *interp, Ns_Set *row, int flags, int *statusPtr)
{
if (row == NULL) {
*statusPtr = NS_ERROR;
} else {
Ns_TclEnterSet(interp, row, flags);
*statusPtr = NS_OK;
}
}
|
Back to SourceForge.net Powered by ViewCVS 1.0-dev |