From 5b0430fdf4617b396e462872d438a663b174a3a8 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 12 Oct 2012 17:24:44 +0200 Subject: Completion of prepared query support --- odb/connection.cxx | 79 +++++++++++++++++++++++++++++++++++++++++ odb/connection.hxx | 76 +++++++++++++++++++++++++++++++++++++--- odb/connection.ixx | 58 ++++++++++++++++++++++++++++++ odb/connection.txx | 43 +++++++++++++++++++---- odb/database.hxx | 68 ++++++++++++++++++++++++++++++++++++ odb/database.ixx | 91 ++++++++++++++++++++++++++++++++++++++++++++++++ odb/database.txx | 1 - odb/details/c-string.hxx | 31 +++++++++++++++++ odb/exceptions.cxx | 48 ++++++++++++++++++++++--- odb/exceptions.hxx | 40 +++++++++++++++++++++ odb/prepared-query.hxx | 32 +++++++++++++++-- odb/statement.cxx | 9 +++++ odb/statement.hxx | 22 +++++++++--- 13 files changed, 574 insertions(+), 24 deletions(-) create mode 100644 odb/details/c-string.hxx diff --git a/odb/connection.cxx b/odb/connection.cxx index d14e510..f479131 100644 --- a/odb/connection.cxx +++ b/odb/connection.cxx @@ -2,12 +2,91 @@ // copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC // license : GNU GPL v2; see accompanying LICENSE file +#include #include +#include // prepared_* + +using namespace std; namespace odb { connection:: ~connection () { + for (prepared_map_type::iterator i (prepared_map_.begin ()), + e (prepared_map_.end ()); i != e; ++i) + { + if (i->second.params != 0) + i->second.params_deleter (i->second.params); + } + } + + void connection:: + cache_query_ (details::shared_ptr pq, + const type_info& ti, + void* params, + const type_info* params_info, + void (*params_deleter) (void*)) + { + pair r ( + prepared_map_.insert ( + prepared_map_type::value_type (pq->name, prepared_entry_type ()))); + + if (!r.second) + throw prepared_already_cached (pq->name); + + prepared_entry_type& e (r.first->second); + + e.prep_query = pq; + e.type_info = &ti; + + // Mark the statement as cached. + // + pq->stmt->cached (true); + + e.params = params; + e.params_info = params_info; + e.params_deleter = params_deleter; + } + + details::shared_ptr connection:: + lookup_query_ (const char* name, + const type_info& ti, + void** params, + const type_info* params_info) const + { + prepared_map_type::const_iterator i (prepared_map_.find (name)); + + if (i == prepared_map_.end ()) + { + // See if there is a factory. + // + database_type::query_factory_type f ( + database_.lookup_query_factory (name)); + + if (f) + { + f (name, const_cast (*this)); + i = prepared_map_.find (name); + } + } + + if (i == prepared_map_.end ()) + return details::shared_ptr (); + + // Make sure the types match. + // + if (*i->second.type_info != ti) + throw prepared_type_mismatch (name); + + if (params != 0) + { + if (*i->second.params_info != *params_info) + throw prepared_type_mismatch (name); + + *params = i->second.params; + } + + return i->second.prep_query; } } diff --git a/odb/connection.hxx b/odb/connection.hxx index f274b39..b4a9d5b 100644 --- a/odb/connection.hxx +++ b/odb/connection.hxx @@ -7,14 +7,20 @@ #include +#include #include -#include // std::size_t +#include // std::auto_ptr, std::unique_ptr +#include // std::size_t +#include #include +#include #include #include +#include // ODB_CXX11 #include +#include #include namespace odb @@ -57,15 +63,37 @@ namespace odb public: template prepared_query - prepare_query (const char* name, const char* q); + prepare_query (const char* name, const char*); template prepared_query - prepare_query (const char* name, const std::string& q); + prepare_query (const char* name, const std::string&); template prepared_query - prepare_query (const char* name, const query& q); + prepare_query (const char* name, const query&); + + template + void + cache_query (const prepared_query&); + + template + void + cache_query (const prepared_query&, std::auto_ptr

params); + +#ifdef ODB_CXX11 + template + void + cache_query (const prepared_query&, std::unique_ptr

