File indexing completed on 2025-02-23 05:15:19
0001 // 0002 // Copyright (C) 2004-2007 Maciej Sobczak, Stephen Hutton 0003 // Distributed under the Boost Software License, Version 1.0. 0004 // (See accompanying file LICENSE_1_0.txt or copy at 0005 // http://www.boost.org/LICENSE_1_0.txt) 0006 // 0007 0008 #define SOCI_ORACLE_SOURCE 0009 #include "soci/oracle/soci-oracle.h" 0010 #include "soci/callbacks.h" 0011 #include "soci/session.h" 0012 #include "error.h" 0013 #include <cctype> 0014 #include <cstdio> 0015 #include <cstring> 0016 #include <ctime> 0017 #include <sstream> 0018 0019 #ifdef _MSC_VER 0020 #pragma warning(disable:4355) 0021 #endif 0022 0023 using namespace soci; 0024 using namespace soci::details; 0025 using namespace soci::details::oracle; 0026 0027 namespace // unnamed 0028 { 0029 0030 sb4 fo_callback(void * /* svchp */, void * /* envhp */, void * fo_ctx, 0031 ub4 /* fo_type */, ub4 fo_event) 0032 { 0033 oracle_session_backend * backend = 0034 static_cast<oracle_session_backend *>(fo_ctx); 0035 0036 failover_callback * callback = backend->failoverCallback_; 0037 0038 if (callback != NULL) 0039 { 0040 session * sql = backend->session_; 0041 0042 switch (fo_event) 0043 { 0044 case OCI_FO_BEGIN: 0045 // failover operation was initiated 0046 0047 try 0048 { 0049 callback->started(); 0050 } 0051 catch (...) 0052 { 0053 // ignore exceptions from user callbacks 0054 } 0055 0056 break; 0057 0058 case OCI_FO_END: 0059 // failover was successful 0060 0061 try 0062 { 0063 callback->finished(*sql); 0064 } 0065 catch (...) 0066 { 0067 // ignore exceptions from user callbacks 0068 } 0069 0070 break; 0071 0072 case OCI_FO_ABORT: 0073 // failover was aborted with no possibility to recovery 0074 0075 try 0076 { 0077 callback->aborted(); 0078 } 0079 catch (...) 0080 { 0081 // ignore exceptions from user callbacks 0082 } 0083 0084 break; 0085 0086 case OCI_FO_ERROR: 0087 // failover failed, but can be retried 0088 0089 try 0090 { 0091 bool retry = false; 0092 std::string newTarget; 0093 callback->failed(retry, newTarget); 0094 0095 // newTarget is ignored, as the new target 0096 // is selected by Oracle client configuration 0097 0098 if (retry) 0099 { 0100 return OCI_FO_RETRY; 0101 } 0102 } 0103 catch (...) 0104 { 0105 // ignore exceptions from user callbacks 0106 } 0107 0108 break; 0109 0110 case OCI_FO_REAUTH: 0111 // nothing interesting 0112 break; 0113 0114 default: 0115 // ignore unknown callback types (if any) 0116 break; 0117 } 0118 } 0119 0120 return 0; 0121 } 0122 0123 } // unnamed namespace 0124 0125 oracle_session_backend::oracle_session_backend(std::string const & serviceName, 0126 std::string const & userName, std::string const & password, int mode, 0127 bool decimals_as_strings, int charset, int ncharset) 0128 : envhp_(NULL), srvhp_(NULL), errhp_(NULL), svchp_(NULL), usrhp_(NULL), 0129 decimals_as_strings_(decimals_as_strings) 0130 { 0131 // assume service/user/password are utf8-compatible already 0132 const int defaultSourceCharSetId = 871; 0133 0134 // maximum length of a connect descriptor is documented as 4KiB in the 0135 // "Syntax Rules for Configuration Files" section of Oracle documentation 0136 const size_t serviceBufLen = 4096; 0137 0138 // user names must be identifiers which are limited to pitiful 30 0139 // characters in Oracle (see "Database Object Naming Rules" section of the 0140 // SQL language reference) and so, apparently, are the passwords, so just 0141 // 32 should be enough for them. 0142 const size_t authBufLen = 32; 0143 0144 char nlsService[serviceBufLen]; 0145 size_t nlsServiceLen; 0146 char nlsUserName[authBufLen]; 0147 size_t nlsUserNameLen; 0148 char nlsPassword[authBufLen]; 0149 size_t nlsPasswordLen; 0150 0151 sword res; 0152 0153 // create the environment 0154 res = OCIEnvNlsCreate(&envhp_, OCI_THREADED | OCI_ENV_NO_MUTEX, 0155 0, 0, 0, 0, 0, 0, charset, ncharset); 0156 if (res != OCI_SUCCESS) 0157 { 0158 throw soci_error("Cannot create environment"); 0159 } 0160 0161 // create the server handle 0162 res = OCIHandleAlloc(envhp_, reinterpret_cast<dvoid**>(&srvhp_), 0163 OCI_HTYPE_SERVER, 0, 0); 0164 if (res != OCI_SUCCESS) 0165 { 0166 clean_up(); 0167 throw soci_error("Cannot create server handle"); 0168 } 0169 0170 // create the error handle 0171 res = OCIHandleAlloc(envhp_, reinterpret_cast<dvoid**>(&errhp_), 0172 OCI_HTYPE_ERROR, 0, 0); 0173 if (res != OCI_SUCCESS) 0174 { 0175 clean_up(); 0176 throw soci_error("Cannot create error handle"); 0177 } 0178 0179 if (charset != 0) 0180 { 0181 // convert service/user/password to the expected charset 0182 0183 res = OCINlsCharSetConvert(envhp_, errhp_, 0184 charset, nlsService, serviceBufLen, 0185 defaultSourceCharSetId, serviceName.c_str(), serviceName.size(), &nlsServiceLen); 0186 if (res != OCI_SUCCESS) 0187 { 0188 std::string msg; 0189 int errNum; 0190 get_error_details(res, errhp_, msg, errNum); 0191 clean_up(); 0192 throw oracle_soci_error(msg, errNum); 0193 } 0194 0195 res = OCINlsCharSetConvert(envhp_, errhp_, 0196 charset, nlsUserName, authBufLen, 0197 defaultSourceCharSetId, userName.c_str(), userName.size(), &nlsUserNameLen); 0198 if (res != OCI_SUCCESS) 0199 { 0200 std::string msg; 0201 int errNum; 0202 get_error_details(res, errhp_, msg, errNum); 0203 clean_up(); 0204 throw oracle_soci_error(msg, errNum); 0205 } 0206 0207 res = OCINlsCharSetConvert(envhp_, errhp_, 0208 charset, nlsPassword, authBufLen, 0209 defaultSourceCharSetId, password.c_str(), password.size(), &nlsPasswordLen); 0210 if (res != OCI_SUCCESS) 0211 { 0212 std::string msg; 0213 int errNum; 0214 get_error_details(res, errhp_, msg, errNum); 0215 clean_up(); 0216 throw oracle_soci_error(msg, errNum); 0217 } 0218 } 0219 else 0220 { 0221 // do not perform any charset conversions 0222 0223 nlsServiceLen = serviceName.size(); 0224 if (nlsServiceLen < serviceBufLen) 0225 { 0226 std::strcpy(nlsService, serviceName.c_str()); 0227 } 0228 else 0229 { 0230 throw soci_error("Service name is too long."); 0231 } 0232 0233 nlsUserNameLen = userName.size(); 0234 if (nlsUserNameLen < authBufLen) 0235 { 0236 std::strcpy(nlsUserName, userName.c_str()); 0237 } 0238 else 0239 { 0240 throw soci_error("User name is too long."); 0241 } 0242 0243 nlsPasswordLen = password.size(); 0244 if (nlsPasswordLen < authBufLen) 0245 { 0246 std::strcpy(nlsPassword, password.c_str()); 0247 } 0248 else 0249 { 0250 throw soci_error("Password is too long."); 0251 } 0252 } 0253 0254 // create the server context 0255 res = OCIServerAttach(srvhp_, errhp_, 0256 reinterpret_cast<text*>(nlsService), 0257 static_cast<sb4>(nlsServiceLen), OCI_DEFAULT); 0258 if (res != OCI_SUCCESS) 0259 { 0260 std::string msg; 0261 int errNum; 0262 get_error_details(res, errhp_, msg, errNum); 0263 clean_up(); 0264 throw oracle_soci_error(msg, errNum); 0265 } 0266 0267 // register failover callback 0268 OCIFocbkStruct fo; 0269 fo.fo_ctx = this; 0270 fo.callback_function = &fo_callback; 0271 0272 res = OCIAttrSet(srvhp_, static_cast<ub4>(OCI_HTYPE_SERVER), 0273 &fo, 0, static_cast<ub4>(OCI_ATTR_FOCBK), errhp_); 0274 if (res != OCI_SUCCESS) 0275 { 0276 std::string msg; 0277 int errNum; 0278 get_error_details(res, errhp_, msg, errNum); 0279 clean_up(); 0280 throw oracle_soci_error(msg, errNum); 0281 } 0282 0283 // create service context handle 0284 res = OCIHandleAlloc(envhp_, reinterpret_cast<dvoid**>(&svchp_), 0285 OCI_HTYPE_SVCCTX, 0, 0); 0286 if (res != OCI_SUCCESS) 0287 { 0288 clean_up(); 0289 throw soci_error("Cannot create service context"); 0290 } 0291 0292 // set the server attribute in the context handle 0293 res = OCIAttrSet(svchp_, OCI_HTYPE_SVCCTX, srvhp_, 0, 0294 OCI_ATTR_SERVER, errhp_); 0295 if (res != OCI_SUCCESS) 0296 { 0297 std::string msg; 0298 int errNum; 0299 get_error_details(res, errhp_, msg, errNum); 0300 clean_up(); 0301 throw oracle_soci_error(msg, errNum); 0302 } 0303 0304 // allocate user session handle 0305 res = OCIHandleAlloc(envhp_, reinterpret_cast<dvoid**>(&usrhp_), 0306 OCI_HTYPE_SESSION, 0, 0); 0307 if (res != OCI_SUCCESS) 0308 { 0309 clean_up(); 0310 throw soci_error("Cannot allocate user session handle"); 0311 } 0312 0313 // select credentials type - use rdbms based credentials by default 0314 // and switch to external credentials if username and 0315 // password are both not specified 0316 ub4 credentialType = OCI_CRED_RDBMS; 0317 if (userName.empty() && password.empty()) 0318 { 0319 credentialType = OCI_CRED_EXT; 0320 } 0321 else 0322 { 0323 // set username attribute in the user session handle 0324 res = OCIAttrSet(usrhp_, OCI_HTYPE_SESSION, 0325 reinterpret_cast<dvoid*>(nlsUserName), 0326 static_cast<ub4>(nlsUserNameLen), OCI_ATTR_USERNAME, errhp_); 0327 if (res != OCI_SUCCESS) 0328 { 0329 clean_up(); 0330 throw soci_error("Cannot set username"); 0331 } 0332 0333 // set password attribute 0334 res = OCIAttrSet(usrhp_, OCI_HTYPE_SESSION, 0335 reinterpret_cast<dvoid*>(nlsPassword), 0336 static_cast<ub4>(nlsPasswordLen), OCI_ATTR_PASSWORD, errhp_); 0337 if (res != OCI_SUCCESS) 0338 { 0339 clean_up(); 0340 throw soci_error("Cannot set password"); 0341 } 0342 } 0343 0344 // begin the session 0345 res = OCISessionBegin(svchp_, errhp_, usrhp_, 0346 credentialType, mode); 0347 if (res != OCI_SUCCESS && res != OCI_SUCCESS_WITH_INFO) 0348 { 0349 std::string msg; 0350 int errNum; 0351 get_error_details(res, errhp_, msg, errNum); 0352 clean_up(); 0353 throw oracle_soci_error(msg, errNum); 0354 } 0355 0356 // set the session in the context handle 0357 res = OCIAttrSet(svchp_, OCI_HTYPE_SVCCTX, usrhp_, 0358 0, OCI_ATTR_SESSION, errhp_); 0359 if (res != OCI_SUCCESS) 0360 { 0361 std::string msg; 0362 int errNum; 0363 get_error_details(res, errhp_, msg, errNum); 0364 clean_up(); 0365 throw oracle_soci_error(msg, errNum); 0366 } 0367 } 0368 0369 oracle_session_backend::~oracle_session_backend() 0370 { 0371 clean_up(); 0372 } 0373 0374 bool oracle_session_backend::is_connected() 0375 { 0376 return OCIPing(svchp_, errhp_, OCI_DEFAULT) == OCI_SUCCESS; 0377 } 0378 0379 void oracle_session_backend::begin() 0380 { 0381 // This code is commented out because it causes one of the transaction 0382 // tests in common_tests::test10() to fail with error 'Invalid handle' 0383 // With the code commented out, all tests pass. 0384 // sword res = OCITransStart(svchp_, errhp_, 0, OCI_TRANS_NEW); 0385 // if (res != OCI_SUCCESS) 0386 // { 0387 // throworacle_soci_error(res, errhp_); 0388 // } 0389 } 0390 0391 void oracle_session_backend::commit() 0392 { 0393 sword res = OCITransCommit(svchp_, errhp_, OCI_DEFAULT); 0394 if (res != OCI_SUCCESS) 0395 { 0396 throw_oracle_soci_error(res, errhp_); 0397 } 0398 } 0399 0400 void oracle_session_backend::rollback() 0401 { 0402 sword res = OCITransRollback(svchp_, errhp_, OCI_DEFAULT); 0403 if (res != OCI_SUCCESS) 0404 { 0405 throw_oracle_soci_error(res, errhp_); 0406 } 0407 } 0408 0409 void oracle_session_backend::clean_up() 0410 { 0411 if (svchp_ != NULL && errhp_ != NULL && usrhp_ != NULL) 0412 { 0413 OCISessionEnd(svchp_, errhp_, usrhp_, OCI_DEFAULT); 0414 } 0415 0416 if (usrhp_) { OCIHandleFree(usrhp_, OCI_HTYPE_SESSION); } 0417 if (svchp_) { OCIHandleFree(svchp_, OCI_HTYPE_SVCCTX); } 0418 if (srvhp_) 0419 { 0420 OCIServerDetach(srvhp_, errhp_, OCI_DEFAULT); 0421 OCIHandleFree(srvhp_, OCI_HTYPE_SERVER); 0422 } 0423 if (errhp_) { OCIHandleFree(errhp_, OCI_HTYPE_ERROR); } 0424 if (envhp_) { OCIHandleFree(envhp_, OCI_HTYPE_ENV); } 0425 } 0426 0427 oracle_statement_backend * oracle_session_backend::make_statement_backend() 0428 { 0429 return new oracle_statement_backend(*this); 0430 } 0431 0432 oracle_rowid_backend * oracle_session_backend::make_rowid_backend() 0433 { 0434 return new oracle_rowid_backend(*this); 0435 } 0436 0437 oracle_blob_backend * oracle_session_backend::make_blob_backend() 0438 { 0439 return new oracle_blob_backend(*this); 0440 } 0441 0442 bool oracle_session_backend::get_next_sequence_value( 0443 session &s, std::string const &sequence, 0444 long long &value) 0445 { 0446 s << "select " + sequence + ".nextval from dual", into(value); 0447 0448 return true; 0449 } 0450 0451 ub2 oracle_session_backend::get_double_sql_type() const 0452 { 0453 // SQLT_BDOUBLE avoids unnecessary conversions which is better from both 0454 // performance and correctness point of view as it avoids rounding 0455 // problems, however it's only available starting in Oracle 10.1, so 0456 // normally we should do run-time Oracle version detection here, but for 0457 // now just assume that if we use new headers (i.e. have high enough 0458 // compile-time version), then the run-time is at least as high. 0459 #ifdef SQLT_BDOUBLE 0460 return SQLT_BDOUBLE; 0461 #else 0462 return SQLT_FLT; 0463 #endif 0464 }