diff --git a/catalogue/OracleCatalogue.cpp b/catalogue/OracleCatalogue.cpp index d5635847eb838973992ff79af13e046e6d664878..1d01bd6fb85357133471700947d9d6065d8734fd 100644 --- a/catalogue/OracleCatalogue.cpp +++ b/catalogue/OracleCatalogue.cpp @@ -293,6 +293,7 @@ void OracleCatalogue::filesWrittenToTape(const std::list<TapeFileWritten> &event checkTapeFileWrittenFieldsAreSet(firstEvent); const time_t now = time(nullptr); const std::string nowStr = std::to_string(now); + const uint32_t creationTimeMaxFieldSize = nowStr.length() + 1; std::lock_guard<std::mutex> m_lock(m_mutex); auto conn = m_connPool.getConn(); rdbms::AutoRollback autoRollback(conn); @@ -301,23 +302,9 @@ void OracleCatalogue::filesWrittenToTape(const std::list<TapeFileWritten> &event uint64_t expectedFSeq = tape.lastFSeq + 1; uint64_t totalCompressedBytesWritten = 0; - uint32_t vidMaxFieldSize = 0; - uint32_t fSeqMaxFieldSize = 0; - uint32_t blockIdMaxFieldSize = 0; - uint32_t compressedSizeMaxFieldSize = 0; - uint32_t copyNbMaxFieldSize = 0; - const uint32_t creationTimeMaxFieldSize = nowStr.length() + 1; - uint32_t archiveFileIdMaxFieldSize = 0; - - std::unique_ptr<ub2[]> vidFieldSizeArray(new ub2[events.size()]); - std::unique_ptr<ub2[]> fSeqFieldSizeArray(new ub2[events.size()]); - std::unique_ptr<ub2[]> blockIdFieldSizeArray(new ub2[events.size()]); - std::unique_ptr<ub2[]> compressedSizeFieldSizeArray(new ub2[events.size()]); - std::unique_ptr<ub2[]> copyNbFieldSizeArray(new ub2[events.size()]); - std::unique_ptr<ub2[]> creationTimeFieldSizeArray(new ub2[events.size()]); - std::unique_ptr<ub2[]> archiveFileIdFieldSizeArray(new ub2[events.size()]); - uint32_t i = 0; + TapeFileBatch tapeFileBatch(events.size()); + for (const auto &event: events) { checkTapeFileWrittenFieldsAreSet(firstEvent); @@ -328,49 +315,20 @@ void OracleCatalogue::filesWrittenToTape(const std::list<TapeFileWritten> &event if (expectedFSeq != event.fSeq) { exception::Exception ex; ex.getMessage() << "FSeq mismatch for tape " << firstEvent.vid << ": expected=" << expectedFSeq << " actual=" << - firstEvent.fSeq; + firstEvent.fSeq; throw ex; } expectedFSeq++; totalCompressedBytesWritten += event.compressedSize; - vidFieldSizeArray[i] = event.vid.length() + 1; - if (vidFieldSizeArray[i] > vidMaxFieldSize) { - vidMaxFieldSize = vidFieldSizeArray[i]; - } - - const std::string fSeqStr = std::to_string(event.fSeq); - fSeqFieldSizeArray[i] = fSeqStr.length() + 1; - if (fSeqFieldSizeArray[i] > fSeqMaxFieldSize) { - fSeqMaxFieldSize = fSeqFieldSizeArray[i]; - } - - const std::string blockIdStr = std::to_string(event.blockId); - blockIdFieldSizeArray[i] = blockIdStr.length() + 1; - if (blockIdFieldSizeArray[i] > blockIdMaxFieldSize) { - blockIdMaxFieldSize = blockIdFieldSizeArray[i]; - } - - const std::string compressedSizeStr = std::to_string(event.compressedSize); - compressedSizeFieldSizeArray[i] = compressedSizeStr.length() + 1; - if (compressedSizeFieldSizeArray[i] > compressedSizeMaxFieldSize) { - compressedSizeMaxFieldSize = compressedSizeFieldSizeArray[i]; - } - - const std::string copyNbStr = std::to_string(event.copyNb); - copyNbFieldSizeArray[i] = copyNbStr.length() + 1; - if (copyNbFieldSizeArray[i] > copyNbMaxFieldSize) { - copyNbMaxFieldSize = copyNbFieldSizeArray[i]; - } - - creationTimeFieldSizeArray[i] = creationTimeMaxFieldSize; - - const std::string archiveFileIdStr = std::to_string(event.archiveFileId); - archiveFileIdFieldSizeArray[i] = archiveFileIdStr.length() + 1; - if (archiveFileIdFieldSizeArray[i] > archiveFileIdMaxFieldSize) { - archiveFileIdMaxFieldSize = archiveFileIdFieldSizeArray[i]; - } + tapeFileBatch.vid.setFieldLenToValueLen(i, event.vid); + tapeFileBatch.fSeq.setFieldLenToValueLen(i, event.fSeq); + tapeFileBatch.blockId.setFieldLenToValueLen(i, event.blockId); + tapeFileBatch.compressedSize.setFieldLenToValueLen(i, event.compressedSize); + tapeFileBatch.copyNb.setFieldLenToValueLen(i, event.copyNb); + tapeFileBatch.creationTime.setFieldLen(i, creationTimeMaxFieldSize); + tapeFileBatch.archiveFileId.setFieldLenToValueLen(i, event.archiveFileId); i++; } @@ -404,52 +362,15 @@ void OracleCatalogue::filesWrittenToTape(const std::list<TapeFileWritten> &event } } - std::unique_ptr<char[]> vidCStrArray(new char[events.size() * vidMaxFieldSize]); - std::unique_ptr<char[]> fSeqCStrArray(new char[events.size() * fSeqMaxFieldSize]); - std::unique_ptr<char[]> blockIdCStrArray(new char[events.size() * blockIdMaxFieldSize]); - std::unique_ptr<char[]> compressedSizeCStrArray(new char[events.size() * compressedSizeMaxFieldSize]); - std::unique_ptr<char[]> copyNbCStrArray(new char[events.size() * copyNbMaxFieldSize]); - std::unique_ptr<char[]> creationTimeCStrArray(new char[events.size() * creationTimeMaxFieldSize]); - std::unique_ptr<char[]> archiveFileIdCStrArray(new char[events.size() * archiveFileIdMaxFieldSize]); - i = 0; for (const auto &event: events) { - { - char *const element = vidCStrArray.get() + i * vidMaxFieldSize; - strncpy(element, event.vid.c_str(), vidMaxFieldSize); - element[vidMaxFieldSize - 1] = '\0'; - } - { - char *const element = fSeqCStrArray.get() + i * fSeqMaxFieldSize; - strncpy(element, std::to_string(event.fSeq).c_str(), fSeqMaxFieldSize); - element[fSeqMaxFieldSize - 1] = '\0'; - } - { - char *const element = blockIdCStrArray.get() + i * blockIdMaxFieldSize; - strncpy(element, std::to_string(event.blockId).c_str(), blockIdMaxFieldSize); - element[blockIdMaxFieldSize - 1] = '\0'; - } - { - char *const element = compressedSizeCStrArray.get() + i * compressedSizeMaxFieldSize; - strncpy(element, std::to_string(event.compressedSize).c_str(), compressedSizeMaxFieldSize); - element[compressedSizeMaxFieldSize - 1] = '\0'; - } - { - char *const element = copyNbCStrArray.get() + i * copyNbMaxFieldSize; - strncpy(element, std::to_string(event.copyNb).c_str(), copyNbMaxFieldSize); - element[copyNbMaxFieldSize - 1] = '\0'; - } - { - char *const element = creationTimeCStrArray.get() + i * creationTimeMaxFieldSize; - strncpy(element, nowStr.c_str(), creationTimeMaxFieldSize); - element[creationTimeMaxFieldSize - 1] = '\0'; - } - { - char *const element = archiveFileIdCStrArray.get() + i * archiveFileIdMaxFieldSize; - strncpy(element, std::to_string(event.archiveFileId).c_str(), archiveFileIdMaxFieldSize); - element[archiveFileIdMaxFieldSize - 1] = '\0'; - } - + tapeFileBatch.vid.copyStrIntoField(i, event.vid.c_str()); + tapeFileBatch.fSeq.copyStrIntoField(i, std::to_string(event.fSeq)); + tapeFileBatch.blockId.copyStrIntoField(i, std::to_string(event.blockId)); + tapeFileBatch.compressedSize.copyStrIntoField(i, std::to_string(event.compressedSize)); + tapeFileBatch.copyNb.copyStrIntoField(i, std::to_string(event.copyNb)); + tapeFileBatch.creationTime.copyStrIntoField(i, nowStr); + tapeFileBatch.archiveFileId.copyStrIntoField(i, std::to_string(event.archiveFileId)); i++; } @@ -472,21 +393,14 @@ void OracleCatalogue::filesWrittenToTape(const std::list<TapeFileWritten> &event ":ARCHIVE_FILE_ID)"; auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); rdbms::OcciStmt &occiStmt = dynamic_cast<rdbms::OcciStmt &>(*stmt); - occiStmt->setDataBuffer(occiStmt.getParamIdx(":VID"), vidCStrArray.get(), oracle::occi::OCCI_SQLT_STR, - vidMaxFieldSize, vidFieldSizeArray.get()); - occiStmt->setDataBuffer(occiStmt.getParamIdx(":FSEQ"), fSeqCStrArray.get(), oracle::occi::OCCI_SQLT_STR, - fSeqMaxFieldSize, fSeqFieldSizeArray.get()); - occiStmt->setDataBuffer(occiStmt.getParamIdx(":BLOCK_ID"), blockIdCStrArray.get(), oracle::occi::OCCI_SQLT_STR, - blockIdMaxFieldSize, blockIdFieldSizeArray.get()); - occiStmt->setDataBuffer(occiStmt.getParamIdx(":COMPRESSED_SIZE_IN_BYTES"), compressedSizeCStrArray.get(), - oracle::occi::OCCI_SQLT_STR, compressedSizeMaxFieldSize, compressedSizeFieldSizeArray.get()); - occiStmt->setDataBuffer(occiStmt.getParamIdx(":COPY_NB"), copyNbCStrArray.get(), oracle::occi::OCCI_SQLT_STR, - copyNbMaxFieldSize, copyNbFieldSizeArray.get()); - occiStmt->setDataBuffer(occiStmt.getParamIdx(":CREATION_TIME"), creationTimeCStrArray.get(), - oracle::occi::OCCI_SQLT_STR, creationTimeMaxFieldSize, creationTimeFieldSizeArray.get()); - occiStmt->setDataBuffer(occiStmt.getParamIdx(":ARCHIVE_FILE_ID"), archiveFileIdCStrArray.get(), - oracle::occi::OCCI_SQLT_STR, archiveFileIdMaxFieldSize, archiveFileIdFieldSizeArray.get()); - occiStmt->executeArrayUpdate(events.size()); + occiStmt.setColumn(tapeFileBatch.vid); + occiStmt.setColumn(tapeFileBatch.fSeq); + occiStmt.setColumn(tapeFileBatch.blockId); + occiStmt.setColumn(tapeFileBatch.compressedSize); + occiStmt.setColumn(tapeFileBatch.copyNb); + occiStmt.setColumn(tapeFileBatch.creationTime); + occiStmt.setColumn(tapeFileBatch.archiveFileId); + occiStmt->executeArrayUpdate(tapeFileBatch.nbRows); conn.commit(); diff --git a/catalogue/OracleCatalogue.hpp b/catalogue/OracleCatalogue.hpp index f2b39843ed72fbb2a73751128cabebe91c07baa8..f2a5089223f6d7b9d7172e27cfaedfb3a0ed672f 100644 --- a/catalogue/OracleCatalogue.hpp +++ b/catalogue/OracleCatalogue.hpp @@ -19,6 +19,10 @@ #pragma once #include "catalogue/RdbmsCatalogue.hpp" +#include "rdbms/OcciColumn.hpp" + +#include <occi.h> +#include <string.h> namespace cta { namespace catalogue { @@ -98,6 +102,28 @@ private: */ common::dataStructures::Tape selectTapeForUpdate(rdbms::PooledConn &conn, const std::string &vid); + struct TapeFileBatch { + size_t nbRows; + rdbms::OcciColumn vid; + rdbms::OcciColumn fSeq; + rdbms::OcciColumn blockId; + rdbms::OcciColumn compressedSize; + rdbms::OcciColumn copyNb; + rdbms::OcciColumn creationTime; + rdbms::OcciColumn archiveFileId; + + TapeFileBatch(const size_t nbRowsValue): + nbRows(nbRowsValue), + vid("VID", nbRows), + fSeq("FSEQ", nbRows), + blockId("BLOCK_ID", nbRows), + compressedSize("COMPRESSED_SIZE_IN_BYTES", nbRows), + copyNb("COPY_NB", nbRows), + creationTime("CREATION_TIME", nbRows), + archiveFileId("ARCHIVE_FILE_ID", nbRows) { + } + }; + }; // class OracleCatalogue } // namespace catalogue diff --git a/rdbms/CMakeLists.txt b/rdbms/CMakeLists.txt index 2211e1211d863e23fb19876b1b9e5cdd761c3322..d96294db9bd6bb9756f3fbd54cc98a2d6b63ea6d 100644 --- a/rdbms/CMakeLists.txt +++ b/rdbms/CMakeLists.txt @@ -59,6 +59,7 @@ if (OCCI_SUPPORT) set (RDBMS_LIB_SRC_FILES ${RDBMS_LIB_SRC_FILES} ConnFactoryFactory.cpp + OcciColumn.cpp OcciConn.cpp OcciConnFactory.cpp OcciEnv.cpp @@ -89,11 +90,20 @@ target_link_libraries (ctardbms install (TARGETS ctardbms DESTINATION usr/${CMAKE_INSTALL_LIBDIR}) -set(RDBMS_UNIT_TESTS_LIB_SRC_FILES - ConnPoolTest.cpp - LoginTest.cpp - ParamNameToIdxTest.cpp - SqliteStmtTest.cpp) +if (OCCI_SUPPORT) + set(RDBMS_UNIT_TESTS_LIB_SRC_FILES + ConnPoolTest.cpp + LoginTest.cpp + OcciColumnTest.cpp + ParamNameToIdxTest.cpp + SqliteStmtTest.cpp) +else (OCCI_SUPPORT) + set(RDBMS_UNIT_TESTS_LIB_SRC_FILES + ConnPoolTest.cpp + LoginTest.cpp + ParamNameToIdxTest.cpp + SqliteStmtTest.cpp) +endif (OCCI_SUPPORT) add_library (ctardbmsunittests SHARED ${RDBMS_UNIT_TESTS_LIB_SRC_FILES}) diff --git a/rdbms/OcciColumn.cpp b/rdbms/OcciColumn.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7e29e1193857497356916e7ee28d6a7dd9bbf649 --- /dev/null +++ b/rdbms/OcciColumn.cpp @@ -0,0 +1,153 @@ +/* + * 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 "rdbms/OcciColumn.hpp" + +namespace cta { +namespace rdbms { + +//------------------------------------------------------------------------------ +// constructor +//------------------------------------------------------------------------------ +OcciColumn::OcciColumn(const std::string &colName, const size_t nbRows): + m_colName(colName), + m_nbRows(nbRows), + m_maxFieldLength(0) { + m_fieldLengths.reset(new ub2[m_nbRows]); + ub2 *fieldLengths = m_fieldLengths.get(); + if (nullptr == fieldLengths) { + exception::Exception ex; + ex.getMessage() << __FUNCTION__ << " failed: colName=" << m_colName << ": Failed to allocate array of field" + " lengths for database column: nbRows=" << nbRows; + throw ex; + } + for(size_t i = 0; i < m_nbRows; i++) { + fieldLengths[i] = 0; + } +} + +//------------------------------------------------------------------------------ +// getColName +//------------------------------------------------------------------------------ +const std::string &OcciColumn::getColName() const { + return m_colName; +} + +//------------------------------------------------------------------------------ +// getNbRows +//------------------------------------------------------------------------------ +size_t OcciColumn::getNbRows() const { + return m_nbRows; +} + +//------------------------------------------------------------------------------ +// setFieldLen +//------------------------------------------------------------------------------ +void OcciColumn::setFieldLen(const size_t index, const ub2 length) { + try { + if (nullptr != m_buffer.get()) { + exception::Exception ex; + ex.getMessage() << "Failed to set length at index " << index << " to value " << length << ": " + "A field length cannot be set after the column buffer has been allocated"; + throw ex; + } + if (index > m_nbRows - 1) { + exception::Exception ex; + ex.getMessage() << "Failed to set length at index " << index << " to value " << length << ": " + "Index is greater than permitted maximum of " << (m_nbRows - 1); + throw ex; + } + m_fieldLengths[index] = length; + if (length > m_maxFieldLength) { + m_maxFieldLength = length; + } + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: colName=" + m_colName + ": " + + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getFieldLength +//------------------------------------------------------------------------------ +ub2 *OcciColumn::getFieldLengths() { + return m_fieldLengths.get(); +} + +//------------------------------------------------------------------------------ +// getBuffer +//------------------------------------------------------------------------------ +char *OcciColumn::getBuffer() { + try { + if (nullptr == m_buffer.get()) { + const size_t bufSize = m_nbRows * m_maxFieldLength; + + if (0 == bufSize) { + exception::Exception ex; + ex.getMessage() << __FUNCTION__ << " failed: Failed to allocate buffer for database column:" + " The size of the buffer to be allocated is zero which is invalid"; + throw ex; + } + + m_buffer.reset(new char[bufSize]); + if (nullptr == m_buffer.get()) { + exception::Exception ex; + ex.getMessage() << __FUNCTION__ << " failed: Failed to allocate buffer for database column:" + " bufSize=" << bufSize; + throw ex; + } + } + return m_buffer.get(); + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: colName=" + m_colName + ": " + + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getMaxFieldLength +//------------------------------------------------------------------------------ +ub2 OcciColumn::getMaxFieldLength() const { + return m_maxFieldLength; +} + +//------------------------------------------------------------------------------ +// copyStrIntoField +//------------------------------------------------------------------------------ +void OcciColumn::copyStrIntoField(const size_t index, const std::string &str) { + try { + const size_t strLenIncludingNull = str.length() + 1; + if(strLenIncludingNull > m_maxFieldLength) { + exception::Exception ex; + ex.getMessage() << "String length including the null terminator is greater than the maximum field length:" + " strLenIncludingNull=" << strLenIncludingNull << " maxFieldLength=" << m_maxFieldLength; + throw ex; + } + char *const buf = getBuffer(); + char *const element = buf + index * m_maxFieldLength; + strncpy(element, str.c_str(), m_maxFieldLength); + element[m_maxFieldLength - 1] = '\0'; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: colName=" + m_colName + ": " + + ex.getMessage().str()); + } +} + +} // namespace rdbms +} // namespace cta diff --git a/rdbms/OcciColumn.hpp b/rdbms/OcciColumn.hpp new file mode 100644 index 0000000000000000000000000000000000000000..934892694ebcbf4ef69c6ef95536b127591fa6ea --- /dev/null +++ b/rdbms/OcciColumn.hpp @@ -0,0 +1,169 @@ +/* + * 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/>. + */ + +#pragma once + +#include "catalogue/RdbmsCatalogue.hpp" + +#include <occi.h> +#include <string.h> +#include <typeinfo> + +namespace cta { +namespace rdbms { + +/** + * A class to help with preparing batch inserts and updatesi with the OCCI + * interface. + */ +class OcciColumn { +public: + /** + * Constructor. + * + * @param colName The name of the column. + * @param nbRows The number of rows in the column. + */ + OcciColumn(const std::string &colName, const size_t nbRows); + + /** + * Returns the name of the column. + * + * @return The name of the column. + */ + const std::string &getColName() const; + + /** + * Returns the number of rows in the column. + * + * @return The number of rows in the column. + */ + size_t getNbRows() const; + + /** + * Sets the length of the field at the specified index to the length of the + * specified value. + * + * Please not that this method does not record the specified value. This + * method only stores the length of the value. + * + * @param index The index of the field. + * @param value The value whose length is to be recorded. + */ + template<typename T> void setFieldLenToValueLen(const size_t index, const T &value) { + setFieldLenToValueLen(index, value, std::is_integral<T>()); + } + +private: + /** + * Sets the length of the field at the specified index to the length of the + * specified value. + * + * Please not that this method does not record the specified value. This + * method only stores the length of the value. + * + * @param index The index of the field. + * @param value The value whose length is to be recorded. + */ + template<typename T> void setFieldLenToValueLen(const size_t index, const T &value, std::false_type) { + setFieldLen(index, value.length() + 1); + } + + /** + * Sets the length of the field at the specified index to the length of the + * specified value. + * + * Please not that this method does not record the specified value. This + * method only stores the length of the value. + * + * @param index The index of the field. + * @param value The value whose length is to be recorded. + */ + template<typename T> void setFieldLenToValueLen(const size_t index, const T &value, std::true_type) { + setFieldLen(index, std::to_string(value).length() + 1); + } + +public: + /** + * Sets the length of the field at the specified index. + * + * @param index The index of the field. + * @param length The length of the field. + */ + void setFieldLen(const size_t index, const ub2 length); + + /** + * Returns the array of field lengths. + * + * @return The array of field lengths. + */ + ub2 *getFieldLengths(); + + /** + * Returns the buffer of column field values. + * + * @return The buffer of column field values. + */ + char *getBuffer(); + + /** + * Returns the maximum of the field lengths. + * + * @return the maximum of the field lengths. + */ + ub2 getMaxFieldLength() const; + + /** + * Copies the specified string value into the field at the specified index. + * + * @param index The index of the field. + * @param str The string value. + */ + void copyStrIntoField(const size_t index, const std::string &str); + +private: + + /** + * The name of the column. + */ + std::string m_colName; + + /** + * The number of rows in the column. + */ + size_t m_nbRows; + + /** + * The buffer of column field values. + */ + std::unique_ptr<char[]> m_buffer; + + /** + * The array of field lengths. + */ + std::unique_ptr<ub2[]> m_fieldLengths; + + /** + * The maximum of all the field lengths. + */ + ub2 m_maxFieldLength; + +}; // OcciColumn + +} // namespace rdbms +} // namespace cta diff --git a/rdbms/OcciColumnTest.cpp b/rdbms/OcciColumnTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c9ea054c3334bb0b0baaac69db9c4134d5fdbdea --- /dev/null +++ b/rdbms/OcciColumnTest.cpp @@ -0,0 +1,250 @@ +/* + * 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 "OcciColumn.hpp" + +#include <gtest/gtest.h> + +namespace unitTests { + +class cta_rdbms_OcciColumnTest : public ::testing::Test { +protected: + + virtual void SetUp() { + } + + virtual void TearDown() { + } +}; + +TEST_F(cta_rdbms_OcciColumnTest, getColName) { + using namespace cta; + using namespace cta::rdbms; + + const std::string colName = "TEST_COLUMN"; + const size_t nbRows = 1; + OcciColumn col(colName, nbRows); + + ASSERT_EQ(colName, col.getColName()); +} + +TEST_F(cta_rdbms_OcciColumnTest, getNbRows) { + using namespace cta; + using namespace cta::rdbms; + + const std::string colName = "TEST_COLUMN"; + const size_t nbRows = 1; + OcciColumn col(colName, nbRows); + + ASSERT_EQ(nbRows, col.getNbRows()); +} + +TEST_F(cta_rdbms_OcciColumnTest, setFieldLen) { + using namespace cta; + using namespace cta::rdbms; + + const std::string colName = "TEST_COLUMN"; + const size_t nbRows = 1; + OcciColumn col(colName, nbRows); + + ASSERT_EQ(0, col.getMaxFieldLength()); + + const ub2 field0Len = 1234; + col.setFieldLen(0, field0Len); + ASSERT_EQ(field0Len, col.getMaxFieldLength()); +} + +TEST_F(cta_rdbms_OcciColumnTest, setFieldLenToValueLen_stringValue) { + using namespace cta; + using namespace cta::rdbms; + + const std::string colName = "TEST_COLUMN"; + const size_t nbRows = 1; + OcciColumn col(colName, nbRows); + + ASSERT_EQ(0, col.getMaxFieldLength()); + + const std::string field0Value = "field0Value"; + col.setFieldLenToValueLen(0, field0Value); + ASSERT_EQ(field0Value.length() + 1, col.getMaxFieldLength()); +} + +TEST_F(cta_rdbms_OcciColumnTest, setFieldLenToValueLen_uint64_tValue) { + using namespace cta; + using namespace cta::rdbms; + + const std::string colName = "TEST_COLUMN"; + const size_t nbRows = 1; + OcciColumn col(colName, nbRows); + + ASSERT_EQ(0, col.getMaxFieldLength()); + + const uint64_t field0Value = 1234; + col.setFieldLenToValueLen(0, field0Value); + ASSERT_EQ(5, col.getMaxFieldLength()); +} + +TEST_F(cta_rdbms_OcciColumnTest, setFieldLen_tooLate) { + using namespace cta; + using namespace cta::rdbms; + + const std::string colName = "TEST_COLUMN"; + const size_t nbRows = 2; + OcciColumn col(colName, nbRows); + + const ub2 field0Len = 1234; + const ub2 field1Len = 5678; + col.setFieldLen(0, field0Len); + + col.getBuffer(); + + ASSERT_THROW(col.setFieldLen(1, field1Len), exception::Exception); +} + +TEST_F(cta_rdbms_OcciColumnTest, setFieldLen_invalidIndex) { + using namespace cta; + using namespace cta::rdbms; + + const std::string colName = "TEST_COLUMN"; + const size_t nbRows = 1; + OcciColumn col(colName, nbRows); + + const ub2 field1Len = 5678; + ASSERT_THROW(col.setFieldLen(1, field1Len), exception::Exception); +} + +TEST_F(cta_rdbms_OcciColumnTest, getFieldLengths) { + using namespace cta; + using namespace cta::rdbms; + + const std::string colName = "TEST_COLUMN"; + const size_t nbRows = 3; + OcciColumn col(colName, nbRows); + + const ub2 field0Len = 1234; + const ub2 field1Len = 5678; + const ub2 field2Len = 9012; + col.setFieldLen(0, field0Len); + col.setFieldLen(1, field1Len); + col.setFieldLen(2, field2Len); + + ub2 *const fieldLens = col.getFieldLengths(); + + ASSERT_EQ(field0Len, fieldLens[0]); + ASSERT_EQ(field1Len, fieldLens[1]); + ASSERT_EQ(field2Len, fieldLens[2]); +} + +TEST_F(cta_rdbms_OcciColumnTest, getBuffer) { + using namespace cta; + using namespace cta::rdbms; + + const std::string colName = "TEST_COLUMN"; + const size_t nbRows = 1; + OcciColumn col(colName, nbRows); + + const ub2 field0Len = 1234; + col.setFieldLen(0, field0Len); + + char *const buf = col.getBuffer(); + ASSERT_NE(nullptr, buf); +} + +TEST_F(cta_rdbms_OcciColumnTest, getBuffer_tooEarly) { + using namespace cta; + using namespace cta::rdbms; + + const std::string colName = "TEST_COLUMN"; + const size_t nbRows = 1; + OcciColumn col(colName, nbRows); + + ASSERT_THROW(col.getBuffer(), exception::Exception); +} + +TEST_F(cta_rdbms_OcciColumnTest, getMaxFieldLength) { + using namespace cta; + using namespace cta::rdbms; + + const std::string colName = "TEST_COLUMN"; + const size_t nbRows = 4; + OcciColumn col(colName, nbRows); + + const ub2 field0Len = 1234; + const ub2 field1Len = 5678; + const ub2 field2Len = 9012; + const ub2 field3Len = 3456; + col.setFieldLen(0, field0Len); + col.setFieldLen(1, field1Len); + col.setFieldLen(2, field2Len); + col.setFieldLen(3, field3Len); + + ASSERT_EQ(field2Len, col.getMaxFieldLength()); +} + +TEST_F(cta_rdbms_OcciColumnTest, copyStrIntoField_1_oneField) { + using namespace cta; + using namespace cta::rdbms; + + const std::string colName = "TEST_COLUMN"; + const size_t nbRows = 1; + OcciColumn col(colName, nbRows); + + const std::string field0Value = "FIELD 0 VALUE"; + col.setFieldLen(0, field0Value.length() + 1); + col.copyStrIntoField(0, field0Value); + + char *const buf = col.getBuffer(); + ASSERT_EQ(field0Value, std::string(buf)); +} + +TEST_F(cta_rdbms_OcciColumnTest, copyStrIntoField_twoFields) { + using namespace cta; + using namespace cta::rdbms; + + const std::string colName = "TEST_COLUMN"; + const size_t nbRows = 2; + OcciColumn col(colName, nbRows); + + const std::string field0Value = "FIELD 0 VALUE"; + const std::string field1Value = "FIELD 1 VALUE"; + col.setFieldLen(0, field0Value.length() + 1); + col.setFieldLen(1, field1Value.length() + 1); + col.copyStrIntoField(0, field0Value); + col.copyStrIntoField(1, field1Value); + + char *const buf = col.getBuffer(); + const char *const bufField0 = buf; + const char *const bufField1 = buf + col.getMaxFieldLength(); + ASSERT_EQ(field0Value, std::string(bufField0)); + ASSERT_EQ(field1Value, std::string(bufField1)); +} + +TEST_F(cta_rdbms_OcciColumnTest, copyStrIntoField_tooLong) { + using namespace cta; + using namespace cta::rdbms; + + const std::string colName = "TEST_COLUMN"; + const size_t nbRows = 1; + OcciColumn col(colName, nbRows); + + const std::string field0Value = "FIELD 0 VALUE"; + col.setFieldLen(0, 1); + ASSERT_THROW(col.copyStrIntoField(0, field0Value), exception::Exception); +} + +} // namespace unitTests diff --git a/rdbms/OcciStmt.cpp b/rdbms/OcciStmt.cpp index e89df1844105f69cf26a0907caaac58b81785bb8..fe5227145f1ed428b8a0ed35b9b2f04ffbcd4a1b 100644 --- a/rdbms/OcciStmt.cpp +++ b/rdbms/OcciStmt.cpp @@ -18,6 +18,7 @@ #include "common/exception/Exception.hpp" #include "common/make_unique.hpp" +#include "rdbms/OcciColumn.hpp" #include "rdbms/OcciConn.hpp" #include "rdbms/OcciRset.hpp" #include "rdbms/OcciStmt.hpp" @@ -238,6 +239,16 @@ oracle::occi::Statement *OcciStmt::operator->() const { return get(); } +//------------------------------------------------------------------------------ +// setColumn +//------------------------------------------------------------------------------ +void OcciStmt::setColumn(OcciColumn &col) { + const std::string paramName = std::string(":") + col.getColName(); + const auto paramIdx = getParamIdx(paramName); + m_stmt->setDataBuffer(paramIdx, col.getBuffer(), oracle::occi::OCCI_SQLT_STR, + col.getMaxFieldLength(), col.getFieldLengths()); +} + //------------------------------------------------------------------------------ // connShouldBeClosed //------------------------------------------------------------------------------ diff --git a/rdbms/OcciStmt.hpp b/rdbms/OcciStmt.hpp index bbfdfc05cfdec49189885a2ecd458e97ac0812b5..7508628f32be64cb44cff6bff678c49df6ccdcf5 100644 --- a/rdbms/OcciStmt.hpp +++ b/rdbms/OcciStmt.hpp @@ -40,6 +40,8 @@ class OcciConn; */ class OcciRset; +class OcciColumn; + /** * A convenience wrapper around an OCCI prepared statement. */ @@ -150,6 +152,13 @@ public: */ oracle::occi::Statement *operator->() const; + /** + * Sets the specified column data for a batch-based database access. + * + * @param The column data. + */ + void setColumn(OcciColumn &col); + private: /**