params); +#endif + + template + prepared_query + lookup_query (const char* name) const; + + template + prepared_query + lookup_query (const char* name, P*& params) const; // SQL statement tracing. // @@ -98,10 +126,50 @@ namespace odb protected: connection (database_type&); + template ::kind> + struct query_; + + virtual void + cache_query_ (details::shared_ptr pq, + const std::type_info& ti, + void* params, + const std::type_info* params_info, + void (*params_deleter) (void*)); + + details::shared_ptr + lookup_query_ (const char* name, + const std::type_info& ti, + void** params, // out + const std::type_info* params_info) const; + + template + static void + params_deleter (void*); + private: connection (const connection&); connection& operator= (const connection&); + // Prepared query cache. + // + protected: + struct prepared_entry_type + { + details::shared_ptr prep_query; + const std::type_info* type_info; + void* params; + const std::type_info* params_info; + void (*params_deleter) (void*); + }; + + typedef + std::map + prepared_map_type; + + prepared_map_type prepared_map_; + protected: database_type& database_; tracer_type* tracer_; diff --git a/odb/connection.ixx b/odb/connection.ixx index fd1a624..8842d8e 100644 --- a/odb/connection.ixx +++ b/odb/connection.ixx @@ -3,6 +3,7 @@ // license : GNU GPL v2; see accompanying LICENSE file #include // std::string +#include namespace odb { @@ -44,6 +45,63 @@ namespace odb return prepare_query (n, query (q)); } + template + inline prepared_query connection:: + prepare_query (const char* n, const query& q) + { + return query_::call (*this, n, q); + } + + template + inline void connection:: + cache_query (const prepared_query& pq) + { + assert (pq); + cache_query_ (pq.impl_, typeid (T), 0, 0, 0); + } + + template + inline void connection:: + cache_query (const prepared_query& pq, std::auto_ptr

params) + { + assert (pq); + assert (params.get () != 0); + cache_query_ ( + pq.impl_, typeid (T), params.get (), &typeid (P), ¶ms_deleter

); + params.release (); + } + +#ifdef ODB_CXX11 + template + inline void connection:: + cache_query (const prepared_query& pq, std::unique_ptr

params) + { + assert (pq); + assert (params); + cache_query_ ( + pq.impl_, typeid (T), params.get (), &typeid (P), ¶ms_deleter

); + params.release (); + } +#endif + + template + inline prepared_query connection:: + lookup_query (const char* name) const + { + return prepared_query (lookup_query_ (name, typeid (T), 0, 0)); + } + + template + inline prepared_query connection:: + lookup_query (const char* name, P*& params) const + { + return prepared_query ( + lookup_query_ (name, + typeid (T), + reinterpret_cast (¶ms), + &typeid (P))); + } + inline void connection:: tracer (tracer_type& t) { diff --git a/odb/connection.txx b/odb/connection.txx index c5662d9..7861851 100644 --- a/odb/connection.txx +++ b/odb/connection.txx @@ -4,13 +4,42 @@ namespace odb { - template - prepared_query connection:: - prepare_query (const char* n, const query& q) + template + struct connection::query_ { - //@@ Views. Inline? - // - return prepared_query ( - object_traits_impl::prepare_query (*this, n, q)); + template + static prepared_query + call (connection& c, const char* n, const Q& q) + { + // C++ compiler complaining there is no prepare_query()? Perhaps + // you forgot to specify --generate-prepared when compiling your + // persistent classes. + // + return prepared_query ( + object_traits_impl::prepare_query (c, n, q)); + } + }; + + template + struct connection::query_ + { + template + static prepared_query + call (connection& c, const char* n, const Q& q) + { + // C++ compiler complaining there is no prepare_query()? Perhaps + // you forgot to specify --generate-prepared when compiling your + // views. + // + return prepared_query ( + view_traits_impl::prepare_query (c, n, q)); + } + }; + + template + void connection:: + params_deleter (void* p) + { + delete static_cast (p); } } diff --git a/odb/database.hxx b/odb/database.hxx index 99205e7..506245f 100644 --- a/odb/database.hxx +++ b/odb/database.hxx @@ -7,17 +7,27 @@ #include +#include // ODB_CXX11 + +#include #include +#include // std::auto_ptr, std::unique_ptr #include // std::size_t +#ifdef ODB_CXX11 +# include // std::function +#endif + #include #include #include +#include #include #include #include #include +#include namespace odb { @@ -214,6 +224,59 @@ namespace odb result query (const odb::query&, bool cache = true); + // Query preparation. + // + template + prepared_query + prepare_query (const char* name, const char*); + + template + prepared_query + prepare_query (const char* name, const std::string&); + + template + prepared_query + prepare_query (const char* name, const odb::query&); + + template + void + cache_query (const prepared_query&); + + template + void + cache_query (const prepared_query&, std::auto_ptr

