Skip to content
Snippets Groups Projects
OcciStmt.cpp 11.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * The CERN Tape Archive (CTA) project
     * Copyright (C) 2015  CERN
     *
     * This program is free software: you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation, either version 3 of the License, or
     * (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     */
    
    
    #include "common/exception/Exception.hpp"
    
    #include "common/exception/LostDatabaseConnection.hpp"
    
    #include "common/make_unique.hpp"
    
    #include "common/threading/MutexLocker.hpp"
    
    #include "rdbms/CheckConstraintError.hpp"
    #include "rdbms/PrimaryKeyError.hpp"
    #include "rdbms/UniqueError.hpp"
    
    #include "rdbms/wrapper/OcciColumn.hpp"
    #include "rdbms/wrapper/OcciConn.hpp"
    #include "rdbms/wrapper/OcciRset.hpp"
    #include "rdbms/wrapper/OcciStmt.hpp"
    
    #include <iostream>
    
    #include <map>
    #include <sstream>
    
    #include <stdexcept>
    
    //------------------------------------------------------------------------------
    // constructor
    //------------------------------------------------------------------------------
    
    OcciStmt::OcciStmt(
      const std::string &sql,
    
      OcciConn &conn,
      oracle::occi::Statement *const stmt) :
    
      m_conn(conn),
      m_stmt(stmt) {
    
    }
    
    //------------------------------------------------------------------------------
    // destructor
    //------------------------------------------------------------------------------
    
      try {
        close(); // Idempotent close() method
    
        // Destructor does not throw
      }
    }
    
    
    //------------------------------------------------------------------------------
    // clear
    //------------------------------------------------------------------------------
    void OcciStmt::clear() {
    }
    
    
    //------------------------------------------------------------------------------
    // close
    //------------------------------------------------------------------------------
    
    void OcciStmt::close() {
    
        threading::MutexLocker locker(m_mutex);
    
        if (nullptr != m_stmt) {
    
          m_conn.closeStmt(m_stmt);
    
          m_stmt = nullptr;
    
        }
      } catch(exception::Exception &ex) {
    
        throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
    
          getSqlForException() + ": " + ex.getMessage().str());
    
        throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
    
          getSqlForException() + ": " + se.what());
    
    Steven Murray's avatar
    Steven Murray committed
    //------------------------------------------------------------------------------
    
    Steven Murray's avatar
    Steven Murray committed
    //------------------------------------------------------------------------------
    
    void OcciStmt::bindUint8(const std::string &paramName, const optional<uint8_t> &paramValue) {
    
    Steven Murray's avatar
    Steven Murray committed
      try {
    
        return bindInteger<uint8_t>(paramName, paramValue);
    
    Steven Murray's avatar
    Steven Murray committed
      } catch(exception::Exception &ex) {
        ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
        throw;
      }
    }
    
    
    Steven Murray's avatar
    Steven Murray committed
    //------------------------------------------------------------------------------
    
    Steven Murray's avatar
    Steven Murray committed
    //------------------------------------------------------------------------------
    
    void OcciStmt::bindUint16(const std::string &paramName, const optional<uint16_t> &paramValue) {
    
    Steven Murray's avatar
    Steven Murray committed
      try {
    
        return bindInteger<uint16_t>(paramName, paramValue);
    
    Steven Murray's avatar
    Steven Murray committed
      } catch(exception::Exception &ex) {
        ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
        throw;
      }
    }
    
    
    Steven Murray's avatar
    Steven Murray committed
    //------------------------------------------------------------------------------
    
    Steven Murray's avatar
    Steven Murray committed
    //------------------------------------------------------------------------------
    
    void OcciStmt::bindUint32(const std::string &paramName, const optional<uint32_t> &paramValue) {
    
    Steven Murray's avatar
    Steven Murray committed
      try {
    
        return bindInteger<uint32_t>(paramName, paramValue);
    
    Steven Murray's avatar
    Steven Murray committed
      } catch(exception::Exception &ex) {
        ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
        throw;
      }
    }
    
    
    //------------------------------------------------------------------------------
    
    //------------------------------------------------------------------------------
    
    void OcciStmt::bindUint64(const std::string &paramName, const optional<uint64_t> &paramValue) {
    
        return bindInteger<uint64_t>(paramName, paramValue);
    
      } catch(exception::Exception &ex) {
    
    Steven Murray's avatar
    Steven Murray committed
        ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
        throw;
    
    Steven Murray's avatar
    Steven Murray committed
    //------------------------------------------------------------------------------
    // bindBlob
    //------------------------------------------------------------------------------
    
    void OcciStmt::bindBlob(const std::string &paramName, const std::string &paramValue) {
    
      throw exception::Exception("OcciStmt::bindBlob not implemented.");
    
    //------------------------------------------------------------------------------
    
    //------------------------------------------------------------------------------
    
    void OcciStmt::bindDouble(const std::string &paramName, const optional<double> &paramValue) {
    
      try {
        const unsigned paramIdx = getParamIdx(paramName);
        if(paramValue) {
          // Bind integer as a string in order to support 64-bit integers
          m_stmt->setDouble(paramIdx, paramValue.value());
        } else {
          m_stmt->setNull(paramIdx, oracle::occi::OCCIDOUBLE);
        }
    
      } catch(exception::Exception &ex) {
    
        throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
    
          getSqlForException() + ": " + ex.getMessage().str());
    
      } catch(std::exception &se) {
    
        throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
    
          getSqlForException() + ": " + se.what());
    
    //------------------------------------------------------------------------------
    
    //------------------------------------------------------------------------------
    
    void OcciStmt::bindString(const std::string &paramName, const optional<std::string> &paramValue) {
    
      try {
        if(paramValue && paramValue.value().empty()) {
          throw exception::Exception(std::string("Optional string parameter ") + paramName + " is an empty string. "
            " An optional string parameter should either have a non-empty string value or no value at all."); 
        }
    
    
        const unsigned paramIdx = getParamIdx(paramName);
    
        if(paramValue) {
          m_stmt->setString(paramIdx, paramValue.value());
        } else {
    
          m_stmt->setNull(paramIdx, oracle::occi::OCCISTRING);
    
      } catch(exception::Exception &ex) {
    
        throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
    
          getSqlForException() + ": " + ex.getMessage().str());
    
      } catch(std::exception &se) {
    
        throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
    
          getSqlForException() + ": " + se.what());
    
    
    //------------------------------------------------------------------------------
    
    //------------------------------------------------------------------------------
    
    std::unique_ptr<RsetWrapper> OcciStmt::executeQuery() {
    
      using namespace oracle;
    
    
      const auto autocommitMode = m_conn.getAutocommitMode();
    
    
        switch(autocommitMode) {
        case AutocommitMode::AUTOCOMMIT_ON:
          m_stmt->setAutoCommit(true);
          break;
        case AutocommitMode::AUTOCOMMIT_OFF:
          m_stmt->setAutoCommit(false);
          break;
        default:
         throw exception::Exception("Unknown autocommit mode");
        }
    
    
        return cta::make_unique<OcciRset>(*this, m_stmt->executeQuery());
    
      } catch(occi::SQLException &ex) {
    
        std::ostringstream msg;
        msg << std::string(__FUNCTION__) << " failed for SQL statement " << getSqlForException() << ": " << ex.what();
    
    
        if(connShouldBeClosed(ex)) {
          // Close the statement first and then the connection
          try {
            close();
          } catch(...) {
          }
    
          try {
            m_conn.close();
          } catch(...) {
          }
    
          throw exception::LostDatabaseConnection(msg.str());
    
        throw exception::Exception(msg.str());
    
    //------------------------------------------------------------------------------
    // executeNonQuery
    //------------------------------------------------------------------------------
    
    void OcciStmt::executeNonQuery() {
    
      const auto autocommitMode = m_conn.getAutocommitMode();
    
    
        switch(autocommitMode) {
        case AutocommitMode::AUTOCOMMIT_ON:
          m_stmt->setAutoCommit(true);
          break;
        case AutocommitMode::AUTOCOMMIT_OFF:
          m_stmt->setAutoCommit(false);
          break;
        default:
         throw exception::Exception("Unknown autocommit mode");
        }
    
    
      } catch(occi::SQLException &ex) {
    
        std::ostringstream msg;
        msg << std::string(__FUNCTION__) << " failed for SQL statement " << getSqlForException() << ": " << ex.what();
    
    
        if(connShouldBeClosed(ex)) {
          // Close the statement first and then the connection
          try {
            close();
          } catch(...) {
          }
    
          try {
            m_conn.close();
          } catch(...) {
          }
    
          throw exception::LostDatabaseConnection(msg.str());
    
    
        switch(ex.getErrorCode()) {
        case 1:
    
          throw UniqueError(msg.str());
    
          throw CheckConstraintError(msg.str());
    
    //------------------------------------------------------------------------------
    // getNbAffectedRows
    //------------------------------------------------------------------------------
    uint64_t OcciStmt::getNbAffectedRows() const {
    
      return m_stmt->getUb8RowCount();
    
    //------------------------------------------------------------------------------
    // get
    //------------------------------------------------------------------------------
    oracle::occi::Statement *OcciStmt::get() const {
      return m_stmt;
    }
    
    //------------------------------------------------------------------------------
    // operator->
    //------------------------------------------------------------------------------
    oracle::occi::Statement *OcciStmt::operator->() const {
      return get();
    }
    
    
    //------------------------------------------------------------------------------
    // setColumn
    //------------------------------------------------------------------------------
    
    void OcciStmt::setColumn(OcciColumn &col, oracle::occi::Type type) {
    
      const std::string paramName = std::string(":") + col.getColName();
      const auto paramIdx = getParamIdx(paramName);
    
      m_stmt->setDataBuffer(paramIdx, col.getBuffer(), type, col.getMaxFieldLength(),
        col.getFieldLengths());
    
    //------------------------------------------------------------------------------
    // connShouldBeClosed
    //------------------------------------------------------------------------------
    bool OcciStmt::connShouldBeClosed(const oracle::occi::SQLException &ex) {
      using namespace oracle;
    
      switch(ex.getErrorCode()) {
      case    28:
    
      case 12541:
      case 12571:
      case 24338:
      case 12537:
      case 25401:
      case 25409:
      case 32102:
        return true;
      default:
        return false;
      };
    }