diff --git a/CHANGELOG.md b/CHANGELOG.md index 7883609082fd5b611d9aa4e97498c8e0282c7e19..aac52f38bbb918b6cdab7c7a1c295d7af3b8fe99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +## [0.11.0] - 2020-01-21 + +### Added + +- Added support for time to live feature. + +### Changed + +- Excluded large parts of PqxxExtension.hpp from liniting (since it conforms to pqxx coding standards) + ## [0.10.0] - 2019-12-06 ### Added diff --git a/include/hdb++/HdbppTimescaleDb.hpp b/include/hdb++/HdbppTimescaleDb.hpp index 6c7169f51cf77543152c0814af5f90928f7b3a41..e9d03900c35709b9009fc9d0eb093fbe0de91776 100644 --- a/include/hdb++/HdbppTimescaleDb.hpp +++ b/include/hdb++/HdbppTimescaleDb.hpp @@ -86,7 +86,7 @@ public: * @throw Tango::DevFailed */ virtual void configure_Attr( - std::string fqdn_attr_name, int type, int format, int write_type, unsigned int /* ttl */); + std::string fqdn_attr_name, int type, int format, int write_type, unsigned int ttl); /** * @brief Update the ttl value for an attribute. @@ -95,7 +95,7 @@ public: * is raised * * @param fqdn_attr_name Fully qualified attribute nam - * @param ttl The time to live in hour, 0 for infinity + * @param ttl The time to live in hours, 0 for infinity * @throw Tango::DevFailed */ virtual void updateTTL_Attr(std::string fqdn_attr_name, unsigned int ttl); diff --git a/src/DbConnection.cpp b/src/DbConnection.cpp index 9fcd8768d83700fcc88a2857df2bd5c3d364fda8..a2463ae7b733fc2e4ca393b63e01cb38f2f158f8 100644 --- a/src/DbConnection.cpp +++ b/src/DbConnection.cpp @@ -108,6 +108,7 @@ namespace pqxx_conn const string &att_family, const string &att_member, const string &att_name, + unsigned int ttl, const AttributeTraits &traits) { assert(!full_attr_name.empty()); @@ -160,6 +161,7 @@ namespace pqxx_conn att_family, att_member, att_name, + ttl, false, static_cast<unsigned int>(traits.type()), static_cast<unsigned int>(traits.formatType()), @@ -391,6 +393,7 @@ namespace pqxx_conn { tx.conn().prepare(_query_builder.storeDataEventErrorName(traits), _query_builder.storeDataEventErrorStatement(traits)); + spdlog::trace("Created prepared statement for: {}", _query_builder.storeDataEventErrorName(traits)); } @@ -413,6 +416,45 @@ namespace pqxx_conn } } + //============================================================================= + //============================================================================= + void DbConnection::storeAttributeTtl(const std::string &full_attr_name, unsigned int ttl) + { + assert(!full_attr_name.empty()); + assert(_conn != nullptr); + assert(_conf_id_cache != nullptr); + + checkConnection(LOCATION_INFO); + checkAttributeExists(full_attr_name, LOCATION_INFO); + + spdlog::trace("Setting ttl for attribute: {} to: {}", full_attr_name, ttl); + + try + { + // create and perform a pqxx transaction + pqxx::perform([&, this]() { + pqxx::work tx {(*_conn), StoreTtl}; + + if (!tx.prepared(StoreTtl).exists()) + { + tx.conn().prepare(StoreTtl, QueryBuilder::storeTtlStatement()); + spdlog::trace("Created prepared statement for: {}", StoreTtl); + } + + // no result expected + tx.exec_prepared0(StoreTtl, ttl, _conf_id_cache->value(full_attr_name)); + tx.commit(); + }); + } + catch (const pqxx::pqxx_exception &ex) + { + handlePqxxError("The attribute [" + full_attr_name + "] ttl [" + std::to_string(ttl) + "] was not saved.", + ex.base().what(), + QueryBuilder::storeTtlStatement(), + LOCATION_INFO); + } + } + //============================================================================= //============================================================================= string DbConnection::fetchLastHistoryEvent(const string &full_attr_name) diff --git a/src/DbConnection.hpp b/src/DbConnection.hpp index 3749f86b67cb5a23341f906b8069d29d3cddc08a..2a1b0351f5964f720856d6e328773da9f24e5994 100644 --- a/src/DbConnection.hpp +++ b/src/DbConnection.hpp @@ -73,6 +73,7 @@ namespace pqxx_conn const std::string &att_family, const std::string &att_member, const std::string &att_name, + unsigned int ttl, const AttributeTraits &traits); // store a new history event in the database @@ -109,6 +110,9 @@ namespace pqxx_conn const std::string &error_msg, const AttributeTraits &traits); + // update the attribute ttl value + void storeAttributeTtl(const std::string &full_attr_name, unsigned int ttl); + // fetch API // get the last history event for the given attribute diff --git a/src/HdbppTimescaleDb.cpp b/src/HdbppTimescaleDb.cpp index 99c7f3bdbdb7875fe2918d40e8b756f4e53bbcc3..a4ad29a41f286c0aa48675d7c3df8daa8cab4e72 100644 --- a/src/HdbppTimescaleDb.cpp +++ b/src/HdbppTimescaleDb.cpp @@ -25,6 +25,7 @@ #include "HdbppTxHistoryEvent.hpp" #include "HdbppTxNewAttribute.hpp" #include "HdbppTxParameterEvent.hpp" +#include "HdbppTxUpdateTtl.hpp" #include "LibUtils.hpp" #include <locale> @@ -235,7 +236,7 @@ void HdbppTimescaleDb::insert_param_Attr( //============================================================================= //============================================================================= void HdbppTimescaleDb::configure_Attr( - std::string fqdn_attr_name, int type, int format, int write_type, unsigned int /* ttl */) + std::string fqdn_attr_name, int type, int format, int write_type, unsigned int ttl) { assert(!fqdn_attr_name.empty()); spdlog::trace("Insert new attribute request for attribute: {}", fqdn_attr_name); @@ -248,6 +249,7 @@ void HdbppTimescaleDb::configure_Attr( .withTraits(static_cast<Tango::AttrWriteType>(write_type), static_cast<Tango::AttrDataFormat>(format), static_cast<Tango::CmdArgType>(type)) + .withTtl(ttl) .store(); } @@ -258,7 +260,7 @@ void HdbppTimescaleDb::updateTTL_Attr(std::string fqdn_attr_name, unsigned int t assert(!fqdn_attr_name.empty()); spdlog::trace("TTL event request for attribute: {}, with ttl: {}", fqdn_attr_name, ttl); - // TODO implement + Conn->createTx<HdbppTxUpdateTtl>().withName(fqdn_attr_name).withTtl(ttl).store(); } //============================================================================= diff --git a/src/HdbppTxNewAttribute.hpp b/src/HdbppTxNewAttribute.hpp index 5a01203db87ac0e0721a9747c0c6e7de208ecb8d..bbcced70b259a9a9ec9ab38f69c5a0a45ec922ef 100644 --- a/src/HdbppTxNewAttribute.hpp +++ b/src/HdbppTxNewAttribute.hpp @@ -50,6 +50,12 @@ public: return *this; } + HdbppTxNewAttribute<Conn> &withTtl(unsigned int ttl) + { + _ttl = ttl; + return *this; + } + // trigger the database storage routines HdbppTxNewAttribute<Conn> &store(); @@ -59,6 +65,7 @@ public: private: AttributeName _attr_name; AttributeTraits _traits; + unsigned int _ttl = 0; }; //============================================================================= @@ -167,6 +174,7 @@ HdbppTxNewAttribute<Conn> &HdbppTxNewAttribute<Conn>::store() _attr_name.family(), _attr_name.member(), _attr_name.name(), + _ttl, _traits); HdbppTxBase<Conn>::connection() diff --git a/src/HdbppTxUpdateTtl.hpp b/src/HdbppTxUpdateTtl.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d957585bb22cc6e4d6e4bf3688cf0c05531f3040 --- /dev/null +++ b/src/HdbppTxUpdateTtl.hpp @@ -0,0 +1,124 @@ +/* Copyright (C) : 2014-2019 + European Synchrotron Radiation Facility + BP 220, Grenoble 38043, FRANCE + + This file is part of libhdb++timescale. + + libhdb++timescale is free software: you can redistribute it and/or modify + it under the terms of the Lesser GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libhdb++timescale 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 Lesser + GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with libhdb++timescale. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef _HDBPP_TX_UPDATE_TTL_HPP +#define _HDBPP_TX_UPDATE_TTL_HPP + +#include "AttributeTraits.hpp" +#include "HdbppTxBase.hpp" +#include "LibUtils.hpp" + +#include <iostream> +#include <string> + +namespace hdbpp_internal +{ +// Update the time to live (ttl) for the attribute in the database. +template<typename Conn> +class HdbppTxUpdateTtl : public HdbppTxBase<Conn> +{ +public: + HdbppTxUpdateTtl(Conn &conn) : HdbppTxBase<Conn>(conn) {} + + HdbppTxUpdateTtl<Conn> &withName(const std::string &fqdn_attr_name) + { + _attr_name = AttributeName {fqdn_attr_name}; + return *this; + } + + HdbppTxUpdateTtl<Conn> &withTtl(unsigned int ttl) + { + _ttl = ttl; + return *this; + } + + // trigger the database storage routines + HdbppTxUpdateTtl<Conn> &store(); + + /// @brief Print the HdbppTxUpdateTtl object to the stream + void print(std::ostream &os) const noexcept override; + +private: + AttributeName _attr_name; + + // ttl for the attribute + unsigned int _ttl = 0; +}; + +//============================================================================= +//============================================================================= +template<typename Conn> +HdbppTxUpdateTtl<Conn> &HdbppTxUpdateTtl<Conn>::store() +{ + if (_attr_name.empty()) + { + std::string msg {"AttributeName is reporting empty. Unable to complete the transaction."}; + spdlog::error("Error: {}", msg); + Tango::Except::throw_exception("Invalid Argument", msg, LOCATION_INFO); + } + else if (HdbppTxBase<Conn>::connection().isClosed()) + { + string msg {"The connection is reporting it is closed. Unable to store parameter event. For attribute" + + _attr_name.fqdnAttributeName()}; + + spdlog::error("Error: {}", msg); + Tango::Except::throw_exception("Invalid Argument", msg, LOCATION_INFO); + } + + auto prepared_attr_name = HdbppTxBase<Conn>::attrNameForStorage(_attr_name); + + // check if this attribute exists in the database, if it does not we have + // an error condition + if (HdbppTxBase<Conn>::connection().fetchAttributeArchived(prepared_attr_name)) + { + // now store the parameter event + HdbppTxBase<Conn>::connection().storeAttributeTtl(HdbppTxBase<Conn>::attrNameForStorage(_attr_name), _ttl); + } + else + { + // attribute does not exist, this is an error condition + std::string msg {"Attempt to update the ttl for an attribute that does not exist. Attribute: " + + _attr_name.fqdnAttributeName()}; + + spdlog::error("Error: {}", msg); + Tango::Except::throw_exception("Consistency Error", msg, LOCATION_INFO); + } + + // success in running the store command, so set the result as true + HdbppTxBase<Conn>::setResult(true); + return *this; +} + +//============================================================================= +//============================================================================= +template<typename Conn> +void HdbppTxUpdateTtl<Conn>::print(std::ostream &os) const noexcept +{ + // TODO can not print tango objects, the operator<< are not const correct! + + os << "HdbppTxUpdateTtl(base: "; + HdbppTxBase<Conn>::print(os); + + os << ", " + << "_attr_name: " << _attr_name << ", " + << "_ttl: " << _ttl << ")"; +} + +} // namespace hdbpp_internal +#endif // _HDBPP_TX_UPDATE_TTL_HPP diff --git a/src/PqxxExtension.hpp b/src/PqxxExtension.hpp index fbaeaf3de184063e5a5207055c9af5121b6e09c0..1b7092914025c02bdbdd5a455f721f3f64ad6b61 100644 --- a/src/PqxxExtension.hpp +++ b/src/PqxxExtension.hpp @@ -36,6 +36,11 @@ #include <tango.h> +// This file conforms to the pqxx style, rather than our own, since it is an extension +// to that project, therefore we have many liniting readability errors raised when +// using clang-tidy. To make our compile clean, we simply disable linting for many lines +// in the file + namespace pqxx { namespace internal @@ -43,78 +48,91 @@ namespace internal template<> struct type_name<uint8_t> { + // NOLINTNEXTLINE (readability-identifier-naming) static constexpr const char *value = "uint8_t"; }; template<> struct type_name<Tango::DevState> { + // NOLINTNEXTLINE (readability-identifier-naming) static constexpr const char *value = "Tango::DevState"; }; template<> struct type_name<std::vector<double>> { + // NOLINTNEXTLINE (readability-identifier-naming) static constexpr const char *value = "vector<double>"; }; template<> struct type_name<std::vector<float>> { + // NOLINTNEXTLINE (readability-identifier-naming) static constexpr const char *value = "vector<float>"; }; template<> struct type_name<std::vector<int32_t>> { + // NOLINTNEXTLINE (readability-identifier-naming) static constexpr const char *value = "vector<int32_t>"; }; template<> struct type_name<std::vector<uint32_t>> { + // NOLINTNEXTLINE (readability-identifier-naming) static constexpr const char *value = "vector<uint32_t>"; }; template<> struct type_name<std::vector<int64_t>> { + // NOLINTNEXTLINE (readability-identifier-naming) static constexpr const char *value = "vector<int64_t>"; }; template<> struct type_name<std::vector<uint64_t>> { + // NOLINTNEXTLINE (readability-identifier-naming) static constexpr const char *value = "vector<uint64_t>"; }; template<> struct type_name<std::vector<int16_t>> { + // NOLINTNEXTLINE (readability-identifier-naming) static constexpr const char *value = "vector<int16_t>"; }; template<> struct type_name<std::vector<uint16_t>> { + // NOLINTNEXTLINE (readability-identifier-naming) static constexpr const char *value = "vector<uint16_t>"; }; template<> struct type_name<std::vector<uint8_t>> { + // NOLINTNEXTLINE (readability-identifier-naming) static constexpr const char *value = "vector<uint8_t>"; }; template<> struct type_name<std::vector<bool>> { + // NOLINTNEXTLINE (readability-identifier-naming) static constexpr const char *value = "vector<bool>"; }; template<> struct type_name<std::vector<std::string>> { + // NOLINTNEXTLINE (readability-identifier-naming) static constexpr const char *value = "vector<std::string>"; }; } // namespace internal @@ -127,15 +145,22 @@ struct string_traits<std::vector<T>> { public: static constexpr const char *name() noexcept { return internal::type_name<T>::value; } + + // NOLINTNEXTLINE (readability-identifier-naming) static constexpr bool has_null() noexcept { return false; } - static bool is_null(const std::vector<T> &) { return false; } + + // NOLINTNEXTLINE (readability-identifier-naming) + static bool is_null(const std::vector<T> & /*unused*/) { return false; } + [[noreturn]] static std::vector<T> null() { internal::throw_null_conversion(name()); } + // NOLINTNEXTLINE (readability-identifier-naming) static void from_string(const char str[], std::vector<T> &value) { if (str == nullptr) internal::throw_null_conversion(name()); + // NOLINTNEXTLINE (cppcoreguidelines-pro-bounds-pointer-arithmetic) if (str[0] != '{' || str[strlen(str) - 1] != '}') throw pqxx::conversion_error("Invalid array format"); @@ -166,6 +191,7 @@ public: } } + // NOLINTNEXTLINE (readability-identifier-naming) static std::string to_string(const std::vector<T> &value) { if (value.empty()) @@ -184,15 +210,22 @@ struct string_traits<std::vector<std::string>> { public: static constexpr const char *name() noexcept { return "vector<string>"; } + + // NOLINTNEXTLINE (readability-identifier-naming) static constexpr bool has_null() noexcept { return false; } - static bool is_null(const std::vector<std::string> &) { return false; } + + // NOLINTNEXTLINE (readability-identifier-naming) + static bool is_null(const std::vector<std::string> & /*unused*/) { return false; } + [[noreturn]] static std::vector<std::string> null() { internal::throw_null_conversion(name()); } + // NOLINTNEXTLINE (readability-identifier-naming) static void from_string(const char str[], std::vector<std::string> &value) { if (str == nullptr) internal::throw_null_conversion(name()); + // NOLINTNEXTLINE (cppcoreguidelines-pro-bounds-pointer-arithmetic) if (str[0] != '{' || str[strlen(str) - 1] != '}') throw pqxx::conversion_error("Invalid array format"); @@ -223,6 +256,7 @@ public: } } + // NOLINTNEXTLINE (readability-identifier-naming) static std::string to_string(const std::vector<std::string> &value) { // This function should not be used, so we do a simple basic conversion @@ -239,15 +273,22 @@ struct string_traits<std::vector<bool>> { public: static constexpr const char *name() noexcept { return "std::vector<bool>"; } + + // NOLINTNEXTLINE (readability-identifier-naming) static constexpr bool has_null() noexcept { return false; } - static bool is_null(const std::vector<bool> &) { return false; } + + // NOLINTNEXTLINE (readability-identifier-naming) + static bool is_null(const std::vector<bool> & /*unused*/) { return false; } + [[noreturn]] static std::vector<bool> null() { internal::throw_null_conversion(name()); } + // NOLINTNEXTLINE (readability-identifier-naming) static void from_string(const char str[], std::vector<bool> &value) { if (str == nullptr) internal::throw_null_conversion(name()); + // NOLINTNEXTLINE (cppcoreguidelines-pro-bounds-pointer-arithmetic) if (str[0] != '{' || str[strlen(str) - 1] != '}') throw pqxx::conversion_error("Invalid array format"); @@ -276,6 +317,7 @@ public: } } + // NOLINTNEXTLINE (readability-identifier-naming) static std::string to_string(const std::vector<bool> &value) { if (value.empty()) diff --git a/src/QueryBuilder.cpp b/src/QueryBuilder.cpp index cbffde264407e3d291b0cf8adc0592f8c78f9e81..625eef4aad17f3bacc3bc165dc5c51282dd8d788 100644 --- a/src/QueryBuilder.cpp +++ b/src/QueryBuilder.cpp @@ -145,20 +145,21 @@ namespace pqxx_conn schema::ConfColFamily + "," + schema::ConfColMember + "," + schema::ConfColLastName + "," + + schema::ConfColTtl + "," + schema::ConfColHide + ") (" + "SELECT " + "$1," + schema::ConfTypeColTypeId + "," + schema::ConfFormatColFormatId + "," + schema::ConfWriteColWriteId + - ",$2,$3,$4,$5,$6,$7,$8 " + + ",$2,$3,$4,$5,$6,$7,$8,$9 " + "FROM " + schema::ConfTypeTableName + ", " + schema::ConfFormatTableName + ", " + schema::ConfWriteTableName + " " + - "WHERE " + schema::ConfTypeTableName + "." + schema::ConfTypeColTypeNum + " = $9 " + - "AND " + schema::ConfFormatTableName + "." + schema::ConfFormatColFormatNum + " = $10 " + - "AND " + schema::ConfWriteTableName + "." + schema::ConfWriteColWriteNum + " = $11) " + + "WHERE " + schema::ConfTypeTableName + "." + schema::ConfTypeColTypeNum + " = $10 " + + "AND " + schema::ConfFormatTableName + "." + schema::ConfFormatColFormatNum + " = $11 " + + "AND " + schema::ConfWriteTableName + "." + schema::ConfWriteColWriteNum + " = $12) " + "RETURNING " + schema::ConfColId; // clang-format on @@ -273,6 +274,19 @@ namespace pqxx_conn return query; } + //============================================================================= + //============================================================================= + const std::string &QueryBuilder::storeTtlStatement() + { + // clang-format off + static string query = + "UPDATE " + schema::ConfTableName + " SET " + + schema::ConfColTtl + "=$1::int WHERE " + schema::ConfColId + "=$2"; + // clang-format on + + return query; + } + //============================================================================= //============================================================================= const string QueryBuilder::fetchAllValuesStatement( diff --git a/src/QueryBuilder.hpp b/src/QueryBuilder.hpp index 4555f6f9cb4faec0486188c3977e657d5b735067..0dc52f9543a55347826a70a767fcd79068207844 100644 --- a/src/QueryBuilder.hpp +++ b/src/QueryBuilder.hpp @@ -141,6 +141,7 @@ namespace pqxx_conn const string StoreDataEvent = "StoreDataEvent"; const string StoreDataEventError = "StoreDataEventError"; const string StoreErrorString = "StoreErrorString"; + const string StoreTtl = "StoreTtl"; const string FetchLastHistoryEvent = "FetchLastHistoryEvent"; const string FetchAttributeTraits = "FetchAttributeTraits"; const string FetchValue = "FetchKey"; @@ -161,6 +162,7 @@ namespace pqxx_conn static const std::string &storeHistoryStringStatement(); static const std::string &storeParameterEventStatement(); static const std::string &storeErrorStatement(); + static const std::string &storeTtlStatement(); static const std::string &fetchLastHistoryEventStatement(); static const std::string &fetchAttributeTraitsStatement(); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 353a4276193140c384f27b8b4fea3a2d87a45d48..8d839445587955bd08c870cc81bdea70a0ebb113 100755 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,6 +17,7 @@ set(TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/HdbppTxNewAttributeTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/HdbppTxHistoryEventTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/HdbppTxParameterEventTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/HdbppTxUpdateTtlTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/QueryBuilderTests.cpp) add_executable(unit-tests ${TEST_SOURCES}) diff --git a/test/DbConnectionTests.cpp b/test/DbConnectionTests.cpp index e83015eb339f02c334c633f131ef611762e78db0..95ad208a851f6c03af337bfca499e4c35298190d 100644 --- a/test/DbConnectionTests.cpp +++ b/test/DbConnectionTests.cpp @@ -131,8 +131,8 @@ protected: pqxx::connection &verifyConn(); void clearTables(); - void storeAttribute(const AttributeTraits &traits); - string storeAttributeByTraits(const AttributeTraits &traits); + void storeAttribute(const AttributeTraits &traits, unsigned int ttl = 0); + string storeAttributeByTraits(const AttributeTraits &traits, unsigned int ttl = 0); template<Tango::CmdArgType Type> tuple<vector<typename TangoTypeTraits<Type>::type>, vector<typename TangoTypeTraits<Type>::type>> @@ -205,7 +205,7 @@ void DbConnectionTestsFixture::clearTables() //============================================================================= //============================================================================= -void DbConnectionTestsFixture::storeAttribute(const AttributeTraits &traits) +void DbConnectionTestsFixture::storeAttribute(const AttributeTraits &traits, unsigned int ttl) { REQUIRE_NOTHROW(testConn().storeAttribute(attr_name::TestAttrFinalName, attr_name::TestAttrCs, @@ -213,12 +213,13 @@ void DbConnectionTestsFixture::storeAttribute(const AttributeTraits &traits) attr_name::TestAttrFamily, attr_name::TestAttrMember, attr_name::TestAttrName, + ttl, traits)); } //============================================================================= //============================================================================= -string DbConnectionTestsFixture::storeAttributeByTraits(const AttributeTraits &traits) +string DbConnectionTestsFixture::storeAttributeByTraits(const AttributeTraits &traits, unsigned int ttl) { auto name = attr_name::TestAttrFinalName + "_" + tangoEnumToString(traits.type()) + "_" + tangoEnumToString(traits.writeType()) + "_" + tangoEnumToString(traits.formatType()); @@ -229,6 +230,7 @@ string DbConnectionTestsFixture::storeAttributeByTraits(const AttributeTraits &t attr_name::TestAttrFamily, attr_name::TestAttrMember, attr_name::TestAttrName, + ttl, traits)); return name; @@ -397,7 +399,7 @@ TEST_CASE_METHOD(pqxx_conn_test::DbConnectionTestsFixture, AttributeTraits traits {Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE}; REQUIRE_NOTHROW(clearTables()); - REQUIRE_NOTHROW(storeAttribute(traits)); + REQUIRE_NOTHROW(storeAttribute(traits, 99)); { pqxx::work tx {verifyConn()}; @@ -422,6 +424,7 @@ TEST_CASE_METHOD(pqxx_conn_test::DbConnectionTestsFixture, REQUIRE(attr_row.at(schema::ConfColLastName).as<string>() == attr_name::TestAttrName); REQUIRE(attr_row.at(schema::ConfColTableName).as<string>() == QueryBuilder().tableName(traits)); REQUIRE(attr_row.at(schema::ConfColTypeId).as<int>() == type_row.at(schema::ConfTypeColTypeId).as<int>()); + REQUIRE(attr_row.at(schema::ConfColTtl).as<int>() == 99); REQUIRE(attr_row.at(schema::ConfColFormatTypeId).as<int>() == format_row.at(schema::ConfFormatColFormatId).as<int>()); @@ -447,6 +450,7 @@ TEST_CASE_METHOD(pqxx_conn_test::DbConnectionTestsFixture, attr_name::TestAttrFamily, attr_name::TestAttrMember, attr_name::TestAttrName, + 0, traits), Tango::DevFailed); @@ -466,6 +470,7 @@ TEST_CASE_METHOD(pqxx_conn_test::DbConnectionTestsFixture, attr_name::TestAttrFamily, attr_name::TestAttrMember, attr_name::TestAttrName, + 0, traits), Tango::DevFailed); @@ -1169,3 +1174,46 @@ TEST_CASE_METHOD(pqxx_conn_test::DbConnectionTestsFixture, REQUIRE(testConn().fetchAttributeTraits(attr_name::TestAttrFQDName) == traits); SUCCEED("Passed"); } + +TEST_CASE_METHOD(pqxx_conn_test::DbConnectionTestsFixture, + "Upating an attribute ttl in the database", + "[db-access][hdbpp-db-access][db-connection]") +{ + AttributeTraits traits {Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE}; + unsigned int new_ttl = 100; + + REQUIRE_NOTHROW(clearTables()); + auto name = storeAttributeByTraits(traits); + REQUIRE_NOTHROW(testConn().storeAttributeTtl(name, new_ttl)); + + { + pqxx::work tx {verifyConn()}; + + string query = "SELECT ttl FROM "; + query += schema::ConfTableName; + query += " WHERE "; + query += schema::ConfColName; + query += "='"; + query += name; + query += "'"; + + // get the attribute ttl + pqxx::row attr_row; + REQUIRE_NOTHROW(attr_row = tx.exec1(query)); + tx.commit(); + + REQUIRE(attr_row.at(0).as<unsigned int>() == new_ttl); + } + + SUCCEED("Passed"); +} + +TEST_CASE_METHOD(pqxx_conn_test::DbConnectionTestsFixture, + "storeAttributeTtl() throws an exception when the attribute is not archived", + "[db-access][hdbpp-db-access][db-connection]") +{ + AttributeTraits traits {Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE}; + REQUIRE_NOTHROW(clearTables()); + REQUIRE_THROWS(testConn().storeAttributeTtl(attr_name::TestAttrFQDName, 100)); + SUCCEED("Passed"); +} \ No newline at end of file diff --git a/test/HdbppTxDataEventErrorTests.cpp b/test/HdbppTxDataEventErrorTests.cpp index 46279eb2904107c461068f3f37d092f040136487..c5d7bd53d4e386a827e051f244f2edccbe5691e0 100644 --- a/test/HdbppTxDataEventErrorTests.cpp +++ b/test/HdbppTxDataEventErrorTests.cpp @@ -42,10 +42,10 @@ public: bool isOpen() const noexcept override { return _conn_state; } bool isClosed() const noexcept override { return !isOpen(); } - void storeDataEventError(const std::string &full_attr_name, + void storeDataEventError(const string &full_attr_name, double event_time, int quality, - const std::string &error_msg, + const string &error_msg, const AttributeTraits &traits) { if (store_attribute_triggers_ex) diff --git a/test/HdbppTxDataEventTests.cpp b/test/HdbppTxDataEventTests.cpp index 8288c14cd2e461370a1db882bd67dc2fb907f6ec..4d06bce9128c1952c1e75064efebb8d77489286f 100644 --- a/test/HdbppTxDataEventTests.cpp +++ b/test/HdbppTxDataEventTests.cpp @@ -128,11 +128,11 @@ public: bool isClosed() const noexcept override { return !isOpen(); } template<typename T> - void storeDataEvent(const std::string &full_attr_name, + void storeDataEvent(const string &full_attr_name, double event_time, int quality, - std::unique_ptr<vector<T>> value_r, - std::unique_ptr<vector<T>> value_w, + unique_ptr<vector<T>> value_r, + unique_ptr<vector<T>> value_w, const AttributeTraits &traits) { if (store_attribute_triggers_ex) diff --git a/test/HdbppTxHistoryEventTests.cpp b/test/HdbppTxHistoryEventTests.cpp index 255b8b45d40b1d2b12f508e4750b07d9f74997c0..191eeb2df2ecff7ff00a57ef5025d84ed1b03d16 100644 --- a/test/HdbppTxHistoryEventTests.cpp +++ b/test/HdbppTxHistoryEventTests.cpp @@ -44,7 +44,7 @@ public: bool isClosed() const noexcept override { return !isOpen(); } // storage API - void storeHistoryEvent(const string &full_attr_name, const std::string &event) + void storeHistoryEvent(const string &full_attr_name, const string &event) { if (store_attribute_triggers_ex) throw runtime_error("A test exception"); @@ -54,7 +54,7 @@ public: event_seq.push_back(event); } - std::string fetchLastHistoryEvent(const string & /* unused */) { return att_last_event; } + string fetchLastHistoryEvent(const string & /* unused */) { return att_last_event; } // expose the results of the store function so they can be checked // in the results diff --git a/test/HdbppTxNewAttributeTests.cpp b/test/HdbppTxNewAttributeTests.cpp index bb51b0cf3550a4979589f86d8db357b9e0301b88..283ed7787293a8ec80d0f21e16eee48d0ce41a63 100644 --- a/test/HdbppTxNewAttributeTests.cpp +++ b/test/HdbppTxNewAttributeTests.cpp @@ -47,6 +47,7 @@ public: const string &att_family, const string &att_member, const string &att_name, + unsigned int ttl, const AttributeTraits &traits) { if (store_attribute_triggers_ex) @@ -58,10 +59,11 @@ public: new_att_family = att_family; new_att_member = att_member; new_att_name = att_name; + new_att_ttl = ttl; att_traits = traits; } - void storeHistoryEvent(const string & /* full_attr_name */, const std::string &event) + void storeHistoryEvent(const string & /* full_attr_name */, const string &event) { new_att_last_event = event; @@ -72,11 +74,11 @@ public: att_archived = true; } - std::string fetchLastHistoryEvent(const string & /* unused */) { return new_att_last_event; } + string fetchLastHistoryEvent(const string & /* unused */) { return new_att_last_event; } - bool fetchAttributeArchived(const std::string & /* unused */) { return att_archived; } + bool fetchAttributeArchived(const string & /* unused */) { return att_archived; } - AttributeTraits fetchAttributeTraits(const std::string & /* unused */) { return att_traits; } + AttributeTraits fetchAttributeTraits(const string & /* unused */) { return att_traits; } // expose the results of the store function so they can be checked // in the results @@ -92,6 +94,7 @@ public: AttributeTraits att_traits; bool store_attribute_triggers_ex = false; bool att_archived = false; + unsigned int new_att_ttl = 0; private: // connection is always open unless test specifies closed @@ -109,7 +112,7 @@ SCENARIO("Construct and store HdbppTxNewAttribute data without error", "[hdbpp-t WHEN("Passing a valid configuration with method chaining") { - tx.withName(TestAttrFQDName).withTraits(Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE); + tx.withName(TestAttrFQDName).withTraits(Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE).withTtl(10); THEN("Then storing the HdbppTxNewAttribute object does not raise an exception") { @@ -128,6 +131,7 @@ SCENARIO("Construct and store HdbppTxNewAttribute data without error", "[hdbpp-t REQUIRE(conn.new_att_family == TestAttrFamily); REQUIRE(conn.new_att_member == TestAttrMember); REQUIRE(conn.new_att_name == TestAttrName); + REQUIRE(conn.new_att_ttl == 10); REQUIRE(conn.att_traits == AttributeTraits(Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE)); } } diff --git a/test/HdbppTxParameterEventTests.cpp b/test/HdbppTxParameterEventTests.cpp index 9fea92361a2ca3e239cbf74c2c1addb89c7e8ed8..90e9eca28000f3dfa412e9b0f4af7d05641f6ac2 100644 --- a/test/HdbppTxParameterEventTests.cpp +++ b/test/HdbppTxParameterEventTests.cpp @@ -65,17 +65,17 @@ public: bool isClosed() const noexcept override { return !isOpen(); } // storage API - void storeParameterEvent(const std::string &full_attr_name, + void storeParameterEvent(const string &full_attr_name, double event_time, - const std::string &label, - const std::string &unit, - const std::string &standard_unit, - const std::string &display_unit, - const std::string &format, - const std::string &archive_rel_change, - const std::string &archive_abs_change, - const std::string &archive_period, - const std::string &description) + const string &label, + const string &unit, + const string &standard_unit, + const string &display_unit, + const string &format, + const string &archive_rel_change, + const string &archive_abs_change, + const string &archive_period, + const string &description) { if (store_attribute_triggers_ex) throw runtime_error("A test exception"); diff --git a/test/HdbppTxUpdateTtlTests.cpp b/test/HdbppTxUpdateTtlTests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6dcf2b44b7a2163ed2b15dc47b4f481d0b6b000b --- /dev/null +++ b/test/HdbppTxUpdateTtlTests.cpp @@ -0,0 +1,162 @@ +/* Copyright (C) : 2014-2019 + European Synchrotron Radiation Facility + BP 220, Grenoble 38043, FRANCE + + This file is part of libhdb++timescale. + + libhdb++timescale is free software: you can redistribute it and/or modify + it under the terms of the Lesser GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libhdb++timescale 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 Lesser + GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with libhdb++timescale. If not, see <http://www.gnu.org/licenses/>. */ + +#include "ConnectionBase.hpp" +#include "HdbppTxFactory.hpp" +#include "HdbppTxUpdateTtl.hpp" +#include "TestHelpers.hpp" +#include "catch2/catch.hpp" + +using namespace std; +using namespace hdbpp_internal; +using namespace hdbpp_test::attr_name; + +namespace hdbpp_update_ttl_test +{ +// Mock connection to test the HdbppTxUpdateTtl class +class MockConnection : public ConnectionBase, public HdbppTxFactory<MockConnection> +{ +public: + // Enforced connection API from ConnectionBase + void connect(const string & /* connect_str */) override { _conn_state = true; } + void disconnect() override { _conn_state = false; } + bool isOpen() const noexcept override { return _conn_state; } + bool isClosed() const noexcept override { return !isOpen(); } + + // mock storeAttribute just records parameters + void storeAttributeTtl(const string &full_attr_name, unsigned int ttl) + { + if (store_ttl_triggers_ex) + throw runtime_error("A test exception"); + + att_name = full_attr_name; + att_ttl = ttl; + } + + bool fetchAttributeArchived(const string & /* unused */) { return att_archived; } + + // expose the results of the store function so they can be checked + // in the results + + // storeTtl results + string att_name; + unsigned int att_ttl = 0; + bool store_ttl_triggers_ex = false; + bool att_archived = true; + +private: + // connection is always open unless test specifies closed + bool _conn_state = true; +}; +}; // namespace hdbpp_update_ttl_test + +SCENARIO("Construct and store HdbppTxUpdateTtl data without error", "[hdbpp-tx][hdbpp-tx-update-ttl]") +{ + hdbpp_update_ttl_test::MockConnection conn; + auto ttl = 10; + + GIVEN("An HdbppTxUpdateTtl object with no data set") + { + auto tx = conn.createTx<HdbppTxUpdateTtl>(); + + WHEN("Passing a valid configuration with method chaining") + { + tx.withName(TestAttrFQDName).withTtl(ttl); + + THEN("Then storing the HdbppTxUpdateTtl object does not raise an exception") + { + REQUIRE_NOTHROW(tx.store()); + REQUIRE(tx.result()); + } + AND_WHEN("The result of the store is examined after storing") + { + REQUIRE_NOTHROW(tx.store()); + REQUIRE(tx.result()); + + THEN("The data is the same as that passed via method chaining") + { + REQUIRE(conn.att_name == TestAttrFinalName); + REQUIRE(conn.att_ttl == ttl); + } + } + } + } +} + +SCENARIO( + "When attempting to store invalid HdbppTxUpdateTtl states, errors are thrown", "[hdbpp-tx][hdbpp-tx-update-ttl]") +{ + hdbpp_update_ttl_test::MockConnection conn; + auto ttl = 10; + + GIVEN("An HdbppTxUpdateTtl object with no data set") + { + auto tx = conn.createTx<HdbppTxUpdateTtl>(); + + WHEN("Attempting to store without setting data") + { + THEN("An exception is raised and result is false") + { + REQUIRE_THROWS(tx.store()); + REQUIRE(!tx.result()); + } + } + WHEN("Attempting to store without the attribute existing first") + { + conn.att_archived = false; + tx.withName(TestAttrFQDName).withTtl(ttl); + + THEN("An exception is raised and result is false") + { + REQUIRE_THROWS(tx.store()); + REQUIRE(!tx.result()); + } + } + WHEN("Attempting to store with valid data, but disconnected connection") + { + conn.disconnect(); + REQUIRE(conn.isClosed()); + + tx.withName(TestAttrFQDName).withTtl(ttl); + + THEN("An exception is raised and result is false") + { + REQUIRE_THROWS(tx.store()); + REQUIRE(!tx.result()); + } + } + } +} + +SCENARIO("HdbppTxUpdateTtl Simulated exception received", "[hdbpp-tx][hdbpp-tx-update-ttl]") +{ + hdbpp_update_ttl_test::MockConnection conn; + + GIVEN("An HdbppTxUpdateTtl object with name and traits set") + { + auto tx = conn.createTx<HdbppTxUpdateTtl>().withName(TestAttrFQDName).withTtl(10); + + WHEN("Storing the HdbppTxUpdateTtl object with a triggered exception set") + { + conn.store_ttl_triggers_ex = true; + + THEN("An exception is raised") { REQUIRE_THROWS_AS(tx.store(), runtime_error); } + } + } +} diff --git a/test/main.cpp b/test/main.cpp index 0b69841a622379923a6cc5ecab354e55cf969b58..b1a62c68e36b1f776b4856ace0050f6f671b3283 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -18,7 +18,6 @@ along with libhdb++timescale. If not, see <http://www.gnu.org/licenses/>. */ #define CATCH_CONFIG_RUNNER -#undef CATCH_CONFIG_FAST_COMPILE #include "LibUtils.hpp" #include "catch2/catch.hpp"