
 //
 // ocipl.h
 //
 // Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Martin Fuchs <martin-fuchs@gmx.net>
 //
 // OCIPL Version 1.7
 //

 /// \file ocipl.h
 /// OCIPL header file


/*

  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  * Redistributions of source code must retain the above copyright
	notice, this list of conditions and the following disclaimer.
  * Redistributions in binary form must reproduce the above copyright
	notice, this list of conditions and the following disclaimer in
	the documentation and/or other materials provided with the
	distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef _OCIPL_H


#ifdef UNICODE
#ifndef _UNICODE
#define _UNICODE
#endif
#else
#ifdef _UNICODE
#define UNICODE
#endif
#endif

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#define	TCHAR char
#endif

#ifdef _MSC_VER
#pragma warning(disable: 4786)
#else
#define LONGLONG long long
#endif


 // secure CRT functions
#ifdef __STDC_WANT_SECURE_LIB__

#ifndef _stprintf_s1
#define _stprintf_s1 sprintf_s
#endif

#else

#ifndef strcpy_s
#define strcpy_s(d, l, s) strcpy(d, s)
#endif

#ifndef _strupr_s
#define _strupr_s(b, s) strupr(b)
#endif

#ifndef _tcscpy_s
#define _tcscpy_s(d, l, s) _tcscpy(d, s)
#endif

#ifndef _tcsupr_s
#define _tcsupr_s(b, s) _tcsupr(b)
#endif

#ifndef _stprintf_s1
#define _stprintf_s1(b, l, f, p1) ((b)+(l),_stprintf(b, f, p1))
#endif

#endif // __STDC_WANT_SECURE_LIB__


#if _MSC_VER>=1400
#ifndef _CRT_SECURE_NO_DEPRECATE
#define __STDC_WANT_SECURE_LIB__	1
#endif

#ifndef COUNTOF
#define COUNTOF _countof
#endif

#else // _MSC_VER>=1400

#ifndef COUNTOF
#define COUNTOF(b) (sizeof(b)/sizeof(b[0]))
#define _countof COUNTOF
#endif

#endif


#include <map>
#include <set>
#include <string>
#include <vector>
#include <ostream>
#include <sstream>
#include <exception>

#ifndef min
#define min(a,b) ((a)<(b)?(a):(b))
#endif


#undef SQL_SUCCESS
#undef SQL_ERROR

#define iterator __iterator_
#if !__STDC__
#define __STDC__ 1	// for Oracle 8.0 header files
#endif
#include <oci.h>
#undef iterator

 // determine OCI version from available constants
#ifndef ORACLE_VERSION
#ifdef OCI_MAJOR_VERSION	// OCI_MAJOR_VERSION/OCI_MINOR_VERSION is only available since Oracle 10.
#define	ORACLE_VERSION	(10*OCI_MAJOR_VERSION+OCI_MINOR_VERSION)
#else
#if OCI_HTYPE_LAST>=29
#define	ORACLE_VERSION	102	// Oracle 10g2
#elif OCI_HTYPE_LAST>=27
#define	ORACLE_VERSION	92	// Oracle 9i2
#elif OCI_HTYPE_LAST>=18
#define	ORACLE_VERSION	82	// Oracle 8.1.6/7
#elif OCI_HTYPE_LAST>=17
#define	ORACLE_VERSION	81	// Oracle 8.1.5
#else
#define	ORACLE_VERSION	80	// Oracle 8.0.x
#endif
#endif // OCI_MAJOR_VERSION
#endif


 // Unicode support
#ifdef _WIN32

#include <tchar.h>

#else // _WIN32

#include <wchar.h>
#ifndef UNICODE
#define TEXT(x) x
#endif

#endif // _WIN32

#ifdef UNICODE
typedef utext TOraText;
typedef std::wstring tstring;
typedef std::wstringbuf tstringbuf;
typedef std::wostringstream tostringstream;
typedef std::wistream tistream;
typedef std::wostream tostream;
#else
typedef OraText TOraText;
typedef std::string tstring;
typedef std::stringbuf tstringbuf;
typedef std::ostringstream tostringstream;
typedef std::istream tistream;
typedef std::ostream tostream;
#endif


#ifndef _MSC_VER

#ifndef UNICODE
#define _stprintf sprintf
#define _tcscmp strcmp
#define _tcscpy strcpy
#define _tcsdup _strdup
#define _tcslen strlen
#define _tcsnicmp strncasecmp
#define _tcsupr strupr
#define _tcspbrk strpbrk
#endif

#ifndef _strdup
#define _strdup strdup
#endif

#ifndef _snprintf
#define _snprintf snprintf
#endif

extern char* strupr(char*);

#endif // _MSC_VER


#ifdef _MSC_VER
#include <minmax.h>

#ifndef	OCIPL_NO_COMMENT
#pragma comment(lib, "oci")

#if defined(_DEBUG) && defined(_DLL)	// DEBUG version only supported with MSVCRTD
#if _MSC_VER==1400
#pragma comment(lib, "ocipl-vc8d.lib")
#else
#pragma comment(lib, "ocipl-vc6d.lib")
#endif
#else
#ifdef _DLL
#if _MSC_VER==1400
#pragma comment(lib, "ocipl-vc8")
#else
#pragma comment(lib, "ocipl-vc6")
#endif
#elif defined(_MT)
#if _MSC_VER==1400
#pragma comment(lib, "ocipl-vc8t")
#else
#pragma comment(lib, "ocipl-vc6t")
#endif
#else
 // -ML is no more supported by VS2005.
#pragma comment(lib, "ocipl-vc6l")
#endif
#endif

#endif // OCIPL_NO_COMMENT
#endif // _MSC_VER


//#include <assert.h>

#if 1 // use stdlib allocation
#define ocipl_alloc malloc
#define ocipl_realloc realloc
#define ocipl_free free
#else
extern void* ocipl_alloc(size_t);
extern void* ocipl_realloc(void* p, size_t l);
extern void ocipl_free(void*);
#endif

extern size_t max_strlen(const char* s, size_t max_len);
extern char* max_strdup(const char* s, size_t max_len);


namespace OCIPL {


 // default bulk fetch size
extern int g_OCIPL_BULK_ROWS;

 // default string buffer size
#define OCIPL_DEFAULT_STRING_BUFFER	2000


#ifndef istspace
inline int istspace(TCHAR c)
{
#ifdef UNICODE
	return _istspace((unsigned short)c);
#else
	return isspace((unsigned char)c);
#endif
}
#endif


struct SqlStatement;

 /// OCI Error handling
struct OciException : public std::exception
{
	typedef std::exception super;

	OciException(OCIEnv* envh, const char* file, int line);
	OciException(OCIError* errh, const char* file, int line);
	OciException(const char* msg, const char* file, int line);
	OciException(OCIError* errh, SqlStatement& stmt, const char* file, int line);
	OciException(const OciException&);

	~OciException() throw();

	 // return exception message text
	virtual const char* what() const throw()
	{
		return _msg;
	}

	int	_sql_error_code;
	char*		_msg;
	const char*	_file;
	int			_line;
	tstring		_last_sql;
	ub2	_parse_offset;
};

 // some common Oracle error codes
#define	SQLCODE_DUPLICATE_KEY	1
#define	SQLCODE_NO_DATA_FOUND	1403
#define	SQLCODE_TOO_MANY_ROWS	1422


enum OCI_STATE {
	OS_NONE,
	OS_PREPARE,
	OS_RELEASE,
	OS_EXECUTE,
	OS_FETCH,
	OS_TRANSACTION
};


 /// error handling functions

struct OciEnv;

extern void throw_oci_exception(OCIError* errh, sword res);
extern void throw_oci_exception(OCIEnv* envh, sword res);

void oci_check_error(OCIError* errh, sword res);
void oci_check_error(OCIEnv* envh, sword res);
void oci_check_error(OciEnv& env, sword res);

#define	CODEPOS	__FILE__, __LINE__

 // throw the given exception
#ifdef _MSC_VER
__declspec(noreturn)
#endif
void throw_ocipl_exception(OciException e);

 // macro to call throw_ocipl_exception()
#define	THROW_OCI_EXCEPTION(par) throw_ocipl_exception(OCIPL::OciException(par, CODEPOS))


 /// mapping between OCI handle types and the corresponding type and attribute constants

template<typename TYPE>
  struct OciHandleID
{
	static ub4 get_type_id();
	static ub4 get_attr_id();
};

inline ub4 OciHandleID<OCIEnv>::get_type_id()			{return OCI_HTYPE_ENV;}
inline ub4 OciHandleID<OCIError>::get_type_id()			{return OCI_HTYPE_ERROR;}
inline ub4 OciHandleID<OCISvcCtx>::get_type_id()		{return OCI_HTYPE_SVCCTX;}
inline ub4 OciHandleID<OCIStmt>::get_type_id()			{return OCI_HTYPE_STMT;}
inline ub4 OciHandleID<OCIBind>::get_type_id()			{return OCI_HTYPE_BIND;}
inline ub4 OciHandleID<OCIDefine>::get_type_id()		{return OCI_HTYPE_DEFINE;}
inline ub4 OciHandleID<OCIDescribe>::get_type_id()		{return OCI_HTYPE_DESCRIBE;}
inline ub4 OciHandleID<OCIServer>::get_type_id()		{return OCI_HTYPE_SERVER;}
inline ub4 OciHandleID<OCISession>::get_type_id()		{return OCI_HTYPE_SESSION;}
inline ub4 OciHandleID<OCITrans>::get_type_id()			{return OCI_HTYPE_TRANS;}
inline ub4 OciHandleID<OCIComplexObject>::get_type_id()	{return OCI_HTYPE_COMPLEXOBJECT;}
inline ub4 OciHandleID<OCISecurity>::get_type_id()		{return OCI_HTYPE_SECURITY;}
#if ORACLE_VERSION>=81
inline ub4 OciHandleID<OCISubscription>::get_type_id()	{return OCI_HTYPE_SUBSCRIPTION;}
#endif
/*
inline ub4 OciHandleID<>::get_type_id() {return OCI_HTYPE_DIRPATH_CTX;}
inline ub4 OciHandleID<>::get_type_id() {return OCI_HTYPE_DIRPATH_COLUMN_ARRAY;}
inline ub4 OciHandleID<>::get_type_id() {return OCI_HTYPE_DIRPATH_STREAM;}
inline ub4 OciHandleID<>::get_type_id() {return OCI_HTYPE_PROC;}
*/

inline ub4 OciHandleID<OCIServer>::get_attr_id()		{return OCI_ATTR_SERVER;}
inline ub4 OciHandleID<OCISession>::get_attr_id()		{return OCI_ATTR_SESSION;}


 /// "simple" OCI handles without OciEnv

template<typename TYPE>
  struct OciSimpleHandle : public OciHandleID<TYPE>
{
	typedef OciHandleID<TYPE> super;
	typedef TYPE HandleType;

	 // default constructor without handle allocation
	OciSimpleHandle()
	 : _handle(0), _envh(0)
	{
	}

	 // contructor consuming the given handle
	OciSimpleHandle(OCIEnv* envh, HandleType* handle)
	 : _envh(envh), _handle(handle)
	{
	}

	 // constructor allocating a new handle of the type TYPE
	OciSimpleHandle(OCIEnv* envh)
	 : _handle(0)
	{
		alloc(envh);
	}

	 // The destructor destroys the attached OCI handle.
	~OciSimpleHandle()
	{
		if (_handle)
			destroy();
	}

	 // return the managed OCI handle
	operator HandleType*()
	{
		return _handle;
	}

	 // allocate a new handle of the type TYPE
	void alloc(OCIEnv* envh)
	{
		 // If we already manage a handle, free it.
		if (_handle)
			destroy();

		_envh = envh;

		sword res = OCIHandleAlloc(envh, (dvoid**)&_handle, super::get_type_id(), 0, 0);
		oci_check_error(envh, res);
	}

	 // free the attached handle
	void destroy()
	{
 		sword res = OCIHandleFree(_handle, super::get_type_id());
		_handle = 0;

		if (_envh) {
			oci_check_error(_envh, res);
			_envh = 0;
		}
	}

	 // return the given attrtype of the attached OCI handle into *attributep
	void get_attribute(dvoid* attributep, ub4* sizep, ub4 attrtype, OCIError* errh)
	{
		sword res = OCIAttrGet(_handle, super::get_type_id(), attributep, sizep, attrtype, errh);
		oci_check_error(_envh, res);
	}

protected:
	HandleType*	_handle;// wrapped OCI handle
	OCIEnv*		_envh;	// OCI environment
};


 /// wrapping of already existing OCI handles without freeing them in destructor

template<typename TYPE>
  struct OciHandleWrapper : public OciHandleID<TYPE>
{
	typedef TYPE HandleType;

	 // create a wrapper object from the given handle
	OciHandleWrapper(HandleType* handle)
	 :	_handle(handle)
	{
	}

	 // return the managed OCI handle
	operator HandleType*()
	{
		return _handle;
	}

protected:
	HandleType*	_handle;
};


 /// OCI Error Handles
typedef OciSimpleHandle<OCIError> OciError;


 /// OCI Describe Handles
typedef OciSimpleHandle<OCIDescribe> OciDescribe;


 /// OCI environment handles

struct OciEnv : public OciHandleWrapper<OCIEnv>
{
	typedef OciHandleWrapper<OCIEnv> super;

	OciEnv(OCIEnv* envh)
	 :	super(envh)
	{
		 // allocate error handle
		_errh.alloc(_handle);
	}

	OciError	_errh;	// error handle
};


 /// wrapper for the OCIEnv handle

struct OciEnvAlloc : public OciSimpleHandle<OCIEnv>
{
	typedef OciSimpleHandle<OCIEnv> super;

