// // ocipl.h // // Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Martin Fuchs // // 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 #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 #define _stprintf_s1(b, l, f, p1) _stprintf(b, f, p1) #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 #include #include #include #include #include #include #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 #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 #else // _WIN32 #include #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 #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 #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 struct OciHandleID { static ub4 get_type_id(); static ub4 get_attr_id(); }; inline ub4 OciHandleID::get_type_id() {return OCI_HTYPE_ENV;} inline ub4 OciHandleID::get_type_id() {return OCI_HTYPE_ERROR;} inline ub4 OciHandleID::get_type_id() {return OCI_HTYPE_SVCCTX;} inline ub4 OciHandleID::get_type_id() {return OCI_HTYPE_STMT;} inline ub4 OciHandleID::get_type_id() {return OCI_HTYPE_BIND;} inline ub4 OciHandleID::get_type_id() {return OCI_HTYPE_DEFINE;} inline ub4 OciHandleID::get_type_id() {return OCI_HTYPE_DESCRIBE;} inline ub4 OciHandleID::get_type_id() {return OCI_HTYPE_SERVER;} inline ub4 OciHandleID::get_type_id() {return OCI_HTYPE_SESSION;} inline ub4 OciHandleID::get_type_id() {return OCI_HTYPE_TRANS;} inline ub4 OciHandleID::get_type_id() {return OCI_HTYPE_COMPLEXOBJECT;} inline ub4 OciHandleID::get_type_id() {return OCI_HTYPE_SECURITY;} #if ORACLE_VERSION>=81 inline ub4 OciHandleID::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::get_attr_id() {return OCI_ATTR_SERVER;} inline ub4 OciHandleID::get_attr_id() {return OCI_ATTR_SESSION;} /// "simple" OCI handles without OciEnv template struct OciSimpleHandle : public OciHandleID { typedef OciHandleID 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 struct OciHandleWrapper : public OciHandleID { 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; /// OCI Describe Handles typedef OciSimpleHandle OciDescribe; /// OCI environment handles struct OciEnv : public OciHandleWrapper { typedef OciHandleWrapper 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 { typedef OciSimpleHandle 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 struct OciHandle : public OciHandleID { typedef OciHandleID 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 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 struct OciDescriptor : public OciHandleID { typedef OciHandleID 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::get_type_id() {return OCI_DTYPE_PARAM;} typedef OciDescriptor 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 { typedef OciHandle 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 { typedef OciHandleWrapper 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 { typedef OciHandle 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) { _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 _session; bool _connected; }; #if ORACLE_VERSION>=90 /// a map to cache SQL statements struct StatementCache : public std::map { }; #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 OciBindHandle; typedef OciHandle 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 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 = # 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 { typedef OciHandle 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(par.valuep), par.value_sz, par.dty, const_cast(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(par.valuep), par.value_sz, par.dty, const_cast(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_param(int idx, BindInput par, OCIBind* bindp=NULL) { return bind_input(idx, par, bindp); } OCIBind* bind_param(const tstring& name, BindInput par, OCIBind* bindp=NULL) { return bind_input(name, par, bindp); } */ // 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 ColumnVector; ColumnVector _values; typedef std::map 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 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 BlobWriter; typedef LobWriter 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 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 BlobReader; typedef LobReader ClobReader; #endif extern const TCHAR* get_oci_ptype_str(ub1 type); } // namespace OCIPL #define _OCIPL_H #endif // _OCIPL_H