diff --git a/catalogue/CMakeLists.txt b/catalogue/CMakeLists.txt index 195abc232c1c581f49ecef908fce1eed05730bcc..a2b0588a39e0658aafa4fdcbd2b5904d6ba85292 100644 --- a/catalogue/CMakeLists.txt +++ b/catalogue/CMakeLists.txt @@ -24,6 +24,7 @@ set (CATALOGUE_LIB_SRC_FILES ArchiveFileRow.cpp Catalogue.cpp DbLogin.cpp + DbRset.cpp TapeFileWritten.cpp OcciConn.cpp OcciEnv.cpp diff --git a/catalogue/DbRset.cpp b/catalogue/DbRset.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ac7d77bf3d24cbbf3b31ddadfe70a0805ceebf2b --- /dev/null +++ b/catalogue/DbRset.cpp @@ -0,0 +1,31 @@ +/* + * 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 "catalogue/DbRset.hpp" + +namespace cta { +namespace catalogue { + +//------------------------------------------------------------------------------ +// destructor +//------------------------------------------------------------------------------ +DbRset::~DbRset() throw() { +} + +} // namespace catalogue +} // namespace cta diff --git a/catalogue/DbRset.hpp b/catalogue/DbRset.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8e6dc17dc9b38c42bf18657e4b3b22dabe31deec --- /dev/null +++ b/catalogue/DbRset.hpp @@ -0,0 +1,100 @@ +/* + * 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 <stdint.h> + +namespace cta { +namespace catalogue { + +/** + * Abstract class specificing the interface to the result set of an sql query. + * + * Please note that this interface intentionally uses C-strings instead of + * std::string so that it can be used by code compiled against the CXX11 ABI and + * by code compiled against a pre-CXX11 ABI. + */ +class DbRset { +public: + + /** + * Destructor. + * + * Please note that this method will delete the memory asscoiated with any + * C-strings returned by the columnText() method. + */ + virtual ~DbRset() throw() = 0; + + /** + * Returns the SQL statement. + * + * @return The SQL statement. + */ + virtual const char *getSql() const = 0; + + /** + * Attempts to get the next row of the result set. + * + * Please note that this method will delete the memory associated with any + * C-strings returned by the columnText() method. + * + * @return True if a row has been retrieved else false if there are no more + * rows in the result set. + */ + virtual bool next() = 0; + + /** + * Returns true if the specified column contains a null value. + * + * @param colName The name of the column. + * @return True if the specified column contains a null value. + */ + virtual bool columnIsNull(const char *const colName) const = 0; + + /** + * Returns the value of the specified column as a string. + * + * Please note that a C-string is returned instead of an std::string so that + * this method can be used by code compiled against the CXX11 ABI and by code + * compiled against a pre-CXX11 ABI. + * + * Please note that if the value of the column is NULL within the database + * then an empty string shall be returned. Use the columnIsNull() method to + * determine whether not a column contains a NULL value. + * + * @param colName The name of the column. + * @return The string value of the specified column. Please note that the + * returned string should not be deleted. The string should be copied before + * the next call to the next() method. The DbRset class is responsible + * for freeing the memory. + */ + virtual const char *columnText(const char *const colName) const = 0; + + /** + * Returns the value of the specified column as an integer. + * + * @param colName The name of the column. + * @return The value of the specified column. + */ + virtual uint64_t columnUint64(const char *const colName) const = 0; + +}; // class DbRset + +} // namespace catalogue +} // namespace cta diff --git a/catalogue/OcciRset.cpp b/catalogue/OcciRset.cpp index 8ee0f2668e522e8b91ce314a29cb693cb82484a3..5468c9152383e1472a78be1fc9e3929f2bd78217 100644 --- a/catalogue/OcciRset.cpp +++ b/catalogue/OcciRset.cpp @@ -225,6 +225,41 @@ OcciRset::~OcciRset() throw() { } } +//------------------------------------------------------------------------------ +// getSql +//------------------------------------------------------------------------------ +const char *OcciRset::getSql() const { + return m_stmt.getSql(); +} + +//------------------------------------------------------------------------------ +// next +//------------------------------------------------------------------------------ +bool OcciRset::next() { + using namespace oracle; + + try { + m_textColumnCache->clear(); + const occi::ResultSet::Status status = m_rset->next(); + return occi::ResultSet::DATA_AVAILABLE == status; + } catch(std::exception &ne) { + throw std::runtime_error(std::string(__FUNCTION__) + " failed for SQL statement " + m_stmt.getSql() + ": " + + ne.what()); + } +} + +//------------------------------------------------------------------------------ +// columnIsNull +//------------------------------------------------------------------------------ +bool OcciRset::columnIsNull(const char *const colName) const { + try { + const int colIdx = m_colNameToIdx->getIdx(colName); + return m_rset->isNull(colIdx); + } catch(std::exception &ne) { + throw std::runtime_error(std::string(__FUNCTION__) + " failed: " + ne.what()); + } +} + //------------------------------------------------------------------------------ // close //------------------------------------------------------------------------------ @@ -251,26 +286,10 @@ oracle::occi::ResultSet *OcciRset::operator->() const { return get(); } -//------------------------------------------------------------------------------ -// next -//------------------------------------------------------------------------------ -bool OcciRset::next() { - using namespace oracle; - - try { - m_textColumnCache->clear(); - const occi::ResultSet::Status status = m_rset->next(); - return occi::ResultSet::DATA_AVAILABLE == status; - } catch(std::exception &ne) { - throw std::runtime_error(std::string(__FUNCTION__) + " failed for SQL statement " + m_stmt.getSql() + ": " + - ne.what()); - } -} - //------------------------------------------------------------------------------ // columnText //------------------------------------------------------------------------------ -const char * OcciRset::columnText(const char *const colName) { +const char * OcciRset::columnText(const char *const colName) const { try { std::lock_guard<std::mutex> lock(m_mutex); @@ -293,5 +312,20 @@ const char * OcciRset::columnText(const char *const colName) { } } +//------------------------------------------------------------------------------ +// columnUint64 +//------------------------------------------------------------------------------ +uint64_t OcciRset::columnUint64(const char *const colName) const { + try { + std::lock_guard<std::mutex> lock(m_mutex); + + const int colIdx = m_colNameToIdx->getIdx(colName); + return m_rset->getUInt(colIdx); + } catch(std::exception &ne) { + throw std::runtime_error(std::string(__FUNCTION__) + " failed for SQL statement " + m_stmt.getSql() + ": " + + ne.what()); + } +} + } // namespace catalogue } // namespace cta diff --git a/catalogue/OcciRset.hpp b/catalogue/OcciRset.hpp index 791304632bc5136278c1f229b0fa7fa3f9161d37..326a5eb15253633431e11ab1565bb20f69422804 100644 --- a/catalogue/OcciRset.hpp +++ b/catalogue/OcciRset.hpp @@ -18,6 +18,8 @@ #pragma once +#include "catalogue/DbRset.hpp" + #include <memory> #include <mutex> #include <occi.h> @@ -26,7 +28,7 @@ namespace cta { namespace catalogue { /** - * Forward declaraion to avoid a circular dependency beween OcciRset and + * Forward declaration to avoid a circular dependency between OcciRset and * OcciStmt. */ class OcciStmt; @@ -34,7 +36,7 @@ class OcciStmt; /** * A convenience wrapper around an OCCI result set. */ -class OcciRset { +class OcciRset: public DbRset { public: /** @@ -50,62 +52,92 @@ public: /** * Destructor. - */ - ~OcciRset() throw(); - - /** - * Idempotent close() method. The destructor calls this method. - */ - void close(); - - /** - * Returns the underlying OCCI result set. - * - * This method will always return a valid pointer. * - * @return The underlying OCCI result set. + * Please note that this method will delete the memory asscoiated with any + * C-strings returned by the columnText() method. */ - oracle::occi::ResultSet *get() const; + virtual ~OcciRset() throw(); /** - * An alias for the get() method. + * Returns the SQL statement. * - * @return The underlying OCCI result set. + * @return The SQL statement. */ - oracle::occi::ResultSet *operator->() const; + virtual const char *getSql() const; /** * Attempts to get the next row of the result set. * + * Please note that this method will delete the memory associated with any + * C-strings returned by the columnText() method. + * * @return True if a row has been retrieved else false if there are no more * rows in the result set. */ - bool next(); + virtual bool next(); + + /** + * Returns true if the specified column contains a null value. + * + * @param colName The name of the column. + * @return True if the specified column contains a null value. + */ + virtual bool columnIsNull(const char *const colName) const; /** * Returns the value of the specified column as a string. * - * Please note that a C string is returned instead of an std::string so that + * Please note that a C-string is returned instead of an std::string so that * this method can be used by code compiled against the CXX11 ABI and by code - * compiled against the pre-CXX11 ABI. + * compiled against a pre-CXX11 ABI. * * Please note that if the value of the column is NULL within the database - * then a NULL pointer is returned. + * then an empty string shall be returned. Use the columnIsNull() method to + * determine whether not a column contains a NULL value. * * @param colName The name of the column. - * @return The string value of the specified column or NULL if the value of - * the column within the database is NULL. Please note that the caller should - * NOT delete the returned string. The string will be automatically deleted - * when OcciRset::next() is called or when the OcciRset destructor is called. + * @return The string value of the specified column. Please note that the + * returned string should not be deleted. The string should be copied before + * the next call to the next() method. The DbRset class is responsible + * for freeing the memory. */ - const char *columnText(const char *const colName); + virtual const char *columnText(const char *const colName) const; + + /** + * Returns the value of the specified column as an integer. + * + * @param colName The name of the column. + * @return The value of the specified column. + */ + virtual uint64_t columnUint64(const char *const colName) const; + + /** + * Idempotent close() method. The destructor calls this method. + */ + void close(); + + /** + * Returns the underlying OCCI result set. + * + * This method will always return a valid pointer. + * + * @return The underlying OCCI result set. + */ + oracle::occi::ResultSet *get() const; + + /** + * An alias for the get() method. + * + * @return The underlying OCCI result set. + */ + oracle::occi::ResultSet *operator->() const; private: /** * Mutex used to serialize access to this object. */ - std::mutex m_mutex; + mutable std::mutex m_mutex; /** * The OCCI statement. @@ -134,7 +166,20 @@ private: */ std::unique_ptr<ColumnNameToIdx> m_colNameToIdx; + /** + * Forward declaration of the nest class TextColumnCache that is intentionally + * hidden in the cpp file of the SqliteRset class. The class is hidden in + * order to enable the SqliteRset class to be used by code compiled against + * the CXX11 ABI and used by code compiled against the pre-CXX11 ABI. + */ class TextColumnCache; + + /** + * Map from column name to column text value. This map is used to cache the + * results of calling OcciRset::columnText() in the form of C-strings so that + * the OcciRset class can provide a similar memory management policy for + * C-strings as the SQLite API. + */ std::unique_ptr<TextColumnCache> m_textColumnCache; /** diff --git a/catalogue/OcciRsetTest.cpp b/catalogue/OcciRsetTest.cpp index c4bb08fa8295510b251a0ba3cdae069f53f85270..c59e60b7fac3a3e0a83f8ad976669c263a3a24bb 100644 --- a/catalogue/OcciRsetTest.cpp +++ b/catalogue/OcciRsetTest.cpp @@ -76,4 +76,25 @@ TEST_F(cta_catalogue_OcciRsetTest, executeQueryRelyOnRsetDestructorForCacheDelet ASSERT_EQ(std::string("X"), text); } +TEST_F(cta_catalogue_OcciRsetTest, eexcuteQuery_uint32_t) { + using namespace cta; + using namespace cta::catalogue; + + const DbLogin dbLogin = DbLogin::readFromFile(g_cmdLineArgs.oraDbConnFile); + OcciEnv env; + std::unique_ptr<OcciConn> conn(env.createConn( + dbLogin.username.c_str(), + dbLogin.password.c_str(), + dbLogin.database.c_str())); + const char *const sql = "SELECT 1234 AS I FROM DUAL"; + std::unique_ptr<OcciStmt> stmt(conn->createStmt(sql)); + std::unique_ptr<OcciRset> rset(stmt->executeQuery()); + ASSERT_TRUE(rset->next()); + const uint32_t i = rset->columnUint64("I"); + ASSERT_EQ(1234, i); + ASSERT_FALSE(rset->next()); +} + +// TODO - Implement 64-bit int test because the current code will fail + } // namespace unitTests diff --git a/catalogue/SqliteRset.hpp b/catalogue/SqliteRset.hpp index 5688f19d0478a23efe7c3195cef2137e450d18be..3733b22ad185d62999fd8c95e085a1ca7f3c02db 100644 --- a/catalogue/SqliteRset.hpp +++ b/catalogue/SqliteRset.hpp @@ -45,6 +45,9 @@ public: /** * Destructor. + * + * Please note that this method will delete the memory asscoiated with any + * C-strings returned by the columnText() method. */ ~SqliteRset() throw(); @@ -58,6 +61,9 @@ public: /** * Attempts to get the next row of the result set. * + * Please note that this method will delete the memory asscoiated with any + * C-strings returned by the columnText() method. + * * @return True if a row has been retrieved else false if there are no more * rows in the result set. */ @@ -74,7 +80,7 @@ public: /** * Returns the value of the specified column as a string. * - * Please note that a C string is returned instead of an std::string so that + * Please note that a C-string is returned instead of an std::string so that * this method can be used by code compiled against the CXX11 ABI and by code * compiled against a pre-CXX11 ABI. * @@ -132,7 +138,7 @@ private: */ void populateColNameToIdxAndTypeMap(); -}; // class SqlLiteStmt +}; // class SqlLiteRset } // namespace catalogue } // namespace cta