	 // constructor allocating a new environemnt handle
	OciEnvAlloc(ub4 oci_mode=OCI_OBJECT)	// OCI_OBJECT for OCINumber... functions
	{
#if ORACLE_VERSION>=81
#ifdef UNICODE
		sword res = OCIEnvNlsCreate(&_handle, oci_mode, 0/*ctxp*/, 0, 0, 0, 0/*xtramem_sz*/, 0/*usrmempp*/, OCI_UTF16ID, OCI_UTF16ID);
#else
		sword res = OCIEnvCreate(&_handle, oci_mode, 0/*ctxp*/, 0, 0, 0, 0/*xtramem_sz*/, 0/*usrmempp*/);
#endif
		//oci_check_error(res);
#else
		sword res = OCIInitialize(oci_mode, 0/*ctxp*/, 0, 0, 0);
		//oci_check_error(res);

		OCIEnvInit(&_handle, OCI_DEFAULT, (size_t)0, (dvoid **) 0);
#endif
	}
};


 /// OCI handles

template<typename TYPE>
  struct OciHandle : public OciHandleID<TYPE>
{
	typedef OciHandleID<TYPE> super;
	typedef TYPE HandleType;

	 // constructor without handle allocation
	OciHandle(OciEnv& env)
	 :	_env(env),
		_handle(0)
	{
		alloc();
	}

	 // contructor consuming the given handle
	OciHandle(OciEnv& env, HandleType* handle)
	 :	_env(env),
		_handle(handle)
	{
	}

	 // copy constructor to transfer handle ownership
	OciHandle(const OciHandle& other)
	 :	_env(other._env),
		_handle(other._handle)
	{
		other._handle = NULL;
	}

	 // The destructor destroys the attached OCI handle.
	~OciHandle()
	{
		if (_handle)
			destroy();
	}

	 // allocate a new handle of the type TYPE
	void alloc()
	{
		if (_handle)
			destroy();

		sword res = OCIHandleAlloc(_env, (dvoid**)&_handle, super::get_type_id(), 0, 0);
		oci_check_error(_env, res);
	}

	 // free the attached handle
	void destroy()
	{
 		sword res = OCIHandleFree(_handle, super::get_type_id());
		_handle = 0;

		oci_check_error(_env, res);
	}

	 // set an handle attribute to the given string
	void set_attribute(ub4 attrtype, const tstring& str)
	{
		sword res = OCIAttrSet(_handle, super::get_type_id(), (TOraText*)str.c_str(), (ub4)str.length()*sizeof(TOraText), attrtype, _env._errh);
		oci_check_error(_env, res);
	}

	 // set an handle attribute to the given numeric value
	void set_attribute(ub4 attrtype, ub4 value)
	{
		sword res = OCIAttrSet(_handle, super::get_type_id(), (OraText*)&value, (ub4)sizeof(value), attrtype, _env._errh);
		oci_check_error(_env, res);
	}

	 // return the given attrtype of the attached OCI handle into *attributep
	void get_attribute(dvoid* attributep, ub4* sizep, ub4 attrtype)
	{
		sword res = OCIAttrGet(_handle, super::get_type_id(), attributep, sizep, attrtype, _env._errh);
		oci_check_error(_env, res);
	}

	 // set an handle attribute to the given handle value
	void set_attribute_handle(ub4 attrtype, void* handle)
	{
		sword res = OCIAttrSet(_handle, super::get_type_id(), handle, 0, attrtype, _env._errh);
		oci_check_error(_env, res);
	}

	 // set an handle attribute to the given handle value
	template<typename ATTR> void set_attribute(ATTR& attr)
	{
		sword res = OCIAttrSet(_handle, super::get_type_id(), attr, 0, ATTR::get_attr_id(), _env._errh);
		oci_check_error(_env, res);
	}

	 // return the managed OCI handle
	operator HandleType*()
	{
		return _handle;
	}

protected:
	HandleType*	_handle;

public:
	OciEnv&		_env;	// OCI environment
};


 /// OCI descriptor handles

template<typename TYPE>
  struct OciDescriptor : public OciHandleID<TYPE>
{
	typedef OciHandleID<TYPE> super;
	typedef TYPE HandleType;

	 // constructor without handle allocation
	OciDescriptor(OciEnv& env)
	 : _env(env)
	{
	}

	 // contructor consuming the given descriptor handle
	OciDescriptor(OciEnv& env, HandleType* handle)
	 : _env(env), _handle(handle)
	{
	}

	 // The destructor destroys the attached OCI descriptor handle.
	~OciDescriptor()
	{
		if (_handle)
			destroy();
	}

	 // return the managed OCI descriptor handle
	operator HandleType*()
	{
		return _handle;
	}

	 // free the attached descriptor handle
	void destroy()
	{
 		sword res = OCIDescriptorFree(_handle, super::get_type_id());
		_handle = 0;

		oci_check_error(_env, res);
	}

	 // return the given attrtype of the attached OCI handle into *attributep
	void get_attribute(dvoid* attributep, ub4* sizep, ub4 attrtype)
	{
		sword res = OCIAttrGet(_handle, super::get_type_id(), attributep, sizep, attrtype, _env._errh);
		oci_check_error(_env, res);
	}

protected:
	HandleType*	_handle;// wrapped OCI handle
	OciEnv&		_env;	// OCI environment
};


inline ub4 OciHandleID<OCIParam>::get_type_id()			{return OCI_DTYPE_PARAM;}

typedef OciDescriptor<OCIParam> OciParam;


 // check error result and report using an exception
inline void oci_check_error(OCIError* errh, sword res)
{
	if (res != OCI_SUCCESS)
		throw_oci_exception(errh, res);	// doesn't throw an exception for res==OCI_SUCCESS_WITH_INFO
}

 // check error result and report using an exception
inline void oci_check_error(OCIEnv* envh, sword res)
{
	if (res != OCI_SUCCESS)
		throw_oci_exception(envh, res);	// doesn't throw an exception for res==OCI_SUCCESS_WITH_INFO
}

 // check error result and report using an exception
inline void oci_check_error(OciEnv& env, sword res)
{
	oci_check_error(env._errh, res);	// doesn't throw an exception for res==OCI_SUCCESS_WITH_INFO
}


#if ORACLE_VERSION>=81

 /// reading of Oracle message files

struct OciMessageFile	// needs initialization in OCI_OBJECT mode
{
	 // initialize OciMessageFile with product and facility
	OciMessageFile(OciEnv& env, const TCHAR* product, const TCHAR* facility)
	 : _env(env), _msgh(0)
	{
		open(product, facility);
	}

	 // free the attached message file handle
	~OciMessageFile()
	{
		if (_msgh)
			close();
	}

	 // return the managed message file handle
	operator OCIMsg*()
	{
		return _msgh;
	}

	 // open message file for the given product and facility
	void open(const TCHAR* product, const TCHAR* facility, OCIDuration dur=OCI_DURATION_PROCESS)
	{
		sword res = OCIMessageOpen(_env, _env._errh, &_msgh, (OraText*)(TOraText*)product, (OraText*)(TOraText*)facility, dur);
		oci_check_error(_env._errh, res);
	}

	 // free the attached message file handle
	void OciMessageFile::close()
	{
		sword res = OCIMessageClose(_env, _env._errh, _msgh);
		oci_check_error(_env._errh, res);

		_msgh = 0;
	}

	 // return message text for the goven message number
	TOraText* get_msg(ub4 msgno) const
	{
		return (TOraText*)OCIMessageGet(_msgh, msgno, 0, 0);
	}

protected:
	OCIMsg*		_msgh;	// message file handle
	OciEnv&		_env;	// OCI environment
};

#endif


 /// encapsulation of the OCIServer handle, used in OCIPL::OciLogin

struct OracleServer : public OciHandle<OCIServer>
{
	typedef OciHandle<OCIServer> super;

	 // create empty OracleServer object
	OracleServer(OciEnv& env)
	 :	super(env)
	{
#if ORACLE_VERSION>=82
		_version = 0;
#endif
		_version_string[0] = '\0';
	}

	 // The destructor destroys the owned server handle.
	~OracleServer()
	{
		if (_handle)
			detach();
	}

	 // connect to the Oracle server using the given TNSNAME (only for backward compatibility)
	void attach(const tstring& tnsname)
	{
		sword res = OCIServerAttach(_handle, _env._errh, (OraText*)tnsname.c_str(), (sb4)tnsname.length()*sizeof(TOraText), OCI_DEFAULT);
		oci_check_error(_env, res);

#if ORACLE_VERSION>=82
		res = OCIServerRelease(_handle, _env._errh, (OraText*)_version_string, sizeof(_version_string), OCI_HTYPE_SERVER, &_version);
#else
		res = OCIServerVersion(_handle, _env._errh, (OraText*)_version_string, sizeof(_version_string), OCI_HTYPE_SERVER);
#endif
		oci_check_error(_env, res);
	}

	 // release the server connection
	void detach()
	{
		sword res = OCIServerDetach(_handle, _env._errh, OCI_DEFAULT);
		oci_check_error(_env, res);

#if ORACLE_VERSION>=82
		_version = 0;
#endif
		_version_string[0] = '\0';
	}

#if ORACLE_VERSION>=82
	ub4		_version;	// numeric version identifier, e.g. 0x9200500
#endif
	TCHAR	_version_string[2000];	// version string
};


 /// database login parameter structure

struct LoginPara
{
	tstring	_tnsname;
	tstring	_username;
	tstring	_password;
	ub4		_mode;		// mode parameter used in OCISessionBegin()

	 // empty default constructor
	LoginPara()
	 :	_mode(OCI_DEFAULT)
	{
	}

	 // construct login structure from username, password and TNSNAME
	LoginPara(const TCHAR* username, const TCHAR* password, const TCHAR* tnsname)
	 :	_username(username),
		_password(password),
		_tnsname(tnsname),
		_mode(OCI_DEFAULT)
	{
	}

	 // initialize login structure from an connection string of the form USER/PASSWORD@TNSNAME
	void	parse(const TCHAR* conn_str);
};


struct OciContext : OciHandleWrapper<OCISvcCtx>
{
	typedef OciHandleWrapper<OCISvcCtx> super;

	OciContext(OciEnv& env, OCISvcCtx* ctx)
	 :	super(ctx),
		_state(OS_NONE),
		_stmt_type(0),
		_trans_cnt(0),
		_env(env)
	{
	}

	 // Commit the current transaction.
	void commit(ub4 flags=OCI_DEFAULT);

	 // Rollback the current transaction.
	void rollback(ub4 flags=OCI_DEFAULT);

	void set_state(OCI_STATE state)
	{
		//assert(_state==OS_NONE);	// problematisch bei gleichzeitigem Zugriff auf die Datenbankverbindung aus mehreren Threads
		_state = state;
	}

	void set_state(OCI_STATE state, int stmt_type)
	{
		//assert(_state==OS_NONE);	// problematisch bei gleichzeitigem Zugriff auf die Datenbankverbindung aus mehreren Threads
		_state = state;
		_stmt_type = stmt_type;

		if (state == OS_EXECUTE)
			if ((stmt_type>=OCI_STMT_UPDATE && stmt_type<=OCI_STMT_INSERT) ||
				stmt_type==OCI_STMT_BEGIN || stmt_type==OCI_STMT_DECLARE)
				++_trans_cnt;
	}

	void reset_state()
	{
		_state = OS_NONE;
		_stmt_type = 0;
	}

	bool maybe_active_transaction() const
	{
		return _trans_cnt > 0;
	}

	OCI_STATE _state;	// current OCI operation
	int	_stmt_type;		// current OCI statement type
	int	_trans_cnt;		// counter for (possibly) transactional operations

private:
	OciEnv&	_env;	// OCI environment
};


 /// simple OCI login to database

struct OciLogin : public OciHandle<OCISvcCtx>
{
	typedef OciHandle<OCISvcCtx> super;

	 // constructor without login
	OciLogin(OciEnv& env)
	 :	super(env),
		_server(env),
		_ctx(env, _handle),
		_session(env),
		_connected(false)
	{
	}

	 // constructor to login using the given LoginPara struct
	OciLogin(OciEnv& env, const LoginPara& login_para)
	 :	super(env),
		_server(env),
		_ctx(env, _handle),
		_session(env),
		_connected(false)
	{
		connect(login_para);
	}

	 // constructor to login using name, password and TNSNAME
	OciLogin(OciEnv& env, const tstring& username, const tstring& password, const tstring& tnsname, ub4 mode=OCI_DEFAULT)
	 :	super(env),
		_server(env),
		_ctx(env, _handle),
		_session(env),
		_connected(false)
	{
		connect(username, password, tnsname, mode);
	}

	 // login using the given LoginPara struct
	void connect(const LoginPara& login_para)
	{
		connect(login_para._username, login_para._password, login_para._tnsname, login_para._mode);
	}

	 // login using name, password and TNSNAME
	void connect(const tstring& username, const tstring& password, const tstring& tnsname, ub4 mode=OCI_DEFAULT)
	{
		 // set server name
		_server.attach(tnsname);
		_tnsname = tnsname;	// store database name for further reference in the application

		 // set session parameters
		_session.set_attribute(OCI_ATTR_USERNAME, username);
		_session.set_attribute(OCI_ATTR_PASSWORD, password);

		 // login using _server and _session
		set_attribute(_server);

		sword res = OCISessionBegin(_handle, _env._errh, _session, OCI_CRED_RDBMS, mode);

		if (res!=OCI_SUCCESS && res!=OCI_SUCCESS_WITH_INFO) {
			_server.detach();
			oci_check_error(_env, res);
		}

		 // look for warning messages ("ORA-28002: the password will expire within ... days")
		if (res == OCI_SUCCESS_WITH_INFO) {
			TOraText buffer[1024];

			sb4 errorcode;
			sword res = OCIErrorGet(_env._errh, 1, NULL, &errorcode, (OraText*)buffer, sizeof(buffer), OCI_HTYPE_ERROR);

			if (res == OCI_SUCCESS)
				_warnings = (const TCHAR*)buffer;
		}

		_connected = true;

		set_attribute(_session);

		/* automatically allocate and initialize server and session handles
		sword res = OCILogon(_env, _env._errh, &_ctx,
							  (TOraText*)username.c_str(), username.length(),
							  (TOraText*)password.c_str(), password.length(),
							  (TOraText*)tnsname.c_str(), tnsname.length());
		oci_check_error(_env, res);
		*/
	}

