make compatible with aolserver 4.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.
*/
static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/nsodbc/nsodbc.c,v 1.4 2003/07/23 18:52:38 elizthom Exp $, compiled: " __DATE__ " " __TIME__;
#include "ns.h"
#include "nsdb.h"
#ifndef REDBRICK
#include <sql.h>
#include <sqlext.h>
#else
#include <rbsql.h>
#include <rbsqlext.h>
#endif
#define RC_OK(rc) (!((rc)>>1))
#define MAX_ERROR_MSG 500
#define MAX_IDENTIFIER 256
static char *ODBCName(void);
static int ODBCServerInit(char *server, char *hModule, char *driver);
static int ODBCOpenDb(Ns_DbHandle *handle);
static int ODBCCloseDb(Ns_DbHandle *handle);
static int ODBCGetRow(Ns_DbHandle *handle, Ns_Set *row);
static int ODBCCancel(Ns_DbHandle *handle);
static int ODBCExec(Ns_DbHandle *handle, char *sql);
static Ns_Set *ODBCBindRow(Ns_DbHandle *handle);
static int ODBCFreeStmt(Ns_DbHandle *handle);
static void ODBCLog(RETCODE rc, Ns_DbHandle *handle);
static char *odbcName = "ODBC";
static HENV odbcenv;
static Tcl_CmdProc ODBCCmd;
static Ns_DbProc odbcProcs[] = {
{DbFn_Name, ODBCName},
{DbFn_ServerInit, ODBCServerInit},
{DbFn_OpenDb, ODBCOpenDb},
{DbFn_CloseDb, ODBCCloseDb},
{DbFn_GetRow, ODBCGetRow},
{DbFn_Flush, ODBCCancel},
{DbFn_Cancel, ODBCCancel},
{DbFn_Exec, ODBCExec},
{DbFn_BindRow, ODBCBindRow},
{0, NULL}
};
/*
*----------------------------------------------------------------------
*
* Ns_DbDriverInit -
*
* ODBC module load routine.
*
* Results:
* NS_OK or NS_ERROR.
*
* Side effects:
* Initializes ODBC.
*
*----------------------------------------------------------------------
*/
NS_EXPORT int Ns_ModuleVersion = 1;
NS_EXPORT int
Ns_DbDriverInit(char *driver, char *configPath)
{
RETCODE rc;
rc = SQLAllocEnv(&odbcenv);
if (rc != SQL_SUCCESS) {
Ns_Log(Error, "%s: failed to allocate odbc", driver);
return NS_ERROR;
}
if (Ns_DbRegisterDriver(driver, odbcProcs) != NS_OK) {
Ns_Log(Error, "%s: failed to register driver", driver);
return NS_ERROR;
}
return NS_OK;
}
/*
*----------------------------------------------------------------------
*
* ODBCServerInit -
*
* Initialize the server when nsdb is ready.
*
* Results:
* NS_OK or NS_ERROR.
*
* Side effects:
* Adds ns_odbc command.
*
*----------------------------------------------------------------------
*/
static int
AddCmd(Tcl_Interp *interp, void *ignored)
{
Tcl_CreateCommand(interp, "ns_odbc", ODBCCmd, NULL, NULL);
return NS_OK;
}
static int
ODBCServerInit(char *server, char *hModule, char *driver)
{
return Ns_TclInitInterps(server, AddCmd, NULL);
}
/*
*----------------------------------------------------------------------
*
* ODBCName -
*
* Return the ODBC driver name.
*
* Results:
* Pointer to static string name.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static char *
ODBCName(void)
{
return odbcName;
}
/*
*----------------------------------------------------------------------
*
* ODBCOpenDb -
*
* Open an ODBC datasource.
*
* Results:
* NS_OK or NS_ERROR.
*
* Side effects:
* Performs both SQLAllocConnect and SQLConnect.
*
*----------------------------------------------------------------------
*/
static int
ODBCOpenDb(Ns_DbHandle *handle)
{
HDBC hdbc;
RETCODE rc;
int userlen, passwordlen;
handle->connected = NS_FALSE;
handle->connection = NULL;
handle->statement = NULL;
rc = SQLAllocConnect(odbcenv, &hdbc);
ODBCLog(rc, handle);
if (!RC_OK(rc)) {
return NS_ERROR;
}
if (handle->user != NULL) {
userlen = strlen(handle->user);
} else {
userlen = 0;
}
if (handle->password != NULL) {
passwordlen = strlen(handle->password);
} else {
passwordlen = 0;
}
Ns_Log(Notice, "%s[%s]: attemping to open '%s'",
handle->driver, handle->poolname, handle->datasource);
rc = SQLConnect(hdbc, handle->datasource,
(SWORD) strlen(handle->datasource), handle->user,
(SWORD) userlen, handle->password, (SWORD) passwordlen);
ODBCLog(rc, handle);
if (!RC_OK(rc)) {
return NS_ERROR;
}
handle->connection = (void *) hdbc;
handle->connected = NS_TRUE;
return NS_OK;
}
/*
*----------------------------------------------------------------------
*
* ODBCCloseDb -
*
* Close an ODBC datasource.
*
* Results:
* NS_OK or NS_ERROR.
*
* Side effects:
* Performs both SQLDisconnect and SQLFreeConnect.
*
*----------------------------------------------------------------------
*/
static int
ODBCCloseDb(Ns_DbHandle *handle)
{
RETCODE rc;
rc = SQLDisconnect((HDBC) handle->connection);
if (!RC_OK(rc)) {
return NS_ERROR;
}
rc = SQLFreeConnect((HDBC) handle->connection);
if (!RC_OK(rc)) {
return NS_ERROR;
}
handle->connection = NULL;
handle->connected = NS_FALSE;
return NS_OK;
}
/*
*----------------------------------------------------------------------
*
* ODBExec -
*
* Send an SQL statement.
*
* Results:
* NS_DML, NS_ROWS, or NS_ERROR.
*
* Side effects:
* Database may be modified or rows may be waiting.
*
*----------------------------------------------------------------------
*/
static int
ODBCExec(Ns_DbHandle *handle, char *sql)
{
HSTMT hstmt;
RETCODE rc;
int status;
short numcols;
/*
* Allocate a new statement.
*/
rc = SQLAllocStmt((HDBC) handle->connection, &hstmt);
ODBCLog(rc, handle);
if (!RC_OK(rc)) {
return NS_ERROR;
}
/*
* Send the SQL and determine if rows are available.
*/
handle->statement = hstmt;
rc = SQLExecDirect(hstmt, sql, SQL_NTS);
ODBCLog(rc, handle);
if (RC_OK(rc)) {
rc = SQLNumResultCols(hstmt, &numcols);
ODBCLog(rc, handle);
if (RC_OK(rc)) {
if (numcols != 0) {
handle->fetchingRows = 1;
status = NS_ROWS;
} else {
status = NS_DML;
}
}
}
/*
* Free the statement unless rows are waiting.
*/
if (!RC_OK(rc)) {
status = NS_ERROR;
}
if (status != NS_ROWS && ODBCFreeStmt(handle) != NS_OK) {
status = NS_ERROR;
}
return status;
}
/*
*----------------------------------------------------------------------
*
* ODBCBindRow -
*
* Bind row after an ODBCExec which returned NS_ROWS.
*
* Results:
* NS_OK or NS_ERROR.
*
* Side effects:
* Given Ns_Set is modified with column names.
*
*----------------------------------------------------------------------
*/
static Ns_Set *
ODBCBindRow(Ns_DbHandle *handle)
{
HSTMT hstmt;
RETCODE rc;
Ns_Set *row;
short i, numcols;
char colname[100];
short cbcolname;
SWORD sqltype, ibscale, nullable;
UDWORD cbcoldef;
if (!handle->fetchingRows) {
Ns_Log(Error, "%s[%s]: no waiting rows",
handle->driver, handle->poolname);
return NULL;
}
row = handle->row;
hstmt = (HSTMT) handle->statement;
rc = SQLNumResultCols(hstmt, &numcols);
for (i = 1; RC_OK(rc) && i <= numcols; i++) {
rc = SQLDescribeCol(hstmt, i, colname, sizeof(colname),
&cbcolname, &sqltype, &cbcoldef, &ibscale, &nullable);
ODBCLog(rc, handle);
if (RC_OK(rc)) {
Ns_SetPut(row, colname, NULL);
}
}
if (!RC_OK(rc)) {
ODBCFreeStmt(handle);
row = NULL;
}
return row;
}
/*
*----------------------------------------------------------------------
*
* ODBCGetRow -
*
* Fetch the next row.
*
* Results:
* NS_OK or NS_ERROR.
*
* Side effects:
* Given Ns_Set is modified with new values.
*
*----------------------------------------------------------------------
*/
static int
ODBCGetRow(Ns_DbHandle *handle, Ns_Set *row)
{
RETCODE rc;
short i;
HSTMT hstmt;
short numcols;
if (!handle->fetchingRows) {
Ns_Log(Error, "%s[%s]: no waiting rows",
handle->driver, handle->poolname);
return NS_ERROR;
}
hstmt = (HSTMT) handle->statement;
rc = SQLNumResultCols(hstmt, &numcols);
ODBCLog(rc, handle);
if (!RC_OK(rc)) {
goto error;
}
if (numcols != Ns_SetSize(row)) {
Ns_Log(Error, "%s[%s]: mismatched number of rows",
handle->driver, handle->poolname);
goto error;
}
rc = SQLFetch(hstmt);
ODBCLog(rc, handle);
if (rc == SQL_NO_DATA_FOUND) {
ODBCFreeStmt(handle);
return NS_END_DATA;
}
for (i = 1; RC_OK(rc) && i <= numcols; i++) {
char datum[8192];
SDWORD cbvalue;
rc = SQLGetData(hstmt, i, SQL_C_CHAR, datum, sizeof(datum), &cbvalue);
ODBCLog(rc, handle);
if (RC_OK(rc)) {
Ns_SetPutValue(row, i - 1, cbvalue == SQL_NULL_DATA ? "" : datum);
}
}
if (!RC_OK(rc)) {
error:
ODBCFreeStmt(handle);
return NS_ERROR;
}
return NS_OK;
}
/*
*----------------------------------------------------------------------
*
* ODBCCancel -
*
* Cancel the active select.
*
* Results:
* NS_OK or NS_ERROR.
*
* Side effects:
* Rows are flushed.
*
*----------------------------------------------------------------------
*/
static int
ODBCCancel(Ns_DbHandle *handle)
{
RETCODE rc;
int status;
status = NS_OK;
if (handle->fetchingRows) {
rc = SQLCancel((HSTMT) handle->statement);
ODBCLog(rc, handle);
status = ODBCFreeStmt(handle);
}
return status;
}
/*
*----------------------------------------------------------------------
*
* ODBCCmd -
*
* Process the ns_odbc command.
*
* Results:
* Standard Tcl result.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static int
ODBCCmd(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
Ns_DbHandle *handle;
RETCODE rc;
char buf[MAX_IDENTIFIER];
SWORD FAR cbInfoValue;
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " cmd handle\"", NULL);
return TCL_ERROR;
}
if (Ns_TclDbGetHandle(interp, argv[2], &handle) != TCL_OK) {
return TCL_ERROR;
}
/*
* Make sure this is an open ODBC handle.
*/
if (Ns_DbDriverName(handle) != odbcName) {
Tcl_AppendResult(interp, "handle \"", argv[1], "\" is not of type \"",
odbcName, "\"", NULL);
return TCL_ERROR;
}
if (handle->connection == NULL) {
Tcl_AppendResult(interp, "handle \"", argv[1], "\" not connected", NULL);
return TCL_ERROR;
}
if (STREQ(argv[1], "dbmsname")) {
rc = SQLGetInfo((HDBC) handle->connection, SQL_DBMS_NAME, buf, sizeof(buf), &cbInfoValue);
ODBCLog(rc, handle);
if (!RC_OK(rc)) {
Tcl_SetResult(interp, "could not determine dbmsname", TCL_STATIC);
return TCL_ERROR;
}
} else if (STREQ(argv[1], "dbmsver")) {
rc = SQLGetInfo((HDBC) handle->connection, SQL_DBMS_VER, buf, sizeof(buf), &cbInfoValue);
ODBCLog(rc, handle);
if (!RC_OK(rc)) {
Tcl_SetResult(interp, "could not determine dbmsver", TCL_STATIC);
return TCL_ERROR;
}
} else {
Tcl_AppendResult(interp, "unknown command \"", argv[1],
"\": should be dbmsname or dbmsver.", NULL);
return TCL_ERROR;
}
Tcl_SetResult(interp, buf, TCL_VOLATILE);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* ODBCLog -
*
* Log a possible error.
*
* Results:
* None.
*
* Side effects:
* May add log entry.
*
*----------------------------------------------------------------------
*/
static void
ODBCLog(RETCODE rc, Ns_DbHandle *handle)
{
HDBC hdbc;
HSTMT hstmt;
Ns_LogSeverity severity;
char szSQLSTATE[6];
SDWORD nErr;
char msg[MAX_ERROR_MSG + 1];
SWORD cbmsg;
if (rc == SQL_SUCCESS_WITH_INFO) {
severity = Warning;
} else if (rc == SQL_ERROR) {
severity = Error;
} else {
return;
}
hdbc = (HDBC) handle->connection;
hstmt = (HSTMT) handle->statement;
while (SQLError(odbcenv, hdbc, hstmt, szSQLSTATE, &nErr, msg,
sizeof(msg), &cbmsg) == SQL_SUCCESS) {
Ns_Log(severity, "%s[%s]: odbc message: "
"SQLSTATE = %s, Native err = %ld, msg = '%s'",
handle->driver, handle->poolname, szSQLSTATE, nErr, msg);
strcpy(handle->cExceptionCode, szSQLSTATE);
Ns_DStringFree(&(handle->dsExceptionMsg));
Ns_DStringAppend(&(handle->dsExceptionMsg), msg);
}
}
/*
*----------------------------------------------------------------------
*
* ODBCFreeStmt -
*
* Free the handle's current statement.
*
* Results:
* NS_OK or NS_ERROR.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static int
ODBCFreeStmt(Ns_DbHandle *handle)
{
RETCODE rc;
rc = SQLFreeStmt((HSTMT) handle->statement, SQL_DROP);
handle->statement = NULL;
handle->fetchingRows = 0;
if (!RC_OK(rc)) {
return NS_ERROR;
}
return NS_OK;
}
|
Back to SourceForge.net Powered by ViewCVS 1.0-dev |