// // ocipl.cpp // // Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Martin Fuchs // // OCIPL Version 1.7 // /// \file ocipl.cpp /// OCIPL implementation 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_NO_COMMENT #define OCIPL_NO_COMMENT // no #pragma comment(lib, ...) statements in .lib files -> free selection of orasql library #endif #define _olint // for #define CONST const #include "ocipl.h" #include #include #include #include #ifndef _MSC_VER char* strupr(char* s) { for(char*p=s; *p; ++p) *p = toupper(*p); return s; } #endif size_t max_strlen(const char* s, size_t max_len) { if (!s) return 0; size_t cnt = 0; while(cnt) #else std::use_facet >(std::locale::empty()) #endif .narrow((const TCHAR*)buffer, (const TCHAR*)buffer+_tcslen((const TCHAR*)buffer)+1, 0, nbuffer); _msg = max_strdup(nbuffer, COUNTOF(nbuffer)); #else _msg = max_strdup(buffer, COUNTOF(buffer)); #endif } else { _sql_error_code = 0; _msg = NULL; } _file = file; _line = line; _parse_offset = 0; } OciException::OciException(OCIError* errh, SqlStatement& stmt, const char* file, int line) { char buffer[4096]; sb4 errorcode; #ifdef _UNICODE TCHAR wbuffer[4096]; sword res = OCIErrorGet(errh, 1, NULL, &errorcode, (OraText*)wbuffer, sizeof(wbuffer), OCI_HTYPE_ERROR); #ifdef _MSC_VER std::_USE(std::locale::empty(), std::ctype) #else std::use_facet >(std::locale::empty()) #endif .narrow((const TCHAR*)wbuffer, (const TCHAR*)wbuffer+_tcslen((const TCHAR*)wbuffer)+1, 0, buffer); #else sword res = OCIErrorGet(errh, 1, NULL, &errorcode, (OraText*)buffer, sizeof(buffer), OCI_HTYPE_ERROR); #endif ub4 size = sizeof(_parse_offset); if (OCIAttrGet(stmt, OCI_HTYPE_STMT, &_parse_offset, &size, OCI_ATTR_PARSE_ERROR_OFFSET, errh) != OCI_SUCCESS) _parse_offset = 0; if (res == OCI_SUCCESS) _sql_error_code = errorcode; else { *buffer = '\0'; _sql_error_code = 0; } _last_sql = stmt.get_last_sql(); _file = file; _line = line; if (!_last_sql.empty()) { file = stmt.get_last_file(); // copy source code position from statement if available if (file) { _file = file; _line = stmt.get_last_line(); } const TCHAR* sql = _last_sql.c_str(); size_t l = max_strlen(buffer, COUNTOF(buffer)); int line = 1; const TCHAR* s = sql; const TCHAR* e = sql + _parse_offset; const TCHAR* lp = sql; while(s < e) if (*s == '\n') { ++line; if (*++s == '\r') ++s; lp = s; } else ++s; size_t column = 1 + (s-lp); #ifdef __STDC_WANT_SECURE_LIB__ char* b = buffer+l + _snprintf_s(buffer+l, sizeof(buffer)-l, _TRUNCATE, "\nerror at SQL position %d [%d.%d]:\n", _parse_offset, line, column); #else char* b = buffer+l + _snprintf(buffer+l, sizeof(buffer)-l-1, "\nerror at SQL position %d [%d.%d]:\n", _parse_offset, line, column); #endif char* p = b; s = lp; while(*s && *s!='\n' && p #ifdef _MSC_VER #if ORACLE_VERSION>=110 #pragma comment(lib, "orasql11") #elif ORACLE_VERSION>=100 #pragma comment(lib, "orasql10") #elif ORACLE_VERSION>=90 #pragma comment(lib, "orasql9") #elif ORACLE_VERSION>=81 // no orasql library for Oracle 8.0 available #pragma comment(lib, "orasql8") #endif #endif #if ORACLE_VERSION>=81 // no SQLEnvGet() and SQLSvcCtxGet() for Oracle 8.0.x OciConnection* OciConnection::CreateFromESqlContext(void* context) { OCIEnv* envhp = NULL; sword res = SQLEnvGet(context, &envhp); if (res != OCI_SUCCESS) return NULL; OCISvcCtx* svchp = NULL; res = SQLSvcCtxGet(context, NULL, 0, &svchp); oci_check_error(envhp, res); return new OciConnection(envhp, svchp); } #endif #endif // _NO_ESQL /// cancel a pending OCI call in the worker thread void OciConnection::cancel() { sword res = OCIBreak(_ctx, _env._errh); oci_check_error(_env._errh, res); } #if ORACLE_VERSION>=81 /// reset() is to be called after handling the cancellation in the calling worker thread. void OciConnection::reset() { sword res = OCIReset(_ctx, _env._errh); oci_check_error(_env._errh, res); } #endif // commit the current transaction void OciContext::commit(ub4 flags) { LocalOciState state(*this, OS_TRANSACTION); sword res = OCITransCommit(_handle, _env._errh, flags); oci_check_error(_env, res); _trans_cnt = 0; } // rollback the current transaction void OciContext::rollback(ub4 flags) { LocalOciState state(*this, OS_TRANSACTION); sword res = OCITransRollback(_handle, _env._errh, flags); oci_check_error(_env, res); _trans_cnt = 0; } SqlString& SqlString::operator=(const TCHAR* s) { size_t l = s? _tcslen(s): 0; if (l > _blen) resize(l); // resize for longer new strings _tcscpy_s(_str, _blen+1, s?s:TEXT("")); _ind = l>0? 0: -1; return *this; } SqlString& SqlString::operator=(const tstring& s) { if (!s.empty()) { size_t l = s.length(); if (_blen < l) resize(l); // resize for longer new strings memcpy(_str, s.c_str(), (l+1)*sizeof(char)); _ind = 0; } else { *_str = '\0'; _ind = -1; } return *this; } int number_to_int(const oraclenumber& val, OCIError* errh) { int x; #if ORACLE_VERSION>=81 // Oracle Version >= 8.1.x boolean isInt; sword res = OCINumberIsInt(errh, (OCINumber*)&val, &isInt); if (res==OCI_SUCCESS && isInt) { res = OCINumberToInt(errh, (OCINumber*)&val, sizeof(x), OCI_NUMBER_SIGNED, &x); oci_check_error(errh, res); } else #else // OCI.DLL of Oracle Version 8.0 sword res = OCINumberToInt(errh, (OCINumber*)&val, sizeof(x), OCI_NUMBER_SIGNED, &x); if (res != OCI_SUCCESS) #endif { double d = number_to_double(val, errh); x = int(d + .5); } //x = atoi(str(true).c_str()); return x; } #ifdef _MSC_VER LONGLONG number_to_int64(const oraclenumber& val, OCIError* errh, int null) { LONGLONG x; sword res = OCINumberToInt(errh, (OCINumber*)&val, sizeof(x), OCI_NUMBER_SIGNED, &x); oci_check_error(errh, res); return x; } #endif double number_to_double(const oraclenumber& val, OCIError* errh) { double x; sword res = OCINumberToReal(errh, (OCINumber*)&val, sizeof(x), &x); oci_check_error(errh, res); return x; } tstring number_to_str(const oraclenumber& val, OCIError* errh, const TCHAR* fmt, const TCHAR* nls_fmt) { OraText buffer[128]; ub4 l = sizeof(buffer); sword res = OCINumberToText(errh, (OCINumber*)&val, (const OraText*)fmt, (ub4)_tcslen(fmt)*sizeof(TOraText), (const OraText*)nls_fmt, (ub4)_tcslen(nls_fmt)*sizeof(TOraText), &l, buffer); oci_check_error(errh, res); // const TCHAR* p = (const TCHAR*)buffer; // while(istspace(*p)) // ++p; return (const TCHAR*)buffer;//p } SqlNumber::SqlNumber(const oraclenumber* pnum) { memcpy(&_val.exp, &pnum->exp, pnum->len); _val.len = pnum->len; _ind.set(); } SqlNumber::SqlNumber(OCIError* errh, const TCHAR* str, const TCHAR* fmt, const TCHAR* nls_fmt) { if (str) { while(istspace(*str)) ++str; if (*str) { sword res = OCINumberFromText(errh, (const OraText*)str, (ub4)_tcslen(str)*sizeof(TOraText), (const OraText*)fmt, (ub4)_tcslen(fmt)*sizeof(TOraText), (const OraText*)nls_fmt, (ub4)_tcslen(nls_fmt)*sizeof(TOraText), (OCINumber*)&_val); oci_check_error(errh, res); _ind.set(); } else _ind.clear(); } else _ind.clear(); } /* Oracle stores values of the NUMBER datatype in a variable-length format. The first byte is the exponent and is followed by 1 to 20 mantissa bytes. The high-order bit of the exponent byte is the sign bit; it is set for positive numbers and it is cleared for negative numbers. The lower 7 bits represent the exponent, which is a base-100 digit with an offset of 65. To calculate the decimal exponent, add 65 to the base-100 exponent and add another 128 if the number is positive. If the number is negative, you do the same, but subsequently the bits are inverted. For example, -5 has a base-100 exponent = 62 (0x3e). The decimal exponent is thus (~0x3e) -128 - 65 = 0xc1 -128 -65 = 193 -128 -65 = 0. Each mantissa byte is a base-100 digit, in the range 1 to 100. For positive numbers, the digit has 1 added to it. So, the mantissa digit for the value 5 is 6. For negative numbers, instead of adding 1 the digit is subtracted from 101. So, the mantissa digit for the number -5 is 96 (101 - 5). Negative numbers have a byte containing 102 appended to the data bytes. However, negative numbers that have 20 mantissa bytes do not have the trailing 102 byte. Because the mantissa digits are stored in base-100, each byte can represent two decimal digits. The mantissa is normalized; leading zeroes are not stored. Up to 20 data bytes can represent the mantissa. However, only 19 are guaranteed to be accurate. The 19 data bytes, each representing a base-100 digit, yield a maximum precision of 38 digits for an internal datatype NUMBER. */ /* tstring SqlNumber::str(bool internal) const { int sign = _val.exp & 0x80; int exp; tstring ret = sign? internal?TEXT(" "):TEXT(""): TEXT("-"); if (sign) { if (_val.exp == 0x80) return TEXT("0"); exp = (_val.exp & 0x7F) - 65; } else exp = 62 - (_val.exp & 0x7F); if (exp < 0) { ret.append(TEXT("0.")); for(int e=exp; ++e<0; ) ret.append(1, '0'); } const ub1* p = _val.mant; if (sign) { for(int n=_val.len; ; ) { int x = *p++ - 1; if (internal || x/10 || !ret.empty()) // don't print leading zeros ret.append(1, '0' + x/10); ret.append(1, '0' + x%10); --exp; if (--n <= 1) break; if (exp == -1) ret.append(1, '.'); } } else { for(int n=_val.len; ; ) { int x = 101 - *p++; ret.append(1, '0' + x/10); ret.append(1, '0' + x%10); --exp; if (--n <= 2) break; if (exp == -1) ret.append(1, '.'); } if (_val.len == 21) { int x = 101 - *p++; ret.append(1, '0' + x/10); ret.append(1, '0' + x%10); --exp; }// else // assert(*p==102); } if (internal) for(; exp>=0; --exp) ret.append(TEXT("00")); else if (_tcschr(ret.c_str(), '.')) { // remove trailing zeros while(!ret.empty() && ret.at(ret.length()-1)=='0') ret.resize(ret.length()-1); if (!ret.empty() && ret.at(ret.length()-1)=='.') ret.resize(ret.length()-1); } return ret; } */ tstring ocidate_to_string(const OCIDate& date) // Usage of OCIDateToText would also be possible. { TCHAR buffer[40]; #ifdef __STDC_WANT_SECURE_LIB__ _stprintf_s(buffer, COUNTOF(buffer), #else _stprintf(buffer, #endif TEXT("%02d.%02d.%04d %02d:%02d:%02d"), date.OCIDateDD, date.OCIDateMM, date.OCIDateYYYY, date.OCIDateTime.OCITimeHH, date.OCIDateTime.OCITimeMI, date.OCIDateTime.OCITimeSS); return buffer; } tstring ocidate_to_short_string(const OCIDate& date) { TCHAR buffer[40]; #ifdef __STDC_WANT_SECURE_LIB__ _stprintf_s(buffer, COUNTOF(buffer), #else _stprintf(buffer, #endif TEXT("%02d.%02d.%04d"), date.OCIDateDD, date.OCIDateMM, date.OCIDateYYYY); return buffer; } struct tm* ocidate_to_gmt(const OCIDate& date) { struct tm ltime = { date.OCIDateTime.OCITimeSS, date.OCIDateTime.OCITimeMI, date.OCIDateTime.OCITimeHH, date.OCIDateDD, date.OCIDateMM - 1, date.OCIDateYYYY - 1900, -1, -1, -1 }; time_t time = mktime(<ime); #ifdef __STDC_WANT_SECURE_LIB__ struct tm newtime; gmtime_s(&newtime, &time); struct tm* gmt = &newtime; #else struct tm* gmt = gmtime(&time); #endif return gmt; } struct tm* ocidate_to_lctime(const OCIDate& date) { struct tm ltime = { date.OCIDateTime.OCITimeSS, date.OCIDateTime.OCITimeMI, date.OCIDateTime.OCITimeHH, date.OCIDateDD, date.OCIDateMM - 1, date.OCIDateYYYY - 1900, -1, -1, -1 }; time_t time = mktime(<ime); #ifdef __STDC_WANT_SECURE_LIB__ struct tm newtime; localtime_s(&newtime, &time); struct tm* lct = &newtime; #else struct tm* lct = localtime(&time); #endif return lct; } time_t ocidate_to_time_t(const OCIDate& date) { time_t time = mktime(ocidate_to_lctime(date)); return time; } #ifdef _WIN32 bool ocidate_to_systemtime(const OCIDate& date, SYSTEMTIME* pSysTime) { SYSTEMTIME stime; stime.wYear = date.OCIDateYYYY; stime.wMonth = date.OCIDateMM; stime.wDayOfWeek = 0; stime.wDay = date.OCIDateDD; stime.wHour = date.OCIDateTime.OCITimeHH; stime.wMinute = date.OCIDateTime.OCITimeMI; stime.wSecond = date.OCIDateTime.OCITimeSS; stime.wMilliseconds = 0; // Validate date and fill the wDayOfWeek field FILETIME ftime; if (!SystemTimeToFileTime(&stime, &ftime)) return false; if (!FileTimeToSystemTime(&ftime, pSysTime)) return false; return true; } #endif tstring ocidatetime_to_string(const ocidatetime& date) { TCHAR buffer[40]; #ifdef __STDC_WANT_SECURE_LIB__ _stprintf_s(buffer, COUNTOF(buffer), #else _stprintf(buffer, #endif TEXT("%02d.%02d.%04d %02d:%02d:%02d"), date.day, date.month, 100*(date.century-100)+(date.year-100), date.hour-1, date.minute-1, date.second-1); return buffer; } OciDate::OciDate(const OCIDate& date, OCIInd ind) : SqlValue(ind) { OCIDate::operator=(date); } OciDate::OciDate(OCIError* errh, const TCHAR* str, const TCHAR* fmt) { if (str) { while(istspace(*str)) ++str; if (*str) { sword res = OCIDateFromText(errh, (const OraText*)str, (ub4)_tcslen(str)*sizeof(TOraText), (const OraText*)fmt, (ub4)_tcslen(fmt)*sizeof(TOraText), NULL/*lang*/, 0, this); oci_check_error(errh, res); _ind.set(); } else _ind.clear(); } else _ind.clear(); } OciDate::OciDate(struct tm* t) { OCIDateYYYY = t->tm_year + 1900; OCIDateMM = t->tm_mon + 1; OCIDateDD = t->tm_mday; OCIDateTime.OCITimeHH = t->tm_hour; OCIDateTime.OCITimeMI = t->tm_min; OCIDateTime.OCITimeSS = t->tm_sec; ind().set(); } int OciDate::compare(const OciDate& other) const { if (OCIDateYYYY < other.OCIDateYYYY) return -1; else if (OCIDateYYYY > other.OCIDateYYYY) return 1; if (OCIDateMM < other.OCIDateMM) return -1; else if (OCIDateMM > other.OCIDateMM) return 1; if (OCIDateDD < other.OCIDateDD) return -1; else if (OCIDateDD > other.OCIDateDD) return 1; if (OCIDateTime.OCITimeHH < other.OCIDateTime.OCITimeHH) return -1; else if (OCIDateTime.OCITimeHH > other.OCIDateTime.OCITimeHH) return 1; if (OCIDateTime.OCITimeMI < other.OCIDateTime.OCITimeMI) return -1; else if (OCIDateTime.OCITimeMI > other.OCIDateTime.OCITimeMI) return 1; if (OCIDateTime.OCITimeSS < other.OCIDateTime.OCITimeSS) return -1; else if (OCIDateTime.OCITimeSS > other.OCIDateTime.OCITimeSS) return 1; return 0; } tstring ColumnTypeInfo::get_type_str(bool show_null) const { tostringstream str; switch(_data_type) { case OCI_TYPECODE_CHAR: str << TEXT("CHAR(") << _width << TEXT(")"); break; case OCI_TYPECODE_VARCHAR: str << TEXT("VARCHAR(") << _width << TEXT(")"); break; case OCI_TYPECODE_VARCHAR2: str << TEXT("VARCHAR2(") << _width << TEXT(")"); break; case OCI_TYPECODE_NUMBER: if (_precision == 0) str << TEXT("NUMBER"); else if (_scale == -127) { if (_precision == 126) str << TEXT("FLOAT"); else str << TEXT("FLOAT(") << (int)_precision << TEXT(")"); } else if (_scale) str << TEXT("NUMBER(") << (int)_precision << TEXT(",") << (int)_scale << TEXT(")"); else str << TEXT("NUMBER(") << (int)_precision << TEXT(")"); break; #ifdef SQLT_BFLOAT case OCI_TYPECODE_BFLOAT: str << TEXT("BINARY_FLOAT"); break; case OCI_TYPECODE_BDOUBLE: str << TEXT("BINARY_DOUBLE"); break; #endif case OCI_TYPECODE_DATE: str << TEXT("DATE"); break; #if ORACLE_VERSION>=81 case OCI_TYPECODE_TIMESTAMP: str << TEXT("TIMESTAMP"); break; #endif case OCI_TYPECODE_BLOB: str << TEXT("BLOB"); break; case OCI_TYPECODE_BFILE: str << TEXT("BFILE"); break; case OCI_TYPECODE_CLOB: str << TEXT("CLOB"); break; case OCI_TYPECODE_CFILE: str << TEXT("CFILE"); break; case SQLT_LNG: str << TEXT("LONG"); break; default: str << TEXT("unknown"); } if (show_null) if (!_nullable) str << TEXT(" NOT NULL"); else str << TEXT(" NULL"); return str.str(); } SqlStatement::~SqlStatement() { delete _res; _res = NULL; } #ifndef FORCE_CODEPOS void SqlStatement::prepare(const tstring& sql, ub4 lang) { prepare(sql, NULL, -1, lang); } #endif void SqlStatement::prepare(const tstring& sql, const char* file, int line, ub4 lang) { delete _res; _res = NULL; _last_sql = sql; _last_file = file; _last_line = line; LocalOciState state(_ctx, OS_PREPARE); sword res = OCIStmtPrepare(_handle/*stmtp*/, _env._errh, (OraText*)sql.c_str(), (ub4)sql.length()*sizeof(TOraText), lang, OCI_DEFAULT); oci_check_error(_env, res); _state = PREPARED; _stmt_type = -1; _result_buffers = 0; } #if ORACLE_VERSION>=90 #ifndef FORCE_CODEPOS void SqlCacheStmt::prepare(const tstring& sql, ub4 lang) { prepare(sql, NULL, -1, lang); } #endif void SqlCacheStmt::prepare(const tstring& sql, const char* file, int line, ub4 lang) { if (!_tag.empty()) release(); tstring& tag = _stmt_cache[sql]; if (tag.empty()) { TCHAR buffer[16]; #ifdef __STDC_WANT_SECURE_LIB__ _stprintf_s(buffer, COUNTOF(buffer), #else _stprintf(buffer, #endif TEXT("%x"), _stmt_cache.size()); tag = buffer; } _last_sql = sql; _last_file = file; _last_line = line; _tag = tag; LocalOciState state(_ctx, OS_PREPARE); sword res = OCIStmtPrepare2(_ctx, &_handle/*stmtp*/, _env._errh, (OraText*)sql.c_str(), (ub4)sql.length()*sizeof(TOraText), (OraText*)_tag.c_str(), (ub4)_tag.length()*sizeof(TOraText), lang, OCI_DEFAULT); oci_check_error(_env, res); _state = PREPARED; _stmt_type = -1; _result_buffers = 0; } void SqlCacheStmt::release() { delete _res; _res = NULL; LocalOciState state(_ctx, OS_RELEASE); sword res = OCIStmtRelease(_handle, _env._errh, (OraText*)_tag.c_str(), (ub4)_tag.length()*sizeof(TOraText), OCI_DEFAULT); _handle = 0; oci_check_error(_env, res); } SqlCacheStmt::~SqlCacheStmt() { release(); } #endif int SqlStatement::get_stmt_type() const { if (_stmt_type == -1) { ub2 stmt_type; // OCI_STMT_SELECT, OCI_STMT_UPDATE, ... ub4 size = sizeof(stmt_type); sword res = OCIAttrGet(_handle/*stmtp*/, get_type_id(), &stmt_type, &size, OCI_ATTR_STMT_TYPE, _env._errh); oci_check_error(_env, res); _stmt_type = stmt_type; } return _stmt_type; } ub4 SqlStatement::fetched_rows() const { /* if ORACLE_VERSION>=90 ub4 row_count; ub4 size = sizeof(row_count); sword res = OCIAttrGet(_handle, get_type_id(), &row_count, &size, OCI_ATTR_ROWS_FETCHED, _env._errh); oci_check_error(_env, res); return row_count; else */ return _last_fetched_row - _last_row; } ub4 SqlStatement::row_count() const { ub4 row_count; ub4 size = sizeof(row_count); sword res = OCIAttrGet(_handle/*stmtp*/, get_type_id(), &row_count, &size, OCI_ATTR_ROW_COUNT, _env._errh); oci_check_error(_env, res); return row_count; } OCIDefine* SqlStatement::bind_result(int idx, DefinePar par, OCIDefine* defnp) { sword res = OCIDefineByPos(_handle/*stmtp*/, &defnp, _env._errh, idx+1, par.valuep, par.value_sz, par.dty, par.indp, (ub2*)0, (ub2*)0, OCI_DEFAULT); oci_check_error(_env, res); if (_state & DEFINED) assert(_result_buffers==par.buffers); // check if all bind variables have the same buffer count if (_state & EXECUTED) { delete _res; _res = NULL; _state = (_state|DEFINED) & ~(FETCHED|EOF_DATA); } else _state |= DEFINED; _result_buffers = par.buffers; return defnp; // Implicitly allocated define handles are freed automatically with the statement handle. } void SqlStatement::execute(ub4 rows, ub4 mode) { // a SELECT statement without ready defined variables? if (!(_state&DEFINED) && get_stmt_type()==OCI_STMT_SELECT) { execute_internal(0, mode); // define resultset variables assert(!_res); if (rows > 0) { _res = new SqlResult(*this, rows); fetch(rows); } else { _res = new SqlResult(*this, _bulk_rows); } } else execute_internal(rows, mode); } void SqlStatement::execute_fetch(ub4 mode) { // A SELECT statement without ready defined variables? if (!(_state&DEFINED) && get_stmt_type()==OCI_STMT_SELECT) { execute_internal(0, mode); // define resultset variables assert(!_res); _res = new SqlResult(*this, _bulk_rows); fetch(_result_buffers); } else execute_internal(_result_buffers, mode); } void SqlStatement::execute_internal(ub4 rows, ub4 mode) { int stmt_type = get_stmt_type(); LocalOciState state(_ctx, OS_EXECUTE, stmt_type); if (rows==0 && stmt_type==OCI_STMT_SELECT) { // Optimizing of TCP packets by transfering multiple datasets in one packet try { //set_attribute(OCI_ATTR_PREFETCH_MEMORY, 1500); set_attribute(OCI_ATTR_PREFETCH_ROWS, 10); } catch(std::exception&) { // ignore exception of this optional performance tuning } } // execute and fetch sword res = OCIStmtExecute(_ctx, _handle, _env._errh, rows, 0/*rowoff*/, (CONST OCISnapshot*)0, (OCISnapshot*)0, mode); _last_row = 0; _last_fetched_row = row_count(); if (res==OCI_NO_DATA && stmt_type>=OCI_STMT_SELECT && stmt_type<=OCI_STMT_INSERT) { _state |= EOF_DATA; // There are no additional rows pending to be fetched. } else if (res == OCI_ERROR) { _state = STMT_ERROR; //throw OCI_EXCEPTION(_env._errh, *this, CODEPOS); throw_ocipl_exception(OciException(_env._errh, *this, CODEPOS)); } else { if (rows > 0) _state |= EXECUTED|FETCHED; else _state = (_state|EXECUTED) & ~(FETCHED|EOF_DATA); oci_check_error(_env, res); // There may be more rows available to be fetched (for queries) or the DML statement succeeded. } } void SqlStatement::fetch(ub4 rows/*=-1*/) { if (!(_state & EXECUTED)) { if (rows == (ub4)-1) if (!(_state & DEFINED)) rows = _bulk_rows; // optimize using bulk fetch else rows = _result_buffers; execute(rows); } else { if (rows == (ub4)-1) rows = _result_buffers; LocalOciState state(_ctx, OS_FETCH); #if ORACLE_VERSION>=90 sword res = OCIStmtFetch2(_handle, _env._errh, rows, OCI_FETCH_NEXT, 0, OCI_DEFAULT); #else sword res = OCIStmtFetch(_handle, _env._errh, rows, OCI_FETCH_NEXT, OCI_DEFAULT); #endif _last_row = _last_fetched_row; _last_fetched_row = row_count(); if (res != OCI_NO_DATA) { // more data available or did there occur an error? _state |= FETCHED; oci_check_error(_env, res); // There may be more rows available to be fetched. } else { _state |= EOF_DATA; // There are no additional rows pending to be fetched. } } } void SqlStatement::get_column_type(int idx, ColumnType& info) const { dvoid* parmdp; sword res = OCIParamGet(_handle, get_type_id(), _env._errh, &parmdp, idx+1); oci_check_error(_env, res); info.init(_env._errh, parmdp); res = OCIDescriptorFree(parmdp, OCI_DTYPE_PARAM); oci_check_error(_env, res); } void ColumnType::init(OCIError* errh, dvoid* handle) { // get column name TOraText* pcol_name = NULL; ub4 size = sizeof(pcol_name); sword res = OCIAttrGet(handle, OCI_DTYPE_PARAM, &pcol_name, &size, OCI_ATTR_NAME, errh); oci_check_error(errh, res); _name.assign((const TCHAR*)pcol_name, size); // get column data type size = sizeof(_data_type); res = OCIAttrGet(handle, OCI_DTYPE_PARAM, &_data_type, &size, OCI_ATTR_DATA_TYPE, errh); oci_check_error(errh, res); // get NULL-ability flag size = sizeof(_nullable); res = OCIAttrGet(handle, OCI_DTYPE_PARAM, &_nullable, &size, OCI_ATTR_IS_NULL, errh); oci_check_error(errh, res); #if ORACLE_VERSION>=90 // retrieve the length semantics for the column size = sizeof(_char_semantics); res = OCIAttrGet(handle, OCI_DTYPE_PARAM, &_char_semantics, &size, OCI_ATTR_CHAR_USED, errh); if (res != OCI_SUCCESS) _char_semantics = 0; size = sizeof(_width); if (_char_semantics) // get the column width in characters res = OCIAttrGet(handle, OCI_DTYPE_PARAM, &_width, &size, OCI_ATTR_CHAR_SIZE, errh); else #endif // get the column width in bytes res = OCIAttrGet(handle, OCI_DTYPE_PARAM, &_width, &size, OCI_ATTR_DATA_SIZE, errh); oci_check_error(errh, res); switch(_data_type) { default: //case OCI_TYPECODE_VARCHAR: _precision = 0; _scale = -127; break; case OCI_TYPECODE_NUMBER: #ifdef SQLT_BFLOAT case OCI_TYPECODE_BFLOAT: case OCI_TYPECODE_BDOUBLE: #endif size = sizeof(_precision); res = OCIAttrGet(handle, OCI_DTYPE_PARAM, &_precision, &size, OCI_ATTR_PRECISION, errh); oci_check_error(errh, res); size = sizeof(_scale); res = OCIAttrGet(handle, OCI_DTYPE_PARAM, &_scale, &size, OCI_ATTR_SCALE, errh); oci_check_error(errh, res); break; case OCI_TYPECODE_DATE: #if ORACLE_VERSION>=81 //case OCI_TYPECODE_TIME: //case OCI_TYPECODE_TIMESTAMP: #endif _precision = 0; _scale = -127; break; } } void SqlStatement::print_results(tostream& out, bool header) { if (!(_state & EXECUTED)) execute(); else if (!(_state & FETCHED)) fetch(_result_buffers); if (header) _res->print_columns(out); int row_cnt = 0; for(ResultIterator it(*this); it.has_next(); ++it) { ++row_cnt; out << TEXT("row ") << row_cnt << TEXT(":\n"); _res->print_values(out, it); out << TEXT("\n"); } } SqlRecord::SqlRecord(SqlStatement& stmt, int rows) : _env(stmt._env) { stmt.ensure_execute(); int column_cnt = stmt.get_column_count(); _values.resize(column_cnt); ColumnVector::iterator it = _values.begin(); for(int idx=0; idx_type); it->_pvar = new SqlVariantArray(it->_type, rows); } //_name_map.clear(); } SqlRecord::~SqlRecord() { for(ColumnVector::iterator it=_values.begin(); it!=_values.end(); ++it) delete it->_pvar; } void SqlRecord::alloc_column(size_t col_idx, const ColumnTypeInfo& type, int rows) { Data& data = _values[col_idx]; // (re)allocate variant data buffers delete data._pvar; data._pvar = new SqlVariantArray(type, rows); } const ColumnType& SqlRecord::get_column_type(const tstring& col_name) const { /* for(ColumnVector::const_iterator it=_values.begin(); it!=_values.end(); ++it) if (!stricmp(it->_type._name.c_str(), col_name.c_str())) return it->_type; */ NameMap::const_iterator found = find_column(col_name); if (found != _name_map.end()) { int idx = found->second; return _values[idx]._type; } else { THROW_OCI_EXCEPTION("column not found"); return *(ColumnType*)NULL; } } const SqlVariantArray& SqlRecord::get_column(const tstring& col_name) const { /* for(ColumnVector::const_iterator it=_values.begin(); it!=_values.end(); ++it) if (!stricmp(it->_type._name.c_str(), col_name.c_str())) return *it->_pvar; */ NameMap::const_iterator found = find_column(col_name); if (found != _name_map.end()) { int idx = found->second; return *_values[idx]._pvar; } else { THROW_OCI_EXCEPTION("column not found"); return *(SqlVariantArray*)NULL; } } SqlRecord::NameMap::const_iterator SqlRecord::find_column(const tstring& col_name) const { tstring upper_name; // initialize column name map if (_name_map.empty()) { int idx = 0; for(ColumnVector::const_iterator it=_values.begin(); it!=_values.end(); ++it,++idx) { upper_name = it->_type._name.c_str(); _tcsupr_s((TCHAR*)upper_name.data(), upper_name.length()+1); _name_map[upper_name] = idx; } } upper_name = col_name; _tcsupr_s((TCHAR*)upper_name.data(), upper_name.length()+1); return _name_map.find(upper_name); } void SqlRecord::bind_result(SqlStatement& stmt) { int idx = 0; for(ColumnVector::iterator it=_values.begin(); it!=_values.end(); ++it,++idx) it->_pvar->bind_result(stmt, idx); } void SqlRecord::print_columns(tostream& out) { for(ColumnVector::const_iterator it=_values.begin(); it!=_values.end(); ++it) out << it->_type._name << TEXT(": ") << it->_type.get_type_str() << TEXT("\n"); out << TEXT("\n"); } void SqlRecord::print_values(tostream& out, int row) { ColumnVector::const_iterator it_type = _values.begin(); for(ColumnVector::const_iterator it=_values.begin(); it!=_values.end(); ++it,++it_type) out << it->_type._name << TEXT("=") << it->_pvar->str(row, _env._errh) << TEXT("\n"); } SqlVariantArray::SqlVariantArray(const ColumnTypeInfo& type, int size) : _size(size), _data_type(type._data_type) { switch(type._data_type) { case OCI_TYPECODE_CHAR: _data_type = OCI_TYPECODE_VARCHAR; // fall through case OCI_TYPECODE_VARCHAR: _pstring = new SqlStringArray(size, type._width); break; case OCI_TYPECODE_NUMBER: _pnumber = new SqlNumberArray(size); break; #ifdef SQLT_BFLOAT case OCI_TYPECODE_BFLOAT: _pfloat = new float[size]; break; case OCI_TYPECODE_BDOUBLE: _pdouble = new double[size]; break; #endif case OCI_TYPECODE_DATE: _poci_date = new OciDateArray(size); break; #if ORACLE_VERSION>=81 case OCI_TYPECODE_TIME: case OCI_TYPECODE_TIMESTAMP: _psql_date = new SqlDateTimeArray(size); break; /* no LOB allocation without available handles case OCI_TYPECODE_BLOB: _pblob = new OciBlobArray(size); break; case OCI_TYPECODE_CLOB: _pclob = new OciClobArray(size); break; */ #endif case SQLT_LNG: _pstring = new SqlStringArray(size, type._width); //@@ break; default: THROW_OCI_EXCEPTION("unsupported datatype"); } } SqlVariantArray::SqlVariantArray(const SqlVariantArray& other) : _size(0), _data_type(0) { assign(other); } void SqlVariantArray::assign(const SqlVariantArray& other) { clear(); _size = other._size; _data_type = other._data_type; switch(other._data_type) { case 0: break; case OCI_TYPECODE_VARCHAR: _pstring = new SqlStringArray(*other._pstring); //@@ break; case OCI_TYPECODE_NUMBER: _pnumber = new SqlNumberArray(*other._pnumber); break; #ifdef SQLT_BFLOAT case OCI_TYPECODE_BFLOAT: _pfloat = new float[_size]; memcpy(_pfloat, other._pfloat, sizeof(float)*_size); break; case OCI_TYPECODE_BDOUBLE: _pdouble = new double[_size]; memcpy(_pfloat, other._pfloat, sizeof(double)*_size); break; #endif case OCI_TYPECODE_DATE: _poci_date = new OciDateArray(*other._poci_date); break; #if ORACLE_VERSION>=81 case OCI_TYPECODE_TIME: case OCI_TYPECODE_TIMESTAMP: _psql_date = new SqlDateTimeArray(*other._psql_date); break; #endif /* case OCI_TYPECODE_BLOB: _pblob = new OciBlobArray(*other._pblob); break; case OCI_TYPECODE_CLOB: _pclob = new OciClobArray(*other._pclob); break; */ case SQLT_LNG: _pstring = new SqlStringArray(*other._pstring); //@@ break; default: assert(0); } } void SqlVariantArray::clear() { switch(_data_type) { case 0: break; case OCI_TYPECODE_VARCHAR: delete _pstring; break; case OCI_TYPECODE_NUMBER: delete _pnumber; break; #ifdef SQLT_BFLOAT case OCI_TYPECODE_BFLOAT: delete _pfloat; break; case OCI_TYPECODE_BDOUBLE: delete _pfloat; break; #endif case OCI_TYPECODE_DATE: delete _poci_date; break; #if ORACLE_VERSION>=81 case OCI_TYPECODE_TIME: case OCI_TYPECODE_TIMESTAMP: delete _psql_date; break; #endif /* case OCI_TYPECODE_BLOB: delete _pblob; break; case OCI_TYPECODE_CLOB: delete _pclob; break; */ case SQLT_LNG: delete _pstring; //@@ break; default: assert(0); } _size = 0; _data_type = 0; } tstring SqlVariantArray::str(int row, OCIError* errh) const { #ifdef SQLT_BFLOAT TCHAR buffer[16]; #endif switch(_data_type) { case 0: return TEXT(""); case OCI_TYPECODE_VARCHAR: return _pstring->c_str(row); // return *_string; case OCI_TYPECODE_NUMBER: return _pnumber->str(row, errh); #ifdef SQLT_BFLOAT case OCI_TYPECODE_BFLOAT: #ifdef __STDC_WANT_SECURE_LIB__ _stprintf_s(buffer, COUNTOF(buffer), #else _stprintf(buffer, #endif TEXT("%f"), _pfloat[row]); return buffer; case OCI_TYPECODE_BDOUBLE: #ifdef __STDC_WANT_SECURE_LIB__ _stprintf_s(buffer, COUNTOF(buffer), #else _stprintf(buffer, #endif TEXT("%f"), _pdouble[row]); return buffer; #endif case OCI_TYPECODE_DATE: return _poci_date->str(row); #if ORACLE_VERSION>=81 case OCI_TYPECODE_TIME: case OCI_TYPECODE_TIMESTAMP: return _psql_date->str(row); #endif /* case OCI_TYPECODE_BLOB: return TEXT("(BLOB)"); case OCI_TYPECODE_BFILE: return TEXT("(BFILE)"); case OCI_TYPECODE_CLOB: return TEXT("(CLOB)"); case OCI_TYPECODE_CFILE: return TEXT("(CFILE)"); */ case SQLT_LNG: return _pstring->c_str(row); //@@ default: assert(0); return TEXT(""); } } void SqlVariantArray::set_string(int row, const TCHAR* str) { if (_data_type != OCI_TYPECODE_VARCHAR) THROW_OCI_EXCEPTION("invalid variant data type"); _pstring->assign(row, str); } SqlNumber SqlVariantArray::number(int row, OCIError* errh) const { switch(_data_type) { case 0: return SqlNumber(); case OCI_TYPECODE_VARCHAR: return SqlNumber(errh, _pstring->c_str(row)); case OCI_TYPECODE_NUMBER: if (_pnumber->is_not_null(row)) return _pnumber->get_ref(row); else return SqlNumber(); #ifdef SQLT_BFLOAT case OCI_TYPECODE_BFLOAT: return SqlNumber(errh, _pfloat[row]); case OCI_TYPECODE_BDOUBLE: return SqlNumber(errh, _pdouble[row]); #endif //case OCI_TYPECODE_DATE: // return ... default: THROW_OCI_EXCEPTION("no conversion to number"); //return SqlNumber(); // not reached } } void SqlVariantArray::set_number(int row, const SqlNumber& num) { if (_data_type != OCI_TYPECODE_NUMBER) THROW_OCI_EXCEPTION("invalid variant data type"); _pnumber->set_number(row, num); } OciDate SqlVariantArray::oci_date(int row, OCIError* errh) const { switch(_data_type) { case 0: return OciDate(); case OCI_TYPECODE_VARCHAR: return OciDate(errh, _pstring->c_str(row)); case OCI_TYPECODE_DATE: if (_poci_date->is_not_null(row)) return *_poci_date->get_ref(row); else return OciDate(); /*@@todo convert type #if ORACLE_VERSION>=81 case OCI_TYPECODE_TIME: case OCI_TYPECODE_TIMESTAMP: return *_sql_date; #endif */ default: THROW_OCI_EXCEPTION("no conversion to date"); //return OciDate(); // not reached } } void SqlVariantArray::set_date(int row, const OciDate& date) { if (_data_type != OCI_TYPECODE_DATE) THROW_OCI_EXCEPTION("invalid variant data type"); _poci_date->set_date(row, date); } SqlDateTime SqlVariantArray::sql_date(int row, OCIError* errh) const { switch(_data_type) { case 0: return SqlDateTime(); /*@@todo convert type case OCI_TYPECODE_VARCHAR: return SqlDateTime(errh, *_string); case OCI_TYPECODE_DATE: return *_oci_date; */ #if ORACLE_VERSION>=81 case OCI_TYPECODE_TIME: case OCI_TYPECODE_TIMESTAMP: if (_psql_date->is_not_null(row)) return *_psql_date->get_ref(row); else return SqlDateTime(); #endif default: THROW_OCI_EXCEPTION("no conversion to date"); //return SqlDateTime(); // not reached } } void SqlVariantArray::set_datetime(int row, const SqlDateTime& datetime) { if (_data_type!=OCI_TYPECODE_TIME && _data_type!=OCI_TYPECODE_TIMESTAMP) THROW_OCI_EXCEPTION("invalid variant data type"); _psql_date->set_datetime(row, datetime); } bool SqlVariantArray::is_not_null(int row) const { switch(_data_type) { case 0: #ifdef SQLT_BFLOAT case OCI_TYPECODE_BFLOAT: case OCI_TYPECODE_BDOUBLE: #endif return true; case OCI_TYPECODE_VARCHAR: return _pstring->is_not_null(row); case OCI_TYPECODE_NUMBER: return _pnumber->is_not_null(row); case OCI_TYPECODE_DATE: return _poci_date->is_not_null(row); #if ORACLE_VERSION>=81 case OCI_TYPECODE_TIME: case OCI_TYPECODE_TIMESTAMP: return _psql_date->is_not_null(row); #endif /* case OCI_TYPECODE_BLOB: return _pblob->is_not_null(row); case OCI_TYPECODE_CLOB: return _pclob->is_not_null(row); */ case SQLT_LNG: return _pstring->is_not_null(row); //@@ default: assert(0); return true; } } void SqlVariantArray::bind_result(SqlStatement& stmt, int idx) { switch(_data_type) { case OCI_TYPECODE_VARCHAR: stmt.bind_result(idx, *_pstring); break; case OCI_TYPECODE_NUMBER: stmt.bind_result(idx, *_pnumber); break; #ifdef SQLT_BFLOAT case OCI_TYPECODE_BFLOAT: stmt.bind_result(idx, *_pfloat); break; case OCI_TYPECODE_BDOUBLE: stmt.bind_result(idx, *_pdouble); break; #endif case OCI_TYPECODE_DATE: stmt.bind_result(idx, *_poci_date); break; #if ORACLE_VERSION>=81 case OCI_TYPECODE_TIME: case OCI_TYPECODE_TIMESTAMP: stmt.bind_result(idx, *_psql_date); break; #endif /* case OCI_TYPECODE_BLOB: stmt.bind_result(idx, *_pblob); break; case OCI_TYPECODE_CLOB: stmt.bind_result(idx, *_pclob); break; */ case SQLT_LNG: stmt.bind_result(idx, *_pstring); //@@ break; default: assert(0); } } // buffer bind values void SqlVariantArray::bind_input(SqlStatement& stmt, int idx, int row) { switch(_data_type) { case OCI_TYPECODE_VARCHAR: stmt.bind_input(idx, BindInput(_pstring->str(row), _pstring->alen(), SQLT_STR)); break; case OCI_TYPECODE_NUMBER: stmt.bind_input(idx, BindInput(_pnumber->get_number(row), _pnumber->ind(row))); break; #ifdef SQLT_BFLOAT case OCI_TYPECODE_BFLOAT: stmt.bind_input(idx, *_pfloat); break; case OCI_TYPECODE_BDOUBLE: stmt.bind_input(idx, *_pdouble); break; #endif case OCI_TYPECODE_DATE: stmt.bind_input(idx, BindInput(_poci_date->get_date(row), _poci_date->ind(row))); break; #if ORACLE_VERSION>=81 case OCI_TYPECODE_TIME: case OCI_TYPECODE_TIMESTAMP: stmt.bind_input(idx, BindInput(_psql_date->get_datetime(row), _psql_date->ind(row))); break; #endif /* case OCI_TYPECODE_BLOB: stmt.bind_input(idx, *_pblob); break; case OCI_TYPECODE_CLOB: stmt.bind_input(idx, *_pclob); break; */ case SQLT_LNG: stmt.bind_input(idx, BindInput(_pstring->str(row), _pstring->alen(), SQLT_STR)); //@@ break; default: assert(0); } } /* void SqlStringArray::assign(int row, const dvoid* ptr, sb4 size) { //TODO resize buffer as needed if (value_sz == -1) strcpy_s(_buffer, _blen, s); else { memcpy(_buffer, ptr, size); _buffer[size] = '\0'; } } */ const TCHAR* get_oci_ptype_str(ub1 type) { switch(type) { case OCI_PTYPE_TABLE: return TEXT("table"); case OCI_PTYPE_VIEW: return TEXT("view"); case OCI_PTYPE_PROC: return TEXT("procedure"); case OCI_PTYPE_FUNC: return TEXT("function"); case OCI_PTYPE_PKG: return TEXT("package"); case OCI_PTYPE_TYPE: return TEXT("user-defined type"); case OCI_PTYPE_SYN: return TEXT("synonym"); case OCI_PTYPE_SEQ: return TEXT("sequence"); case OCI_PTYPE_COL: return TEXT("column"); case OCI_PTYPE_ARG: return TEXT("argument"); case OCI_PTYPE_LIST: return TEXT("list"); case OCI_PTYPE_TYPE_ATTR: return TEXT("user-defined type's attribute"); case OCI_PTYPE_TYPE_COLL: return TEXT("collection type's element "); case OCI_PTYPE_TYPE_METHOD: return TEXT("user-defined type's method"); case OCI_PTYPE_TYPE_ARG: return TEXT("user-defined type method's argument"); case OCI_PTYPE_TYPE_RESULT: return TEXT("user-defined type method's result"); #if ORACLE_VERSION>=81 case OCI_PTYPE_SCHEMA: return TEXT("schema"); case OCI_PTYPE_DATABASE: return TEXT("database"); #endif } return TEXT("unknown type"); } static tstring tokenize(const TCHAR** ptr, const TCHAR* sep) { const TCHAR* start = *ptr; const TCHAR* end; const TCHAR* found = _tcspbrk(*ptr, sep); if (found) { end = found; *ptr = found; } else *ptr = end = start + _tcslen(start); return tstring(start, end-start); } static tstring tokenize(const TCHAR** ptr, const TCHAR* sep, TCHAR key) { if (**ptr != key) return TEXT(""); ++*ptr; return tokenize(ptr, sep); } void LoginPara::parse(const TCHAR* conn_str) { const TCHAR* ptr = conn_str; _username = tokenize(&ptr, TEXT("/@ ")); _password = tokenize(&ptr, TEXT("@ "), TEXT('/')); _tnsname = tokenize(&ptr, TEXT(" "), TEXT('@')); while(istspace(*ptr)) ++ptr; if (!_tcsnicmp(ptr, TEXT("as sysdba"), 9)) { _mode = OCI_SYSDBA; ptr += 9; } else if (!_tcsnicmp(ptr, TEXT("as sysoper"), 10)) { _mode = OCI_SYSDBA; ptr += 10; } else if (!_tcsnicmp(ptr, TEXT("migrate"), 7)) { _mode = OCI_MIGRATE; ptr += 7; } } } // namespace OCIPL