	 // The destructor disconnects from the connected Oracle server.
	~OciLogin()
	{
		disconnect();
	}

	 // disconnect from the connected Oracle server
	void disconnect()
	{
		if (_connected) {
			/* automatically free server and session handles
			sword res = OCILogoff(_svchp, _env._errh);
			_svchp = 0;
			*/
			sword res = OCISessionEnd(_handle, _env._errh, _session, OCI_DEFAULT);
			oci_check_error(_env, res);

			_server.detach();

			_connected = false;
		}
	}

	 // Return true for an active connection.
	bool connected() const
	{
		return _connected;
	}

	 // Commit the current transaction.
	void commit(ub4 flags=OCI_DEFAULT)
	{
		_ctx.commit(flags);
	}

	 // Rollback the current transaction.
	void rollback(ub4 flags=OCI_DEFAULT)
	{
		_ctx.rollback(flags);
	}

	OracleServer _server;
	tstring	_warnings;
	tstring	_tnsname;

protected:
	OciContext _ctx;
	OciHandle<OCISession> _session;

	bool	_connected;
};


#if ORACLE_VERSION>=90

 /// a map to cache SQL statements
struct StatementCache : public std::map<tstring, tstring>
{
};

#endif


 /// wrap OCIEnv and OCISvcCtx together

struct OciConnection
{
	OciConnection(OCIEnv* envh, OCISvcCtx* ctx)
	 :	_env(envh),
		_ctx(_env, ctx)
	{
	}

	static OciConnection* CreateFromESqlContext(void* context);

	 // Commit the current transaction.
	void commit(ub4 flags=OCI_DEFAULT)
	{
		_ctx.commit(flags);
	}

	 // Rollback the current transaction.
	void rollback(ub4 flags=OCI_DEFAULT)
	{
		_ctx.rollback(flags);
	}

	 // cancel a pending OCI call in the worker thread
	void	cancel();

#if ORACLE_VERSION>=81
	 // reset() is to be called after handling the cancellation in the calling worker thread.
	void	reset();
#endif

	OciEnv		_env;
	OciContext	_ctx;

#if ORACLE_VERSION>=90
	StatementCache	_stmt_cache;
#endif

	 // return error handle of the environment object
	operator OCIError*()
	{
		return _env._errh;
	}

private:
	OciConnection(const OciConnection&);	// disallow copy constructor calls
};


 /// column type information

struct ColumnTypeInfo
{
	ub2		_data_type;
	ub2		_width;
	ub1		_char_semantics;	// for Oracle 9
	sb1		_scale;
	sb2		_precision;
	ub1		_nullable;

	 // return a textual representation of the column type
	tstring	get_type_str(bool show_null=false) const;
};


 /// column type description including name

struct ColumnType : public ColumnTypeInfo
{
	tstring	_name;

	 // The empty default constructor just initializes all member variables.
	ColumnType()
	{
		_data_type = 0;
		_width = 0;
		_char_semantics = 0;
		_precision = 0;
		_scale = -127;
		_nullable = false;
	}

	 // retrieve type information from the given column handle
	void	init(OCIError* errh, dvoid* handle);
};


typedef OciHandle<OCIBind> OciBindHandle;
typedef OciHandle<OCIDefine> OciDefineHandle;


 /// Indicator variables are used to store information about the NULL-state of bind variables and result columns.

struct SqlInd
{
	 // The constructor defaults to state 'NULL'.
	SqlInd(OCIInd ind=OCI_IND_NULL)
	 : _ind(ind)
	{
	}

	operator OCIInd() const {return _ind;}
	operator OCIInd*() {return &_ind;}
	operator const OCIInd*() const {return &_ind;}

	bool is_null() const {return _ind==OCI_IND_NULL;}
	bool is_not_null() const {return _ind!=OCI_IND_NULL;}

	void clear() {_ind = OCI_IND_NULL;}
	void set() {_ind = OCI_IND_NOTNULL;}

protected:
	OCIInd	_ind;	// short
};


 /// base class of bind variable structures containing the indicator variable

struct SqlValue
{
	SqlValue() {}
	SqlValue(OCIInd ind) : _ind(ind) {}
	SqlValue(const SqlValue& other) : _ind(other._ind) {}

	bool is_null() const {return _ind.is_null();}
	bool is_not_null() const {return _ind.is_not_null();}

	const SqlInd& ind() const {return _ind;}
	SqlInd& ind() {return _ind;}

protected:
	SqlInd	_ind;
};


enum {INT_NULL = -1};

 /// array of indicator variables

struct SqlIndArray
{
	 // constructor determining the array length
	SqlIndArray(int size=g_OCIPL_BULK_ROWS)
	 :	_count(size),
		_pind(new SqlInd[size])
	{
	}

	 // copy onstructor
	SqlIndArray(const SqlIndArray& other)
	 :	_count(other._count),
		_pind(new SqlInd[other._count])
	{
		memcpy(_pind, other._pind, other._count*sizeof(SqlInd));
	}

	 // The destructor just frees the array's allocated memory.
	~SqlIndArray()
	{
		delete [] _pind;
	}

	bool is_null(int idx) const {return _pind[idx].is_null();}
	bool is_not_null(int idx) const {return _pind[idx].is_not_null();}

	const SqlInd& ind(int idx) const {return _pind[idx];}
	SqlInd& ind(int idx) {return _pind[idx];}

	int get_buffer_count() const {return _count;}

protected:
	int		_count;
	SqlInd*	_pind;
};


 /// int wrapper with indicator variable

struct SqlInt : public SqlValue
{
	 // The default constructor initializes the value to NULL.
	SqlInt()
	{
	}

	 // constructor taking an int value
	SqlInt(int value)
	 :	_value(value)
	{
		_ind.set();
	}

	 // constructor taking an int value and indicator information about the NULL-state
	SqlInt(int value, OCIInd ind)
	 :	SqlValue(ind),
		_value(value)
	{
	}

	 // copy constructor
	SqlInt(const SqlInt& other)
	 :	SqlValue(other),
		_value(other._value)
	{
	}

	 // return the current integer value or INT_NULL for NULL values
	operator int() const
	{
		return _ind.is_not_null()? _value: INT_NULL;
	}

	int get_int(int null=INT_NULL) const
	{
		return _ind.is_not_null()? _value: null;
	}

	 // return a non-const pointer to the wrapped int variable
	int* get_ref()
	{
		return &_value;
	}

	 // return a const pointer to the wraped int variable
	const int* get_ptr() const
	{
		return &_value;
	}

	 // return the current value as decimal string or an empty string for NULL values
	tstring str() const
	{
		TCHAR buffer[16];

		if (_ind.is_not_null()) {
			_stprintf_s1(buffer, sizeof(buffer)/sizeof(TCHAR), TEXT("%d"), _value);
			return buffer;
		} else
			return tstring();
	}

protected:
	int		_value;	// wrapped int variable
};


 /// array of integer bind variable buffers

struct SqlIntArray : public SqlIndArray
{
	 // constructor determining the array length
	SqlIntArray(int size=g_OCIPL_BULK_ROWS)
	 :	SqlIndArray(size),
		_pvalues((int*)ocipl_alloc(size*sizeof(int)))
	{
	}

	SqlIntArray(const SqlIntArray& other)
	 :	SqlIndArray(other),
		_pvalues((int*)ocipl_alloc(other._count*sizeof(int)))
	{
		memcpy(_pvalues, other._pvalues, other._count*sizeof(int));
	}

	~SqlIntArray()
	{
		ocipl_free(_pvalues);
	}

	int get_int(int row, int null=INT_NULL) const {return _pind[row].is_not_null()? _pvalues[row]: null;}

	int* get_ref(int row) {return &_pvalues[row];}
	const int* get_ptr(int row) const {return &_pvalues[row];}

	tstring str(int row) const
	{
		TCHAR buffer[16];

		if (_pind[row].is_not_null()) {
			_stprintf_s1(buffer, sizeof(buffer)/sizeof(TCHAR), TEXT("%d"), _pvalues[row]);
			return buffer;
		} else
			return tstring();
	}

protected:
	int*	_pvalues;
};


 /// string buffer bind variable structure

struct SqlString : public SqlValue
{
	SqlString(int blen=OCIPL_DEFAULT_STRING_BUFFER)
	 :	_blen(blen),
		_str((TCHAR*)ocipl_alloc((blen+1)*sizeof(TCHAR)))
	{
		*_str = '\0';
	}

	SqlString(const TCHAR* s)
	 :	_blen(_tcslen(s)),
		_str(_tcsdup(s))
	{
		if (*s)
			_ind.set();
	}

	SqlString(const TCHAR* s, int blen)
	 :	_blen(blen),
		_str((TCHAR*)ocipl_alloc((blen+1)*sizeof(TCHAR)))
	{
		ocipl_tcscpyn(_str, s, blen+1);

		if (*s)
			_ind.set();
	}

	SqlString(const SqlString& other)
	 :	SqlValue(other),
		_blen(_tcslen(other._str)),
		_str(_tcsdup(other._str))
	{
	}

	~SqlString()
	{
		ocipl_free(_str);
	}

	size_t len() const {return is_null()? 0: _tcslen(_str);}
	size_t blen() const {return _blen;}
	size_t alen() const {return _blen + 1;}

	void resize(size_t l) {_blen = l; _str = (TCHAR*)ocipl_realloc(_str, (l+1)*sizeof(TCHAR)); _str[l] = '\0';}
	void clear() {*_str = '\0'; _ind.clear();}

	TCHAR* str() {return _str;}	// Note: _ind is not checked by this function.
	TCHAR* str(int l) {resize(l); return _str;}	// Note: _ind is not changed by this function.

	const TCHAR* c_str() const {return is_null()? TEXT(""): _str;}
	operator const TCHAR*() const {return is_null()? TEXT(""): _str;}

	SqlString& operator=(const TCHAR* s);
	SqlString& operator=(const tstring& s);

	bool operator==(const TCHAR* s) const {return !_tcscmp(c_str(), s);}
	bool operator!=(const TCHAR* s) const {return _tcscmp(c_str(), s) != 0;}
	bool operator<(const TCHAR* s) const {return _tcscmp(c_str(), s) < 0;}

	friend tostream& operator<<(tostream& os, const SqlString& str)
		{if (str.is_not_null()) os << str._str; return os;}

	static inline TCHAR* ocipl_tcscpyn(TCHAR* dest, const TCHAR* source, size_t count)
	{
		TCHAR* d = dest;

		for(const TCHAR* s=source; count&&(*d++=*s++); )
			count--;

		return dest;
	}

protected:
	size_t	_blen;
	TCHAR*	_str;
};


 /// array of string bind variable buffers

struct SqlStringArray : public SqlIndArray
{
	 // constructor determining the array length and string buffer size
	SqlStringArray(int size=g_OCIPL_BULK_ROWS, int blen=OCIPL_DEFAULT_STRING_BUFFER)
	 :	SqlIndArray(size),
		_blen(blen),
		_buffer((TCHAR*)ocipl_alloc(size*(blen+1)*sizeof(TCHAR)))
	{
		memset(_buffer, 0, size*(blen+1)*sizeof(TCHAR));
	}

	SqlStringArray(const SqlStringArray& other)
	 :	SqlIndArray(other),
		_blen(other._blen),
		_buffer((TCHAR*)ocipl_alloc(other._count*(other._blen+1)*sizeof(TCHAR)))
	{
		memcpy(_buffer, 0, other._count*(other._blen+1)*sizeof(TCHAR));
	}

	~SqlStringArray()
	{
		ocipl_free(_buffer);
	}

	size_t len(int row) const {return is_not_null(row)? _tcslen(_buffer): 0;}
	size_t blen() const {return _blen;}
	size_t alen() const {return _blen+1;}

	void clear(int row) {*_buffer = '\0'; _pind[row].clear();}
//	void assign(int row, const dvoid* ptr, sb4 size);
	void assign(int row, const TCHAR* s) {strcpy_s(_buffer, _blen, s);}//TODO resize buffer as needed

	TCHAR* str(int row) {return _buffer + row*(_blen+1);}	// Note: _ind is not checked by this function.
	const TCHAR* c_str(int row) const {return is_not_null(row)? _buffer + row*(_blen+1): TEXT("");}

protected:
	size_t	_blen;
	TCHAR*	_buffer;
};


extern tstring ocidate_to_string(const OCIDate& date);
extern tstring ocidate_to_short_string(const OCIDate& date);
extern struct tm* ocidate_to_gmt(const OCIDate& date);
extern struct tm* ocidate_to_lctime(const OCIDate& date);
extern time_t ocidate_to_time_t(const OCIDate& date);

#ifdef _WIN32
extern bool ocidate_to_systemtime(const OCIDate& date, SYSTEMTIME* pSysTime);
#endif


 /// wrapper for OCI DATE type

struct OciDate : public OCIDate, public SqlValue
{
	OciDate() {}
	OciDate(const OCIDate& date, OCIInd ind=OCI_IND_NOTNULL);
	OciDate(OCIError* errh, const TCHAR* str, const TCHAR* fmt=TEXT("dd.mm.yyyy"));
	OciDate(struct tm* t);

