Newer
Older
/*
* 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"
Steven Murray
committed
#include "common/threading/MutexLocker.hpp"
#include "rdbms/CheckConstraintError.hpp"
#include "rdbms/PrimaryKeyError.hpp"
#include "rdbms/UniqueError.hpp"
Steven Murray
committed
#include "rdbms/wrapper/OcciColumn.hpp"
#include "rdbms/wrapper/OcciConn.hpp"
#include "rdbms/wrapper/OcciRset.hpp"
#include "rdbms/wrapper/OcciStmt.hpp"
#include <map>
#include <sstream>
namespace rdbms {
Steven Murray
committed
namespace wrapper {
//------------------------------------------------------------------------------
// constructor
//------------------------------------------------------------------------------
OcciStmt::OcciStmt(
const std::string &sql,
OcciConn &conn,
oracle::occi::Statement *const stmt) :
StmtWrapper(sql),
m_conn(conn),
m_stmt(stmt) {
}
//------------------------------------------------------------------------------
// destructor
//------------------------------------------------------------------------------
OcciStmt::~OcciStmt() {
try {
close(); // Idempotent close() method
// Destructor does not throw
}
}
Steven Murray
committed
//------------------------------------------------------------------------------
// clear
//------------------------------------------------------------------------------
void OcciStmt::clear() {
}
//------------------------------------------------------------------------------
// close
//------------------------------------------------------------------------------
void OcciStmt::close() {
try {
Steven Murray
committed
threading::MutexLocker locker(m_mutex);
m_conn.closeStmt(m_stmt);
}
} 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());
//------------------------------------------------------------------------------
Steven Murray
committed
// bindUint8
//------------------------------------------------------------------------------
Steven Murray
committed
void OcciStmt::bindUint8(const std::string ¶mName, const optional<uint8_t> ¶mValue) {
Steven Murray
committed
return bindInteger<uint8_t>(paramName, paramValue);
} catch(exception::Exception &ex) {
ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
throw;
}
}
//------------------------------------------------------------------------------
Steven Murray
committed
// bindUint16
//------------------------------------------------------------------------------
Steven Murray
committed
void OcciStmt::bindUint16(const std::string ¶mName, const optional<uint16_t> ¶mValue) {
Steven Murray
committed
return bindInteger<uint16_t>(paramName, paramValue);
} catch(exception::Exception &ex) {
ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
throw;
}
}
//------------------------------------------------------------------------------
Steven Murray
committed
// bindUint32
//------------------------------------------------------------------------------
Steven Murray
committed
void OcciStmt::bindUint32(const std::string ¶mName, const optional<uint32_t> ¶mValue) {
Steven Murray
committed
return bindInteger<uint32_t>(paramName, paramValue);
} catch(exception::Exception &ex) {
ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
throw;
}
}
//------------------------------------------------------------------------------
Steven Murray
committed
// bindUint64
//------------------------------------------------------------------------------
Steven Murray
committed
void OcciStmt::bindUint64(const std::string ¶mName, const optional<uint64_t> ¶mValue) {
Steven Murray
committed
return bindInteger<uint64_t>(paramName, paramValue);
} catch(exception::Exception &ex) {
ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
throw;
}
}
//------------------------------------------------------------------------------
// bindBlob
//------------------------------------------------------------------------------
void OcciStmt::bindBlob(const std::string ¶mName, const std::string ¶mValue) {
throw exception::Exception("OcciStmt::bindBlob not implemented.");
//------------------------------------------------------------------------------
Steven Murray
committed
// bindDouble
//------------------------------------------------------------------------------
Steven Murray
committed
void OcciStmt::bindDouble(const std::string ¶mName, const optional<double> ¶mValue) {
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);
}
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
committed
// bindString
//------------------------------------------------------------------------------
Steven Murray
committed
void OcciStmt::bindString(const std::string ¶mName, const optional<std::string> ¶mValue) {
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);
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());
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
std::unique_ptr<RsetWrapper> OcciStmt::executeQuery() {
const auto autocommitMode = m_conn.getAutocommitMode();
Steven Murray
committed
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");
}
Steven Murray
committed
return cta::make_unique<OcciRset>(*this, m_stmt->executeQuery());
std::ostringstream msg;
msg << std::string(__FUNCTION__) << " failed for SQL statement " << getSqlForException() << ": " << ex.what();
Steven Murray
committed
if(connShouldBeClosed(ex)) {
// Close the statement first and then the connection
try {
close();
} catch(...) {
}
try {
m_conn.close();
} catch(...) {
}
throw exception::LostDatabaseConnection(msg.str());
Steven Murray
committed
}
throw exception::Exception(msg.str());
//------------------------------------------------------------------------------
// executeNonQuery
//------------------------------------------------------------------------------
void OcciStmt::executeNonQuery() {
using namespace oracle;
const auto autocommitMode = m_conn.getAutocommitMode();
try {
Steven Murray
committed
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");
}
m_stmt->executeUpdate();
std::ostringstream msg;
msg << std::string(__FUNCTION__) << " failed for SQL statement " << getSqlForException() << ": " << ex.what();
Steven Murray
committed
if(connShouldBeClosed(ex)) {
// Close the statement first and then the connection
try {
close();
} catch(...) {
}
try {
m_conn.close();
} catch(...) {
}
throw exception::LostDatabaseConnection(msg.str());
Steven Murray
committed
}
switch(ex.getErrorCode()) {
case 1:
throw UniqueError(msg.str());
throw CheckConstraintError(msg.str());
Steven Murray
committed
throw exception::Exception(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();
}
Steven Murray
committed
//------------------------------------------------------------------------------
// setColumn
//------------------------------------------------------------------------------
void OcciStmt::setColumn(OcciColumn &col, oracle::occi::Type type) {
Steven Murray
committed
const std::string paramName = std::string(":") + col.getColName();
const auto paramIdx = getParamIdx(paramName);
m_stmt->setDataBuffer(paramIdx, col.getBuffer(), type, col.getMaxFieldLength(),
col.getFieldLengths());
Steven Murray
committed
}
Steven Murray
committed
//------------------------------------------------------------------------------
// connShouldBeClosed
//------------------------------------------------------------------------------
bool OcciStmt::connShouldBeClosed(const oracle::occi::SQLException &ex) {
using namespace oracle;
switch(ex.getErrorCode()) {
case 28:
Steven Murray
committed
case 1003:
case 1008:
case 1012:
case 1033:
case 1089:
Steven Murray
committed
case 2392:
case 2396:
Steven Murray
committed
case 2399:
case 3113:
case 3114:
case 3135:
case 12170:
Steven Murray
committed
case 12514:
Steven Murray
committed
case 12541:
case 12571:
case 24338:
case 12537:
case 25401:
case 25409:
case 32102:
return true;
default:
return false;
};
}
Steven Murray
committed
} // namespace wrapper
} // namespace rdbms
} // namespace cta