params); + +#ifdef ODB_CXX11 + template + void + cache_query (const prepared_query&, std::unique_ptr

params); +#endif + + template + prepared_query + lookup_query (const char* name) const; + + template + prepared_query + lookup_query (const char* name, P*& params) const; + + // Prepared query factory. + // + public: +#ifdef ODB_CXX11 + typedef + std::function + query_factory_type; +#else + typedef void (*query_factory_type) (const char*, connection&); +#endif + + void + query_factory (const char* name, query_factory_type); + + query_factory_type + lookup_query_factory (const char* name) const; + // Native database statement execution. // public: @@ -326,8 +389,13 @@ namespace odb struct query_; protected: + typedef + std::map + query_factory_map; + database_id id_; tracer_type* tracer_; + query_factory_map query_factory_map_; }; } diff --git a/odb/database.ixx b/odb/database.ixx index aeffdec..5b6f876 100644 --- a/odb/database.ixx +++ b/odb/database.ixx @@ -3,6 +3,9 @@ // license : GNU GPL v2; see accompanying LICENSE file #include // std::strlen() +#include // std::move + +#include namespace odb { @@ -25,6 +28,30 @@ namespace odb } inline void database:: + query_factory (const char* name, query_factory_type f) + { + if (f) +#ifdef ODB_CXX11 + query_factory_map_[name] = std::move (f); +#else + query_factory_map_[name] = f; +#endif + else + query_factory_map_.erase (name); + } + + inline database::query_factory_type database:: + lookup_query_factory (const char* name) const + { + query_factory_map::const_iterator i (query_factory_map_.find (name)); + + if (i == query_factory_map_.end ()) + i = query_factory_map_.find (""); // Wildcard factory. + + return i != query_factory_map_.end () ? i->second : 0; + } + + inline void database:: tracer (tracer_type& t) { tracer_ = &t; @@ -410,6 +437,70 @@ namespace odb return query (odb::query (q), cache); } + template + inline prepared_query database:: + prepare_query (const char* n, const char* q) + { + return prepare_query (n, odb::query (q)); + } + + template + inline prepared_query database:: + prepare_query (const char* n, const std::string& q) + { + return prepare_query (n, odb::query (q)); + } + + template + inline prepared_query database:: + prepare_query (const char* n, const odb::query& q) + { + connection_type& c (transaction::current ().connection ()); + return c.prepare_query (n, q); + } + + template + inline void database:: + cache_query (const prepared_query& pq) + { + connection_type& c (transaction::current ().connection ()); + c.cache_query (pq); + } + + template + inline void database:: + cache_query (const prepared_query& pq, std::auto_ptr

params) + { + connection_type& c (transaction::current ().connection ()); + c.cache_query (pq, params); + } + +#ifdef ODB_CXX11 + template + inline void database:: + cache_query (const prepared_query& pq, std::unique_ptr