	tstring str() const
	{
		if (_ind.is_not_null())
			return ocidate_to_string(*this);
		else
			return tstring();
	}

	tstring short_str() const
	{
		if (_ind.is_not_null())
			return ocidate_to_short_string(*this);
		else
			return tstring();
	}

	int get_int_date() const
	{
		if (_ind.is_not_null())
			return ((OCIDateYYYY*100) + OCIDateMM)*100 + OCIDateDD;
		else
			return 0;
	}

	struct tm* get_gmt() const
	{
		if (_ind.is_not_null())
			return ocidate_to_gmt(*this);
		else
			return 0;
	}

	struct tm* get_lctime() const
	{
		if (_ind.is_not_null())
			return ocidate_to_lctime(*this);
		else
			return 0;
	}

	time_t get_time_t() const
	{
		if (_ind.is_not_null())
			return ocidate_to_time_t(*this);
		else
			return 0;
	}

#ifdef _WIN32
	bool get_systemtime(SYSTEMTIME* pSysTime) const
	{
		if (_ind.is_not_null())
			return ocidate_to_systemtime(*this, pSysTime);
		else
			return false;
	}
#endif

	int compare(const OciDate& other) const;
};


 /// OCI DATE type bind variable array

struct OciDateArray : public SqlIndArray
{
	 // constructor determining the array length
	OciDateArray(int size=g_OCIPL_BULK_ROWS)
	 :	SqlIndArray(size),
		_buffers((OCIDate*)ocipl_alloc(size*sizeof(OCIDate)))
	{
	}

	~OciDateArray()
	{
		ocipl_free(_buffers);
	}

	const OCIDate& get_date(int row) const {return _buffers[row];}
	OciDate get_oci_date(int row) const {return OciDate(_buffers[row], _pind[row]);}

	OCIDate* get_ref(int row) {return &_buffers[row];}
	const OCIDate* get_ref(int row) const {return &_buffers[row];}

	tstring str(int row) const
	{
		if (_pind[row].is_not_null())
			return ocidate_to_string(_buffers[row]);
		else
			return tstring();
	}

	tstring short_str(int row) const
	{
		if (_pind[row].is_not_null())
			return ocidate_to_short_string(_buffers[row]);
		else
			return tstring();
	}

	int get_int_date(int row) const
	{
		if (_pind[row].is_not_null()) {
			const OCIDate& date = _buffers[row];
			return ((date.OCIDateYYYY*100) + date.OCIDateMM)*100 + date.OCIDateDD;
		} else
			return 0;
	}

	struct tm* get_gmt(int row) const
	{
		if (_pind[row].is_not_null())
			return ocidate_to_gmt(_buffers[row]);
		else
			return 0;
	}

	struct tm* get_lctime(int row) const
	{
		if (_pind[row].is_not_null())
			return ocidate_to_lctime(_buffers[row]);
		else
			return 0;
	}

	time_t get_time_t(int row) const
	{
		if (_pind[row].is_not_null())
			return ocidate_to_time_t(_buffers[row]);
		else
			return 0;
	}

	void set_date(int row, const OciDate& date) {_buffers[row] = date; _pind[row] = date.ind();}

protected:
	OCIDate* _buffers;
};


 /// internal DATE representation
struct ocidatetime {
	ub1	century;
	ub1	year;
	ub1	month;
	ub1	day;
	ub1	hour;
	ub1	minute;
	ub1	second;
};

extern tstring ocidatetime_to_string(const ocidatetime& date);


 /// wrapper for internal Oracle DATE type

struct SqlDateTime : public SqlValue
{
	SqlDateTime() {}

	SqlDateTime(const ocidatetime& date, OCIInd ind=OCI_IND_NOTNULL)
	 :	SqlValue(ind),
		_date(date)
	{
	}

	ocidatetime* get_ref() {return &_date;}
	const ocidatetime* get_ptr() const {return &_date;}

	const ocidatetime& get_datetime() const {return _date;}

	tstring str() const
	{
		if (_ind.is_not_null())
			return ocidatetime_to_string(_date);
		else
			return tstring();
	}

protected:
	ocidatetime _date;
};


 /// wrapper for internal Oracle DATE type

struct SqlDateTimeArray : public SqlIndArray
{
	 // constructor determining the array length
	SqlDateTimeArray(int size=g_OCIPL_BULK_ROWS)
	 :	SqlIndArray(size),
		_buffers((ocidatetime*)ocipl_alloc(size*sizeof(ocidatetime)))
	{
	}

	~SqlDateTimeArray()
	{
		ocipl_free(_buffers);
	}

	const ocidatetime& get_datetime(int row) const {return _buffers[row];}
	SqlDateTime get_sql_date(int row) const {return SqlDateTime(_buffers[row], _pind[row]);}

	ocidatetime* get_ref(int row) {return &_buffers[row];}
	const ocidatetime* get_ptr(int row) const {return &_buffers[row];}

	tstring str(int row) const
	{
		if (_pind[row].is_not_null())
			return ocidatetime_to_string(_buffers[row]);
		else
			return tstring();
	}

	void set_datetime(int row, const SqlDateTime& datetime) {_buffers[row] = datetime.get_datetime(); _pind[row] = datetime.ind();}

protected:
	ocidatetime* _buffers;
};


 /// internal NUMBER representation
struct oraclenumber {
	ub1	len;
	ub1	exp;
	ub1	mant[20];
};


extern int number_to_int(const oraclenumber& val, OCIError* errh);
extern double number_to_double(const oraclenumber& val, OCIError* errh);
extern tstring number_to_str(const oraclenumber& val, OCIError* errh, const TCHAR* fmt, const TCHAR* nls_fmt);

#ifdef _MSC_VER
extern LONGLONG number_to_int64(const oraclenumber& val, OCIError* errh, int null=INT_NULL);
#endif


 /// wrapper for internal Oracle NUMBER type

struct SqlNumber : public SqlValue
{
	SqlNumber() {}

	SqlNumber(const oraclenumber* pnum);

	SqlNumber(OCIError* errh, const TCHAR* s,
				const TCHAR* fmt=TEXT("99999999999999999999999999999999999999D99999999999999999999"),
				const TCHAR* nls_fmt=TEXT("NLS_NUMERIC_CHARACTERS='.,'"));

	SqlNumber(OCIError* errh, double d)
	{
		sword res = OCINumberFromReal(errh, &d, sizeof(d), (OCINumber*)&_val);
		oci_check_error(errh, res);
		_ind.set();
	}

	 // constructor taking an double value and indicator information about the NULL-state
	SqlNumber(OCIError* errh, double d, OCIInd ind)
	 :	SqlValue(ind)
	{
		if (ind != OCI_IND_NULL) {
			sword res = OCINumberFromReal(errh, &d, sizeof(d), (OCINumber*)&_val);
			oci_check_error(errh, res);
		}
	}

	SqlNumber(OCIError* errh, int i)
	{
		sword res = OCINumberFromInt(errh, &i, sizeof(i), OCI_NUMBER_SIGNED, (OCINumber*)&_val);
		oci_check_error(errh, res);
		_ind.set();
	}

	 // constructor taking an int value and indicator information about the NULL-state
	SqlNumber(OCIError* errh, int i, OCIInd ind)
	 :	SqlValue(ind)
	{
		if (ind != OCI_IND_NULL) {
			sword res = OCINumberFromInt(errh, &i, sizeof(i), OCI_NUMBER_SIGNED, (OCINumber*)&_val);
			oci_check_error(errh, res);
		}
	}

	SqlNumber(ub1* num_ptr, ub1 num_len)
	{
		memcpy(&_val.exp, num_ptr, num_len);
		_val.len = num_len;
		_ind.set();
	}

	const oraclenumber& get_number() const {return _val;}

	oraclenumber* get_ref() {return &_val;}
	const oraclenumber* get_ptr() const {return &_val;}

	//tstring str(bool internal=false) const;
	tstring str(OCIError* errh, const TCHAR* fmt=TEXT("TM9"),//"FM99999999999999999999999999999999999999.99999999999999999999"
					const TCHAR* nls_fmt=TEXT("NLS_NUMERIC_CHARACTERS='.,'")) const
	{
		if (_ind.is_not_null())
			return number_to_str(_val, errh, fmt, nls_fmt);
		else
			return tstring();
	}

	operator OCINumber*() {return (OCINumber*)&_val;}
	operator const OCINumber*() const {return (OCINumber*)&_val;}

	int get_int(OCIError* errh, int null=INT_NULL) const
	{
		if (is_null())
			return null;
		else
			return number_to_int(_val, errh);
	}

#ifdef _MSC_VER
	LONGLONG get_int64(OCIError* errh, int null=INT_NULL) const
	{
		if (is_null())
			return null;
		else
			return number_to_int64(_val, errh, null);
	}
#endif

	double get_double(OCIError* errh, double null=0.) const
	{
		if (is_null())
			return null;
		else
			return number_to_double(_val, errh);	// atof(str(true).c_str());
	}

protected:
	oraclenumber _val;
};


 /// array of NUMBER bind variable buffers

struct SqlNumberArray : public SqlIndArray
{
	 // constructor determining the array length
	SqlNumberArray(int size=g_OCIPL_BULK_ROWS)
	 :	SqlIndArray(size),
		_pvalues((oraclenumber*)ocipl_alloc(size*sizeof(oraclenumber)))
	{
	}

	~SqlNumberArray()
	{
		ocipl_free(_pvalues);
	}

	const oraclenumber& get_number(int row) const {return _pvalues[row];}

	oraclenumber* get_ref(int row) {return &_pvalues[row];}
	const oraclenumber* get_ptr(int row) const {return &_pvalues[row];}

	//tstring str(bool internal=false) const;
	tstring str(int row, OCIError* errh, const TCHAR* fmt=TEXT("TM9"),//"FM99999999999999999999999999999999999999.99999999999999999999"
					const TCHAR* nls_fmt=TEXT("NLS_NUMERIC_CHARACTERS='.,'")) const
	{
		if (_pind[row].is_not_null())
			return number_to_str(_pvalues[row], errh, fmt, nls_fmt);
		else
			return tstring();
	}

	int get_int(int row, OCIError* errh, int null=INT_NULL) const
	{
		if (is_not_null(row))
			return number_to_int(_pvalues[row], errh);
		else
			return null;
	}

#ifdef _MSC_VER
	LONGLONG get_int64(int row, OCIError* errh, int null=INT_NULL) const
	{
		if (is_not_null(row))
			return number_to_int64(_pvalues[row], errh, null);
		else
			return null;
	}
#endif

	double get_double(int row, OCIError* errh, double null=0.) const
	{
		if (is_not_null(row))
			return number_to_double(_pvalues[row], errh);
		else
			return null;
	}

	void set_number(int row, const SqlNumber& num) {_pvalues[row] = num.get_number(); _pind[row] = num.ind();}

protected:
	oraclenumber* _pvalues;
};


 /// base class for the BLOB/CLOB data holders OCIPL::OciLob and OCIPL::OciClob

//template<typename TYPE>
struct OciLob : public SqlValue
{
//	OciLob() : _loc(NULL) {}

	OciLob(OciConnection& conn)
	 :	_conn(conn)
	{
		sword res = OCIDescriptorAlloc(conn._env, (dvoid**)&_loc, OCI_DTYPE_LOB, 0, NULL);
		oci_check_error(conn._env, res);
	}

	OciLob(OciConnection& conn, OCILobLocator* loc)
	 :	_conn(conn),
		_loc(loc)
	{
	}

	OciLob(const OciLob& other)
	 :	_conn(other._conn)
	{
		sword res = OCIDescriptorAlloc(_conn._env, (dvoid**)&_loc, OCI_DTYPE_LOB, 0, NULL);
		oci_check_error(_conn._env._errh, res);

#if ORACLE_VERSION>=81
		res = OCILobLocatorAssign(_conn._ctx, _conn._env._errh, other._loc, &_loc);
#else
		res = OCILobAssign(_conn._env, _conn._env._errh, other._loc, &_loc);	// no support for temporary LOBs
#endif
		oci_check_error(_conn._env._errh, res);

		clear();
	}

	~OciLob()
	{
#if ORACLE_VERSION>=81
		 // According to the Oracle 10g documentation we should try to free
		 // implicit created temporary LOBs as soon as possible.
		if (is_temporary()) {
			sword res = OCILobFreeTemporary(_conn._ctx, _conn._env._errh, _loc);
			oci_check_error(_conn._env._errh, res);
		}
#endif

		sword res = OCIDescriptorFree(_loc, OCI_DTYPE_LOB);
		oci_check_error(_conn._env._errh, res);
	}

	boolean operator==(const OciLob& other) const
	{
		boolean is_equal;

		sword res = OCILobIsEqual(_conn._env, _loc, other._loc, &is_equal);

		oci_check_error(_conn._env._errh, res);

		return is_equal;
	}

	void clear()
	{
		ub4 lobEmpty = 0;
		sword res = OCIAttrSet(_loc, OCI_DTYPE_LOB, &lobEmpty, 0, OCI_ATTR_LOBEMPTY, _conn._env._errh);
		oci_check_error(_conn._env._errh, res);
	}

	void* get_ref() {return &_loc;}
	const void* get_ptr() const {return &_loc;}

	//tstring str(bool internal=false) const;


	ub4	get_chunk_size()
	{
		ub4 len;

		sword res = OCILobGetLength(_conn._ctx, _conn._env._errh, _loc, &len);
		oci_check_error(_conn._env._errh, res);

		return len;
	}

#if ORACLE_VERSION>=81
	ub4	get_length()
	{
		ub4 size;

		sword res = OCILobGetChunkSize(_conn._ctx, _conn._env._errh, _loc, &size);
		oci_check_error(_conn._env._errh, res);

		return size;
	}

