From b148409655e62e993043c792b464ffc3bb64c905 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 25 Aug 2024 17:28:47 +0200 Subject: [PATCH] Implement session::get_last_query_with_context() Fixes #678 Fixes #1027 --- include/soci/logger.h | 14 ++++++++ include/soci/session.h | 3 ++ src/core/logger.cpp | 7 ++++ src/core/session.cpp | 79 ++++++++++++++++++++++++++++++++++++++++++ src/core/statement.cpp | 36 ++++++++++++------- tests/common-tests.h | 12 +++++-- 6 files changed, 137 insertions(+), 14 deletions(-) diff --git a/include/soci/logger.h b/include/soci/logger.h index f44e88459..302ffc6fe 100644 --- a/include/soci/logger.h +++ b/include/soci/logger.h @@ -30,6 +30,11 @@ class SOCI_DECL logger_impl // Called to indicate that a new query is about to be executed. virtual void start_query(std::string const & query) = 0; + // Called to log a parameter that is bound to the currently active query + virtual void add_query_parameter(std::string name, std::string value) = 0; + + virtual void reset_query_parameter() = 0; + logger_impl * clone() const; // These methods are for compatibility only as they're used to implement @@ -38,6 +43,7 @@ class SOCI_DECL logger_impl virtual void set_stream(std::ostream * s); virtual std::ostream * get_stream() const; virtual std::string get_last_query() const; + virtual std::string get_last_query_with_context() const; private: // Override to return a new heap-allocated copy of this object. @@ -68,10 +74,18 @@ class SOCI_DECL logger void start_query(std::string const & query) { m_impl->start_query(query); } + virtual void add_query_parameter(std::string name, std::string value) + { + m_impl->add_query_parameter(std::move(name), std::move(value)); + } + + virtual void reset_query_parameter() { m_impl->reset_query_parameter(); } + // Methods used for the implementation of session basic logging support. void set_stream(std::ostream * s) { m_impl->set_stream(s); } std::ostream * get_stream() const { return m_impl->get_stream(); } std::string get_last_query() const { return m_impl->get_last_query(); } + std::string get_last_query_with_context() const { return m_impl->get_last_query_with_context(); } private: logger_impl * m_impl; diff --git a/include/soci/session.h b/include/soci/session.h index 22c6727af..157080989 100644 --- a/include/soci/session.h +++ b/include/soci/session.h @@ -106,7 +106,10 @@ class SOCI_DECL session std::ostream * get_log_stream() const; void log_query(std::string const & query); + void reset_query_parameter(); + void add_query_parameter(std::string name, std::string value); std::string get_last_query() const; + std::string get_last_query_with_context() const; void set_got_data(bool gotData); bool got_data() const; diff --git a/src/core/logger.cpp b/src/core/logger.cpp index 6e9e1e08c..51520514f 100644 --- a/src/core/logger.cpp +++ b/src/core/logger.cpp @@ -57,6 +57,13 @@ std::string logger_impl::get_last_query() const SOCI_DUMMY_RETURN(std::string()); } +std::string logger_impl::get_last_query_with_context() const +{ + throw_not_supported(); + + SOCI_DUMMY_RETURN(std::string()); +} + logger::logger(logger_impl * impl) : m_impl(impl) { diff --git a/src/core/session.cpp b/src/core/session.cpp index fab538f8e..5b703fe1f 100644 --- a/src/core/session.cpp +++ b/src/core/session.cpp @@ -26,6 +26,15 @@ void ensureConnected(session_backend * backEnd) } } +struct query_parameter +{ + query_parameter(std::string name = {}, std::string value = {}) + : name(std::move(name)), value(std::move(value)) {} + + std::string name; + std::string value; +}; + // Standard logger class used by default. class standard_logger_impl : public logger_impl { @@ -43,8 +52,16 @@ class standard_logger_impl : public logger_impl } lastQuery_ = query; + reset_query_parameter(); + } + + virtual void add_query_parameter(std::string name, std::string value) + { + queryParams_.emplace_back(std::move(name), std::move(value)); } + virtual void reset_query_parameter() { queryParams_.clear(); } + virtual void set_stream(std::ostream * s) { logStream_ = s; @@ -60,6 +77,31 @@ class standard_logger_impl : public logger_impl return lastQuery_; } + virtual std::string get_last_query_with_context() const + { + if (queryParams_.empty()) { + return get_last_query(); + } + + std::string query = get_last_query(); + + query += " with "; + + for (std::size_t i = 0; i < queryParams_.size(); ++i) + { + const query_parameter ¶m = queryParams_[i]; + + query += ":" + param.name + "=" + param.value; + + if (i + 1 < queryParams_.size()) + { + query += ", "; + } + } + + return query; + } + private: virtual logger_impl* do_clone() const { @@ -68,6 +110,7 @@ class standard_logger_impl : public logger_impl std::ostream * logStream_; std::string lastQuery_; + std::vector queryParams_; }; } // namespace anonymous @@ -449,6 +492,30 @@ void session::log_query(std::string const & query) } } +void session::reset_query_parameter() +{ + if (isFromPool_) + { + pool_->at(poolPosition_).reset_query_parameter(); + } + else + { + logger_.reset_query_parameter(); + } +} + +void session::add_query_parameter(std::string name, std::string value) +{ + if (isFromPool_) + { + pool_->at(poolPosition_).add_query_parameter(std::move(name), std::move(value)); + } + else + { + logger_.add_query_parameter(std::move(name), std::move(value)); + } +} + std::string session::get_last_query() const { if (isFromPool_) @@ -461,6 +528,18 @@ std::string session::get_last_query() const } } +std::string session::get_last_query_with_context() const +{ + if (isFromPool_) + { + return pool_->at(poolPosition_).get_last_query_with_context(); + } + else + { + return logger_.get_last_query_with_context(); + } +} + void session::set_got_data(bool gotData) { if (isFromPool_) diff --git a/src/core/statement.cpp b/src/core/statement.cpp index 7bf5e777c..d91dc0410 100644 --- a/src/core/statement.cpp +++ b/src/core/statement.cpp @@ -17,11 +17,27 @@ #include #include #include +#include +#include using namespace soci; using namespace soci::details; +std::string get_name(const details::use_type_base ¶m, std::size_t position, + const statement_backend *backend) +{ + // Use the name specified in the "use()" call if any, + // otherwise get the name of the matching parameter from + // the query itself, as parsed by the backend. + std::string name = param.get_name(); + if (backend && name.empty()) + name = backend->get_parameter_name(static_cast(position)); + + return name.empty() ? std::to_string(position + 1) : name; +} + + statement_impl::statement_impl(session & s) : session_(s), refCount_(1), row_(0), fetchSize_(1), initialFetchSize_(1), @@ -574,10 +590,17 @@ void statement_impl::pre_fetch() void statement_impl::pre_use() { + session_.reset_query_parameter(); + std::size_t const usize = uses_.size(); for (std::size_t i = 0; i != usize; ++i) { uses_[i]->pre_use(); + + std::string name = get_name(*uses_[i], i, backEnd_); + std::stringstream value; + uses_[i]->dump_value(value); + session_.add_query_parameter(std::move(name), value.str()); } } @@ -854,20 +877,9 @@ statement_impl::rethrow_current_exception_with_context(char const* operation) details::use_type_base const& u = *uses_[i]; - // Use the name specified in the "use()" call if any, - // otherwise get the name of the matching parameter from - // the query itself, as parsed by the backend. - std::string name = u.get_name(); - if (name.empty()) - name = backEnd_->get_parameter_name(static_cast(i)); + oss << ":" << get_name(u, i, backEnd_); - oss << ":"; - if (!name.empty()) - oss << name; - else - oss << (i + 1); oss << "="; - u.dump_value(oss); } } diff --git a/tests/common-tests.h b/tests/common-tests.h index 139bd2ba6..69de68691 100644 --- a/tests/common-tests.h +++ b/tests/common-tests.h @@ -3640,16 +3640,20 @@ TEST_CASE_METHOD(common_tests, "Basic logging support", "[core][logging]") catch (...) {} CHECK(sql.get_last_query() == "drop table soci_test1"); + CHECK(sql.get_last_query() == sql.get_last_query_with_context()); sql.set_log_stream(NULL); try { - sql << "drop table soci_test2"; + int val1 = 1; + std::string val2 = "b"; + sql << "insert into soci_test2 (a,b) values (:first,:second)", use(val1), use(val2); } catch (...) {} - CHECK(sql.get_last_query() == "drop table soci_test2"); + CHECK(sql.get_last_query() == "insert into soci_test2 (a,b) values (:first,:second)"); + CHECK(sql.get_last_query() == "insert into soci_test2 (a,b) values (:first,:second) with :first=1, :second=\"b\""); sql.set_log_stream(&log); @@ -6974,6 +6978,10 @@ TEST_CASE_METHOD(common_tests, "Logger", "[core][log]") m_logbuf.push_back(query); } + virtual void reset_query_parameter() {} + + virtual void add_query_parameter(std::string /*name*/, std::string /*value*/) {} + private: virtual logger_impl* do_clone() const {