diff --git a/benchmark/QueryBuilderTests.cpp b/benchmark/QueryBuilderTests.cpp index 5da87889875fa1c9db4de13e2f0d41b2fbbb88be..24970008c1e8e951ea57d525574bc4a97c5535cb 100644 --- a/benchmark/QueryBuilderTests.cpp +++ b/benchmark/QueryBuilderTests.cpp @@ -99,8 +99,7 @@ void bmStoreDataEventQueryNoCache(benchmark::State& state) { // TEST - Testing how long it takes to build an Insert Data Event query with // an empty cache (this forces the full string to be built) - hdbpp_internal::LogConfigurator::initLoggingMetrics(false, false); - hdbpp_internal::LogConfigurator::setLoggingLevel(spdlog::level::err); + hdbpp_internal::LogConfigurator::initLogging(); hdbpp_internal::AttributeTraits traits {static_cast<Tango::AttrWriteType>(state.range(0)), Tango::SCALAR, Tango::DEV_DOUBLE}; @@ -109,7 +108,7 @@ void bmStoreDataEventQueryNoCache(benchmark::State& state) { // define the builder here so its cache is always fresh hdbpp_internal::pqxx_conn::QueryBuilder query_builder; - query_builder.storeDataEventQuery<T>(traits); + query_builder.storeDataEventStatement<T>(traits); } } @@ -120,8 +119,7 @@ void bmStoreDataEventQueryCache(benchmark::State& state) { // TEST - Testing the full lookup for an Insert Data QueryEvent query when the cache // map is fully populated - hdbpp_internal::LogConfigurator::initLoggingMetrics(false, false); - hdbpp_internal::LogConfigurator::setLoggingLevel(spdlog::level::err); + hdbpp_internal::LogConfigurator::initLogging(); hdbpp_internal::AttributeTraits traits {static_cast<Tango::AttrWriteType>(state.range(0)), Tango::SCALAR, Tango::DEV_DOUBLE}; @@ -149,13 +147,13 @@ void bmStoreDataEventQueryCache(benchmark::State& state) for (auto &type : types) for (auto &format : format_types) for (auto &write : write_types) - query_builder.storeDataEventQuery<T>(hdbpp_internal::AttributeTraits{write, format, type}); + query_builder.storeDataEventStatement<T>(hdbpp_internal::AttributeTraits{write, format, type}); for (auto _ : state) - query_builder.storeDataEventQuery<T>(traits); + query_builder.storeDataEventStatement<T>(traits); } BENCHMARK_TEMPLATE(bmStoreDataEventQueryNoCache, bool)->Apply(writeTypeArgs); BENCHMARK_TEMPLATE(bmStoreDataEventQueryCache, bool)->Apply(writeTypeArgs); -BENCHMARK_MAIN(); \ No newline at end of file +BENCHMARK_MAIN(); diff --git a/src/ColumnCache.hpp b/src/ColumnCache.hpp index 44674be924839302a1a1923a56367f4554a30480..41d94724d387649dddcc1895f82b8d773bb0c6de 100644 --- a/src/ColumnCache.hpp +++ b/src/ColumnCache.hpp @@ -126,7 +126,7 @@ namespace pqxx_conn if (!tx.prepared(_fetch_all_query_name).exists()) { tx.conn().prepare(_fetch_all_query_name, - QueryBuilder::fetchAllValuesQuery(_column_name, _table_name, _reference)); + QueryBuilder::fetchAllValuesStatement(_column_name, _table_name, _reference)); spdlog::trace("Created prepared statement for: {}", _fetch_all_query_name); } @@ -182,7 +182,7 @@ namespace pqxx_conn if (!tx.prepared(_fetch_id_query_name).exists()) { tx.conn().prepare( - _fetch_id_query_name, QueryBuilder::fetchValueQuery(_column_name, _table_name, _reference)); + _fetch_id_query_name, QueryBuilder::fetchValueStatement(_column_name, _table_name, _reference)); spdlog::trace("Created prepared statement for: {}", _fetch_id_query_name); } diff --git a/src/DbConnection.cpp b/src/DbConnection.cpp index 85e16048df8c92e446e2a8e762c6b888a072d2f6..fb01213246fbf07b21eb58326d185a51bf4c9c87 100644 --- a/src/DbConnection.cpp +++ b/src/DbConnection.cpp @@ -146,7 +146,7 @@ namespace pqxx_conn if (!tx.prepared(StoreAttribute).exists()) { - tx.conn().prepare(StoreAttribute, QueryBuilder::storeAttributeQuery()); + tx.conn().prepare(StoreAttribute, QueryBuilder::storeAttributeStatement()); spdlog::trace("Created prepared statement for: {}", StoreAttribute); } @@ -180,7 +180,7 @@ namespace pqxx_conn { handlePqxxError("The attribute [" + full_attr_name + "] was not saved.", ex.base().what(), - QueryBuilder::storeAttributeQuery(), + QueryBuilder::storeAttributeStatement(), LOCATION_INFO); } } @@ -225,7 +225,7 @@ namespace pqxx_conn if (!tx.prepared(StoreHistoryEvent).exists()) { - tx.conn().prepare(StoreHistoryEvent, QueryBuilder::storeHistoryEventQuery()); + tx.conn().prepare(StoreHistoryEvent, QueryBuilder::storeHistoryEventStatement()); spdlog::trace("Created prepared statement for: {}", StoreHistoryEvent); } @@ -240,7 +240,7 @@ namespace pqxx_conn { handlePqxxError("The attribute [" + full_attr_name + "] event [" + event + "] was not saved.", ex.base().what(), - QueryBuilder::storeHistoryEventQuery(), + QueryBuilder::storeHistoryEventStatement(), LOCATION_INFO); } } @@ -300,7 +300,7 @@ namespace pqxx_conn if (!tx.prepared(StoreParameterEvent).exists()) { - tx.conn().prepare(StoreParameterEvent, QueryBuilder::storeParameterEventQuery()); + tx.conn().prepare(StoreParameterEvent, QueryBuilder::storeParameterEventStatement()); spdlog::trace("Created prepared statement for: {}", StoreParameterEvent); } @@ -327,7 +327,7 @@ namespace pqxx_conn { handlePqxxError("The attribute [" + full_attr_name + "] parameter event was not saved.", ex.base().what(), - QueryBuilder::storeParameterEventQuery(), + QueryBuilder::storeParameterEventStatement(), LOCATION_INFO); } } @@ -384,7 +384,7 @@ namespace pqxx_conn if (!tx.prepared(_query_builder.storeDataEventErrorName(traits)).exists()) { tx.conn().prepare(_query_builder.storeDataEventErrorName(traits), - _query_builder.storeDataEventErrorQuery(traits)); + _query_builder.storeDataEventErrorStatement(traits)); spdlog::trace( "Created prepared statement for: {}", _query_builder.storeDataEventErrorName(traits)); } @@ -434,7 +434,7 @@ namespace pqxx_conn pqxx::work tx {(*_conn), FetchLastHistoryEvent}; if (!tx.prepared(FetchLastHistoryEvent).exists()) - tx.conn().prepare(FetchLastHistoryEvent, QueryBuilder::fetchLastHistoryEventQuery()); + tx.conn().prepare(FetchLastHistoryEvent, QueryBuilder::fetchLastHistoryEventStatement()); // unless this is the first time this attribute event history has // been queried, then we expect something back @@ -452,7 +452,7 @@ namespace pqxx_conn { handlePqxxError("Can not return last event for attribute [" + full_attr_name + "].", ex.base().what(), - QueryBuilder::fetchLastHistoryEventQuery(), + QueryBuilder::fetchLastHistoryEventStatement(), LOCATION_INFO); } @@ -504,7 +504,7 @@ namespace pqxx_conn pqxx::work tx {(*_conn), FetchAttributeTraits}; if (!tx.prepared(FetchAttributeTraits).exists()) - tx.conn().prepare(FetchAttributeTraits, QueryBuilder::fetchAttributeTraitsQuery()); + tx.conn().prepare(FetchAttributeTraits, QueryBuilder::fetchAttributeTraitsStatement()); // always expect a result, the type info for the attribute auto row = tx.exec_prepared1(FetchAttributeTraits, full_attr_name); @@ -519,7 +519,7 @@ namespace pqxx_conn { handlePqxxError("Can not return the type traits for attribute [" + full_attr_name + "].", ex.base().what(), - QueryBuilder::fetchAttributeTraitsQuery(), + QueryBuilder::fetchAttributeTraitsStatement(), LOCATION_INFO); } @@ -541,7 +541,7 @@ namespace pqxx_conn if (!tx.prepared(StoreHistoryString).exists()) { - tx.conn().prepare(StoreHistoryString, QueryBuilder::storeHistoryStringQuery()); + tx.conn().prepare(StoreHistoryString, QueryBuilder::storeHistoryStringStatement()); spdlog::trace("Created prepared statement for: {}", StoreHistoryString); } @@ -562,7 +562,7 @@ namespace pqxx_conn { handlePqxxError("The event [" + event + "] for attribute [" + full_attr_name + "] was not saved.", ex.base().what(), - QueryBuilder::storeHistoryStringQuery(), + QueryBuilder::storeHistoryStringStatement(), LOCATION_INFO); } } @@ -582,7 +582,7 @@ namespace pqxx_conn if (!tx.prepared(StoreErrorString).exists()) { - tx.conn().prepare(StoreErrorString, QueryBuilder::storeErrorQuery()); + tx.conn().prepare(StoreErrorString, QueryBuilder::storeErrorStatement()); spdlog::trace("Created prepared statement for: {}", StoreErrorString); } @@ -606,7 +606,7 @@ namespace pqxx_conn { handlePqxxError("The error string [" + error_msg + "] for attribute [" + full_attr_name + "] was not saved", ex.base().what(), - QueryBuilder::storeErrorQuery(), + QueryBuilder::storeErrorStatement(), LOCATION_INFO); } } diff --git a/src/DbConnection.tpp b/src/DbConnection.tpp index 8c71da326167988670f43790152e7ab01f59b7bc..a31b01d46e1a0ba690d3708264816639f823b112 100644 --- a/src/DbConnection.tpp +++ b/src/DbConnection.tpp @@ -103,43 +103,13 @@ namespace pqxx_conn // to avoid the quoting. Its likely we will need more for DevEncoded and DevEnum if (traits.isArray() && traits.type() == Tango::DEV_STRING) { - auto prepare_array = [](auto &value) { - auto iter = value->begin(); - std::string result = "ARRAY["; - - result = "$$" + pqxx::to_string((*iter)) + "$$"; - - for (++iter; iter != value->end(); ++iter) - { - result += ","; - result += "$$" + pqxx::to_string((*iter)) + "$$"; - } - - result += "]"; - return result; - }; - - auto query = "INSERT INTO " + QueryBuilder::tableName(traits) + " (" + DAT_COL_ID + "," + DAT_COL_DATA_TIME; - - if (traits.hasReadData()) - query = query + "," + DAT_COL_VALUE_R; - - if (traits.hasWriteData()) - query = query + "," + DAT_COL_VALUE_W; - - // split to ensure increments are in the correct order - query = query + "," + DAT_COL_QUALITY + ") VALUES (" + pqxx::to_string(full_attr_name); - query = query + ",TO_TIMESTAMP(" + pqxx::to_string(event_time) + ")"; - - // add the read parameter with cast - if (traits.hasReadData()) - query = query + "," + prepare_array(value_r); - - // add the write parameter with cast - if (traits.hasWriteData()) - query = query + "," + prepare_array(value_w); - - query = query + "," + pqxx::to_string(quality) + ")"; + auto query = _query_builder.storeDataEventString<T>( + pqxx::to_string(full_attr_name), + pqxx::to_string(event_time), + pqxx::to_string(quality), + value_r, + value_w, + traits); tx.exec0(query); } @@ -150,7 +120,7 @@ namespace pqxx_conn if (!tx.prepared(_query_builder.storeDataEventName(traits)).exists()) { tx.conn().prepare( - _query_builder.storeDataEventName(traits), _query_builder.storeDataEventQuery<T>(traits)); + _query_builder.storeDataEventName(traits), _query_builder.storeDataEventStatement<T>(traits)); } // get the pqxx prepared statement invocation object to allow us to @@ -200,7 +170,7 @@ namespace pqxx_conn { handlePqxxError("The attribute [" + full_attr_name + "] data event was not saved.", ex.base().what(), - _query_builder.storeDataEventQuery<T>(traits), + _query_builder.storeDataEventStatement<T>(traits), LOCATION_INFO); } } diff --git a/src/QueryBuilder.cpp b/src/QueryBuilder.cpp index 32208b8e2973ddadb8f11823a69b439450e87f72..f928ed28c1a4b6d1e3cefacdfa9f24b97826cb1c 100644 --- a/src/QueryBuilder.cpp +++ b/src/QueryBuilder.cpp @@ -130,7 +130,7 @@ namespace pqxx_conn //============================================================================= //============================================================================= - const string &QueryBuilder::storeAttributeQuery() + const string &QueryBuilder::storeAttributeStatement() { // clang-format off static string query = @@ -167,7 +167,7 @@ namespace pqxx_conn //============================================================================= //============================================================================= - const string &QueryBuilder::storeHistoryStringQuery() + const string &QueryBuilder::storeHistoryStringStatement() { // clang-format off static string query = @@ -181,7 +181,7 @@ namespace pqxx_conn //============================================================================= //============================================================================= - const string &QueryBuilder::storeHistoryEventQuery() + const string &QueryBuilder::storeHistoryEventStatement() { // clang-format off static string query = @@ -200,7 +200,7 @@ namespace pqxx_conn //============================================================================= //============================================================================= - const string &QueryBuilder::storeParameterEventQuery() + const string &QueryBuilder::storeParameterEventStatement() { // clang-format off static string query = @@ -225,7 +225,7 @@ namespace pqxx_conn //============================================================================= //============================================================================= - const string &QueryBuilder::storeDataEventErrorQuery(const AttributeTraits &traits) + const string &QueryBuilder::storeDataEventErrorStatement(const AttributeTraits &traits) { // search the cache for a previous entry auto result = _data_event_error_queries.find(traits); @@ -260,7 +260,7 @@ namespace pqxx_conn //============================================================================= //============================================================================= - const string &QueryBuilder::storeErrorQuery() + const string &QueryBuilder::storeErrorStatement() { // clang-format off static string query = @@ -273,7 +273,7 @@ namespace pqxx_conn //============================================================================= //============================================================================= - const string QueryBuilder::fetchAllValuesQuery( + const string QueryBuilder::fetchAllValuesStatement( const string &column_name, const string &table_name, const string &reference) { return "SELECT " + column_name + ", " + reference + " " + "FROM " + table_name; @@ -281,7 +281,7 @@ namespace pqxx_conn //============================================================================= //============================================================================= - const string QueryBuilder::fetchValueQuery( + const string QueryBuilder::fetchValueStatement( const string &column_name, const string &table_name, const string &reference) { return "SELECT " + column_name + " " + "FROM " + table_name + " WHERE " + reference + "=$1"; @@ -289,7 +289,7 @@ namespace pqxx_conn //============================================================================= //============================================================================= - const string &QueryBuilder::fetchLastHistoryEventQuery() + const string &QueryBuilder::fetchLastHistoryEventStatement() { // clang-format off static string query = @@ -307,7 +307,7 @@ namespace pqxx_conn //============================================================================= //============================================================================= - const std::string &QueryBuilder::fetchAttributeTraitsQuery() + const std::string &QueryBuilder::fetchAttributeTraitsStatement() { // clang-format off static string query = diff --git a/src/QueryBuilder.hpp b/src/QueryBuilder.hpp index 82bc533694190a73327d71138a7f6df59187cc4f..502a815ec642efb43a67d46696ca7baeb07d888e 100644 --- a/src/QueryBuilder.hpp +++ b/src/QueryBuilder.hpp @@ -23,6 +23,8 @@ #include "AttributeTraits.hpp" #include "HdbppDefines.hpp" #include "TimescaleSchema.hpp" +#include "PqxxExtension.hpp" + #include "spdlog/spdlog.h" #include <iostream> @@ -59,6 +61,71 @@ namespace pqxx_conn // queries, it is specialized for all possible tango types in the source file template<typename T> std::string postgresCast(bool is_array); + + //============================================================================= + //============================================================================= + template<typename T> + struct ToString + { + static std::string run(std::unique_ptr<std::vector<T>> &value, const AttributeTraits&) + { + return pqxx::to_string(value); + } + }; + + //============================================================================= + //============================================================================= + template<> + struct ToString<bool> + { + static std::string run(std::unique_ptr<std::vector<bool>> &value, const AttributeTraits &traits) + { + // a vector<bool> is not actually a vector<bool>, rather its some kind of bitfield. When + // trying to return an element, we appear to get some kind of bitfield reference (?), + // so we return the value to a local variable to remove the referene to the bitfield and + // this ensure its actually a bool passed into the conversion framework + if (traits.isScalar()) + { + bool v = (*value)[0]; + return pqxx::to_string(v); + } + + // handled by our own extensions + pqxx::to_string(value); + } + }; + + //============================================================================= + //============================================================================= + template<> + struct ToString<std::string> + { + static std::string run(std::unique_ptr<std::vector<std::string>> &value, const AttributeTraits &traits) + { + // arrays of strings need both the ARRAY keywords and dollar escaping, this is so we + // do not have to rely on the postgres escape functions that double and then store + // escaped characters. This is a mess when extracting the array of strings. + if (traits.isScalar()) + { + // no special case needed for a scalar string + return pqxx::to_string((*value)[0]); + } + + auto iter = value->begin(); + std::string result = "ARRAY["; + + result = "$$" + pqxx::to_string((*iter)) + "$$"; + + for (++iter; iter != value->end(); ++iter) + { + result += ","; + result += "$$" + pqxx::to_string((*iter)) + "$$"; + } + + result += "]"; + return result; + } + }; }; // namespace query_utils // these are used as transactions names for pqxx, some are used to as prepared @@ -82,41 +149,53 @@ namespace pqxx_conn { public: - // Non-static methods + // Static Prepared statement strings + // these builder functions require no caching, so can be simple static + // functions + + static std::string tableName(const AttributeTraits &traits); + static const std::string &storeAttributeStatement(); + static const std::string &storeHistoryEventStatement(); + static const std::string &storeHistoryStringStatement(); + static const std::string &storeParameterEventStatement(); + static const std::string &storeErrorStatement(); + static const std::string &fetchLastHistoryEventStatement(); + static const std::string &fetchAttributeTraitsStatement(); + + static const std::string fetchValueStatement( + const std::string &column_name, const std::string &table_name, const std::string &reference); + + static const std::string fetchAllValuesStatement( + const std::string &column_name, const std::string &table_name, const std::string &reference); + + // Non-static prepared statements + // these builder functions cache the built queries, therefore they + // are not static like the others sincethey require data storage - // these builder functions cache the built query names, therefore they - // are not static like the others const std::string &storeDataEventName(const AttributeTraits &traits); const std::string &storeDataEventErrorName(const AttributeTraits &traits); - // like the query name functions, these cache data internally to speed up the - // process of putting data int the db + // builds a prepared statement for the given traits template<typename T> - const std::string &storeDataEventQuery(const AttributeTraits &traits); - - const std::string &storeDataEventErrorQuery(const AttributeTraits &traits); + const std::string &storeDataEventStatement(const AttributeTraits &traits); + // A varient of storeDataEventStatement that builds a string based on the + // parameters, this is then executed. Does not benfit from caching + template<typename T> + const std::string storeDataEventString( + const std::string &full_attr_name, + const std::string &event_time, + const std::string &quality, + std::unique_ptr<vector<T>> &value_r, + std::unique_ptr<vector<T>> &value_w, + const AttributeTraits &traits); + + // Builds a prepared statement for data event errors + const std::string &storeDataEventErrorStatement(const AttributeTraits &traits); + + // Utility void print(std::ostream &os) const noexcept; - // Static methods - - static std::string tableName(const AttributeTraits &traits); - static const std::string &storeAttributeQuery(); - static const std::string &storeHistoryEventQuery(); - static const std::string &storeHistoryStringQuery(); - static const std::string &storeParameterEventQuery(); - static const std::string &storeErrorQuery(); - static const std::string &fetchLastHistoryEventQuery(); - static const std::string &fetchAttributeTraitsQuery(); - - // these query strings are built each call, so are cached in the class - // that requests them - static const std::string fetchValueQuery( - const std::string &column_name, const std::string &table_name, const std::string &reference); - - static const std::string fetchAllValuesQuery( - const std::string &column_name, const std::string &table_name, const std::string &reference); - private: // generic function to handle caching items into the cache maps const string &handleCache( @@ -134,7 +213,7 @@ namespace pqxx_conn //============================================================================= //============================================================================= template<typename T> - const string &QueryBuilder::storeDataEventQuery(const AttributeTraits &traits) + const string &QueryBuilder::storeDataEventStatement(const AttributeTraits &traits) { // search the cache for a previous entry auto result = _data_event_queries.find(traits); @@ -182,6 +261,43 @@ namespace pqxx_conn // return the previously cached example return result->second; } + + template<typename T> + const std::string QueryBuilder::storeDataEventString(const std::string &full_attr_name, + const std::string &event_time, + const std::string &quality, + std::unique_ptr<vector<T>> &value_r, + std::unique_ptr<vector<T>> &value_w, + const AttributeTraits &traits) + { + auto query = "INSERT INTO " + QueryBuilder::tableName(traits) + " (" + DAT_COL_ID + "," + DAT_COL_DATA_TIME; + + if (traits.hasReadData()) + query = query + "," + DAT_COL_VALUE_R; + + if (traits.hasWriteData()) + query = query + "," + DAT_COL_VALUE_W; + + // split to ensure increments are in the correct order + query = query + "," + DAT_COL_QUALITY + ") VALUES (" + full_attr_name; + query = query + ",TO_TIMESTAMP(" + event_time + ")"; + + // add the read parameter with cast + if (traits.hasReadData()) + query = query + "," + query_utils::ToString<T>::run(value_r, traits) + + "::" + query_utils::postgresCast<T>(traits.isArray()); + + // add the write parameter with cast + if (traits.hasWriteData()) + query = query + "," + query_utils::ToString<T>::run(value_w, traits) + + "::" + query_utils::postgresCast<T>(traits.isArray()); + + query = query + "," + quality + ")"; + + // now return the built query + return query; + } + } // namespace pqxx_conn } // namespace hdbpp_internal #endif // _QUERY_BUILDER_HPP diff --git a/test/QueryBuilderTests.cpp b/test/QueryBuilderTests.cpp index 16feaf0b956023b1f3589f0b6cd522d353efd984..793f5c1bff988c43dd19e04eb48d75d396a51a76 100644 --- a/test/QueryBuilderTests.cpp +++ b/test/QueryBuilderTests.cpp @@ -19,14 +19,107 @@ #include "QueryBuilder.hpp" #include "TimescaleSchema.hpp" +#include "TestHelpers.hpp" #include "catch2/catch.hpp" using namespace std; using namespace hdbpp_internal; using namespace hdbpp_internal::pqxx_conn; +using namespace hdbpp_test::attr_name; using namespace Catch::Matchers; -SCENARIO("storeDataEventQuery() returns the correct Value fields for the given traits", "[query-string]") +SCENARIO("storeDataEventString() returns the correct Value fields for the given traits", "[query-string]") +{ + GIVEN("A query builder object with nothing cached") + { + QueryBuilder query_builder; + auto value_r = make_unique<vector<double>>(1.1, 2.2); + auto value_r_empty = make_unique<vector<double>>(); + auto value_w = make_unique<vector<double>>(3.3, 4.4); + auto value_w_empty = make_unique<vector<double>>(); + + WHEN("Requesting a query string for traits configured for Tango::READ") + { + AttributeTraits traits {Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE}; + + auto result = query_builder.storeDataEventString<double>( + TestAttrFQDName, + string("0"), + string("1"), + value_r, + value_w_empty, + traits); + + THEN("The result must include the DAT_COL_VALUE_R field only") + { + REQUIRE_THAT(result, Contains(DAT_COL_VALUE_R)); + REQUIRE_THAT(result, !Contains(DAT_COL_VALUE_W)); + REQUIRE_THAT(result, Contains(query_utils::ToString<double>::run(value_r, traits))); + } + } + WHEN("Requesting a query string for traits configured for Tango::WRITE") + { + AttributeTraits traits {Tango::WRITE, Tango::SCALAR, Tango::DEV_DOUBLE}; + + auto result = query_builder.storeDataEventString<double>( + TestAttrFQDName, + string("0"), + string("1"), + value_r_empty, + value_w, + traits); + + THEN("The result must include the DAT_COL_VALUE_W field only") + { + REQUIRE_THAT(result, !Contains(DAT_COL_VALUE_R)); + REQUIRE_THAT(result, Contains(DAT_COL_VALUE_W)); + REQUIRE_THAT(result, Contains(query_utils::ToString<double>::run(value_w, traits))); + } + } + WHEN("Requesting a query string for traits configured for Tango::READ_WRITE") + { + AttributeTraits traits {Tango::READ_WRITE, Tango::SCALAR, Tango::DEV_DOUBLE}; + + auto result = query_builder.storeDataEventString<double>( + TestAttrFQDName, + string("0"), + string("1"), + value_r, + value_w, + traits); + + THEN("The result must include both the DAT_COL_VALUE_R and DAT_COL_VALUE_W field") + { + REQUIRE_THAT(result, Contains(DAT_COL_VALUE_R)); + REQUIRE_THAT(result, Contains(DAT_COL_VALUE_W)); + REQUIRE_THAT(result, Contains(query_utils::ToString<double>::run(value_r, traits))); + REQUIRE_THAT(result, Contains(query_utils::ToString<double>::run(value_w, traits))); + } + } + WHEN("Requesting a query string for traits configured for Tango::READ_WITH_WRITE") + { + AttributeTraits traits {Tango::READ_WITH_WRITE, Tango::SCALAR, Tango::DEV_DOUBLE}; + + auto result = query_builder.storeDataEventString<double>( + TestAttrFQDName, + string("0"), + string("1"), + value_r, + value_w, + traits); + + THEN("The result must include both the DAT_COL_VALUE_R and DAT_COL_VALUE_W field") + { + REQUIRE_THAT(result, Contains(DAT_COL_VALUE_R)); + REQUIRE_THAT(result, Contains(DAT_COL_VALUE_W)); + REQUIRE_THAT(result, Contains(query_utils::ToString<double>::run(value_r, traits))); + REQUIRE_THAT(result, Contains(query_utils::ToString<double>::run(value_w, traits))); + } + } + } +} + +SCENARIO("storeDataEventStatement() returns the correct Value fields for the given traits", "[query-string]") { GIVEN("A query builder object with nothing cached") { @@ -35,7 +128,7 @@ SCENARIO("storeDataEventQuery() returns the correct Value fields for the given t WHEN("Requesting a query string for traits configured for Tango::READ") { AttributeTraits traits {Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE}; - auto result = query_builder.storeDataEventQuery<double>(traits); + auto result = query_builder.storeDataEventStatement<double>(traits); THEN("The result must include the DAT_COL_VALUE_R field only") { @@ -48,7 +141,7 @@ SCENARIO("storeDataEventQuery() returns the correct Value fields for the given t WHEN("Requesting a query string for traits configured for Tango::WRITE") { AttributeTraits traits {Tango::WRITE, Tango::SCALAR, Tango::DEV_DOUBLE}; - auto result = query_builder.storeDataEventQuery<double>(traits); + auto result = query_builder.storeDataEventStatement<double>(traits); THEN("The result must include the DAT_COL_VALUE_W field only") { @@ -61,7 +154,7 @@ SCENARIO("storeDataEventQuery() returns the correct Value fields for the given t WHEN("Requesting a query string for traits configured for Tango::READ_WRITE") { AttributeTraits traits {Tango::READ_WRITE, Tango::SCALAR, Tango::DEV_DOUBLE}; - auto result = query_builder.storeDataEventQuery<double>(traits); + auto result = query_builder.storeDataEventStatement<double>(traits); THEN("The result must include both the DAT_COL_VALUE_R and DAT_COL_VALUE_W field") { @@ -74,7 +167,7 @@ SCENARIO("storeDataEventQuery() returns the correct Value fields for the given t WHEN("Requesting a query string for traits configured for Tango::READ_WITH_WRITE") { AttributeTraits traits {Tango::READ_WITH_WRITE, Tango::SCALAR, Tango::DEV_DOUBLE}; - auto result = query_builder.storeDataEventQuery<double>(traits); + auto result = query_builder.storeDataEventStatement<double>(traits); THEN("The result must include both the DAT_COL_VALUE_R and DAT_COL_VALUE_W field") { @@ -163,4 +256,4 @@ TEST_CASE("Creating valid database table names for types", "[query-string]") } } } -} \ No newline at end of file +}