	boolean	is_open() const
	{
		boolean flag;

		sword res = OCILobIsOpen(_conn._ctx, _conn._env._errh, _loc, &flag);
		oci_check_error(_conn._env._errh, res);

        return flag;
	}

	boolean	is_temporary() const
	{
		boolean flag;

		sword res = OCILobIsTemporary(_conn._env, _conn._env._errh, _loc, &flag);
		oci_check_error(_conn._env._errh, res);

        return flag;
	}
#endif


	void	copy(const OciLob& src, ub4 amount, ub4 dst_offset, ub4 src_offset)
	{
		sword res = OCILobCopy(_conn._ctx, _conn._env._errh, _loc, src._loc, amount, dst_offset, src_offset);
		oci_check_error(_conn._env._errh, res);
	}

	void	append(const OciLob& src)
	{
		sword res = OCILobAppend(_conn._ctx, _conn._env._errh, _loc, src._loc);
		oci_check_error(_conn._env._errh, res);
	}

	void	trim(ub4 newlen=0)
	{
		sword res = OCILobTrim(_conn._ctx, _conn._env._errh, _loc, newlen);
		oci_check_error(_conn._env._errh, res);
	}

	ub4	erase(ub4 offset, ub4 amount)
	{
		sword res = OCILobErase(_conn._ctx, _conn._env._errh, _loc, &amount, offset);
		oci_check_error(_conn._env._errh, res);

		return amount;
	}


	void	enable_buffering()
	{
		sword res = OCILobEnableBuffering(_conn._ctx, _conn._env._errh, _loc);
		oci_check_error(_conn._env._errh, res);
	}

	void	disable_buffering()
	{
		sword res = OCILobDisableBuffering(_conn._ctx, _conn._env._errh, _loc);
		oci_check_error(_conn._env._errh, res);
	}

	void	flush(ub4 flag=OCI_LOB_BUFFER_NOFREE)
	{
		sword res = OCILobFlushBuffer(_conn._ctx, _conn._env._errh, _loc, flag);
		oci_check_error(_conn._env._errh, res);
	}

protected:
	OciConnection&	_conn;
	OCILobLocator*	_loc;

	friend struct OciOpenLob;
};


#if ORACLE_VERSION>=81

 /// open and close LOBs using OciOpenLob for exception handling
 // Forgetting to close LOBs results in errors in OciLogin::disconnect().
struct OciOpenLob
{
	OciOpenLob(OciLob& lob, ub1 mode=OCI_LOB_READWRITE)
	 :	_lob(lob)
	{
		sword res = OCILobOpen(lob._conn._ctx, lob._conn._env._errh, lob._loc, mode);
		oci_check_error(lob._conn._env._errh, res);
	}

	~OciOpenLob()
	{
		sword res = OCILobClose(_lob._conn._ctx, _lob._conn._env._errh, _lob._loc);
		oci_check_error(_lob._conn._env._errh, res);
	}

protected:
	OciLob&	_lob;
};


 /// BLOB data holder structure
struct OciBlob : public OciLob
{
	OciBlob(OciConnection& conn) : OciLob(conn) {}
	OciBlob(const OciLob& other) : OciLob(other) {}

	ub4	write(const dvoid* bufp, ub4 buflen, ub4 offset/*=1*/, ub4 amount)
	{
		sword res = OCILobWrite(_conn._ctx, _conn._env._errh, _loc,
                      &amount, offset, (dvoid*)bufp, buflen,
					  OCI_ONE_PIECE/*ub1 piece*/, NULL/*dvoid* ctxp*/, NULL/*sb4 (*cbfp)(dvoid*ctxp,dvoid*bufp,ub4*len,ub1*piece)*/,
					  0, 0);

		oci_check_error(_conn._env._errh, res);

		return amount;
	}

	ub4	write_append(const dvoid* bufp, ub4 buflen, ub4 amount)
	{
		sword res = OCILobWriteAppend(_conn._ctx, _conn._env._errh, _loc,
                      &amount, (dvoid*)bufp, buflen,
					  OCI_ONE_PIECE/*ub1 piece*/, NULL/*dvoid* ctxp*/, NULL/*sb4 (*cbfp)(dvoid*ctxp,dvoid*bufp,ub4*len,ub1*piece)*/,
					  0, 0);

		oci_check_error(_conn._env._errh, res);

		return amount;
	}

	ub4	read(dvoid* bufp, ub4 buflen, ub4 offset, ub4 amount)
	{
		sword res = OCILobRead(_conn._ctx, _conn._env._errh, _loc,
                      &amount, offset, bufp, buflen,
					  NULL/*dvoid* ctxp*/, NULL/*sb4 (*cbfp)(dvoid*ctxp,CONST dvoid*bufp,ub4*len,ub1*piece)*/,
					  0, 0);

		oci_check_error(_conn._env._errh, res);

		return amount;
	}
};

struct OciTempBlob : public OciBlob
{
	OciTempBlob(OciConnection& conn, OCIDuration dur=OCI_DURATION_SESSION)
	 :	OciBlob(conn)
	{
		sword res = OCILobCreateTemporary(conn._ctx, conn._env._errh, _loc,
											OCI_DEFAULT, SQLCS_IMPLICIT, OCI_TEMP_BLOB, FALSE, dur);

		oci_check_error(_conn._env._errh, res);
	}

	~OciTempBlob()
	{
		/*already implemented in ~OciLob
		sword res = OCILobFreeTemporary(_conn._ctx, _conn._env._errh, _loc);
		oci_check_error(_conn._env._errh, res);
		*/
	}
};

#endif


 /// CLOB data holder structure

struct OciClob : public OciLob
{
	OciClob(OciConnection& conn) : OciLob(conn) {}
	OciClob(const OciLob& other) : OciLob(other) {}

	ub4	write(const dvoid* bufp, ub4 buflen, ub4 offset, ub4 amount, ub2 csid=0, ub1 csfrm=SQLCS_IMPLICIT)
	{
		sword res = OCILobWrite(_conn._ctx, _conn._env._errh, _loc,
                      &amount, offset, (dvoid*)bufp, buflen,
					  OCI_ONE_PIECE/*ub1 piece*/, NULL/*dvoid* ctxp*/, NULL/*sb4 (*cbfp)(dvoid*ctxp,dvoid*bufp,ub4*len,ub1*piece)*/,
					  csid, csfrm);

		oci_check_error(_conn._env._errh, res);

		return amount;
	}

#if ORACLE_VERSION>=81
	ub4	write_append(const dvoid* bufp, ub4 buflen, ub4 amount, ub2 csid=0, ub1 csfrm=SQLCS_IMPLICIT)
	{
		sword res = OCILobWriteAppend(_conn._ctx, _conn._env._errh, _loc,
                      &amount, (dvoid*)bufp, buflen,
					  OCI_ONE_PIECE/*ub1 piece*/, NULL/*dvoid* ctxp*/, NULL/*sb4 (*cbfp)(dvoid*ctxp,dvoid*bufp,ub4*len,ub1*piece)*/,
					  csid, csfrm);

		oci_check_error(_conn._env._errh, res);

		return amount;
	}
#endif

	ub4	read(dvoid* bufp, ub4 buflen, ub4 offset, ub4 amount, ub2 csid=0, ub1 csfrm=SQLCS_IMPLICIT)
	{
		sword res = OCILobRead(_conn._ctx, _conn._env._errh, _loc,
                      &amount, offset, bufp, buflen,
					  NULL/*dvoid* ctxp*/, NULL/*sb4 (*cbfp)(dvoid*ctxp,CONST dvoid*bufp,ub4*len,ub1*piece)*/,
					  csid, csfrm);

		oci_check_error(_conn._env._errh, res);

		return amount;
	}
};

#if ORACLE_VERSION>=81
struct OciTempClob : public OciClob
{
	OciTempClob(OciConnection& conn, OCIDuration dur=OCI_DURATION_SESSION)
	 :	OciClob(conn)
	{
		sword res = OCILobCreateTemporary(conn._ctx, conn._env._errh, _loc,
											OCI_DEFAULT, SQLCS_IMPLICIT, OCI_TEMP_CLOB, FALSE, dur);

		oci_check_error(_conn._env._errh, res);
	}

	~OciTempClob()
	{
		/*already implemented in ~OciLob
		sword res = OCILobFreeTemporary(_conn._ctx, _conn._env._errh, _loc);
		oci_check_error(_conn._env._errh, res);
		*/
	}
};
#endif


/* todo
struct OciBFile : public OciLob
{
	OciBFile(OciConnection& conn) : OciLob(conn) {}
	OciBFile(const OciLob& other) : OciLob(other) {}
};

struct OciCFile : public OciLob
{
	OciCFile(OciConnection& conn) : OciLob(conn) {}
	OciCFile(const OciLob& other) : OciLob(other) {}
};

sword   OCILobFileClose (OCISvcCtx *svchp, OCIError *errhp,
                         OCILobLocator *filep);

sword   OCILobFileExists (OCISvcCtx *svchp, OCIError *errhp,
                          OCILobLocator *filep,
                          boolean *flag);

sword   OCILobFileGetName (OCIEnv *envhp, OCIError *errhp,
                           CONST OCILobLocator *filep,
                           OraText *dir_alias, ub2 *d_length,
                           OraText *filename, ub2 *f_length);

sword   OCILobFileOpen (OCISvcCtx *svchp, OCIError *errhp,
                        OCILobLocator *filep,
                        ub1 mode);

sword   OCILobFileIsOpen (OCISvcCtx *svchp, OCIError *errhp,
                          OCILobLocator *filep,
                          boolean *flag);

sword   OCILobFileSetName (OCIEnv *envhp, OCIError *errhp,
                           OCILobLocator **filepp,
                           CONST OraText *dir_alias, ub2 d_length,
                           CONST OraText *filename, ub2 f_length);
*/


 /// factory for call parameters of OCI bind functions

struct BindInput
{
	const dvoid*	valuep;
	sb4				value_sz;
	ub2				dty;
	const OCIInd*	indp;

	BindInput(const SqlInt& var)
	{
		valuep	= var.get_ptr();
		value_sz= sizeof(int);
		dty		= SQLT_INT;
		indp	= var.ind();
	}

	BindInput(const int& var, OCIInd* ind=NULL)
	{
		valuep	= (void*)&var;
		value_sz= sizeof(int);
		dty		= SQLT_INT;
		indp	= ind;
	}

	BindInput(const unsigned int& var, OCIInd* ind=NULL)
	{
		valuep	= (void*)&var;
		value_sz= sizeof(unsigned int);
		dty		= SQLT_UIN;
		indp	= ind;
	}

	BindInput(const long& var, OCIInd* ind=NULL)
	{
		valuep	= (void*)&var;
		value_sz= sizeof(long);
		dty		= SQLT_INT;
		indp	= ind;
	}

	BindInput(const unsigned long& var, OCIInd* ind=NULL)
	{
		valuep	= (void*)&var;
		value_sz= sizeof(unsigned long);
		dty		= SQLT_UIN;
		indp	= ind;
	}

	BindInput(const char& var, OCIInd* ind=NULL)
	{
		valuep	= (void*)&var;
		value_sz= sizeof(char);
		dty		= SQLT_CHR;
		indp	= ind;
	}

	BindInput(const float& var, OCIInd* ind=NULL)
	{
		valuep	= (void*)&var;
		value_sz= sizeof(float);
		dty		= SQLT_FLT;
		indp	= ind;
	}

	BindInput(const double& var, OCIInd* ind=NULL)
	{
		valuep	= (void*)&var;
		value_sz= sizeof(double);
		dty		= SQLT_FLT;
		indp	= ind;
	}

	BindInput(const TCHAR* buffer)
	{
		valuep	= (dvoid*) buffer;
		value_sz= buffer? (sb4)(_tcslen(buffer)+1)*sizeof(TCHAR): 0;
		dty		= SQLT_STR;
		indp	= NULL;
	}

	BindInput(const TCHAR* buffer, size_t len, ub2 type=SQLT_AFC, OCIInd* ind=NULL)
	{
		valuep	= (dvoid*) buffer;
		value_sz= (sb4)len;
		dty		= type;
		indp	= ind;
	}

	BindInput(const tstring& str)
	{
		valuep	= (dvoid*) str.c_str();
		value_sz= (sb4)(str.length()+1)*sizeof(TCHAR);
		dty		= SQLT_STR;
		indp	= NULL;
	}

	BindInput(const SqlString& str)
	{
		valuep	= (dvoid*) str.c_str();
		value_sz= (sb4)str.alen()*sizeof(TCHAR);
		dty		= SQLT_STR;
		indp	= NULL;
	}

#ifdef __ISSD_H
	BindInput(const String& str)
	{
		valuep	= (dvoid*) str.c_str();
		value_sz= (sb4)(str.length()+1)*sizeof(TCHAR);
		dty		= SQLT_STR;
		indp	= NULL;
	}
#endif

/*@todo handle OCIString*
	BindInput(SqlString_& str)
	{
		valuep	= (dvoid*) str.str();
		value_sz= str.alen()*sizeof(TCHAR);
		dty		= SQLT_VST;
		indp	= str.ind();
	}
*/
	BindInput(const OciDate& date)
	{
		valuep	= &date;
		value_sz= sizeof(OCIDate);
		dty		= SQLT_ODT;
		indp	= date.ind();
	}

	BindInput(const OCIDate& date, OCIInd* ind)
	{
		valuep	= &date;
		value_sz= sizeof(OCIDate);
		dty		= SQLT_ODT;
		indp	= ind;
	}

