File:  [parser3project] / sql / odbc / parser3odbc.C
Revision 1.1: download - view: text, annotated - select for diffs - revision graph
Fri Sep 21 15:42:21 2001 UTC (24 years, 8 months ago) by parser
Branches: MAIN
CVS tags: HEAD
Initial revision

/** @file
	Parser ODBC driver.

	Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com)

	Author: Alexander Petrosyan <paf@design.ru>(http://design.ru/paf)
*/
static const char *RCSId="$Id: parser3odbc.C,v 1.1 2001/09/21 15:42:21 parser Exp $"; 

#ifndef _MSC_VER
#	error compile ISAPI module with MSVC [no urge for now to make it autoconf-ed (PAF)]
#endif

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include "pa_sql_driver.h"

#include <AFXDB.H>

#define MAX_STRING 0x400

#define snprintf _snprintf
#ifndef strncasecmp
#	define strncasecmp _strnicmp
#endif

static char *lsplit(char *string, char delim) {
    if(string) {
		char *v=strchr(string, delim);
		if(v) {
			*v=0;
			return v+1;
		}
    }
    return 0;
}

/**
	ODBC server driver
*/
class ODBC_Driver : public SQL_Driver {
public:

	ODBC_Driver() : SQL_Driver() {
	}

	/// get api version
	int api_version() { return SQL_DRIVER_API_VERSION; }
	const char *initialize(const char *dlopen_file_spec) { return 0; }
	/**	connect
		@param used_only_in_connect_url
			format: @b DSN=dsn;UID=user;PWD=password (ODBC connect string)
			WARNING: must be used only to connect, for buffer doesn't live long
	*/
	void connect(
		char *used_only_in_connect_url, 
		SQL_Driver_services& services, 
		void **connection ///< output: CDatabase *
		) {
	//	_asm int 3;
		CDatabase *db;
		TRY {
			db=new CDatabase();
			db->OpenEx(used_only_in_connect_url, CDatabase::noOdbcDialog);
			db->BeginTrans();
		} 
		CATCH_ALL (e) {
			_throw(services, e);
			db=0; // calm, compiler
		}
		END_CATCH_ALL

		*(CDatabase **)connection=db;
	}
	void disconnect(void *connection) {
		CDatabase *db=static_cast<CDatabase *>(connection);
		TRY
			delete db;
		CATCH_ALL (e) {
			// nothing
		}
		END_CATCH_ALL
	}
	void commit(SQL_Driver_services& services, void *connection) {
		CDatabase *db=static_cast<CDatabase *>(connection);
		TRY
			db->CommitTrans();
			db->BeginTrans();
		CATCH_ALL (e) {
			_throw(services, e);
		}
		END_CATCH_ALL
	}
	void rollback(SQL_Driver_services& services, void *connection) {
		CDatabase *db=static_cast<CDatabase *>(connection);
		TRY
			db->Rollback();
			db->BeginTrans();
		CATCH_ALL (e) {
			_throw(services, e);
		}
		END_CATCH_ALL
	}

	bool ping(SQL_Driver_services&, void *connection) {
		return true;
	}

	unsigned int quote(
		SQL_Driver_services&, void *connection,
		char *to, const char *from, unsigned int length) {
		/*
			You must allocate the to buffer to be at least length*2+1 bytes long. 
			(In the worse case, each character may need to be encoded as using two bytes, 
			and you need room for the terminating null byte.)

			it's already UNTAINT_TIMES_BIGGER
		*/
		// ' -> ''
		unsigned int result=length;
		while(length--) {
			if(*from=='\'')
				*to++='\'';
			*to++=*from++;
		}
		return result;
	}
	void query(
		SQL_Driver_services& services, void *connection, 
		const char *statement, unsigned long offset, unsigned long limit,
		SQL_Driver_query_event_handlers& handlers) {

		CDatabase *db=static_cast<CDatabase *>(connection);

		while(isspace(*statement)) 
			statement++;
		
		TRY {
			if(strncasecmp(statement, "select", 6)==0) {
				CRecordset rs(db); 
				rs.Open(
					CRecordset::forwardOnly, 
					statement,
					CRecordset::executeDirect   
					);

				int column_count=rs.GetODBCFieldCount();
				if(!column_count)
					services._throw("result contains no columns");

				for(int i=0; i<column_count; i++){
					CString string;
					CODBCFieldInfo fieldinfo;
					rs.GetODBCFieldInfo(i, fieldinfo);
					size_t size=fieldinfo.m_strName.GetLength();
					void *ptr=0;
					if(size) {
						ptr=services.malloc(size);
						memcpy(ptr, (char *)LPCTSTR(fieldinfo.m_strName), size);
					}
					handlers.add_column(ptr, size);
				}

				handlers.before_rows();

				unsigned long row=0;
				while(!rs.IsEOF() && (!limit||(row<offset+limit))) {
					if(row>=offset) {
						handlers.add_row();
						for(int i=0; i<column_count; i++) 						{
							CString string;
							rs.GetFieldValue(i, string);
							size_t size=string.GetLength();
							void *ptr=0;
							if(size) {
								ptr=services.malloc(size);
								memcpy(ptr, (char *)LPCTSTR(string), size);
							}
							handlers.add_row_cell(ptr, size);
						}
					}
					rs.MoveNext();  row++;
				}
				
				rs.Close();
			} else {
				db->ExecuteSQL(statement);
			}
		} 
		CATCH_ALL (e) {
			_throw(services, e);
		}
		END_CATCH_ALL
	}

	void _throw(SQL_Driver_services& services, CException *e) {
		char szCause[MAX_STRING]; szCause[0]=0;
		e->GetErrorMessage(szCause, MAX_STRING);
		char msg[MAX_STRING];
		snprintf(msg, MAX_STRING, "%s: %s",
			e->GetRuntimeClass()->m_lpszClassName,
			*szCause?szCause:"unknown");
		services._throw(msg);
	}

};

extern "C" SQL_Driver *SQL_DRIVER_CREATE() {
	return new ODBC_Driver();
}

E-mail: