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 }