	BindInput(const SqlDateTime& date)
	{
		valuep	= date.get_ptr();
		value_sz= sizeof(ocidatetime);	// 7
		dty		= SQLT_DAT;
		indp	= date.ind();
	}

	BindInput(const ocidatetime& date, OCIInd* ind)
	{
		valuep	= &date;
		value_sz= sizeof(ocidatetime);	// 7
		dty		= SQLT_DAT;
		indp	= ind;
	}

	BindInput(const SqlNumber& num)
	{
		valuep	= num.get_ptr();
		value_sz= sizeof(oraclenumber);	// 22
		dty		= SQLT_VNU;
		indp	= num.ind();
	}

	BindInput(const oraclenumber& num, OCIInd* ind)
	{
		valuep	= &num;
		value_sz= sizeof(oraclenumber);	// 22
		dty		= SQLT_VNU;
		indp	= ind;
	}

#if ORACLE_VERSION>=81
	BindInput(const OciBlob& blob)
	{
		valuep	= blob.get_ptr();
		value_sz= sizeof(OCILobLocator*);
		dty		= SQLT_BLOB;
		indp	= blob.ind();
	}

	BindInput(const OciClob& clob)
	{
		valuep	= clob.get_ptr();
		value_sz= sizeof(OCIClobLocator*);
		dty		= SQLT_CLOB;
		indp	= clob.ind();
	}
#endif

protected:
	BindInput()
	{
	}
};


 /// factory for call parameters of OCI bind functions

struct BindOutput
{
	dvoid*	valuep;
	sb4		value_sz;
	ub2		dty;
	OCIInd*	indp;

	BindOutput(SqlInt& var)
	{
		valuep	= var.get_ref();
		value_sz= sizeof(int);
		dty		= SQLT_INT;
		indp	= var.ind();
	}

	BindOutput(int& var, OCIInd* ind=NULL)
	{
		valuep	= (void*)&var;
		value_sz= sizeof(int);
		dty		= SQLT_INT;
		indp	= ind;
	}

	BindOutput(unsigned int& var, OCIInd* ind=NULL)
	{
		valuep	= (void*)&var;
		value_sz= sizeof(unsigned int);
		dty		= SQLT_UIN;
		indp	= ind;
	}

	BindOutput(long& var, OCIInd* ind=NULL)
	{
		valuep	= (void*)&var;
		value_sz= sizeof(long);
		dty		= SQLT_INT;
		indp	= ind;
	}

	BindOutput(unsigned long& var, OCIInd* ind=NULL)
	{
		valuep	= (void*)&var;
		value_sz= sizeof(unsigned long);
		dty		= SQLT_UIN;
		indp	= ind;
	}

	BindOutput(char& var, OCIInd* ind=NULL)
	{
		valuep	= (void*)&var;
		value_sz= sizeof(char);
		dty		= SQLT_CHR;
		indp	= ind;
	}

	BindOutput(float& var, OCIInd* ind=NULL)
	{
		valuep	= (void*)&var;
		value_sz= sizeof(float);
		dty		= SQLT_FLT;
		indp	= ind;
	}

	BindOutput(double& var, OCIInd* ind=NULL)
	{
		valuep	= (void*)&var;
		value_sz= sizeof(double);
		dty		= SQLT_FLT;
		indp	= ind;
	}

	BindOutput(TCHAR* buffer)
	{
		valuep	= (dvoid*) buffer;
		value_sz= buffer? (sb4)(_tcslen(buffer)+1)*sizeof(TCHAR): 0;
		dty		= SQLT_STR;
		indp	= NULL;
	}

	BindOutput(TCHAR* buffer, size_t len, ub2 type=SQLT_AFC, OCIInd* ind=NULL)
	{
		valuep	= (dvoid*) buffer;
		value_sz= (sb4)len;
		dty		= type;
		indp	= ind;
	}

	BindOutput(tstring& str)
	{
		valuep	= (dvoid*) str.c_str();
		value_sz= (sb4)(str.length()+1)*sizeof(TCHAR);
		dty		= SQLT_STR;
		indp	= NULL;
	}

	BindOutput(SqlString& str)
	{
		valuep	= (dvoid*) str.str();
		value_sz= (sb4)str.alen()*sizeof(TCHAR);
		dty		= SQLT_STR;
		indp	= str.ind();
	}

/*@todo handle OCIString*
	BindOutput(SqlString_& str)
	{
		valuep	= (dvoid*) str.str();
		value_sz= str.alen()*sizeof(TCHAR);
		dty		= SQLT_VST;
		indp	= str.ind();
	}
*/
	BindOutput(OciDate& date)
	{
		valuep	= &date;
		value_sz= sizeof(OCIDate);
		dty		= SQLT_ODT;
		indp	= date.ind();
	}

	BindOutput(SqlDateTime& date)
	{
		valuep	= date.get_ref();
		value_sz= sizeof(ocidatetime);	// 7
		dty		= SQLT_DAT;
		indp	= date.ind();
	}

	BindOutput(SqlNumber& num)
	{
		valuep	= num.get_ref();
		value_sz= sizeof(oraclenumber);	// 22
		dty		= SQLT_VNU;
		indp	= num.ind();
	}

#if ORACLE_VERSION>=81
	BindOutput(OciBlob& blob)
	{
		valuep	= blob.get_ref();
		value_sz= sizeof(OCILobLocator*);
		dty		= SQLT_BLOB;
		indp	= blob.ind();
	}

	BindOutput(OciClob& clob)
	{
		valuep	= clob.get_ref();
		value_sz= sizeof(OCIClobLocator*);
		dty		= SQLT_CLOB;
		indp	= clob.ind();
	}
#endif

protected:
	BindOutput()
	{
	}
};


 /// Binding of input strings with trimming to the specified maximum length

struct TrimBindPar : BindInput
{
	TrimBindPar(const TCHAR* buffer, size_t max_len)
	{
		size_t len = _tcslen(buffer);

		valuep	= (dvoid*) buffer;
		value_sz= (sb4)min(len, max_len)*sizeof(TCHAR);
		dty		= SQLT_AFC;
		indp	= NULL;
	}

	TrimBindPar(const tstring& str, size_t max_len)
	{
		size_t len = str.length();

		valuep	= (dvoid*) str.c_str();
		value_sz= (sb4)min(len, max_len)*sizeof(TCHAR);
		dty		= SQLT_AFC;
		indp	= NULL;
	}

	TrimBindPar(const SqlString& str, size_t max_len)
	{
		size_t len = str.len();

		valuep	= (dvoid*) str.c_str();
		value_sz= (sb4)min(len, max_len)*sizeof(TCHAR);
		dty		= SQLT_AFC;
		indp	= NULL;
	}
};


 /// factory for call parameters for OCI define functions

struct DefinePar
{
	dvoid*	valuep;
	sb4		value_sz;
	ub2		dty;
	OCIInd*	indp;
	int		buffers;

	DefinePar(SqlInt& var)
	{
		valuep	= var.get_ref();
		value_sz= sizeof(int);
		dty		= SQLT_INT;
		indp	= var.ind();
		buffers	= 1;
	}
	DefinePar(SqlIntArray& arr)
	{
		valuep	= arr.get_ref(0);
		value_sz= sizeof(int);
		dty		= SQLT_INT;
		indp	= arr.ind(0);
		buffers	= arr.get_buffer_count();
	}

	DefinePar(int& var, OCIInd* ind=NULL)
	{
		valuep	= &var;
		value_sz= sizeof(int);
		dty		= SQLT_INT;
		indp	= ind;
		buffers	= 1;
	}
	DefinePar(int* arr, int count, OCIInd* ind=NULL)
	{
		valuep	= arr;
		value_sz= sizeof(int);
		dty		= SQLT_INT;
		indp	= ind;
		buffers	= count;
	}

	DefinePar(unsigned int& var, OCIInd* ind=NULL)
	{
		valuep	= &var;
		value_sz= sizeof(unsigned int);
		dty		= SQLT_UIN;
		indp	= ind;
		buffers	= 1;
	}
	DefinePar(unsigned int* arr, int count, OCIInd* ind=NULL)
	{
		valuep	= arr;
		value_sz= sizeof(unsigned int);
		dty		= SQLT_UIN;
		indp	= ind;
		buffers	= count;
	}

	DefinePar(LONGLONG& var, OCIInd* ind=NULL)
	{
		valuep	= &var;
		value_sz= sizeof(LONGLONG);
		dty		= SQLT_INT;
		indp	= ind;
		buffers	= 1;
	}
	DefinePar(LONGLONG* arr, int count, OCIInd* ind=NULL)
	{
		valuep	= arr;
		value_sz= sizeof(LONGLONG);
		dty		= SQLT_INT;
		indp	= ind;
		buffers	= count;
	}

	DefinePar(long& var, OCIInd* ind=NULL)
	{
		valuep	= &var;
		value_sz= sizeof(long);
		dty		= SQLT_INT;
		indp	= ind;
		buffers	= 1;
	}
	DefinePar(long* arr, int count, OCIInd* ind=NULL)
	{
		valuep	= arr;
		value_sz= sizeof(long);
		dty		= SQLT_INT;
		indp	= ind;
		buffers	= count;
	}

	DefinePar(unsigned long& var, OCIInd* ind=NULL)
	{
		valuep	= &var;
		value_sz= sizeof(unsigned long);
		dty		= SQLT_UIN;
		indp	= ind;
		buffers	= 1;
	}
	DefinePar(unsigned long* arr, int count, OCIInd* ind=NULL)
	{
		valuep	= arr;
		value_sz= sizeof(unsigned long);
		dty		= SQLT_UIN;
		indp	= ind;
		buffers	= count;
	}

	DefinePar(double& var, OCIInd* ind=NULL)
	{
		valuep	= &var;
		value_sz= sizeof(double);
		dty		= SQLT_FLT;
		indp	= ind;
		buffers	= 1;
	}
	DefinePar(double* arr, int count, OCIInd* ind=NULL)
	{
		valuep	= arr;
		value_sz= sizeof(double);
		dty		= SQLT_FLT;
		indp	= ind;
		buffers	= count;
	}

#ifdef SQLT_BFLOAT
	DefinePar(float& var, OCIInd* ind=NULL)
	{
		valuep	= &var;
		value_sz= sizeof(float);
		dty		= SQLT_FLT;
		indp	= ind;
		buffers	= 1;
	}
	DefinePar(float* arr, int count, OCIInd* ind=NULL)
	{
		valuep	= arr;
		value_sz= sizeof(float);
		dty		= SQLT_FLT;
		indp	= ind;
		buffers	= count;
	}
#endif

	DefinePar(char& var, OCIInd* ind=NULL)
	{
		valuep	= (void*)&var;
		value_sz= sizeof(char);
		dty		= SQLT_CHR;
		indp	= ind;
		buffers	= 1;
	}

	DefinePar(const TCHAR* buffer, size_t len, ub2 type=SQLT_STR, OCIInd* ind=NULL)
	{
		valuep	= (dvoid*) buffer;
		value_sz= (sb4)len*sizeof(TCHAR);
		dty		= type;
		indp	= ind;
		buffers	= 1;
	}

	DefinePar(SqlString& str)
	{
		valuep	= str.str();
		value_sz= (sb4)str.alen()*sizeof(TCHAR);
		dty		= SQLT_STR;
		indp	= str.ind();
		buffers	= 1;
	}
	DefinePar(SqlStringArray& arr)
	{
		valuep	= arr.str(0);
		value_sz= (sb4)arr.alen()*sizeof(TCHAR);
		dty		= SQLT_STR;
		indp	= arr.ind(0);
		buffers	= arr.get_buffer_count();
	}

/*@todo handle OCIString*
	DefinePar(SqlString& str)
	{
		valuep	= str.str();
		value_sz= (sb4)str.alen()*sizeof(TCHAR);
		dty		= SQLT_VST;
		indp	= str.ind();
		buffers	= 1;
	}
*/

	DefinePar(OciDate& date)
	{
		valuep	= &date;
		value_sz= sizeof(OCIDate);
		dty		= SQLT_ODT;
		indp	= date.ind();
		buffers	= 1;
	}
	DefinePar(OciDateArray& arr)
	{
		valuep	= arr.get_ref(0);
		value_sz= sizeof(OCIDate);
		dty		= SQLT_ODT;
		indp	= arr.ind(0);
		buffers	= arr.get_buffer_count();
	}

	DefinePar(SqlDateTime& date)
	{
		valuep	= date.get_ref();
		value_sz= sizeof(ocidatetime);	// 7
		dty		= SQLT_DAT;
		indp	= date.ind();
		buffers	= 1;
	}
	DefinePar(SqlDateTimeArray& arr)
	{
		valuep	= arr.get_ref(0);
		value_sz= sizeof(ocidatetime);	// 7
		dty		= SQLT_DAT;
		indp	= arr.ind(0);
		buffers	= arr.get_buffer_count();
	}

	DefinePar(SqlNumber& num)
	{
		valuep	= num.get_ref();
		value_sz= sizeof(oraclenumber);	// 22
		dty		= SQLT_VNU;
		indp	= num.ind();
		buffers	= 1;
	}
	DefinePar(SqlNumberArray& arr)
	{
		valuep	= arr.get_ref(0);
		value_sz= sizeof(oraclenumber);	// 22
		dty		= SQLT_VNU;
		indp	= arr.ind(0);
		buffers	= arr.get_buffer_count();
	}

#if ORACLE_VERSION>=81
	DefinePar(OciBlob& blob)
	{
		valuep	= blob.get_ref();
		value_sz= sizeof(OCILobLocator*);
		dty		= SQLT_BLOB;
		indp	= blob.ind();
		buffers	= 1;
	}

