/*
+==========================================================+
| |
| ODBC extension for Win32 Perl |
| ----------------------------- |
| |
| by Dave Roth <rothd@roth.net> |
| |
| version v970208 |
| |
| Copyright (c) 1996-1997 Dave Roth. All rights reserved. |
| This program is free software; you can redistribute |
| it and/or modify it under the same terms as Perl itself. |
| |
+==========================================================+
based on original code by Dan DeMaggio (dmag@umich.edu)
Use under GNU General Public License or Larry Wall's "Artistic License"
*/
#define PERL_POLLUTE
#define __WIN32_ODBC__
#define WIN32_LEAN_AND_MEAN
#include <stdlib.h>
#include <math.h> // VC-5.0 brainmelt
#include <windows.h>
#include <stdio.h>
// ODBC Stuff
#ifdef __CYGWIN__
# include <iodbcinst.h>
#else
# include <sql.h>
# include <sqlext.h>
# include <odbcinst.h>
#endif
// Win32 Perl Stuff
#if defined(__cplusplus)
extern "C" {
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSub.h"
#include "ppport.h"
#if defined(__cplusplus)
}
#endif
// Win32::ODBC Stuff
#include "ODBCbuild.h"
#include "CResults.hpp"
#include "CMom.hpp"
#include "ODBC.h"
#include "Constant.h"
#undef __WIN32_ODBC__
extern CMOM *cMom;
RETCODE TableColList(pTHX_ int iType);
/*----------------------- P E R L F U N C T I O N S -------------------*/
XS(XS_WIN32__ODBC_Constant)
{
dXSARGS;
if (items < 1)
{
croak("Usage: Win32::ODBC::Constant(name)\n");
}
{
STRLEN n_a;
char* name = (char*)SvPV(ST(0),n_a);
ST(0) = sv_newmortal();
sv_setiv(ST(0), constant(name));
}
XSRETURN(1);
}
/*----------------------- M I S C F U N C T I O N S -------------------*/
/*
Allocate memory for a new connection and set up the structure
*/
ODBC_TYPE *NewODBC(){
ODBC_TYPE *h = 0;
int iResult = 0;
CMom *cmDaughter;
#ifdef _DEBUG
if (ghDebug){
DebugConnection("Creating a new ODBC object.", 0);
}
#endif
if (!(cmDaughter = (CMom *) ::cMom->operator[](GetCurrentThreadId()))){
cmDaughter = new CMom(GetCurrentThreadId());
}
if (cmDaughter){
#ifdef _DEBUG
DEBUG_DUMP("NewODBC: Entering Critical Section gCS")
#endif
EnterCriticalSection(&gCS);
if (! ghEnv){
SQLAllocEnv(&ghEnv);
}
LeaveCriticalSection(&gCS);
#ifdef _DEBUG
DEBUG_DUMP("NewODBC: Left Critical Section gCS ")
#endif
if (ghEnv){
if (h= new ODBC_TYPE){
if (h->Error = new ODBC_ERROR){
if (cmDaughter->Add((void *)h)){
iResult = 1;
h->szDSN = 0;
h->szCommand = 0;
CleanODBC(h);
// Add these values AFTER cleaning the ODBC Object
h->conn = cmDaughter->operator[]((void *)h);
#ifdef _DEBUG
DebugConnection("...created ODBC object.", h);
#endif
h->henv = ghEnv;
}
}
if (!iResult){
FreeODBC(h);
delete h;
h = 0;
}
}
}
}
#ifdef _DEBUG
else{
DebugConnection("...failed with new ODBC object: Could not find or create daughter.", h);
}
if (!h){
DebugConnection("...creation of ODBC object failed.", h);
}
#endif
return h;
}
/*
Clean up an ODBC structure.
*/
ODBC_TYPE *CleanODBC(ODBC_TYPE *h){
h->hstmt = SQL_NULL_HSTMT;
h->henv = SQL_NULL_HENV;
h->hdbc = 0;
h->Results = 0;
h->iMaxBufSize = DEFAULT_DATA_BUF_SIZE;
h->numcols = 0;
h->uStmtCloseType = DEFAULT_STMT_CLOSE_TYPE;
h->iDebug = 0;
h->dNumOfRows = 0;
strcpy(h->szUserDSN, "");
if (h->szDSN){
delete [] h->szDSN;
h->szDSN = 0;
}
if (h->szCommand){
delete [] h->szCommand;
h->szCommand = 0;
}
CleanError(h->Error);
return h;
}
/*
Deallocates memory used for an ODBC structure.
If the master list (ODBCLIST) is passed to this it will do nothing unless
there are no other ODBC structures remaining.
*/
int FreeODBC(ODBC_TYPE *h){
int iResult = 1;
#ifdef _DEBUG
if (ghDebug){
char szBuff[1000];
DebugConnection("FreeODBC has been called.\n", h);
}
#endif
// There is a call in this function to itself if we delete the
// last connection. BECAUASE OF THIS do NOT put a critical section
// in this function lest we reach a deadlock condition.
if (h->Results){
delete h->Results;
h->Results = 0;
}
if (h->hstmt){
// If ghDLL is NULL (process has already detached) then
// forget this (Win32 will purge the memory anyway).
if (ghDLL) SQLFreeStmt(h->hstmt, SQL_DROP);
h->hstmt = SQL_NULL_HSTMT;
}
if (h->hdbc){
h->hdbc->iCount--;
if (! h->hdbc->iCount){
// Disconnect ONLY if you have no parents
// If ghDLL is NULL (process has already detached) then
// forget this (Win32 will purge the memory anyway).
if (ghDLL){
if (h->hdbc->iConnected){
SQLDisconnect(h->hdbc->hdbc);
h->hdbc->hdbc = SQL_NULL_HDBC;
h->hdbc->iConnected = 0;
}
SQLFreeConnect(h->hdbc->hdbc);
}
delete h->hdbc;
}
h->hdbc = 0;
}
if (h->Error){
delete h->Error;
h->Error = 0;
}
h->henv = SQL_NULL_HENV;
if (h->iDebug){
#ifdef _DEBUG
DebugConnection("Setting Debug Mode off for connection\n\t\t(due to object destuction).", h);
#endif
RemoveDebug(h);
}
#ifdef _DEBUG
if (ghDebug){
char szBuff[30];
sprintf(szBuff, "FreeODBC was %s.\n", (iResult)? "successful":"unsuccessful");
DebugConnection(szBuff, h);
}
#endif
if (iResult){
CleanODBC(h);
}
return iResult;
}
void AddDebug(ODBC_TYPE *h){
if (! h->iDebug){
if (h->conn){
// Increase the debug flag ONLY if
giDebug++;
}else{
giDebugGlobal = 1;
}
if (! ghDebug){
#if _DEBUG
AllocConsole();
SetConsoleTitle("DEBUG: ODBC.PLL");
ghDebug = GetStdHandle(STD_ERROR_HANDLE);
#endif
}
h->iDebug = 1;
}
}
void RemoveDebug(ODBC_TYPE *h){
if(h->iDebug){
if (h->conn){
giDebug--;
if (giDebug < 1){
giDebug = 0;
}
}else{
// giDebugGlobal = 0;
}
if (!(giDebug + giDebugGlobal)){
#ifdef _DEBUG
DebugConnection("Closing Debug Output File.", h);
#endif
DEBUG_DUMP("RemoveDebug: Entering Critical Section gDCS")
EnterCriticalSection(&gDCS);
ghDebug = 0;
#if _DEBUG
FreeConsole();
#endif
CloseHandle(ghFile);
ghFile = 0;
if(gszFile){
delete [] gszFile;
gszFile = 0;
}
LeaveCriticalSection(&gDCS);
DEBUG_DUMP("RemoveDebug: Left Critical Section gDCS ")
}
h->iDebug = 0;
}
}
/*
Reset (clean) an ODBC structures error state information
*/
void CleanError(ODBC_ERROR *h){
if (h){
strcpy(h->szError, "");
strcpy((char *)h->szSqlState, "");
strcpy(h->szFunction, "");
strcpy(h->szFunctionLevel, "");
h->ErrNum = 0;
h->EOR = 0;
}
return;
}
/*
Reset an ODBC Stmt. (alloc memory if needed, free it up if needed)
*/
RETCODE ResetStmt(ODBC_TYPE *h){
RETCODE iReturnCode = SQL_SUCCESS;
if (h->uStmtCloseType != SQL_DONT_CLOSE){
// If the SQLFreeStmt() failed, should we reallocate the stmt?
// For now let's just return the error code and skip the reallocation.
if (h->hstmt != SQL_NULL_HSTMT){
iReturnCode = SQLFreeStmt(h->hstmt, h->uStmtCloseType);
if (iReturnCode == SQL_SUCCESS){
// We will need to realloc the hstmt ONLY if we DROPPED it!
if(h->uStmtCloseType == SQL_DROP){
h->hstmt = SQL_NULL_HSTMT;
}
}else{
_NT_ODBC_Error(h, "ResetStmt", "1");
}
}
if (h->hstmt == SQL_NULL_HSTMT){
if ((iReturnCode = SQLAllocStmt(h->hdbc->hdbc, &h->hstmt)) != SQL_SUCCESS){
_NT_ODBC_Error(h, "ResetStmt", "2");
}
}
}
return iReturnCode;
}
/*
Process an ODBC structures error state from the ODBC API.
This is called when an error is encountered via the ODBC API.
*/
void _NT_ODBC_Error(ODBC_TYPE * h, char *szFunction, char *szFunctionLevel){
SDWORD cbErrorMsg;
if(!h){
h = ODBCLIST;
}
strcpy((char *)h->Error->szSqlState, "");
strcpy(h->Error->szError, "");
SQLError(h->henv, h->hdbc->hdbc, h->hstmt, (SQLCHAR *)h->Error->szSqlState, (SQLINTEGER *)&(h->Error->ErrNum), (SQLCHAR *)h->Error->szError, ODBC_BUFF_SIZE, (SQLSMALLINT *)&cbErrorMsg);
// Next couple of lines should be NOT needed. If there is no error, then
// we should not have come here in the first place. If, however, there
// is state information that may be relevant (SQL_SUCCESS_WITH_INFO)
if (!h->Error->ErrNum){
h->Error->ErrNum = 911;
}
strcpy(h->Error->szFunction, szFunction);
strcpy(h->Error->szFunctionLevel, szFunctionLevel);
#ifdef _DEBUG
if (giDebug){
DebugDumpError(h);
}
#endif
}
/*
Process an ODBC structures error state from Win32::ODBC.
This is called when an error is encountered NOT from the ODBC API
but from this body of code.
*/
ODBC_TYPE *ODBCError(char *szString, ODBC_TYPE *h, char *szFunction, char * szFunctionLevel){
if (!h){
h = ODBCLIST;
}
if (h){
h->Error->ErrNum = 911;
strcpy(h->Error->szError, szString);
strcpy(h->Error->szFunction, szFunction);
strcpy(h->Error->szFunctionLevel, szFunctionLevel);
}
#ifdef _DEBUG
if (giDebug){
DebugDumpError(h);
}
#endif
return h;
}
#ifdef _DEBUG
void DebugDumpError(ODBC_TYPE *h){
char *szBuff;
int iLength;
if (h && h->iDebug && ghDebug){
iLength = ((h->Error->szError)? strlen(h->Error->szError):0) + ((h->Error->szSqlState)? strlen((const char *)h->Error->szSqlState):0)
+ ((h->Error->szFunction)? strlen(h->Error->szFunction):0) + ((h->Error->szFunctionLevel)? strlen(h->Error->szFunctionLevel):0)
+ 100;
if(szBuff = new char [iLength]){
sprintf(szBuff, "ODBCError from connection %i:\n\tErrno: %i\n\tError:\"%s\"\n\tSQLState: %s\n\tFunction: %s\n\tLevel: %s\n",
h->conn, h->Error->ErrNum, h->Error->szError, h->Error->szSqlState, h->Error->szFunction, h->Error->szFunctionLevel);
DebugConnection(szBuff, h);
}
delete [] szBuff;
}
}
void DebugConnection(char *szString, ODBC_TYPE *h){
DWORD dCount;
CMom *cDaughter = 0;
char *szBuff;
int iLength;
if (ghDebug){
if (::cMom){
cDaughter = (CMom *) ::cMom->operator[](GetCurrentThreadId());
}
iLength = 100;
if (szBuff = new char [iLength]){
sprintf(szBuff, "Thread %05i, Total Threads %03i, Connection %03i, Thread Connections %03i:\n\t", GetCurrentThreadId(), giThread, (h)? h->conn:-1, (cDaughter)? cDaughter->Total():0);
DEBUG_DUMP("DebugConnection: Entering Critical Section gDCS");
EnterCriticalSection(&gDCS);
DebugPrint(szBuff);
DebugPrint(szString);
DebugPrint("\n\n");
LeaveCriticalSection(&gDCS);
DEBUG_DUMP("DebugConnection: Left Critical Section gDCS ");
delete [] szBuff;
}
}
return;
}
void DebugDump(char *szString){
if (giDebug){
DEBUG_DUMP("DebugDump: Entering Critical Section gDCS");
EnterCriticalSection(&gDCS);
DebugPrint(szString);
LeaveCriticalSection(&gDCS);
DEBUG_DUMP("DebugDump: Left Critical Section gDCS ");
}
return;
}
void DebugPrint(char *szString){
DWORD dCount;
if (ghDebug){
WriteConsole(ghDebug, szString, strlen(szString), &dCount, 0);
}
if (!ghFile && gszFile){
ghFile = CreateFile(gszFile, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
DebugPrint("\n\t*******\n\tDumping debug text to: \"");
DebugPrint(gszFile);
DebugPrint("\"\n\t*******\n");
}
if(ghFile){
WriteFile(ghFile, szString, strlen(szString), &dCount, 0);
FlushFileBuffers(ghFile);
}
return;
}
#endif
/*
Map a column name (field name) to a column number that exists in a
resulting dataset.
*/
int ColNameToNum(ODBC_TYPE *h, char *szName){
int iResult = 0;
int x;
char szBuff[ODBC_BUFF_SIZE];
DWORD dBuffLen = 0;
for(x=1; x<=h->numcols; x++){
SQLColAttributes(h->hstmt, x, SQL_COLUMN_NAME, szBuff, ODBC_BUFF_SIZE, (short *)&dBuffLen, NULL);
if(!stricmp(szName, szBuff)){
iResult = x;
break;
}
}
return iResult;
}
/*
Check if the specified connection is valid and return the pointer
to the ODBC structure.
Return a pointer to the master list (ODBCLIST) and genereate an error if
there is no valid connection.
*/
ODBC_TYPE * _NT_ODBC_Verify(int iODBC){
CMom *cmDaughter = 0;
ODBC_TYPE *h = 0;
if(::cMom){
// EnterCriticalSection(&gCS);
if (cmDaughter = (CMom *) ::cMom->operator[](GetCurrentThreadId())){
if (!(h = (ODBC_TYPE *) cmDaughter->operator[]((DWORD)iODBC))){
h = (ODBC_TYPE *) cmDaughter->operator[]((DWORD)0);
}
}
// LeaveCriticalSection(&gCS);
}
return (h);
}
/*
Delete an ODBC structure. This will call other routines to release
allocated memory and reset ODBC state information.
*/
int DeleteConn(int iODBC){
ODBC_TYPE *h = 0;
int iResult = 0;
if(::cMom){
CMom *cmDaughter = 0;
if (cmDaughter = (CMom *) ::cMom->operator[](GetCurrentThreadId())){
if (h = (ODBC_TYPE *) cmDaughter->operator[]((DWORD)iODBC)){
if (FreeODBC(h)){
cmDaughter->Remove((DWORD)iODBC);
delete h;
iResult++;
}
}
}
}
return iResult;
}
/*
Map an Stmt close type string to the actual Stmt close type value.
*/
char *MapCloseType(UWORD uCloseType){
char *szType;
switch(uCloseType){
case SQL_DONT_CLOSE:
szType = "SQL_DONT_CLOSE";
break;
case SQL_DROP:
szType = "SQL_DROP";
break;
case SQL_CLOSE:
szType = "SQL_CLOSE";
break;
case SQL_UNBIND:
szType = "SQL_UNBIND";
break;
case SQL_RESET_PARAMS:
szType = "SQL_RESET_PARAMS";
default:
szType = 0;
}
return szType;
}
/*------------------- P E R L O D B C F U N C T I O N S ---------------*/
/*
ODBC_Connect
Connects to and ODBC Data Source Name (DSN).
*/
XS(XS_WIN32__ODBC_Connect) // ODBC_Connect(Connection string: input) returns connection #
{
dXSARGS;
char szDSN[DSN_LENGTH]; // string to hold datasource name
ODBC_TYPE * h;
char *szIn = 0;
int iTemp = 0;
RETCODE retcode; // Misc ODBC sh!t
UCHAR buff[ODBC_BUFF_SIZE];
SDWORD bufflenout;
int lenn = 0;
STRLEN n_a;
if(items < 1 || !(items & 1)){
// We need at least 1 (DSN) entry. If more then we need them in pairs of
// two, hence (items & 1) make sure we have an odd number of entries...
// (dsn) + (ConnetOption, Value) [ + (ConnectOption, Value)] ...
CROAK("usage: ($Connection, $Err, $ErrText) = ODBC_Connect($DSN [, $ConnectOption , $Value] ...)\n");
}
szIn = SvPV(ST(0), n_a);
if(strcspn(szIn, "[]{}(),;?*=!@") < strlen(szIn)){
strncpy(szDSN, szIn, DSN_LENGTH - 1);
szDSN[DSN_LENGTH - 1] = '\0';
}else{
// Let's assume that the DSN will not exceed DSN_LENGTH
strcpy(szDSN, "DSN=");
strcat(szDSN, szIn); // Data Source string
strcat(szDSN, ";");
}
PUSHMARK(sp);
// Allocate new ODBC connection
if (!(h = NewODBC())){
h = ODBCError("Could not allocate memory of an ODBC connection\n", (ODBC_TYPE*)0, "ODBC_Connect", "1");
}else{
strcpy(h->szUserDSN, szIn);
if (h->hdbc = new ODBC_HDBC){
h->hdbc->hdbc = SQL_NULL_HDBC;
h->hdbc->iConnected = 0;
h->hdbc->iCount = 1; // Set the count to include this particular instance
}else{
_NT_ODBC_Error(h, "ODBC_Connect could not allocate memory for an HDBC connection", "1a");
DeleteConn(h->conn);
}
if (!h->Error->ErrNum){
retcode = SQLAllocConnect(h->henv, &h->hdbc->hdbc);
if (retcode != SQL_SUCCESS)
{
_NT_ODBC_Error((ODBC_TYPE*)0, "ODBC_Connect", "2");
DeleteConn(h->conn);
}
}
if (!h->Error->ErrNum){
// If any pre-connect SQLConnectOptions are specified, do them now...
if (items > 1){
int iTemp = items -1;
UWORD uType;
UDWORD udValue;
char szError[100];
while (iTemp > 1){
uType = (UWORD)SvIV(ST(iTemp - 1));
if (SvIOKp(ST(iTemp)) || SvNOKp(ST(iTemp))){
udValue = SvIV(ST(iTemp));
}else{
udValue = (UDWORD) SvPV(ST(iTemp), n_a);
}
retcode = SQLSetConnectOption(h->hdbc->hdbc, uType, udValue);
if (retcode != SQL_SUCCESS){
sprintf(szError, "2a: Connect Item number %i", (iTemp/2));
_NT_ODBC_Error(h, "ODBC_Connect", szError);
break;
}
iTemp -= 2;
}
}
}
if (!h->Error->ErrNum){
retcode = SQLDriverConnect(h->hdbc->hdbc, (HWND) NULL, (unsigned char *)szDSN, strlen(szDSN), buff, ODBC_BUFF_SIZE, (short *)&bufflenout, SQL_DRIVER_NOPROMPT);
if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO){
_NT_ODBC_Error(h, "ODBC_Connect", "4");
strcpy(ODBCLIST->Error->szError, h->Error->szError);
strcpy((char *)ODBCLIST->Error->szSqlState, (char *)h->Error->szSqlState);
strcpy(ODBCLIST->Error->szFunction, h->Error->szFunction);
strcpy(ODBCLIST->Error->szFunctionLevel, h->Error->szFunctionLevel);
// If connection fails it does not genereate an error num. Hmmm...
if(! h->Error->ErrNum){
h->Error->ErrNum = 911;
}
ODBCLIST->Error->ErrNum = h->Error->ErrNum;
DeleteConn(h->conn);
h = ODBCLIST;
}else{
h->hdbc->iConnected = 1;
if (h->szDSN = new char [strlen((const char *)buff) + 1]){
strcpy(h->szDSN, (char *) buff);
}
if (retcode == SQL_SUCCESS_WITH_INFO){
_NT_ODBC_Error(h, "ODBC_Connect", "5");
h->Error->ErrNum = 0;
}
}
}
if (!h->Error->ErrNum){
retcode = ResetStmt(h);
if (retcode != SQL_SUCCESS){
DeleteConn(h->conn);
}
}
}
if (!h->Error->ErrNum){ // everything is happy
XPUSHs(sv_2mortal(newSVnv((double)0)));
XPUSHs(sv_2mortal(newSVnv((double)h->conn)));
// Report the szError ONLY because it may contain state info.
XPUSHs(sv_2mortal(newSVpv(h->Error->szError, strlen(h->Error->szError))));
}else{
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_Execute) // ODBC_Execute($connection, $sql_text) returns (0,@fieldnames) or (err, errtext)
{
dXSARGS;
ODBC_TYPE * h;
RETCODE retcode; //ODBC gunk
UCHAR buff2[ODBC_BUFF_SIZE];
SDWORD bufflenout;
UWORD x;
char * szSQL;
STRLEN n_a;
if(items < 2){
CROAK("usage: ($err,@fields) = ODBC_Execute($connection, $sql_text)\nprint \"Oops: $field[0]\" if ($err);\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
CleanError(h->Error);
PUSHMARK(sp);
if (h != ODBCLIST){
szSQL = SvPV(ST(1), n_a); // get SQL string
// Do we really want to resetsmtm now?
if (ResetStmt(h) == SQL_SUCCESS){
if (h->szCommand){
delete [] h->szCommand;
h->szCommand = 0;
}
if (h->szCommand = new char [strlen(szSQL) + 1]){
strcpy(h->szCommand, szSQL);
}
}
if (!h->Error->ErrNum){
retcode = SQLExecDirect(h->hstmt, (unsigned char*)szSQL, strlen(szSQL));
if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO){
_NT_ODBC_Error(h, "ODBC_Execute", "1");
}else{
if(h->Results){
delete h->Results;
}
h->Results = new CResults(h);
}
}
}
if (!h->Error->ErrNum){ // everything is happy
XPUSHs(sv_2mortal(newSVnv((double)0)));
retcode = SQLNumResultCols(h->hstmt, (short *)&h->numcols);
for(x=1; x<=h->numcols; x++){
SQLColAttributes(h->hstmt, x, SQL_COLUMN_NAME, buff2, ODBC_BUFF_SIZE, (short *)&bufflenout, NULL);
XPUSHs(sv_2mortal(newSVpv((char *)buff2, strlen((const char*)buff2))));
}
}else{
// Report the error
ReturnError(h);
}
PUTBACK;
}
/*
ODBC_Fetch
Fetch a row from the current dataset.
*/
XS(XS_WIN32__ODBC_Fetch) // ODBC_Fetch($connection) returns (0,@dataelements) or ($err,$errtext)
{
dXSARGS;
ODBC_TYPE * h;
RETCODE retcode; // yet more ODBC garbage
UWORD uType = SQL_FETCH_NEXT;
SDWORD sdRow = 1;
DWORD dRowSetSize = 1;
UWORD *rgfRowStatus = 0;
SQLULEN udCRow = 0;
int iTemp;
if(items < 1 || items > 3){
CROAK("usage: ($err,@col) = ODBC_Fetch($connection [, $Row [, $FetchType]])\n0die \"Oops: $col[0]\" if ($err);\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
// NOTICE: We do not perform a CleanODBC(h) here because a dirty
// h will tell us if there is an error such as EOR (end of records).
if (items > 1){
sdRow = SvIV(ST(1));
// uType = SQL_FETCH_RELATIVE;
}
if (items > 2){
uType = (UWORD)SvIV(ST(2));
}
PUSHMARK(sp);
if (h != ODBCLIST){
SQLGetStmtOption(h->hstmt, SQL_ROWSET_SIZE, &dRowSetSize);
// SQLSetStmtOption(h->hstmt, SQL_ROWSET_SIZE, 1);
if (!(rgfRowStatus = new UWORD [dRowSetSize])){
ODBCError("Can not allocate memory for row status results", h, "ODBC_Fetch", "1b");
}
if (h->Results){
// Clear out the old results
h->Results->Clean();
}
retcode = SQLExtendedFetch(h->hstmt, uType, sdRow, &udCRow, rgfRowStatus);
// SQLSetStmtOption(h->hstmt, SQL_ROWSET_SIZE, dRowSetSize);
if((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)){
_NT_ODBC_Error(h, "ODBC_Fetch", "2");
}
if (retcode == SQL_NO_DATA_FOUND){
ODBCError("No data records remain.", h, "ODBC_Fetch", "3");
h->Error->EOR = 1;
}
if(retcode == SQL_SUCCESS_WITH_INFO){
_NT_ODBC_Error(h, "ODBC_Fetch", "4");
}
}
if (! h->Error->ErrNum){
// everything is happy
XPUSHs(sv_2mortal(newSVnv((double)0)));
if(items > 1){
for(iTemp = 0; iTemp < (int)udCRow; iTemp++){
XPUSHs(sv_2mortal(newSVnv((double)rgfRowStatus[iTemp])));
}
}else{
XPUSHs(sv_2mortal(newSVnv((double)1)));
}
}else{
// Report the error
ReturnError(h);
}
if (rgfRowStatus){
delete [] rgfRowStatus;
}
PUTBACK;
}
XS(XS_WIN32__ODBC_GetError)
{
dXSARGS;
ODBC_TYPE *h;
h = _NT_ODBC_Verify(SvIV(ST(0)));
PUSHMARK(sp);
XPUSHs(sv_2mortal(newSVnv((double)h->Error->ErrNum)));
XPUSHs(sv_2mortal(newSVpv(h->Error->szError, strlen(h->Error->szError))));
XPUSHs(sv_2mortal(newSVpv((char *)h->Error->szSqlState, strlen(( const char *) h->Error->szSqlState))));
if (h->iDebug){
XPUSHs(sv_2mortal(newSVpv((char *)h->Error->szFunction, strlen(( const char *) h->Error->szFunction))));
XPUSHs(sv_2mortal(newSVpv((char *)h->Error->szFunctionLevel, strlen(( const char *) h->Error->szFunctionLevel))));
}
PUTBACK;
}
XS(XS_WIN32__ODBC_Disconnect) // usage: ODBC_Disconnect($conn) or ODBC_Disconnect() for all
{
dXSARGS;
ODBC_TYPE *h;
int iResult = 0;
int conn;
if(items < 1){
conn = 0;
}else{
conn = SvIV(ST(0));
}
PUSHMARK(sp);
if (!(iResult = DeleteConn(conn))){
h = ODBCError("No such connection", (ODBC_TYPE*) 0, "ODBC_Disconnect", "1");
#ifdef _DEBUG
if (h){
if(h->iDebug){
char szBuff[40];
sprintf(szBuff, "ODBC_Disconnect: No such connection %i\n", conn);
DebugConnection(szBuff, h);
}
}
#endif
}
if (iResult){
XPUSHs(sv_2mortal(newSVnv((double)0)));
XPUSHs(sv_2mortal(newSVnv((double)iResult)));
}else{
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_TableList) // ODBC_TableList($connection) returns (0,@TableNames) or ($err,$errtext)
{
dXSARGS;
RETCODE retcode;
if(items != 5){
CROAK("usage: ($Err,@ColumnNames) = ODBC_TableList($connection, $Qualifier, $Owner, $TableName, $TableType)\n");
}
retcode = TableColList(aTHX_ 0);
}
XS(XS_WIN32__ODBC_ColumnList)
{
dXSARGS;
RETCODE retcode;
if(items != 5){
CROAK("usage: ($Err,@ColumnNames) = ODBC_ColumnList($connection, $Qualifier, $Owner, $TableName, $ColumnName)\n");
}
retcode = TableColList(aTHX_ 1);
}
RETCODE TableColList(pTHX_ int iType){
dXSARGS;
ODBC_TYPE * h;
int iTemp;
UCHAR buff2[ODBC_BUFF_SIZE];
SDWORD bufflenout;
UWORD x;
STRLEN n_a;
RETCODE retcode;
UCHAR *szQualifier, *szOwner, *szName, *szType;
SWORD sQLen, sOLen, sNLen, sTLen = 0;
UWORD uTemp = 0;
h = _NT_ODBC_Verify(SvIV(ST(0)));
CleanError(h->Error);
if (szQualifier = (unsigned char *) SvPV(ST(1), n_a)){
if (!(sQLen = strlen((char *)szQualifier))){
szQualifier = 0;
}
}
if (szOwner = (unsigned char *) SvPV(ST(2), n_a)){
if (!(sOLen = strlen((char *)szOwner))){
szOwner = 0;
}
}
if (szName = (unsigned char *) SvPV(ST(3), n_a)){
if (!(sNLen = strlen((char *)szName))){
szName = 0;
}
}
if (szType = (unsigned char *) SvPV(ST(4), n_a)){
if (!(sTLen = strlen((char *)szType))){
szType = 0;
}
}
PUSHMARK(sp);
if (h != ODBCLIST){
CleanError(h->Error);
retcode = ResetStmt(h);
if (h->szCommand){
delete [] h->szCommand;
h->szCommand = 0;
}
/* The funny odd thing about this next line is that when we allocate
memory of h->szCommand it will be based on a sprintf(). The format
string for sprintf() will have references to szQualifier, szOwner, etc.
IF any of those strings are 0 then the %s's in the format string may be
replaced with "(null)" or some other indicator of a null pointer. Therefore
in the event of szQualifier (or whatever) == 0 we will provide an arbitrary
number of bytes for allocation (like 10).
*/
if (h->szCommand = new char [strlen((const char *) TABLE_COMMAND_STRING)
+ 12 // for the name of the function
+ (szQualifier ? strlen((const char *)szQualifier):10)
+ (szOwner ? strlen((const char *)szOwner):10)
+ (szName ? strlen((const char *)szName):10)
+ (szType ? strlen((const char *)szType):10) + 1]){
sprintf(h->szCommand, TABLE_COMMAND_STRING, (iType)? "SQLColumns":"SQLTables", szQualifier, szOwner, szName, szType);
}
if(iType){
retcode = SQLColumns(h->hstmt, szQualifier, sQLen, szOwner, sOLen, szName, sNLen, szType, sTLen);
}else{
retcode = SQLTables(h->hstmt, szQualifier, sQLen, szOwner, sOLen, szName, sNLen, szType, sTLen);
}
if(retcode != SQL_SUCCESS){
_NT_ODBC_Error(h, "ODBC_TableColList", "1");
}
}
if (!h->Error->ErrNum){
if (h->Results){
delete h->Results;
}
h->Results = new CResults (h);
XPUSHs(sv_2mortal(newSVnv((double)0)));
retcode = SQLNumResultCols(h->hstmt, (short *)&h->numcols);
for(x=1; x<=h->numcols; x++){
*buff2 = '\0';
SQLColAttributes(h->hstmt, x, SQL_COLUMN_NAME, buff2, ODBC_BUFF_SIZE, (short *)&bufflenout, NULL);
/*
We need to convert the columns to uppercase since different
ODBC drivers impliment this differently (Access uses upperc, MS SQL server uses lowerc)
We should probably figure out a more reasonable solution.
*/
for(iTemp = strlen((char *)buff2) - 1; iTemp >= 0; iTemp--){
buff2[iTemp] = (char) toupper(buff2[iTemp]);
}
XPUSHs(sv_2mortal(newSVpv((char *)buff2, strlen((const char*)buff2))));
}
}else{
// Report the error
ReturnError(h);
}
PUTBACK;
return retcode;
}
/*
This next chunk of code (XS_WIN32__ODBC_MoreResults) was
graciously donated by Brian Dunfordshore <Brian_Dunfordshore@bridge.com>.
Thanks Brian!
96.07.10
*/
XS(XS_WIN32__ODBC_MoreResults) // usage: ODBC_MoreResults($conn)
{
dXSARGS;
RETCODE retcode; // yet more ODBC garbage
ODBC_TYPE *h;
h = _NT_ODBC_Verify(SvIV(ST(0)));
CleanError(h->Error);
PUSHMARK(sp);
if (h != ODBCLIST){
retcode = SQLMoreResults(h->hstmt);
if((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)){
_NT_ODBC_Error(h, "ODBC_MoreResults", "1");
}
if (retcode == SQL_NO_DATA_FOUND){
h->Error->EOR = 1;
strcpy(h->Error->szError, "No data records remain.");
}
}
if (!h->Error->ErrNum){ // everything is happy
XPUSHs(sv_2mortal(newSVnv((double)0)));
XPUSHs(sv_2mortal(newSVnv((double)1))); // Return a TRUE
}else{
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_MaxBufSize)
{
dXSARGS;
ODBC_TYPE * h;
long iSize;
if(items <1 || items > 2){
CROAK("usage: ($Err, $Size) = ODBC_MaxBufSize($Connection [, $NewSize])\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
CleanError(h->Error);
PUSHMARK(sp);
if (h != ODBCLIST){
if (items > 1){
iSize = SvIV(ST(1));
iSize = ((iSize <= 0)? 0:iSize);
iSize = ((iSize >= MAX_DATA_BUF_SIZE)? MAX_DATA_BUF_SIZE:iSize);
h->iMaxBufSize = iSize;
}
XPUSHs(sv_2mortal(newSVnv((double)0)));
XPUSHs(sv_2mortal(newSVnv((double)h->iMaxBufSize)));
}else{
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_GetConnections)
{
dXSARGS;
int iTemp;
ODBC_TYPE *h;
CMom *cmDaughter = (CMom *) ::cMom->operator[](GetCurrentThreadId());
if(items){
CROAK("usage: (@ConnectionList) = ODBC_GetConnections()\n");
}
PUSHMARK(sp);
for (iTemp = cmDaughter->TotalElements(); iTemp; iTemp--){
h = (ODBC_TYPE *) cmDaughter->operator[]((DWORD)iTemp - 1);
if (h){
XPUSHs(sv_2mortal(newSVnv((double)h->conn)));
}
}
PUTBACK;
}
XS(XS_WIN32__ODBC_GetDSN)
{
dXSARGS;
ODBC_TYPE *h;
char *szDSN = 0;
char *szTemp = 0;
DWORD dSize = 1024;
char *szKeys= 0;
char *szValues = 0;
char *szPointer = 0;
STRLEN n_a;
if(items > 2 || items < 1){
CROAK("usage: ($Err, $DSN) = ODBC_GetDSN($Connection [, $DSN])\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
CleanError(h->Error);
if (items > 1){
szDSN = SvPV(ST(1), n_a);
if (! strlen(szDSN)){
szDSN = 0;
}
}
if (! szDSN){
szDSN = h->szDSN;
while(szDSN){
if (szDSN[0] == ';'){
szDSN++;
}
if (! strnicmp("DSN=", szDSN, 4)){
szDSN += 4 * sizeof(char);
break;
}
szDSN = strchr(szDSN, ';');
}
}
PUSHMARK(sp);
szTemp = szDSN;
if (! (szDSN = new char [strlen(szTemp) + 1])){
szDSN = 0;
ODBCError("Could not allocate memory for DSN comparison", h, "ODBC_GetDSN", "1");
}else{
strcpy(szDSN, szTemp);
szTemp = strchr(szDSN, ';');
if (szTemp){
*szTemp = '\0';
}
if (szKeys = new char [dSize + 1]){
SQLGetPrivateProfileString( szDSN, 0, "", szKeys, dSize, "ODBC.INI");
if (strcmp(szKeys, "")){
szPointer = szKeys;
if (szValues = new char [dSize + 1]){
XPUSHs(sv_2mortal(newSVnv((double)0)));
while(*szPointer){
SQLGetPrivateProfileString( szDSN, szPointer, "", szValues, dSize, "ODBC.INI");
XPUSHs(sv_2mortal(newSVpv(szPointer, strlen(szPointer))));
XPUSHs(sv_2mortal(newSVpv(szValues, strlen(szValues))));
szPointer += strlen(szPointer) + 1;
}
if(szValues) delete [] szValues;
}else{
ODBCError("Could not allocate enough memory", h, "ODBC_GetDSN", "2");
}
}else{
ODBCError("Not a valid DSN", h, "ODBC_GetDSN", "3");
}
if(szKeys) delete [] szKeys;
}else{
ODBCError("Could not allocate enough memory", h, "ODBC_GetDSN", "4");
}
}
if (h->Error->ErrNum){
// Report the error
ReturnError(h);
}
if (szDSN){
delete [] szDSN;
}
PUTBACK;
}
XS(XS_WIN32__ODBC_DataSources)
{
dXSARGS;
ODBC_TYPE *h;
UCHAR szDSN[SQL_MAX_DSN_LENGTH + 1];
SWORD pcbDSN;
UCHAR szDesc[DS_DESCRIPTION_LENGTH];
SWORD pcbDesc;
char *szRequestedDSN = 0;
RETCODE retcode;
STRLEN n_a;
if(items > 1){
CROAK("usage: ($Err, $DSN) = ODBC_DataSources([$DSN])\n");
}
h = ODBCLIST;
if (items){
szRequestedDSN = SvPV(ST(0), n_a);
if (!strlen(szRequestedDSN)){
szRequestedDSN = 0;
}
}
PUSHMARK(sp);
*szDSN = *szDesc = '\0';
retcode= SQLDataSources(h->henv, SQL_FETCH_FIRST, szDSN, SQL_MAX_DSN_LENGTH + 1, &pcbDSN, szDesc, DS_DESCRIPTION_LENGTH, &pcbDesc);
if(retcode == SQL_SUCCESS){
XPUSHs(sv_2mortal(newSVnv((double)0)));
while (retcode == SQL_SUCCESS){
if ((szRequestedDSN && (!stricmp((const char *)szDSN, szRequestedDSN))) || !szRequestedDSN){
XPUSHs(sv_2mortal(newSVpv((char *)szDSN, strlen((char*)szDSN))));
XPUSHs(sv_2mortal(newSVpv((char *)szDesc, strlen((char*)szDesc))));
}
*szDSN = *szDesc = '\0';
retcode= SQLDataSources(h->henv, SQL_FETCH_NEXT, szDSN, SQL_MAX_DSN_LENGTH + 1, &pcbDSN, szDesc, DS_DESCRIPTION_LENGTH, &pcbDesc);
}
}else{
h = ODBCError("No such ODBC connection.", (ODBC_TYPE*) 0, "ODBC_DataSources", "1");
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_Drivers)
{
dXSARGS;
ODBC_TYPE *h;
UCHAR szAttr[DS_DESCRIPTION_LENGTH + 1];
SWORD cbAttr;
UCHAR szDesc[DS_DESCRIPTION_LENGTH];
SWORD cbDesc;
char *szTemp;
RETCODE retcode;
if(items > 0){
CROAK("usage: ($Err, $DSN) = ODBC_Drivers()\n");
}
h = ODBCLIST;
PUSHMARK(sp);
*szDesc = *szAttr = '\0';
retcode = SQLDrivers(h->henv, SQL_FETCH_FIRST, szDesc, DS_DESCRIPTION_LENGTH, &cbDesc, szAttr, DS_DESCRIPTION_LENGTH, &cbAttr);
if(retcode == SQL_SUCCESS){
XPUSHs(sv_2mortal(newSVnv((double)0)));
while (retcode == SQL_SUCCESS){
szTemp = (char *) szAttr;
while(szTemp[0] != '\0'){
szTemp = strchr(szTemp, '\0');
*szTemp++ = ';';
}
XPUSHs(sv_2mortal(newSVpv((char *)szDesc, strlen((char*)szDesc))));
XPUSHs(sv_2mortal(newSVpv((char *)szAttr, strlen((char*)szAttr))));
*szDesc = *szAttr = '\0';
retcode = SQLDrivers(h->henv, SQL_FETCH_NEXT, szDesc, DS_DESCRIPTION_LENGTH, &cbDesc, szAttr, DS_DESCRIPTION_LENGTH, &cbAttr);
}
}else{
h = ODBCError("No such ODBC connection.", (ODBC_TYPE*) 0, "ODBC_Drivers", "1");
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_RowCount)
{
dXSARGS;
ODBC_TYPE *h;
SQLLEN sdRows = 0L;
RETCODE retcode;
if(items != 1){
CROAK("usage: ($Err, $NumOfRows) = ODBC_RowCount($Connection)\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
CleanError(h->Error);
PUSHMARK(sp);
retcode = SQLRowCount(h->hstmt, &sdRows);
if(retcode == SQL_SUCCESS){
XPUSHs(sv_2mortal(newSVnv((double)0)));
XPUSHs(sv_2mortal(newSVnv((double)sdRows)));
}else{
h = ODBCError("No such ODBC connection.", (ODBC_TYPE*) 0, "ODBC_RowCount", "1");
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_Info)
{
dXSARGS;
if(items > 0){
CROAK("usage: ($ExtName, $Version, $Date, $Author, $CompileDate, $Credits, $Comments) = Info()\n");
}
PUSHMARK(sp);
XPUSHs(sv_2mortal(newSVpv(VERNAME, strlen(VERNAME))));
XPUSHs(sv_2mortal(newSVpv(VERSION, strlen(VERSION))));
XPUSHs(sv_2mortal(newSVpv(VERDATE, strlen(VERDATE))));
XPUSHs(sv_2mortal(newSVpv(VERAUTH, strlen(VERAUTH))));
XPUSHs(sv_2mortal(newSVpv(VERDATE, strlen(VERDATE))));
XPUSHs(sv_2mortal(newSVpv(VERTIME, strlen(VERTIME))));
XPUSHs(sv_2mortal(newSVpv(VERCRED, strlen(VERCRED))));
XPUSHs(sv_2mortal(newSVpv(VERCOMMENT, strlen(VERCOMMENT))));
PUTBACK;
}
XS(XS_WIN32__ODBC_GetStmtCloseType)
{
dXSARGS;
ODBC_TYPE * h;
char *szType;
if(items != 1){
CROAK("usage: ($Err, $Type) = ODBC_GetStmtCloseType($Connection)\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
CleanError(h->Error);
PUSHMARK(sp);
if (!(szType = MapCloseType(h->uStmtCloseType))){
ODBCError("Invalid Statment Close Type", h, "ODBC_GetStmtCloseType", "1");
}
if (!h->Error->ErrNum){
XPUSHs(sv_2mortal(newSVnv((double)0)));
XPUSHs(sv_2mortal(newSVpv(szType, strlen(szType))));
}else{
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_SetStmtCloseType)
{
dXSARGS;
ODBC_TYPE * h;
char *szType;
UWORD uType;
if(items != 2){
CROAK("usage: ($Err, $Type) = ODBC_SetStmtCloseType($Connection, $Type)\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
CleanError(h->Error);
uType = (UWORD)SvIV(ST(1));
PUSHMARK(sp);
switch(uType){
case SQL_DONT_CLOSE:
case SQL_DROP:
case SQL_CLOSE:
case SQL_UNBIND:
case SQL_RESET_PARAMS:
h->uStmtCloseType = uType;
if (!(szType = MapCloseType(h->uStmtCloseType))){
ODBCError("Invalid Statment Close Type", h, "ODBC_SetStmtCloseType", "1");
}
break;
default:
ODBCError("Not a valid Stmt Close Type", h, "ODBC_SetStmtCloseType", "2");
}
if (!h->Error->ErrNum){
XPUSHs(sv_2mortal(newSVnv((double)0)));
XPUSHs(sv_2mortal(newSVpv(szType, strlen(szType))));
}else{
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_SetConnectOption)
{
dXSARGS;
ODBC_TYPE * h;
UWORD uType;
UDWORD udValue;
RETCODE rResult = 0;
STRLEN n_a;
if(items != 3){
CROAK("usage: ($Err, $Type) = ODBC_SetConnectOption($Connection, $Type, $Value)\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
CleanError(h->Error);
uType = (UWORD)SvIV(ST(1));
if (SvIOKp(ST(2)) || SvNOKp(ST(2))){
udValue = SvIV(ST(2));
}else{
udValue = (UDWORD) SvPV(ST(2), n_a);
}
PUSHMARK(sp);
if(!h->Error->ErrNum){
rResult = SQLSetConnectOption(h->hdbc->hdbc, uType, udValue);
if (rResult != SQL_SUCCESS){
_NT_ODBC_Error(h, "ODBC_SetConnectOption", "1");
}
}
if (!h->Error->ErrNum){
XPUSHs(sv_2mortal(newSVnv((double)0)));
XPUSHs(sv_2mortal(newSVnv((double)1)));
}else{
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_GetConnectOption)
{
dXSARGS;
ODBC_TYPE * h;
UCHAR ucValue[SQL_MAX_OPTION_STRING_LENGTH + 1];
DWORD *dValue = (DWORD *)ucValue;
UWORD uOption;
RETCODE rResult = 0;
if(items != 2){
CROAK("usage: ($Err, $NumValue, $Value) = ODBC_GetConnectOption($Connection, $Type)\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
CleanError(h->Error);
uOption = (UWORD)SvIV(ST(1));
PUSHMARK(sp);
if(!h->Error->ErrNum){
memset(ucValue, 255, SQL_MAX_OPTION_STRING_LENGTH + 1);
rResult = SQLGetConnectOption(h->hdbc->hdbc, uOption, ucValue);
if (rResult != SQL_SUCCESS){
_NT_ODBC_Error(h, "ODBC_GetConnectOption", "1");
}
}
if (!h->Error->ErrNum){
XPUSHs(sv_2mortal(newSVnv((double)0)));
switch(uOption){
case SQL_ACCESS_MODE:
case SQL_AUTOCOMMIT:
case SQL_LOGIN_TIMEOUT:
case SQL_ODBC_CURSORS:
case SQL_OPT_TRACE:
case SQL_PACKET_SIZE:
case SQL_QUIET_MODE:
case SQL_TRANSLATE_OPTION:
case SQL_TXN_ISOLATION:
// GetConnectOption returned a DWORD
XPUSHs(sv_2mortal(newSVnv((double) *dValue)));
break;
default:
// GetConnectOption returned a string
XPUSHs(sv_2mortal(newSVpv((char *)ucValue, strlen((char *) ucValue))));
break;
}
}else{
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_StmtOption)
{
dXSARGS;
ODBC_TYPE * h;
UCHAR ucValue[SQL_MAX_OPTION_STRING_LENGTH + 1];
DWORD *dValue = (DWORD *)ucValue;
UWORD uOption;
UDWORD udValue;
RETCODE rResult = 0;
STRLEN n_a;
if(items < 2 || items > 3){
CROAK("usage: ($Err, $NumValue, $Value) = ODBC_StmtOption($Connection, $Type [,$Value])\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
CleanError(h->Error);
uOption = (UWORD)SvIV(ST(1));
if (items > 2){
if (SvIOKp(ST(2)) || SvNOKp(ST(2))){
udValue = SvIV(ST(2));
}else{
udValue = (UDWORD) SvPV(ST(2), n_a);
}
}
PUSHMARK(sp);
if(!h->Error->ErrNum){
if(items < 3){
// Less than three parameters passed in so we are GETTING the stmt option
memset(ucValue, 255, SQL_MAX_OPTION_STRING_LENGTH + 1);
rResult = SQLGetStmtOption(h->hstmt, uOption, &ucValue);
if (rResult != SQL_SUCCESS){
_NT_ODBC_Error(h, "ODBC_StmtOption", "1");
}
}else{
// Three parameters passed in so we are SETTING the stmt option
rResult = SQLSetStmtOption(h->hstmt, uOption, udValue);
if (rResult != SQL_SUCCESS){
_NT_ODBC_Error(h, "ODBC_StmtOption", "2");
}
}
}
if (!h->Error->ErrNum){
XPUSHs(sv_2mortal(newSVnv((double)0)));
if(items < 3){
switch(uOption){
/* Even though this function is basically the same
as the ODBC_GetConnectOption the default on the
select() is a DWORD since so few (actually as of
now NONE) of the options return strings. So the
list of strings returns (case: xxx) will be small.
*/
case 0xf0f0: // Something toatly unreasonable since I can't find a real string return.
// GetStmtOption returned a string
XPUSHs(sv_2mortal(newSVpv((char *)ucValue, strlen((char *) ucValue))));
break;
default:
// GetStmtOption returned a DWORD
XPUSHs(sv_2mortal(newSVnv((double) *dValue)));
break;
}
}else{
XPUSHs(sv_2mortal(newSVnv((double)1)));
}
}else{
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_GetFunctions)
{
dXSARGS;
ODBC_TYPE * h;
UWORD uOutput[100];
UWORD uOption;
RETCODE rResult = 0;
int iTemp, iTotal;
iTemp = iTotal = 0;
if(items < 1 || items > 101){
CROAK("usage: ($Err, $NumValue, $Value) = ODBC_GetFunctions($Connection, ($Function1, $Function2 ... $Function100))\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
CleanError(h->Error);
PUSHMARK(sp);
items--;
// We decremented items, so if it's < 1 assume we want ALL functions!
if (items < 1){
uOption = SQL_API_ALL_FUNCTIONS;
iTotal = 100;
items = 1;
}else{
uOption = (UWORD)SvIV(ST(1));
}
while(items--){
rResult = SQLGetFunctions(h->hdbc->hdbc, uOption, &uOutput[iTemp]);
if (rResult != SQL_SUCCESS){
_NT_ODBC_Error(h, "ODBC_GetFunctions", "1");
if(! strcmp((const char*) h->Error->szSqlState, "00000")){
// The requested function number does not exist.
CleanError(h->Error);
uOutput[iTemp] = ~0;
}else{
items = 0;
}
}
iTemp++;
if (items){ // If there are no more stack elements we will screw up
// trying to access ST(1 + iTemp)
uOption = (UWORD)SvIV(ST(1 + iTemp));
}
}
if (!h->Error->ErrNum){
XPUSHs(sv_2mortal(newSVnv((double)0)));
if (!iTotal){
iTotal = iTemp;
}
for (iTemp = 0; iTemp < iTotal; iTemp++){
XPUSHs(sv_2mortal(newSVnv((double) uOutput[iTemp])));
}
}else{
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_Transact)
{
dXSARGS;
ODBC_TYPE * h;
UWORD uType;
RETCODE rResult = 0;
if(items != 2){
CROAK("usage: ($Err, $Type) = ODBC_Transact($Connection, $Type)\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
CleanError(h->Error);
uType = (UWORD)SvIV(ST(1));
PUSHMARK(sp);
if(!h->Error->ErrNum){
if(uType == SQL_ROLLBACK || uType == SQL_COMMIT){
rResult = SQLTransact(h->henv, h->hdbc->hdbc, uType);
if (rResult != SQL_SUCCESS){
_NT_ODBC_Error(h, "ODBC_Transact", "1");
}
}else{
ODBCError("Invalid Transaction Type", h, "ODBC_Transact", "2");
}
}
if (!h->Error->ErrNum){
XPUSHs(sv_2mortal(newSVnv((double)0)));
XPUSHs(sv_2mortal(newSVnv((double)1)));
}else{
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_ConfigDSN)
{
dXSARGS;
ODBC_TYPE * h;
UWORD uType;
int iStack = 0;
char *szDriver = 0;
char *szTemp = 0;
char *szTemp2 = 0;
char *szAttributes = 0;
int iResult = 0;
int iSize = 0;
STRLEN n_a;
if(items < 3){
CROAK("usage: ($Err, $Type) = ODBC_ConfigDSN($Connection, $Function, $Driver, @Attributes)\n");
}
h = _NT_ODBC_Verify(SvIV(ST(iStack)));
CleanError(h->Error);
iStack++;
uType = (UWORD)SvIV(ST(iStack));
iStack++;
szDriver = SvPV(ST(iStack), n_a);
if (strlen(szDriver) == 0){
szDriver = 0;
}
iStack++;
items -= iStack; // Reduce the # of items by 3 (compensate for connection, function and driver)
// Remember: when starting szAttributes = 0, not ""
while (items--){
szTemp = SvPV(ST(iStack), n_a);
if(strcspn(szTemp, "[]{}(),?*!@;") < strlen(szTemp)){
ODBCError("Illegal use of reserved characters []{}(),?*!@;", h, "ODBC_ConfigDSN", "1");
break;
}
iStack++;
iSize += strlen(szTemp) + 2;
if (! (szTemp2 = new char [iSize])){
ODBCError("Could not allocate memory for the attribute list", h, "ODBC_ConfigDSN", "2");
items = 0;
}else{
if (szAttributes) {
strcpy(szTemp2, szAttributes);
}else{
strcpy(szTemp2, "");
}
strcat(szTemp2, szTemp);
strcat(szTemp2, ";");
if (szAttributes){
delete [] szAttributes;
}
szAttributes = szTemp2;
szTemp2 = 0;
}
}
PUSHMARK(sp);
/*
As of now szAttributes contains the entire DSN
*/
if (! h->Error->ErrNum){
if(strcspn(szAttributes, "[]{}(),?*!@") < strlen(szAttributes)){
ODBCError("Illegal use of reserved characters []{}(),?*!@", h, "ODBC_ConfigDSN", "3");
}
// Format Attribute list for the ConfigDataSource function...
// attrib1=value1\0attrib2=value2\0attrib3=value3\0\0
for (;iSize > -1; iSize--){
if (szAttributes[iSize] == ';'){
szAttributes[iSize] = '\0';
}
}
iResult = SQLConfigDataSource(0, (UINT) uType, (LPCSTR) szDriver, (LPCSTR) szAttributes);
}
if (!h->Error->ErrNum){
XPUSHs(sv_2mortal(newSVnv((double)0)));
XPUSHs(sv_2mortal(newSVnv((double)iResult)));
}else{
// Report the error
ReturnError(h);
}
if (szAttributes){
delete [] szAttributes;
}
PUTBACK;
}
XS(XS_WIN32__ODBC_GetInfo)
{
dXSARGS;
ODBC_TYPE * h;
UCHAR *ucValue = 0;
int iSize = sizeof(DWORD) + 1; // Arbitrary size to allocate for result
int iFlag = 0;
DWORD *dValue;
UWORD *uValue;
UWORD uType;
SWORD swBytes = 0;
RETCODE rResult = 0;
if(items != 2){
CROAK("usage: ($Err, $NumValue, $Value) = ODBC_GetInfo($Connection, $Type)\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
CleanError(h->Error);
uType = (UWORD)SvIV(ST(1));
PUSHMARK(sp);
if(!h->Error->ErrNum){
while (!iFlag++){
if (ucValue = (UCHAR *) new char [iSize]){
// Set the memory set to $FF so we can tell if the return value
// is a string. It will have a \0 at the end of it.
memset(ucValue, 0xff, iSize);
dValue = (DWORD *) ucValue;
uValue = (UWORD *) ucValue;
rResult = SQLGetInfo(h->hdbc->hdbc, uType, ucValue, (SWORD) iSize, &swBytes);
if (rResult != SQL_SUCCESS){
_NT_ODBC_Error(h, "ODBC_GetInfo", "1");
}
}else{
ODBCError("Could not allocate memory for result string", h, "ODBC_GetInfo", "2");
ucValue = 0;
}
if (iSize <= (int) swBytes){
if(ucValue){delete [] ucValue;}
iSize = (int) swBytes + 1;
iFlag = 0;
CleanError(h->Error);
}
}
}
if (!h->Error->ErrNum){
XPUSHs(sv_2mortal(newSVnv((double)0)));
if (ucValue[swBytes] == 0){
// GetInfo() returned a string!
XPUSHs(sv_2mortal(newSVpv((char *) ucValue, (int) swBytes)));
}else{
switch (swBytes){
case 2: // We must have a 16 bit value
XPUSHs(sv_2mortal(newSVnv((double) *uValue)));
break;
case 4: // We must have a 32 bit value
XPUSHs(sv_2mortal(newSVnv((double) *dValue)));
break;
}
}
}else{
// Report the error
ReturnError(h);
}
if (ucValue){
delete [] ucValue;
}
PUTBACK;
}
XS(XS_WIN32__ODBC_CleanError)
{
dXSARGS;
ODBC_TYPE * h;
if(items != 1){
CROAK("usage: ($Err) = ODBC_CleanError($Connection)\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
PUSHMARK(sp);
CleanError(h->Error);
XPUSHs(sv_2mortal(newSVnv((double)0)));
XPUSHs(sv_2mortal(newSVnv((double)1)));
PUTBACK;
}
XS(XS_WIN32__ODBC_ColAttributes)
{
dXSARGS;
ODBC_TYPE * h;
UWORD iCol = 0;
UWORD iType = 0;
UCHAR *szName = 0;
UCHAR szBuff[ODBC_BUFF_SIZE];
SWORD dBuffLen = 0;
SQLLEN dValue = 0;
RETCODE rResult;
STRLEN n_a;
if(items != 3){
CROAK("usage: ($Err) = ODBC_ColAttributes($Connection, $ColName, $Description)\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
szName = (unsigned char *) SvPV(ST(1), n_a);
iType = (UWORD)SvIV(ST(2));
PUSHMARK(sp);
CleanError(h->Error);
if (!h->Error->ErrNum){
if (iCol = ColNameToNum(h, (char *)szName)){
memset(szBuff, '\0', ODBC_BUFF_SIZE);
rResult = SQLColAttributes(h->hstmt, iCol, iType, szBuff, ODBC_BUFF_SIZE, (short *) &dBuffLen, (SQLLEN *) &dValue);
if (rResult == SQL_SUCCESS || rResult == SQL_SUCCESS_WITH_INFO){
if (dBuffLen){
XPUSHs(sv_2mortal(newSVnv((double)0)));
XPUSHs(sv_2mortal(newSVpv((char *)szBuff, dBuffLen)));
}else{
XPUSHs(sv_2mortal(newSVnv((double)0)));
XPUSHs(sv_2mortal(newSVnv((double)dValue)));
}
}else{
ODBCError("Unable to determine Column Attribute", h, "ODBC_ColAttributes", "1");
}
}else{
ODBCError("Field name does not exist", h, "ODBC_ColAttributes", "2");
}
}
// Process only an error state. If this has been successfull the values have
// already been processed.
if (h->Error->ErrNum){
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_Debug)
{
dXSARGS;
ODBC_TYPE * h;
int iDebug = 0;
char *szFile = 0;
STRLEN n_a;
if(items < 1 || items > 3){
CROAK("usage: $Debug = ODBC_Debug($Connection [, $Value])\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
CleanError(h->Error);
if (items > 1 && !h->Error->ErrNum){
iDebug = SvIV(ST(1));
if (items > 2){
szFile = SvPV(ST(2), n_a);
}
// What about the output file?
if (szFile){
DEBUG_DUMP("ODBC_Debug: Entering Critical Section gDCS")
EnterCriticalSection(&gDCS);
if (ghFile){
#ifdef _DEBUG
char szBuff[1000];
sprintf(szBuff, "Closing debug file \"%s\"", (gszFile)? gszFile:"none opened");
DebugConnection(szBuff, h);
#endif
CloseHandle(ghFile);
ghFile = 0;
}
// unless a file is specified (not "") then use the default file
if (strcmp(szFile, "") == 0){
if (gszFile){
delete [] gszFile;
}
gszFile = 0;
}else{
if (gszFile = new char [strlen(szFile) + 1]){
strcpy(gszFile, szFile);
}
}
LeaveCriticalSection(&gDCS);
DEBUG_DUMP("ODBC_Debug: Left Critical Section gDCS ")
}
if (iDebug){
AddDebug(h);
#ifdef _DEBUG
if(ghDebug){
char szBuff[1000];
sprintf(szBuff, "Debug mode set on by connection %i.\n", h->conn);
DebugConnection(szBuff, h);
}
#endif
}else{
#ifdef _DEBUG
if (ghDebug){
char szBuff[1000];
sprintf(szBuff, "Debug mode set off by connection %i.\n", h->conn);
DebugConnection(szBuff, h);
}
#endif
RemoveDebug(h);
}
h->iDebug = (iDebug)? 1:0;
}
PUSHMARK(sp);
if (!h->Error->ErrNum){
XPUSHs(sv_2mortal(newSVnv((double)0)));
XPUSHs(sv_2mortal(newSVnv((double)h->iDebug)));
}else{
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_SetPos)
{
dXSARGS;
ODBC_TYPE * h;
UWORD uRow = 1;
UWORD uOption = SQL_POSITION;
UWORD uLock = SQL_LOCK_NO_CHANGE;
RETCODE rResult = 0;
if(items < 2 || items > 4){
CROAK("usage: ($Err, $Type) = ODBC_SetPos($Connection, $Row [, $Option, $Lock})\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
CleanError(h->Error);
uRow = (UWORD)SvIV(ST(1));
if (items > 2){
uOption = (UWORD)SvIV(ST(2));
}
if (items > 3){
uLock = (UWORD)SvIV(ST(3));
}
PUSHMARK(sp);
if(!h->Error->ErrNum){
rResult = SQLSetPos(h->hstmt, uRow, uOption, uLock);
if (rResult != SQL_SUCCESS){
_NT_ODBC_Error(h, "ODBC_SetPos", "1");
// Set h->Error->EOR so that Fetch() knows the error is not because
// of itself. What a hack!
//h->Error->EOR = 1;
}
}
if (!h->Error->ErrNum){
XPUSHs(sv_2mortal(newSVnv((double)0)));
XPUSHs(sv_2mortal(newSVnv((double)1)));
}else{
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_GetData)
{
dXSARGS;
ODBC_TYPE * h;
UCHAR *szBuf = 0;
SDWORD iBuf = DEFAULTCOLSIZE;
int iTemp;
if(items != 1){
CROAK("usage: ($Err, $Type) = ODBC_GetData($Connection)\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
CleanError(h->Error);
PUSHMARK(sp);
/* HERE WE must figure whether to use SQLGetData() beause we are using
SQLFetch() or since we are using SQLExtendedFetch() we are to just
extract the data from h->szFetchBuffer[x].
The flag to determine this is h->iFetchType (1=fetch;2=extended fetch)
*/
if (!h->Error->ErrNum && !h->Error->EOR){
int iTempEnd;
// Allocate a default size to be used. This prevents the problem of ODBC
// returning too small of a size for a regular numberical field.
// ALSO it saves some allocation time, hopefully the average field
// will fit into this size.
// rothd@roth.net 96.05.03
/*
Needed to change SQL_COLUMN_LENGTH to SQL_COLUMN_DISPLAY_SIZE. The
former found the # of bytes needed to represent the C data type
in memory. The latter is used to find the # of bytes needed to store
an ASCII version of the data.
Way big Thanks to Mike Knister <knister@sierra.net> 96.05.13
Removed the ++bufflenout to prevent the wrap-around bug
(when bufflenout = 2147483647, adding 1 yields -2147483648. Bad, very bad.
A whopping big thanks to Jutta M. Klebe <jmk@exc.bybyte.de> 96.04.21
Changed (iBuf < bufflenout) to (iBuf <= bufflenout). How silly! if we
happen to have returning a field of size iBuf there will be no place
for the \0
96.10.07 <rothd@roth.net>
*/
XPUSHs(sv_2mortal(newSVnv((double)0)));
iTempEnd = h->Results->NumOfCols();
// Notice we start at 1 (don't include bookmarks (for now)
for(iTemp = 1; iTemp <= iTempEnd; iTemp++){
CResults *crTemp = h->Results;
DWORD dTemp = crTemp->ReturnSize(iTemp);
SV *sv = 0;
if (dTemp){
sv = sv_2mortal(newSVpv(crTemp->operator[](iTemp), dTemp));
}else{
// If the data field has 0 size (is NULL or empty) then return undef.
sv = &PL_sv_undef;
}
XPUSHs(sv);
}
}else{
// Report the error
ReturnError(h);
}
if (szBuf){
delete [] szBuf;
}
PUTBACK;
}
XS(XS_WIN32__ODBC_DropCursor)
{
dXSARGS;
ODBC_TYPE * h;
UWORD uCloseType, uCloseSpecified = 0;
RETCODE retcode = 0;
if(items < 1 || items > 2){
CROAK("usage: ($Err, $Type) = ODBC_DropCursor($Connection [, $CloseType])\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
if (items > 1){
uCloseSpecified = (UWORD)SvIV(ST(1));
}
CleanError(h->Error);
PUSHMARK(sp);
if(!h->Error->ErrNum){
uCloseType = h->uStmtCloseType;
h->uStmtCloseType = (items > 1)? uCloseSpecified:SQL_DROP;
retcode = ResetStmt(h);
h->uStmtCloseType = uCloseType;
}
if (!h->Error->ErrNum){
XPUSHs(sv_2mortal(newSVnv((double)0)));
XPUSHs(sv_2mortal(newSVnv((double)1)));
}else{
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_CursorName)
{
dXSARGS;
ODBC_TYPE * h;
char *szName;
SWORD sSize = 0;
RETCODE retcode = 0;
STRLEN n_a;
if(items < 1 || items > 2){
CROAK("usage: ($Err, $Type) = ODBC_CursorName($Connection [, $Name])\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
CleanError(h->Error);
PUSHMARK(sp);
if(!h->Error->ErrNum){
if (items > 1){
szName = SvPV(ST(1), n_a);
if (SQLSetCursorName(h->hstmt, (UCHAR *)szName, (SWORD)strlen(szName)) != SQL_SUCCESS){;
_NT_ODBC_Error(h, "ODBC_CursorName", "1");
}
}
szName = 0;
if (!h->Error->ErrNum){
if(SQL_SUCCESS == SQLGetCursorName(h->hstmt, (UCHAR *)szName, sSize, &sSize)){
if(!(szName = new char [++sSize])){
ODBCError("Could not allocate memory", h, "ODBC_CursorName", "2");
}else{
if(SQL_SUCCESS != SQLGetCursorName(h->hstmt, (UCHAR *)szName, sSize, &sSize)){
_NT_ODBC_Error(h, "ODBC_CursorName", "3");
}
}
}else{
_NT_ODBC_Error(h, "ODBC_CursorName", "4");
}
}
}else{
szName = 0;
}
if (!h->Error->ErrNum){
XPUSHs(sv_2mortal(newSVnv((double)0)));
XPUSHs(sv_2mortal(newSVpv(szName, strlen(szName))));
}else{
// Report the error
ReturnError(h);
}
if (szName){
delete [] szName;
}
PUTBACK;
}
XS(XS_WIN32__ODBC_Clone) // ODBC_Connect(Connection string: input) returns connection #
{
dXSARGS;
ODBC_TYPE *h, *hOld = 0;
RETCODE retcode; // Misc ODBC sh!t
if(items != 1){
// We need at least 1 (DSN) entry. If more then we need them in pairs of
// two, hence (items & 1) make sure we have an odd number of entries...
// (dsn) + (ConnetOption, Value) [ + (ConnectOption, Value)] ...
CROAK("usage: ($Connection, $Err, $ErrText) = ODBC_Clone($Num)\n");
}
hOld = _NT_ODBC_Verify(SvIV(ST(0)));
PUSHMARK(sp);
// Allocate new ODBC connection
if (!(h = NewODBC())){
h = ODBCError("Could not allocate memory of an ODBC connection\n", (ODBC_TYPE *)0, "ODBC_Clone", "1");
}else{
// Copy info from the Cloned ODBC connection
strcpy(h->szUserDSN, hOld->szUserDSN);
h->henv = hOld->henv;
h->hdbc = hOld->hdbc;
h->hdbc->iCount++;
if (h->szDSN = new char [strlen((const char *)hOld->szDSN) + 1]){
strcpy(h->szDSN, (char *) hOld->szDSN);
}else{
_NT_ODBC_Error(h, "ODBC_Clone", "2");
h->Error->ErrNum = 0;
DeleteConn(h->conn);
}
}
if (!h->Error->ErrNum){
retcode = ResetStmt(h);
if (retcode != SQL_SUCCESS){
DeleteConn(h->conn);
}
}
if (!h->Error->ErrNum){ // everything is happy
XPUSHs(sv_2mortal(newSVnv((double)0)));
XPUSHs(sv_2mortal(newSVnv((double)h->conn)));
// Report the szError ONLY because it may contain state info.
XPUSHs(sv_2mortal(newSVpv(h->Error->szError, strlen(h->Error->szError))));
}else{
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_GetSQLState)
{
dXSARGS;
ODBC_TYPE * h;
if(items != 1){
CROAK("usage: ($Err, $Type) = ODBC_GetSQLState($Connection)\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
PUSHMARK(sp);
if (h->Error->szSqlState){
XPUSHs(sv_2mortal(newSVnv((double)0)));
XPUSHs(sv_2mortal(newSVpv((char *)h->Error->szSqlState, strlen((const char *) h->Error->szSqlState))));
}else{
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_GetStats)
{
dXSARGS;
ODBC_TYPE * h;
int iTemp = 0;
CMom *cDaughter = 0;
char *Keys[] = { "TotalActiveThreads", // Total number of threads attached to this dll
"TotalActiveODBCThreads", // Total num of threads with ODBC connections
"TotalActiveDebugConnections", // Total number of active connections with active debugging
"TotalHistoryThreads", // Total history of threads that have connected to odbc connections
"TotalHistoryCurrentThread", // Total history of connections on current thread.
"TotalHistoryConnections", // Total history of all connections since dll initialization
"CurrentThreadID", // ID of current thread
"ConnectionErrNum", // Connections Error Number (if any)
"ConnectionErrText", // Connections Error Text (if any)
"ConnectionSQLState", // Connections SQLState (if any)
"ConnectionFunction", // Connections Function (if any)
"ConnectionLevel", // Connections Function level (if any)
"ConnectionDebug"}; // Connections Debug state (if any)
if(items > 1){
CROAK("usage: %Results = ODBC_GetStats([$Connection])\n");
}
h = _NT_ODBC_Verify( (items)? SvIV(ST(0)):0);
PUSHMARK(sp);
if (h && !(h->Error->ErrNum)){
cDaughter = (CMom *) ::cMom->operator[](GetCurrentThreadId());
XPUSHs(sv_2mortal(newSVpv((char *) Keys[iTemp], strlen((const char *) Keys[iTemp])) ));
XPUSHs( sv_2mortal(newSVnv((double) giThread) ));
iTemp++;
XPUSHs(sv_2mortal(newSVpv((char *) Keys[iTemp], strlen((const char *) Keys[iTemp])) ));
XPUSHs( sv_2mortal(newSVnv((double) ((::cMom)? ::cMom->Total():0)) ));
iTemp++;
XPUSHs(sv_2mortal(newSVpv((char *) Keys[iTemp], strlen((const char *) Keys[iTemp])) ));
XPUSHs( sv_2mortal(newSVnv((double) giDebug) ));
iTemp++;
XPUSHs(sv_2mortal(newSVpv((char *) Keys[iTemp], strlen((const char *) Keys[iTemp])) ));
XPUSHs( sv_2mortal(newSVnv((double) ((::cMom)? (::cMom->iHistory):0)) ));
iTemp++;
XPUSHs(sv_2mortal(newSVpv((char *) Keys[iTemp], strlen((const char *) Keys[iTemp])) ));
XPUSHs( sv_2mortal(newSVnv((double) ((cDaughter)? (cDaughter->iHistory):0)) ));
iTemp++;
XPUSHs(sv_2mortal(newSVpv((char *) Keys[iTemp], strlen((const char *) Keys[iTemp])) ));
XPUSHs( sv_2mortal(newSVnv((double) ((::cMom)? ::cMom->iTotalHistory:0)) ));
iTemp++;
XPUSHs(sv_2mortal(newSVpv((char *) Keys[iTemp], strlen((const char *) Keys[iTemp])) ));
XPUSHs( sv_2mortal(newSVnv((double) GetCurrentThreadId())) );
if (items){
iTemp++;
XPUSHs(sv_2mortal(newSVpv((char *) Keys[iTemp], strlen((const char *) Keys[iTemp])) ));
XPUSHs(sv_2mortal(newSVnv((double) h->Error->ErrNum)) );
iTemp++;
XPUSHs(sv_2mortal(newSVpv((char *) Keys[iTemp], strlen((const char *) Keys[iTemp])) ));
XPUSHs(sv_2mortal(newSVpv((char *) h->Error->szSqlState, strlen((const char *) h->Error->szSqlState)) ));
iTemp++;
XPUSHs(sv_2mortal(newSVpv((char *) Keys[iTemp], strlen((const char *) Keys[iTemp])) ));
XPUSHs(sv_2mortal(newSVpv((char *) h->Error->szError, strlen((const char *) h->Error->szError)) ));
iTemp++;
XPUSHs(sv_2mortal(newSVpv((char *) Keys[iTemp], strlen((const char *) Keys[iTemp])) ));
XPUSHs(sv_2mortal(newSVpv((char *) h->Error->szFunction, strlen((const char *) h->Error->szFunction)) ));
iTemp++;
XPUSHs(sv_2mortal(newSVpv((char *) Keys[iTemp], strlen((const char *) Keys[iTemp])) ));
XPUSHs(sv_2mortal(newSVpv((char *) h->Error->szFunctionLevel, strlen((const char *) h->Error->szFunctionLevel)) ));
iTemp++;
XPUSHs(sv_2mortal(newSVpv((char *) Keys[iTemp], strlen((const char *) Keys[iTemp])) ));
XPUSHs(sv_2mortal(newSVnv((double) h->iDebug) ));
}
}else{
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_GetTypeInfo)
{
dXSARGS;
ODBC_TYPE * h;
SWORD sType;
RETCODE retcode= 0;
char *szCommand = "GetTypeInfo()";
if(items != 2){
CROAK("usage: ($Err, $NumValue, $Value) = ODBC_GetTypeInfo($Connection, $Type)\n");
}
h = _NT_ODBC_Verify(SvIV(ST(0)));
CleanError(h->Error);
sType = (SWORD)SvIV(ST(1));
PUSHMARK(sp);
if(!h->Error->ErrNum){
if (ResetStmt(h) == SQL_SUCCESS){
if (h->szCommand){
delete [] h->szCommand;
h->szCommand = 0;
}
if (h->szCommand = new char [strlen(szCommand) + 1]){
strcpy(h->szCommand, szCommand);
}
retcode = SQLGetTypeInfo(h->hstmt, sType);
if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO){
_NT_ODBC_Error(h, "ODBC_GetTypeInfo", "1");
}else{
if(h->Results){
delete h->Results;
}
h->Results = new CResults(h);
}
}
}
if (!h->Error->ErrNum){ // everything is happy
UCHAR buff2[ODBC_BUFF_SIZE];
SDWORD bufflenout;
int x;
XPUSHs(sv_2mortal(newSVnv((double)0)));
retcode = SQLNumResultCols(h->hstmt, (short *)&h->numcols);
for(x=1; x<=h->numcols; x++){
SQLColAttributes(h->hstmt, x, SQL_COLUMN_NAME, buff2, ODBC_BUFF_SIZE, (short *)&bufflenout, NULL);
XPUSHs(sv_2mortal(newSVpv((char *)buff2, strlen((const char*)buff2))));
}
}else{
// Report the error
ReturnError(h);
}
PUTBACK;
}
XS(XS_WIN32__ODBC_ShutDown)
{
// dXSARGS;
TerminateThread();
}
void TerminateThread(){
CMom *cmDaughter;
DEBUG_DUMP("TerminateThread(): Entering Critical Section gDCS")
#ifdef _DEBUG
EnterCriticalSection(&gDCS);
char szBuff[1000];
sprintf(szBuff, "Thread %05i (total threads: %03i) terminating.\n", GetCurrentThreadId(), giThread);
// Entered Debug CS so no other debug messages interrupt us...
// ...since we are in DCS we *MUST* use DebugPrint() not DebugDump()
DebugPrint(szBuff);
// If this thread has a CMom then delete it!
DebugPrint("\t--> Checking for a daughter on this thread...\n");
#endif
if (::cMom){
if (cmDaughter = (CMom *) ::cMom->operator[](GetCurrentThreadId())){
#ifdef _DEBUG
DebugPrint("\t--> Thread daughter about to be deleted...\n");
#endif
delete cmDaughter;
#ifdef _DEBUG
DebugPrint("\t--> Thread daughter has been deleted.\n\n");
#endif
}
#ifdef _DEBUG
else{
DebugPrint("\t--> No daughter on this thread.\n\n");
}
#endif
}
#ifdef _DEBUG
LeaveCriticalSection(&gDCS);
#endif
DEBUG_DUMP("TerminateThread(): Left Critical Section gDCS ")
return;
}
XS(XS_WIN32__ODBC_InitExtension)
{
InitExtension();
}
int InitExtension(){
CMom *cmDaughter = 0;
ODBC_TYPE *h = 0;
int iRetCode = 1;
#if defined(__CYGWIN__) || defined(__MINGW32__)
// Otherwise, this is done in DllMain
InitializeCriticalSection(&gDCS);
InitializeCriticalSection(&gCS);
#endif
if (! ::cMom){
::cMom = new CMom;
}
if (! ::cMom){
iRetCode = FALSE;
}else{
if (cmDaughter = (CMom *) ::cMom->operator[](GetCurrentThreadId())){
h = (ODBC_TYPE*) cmDaughter->operator[]((DWORD)0);
}
if(!h || !cmDaughter){
#ifdef _DEBUG
DebugConnection(" =========> Creating Gratuitous ODBC structure...", 0);
#endif
if (!(h = NewODBC())){
iRetCode = FALSE;
}else{
strcpy(h->szUserDSN, "Gratuitous ODBC Structure");
}
#ifdef _DEBUG
DebugConnection(" =========> Finished creating gratuitous ODBC structure.", 0);
#endif
}
}
return iRetCode;
}
#if defined(__cplusplus)
extern "C"
#endif
XS(boot_Win32__ODBC)
{
dXSARGS;
char* file = __FILE__;
RETCODE iRetCode = 1;
#ifdef _DEBUG
DebugConnection("==============\n\tRunning bootstrap code.", 0);
#endif
// This will force the creation of a daughter Mom and populate it with a
// default ODBC object.
if (iRetCode = InitExtension()){
newXS("Win32::ODBC::constant", XS_WIN32__ODBC_Constant, file);
newXS("Win32::ODBC::ODBCConnect", XS_WIN32__ODBC_Connect, file);
newXS("Win32::ODBC::ODBCExecute", XS_WIN32__ODBC_Execute, file);
newXS("Win32::ODBC::ODBCFetch", XS_WIN32__ODBC_Fetch, file);
newXS("Win32::ODBC::ODBCDisconnect", XS_WIN32__ODBC_Disconnect, file);
newXS("Win32::ODBC::ODBCGetError", XS_WIN32__ODBC_GetError, file);
newXS("Win32::ODBC::ODBCTableList", XS_WIN32__ODBC_TableList, file);
newXS("Win32::ODBC::ODBCColumnList", XS_WIN32__ODBC_ColumnList, file);
newXS("Win32::ODBC::ODBCMoreResults", XS_WIN32__ODBC_MoreResults, file);
newXS("Win32::ODBC::ODBCGetConnections", XS_WIN32__ODBC_GetConnections, file);
newXS("Win32::ODBC::ODBCGetMaxBufSize", XS_WIN32__ODBC_MaxBufSize, file);
newXS("Win32::ODBC::ODBCSetMaxBufSize", XS_WIN32__ODBC_MaxBufSize, file);
newXS("Win32::ODBC::ODBCGetStmtCloseType", XS_WIN32__ODBC_GetStmtCloseType, file);
newXS("Win32::ODBC::ODBCSetStmtCloseType", XS_WIN32__ODBC_SetStmtCloseType, file);
newXS("Win32::ODBC::ODBCDataSources", XS_WIN32__ODBC_DataSources, file);
newXS("Win32::ODBC::ODBCDrivers", XS_WIN32__ODBC_Drivers, file);
newXS("Win32::ODBC::ODBCGetStmtOption", XS_WIN32__ODBC_StmtOption, file);
newXS("Win32::ODBC::ODBCSetStmtOption", XS_WIN32__ODBC_StmtOption, file);
newXS("Win32::ODBC::ODBCSetConnectOption", XS_WIN32__ODBC_SetConnectOption, file);
newXS("Win32::ODBC::ODBCGetConnectOption", XS_WIN32__ODBC_GetConnectOption, file);
newXS("Win32::ODBC::ODBCRowCount", XS_WIN32__ODBC_RowCount, file);
newXS("Win32::ODBC::ODBCCleanError", XS_WIN32__ODBC_CleanError, file);
newXS("Win32::ODBC::Info", XS_WIN32__ODBC_Info, file);
newXS("Win32::ODBC::ODBCColAttributes", XS_WIN32__ODBC_ColAttributes, file);
newXS("Win32::ODBC::ODBCConfigDSN", XS_WIN32__ODBC_ConfigDSN, file);
newXS("Win32::ODBC::ODBCGetFunctions", XS_WIN32__ODBC_GetFunctions, file);
newXS("Win32::ODBC::ODBCTransact", XS_WIN32__ODBC_Transact, file);
newXS("Win32::ODBC::ODBCGetDSN", XS_WIN32__ODBC_GetDSN, file);
newXS("Win32::ODBC::ODBCGetInfo", XS_WIN32__ODBC_GetInfo, file);
newXS("Win32::ODBC::ODBCDebug", XS_WIN32__ODBC_Debug, file);
newXS("Win32::ODBC::ODBCSetPos", XS_WIN32__ODBC_SetPos, file);
newXS("Win32::ODBC::ODBCGetData", XS_WIN32__ODBC_GetData, file);
newXS("Win32::ODBC::ODBCDropCursor", XS_WIN32__ODBC_DropCursor, file);
newXS("Win32::ODBC::ODBCSetCursorName", XS_WIN32__ODBC_CursorName, file);
newXS("Win32::ODBC::ODBCGetCursorName", XS_WIN32__ODBC_CursorName, file);
newXS("Win32::ODBC::ODBCClone", XS_WIN32__ODBC_Clone, file);
newXS("Win32::ODBC::ODBCGetSQLState", XS_WIN32__ODBC_GetSQLState, file);
newXS("Win32::ODBC::ODBCGetStats", XS_WIN32__ODBC_GetStats, file);
newXS("Win32::ODBC::ODBCGetTypeInfo", XS_WIN32__ODBC_GetTypeInfo, file);
newXS("Win32::ODBC::ODBCShutDown", XS_WIN32__ODBC_ShutDown, file);
newXS("Win32::ODBC::ODBCInit", XS_WIN32__ODBC_InitExtension, file);
}
#ifdef _DEBUG
DebugConnection("Finished bootstrap code.\n\t==============", 0);
#endif
// ST(0) = &sv_yes;
XSRETURN(iRetCode);
}
#ifdef WIN32
/* =============== DLL Specific Functions =================== */
BOOL WINAPI
# ifdef __BORLANDC__
DllEntryPoint
# else
DllMain
# endif
(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved){
BOOL bResult = TRUE;
switch(fdwReason){
case DLL_PROCESS_ATTACH:
{
ghDLL = hinstDLL;
giDebug = 0;
ghDebug = 0;
giThread++;
// The Debug CS
InitializeCriticalSection(&gDCS);
// The Global CS
InitializeCriticalSection(&gCS);
#ifdef _DEBUG
DebugDump("DLL attaching to process.\n");
#endif
break;
}
case DLL_THREAD_ATTACH:
{
/*
We will create a new CMom for this thread in the bootstrap
function. This way we are not creating CMom & primary
ODBC objects for every thread without reason
*/
giThread++;
// Since we moved the code to create the threads ODBC object there should
// no longer be a need to enter the critical section gCS so let's remark
// it out for now.
// EnterCriticalSection(&gCS);
#ifdef _DEBUG
char szBuff[100];
DEBUG_DUMP("DLL_THREAD_ATTACH: Entering Critical Section gCS")
sprintf(szBuff, "Thread %05i (total threads: %03i) starting.\n", GetCurrentThreadId(), giThread);
DebugPrint(szBuff);
DEBUG_DUMP("DLL_THREAD_ATTACH: Left Critical Section gCS ")
#endif
// LeaveCriticalSection(&gCS);
break;
}
case DLL_THREAD_DETACH:
{
#ifdef _DEBUG
char szBuff[100];
// Is there a need for this section go enter the critical section gCS?
DEBUG_DUMP("DLL_THREAD_DETACH: Entering Critical Section gCS")
EnterCriticalSection(&gCS);
sprintf(szBuff, "Thread %05i (total threads: %03i) stopping.\n", GetCurrentThreadId(), giThread);
DebugPrint(szBuff);
LeaveCriticalSection(&gCS);
DEBUG_DUMP("DLL_THREAD_DETACH: Left Critical Section gCS ")
#endif
// TerminateThread();
giThread--;
break;
}
case DLL_PROCESS_DETACH:
{
#ifdef _DEBUG
DebugDump("Unloading DLL.\n");
#endif
if (::cMom){
delete ::cMom;
}
if (ghDebug){
ghDebug = 0;
FreeConsole();
}
DeleteCriticalSection(&gCS);
DeleteCriticalSection(&gDCS);
ghDLL = 0;
if (ghFile){
CloseHandle(ghFile);
}
break;
}
default:
break;
}
return bResult;
}
#endif
/*
To do list:
-Convert this beast to OOP.
done kinda -Create classes of reusable hEnv, hDbc, hStmt so we do not
waste memory and connections.
done -Configure/alter DSNs.
done -Return an array of values that describe the total connection
string for a DSN. Similar to DataSources($DSN) but return the
connection string not just the driver string.
done -return values that can have embedded nulls (binary data).
*/