params) + { + connection_type& c (transaction::current ().connection ()); + c.cache_query (pq, std::move (params)); + } +#endif + + template + inline prepared_query database:: + lookup_query (const char* name) const + { + connection_type& c (transaction::current ().connection ()); + return c.lookup_query (name); + } + + template + inline prepared_query database:: + lookup_query (const char* name, P*& params) const + { + connection_type& c (transaction::current ().connection ()); + return c.lookup_query (name, params); + } + // Implementations (i.e., the *_() functions). // template diff --git a/odb/database.txx b/odb/database.txx index 23ea712..5556982 100644 --- a/odb/database.txx +++ b/odb/database.txx @@ -3,7 +3,6 @@ // license : GNU GPL v2; see accompanying LICENSE file #include -#include #include #include diff --git a/odb/details/c-string.hxx b/odb/details/c-string.hxx new file mode 100644 index 0000000..ce029f8 --- /dev/null +++ b/odb/details/c-string.hxx @@ -0,0 +1,31 @@ +// file : odb/details/c-string.hxx +// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_DETAILS_C_STRING_HXX +#define ODB_DETAILS_C_STRING_HXX + +#include + +#include + +#include + +namespace odb +{ + namespace details + { + struct LIBODB_EXPORT c_string_comparator + { + bool + operator() (const char* x, const char* y) const + { + return std::strcmp (x, y) < 0; + } + }; + } +} + +#include + +#endif // ODB_DETAILS_C_STRING_HXX diff --git a/odb/exceptions.cxx b/odb/exceptions.cxx index d56711c..c49d516 100644 --- a/odb/exceptions.cxx +++ b/odb/exceptions.cxx @@ -2,8 +2,6 @@ // copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC // license : GNU GPL v2; see accompanying LICENSE file -#include - #include using namespace std; @@ -106,13 +104,53 @@ namespace odb return "no type information"; } + prepared_already_cached:: + prepared_already_cached (const char* name) + : name_ (name) + { + what_ = "prepared query '"; + what_ += name; + what_ += "' is already cached"; + } + + prepared_already_cached:: + ~prepared_already_cached () throw () + { + } + + const char* prepared_already_cached:: + what () const throw () + { + return what_.c_str (); + } + + prepared_type_mismatch:: + prepared_type_mismatch (const char* name) + : name_ (name) + { + what_ = "type mismatch while looking up prepared query '"; + what_ += name; + what_ += "'"; + } + + prepared_type_mismatch:: + ~prepared_type_mismatch () throw () + { + } + + const char* prepared_type_mismatch:: + what () const throw () + { + return what_.c_str (); + } + unknown_schema:: unknown_schema (const std::string& name) : name_ (name) { - ostringstream ostr; - ostr << "unknown database schema '" << name << "'"; - what_ = ostr.str (); + what_ = "unknown database schema '"; + what_ += name; + what_ += "'"; } unknown_schema:: diff --git a/odb/exceptions.hxx b/odb/exceptions.hxx index 9e37967..90729fc 100644 --- a/odb/exceptions.hxx +++ b/odb/exceptions.hxx @@ -127,6 +127,46 @@ namespace odb what () const throw (); }; + // Prepared query support exceptions. + // + struct LIBODB_EXPORT prepared_already_cached: exception + { + prepared_already_cached (const char* name); + ~prepared_already_cached () throw (); + + const char* + name () const + { + return name_; + } + + virtual const char* + what () const throw (); + + private: + const char* name_; + std::string what_; + }; + + struct LIBODB_EXPORT prepared_type_mismatch: exception + { + prepared_type_mismatch (const char* name); + ~prepared_type_mismatch () throw (); + + const char* + name () const + { + return name_; + } + + virtual const char* + what () const throw (); + + private: + const char* name_; + std::string what_; + }; + // Schema catalog exceptions. // struct LIBODB_EXPORT unknown_schema: exception diff --git a/odb/prepared-query.hxx b/odb/prepared-query.hxx index 60ae48d..e4c9c2b 100644 --- a/odb/prepared-query.hxx +++ b/odb/prepared-query.hxx @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -22,12 +23,19 @@ namespace odb ~prepared_query_impl (); const char* name; + details::shared_ptr stmt; details::shared_ptr (*execute) (prepared_query_impl&); }; template struct prepared_query { + prepared_query () {} + + explicit + prepared_query (details::shared_ptr impl) + : impl_ (impl) {} + result execute (bool cache = true) { @@ -46,11 +54,29 @@ namespace odb return r; } - explicit - prepared_query (details::shared_ptr impl) - : impl_ (impl) {} + const char* + name () const + { + return impl_->name; + } + + typedef odb::statement statement_type; + + statement_type& + statement () const + { + return *impl_->stmt; + } + + typedef details::shared_ptr + prepared_query::*unspecified_bool_type; + operator unspecified_bool_type () const + { + return impl_ ? &prepared_query::impl_ : 0; + } private: + friend class connection; details::shared_ptr impl_; }; diff --git a/odb/statement.cxx b/odb/statement.cxx index 1d96078..eb99227 100644 --- a/odb/statement.cxx +++ b/odb/statement.cxx @@ -2,6 +2,8 @@ // copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC // license : GNU GPL v2; see accompanying LICENSE file +#include + #include namespace odb @@ -10,4 +12,11 @@ namespace odb ~statement () { } + + void statement:: + cached (bool cached) + { + assert (cached); + cached_ = true; + } } diff --git a/odb/statement.hxx b/odb/statement.hxx index b7295ad..3cd5598 100644 --- a/odb/statement.hxx +++ b/odb/statement.hxx @@ -16,6 +16,10 @@ namespace odb { class LIBODB_EXPORT statement: public details::shared_base { + private: + statement (const statement&); + statement& operator= (const statement&); + public: typedef odb::connection connection_type; @@ -28,12 +32,22 @@ namespace odb virtual ~statement () = 0; + // Statement caching status. + // + public: + bool + cached () const + { + return cached_; + } + + virtual void + cached (bool); + protected: - statement () {} + statement (): cached_ (false) {} - private: - statement (const statement&); - statement& operator= (const statement&); + bool cached_; }; } -- cgit v1.1