	DefinePar(OciClob& clob)
	{
		valuep	= clob.get_ref();
		value_sz= sizeof(OCIClobLocator*);
		dty		= SQLT_CLOB;
		indp	= clob.ind();
		buffers	= 1;
	}
#endif
};


struct LocalOciState
{
	LocalOciState(OciContext& ctx, OCI_STATE state)
	 :	_ctx(ctx)
	{
		_ctx.set_state(state);
	}

	LocalOciState(OciContext& ctx, OCI_STATE state, int stmt_type)
	 :	_ctx(ctx)
	{
		_ctx.set_state(state, stmt_type);
	}

	~LocalOciState()
	{
		_ctx.reset_state();
	}

private:
	OciContext&	_ctx;
};


 /// an OCI SQL statement

struct SqlStatement : public OciHandle<OCIStmt>
{
	typedef OciHandle<OCIStmt> super;

	SqlStatement(OciConnection& conn, int bulk_rows=g_OCIPL_BULK_ROWS)
	 :	super(conn._env),
		_ctx(conn._ctx),
		_state(UNINITIALIZED),
		_last_file(NULL),
		_last_line(-1),
		_res(NULL),
		_stmt_type(-1),
		_bulk_rows(bulk_rows),
		_result_buffers(0),
		_last_row((ub4)-1),
		_last_fetched_row((ub4)-1)
	{
	}

#ifndef FORCE_CODEPOS
	SqlStatement(OciConnection& conn, const tstring& sql, ub4 lang=OCI_NTV_SYNTAX, int bulk_rows=g_OCIPL_BULK_ROWS)
	 :	super(conn._env),
		_ctx(conn._ctx),
		_state(UNINITIALIZED),
		_last_file(NULL),
		_last_line(-1),
		_res(NULL),
		_stmt_type(-1),
		_bulk_rows(bulk_rows),
		_result_buffers(0),
		_last_row((ub4)-1),
		_last_fetched_row((ub4)-1)
	{
		prepare(sql, lang);
	}
#endif

	SqlStatement(OciConnection& conn, const tstring& sql, const char* file, int line, ub4 lang=OCI_NTV_SYNTAX, int bulk_rows=g_OCIPL_BULK_ROWS)
	 :	super(conn._env),
		_ctx(conn._ctx),
		_state(UNINITIALIZED),
		_last_file(NULL),
		_last_line(-1),
		_res(NULL),
		_stmt_type(-1),
		_bulk_rows(bulk_rows),
		_result_buffers(0),
		_last_row((ub4)-1),
		_last_fetched_row((ub4)-1)
	{
		prepare(sql, file, line, lang);
	}

	virtual ~SqlStatement();


protected:

	enum _NO_ALLOC_ {NO_HANDLE_ALLOC};

	SqlStatement(OciConnection& conn, int bulk_rows, _NO_ALLOC_)
	 :	super(conn._env, NULL),
		_ctx(conn._ctx),
		_state(UNINITIALIZED),
		_last_file(NULL),
		_last_line(-1),
		_res(NULL),
		_stmt_type(-1),
		_bulk_rows(bulk_rows),
		_result_buffers(0),
		_last_row((ub4)-1),
		_last_fetched_row((ub4)-1)
	{
	}

	OciContext&	_ctx;

	enum STATE {
		UNINITIALIZED=0,
		PREPARED=1, DEFINED=2, EXECUTED=4, FETCHED=8,
		EOF_DATA=16, STMT_ERROR=32
	};
	int	_state;

	tstring	_last_sql;
	const char*	_last_file;
	int			_last_line;

	struct SqlResult* _res;

	mutable int _stmt_type;

	int	_bulk_rows;
	int	_result_buffers;


public:

	bool eof() const {return _state >= EOF_DATA;}


	 // Preparation of a SQL statement

#ifndef FORCE_CODEPOS
	void	prepare(const tstring& sql, ub4 lang=OCI_NTV_SYNTAX);
#endif

	virtual void prepare(const tstring& sql, const char* file, int line, ub4 lang=OCI_NTV_SYNTAX);


	 // get type of prepared statement

	int get_stmt_type() const;	// return OCI_STMT_SELECT, OCI_STMT_UPDATE, ...


	const tstring& get_last_sql() const
	{
		return _last_sql;
	}

	const char* get_last_file() const
	{
		return _last_file;
	}

	int get_last_line() const
	{
		return _last_line;
	}


	 // get number of fetched rows in the last call

	ub4 fetched_rows() const;


	 // get total number of processed rows of this statement

	ub4 row_count() const;


	 // get size of result buffer in data rows

	int result_buffers() const
	{
		return _result_buffers;
	}


	 // Binding of input variables

	OCIBind* bind_input(int idx, BindInput par, OCIBind* bindp=NULL)				// binding by position
	{
		sword res = OCIBindByPos(_handle/*stmtp*/, &bindp, _env._errh, idx+1,
									const_cast<void*>(par.valuep), par.value_sz, par.dty,
									const_cast<OCIInd*>(par.indp), 0/*alenp*/, 0/*rcodep*/,
									0/*maxarr_len*/, 0/*curelep*/, OCI_DEFAULT);
		oci_check_error(_env, res);

		return bindp;	// Implicitly allocated bind handles are freed automatically with the statement handle.
	}

	OCIBind* bind_input(const tstring& name, BindInput par, OCIBind* bindp=NULL)	// binding by name
	{
		sword res = OCIBindByName(_handle/*stmtp*/, &bindp, _env._errh, (OraText*)name.c_str(), (sb4)name.length()*sizeof(TOraText),
									const_cast<void*>(par.valuep), par.value_sz, par.dty,
									const_cast<OCIInd*>(par.indp), 0/*alenp*/, 0/*rcodep*/,
									0/*maxarr_len*/, 0/*curelep*/, OCI_DEFAULT);
		oci_check_error(_env, res);

		return bindp;	// Implicitly allocated bind handles are freed automatically with the statement handle.
	}


	 // Binding of output variables

	OCIBind* bind_output(int idx, BindOutput par, OCIBind* bindp=NULL)				// binding by position
	{
		sword res = OCIBindByPos(_handle/*stmtp*/, &bindp, _env._errh, idx+1,
									par.valuep, par.value_sz, par.dty,
									par.indp, 0/*alenp*/, 0/*rcodep*/,
									0/*maxarr_len*/, 0/*curelep*/, OCI_DEFAULT);
		oci_check_error(_env, res);

		return bindp;	// Implicitly allocated bind handles are freed automatically with the statement handle.
	}

	OCIBind* bind_output(const tstring& name, BindOutput par, OCIBind* bindp=NULL)	// binding by name
	{
		sword res = OCIBindByName(_handle/*stmtp*/, &bindp, _env._errh, (OraText*)name.c_str(), (sb4)name.length()*sizeof(TOraText),
									par.valuep, par.value_sz, par.dty,
									par.indp, 0/*alenp*/, 0/*rcodep*/,
									0/*maxarr_len*/, 0/*curelep*/, OCI_DEFAULT);
		oci_check_error(_env, res);

		return bindp;	// Implicitly allocated bind handles are freed automatically with the statement handle.
	}

	OCIBind* bind_inout(int idx, BindOutput par, OCIBind* bindp=NULL)
	{
		return bind_output(idx, par, bindp);
	}

	OCIBind* bind_inout(const tstring& name, BindOutput par, OCIBind* bindp=NULL)
	{
		return bind_output(name, par, bindp);
	}


	 // Definition of variables to receive query results

	OCIDefine* bind_result(int idx, DefinePar par, OCIDefine* defnp=NULL);


	 // define all columns of a complete recordset

	void bind_result(struct SqlRecord& rec);


	 // execute a prepared SQL statement

	void execute()
	{
		int rows = get_stmt_type()==OCI_STMT_SELECT? 0: 1;

		execute(rows);
	}

	void execute(ub4 rows, ub4 mode=OCI_DEFAULT);

#ifndef FORCE_CODEPOS
	void execute(const tstring& sql)
	{
		prepare(sql);

		int rows = get_stmt_type()==OCI_STMT_SELECT? 0: 1;

		execute(rows);
	}
#endif

	void execute(const tstring& sql, const char* file, int line)
	{
		prepare(sql, file, line);

		int rows = get_stmt_type()==OCI_STMT_SELECT? 0: 1;

		execute(rows);
	}

#ifndef FORCE_CODEPOS
	void execute(const tstring& sql, ub4 rows)
	{
		prepare(sql);

		execute(rows);
	}
#endif

	void execute(const tstring& sql, const char* file, int line, ub4 rows)
	{
		prepare(sql, file, line);

		execute(rows);
	}

	void ensure_execute()
	{
		if (!(_state & EXECUTED))
			execute();
	}


	 // execute a SQL statement and fetch rows of the result set (default number of rows: _bulk_rows)

	void execute_fetch(ub4 mode=OCI_DEFAULT);

#ifndef FORCE_CODEPOS
	void execute_fetch(const tstring& sql, ub4 lang=OCI_NTV_SYNTAX, ub4 mode=OCI_DEFAULT)
	{
		prepare(sql, lang);

		execute_fetch(mode);
	}
#endif

	void execute_fetch(const tstring& sql, const char* file, int line, ub4 lang=OCI_NTV_SYNTAX, ub4 mode=OCI_DEFAULT)
	{
		prepare(sql, file, line, lang);

		execute_fetch(mode);
	}


	 // fetch the next rows of a result set (default number of rows: _bulk_rows)
	void fetch(ub4 rows=(ub4)-1);


	 // next() is used to iterate through result sets without using bulk mode fetching.

	bool next()
	{
		if (!eof()) {
			fetch(1);

			return fetched_rows() > 0;
		} else
			return false;
	}


	 // utility functions for bulk mode fetching

	int first_bulk()
	{
		execute_fetch();

		return fetched_rows();
	}

	int next_bulk()
	{
		if (!eof()) {
			fetch();

			return fetched_rows();
		} else
			return 0;
	}


	ub4 get_column_count() const
	{
		ub4 count;
		ub4 size = sizeof(count);

		sword res = OCIAttrGet(_handle, super::get_type_id(), &count, &size, OCI_ATTR_PARAM_COUNT, _env._errh);
		oci_check_error(_env, res);

		return count;
	}

	void get_column_type(int idx, ColumnType& info) const;


	void print_results(tostream& out, bool header=true);

	void print_results(const tstring& sql, tostream& out, bool header=true)
	{
		execute(sql, CODEPOS);

		if (!eof())
			print_results(out, header);
	}


	const SqlResult& get_result()
	{
		if (!(_state & EXECUTED))
			execute();

		return *_res;
	}


#ifdef _XMLSTORAGE_H
	void dump_results(XMLStorage::XMLPos& pos, const XMLStorage::XS_String& element_name);
	void dump_results(XMLStorage::XMLWriter& writer, const XMLStorage::XS_String& element_name);

	void dump_results(const tstring& sql, XMLStorage::XMLPos& pos, const XMLStorage::XS_String& element_name)
	{
		execute(sql, CODEPOS);

		if (!eof())
			dump_results(pos, element_name);
	}

	void dump_results(const tstring& sql, XMLStorage::XMLWriter& writer, const XMLStorage::XS_String& element_name)
	{
		execute(sql, CODEPOS);

		if (!eof())
			dump_results(writer, element_name);
	}
#endif


	SqlResult* operator->()
	{
		if (!_res)
			fetch();

		return _res;
	}

protected:
	void execute_internal(ub4 rows, ub4 mode);

	ub4	_last_row;
	ub4	_last_fetched_row;
};


#if ORACLE_VERSION>=90

struct SqlCacheStmt : public SqlStatement
{
	typedef SqlStatement super;

	SqlCacheStmt(OciConnection& conn, int bulk_rows=g_OCIPL_BULK_ROWS)
	 :	super(conn, bulk_rows, NO_HANDLE_ALLOC),
		_stmt_cache(conn._stmt_cache)
	{
	}

#ifndef FORCE_CODEPOS
	SqlCacheStmt(OciConnection& conn, const tstring& sql, ub4 lang=OCI_NTV_SYNTAX, int bulk_rows=g_OCIPL_BULK_ROWS)
	 :	super(conn, bulk_rows, NO_HANDLE_ALLOC),
		_stmt_cache(conn._stmt_cache)
	{
		prepare(sql, NULL, -1, lang);
	}
#endif

	SqlCacheStmt(OciConnection& conn, const tstring& sql, const char* file, int line, ub4 lang=OCI_NTV_SYNTAX, int bulk_rows=g_OCIPL_BULK_ROWS)
	 :	super(conn, bulk_rows, NO_HANDLE_ALLOC),
		_stmt_cache(conn._stmt_cache)
	{
		prepare(sql, file, line, lang);
	}

	virtual ~SqlCacheStmt();

	void	prepare(const tstring& sql, ub4 lang=OCI_NTV_SYNTAX);

	virtual void prepare(const tstring& sql, const char* file, int line, ub4 lang=OCI_NTV_SYNTAX);

	void	release();

protected:
	StatementCache&	_stmt_cache;
	tstring		_tag;
};

#endif


 /// variant array to be used as bind variable buffer

struct SqlVariantArray
{
	SqlVariantArray()
	 :	_data_type(0)
	{
		_punion_ptr = NULL;
	}

	SqlVariantArray(const ColumnTypeInfo& type, int size);
	SqlVariantArray(const SqlVariantArray& other);
	~SqlVariantArray() {clear();}

	void clear();
	void assign(const SqlVariantArray& other);

	ub2 get_data_type() const
		{return _data_type;}

	SqlVariantArray& operator=(const SqlVariantArray& other) {assign(other); return *this;}

