diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2012-10-12 17:24:44 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2012-10-19 11:38:24 +0200 |
commit | 5b0430fdf4617b396e462872d438a663b174a3a8 (patch) | |
tree | e89e0cc5b1fdff4e3f49c9c7b7607d2b90108c31 | |
parent | 5c705a90d348a2a9428d5121a24eb47d0d73eb39 (diff) |
Completion of prepared query support
-rw-r--r-- | odb/connection.cxx | 79 | ||||
-rw-r--r-- | odb/connection.hxx | 76 | ||||
-rw-r--r-- | odb/connection.ixx | 58 | ||||
-rw-r--r-- | odb/connection.txx | 43 | ||||
-rw-r--r-- | odb/database.hxx | 68 | ||||
-rw-r--r-- | odb/database.ixx | 91 | ||||
-rw-r--r-- | odb/database.txx | 1 | ||||
-rw-r--r-- | odb/details/c-string.hxx | 31 | ||||
-rw-r--r-- | odb/exceptions.cxx | 48 | ||||
-rw-r--r-- | odb/exceptions.hxx | 40 | ||||
-rw-r--r-- | odb/prepared-query.hxx | 32 | ||||
-rw-r--r-- | odb/statement.cxx | 9 | ||||
-rw-r--r-- | odb/statement.hxx | 22 |
13 files changed, 574 insertions, 24 deletions
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 <odb/database.hxx> #include <odb/connection.hxx> +#include <odb/exceptions.hxx> // 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<prepared_query_impl> pq, + const type_info& ti, + void* params, + const type_info* params_info, + void (*params_deleter) (void*)) + { + pair<prepared_map_type::iterator, bool> 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<prepared_query_impl> 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<connection&> (*this)); + i = prepared_map_.find (name); + } + } + + if (i == prepared_map_.end ()) + return details::shared_ptr<prepared_query_impl> (); + + // 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 <odb/pre.hxx> +#include <map> #include <string> -#include <cstddef> // std::size_t +#include <memory> // std::auto_ptr, std::unique_ptr +#include <cstddef> // std::size_t +#include <typeinfo> #include <odb/forward.hxx> +#include <odb/traits.hxx> #include <odb/query.hxx> #include <odb/prepared-query.hxx> +#include <odb/details/config.hxx> // ODB_CXX11 #include <odb/details/export.hxx> +#include <odb/details/c-string.hxx> #include <odb/details/shared-ptr.hxx> namespace odb @@ -57,15 +63,37 @@ namespace odb public: template <typename T> prepared_query<T> - prepare_query (const char* name, const char* q); + prepare_query (const char* name, const char*); template <typename T> prepared_query<T> - prepare_query (const char* name, const std::string& q); + prepare_query (const char* name, const std::string&); template <typename T> prepared_query<T> - prepare_query (const char* name, const query<T>& q); + prepare_query (const char* name, const query<T>&); + + template <typename T> + void + cache_query (const prepared_query<T>&); + + template <typename T, typename P> + void + cache_query (const prepared_query<T>&, std::auto_ptr<P> params); + +#ifdef ODB_CXX11 + template <typename T, typename P> + void + cache_query (const prepared_query<T>&, std::unique_ptr<P> params); +#endif + + template <typename T> + prepared_query<T> + lookup_query (const char* name) const; + + template <typename T, typename P> + prepared_query<T> + lookup_query (const char* name, P*& params) const; // SQL statement tracing. // @@ -98,10 +126,50 @@ namespace odb protected: connection (database_type&); + template <typename T, + database_id DB, + class_kind kind = class_traits<T>::kind> + struct query_; + + virtual void + cache_query_ (details::shared_ptr<prepared_query_impl> pq, + const std::type_info& ti, + void* params, + const std::type_info* params_info, + void (*params_deleter) (void*)); + + details::shared_ptr<prepared_query_impl> + lookup_query_ (const char* name, + const std::type_info& ti, + void** params, // out + const std::type_info* params_info) const; + + template <typename P> + static void + params_deleter (void*); + private: connection (const connection&); connection& operator= (const connection&); + // Prepared query cache. + // + protected: + struct prepared_entry_type + { + details::shared_ptr<prepared_query_impl> prep_query; + const std::type_info* type_info; + void* params; + const std::type_info* params_info; + void (*params_deleter) (void*); + }; + + typedef + std::map<const char*, prepared_entry_type, details::c_string_comparator> + 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 <cstring> // std::string +#include <cassert> namespace odb { @@ -44,6 +45,63 @@ namespace odb return prepare_query<T> (n, query<T> (q)); } + template <typename T> + inline prepared_query<T> connection:: + prepare_query (const char* n, const query<T>& q) + { + return query_<T, id_default>::call (*this, n, q); + } + + template <typename T> + inline void connection:: + cache_query (const prepared_query<T>& pq) + { + assert (pq); + cache_query_ (pq.impl_, typeid (T), 0, 0, 0); + } + + template <typename T, typename P> + inline void connection:: + cache_query (const prepared_query<T>& pq, std::auto_ptr<P> params) + { + assert (pq); + assert (params.get () != 0); + cache_query_ ( + pq.impl_, typeid (T), params.get (), &typeid (P), ¶ms_deleter<P>); + params.release (); + } + +#ifdef ODB_CXX11 + template <typename T, typename P> + inline void connection:: + cache_query (const prepared_query<T>& pq, std::unique_ptr<P> params) + { + assert (pq); + assert (params); + cache_query_ ( + pq.impl_, typeid (T), params.get (), &typeid (P), ¶ms_deleter<P>); + params.release (); + } +#endif + + template <typename T> + inline prepared_query<T> connection:: + lookup_query (const char* name) const + { + return prepared_query<T> (lookup_query_ (name, typeid (T), 0, 0)); + } + + template <typename T, typename P> + inline prepared_query<T> connection:: + lookup_query (const char* name, P*& params) const + { + return prepared_query<T> ( + lookup_query_ (name, + typeid (T), + reinterpret_cast<void**> (¶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 <typename T> - prepared_query<T> connection:: - prepare_query (const char* n, const query<T>& q) + template <typename T, database_id DB> + struct connection::query_<T, DB, class_object> { - //@@ Views. Inline? - // - return prepared_query<T> ( - object_traits_impl<T, id_default>::prepare_query (*this, n, q)); + template <typename Q> + static prepared_query<T> + 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<T> ( + object_traits_impl<T, DB>::prepare_query (c, n, q)); + } + }; + + template <typename T, database_id DB> + struct connection::query_<T, DB, class_view> + { + template <typename Q> + static prepared_query<T> + 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<T> ( + view_traits_impl<T, DB>::prepare_query (c, n, q)); + } + }; + + template <typename P> + void connection:: + params_deleter (void* p) + { + delete static_cast<P*> (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 <odb/pre.hxx> +#include <odb/details/config.hxx> // ODB_CXX11 + +#include <map> #include <string> +#include <memory> // std::auto_ptr, std::unique_ptr #include <cstddef> // std::size_t +#ifdef ODB_CXX11 +# include <functional> // std::function +#endif + #include <odb/traits.hxx> #include <odb/forward.hxx> #include <odb/query.hxx> +#include <odb/prepared-query.hxx> #include <odb/result.hxx> #include <odb/connection.hxx> #include <odb/exceptions.hxx> #include <odb/details/export.hxx> +#include <odb/details/c-string.hxx> namespace odb { @@ -214,6 +224,59 @@ namespace odb result<T> query (const odb::query<T>&, bool cache = true); + // Query preparation. + // + template <typename T> + prepared_query<T> + prepare_query (const char* name, const char*); + + template <typename T> + prepared_query<T> + prepare_query (const char* name, const std::string&); + + template <typename T> + prepared_query<T> + prepare_query (const char* name, const odb::query<T>&); + + template <typename T> + void + cache_query (const prepared_query<T>&); + + template <typename T, typename P> + void + cache_query (const prepared_query<T>&, std::auto_ptr<P> params); + +#ifdef ODB_CXX11 + template <typename T, typename P> + void + cache_query (const prepared_query<T>&, std::unique_ptr<P> params); +#endif + + template <typename T> + prepared_query<T> + lookup_query (const char* name) const; + + template <typename T, typename P> + prepared_query<T> + lookup_query (const char* name, P*& params) const; + + // Prepared query factory. + // + public: +#ifdef ODB_CXX11 + typedef + std::function<void (const char*, connection&)> + 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<const char*, query_factory_type, details::c_string_comparator> + 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 <cstring> // std::strlen() +#include <utility> // std::move + +#include <odb/transaction.hxx> 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<T> (odb::query<T> (q), cache); } + template <typename T> + inline prepared_query<T> database:: + prepare_query (const char* n, const char* q) + { + return prepare_query<T> (n, odb::query<T> (q)); + } + + template <typename T> + inline prepared_query<T> database:: + prepare_query (const char* n, const std::string& q) + { + return prepare_query<T> (n, odb::query<T> (q)); + } + + template <typename T> + inline prepared_query<T> database:: + prepare_query (const char* n, const odb::query<T>& q) + { + connection_type& c (transaction::current ().connection ()); + return c.prepare_query (n, q); + } + + template <typename T> + inline void database:: + cache_query (const prepared_query<T>& pq) + { + connection_type& c (transaction::current ().connection ()); + c.cache_query (pq); + } + + template <typename T, typename P> + inline void database:: + cache_query (const prepared_query<T>& pq, std::auto_ptr<P> params) + { + connection_type& c (transaction::current ().connection ()); + c.cache_query (pq, params); + } + +#ifdef ODB_CXX11 + template <typename T, typename P> + inline void database:: + cache_query (const prepared_query<T>& pq, std::unique_ptr<P> params) + { + connection_type& c (transaction::current ().connection ()); + c.cache_query (pq, std::move (params)); + } +#endif + + template <typename T> + inline prepared_query<T> database:: + lookup_query (const char* name) const + { + connection_type& c (transaction::current ().connection ()); + return c.lookup_query<T> (name); + } + + template <typename T, typename P> + inline prepared_query<T> database:: + lookup_query (const char* name, P*& params) const + { + connection_type& c (transaction::current ().connection ()); + return c.lookup_query<T, P> (name, params); + } + // Implementations (i.e., the *_() functions). // template <typename T, database_id DB> 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 <odb/exceptions.hxx> -#include <odb/transaction.hxx> #include <odb/no-op-cache-traits.hxx> #include <odb/pointer-traits.hxx> 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 <odb/pre.hxx> + +#include <cstring> + +#include <odb/details/export.hxx> + +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 <odb/post.hxx> + +#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 <sstream> - #include <odb/exceptions.hxx> 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 <odb/forward.hxx> #include <odb/traits.hxx> #include <odb/result.hxx> +#include <odb/statement.hxx> #include <odb/details/export.hxx> #include <odb/details/shared-ptr.hxx> @@ -22,12 +23,19 @@ namespace odb ~prepared_query_impl (); const char* name; + details::shared_ptr<statement> stmt; details::shared_ptr<result_impl> (*execute) (prepared_query_impl&); }; template <typename T> struct prepared_query { + prepared_query () {} + + explicit + prepared_query (details::shared_ptr<prepared_query_impl> impl) + : impl_ (impl) {} + result<T> execute (bool cache = true) { @@ -46,11 +54,29 @@ namespace odb return r; } - explicit - prepared_query (details::shared_ptr<prepared_query_impl> 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_impl> + prepared_query::*unspecified_bool_type; + operator unspecified_bool_type () const + { + return impl_ ? &prepared_query::impl_ : 0; + } private: + friend class connection; details::shared_ptr<prepared_query_impl> 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 <cassert> + #include <odb/statement.hxx> 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_; }; } |