	tstring	str(int row, OCIError* errh) const;
	SqlNumber number(int row, OCIError* errh) const;
	OciDate oci_date(int row, OCIError* errh) const;
	SqlDateTime sql_date(int row, OCIError* errh) const;

	bool is_null(int row) const {return !is_not_null(row);}
	bool is_not_null(int row) const;

	void bind_result(SqlStatement& stmt, int idx);

	void set_string(int row, const TCHAR* str);
	void set_number(int row, const SqlNumber& num);
	void set_date(int row, const OciDate& date);
	void set_datetime(int row, const SqlDateTime& datetime);
#ifdef SQLT_BFLOAT
	void set_float(int row, float f);
	void set_double(int row, double d);
#endif

	void bind_input(SqlStatement& stmt, int idx, int row);

protected:
	union {
		SqlStringArray*	_pstring;
		SqlNumberArray*	_pnumber;
		OciDateArray*	_poci_date;
		SqlDateTimeArray*_psql_date;
//		OciBlobArray*	_pblob;
//		OciClobArray*	_pclob;
		void*	_punion_ptr;
#ifdef SQLT_BFLOAT
		float*		_pfloat;
		double*		_pdouble;
#endif
	};

	int _size;
	ub2	_data_type;
};


 /// a record to hold return columns for a given statement

struct SqlRecord
{
	SqlRecord(SqlStatement& stmt, int rows);
	SqlRecord(OciEnv& env, size_t column_cnt) : _env(_env) {_values.resize(column_cnt);}

	~SqlRecord();

	size_t get_column_count() const
		{return _values.size();}

	SqlVariantArray& get_column(int idx)
		{return *_values[idx]._pvar;}
	ColumnType& get_column_type(int idx)
		{return _values[idx]._type;}

	const SqlVariantArray& get_column(int idx) const
		{return *_values[idx]._pvar;}
	const ColumnType& get_column_type(int idx) const
		{return _values[idx]._type;}

	const ColumnType& get_column_type(const tstring& col_name) const;
	const SqlVariantArray& get_column(const tstring& col_name) const;

	void bind_result(SqlStatement& stmt);

	void alloc_column(size_t col_idx, const ColumnTypeInfo& type, int rows); // allocate buffers for bind variables

	void print_columns(tostream& out);
	void print_values(tostream& out, int row);

#ifdef _XMLSTORAGE_H
	void dump_values(XMLStorage::XMLPos& out, int row);
	void dump_values(XMLStorage::XMLWriter& writer, int row);
#endif

protected:
	 /// data structure for the content of single column
	struct Data {
		Data() : _pvar(NULL) {}

		SqlVariantArray* _pvar;
		ColumnType	_type;
#ifdef _DEBUG
		tstring	_col_name;
#endif
	};
	typedef std::vector<Data> ColumnVector;

	ColumnVector _values;

	typedef std::map<tstring, int> NameMap;
	mutable NameMap	_name_map;

	NameMap::const_iterator find_column(const tstring& col_name) const;

public:
	OciEnv& _env;
};


 /// class to retrieve query result values

struct SqlResult : public SqlRecord
{
	SqlResult(SqlStatement& stmt, int rows=1)
	 :	SqlRecord(stmt, rows),
		_env(stmt._env)
	{
		bind_result(stmt);
	}

	bool is_null(int idx, int row) const
	{
		return get_column(idx).is_null(row);
	}
	int is_null(const tstring& col_name, int row) const
	{
		return get_column(col_name).is_null(row);
	}

	bool is_not_null(int idx, int row) const
	{
		return get_column(idx).is_not_null(row);
	}
	int is_not_null(const tstring& col_name, int row) const
	{
		return get_column(col_name).is_not_null(row);
	}

	int get_int(int idx, int row) const
	{
		return get_column(idx).number(row, _env._errh).get_int(_env._errh);
	}
	int get_int(const tstring& col_name, int row) const
	{
		return get_column(col_name).number(row, _env._errh).get_int(_env._errh);
	}

#ifdef _MSC_VER
	LONGLONG get_int64(int idx, int row) const
	{
		return get_column(idx).number(row, _env._errh).get_int64(_env._errh);
	}
	LONGLONG get_int64(const tstring& col_name, int row) const
	{
		return get_column(col_name).number(row, _env._errh).get_int64(_env._errh);
	}
#endif

	double get_double(int idx, int row) const
	{
		return get_column(idx).number(row, _env._errh).get_double(_env._errh);
	}
	double get_double(const tstring& col_name, int row) const
	{
		return get_column(col_name).number(row, _env._errh).get_double(_env._errh);
	}

	SqlNumber get_number(int idx, int row) const
	{
		return get_column(idx).number(row, _env._errh);
	}
	SqlNumber get_number(const tstring& col_name, int row) const
	{
		return get_column(col_name).number(row, _env._errh);
	}

	tstring get_string(int idx, int row) const
	{
		return get_column(idx).str(row, _env._errh);
	}
	tstring get_string(const tstring& col_name, int row) const
	{
		return get_column(col_name).str(row, _env._errh);
	}

	OciDate get_oci_date(int idx, int row) const
	{
		return get_column(idx).oci_date(row, _env._errh);
	}
	OciDate get_oci_date(const tstring& col_name, int row) const
	{
		return get_column(col_name).oci_date(row, _env._errh);
	}

	SqlDateTime get_sql_date(int idx, int row) const
	{
		return get_column(idx).sql_date(row, _env._errh);
	}
	SqlDateTime get_sql_date(const tstring& col_name, int row) const
	{
		return get_column(col_name).sql_date(row, _env._errh);
	}

	OciEnv&	_env;
};


 /// iterator utilizing bulk mode fetching

struct ResultIterator
{
	typedef ResultIterator myType;

	ResultIterator(SqlStatement& stmt)
	 :	_stmt(stmt),
		_row_idx(0),
		_row_nr(1)
	{
		_fetched = stmt.first_bulk();
	}

	int get_row_idx() const
	{
		return _row_idx;
	}

	operator int() const
	{
		return _row_idx;
	}

	int get_row_nr() const
	{
		return _row_nr;
	}

	bool has_next()
	{
		return _row_idx < _fetched;
	}

	myType& operator++()
	{
		if (++_row_idx >= _fetched) {
			_fetched = _stmt.next_bulk();

			if (_fetched) {
				_row_idx = 0;
				++_row_nr;
			}
		} else
			++_row_nr;

		return *this;
	}

	bool is_null(int idx) const
	{
		return _stmt->is_null(idx, _row_idx);
	}
	int is_null(const tstring& col_name) const
	{
		return _stmt->is_null(col_name, _row_idx);
	}

	bool is_not_null(int idx) const
	{
		return _stmt->is_not_null(idx, _row_idx);
	}
	int is_not_null(const tstring& col_name) const
	{
		return _stmt->is_not_null(col_name, _row_idx);
	}

	int get_int(int idx) const
	{
		return _stmt->get_int(idx, _row_idx);
	}
	int get_int(const tstring& col_name) const
	{
		return _stmt->get_int(col_name, _row_idx);
	}

#ifdef _MSC_VER
	LONGLONG get_int64(int idx) const
	{
		return _stmt->get_int64(idx, _row_idx);
	}
	LONGLONG get_int64(const tstring& col_name) const
	{
		return _stmt->get_int64(col_name, _row_idx);
	}
#endif

	double get_double(int idx) const
	{
		return _stmt->get_double(idx, _row_idx);
	}
	double get_double(const tstring& col_name) const
	{
		return _stmt->get_double(col_name, _row_idx);
	}

	SqlNumber get_number(int idx) const
	{
		return _stmt->get_number(idx, _row_idx);
	}
	SqlNumber get_number(const tstring& col_name) const
	{
		return _stmt->get_number(col_name, _row_idx);
	}

	tstring get_string(int idx) const
	{
		return _stmt->get_string(idx, _row_idx);
	}
	tstring get_string(const tstring& col_name) const
	{
		return _stmt->get_string(col_name, _row_idx);
	}

	OciDate get_oci_date(int idx) const
	{
		return _stmt->get_oci_date(idx, _row_idx);
	}
	OciDate get_oci_date(const tstring& col_name) const
	{
		return _stmt->get_oci_date(col_name, _row_idx);
	}

	SqlDateTime get_sql_date(int idx) const
	{
		return _stmt->get_sql_date(idx, _row_idx);
	}
	SqlDateTime get_sql_date(const tstring& col_name) const
	{
		return _stmt->get_sql_date(col_name, _row_idx);
	}

protected:
	SqlStatement&	_stmt;

	int	_fetched;
	int	_row_idx;
	int	_row_nr;
};


inline void SqlStatement::bind_result(SqlRecord& rec)
	{rec.bind_result(*this);}


#if ORACLE_VERSION>=81

struct BlobWriterBuffer : public tstringbuf
{
	typedef tstringbuf super;
	typedef OciBlob LOB;

	BlobWriterBuffer(LOB& lob)
	 :	_lob(lob)
	{
		_offset = 1;
	}

	virtual ~BlobWriterBuffer()
    {
        sync();
    }

	virtual int sync()
	{
		const tstring& s = str();
		size_t l = s.length();

		if (l > 0) {
#if ORACLE_VERSION>=90
			_lob.write_append((const void*)s.c_str(), (sb4)l*sizeof(TOraText), (sb4)l);
#else
			_lob.write((const void*)s.c_str(), (ub4)l, (ub4)_offset, (ub4)l);
#endif

			_offset += l;
		}

		str(tstring());	// clear the string buffer

		return 0;
	}

protected:
	LOB&	_lob;
	size_t	_offset;
};

struct ClobWriterBuffer : public tstringbuf
{
	typedef tstringbuf super;
	typedef OciClob LOB;

	ClobWriterBuffer(LOB& lob, ub2 csid=0, ub1 csfrm=SQLCS_IMPLICIT)
	 :	_lob(lob),
		_csid(csid),
		_csfrm(csfrm)
	{
	}

	virtual ~ClobWriterBuffer()
    {
        sync();
    }

	virtual int sync()
	{
		const tstring& s = str();
		size_t l = s.length();

		if (l > 0)
			_lob.write_append((const void*)s.c_str(), (ub4)l*sizeof(TOraText), (ub4)l, _csid, _csfrm);

		str(tstring());	// clear the string buffer

		return 0;
	}

protected:
	LOB&	_lob;
	ub2		_csid;
	ub1		_csfrm;
};

template<typename BUFFER>
  struct LobWriter : public tostream
{
	typedef tostream super;

	LobWriter(typename BUFFER::LOB& lob)
	 :	super(&_buffer),
		_buffer(lob)
	{
	}

	LobWriter(typename BUFFER::LOB& lob, ub2 csid, ub1 csfrm=SQLCS_IMPLICIT)
	 :	super(&_buffer),
		_buffer(lob, csid, csfrm)
	{
	}

	void sync()
	{
		_buffer.sync();
	}

protected:
	BUFFER	_buffer;
};

typedef LobWriter<BlobWriterBuffer> BlobWriter;
typedef LobWriter<ClobWriterBuffer> ClobWriter;


struct BlobReaderBuffer : public tstringbuf
{
	typedef tstringbuf super;
	typedef OciBlob LOB;

	BlobReaderBuffer(LOB& lob, ub4 offset=1)
	 :	_lob(lob),
		_offset(offset)
	{
		_bsize = lob.get_chunk_size();
		_buffer = (TCHAR*) ocipl_alloc(_bsize*sizeof(TCHAR));
	}

	~BlobReaderBuffer()
	{
		ocipl_free(_buffer);
	}

	virtual int_type underflow()
	{
		ub4 amount = _lob.read(_buffer, _bsize, _offset, _bsize);
		if (amount <= 0)
			return -1;

		setg(_buffer, _buffer, _buffer+amount);
		_offset += amount;

		return *_buffer;
	}

protected:
	LOB&	_lob;
	ub4		_offset;
	TCHAR*	_buffer;
	int		_bsize;
};

struct ClobReaderBuffer : public tstringbuf
{
	typedef tstringbuf super;
	typedef OciClob LOB;

	ClobReaderBuffer(LOB& lob, ub4 offset=1, ub2 csid=0, ub1 csfrm=SQLCS_IMPLICIT)
	 :	_lob(lob),
		_offset(offset),
		_csid(csid),
		_csfrm(csfrm)
	{
		_bsize = lob.get_chunk_size();
		_buffer = (TCHAR*) ocipl_alloc(_bsize*sizeof(TCHAR));
	}

	~ClobReaderBuffer()
	{
		ocipl_free(_buffer);
	}

	virtual int_type underflow()
	{
		ub4 amount = _lob.read(_buffer, _bsize, _offset, _bsize, _csid, _csfrm);
		if (amount <= 0)
			return -1;

		setg(_buffer, _buffer, _buffer+amount);
		_offset += amount;

		return *_buffer;
	}

protected:
	LOB&	_lob;
	ub4		_offset;
	TCHAR*	_buffer;
	int		_bsize;
	ub2		_csid;
	ub1		_csfrm;
};

template<typename BUFFER>
  struct LobReader : public tistream
{
	typedef tistream super;

	LobReader(typename BUFFER::LOB& lob)
	 :	super(&_buffer),
		_buffer(lob)
	{
	}

	LobReader(typename BUFFER::LOB& lob, ub2 csid, ub1 csfrm=SQLCS_IMPLICIT)
	 :	super(&_buffer),
		_buffer(lob, csid, csfrm)
	{
	}

protected:
	BUFFER	_buffer;
};

typedef LobReader<BlobReaderBuffer> BlobReader;
typedef LobReader<ClobReaderBuffer> ClobReader;

#endif



extern const TCHAR* get_oci_ptype_str(ub1 type);

} // namespace OCIPL

#define	_OCIPL_H
#endif	// _OCIPL_H
