diff options
Diffstat (limited to 'libodb-oracle/odb/oracle')
86 files changed, 19043 insertions, 0 deletions
diff --git a/libodb-oracle/odb/oracle/auto-descriptor.cxx b/libodb-oracle/odb/oracle/auto-descriptor.cxx new file mode 100644 index 0000000..fa83086 --- /dev/null +++ b/libodb-oracle/odb/oracle/auto-descriptor.cxx @@ -0,0 +1,27 @@ +// file : odb/oracle/auto-descriptor.cxx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <oci.h> + +#include <odb/oracle/auto-descriptor.hxx> + +namespace odb +{ + namespace oracle + { + static const ub4 oci_descriptor_types[] = + { + OCI_DTYPE_PARAM, + OCI_DTYPE_LOB, + OCI_DTYPE_TIMESTAMP, + OCI_DTYPE_INTERVAL_YM, + OCI_DTYPE_INTERVAL_DS + }; + + void + oci_descriptor_free (void* d, descriptor_type type) + { + OCIDescriptorFree (d, oci_descriptor_types[type]); + } + } +} diff --git a/libodb-oracle/odb/oracle/auto-descriptor.hxx b/libodb-oracle/odb/oracle/auto-descriptor.hxx new file mode 100644 index 0000000..b856a2c --- /dev/null +++ b/libodb-oracle/odb/oracle/auto-descriptor.hxx @@ -0,0 +1,127 @@ +// file : odb/oracle/auto-descriptor.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_AUTO_DESCRIPTOR_HXX +#define ODB_ORACLE_AUTO_DESCRIPTOR_HXX + +#include <odb/pre.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/oracle-fwd.hxx> + +#include <odb/oracle/details/export.hxx> + +namespace odb +{ + namespace oracle + { + enum descriptor_type + { + dt_param, + dt_lob, + dt_timestamp, + dt_interval_ym, + dt_interval_ds, + dt_default + }; + + LIBODB_ORACLE_EXPORT void + oci_descriptor_free (void* descriptor, descriptor_type type); + + // + // descriptor_type_traits + // + + template <typename D> + struct default_descriptor_type_traits; + + template <> + struct default_descriptor_type_traits<OCIParam> + { static const descriptor_type dtype = dt_param; }; + + template <> + struct default_descriptor_type_traits<OCILobLocator> + { static const descriptor_type dtype = dt_lob; }; + + // + // auto_descriptor_base + // + + template <typename D, descriptor_type type> + struct auto_descriptor_base + { + static void + release (D* d) + { + oci_descriptor_free (d, type); + } + }; + + template <typename D> + struct auto_descriptor_base<D, dt_default> + { + static void + release (D* d) + { + oci_descriptor_free (d, default_descriptor_type_traits<D>::dtype); + } + }; + + // + // auto_descriptor + // + + template <typename D, descriptor_type type = dt_default> + class auto_descriptor: auto_descriptor_base<D, type> + { + public: + auto_descriptor (D* d = 0) + : d_ (d) + { + } + + ~auto_descriptor () + { + if (d_ != 0) + this->release (d_); + } + + operator D* () const + { + return d_; + } + + D*& + get () + { + return d_; + } + + D* + get () const + { + return d_; + } + + void + reset (D* d = 0) + { + if (d_ != 0) + this->release (d_); + + d_ = d; + } + + private: + auto_descriptor (const auto_descriptor&); + auto_descriptor& operator= (const auto_descriptor&); + + protected: + D* d_; + }; + } +} + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_AUTO_DESCRIPTOR_HXX diff --git a/libodb-oracle/odb/oracle/auto-handle.cxx b/libodb-oracle/odb/oracle/auto-handle.cxx new file mode 100644 index 0000000..55614d4 --- /dev/null +++ b/libodb-oracle/odb/oracle/auto-handle.cxx @@ -0,0 +1,37 @@ +// file : odb/oracle/auto-handle.cxx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <oci.h> + +#include <odb/oracle/auto-handle.hxx> + +namespace odb +{ + namespace oracle + { + void + oci_handle_free (void* h, ub4 t) + { + OCIHandleFree (h, t); + } + + void handle_traits<OCISvcCtx>:: + release (OCISvcCtx* h, OCIError* e) + { + OCISessionRelease (h, e, 0, 0, OCI_DEFAULT); + } + + void handle_traits<OCIStmt>:: + release (OCIStmt* h, ub4 m, OCIError* e) + { + OCIStmtRelease (h, e, 0, 0, m); + } + + const ub4 handle_type_traits<OCIEnv>::htype = OCI_HTYPE_ENV; + const ub4 handle_type_traits<OCIError>::htype = OCI_HTYPE_ERROR; + const ub4 handle_type_traits<OCISvcCtx>::htype = OCI_HTYPE_SVCCTX; + const ub4 handle_type_traits<OCIStmt>::htype = OCI_HTYPE_STMT; + const ub4 handle_type_traits<OCIAuthInfo>::htype = OCI_HTYPE_AUTHINFO; + const ub4 handle_type_traits<OCITrans>::htype = OCI_HTYPE_TRANS; + } +} diff --git a/libodb-oracle/odb/oracle/auto-handle.hxx b/libodb-oracle/odb/oracle/auto-handle.hxx new file mode 100644 index 0000000..4da1cdd --- /dev/null +++ b/libodb-oracle/odb/oracle/auto-handle.hxx @@ -0,0 +1,290 @@ +// file : odb/oracle/auto-handle.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_AUTO_HANDLE_HXX +#define ODB_ORACLE_AUTO_HANDLE_HXX + +#include <odb/pre.hxx> + +#include <odb/details/config.hxx> // ODB_CXX11 + +#include <odb/oracle/version.hxx> +#include <odb/oracle/oracle-fwd.hxx> + +#include <odb/oracle/details/export.hxx> + +namespace odb +{ + namespace oracle + { + // + // handle_type_traits + // + + template <typename H> + struct handle_type_traits; + + template <> + struct LIBODB_ORACLE_EXPORT handle_type_traits<OCIEnv> + { static const ub4 htype; }; + + template <> + struct LIBODB_ORACLE_EXPORT handle_type_traits<OCIError> + { static const ub4 htype; }; + + template <> + struct LIBODB_ORACLE_EXPORT handle_type_traits<OCISvcCtx> + { static const ub4 htype; }; + + template <> + struct LIBODB_ORACLE_EXPORT handle_type_traits<OCIStmt> + { static const ub4 htype; }; + + template <> + struct LIBODB_ORACLE_EXPORT handle_type_traits<OCIAuthInfo> + { static const ub4 htype; }; + + template <> + struct LIBODB_ORACLE_EXPORT handle_type_traits<OCITrans> + { static const ub4 htype; }; + + // + // handle_traits + // + + LIBODB_ORACLE_EXPORT void + oci_handle_free (void* handle, ub4 type); + + template <typename H> + struct handle_traits + { + static void + release (H* h) + { + oci_handle_free (h, handle_type_traits<H>::htype); + } + }; + + template <> + struct LIBODB_ORACLE_EXPORT handle_traits<OCISvcCtx> + { + static void + release (OCISvcCtx*, OCIError*); + }; + + template <> + struct LIBODB_ORACLE_EXPORT handle_traits<OCIStmt> + { + static void + release (OCIStmt*, ub4 release_mode, OCIError*); + }; + + // + // auto_handle + // + + template <typename H> + class auto_handle + { + public: + auto_handle (H* h = 0) + : h_ (h) + { + } + + ~auto_handle () + { + if (h_ != 0) + handle_traits<H>::release (h_); + } + + operator H* () const + { + return h_; + } + + H* + get () const + { + return h_; + } + + H* + release () + { + H* h (h_); + h_ = 0; + return h; + } + + void + reset (H* h = 0) + { + if (h_ != 0) + handle_traits<H>::release (h_); + + h_ = h; + } + +#ifdef ODB_CXX11 + auto_handle (auto_handle&& ah) noexcept: h_ (ah.release ()) {} + auto_handle& operator= (auto_handle&& ah) noexcept + { + if (this != &ah) + reset (ah.release ()); + return *this; + } +#endif + + private: + auto_handle (const auto_handle&); + auto_handle& operator= (const auto_handle&); + + private: + H* h_; + }; + + // + // auto_handle<OCISvcCtx> + // + + template <> + class LIBODB_ORACLE_EXPORT auto_handle<OCISvcCtx> + { + public: + auto_handle () + : h_ (0) + { + } + + auto_handle (OCISvcCtx* h, OCIError* e) + : h_ (h), e_ (e) + { + } + + ~auto_handle () + { + if (h_ != 0) + handle_traits<OCISvcCtx>::release (h_, e_); + } + + operator OCISvcCtx* () const + { + return h_; + } + + OCISvcCtx* + get () const + { + return h_; + } + + OCISvcCtx* + release () + { + OCISvcCtx* h (h_); + h_ = 0; + + return h; + } + + void + reset () + { + if (h_ != 0) + { + handle_traits<OCISvcCtx>::release (h_, e_); + h_ = 0; + } + } + + void + reset (OCISvcCtx* h, OCIError* e) + { + if (h_ != 0) + handle_traits<OCISvcCtx>::release (h_, e_); + + h_ = h; + e_ = e; + } + + private: + auto_handle (const auto_handle&); + auto_handle& operator= (const auto_handle&); + + private: + OCISvcCtx* h_; + OCIError* e_; + }; + + // + // auto_handle<OCIStmt> + // + + template <> + class LIBODB_ORACLE_EXPORT auto_handle<OCIStmt> + { + public: + auto_handle () + : h_ (0) + { + } + + auto_handle (OCIStmt* h, ub4 release_mode, OCIError* e) + : h_ (h), release_mode_ (release_mode), e_ (e) + { + } + + ~auto_handle () + { + if (h_ != 0) + handle_traits<OCIStmt>::release (h_, release_mode_, e_); + } + + operator OCIStmt* () const + { + return h_; + } + + OCIStmt* + get () const + { + return h_; + } + + void + reset () + { + if (h_ != 0) + { + handle_traits<OCIStmt>::release (h_, release_mode_, e_); + h_ = 0; + } + } + + void + reset (OCIStmt* h, ub4 release_mode, OCIError* e) + { + if (h_ != 0) + handle_traits<OCIStmt>::release (h_, release_mode_, e_); + + h_ = h; + release_mode_ = release_mode; + e_ = e; + } + + private: + auto_handle (const auto_handle&); + auto_handle& operator= (const auto_handle&); + + private: + OCIStmt* h_; + ub4 release_mode_; + OCIError* e_; + }; + } +} + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_AUTO_HANDLE_HXX diff --git a/libodb-oracle/odb/oracle/binding.hxx b/libodb-oracle/odb/oracle/binding.hxx new file mode 100644 index 0000000..0b3c1d9 --- /dev/null +++ b/libodb-oracle/odb/oracle/binding.hxx @@ -0,0 +1,66 @@ +// file : odb/oracle/binding.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_BINDING_HXX +#define ODB_ORACLE_BINDING_HXX + +#include <odb/pre.hxx> + +#include <cstddef> // std::size_t + +#include <odb/oracle/version.hxx> +#include <odb/oracle/oracle-types.hxx> +#include <odb/oracle/auto-handle.hxx> + +#include <odb/oracle/details/export.hxx> + +namespace odb +{ + namespace oracle + { + class LIBODB_ORACLE_EXPORT binding + { + public: + typedef oracle::bind bind_type; + typedef oracle::change_callback change_callback_type; + + binding () + : bind (0), count (0), version (0), + batch (0), skip (0), status (0), + change_callback (0) {} + + binding (bind_type* b, std::size_t n) + : bind (b), count (n), version (0), + batch (1), skip (0), status (0), + change_callback (0) + { + } + + binding (bind_type* b, std::size_t n, + std::size_t bt, std::size_t s, sb4* st) + : bind (b), count (n), version (0), + batch (bt), skip (s), status (st), + change_callback (0) + { + } + + bind_type* bind; + std::size_t count; + std::size_t version; + + std::size_t batch; + std::size_t skip; + sb4* status; // Batch status array. + + change_callback_type* change_callback; + + private: + binding (const binding&); + binding& operator= (const binding&); + }; + } +} + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_BINDING_HXX diff --git a/libodb-oracle/odb/oracle/buildfile b/libodb-oracle/odb/oracle/buildfile new file mode 100644 index 0000000..8f6716a --- /dev/null +++ b/libodb-oracle/odb/oracle/buildfile @@ -0,0 +1,138 @@ +# file : odb/oracle/buildfile +# license : ODB NCUEL; see accompanying LICENSE file + +define cli: file +cli{*}: extension = cli + +import int_libs = libodb%lib{odb} + +# Note: to build with MinGW rename oci.lib to liboci.dll.a. +# +if ($cc.target.class != 'windows') + import imp_libs = liboci%lib{clntsh} +else + import imp_libs = liboci%lib{oci} + +lib{odb-oracle}: {hxx ixx txx cxx}{* -version} {hxx}{version} \ + details/{hxx ixx txx cxx}{* -options} \ + $imp_libs $int_libs + +# Include the generated version header into the distribution (so that we don't +# pick up an installed one) and don't remove it when cleaning in src (so that +# clean results in a state identical to distributed). +# +hxx{version}: in{version} $src_root/manifest +hxx{version}: +{ + dist = true + clean = ($src_root != $out_root) +} + +# Build options. +# +cxx.poptions =+ "-I$out_root" "-I$src_root" + +obja{*}: cxx.poptions += -DLIBODB_ORACLE_STATIC_BUILD +objs{*}: cxx.poptions += -DLIBODB_ORACLE_SHARED_BUILD + +# Export options. +# +lib{odb-oracle}: +{ + cxx.export.poptions = "-I$out_root" "-I$src_root" + cxx.export.libs = $int_libs +} + +liba{odb-oracle}: cxx.export.poptions += -DLIBODB_ORACLE_STATIC +libs{odb-oracle}: cxx.export.poptions += -DLIBODB_ORACLE_SHARED + +# For pre-releases use the complete version to make sure they cannot be used +# in place of another pre-release or the final version. See the version module +# for details on the version.* variable values. +# +if $version.pre_release + lib{odb-oracle}: bin.lib.version = @"-$version.project_id" +else + lib{odb-oracle}: bin.lib.version = @"-$version.major.$version.minor" + +develop = $config.libodb_oracle.develop + +## Consumption build ($develop == false). +# + +# Use pregenerated versions in the consumption build. +# +lib{odb-oracle}: details/pregenerated/{hxx ixx cxx}{**}: include = (!$develop) + +if! $develop + cxx.poptions =+ "-I($src_base/details/pregenerated)" # Note: must come first. + +# Don't install pregenerated headers since they are only used internally in +# the database implementation (also below). +# +details/pregenerated/{hxx ixx}{*}: install = false + +# Distribute pregenerated versions only in the consumption build. +# +details/pregenerated/{hxx ixx cxx}{*}: dist = (!$develop) + +# +## + +## Development build ($develop == true). +# + +lib{odb-oracle}: details/{hxx ixx cxx}{options}: include = $develop + +if $develop + import! [metadata] cli = cli%exe{cli} + +# In the development build distribute regenerated {hxx ixx cxx}{options}, +# remapping their locations to the paths of the pregenerated versions (which +# are only distributed in the consumption build; see above). This way we make +# sure that the distributed files are always up-to-date. +# +<details/{hxx ixx cxx}{options}>: details/cli{options} $cli +{ + install = false + dist = ($develop ? pregenerated/odb/oracle/details/ : false) + + # Symlink the generated code in src for convenience of development. + # + backlink = true +} +% +if $develop +{{ + options = --include-with-brackets --include-prefix odb/oracle/details \ + --guard-prefix LIBODB_ORACLE_DETAILS --generate-file-scanner \ + --cli-namespace odb::oracle::details::cli --long-usage \ + --generate-specifier --no-combined-flags + + $cli $options -o $out_base/details/ $path($<[0]) + + # If the result differs from the pregenerated version, copy it over. + # + d = [dir_path] $src_base/details/pregenerated/odb/oracle/details/ + + if diff $d/options.hxx $path($>[0]) >- && \ + diff $d/options.ixx $path($>[1]) >- && \ + diff $d/options.cxx $path($>[2]) >- + exit + end + + cp $path($>[0]) $d/options.hxx + cp $path($>[1]) $d/options.ixx + cp $path($>[2]) $d/options.cxx +}} + +# Install into the odb/oracle/ subdirectory of, say, /usr/include/ +# recreating subdirectories. +# +install_include = [dir_path] include/odb/oracle/ + +{hxx ixx txx}{*}: +{ + install = $install_include + install.subdirs = true +} diff --git a/libodb-oracle/odb/oracle/connection-factory.cxx b/libodb-oracle/odb/oracle/connection-factory.cxx new file mode 100644 index 0000000..9ad9474 --- /dev/null +++ b/libodb-oracle/odb/oracle/connection-factory.cxx @@ -0,0 +1,159 @@ +// file : odb/oracle/connection-factory.cxx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <odb/oracle/connection-factory.hxx> +#include <odb/oracle/exceptions.hxx> + +#include <odb/details/lock.hxx> + +using namespace std; + +namespace odb +{ + using namespace details; + + namespace oracle + { + // new_connection_factory + // + connection_ptr new_connection_factory:: + connect () + { + return connection_ptr (new (shared) connection (*this)); + } + + // connection_pool_factory + // + connection_pool_factory::pooled_connection_ptr connection_pool_factory:: + create () + { + return pooled_connection_ptr (new (shared) pooled_connection (*this)); + } + + connection_pool_factory:: + ~connection_pool_factory () + { + // Wait for all the connections currently in use to return to + // the pool. + // + lock l (mutex_); + while (in_use_ != 0) + { + waiters_++; + cond_.wait (l); + waiters_--; + } + } + + connection_ptr connection_pool_factory:: + connect () + { + lock l (mutex_); + + while (true) + { + // See if we have a spare connection. + // + if (connections_.size () != 0) + { + shared_ptr<pooled_connection> c (connections_.back ()); + connections_.pop_back (); + + c->callback_ = &c->cb_; + in_use_++; + return c; + } + + // See if we can create a new one. + // + if (max_ == 0 || in_use_ < max_) + { + shared_ptr<pooled_connection> c (create ()); + c->callback_ = &c->cb_; + in_use_++; + return c; + } + + // Wait until someone releases a connection. + // + waiters_++; + cond_.wait (l); + waiters_--; + } + } + + void connection_pool_factory:: + database (database_type& db) + { + bool first (db_ == 0); + + connection_factory::database (db); + + if (!first) + return; + + if (min_ > 0) + { + connections_.reserve (min_); + + for (size_t i (0); i < min_; ++i) + connections_.push_back (create ()); + } + } + + bool connection_pool_factory:: + release (pooled_connection* c) + { + c->callback_ = 0; + + lock l (mutex_); + + // Determine if we need to keep or free this connection. + // + bool keep (!c->failed () && + (waiters_ != 0 || + min_ == 0 || + (connections_.size () + in_use_ <= min_))); + + in_use_--; + + if (keep) + { + connections_.push_back (pooled_connection_ptr (inc_ref (c))); + connections_.back ()->recycle (); + } + + if (waiters_ != 0) + cond_.signal (); + + return !keep; + } + + // + // connection_pool_factory::pooled_connection + // + + connection_pool_factory::pooled_connection:: + pooled_connection (connection_pool_factory& f) + : connection (f) + { + cb_.arg = this; + cb_.zero_counter = &zero_counter; + } + + connection_pool_factory::pooled_connection:: + pooled_connection (connection_pool_factory& f, OCISvcCtx* handle) + : connection (f, handle) + { + cb_.arg = this; + cb_.zero_counter = &zero_counter; + } + + bool connection_pool_factory::pooled_connection:: + zero_counter (void* arg) + { + pooled_connection* c (static_cast<pooled_connection*> (arg)); + return static_cast<connection_pool_factory&> (c->factory_).release (c); + } + } +} diff --git a/libodb-oracle/odb/oracle/connection-factory.hxx b/libodb-oracle/odb/oracle/connection-factory.hxx new file mode 100644 index 0000000..835759e --- /dev/null +++ b/libodb-oracle/odb/oracle/connection-factory.hxx @@ -0,0 +1,134 @@ +// file : odb/oracle/connection-factory.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_CONNECTION_FACTORY_HXX +#define ODB_ORACLE_CONNECTION_FACTORY_HXX + +#include <odb/pre.hxx> + +#include <vector> +#include <cstddef> // std::size_t +#include <cassert> + +#include <odb/details/mutex.hxx> +#include <odb/details/condition.hxx> +#include <odb/details/shared-ptr.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/forward.hxx> +#include <odb/oracle/connection.hxx> + +#include <odb/oracle/details/export.hxx> + +namespace odb +{ + namespace oracle + { + class LIBODB_ORACLE_EXPORT new_connection_factory: public connection_factory + { + public: + new_connection_factory () {} + + virtual connection_ptr + connect (); + + private: + new_connection_factory (const new_connection_factory&); + new_connection_factory& operator= (const new_connection_factory&); + }; + + class LIBODB_ORACLE_EXPORT connection_pool_factory: + public connection_factory + { + public: + // The max_connections argument specifies the maximum number of + // concurrent connections this pool will maintain. If this value + // is 0 then the pool will create a new connection every time all + // of the existing connections are in use. + // + // The min_connections argument specifies the minimum number of + // connections that should be maintained by the pool. If the + // number of connections maintained by the pool exceeds this + // number and there are no active waiters for a new connection, + // then the pool will release the excess connections. If this + // value is 0 then the pool will maintain all the connections + // that were ever created. + // + connection_pool_factory (std::size_t max_connections = 0, + std::size_t min_connections = 0) + : max_ (max_connections), + min_ (min_connections), + in_use_ (0), + waiters_ (0), + cond_ (mutex_) + { + // max_connections == 0 means unlimited. + // + assert (max_connections == 0 || max_connections >= min_connections); + } + + virtual connection_ptr + connect (); + + virtual void + database (database_type&); + + virtual + ~connection_pool_factory (); + + private: + connection_pool_factory (const connection_pool_factory&); + connection_pool_factory& operator= (const connection_pool_factory&); + + protected: + class LIBODB_ORACLE_EXPORT pooled_connection: public connection + { + public: + pooled_connection (connection_pool_factory&); + pooled_connection (connection_pool_factory&, OCISvcCtx*); + + private: + static bool + zero_counter (void*); + + private: + friend class connection_pool_factory; + + shared_base::refcount_callback cb_; + }; + + friend class pooled_connection; + + typedef details::shared_ptr<pooled_connection> pooled_connection_ptr; + typedef std::vector<pooled_connection_ptr> connections; + + // This function is called whenever the pool needs to create a new + // connection. + // + virtual pooled_connection_ptr + create (); + + protected: + // Return true if the connection should be deleted, false otherwise. + // + bool + release (pooled_connection*); + + protected: + const std::size_t max_; + const std::size_t min_; + + std::size_t in_use_; // Number of connections currently in use. + std::size_t waiters_; // Number of threads waiting for a connection. + + connections connections_; + + details::mutex mutex_; + details::condition cond_; + }; + } +} + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_CONNECTION_FACTORY_HXX diff --git a/libodb-oracle/odb/oracle/connection.cxx b/libodb-oracle/odb/oracle/connection.cxx new file mode 100644 index 0000000..5c2f7a7 --- /dev/null +++ b/libodb-oracle/odb/oracle/connection.cxx @@ -0,0 +1,175 @@ +// file : odb/oracle/connection.cxx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <oci.h> + +#include <string> + +#include <odb/oracle/database.hxx> +#include <odb/oracle/connection.hxx> +#include <odb/oracle/transaction.hxx> +#include <odb/oracle/statement.hxx> +#include <odb/oracle/error.hxx> +#include <odb/oracle/exceptions.hxx> +#include <odb/oracle/auto-descriptor.hxx> +#include <odb/oracle/statement-cache.hxx> + +using namespace std; + +namespace odb +{ + namespace oracle + { + connection:: + connection (connection_factory& cf) + : odb::connection (cf), + failed_ (false), + statement_cache_ (new statement_cache_type (*this)), + lob_buffer_ (0) + { + sword r (0); + + database_type& db (database ()); + + { + OCIError* e (0); + r = OCIHandleAlloc (db.environment (), + reinterpret_cast<void**> (&e), + OCI_HTYPE_ERROR, + 0, + 0); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + error_.reset (e); + } + + auto_handle<OCIAuthInfo> auth_info; + { + OCIAuthInfo* a (0); + r = OCIHandleAlloc (db.environment (), + reinterpret_cast<void**> (&a), + OCI_HTYPE_AUTHINFO, + 0, + 0); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + auth_info.reset (a); + } + + r = OCIAttrSet ( + auth_info, + OCI_HTYPE_AUTHINFO, + reinterpret_cast<OraText*> (const_cast<char*> (db.user ().c_str ())), + static_cast <ub4> (db.user ().size ()), + OCI_ATTR_USERNAME, + error_); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (error_, r); + + r = OCIAttrSet ( + auth_info, + OCI_HTYPE_AUTHINFO, + reinterpret_cast<OraText*> ( + const_cast<char*> (db.password ().c_str ())), + static_cast<ub4> (db.password ().size ()), + OCI_ATTR_PASSWORD, + error_); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (error_, r); + + { + OCISvcCtx* s (0); + + r = OCISessionGet ( + db.environment (), + error_, + &s, + auth_info, + reinterpret_cast<OraText*> (const_cast<char*> (db.db ().c_str ())), + static_cast<ub4> (db.db ().size ()), + 0, + 0, + 0, + 0, + 0, + OCI_SESSGET_STMTCACHE); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (error_, r); + + handle_.reset (s, error_); + } + } + + connection:: + connection (connection_factory& cf, OCISvcCtx* handle) + : odb::connection (cf), + failed_ (false), + statement_cache_ (new statement_cache_type (*this)), + lob_buffer_ (0) + { + sword r (0); + + database_type& db (database ()); + + { + OCIError* e (0); + r = OCIHandleAlloc (db.environment (), + reinterpret_cast<void**> (&e), + OCI_HTYPE_ERROR, + 0, + 0); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + error_.reset (e); + } + + handle_.reset (handle, error_); + } + + connection:: + ~connection () + { + // Deallocate prepared statements before we close the connection. + // + recycle (); + clear_prepared_map (); + statement_cache_.reset (); + } + + transaction_impl* connection:: + begin () + { + return new transaction_impl (connection_ptr (inc_ref (this))); + } + + unsigned long long connection:: + execute (const char* s, std::size_t n) + { + generic_statement st (*this, string (s, n)); + return st.execute (); + } + + // connection_factory + // + connection_factory:: + ~connection_factory () + { + } + + void connection_factory:: + database (database_type& db) + { + odb::connection_factory::db_ = &db; + db_ = &db; + } + } +} diff --git a/libodb-oracle/odb/oracle/connection.hxx b/libodb-oracle/odb/oracle/connection.hxx new file mode 100644 index 0000000..4192728 --- /dev/null +++ b/libodb-oracle/odb/oracle/connection.hxx @@ -0,0 +1,189 @@ +// file : odb/oracle/connection.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_CONNECTION_HXX +#define ODB_ORACLE_CONNECTION_HXX + +#include <odb/pre.hxx> + +#include <odb/connection.hxx> + +#include <odb/details/buffer.hxx> +#include <odb/details/shared-ptr.hxx> +#include <odb/details/unique-ptr.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/forward.hxx> +#include <odb/oracle/query.hxx> +#include <odb/oracle/tracer.hxx> +#include <odb/oracle/transaction-impl.hxx> +#include <odb/oracle/auto-handle.hxx> +#include <odb/oracle/oracle-fwd.hxx> + +#include <odb/oracle/details/export.hxx> + +namespace odb +{ + namespace oracle + { + class statement_cache; + class connection_factory; + + class connection; + typedef details::shared_ptr<connection> connection_ptr; + + class LIBODB_ORACLE_EXPORT connection: public odb::connection + { + public: + typedef oracle::statement_cache statement_cache_type; + typedef oracle::database database_type; + + virtual + ~connection (); + + connection (connection_factory&); + connection (connection_factory&, OCISvcCtx* handle); + + database_type& + database (); + + public: + virtual transaction_impl* + begin (); + + public: + using odb::connection::execute; + + virtual unsigned long long + execute (const char* statement, std::size_t length); + + // Query preparation. + // + public: + 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 oracle::query_base&); + + template <typename T> + prepared_query<T> + prepare_query (const char* name, const odb::query_base&); + + // SQL statement tracing. + // + public: + typedef oracle::tracer tracer_type; + + void + tracer (tracer_type& t) + { + odb::connection::tracer (t); + } + + void + tracer (tracer_type* t) + { + odb::connection::tracer (t); + } + + using odb::connection::tracer; + + public: + bool + failed () const + { + return failed_; + } + + void + mark_failed () + { + failed_ = true; + } + + public: + OCISvcCtx* + handle () + { + return handle_; + } + + OCIError* + error_handle () + { + return error_; + } + + statement_cache_type& + statement_cache () + { + return *statement_cache_; + } + + details::buffer& + lob_buffer () + { + return lob_buffer_; + } + + private: + connection (const connection&); + connection& operator= (const connection&); + + private: + friend class transaction_impl; // invalidate_results() + + private: + // It is important that the error_ member is declared before the + // handle_ member as handle_ depends on error_ during destruction. + // + auto_handle<OCIError> error_; + + auto_handle<OCISvcCtx> handle_; + bool failed_; + + details::unique_ptr<statement_cache_type> statement_cache_; + details::buffer lob_buffer_; + }; + + class LIBODB_ORACLE_EXPORT connection_factory: + public odb::connection_factory + { + public: + typedef oracle::database database_type; + + virtual void + database (database_type&); + + database_type& + database () {return *db_;} + + virtual connection_ptr + connect () = 0; + + virtual + ~connection_factory (); + + connection_factory (): db_ (0) {} + + // Needed to break the circular connection_factory-database dependency + // (odb::connection_factory has the odb::database member). + // + protected: + database_type* db_; + }; + } +} + +#include <odb/oracle/connection.ixx> + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_CONNECTION_HXX diff --git a/libodb-oracle/odb/oracle/connection.ixx b/libodb-oracle/odb/oracle/connection.ixx new file mode 100644 index 0000000..5abaddc --- /dev/null +++ b/libodb-oracle/odb/oracle/connection.ixx @@ -0,0 +1,44 @@ +// file : odb/oracle/connection.ixx +// license : ODB NCUEL; see accompanying LICENSE file + +namespace odb +{ + namespace oracle + { + inline database& connection:: + database () + { + return static_cast<connection_factory&> (factory_).database (); + } + + template <typename T> + inline prepared_query<T> connection:: + prepare_query (const char* n, const char* q) + { + return prepare_query<T> (n, query<T> (q)); + } + + template <typename T> + inline prepared_query<T> connection:: + prepare_query (const char* n, const std::string& q) + { + return prepare_query<T> (n, query<T> (q)); + } + + template <typename T> + inline prepared_query<T> connection:: + prepare_query (const char* n, const oracle::query_base& q) + { + return query_<T, id_oracle>::call (*this, n, q); + } + + template <typename T> + inline prepared_query<T> connection:: + prepare_query (const char* n, const odb::query_base& q) + { + // Translate to native query. + // + return prepare_query<T> (n, oracle::query_base (q)); + } + } +} diff --git a/libodb-oracle/odb/oracle/container-statements.hxx b/libodb-oracle/odb/oracle/container-statements.hxx new file mode 100644 index 0000000..23e1564 --- /dev/null +++ b/libodb-oracle/odb/oracle/container-statements.hxx @@ -0,0 +1,345 @@ +// file : odb/oracle/container-statements.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_CONTAINER_STATEMENTS_HXX +#define ODB_ORACLE_CONTAINER_STATEMENTS_HXX + +#include <odb/pre.hxx> + +#include <cstddef> // std::size_t + +#include <odb/forward.hxx> +#include <odb/schema-version.hxx> +#include <odb/traits.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/oracle-types.hxx> +#include <odb/oracle/statement.hxx> + +#include <odb/oracle/details/export.hxx> + +namespace odb +{ + namespace oracle + { + class connection; + + // Template argument is the generated abstract container traits type. + // That is, it doesn't need to provide column counts and statements. + // + template <typename T> + class container_statements + { + public: + typedef T traits; + + typedef typename traits::data_image_type data_image_type; + typedef typename traits::functions_type functions_type; + + typedef oracle::insert_statement insert_statement_type; + typedef oracle::select_statement select_statement_type; + typedef oracle::delete_statement delete_statement_type; + + typedef oracle::connection connection_type; + + container_statements (connection_type&, binding& id_binding); + + connection_type& + connection () + { + return conn_; + } + + // Functions. + // + functions_type& + functions () + { + return functions_; + } + + // Schema version. + // + const schema_version_migration& + version_migration () const {return *svm_;} + + void + version_migration (const schema_version_migration& svm) {svm_ = &svm;} + + // Id image binding (external). + // + const binding& + id_binding () + { + return id_binding_; + } + + // Data image. The image is split into the id (that comes as a + // binding) and index/key plus value which are in data_image_type. + // The select binding is a subset of the full binding (no id). + // + data_image_type& + data_image () + { + return data_image_; + } + + bind* + data_bind () + { + return insert_image_binding_.bind; + } + + bool + data_binding_test_version () const + { + return data_id_binding_version_ != id_binding_.version || + data_image_version_ != data_image_.version || + insert_image_binding_.version == 0; + } + + void + data_binding_update_version () + { + data_id_binding_version_ = id_binding_.version; + data_image_version_ = data_image_.version; + insert_image_binding_.version++; + select_image_binding_.version++; + } + + // + // Statements. + // + + insert_statement_type& + insert_statement () + { + if (insert_ == 0) + insert_.reset ( + new (details::shared) insert_statement_type ( + conn_, + insert_text_, + versioned_, // Process if versioned. + insert_image_binding_, + 0)); + + return *insert_; + } + + select_statement_type& + select_statement () + { + if (select_ == 0) + select_.reset ( + new (details::shared) select_statement_type ( + conn_, + select_text_, + versioned_, // Process if versioned. + false, // Don't optimize. + id_binding_, + select_image_binding_, + 4096)); // Hardcode a 4kB LOB prefetch size. + + return *select_; + } + + delete_statement_type& + delete_statement () + { + if (delete_ == 0) + delete_.reset ( + new (details::shared) delete_statement_type ( + conn_, delete_text_, id_binding_)); + + return *delete_; + } + + private: + container_statements (const container_statements&); + container_statements& operator= (const container_statements&); + + protected: + connection_type& conn_; + binding& id_binding_; + + functions_type functions_; + + data_image_type data_image_; + std::size_t data_image_version_; + std::size_t data_id_binding_version_; + + binding insert_image_binding_; + binding select_image_binding_; + + const char* insert_text_; + const char* select_text_; + const char* delete_text_; + + bool versioned_; + const schema_version_migration* svm_; + + details::shared_ptr<insert_statement_type> insert_; + details::shared_ptr<select_statement_type> select_; + details::shared_ptr<delete_statement_type> delete_; + }; + + template <typename T> + class smart_container_statements: public container_statements<T> + { + public: + typedef T traits; + typedef typename traits::cond_image_type cond_image_type; + + typedef oracle::update_statement update_statement_type; + typedef oracle::delete_statement delete_statement_type; + + typedef oracle::connection connection_type; + + smart_container_statements (connection_type&, binding& id_binding); + + // Condition image. The image is split into the id (that comes as + // a binding) and index/key/value which is in cond_image_type. + // + cond_image_type& + cond_image () + { + return cond_image_; + } + + bind* + cond_bind () + { + return cond_image_binding_.bind; + } + + bool + cond_binding_test_version () const + { + return cond_id_binding_version_ != this->id_binding_.version || + cond_image_version_ != cond_image_.version || + cond_image_binding_.version == 0; + } + + void + cond_binding_update_version () + { + cond_id_binding_version_ = this->id_binding_.version; + cond_image_version_ = cond_image_.version; + cond_image_binding_.version++; + } + + // Update image. The image is split as follows: value comes + // from the data image, id comes as binding, and index/key + // comes from the condition image. + // + bind* + update_bind () + { + return update_image_binding_.bind; + } + + bool + update_binding_test_version () const + { + return update_id_binding_version_ != this->id_binding_.version || + update_cond_image_version_ != cond_image_.version || + update_data_image_version_ != this->data_image_.version || + update_image_binding_.version == 0; + } + + void + update_binding_update_version () + { + update_id_binding_version_ = this->id_binding_.version; + update_cond_image_version_ = cond_image_.version; + update_data_image_version_ = this->data_image_.version; + update_image_binding_.version++; + } + + // + // Statements. + // + + delete_statement_type& + delete_statement () + { + if (this->delete_ == 0) + this->delete_.reset ( + new (details::shared) delete_statement_type ( + this->conn_, this->delete_text_, this->cond_image_binding_)); + + return *this->delete_; + } + + update_statement_type& + update_statement () + { + if (update_ == 0) + update_.reset ( + new (details::shared) update_statement_type ( + this->conn_, + update_text_, + this->versioned_, // Process if versioned. + update_image_binding_)); + + return *update_; + } + + protected: + cond_image_type cond_image_; + std::size_t cond_image_version_; + std::size_t cond_id_binding_version_; + binding cond_image_binding_; + + std::size_t update_id_binding_version_; + std::size_t update_cond_image_version_; + std::size_t update_data_image_version_; + binding update_image_binding_; + + const char* update_text_; + + details::shared_ptr<update_statement_type> update_; + }; + + // Template argument is the generated concrete container traits type. + // + template <typename T> + class container_statements_impl: public T::statements_type + { + public: + typedef T traits; + typedef typename T::statements_type base; + typedef oracle::connection connection_type; + + container_statements_impl (connection_type&, binding&); + + private: + container_statements_impl (const container_statements_impl&); + container_statements_impl& operator= (const container_statements_impl&); + + private: + bind data_image_bind_[traits::data_column_count]; + }; + + template <typename T> + class smart_container_statements_impl: public container_statements_impl<T> + { + public: + typedef T traits; + typedef oracle::connection connection_type; + + smart_container_statements_impl (connection_type&, binding&); + + private: + bind cond_image_bind_[traits::cond_column_count]; + bind update_image_bind_[traits::value_column_count + + traits::cond_column_count]; + }; + } +} + +#include <odb/oracle/container-statements.txx> + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_CONTAINER_STATEMENTS_HXX diff --git a/libodb-oracle/odb/oracle/container-statements.txx b/libodb-oracle/odb/oracle/container-statements.txx new file mode 100644 index 0000000..6a6c53b --- /dev/null +++ b/libodb-oracle/odb/oracle/container-statements.txx @@ -0,0 +1,96 @@ +// file : odb/oracle/container-statements.txx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <cstddef> // std::size_t +#include <cstring> // std::memset + +namespace odb +{ + namespace oracle + { + // container_statements + // + template <typename T> + container_statements<T>:: + container_statements (connection_type& conn, binding& id) + : conn_ (conn), + id_binding_ (id), + functions_ (this), + insert_image_binding_ (0, 0), // Initialized by impl. + select_image_binding_ (0, 0), // Initialized by impl. + svm_ (0) + { + functions_.insert_ = &traits::insert; + functions_.select_ = &traits::select; + functions_.delete__ = &traits::delete_; + + data_image_.version = 0; + data_image_version_ = 0; + data_id_binding_version_ = 0; + } + + // smart_container_statements + // + template <typename T> + smart_container_statements<T>:: + smart_container_statements (connection_type& conn, binding& id) + : container_statements<T> (conn, id), + cond_image_binding_ (0, 0), // Initialized by impl. + update_image_binding_ (0, 0) // Initialized by impl. + { + this->functions_.update_ = &traits::update; + + cond_image_.version = 0; + cond_image_version_ = 0; + cond_id_binding_version_ = 0; + + update_id_binding_version_ = 0; + update_cond_image_version_ = 0; + update_data_image_version_ = 0; + } + + // container_statements_impl + // + template <typename T> + container_statements_impl<T>:: + container_statements_impl (connection_type& conn, binding& id) + : base (conn, id) + { + this->insert_image_binding_.bind = data_image_bind_; + this->insert_image_binding_.count = traits::data_column_count; + + this->select_image_binding_.bind = data_image_bind_ + + traits::id_column_count; + this->select_image_binding_.count = traits::data_column_count - + traits::id_column_count; + + std::memset (data_image_bind_, 0, sizeof (data_image_bind_)); + + this->insert_text_ = traits::insert_statement; + this->select_text_ = traits::select_statement; + this->delete_text_ = traits::delete_statement; + + this->versioned_ = traits::versioned; + } + + // smart_container_statements_impl + // + template <typename T> + smart_container_statements_impl<T>:: + smart_container_statements_impl (connection_type& conn, binding& id) + : container_statements_impl<T> (conn, id) + { + this->cond_image_binding_.bind = cond_image_bind_; + this->cond_image_binding_.count = traits::cond_column_count; + + this->update_image_binding_.bind = update_image_bind_; + this->update_image_binding_.count = traits::value_column_count + + traits::cond_column_count; + + std::memset (cond_image_bind_, 0, sizeof (cond_image_bind_)); + std::memset (update_image_bind_, 0, sizeof (update_image_bind_)); + + this->update_text_ = traits::update_statement; + } + } +} diff --git a/libodb-oracle/odb/oracle/database.cxx b/libodb-oracle/odb/oracle/database.cxx new file mode 100644 index 0000000..3b720be --- /dev/null +++ b/libodb-oracle/odb/oracle/database.cxx @@ -0,0 +1,352 @@ +// file : odb/oracle/database.cxx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <oci.h> + +#include <sstream> + +#include <odb/oracle/traits.hxx> +#include <odb/oracle/database.hxx> +#include <odb/oracle/statement.hxx> +#include <odb/oracle/transaction.hxx> +#include <odb/oracle/exceptions.hxx> +#include <odb/oracle/error.hxx> + +#include <odb/oracle/details/options.hxx> + +using namespace std; + +namespace odb +{ + namespace oracle + { + using odb::details::transfer_ptr; + + database:: + database (const string& user, + const string& password, + const string& db, + ub2 charset, + ub2 ncharset, + OCIEnv* environment, + transfer_ptr<connection_factory> factory) + : odb::database (id_oracle), + user_ (user), + password_ (password), + db_ (db), + port_ (0), + charset_ (charset), + ncharset_ (ncharset), + environment_ (environment), + factory_ (factory.transfer ()) + { + if (environment_ == 0) + { + sword s (OCIEnvNlsCreate (&environment_, + OCI_THREADED, + 0, 0, 0, 0, 0, 0, + charset, + ncharset)); + + if (s == OCI_ERROR) + translate_error (environment_); + + auto_environment_.reset (environment_); + } + + if (!factory_) + factory_.reset (new connection_pool_factory ()); + + factory_->database (*this); + } + + database:: + database (const string& user, + const string& password, + const string& service, + const string& host, + unsigned int port, + ub2 charset, + ub2 ncharset, + OCIEnv* environment, + transfer_ptr<connection_factory> factory) + : odb::database (id_oracle), + user_ (user), + password_ (password), + service_ (service), + host_ (host), + port_ (port), + charset_ (charset), + ncharset_ (ncharset), + environment_ (environment), + factory_ (factory.transfer ()) + { + if (environment_ == 0) + { + sword s (OCIEnvNlsCreate (&environment_, + OCI_THREADED, + 0, 0, 0, 0, 0, 0, + charset, + ncharset)); + + if (s == OCI_ERROR) + translate_error (environment_); + + auto_environment_.reset (environment_); + } + + ostringstream ss; + + if (!host.empty ()) + { + ss << "//" << host_; + + if (port != 0) + ss << ":" << port; + } + + if (!service_.empty ()) + { + if (!host.empty ()) + ss << "/" << service_; + else + ss << service_; + } + + db_ = ss.str (); + + if (!factory_) + factory_.reset (new connection_pool_factory ()); + + factory_->database (*this); + } + + database:: + database (int& argc, + char* argv[], + bool erase, + ub2 charset, + ub2 ncharset, + OCIEnv* environment, + transfer_ptr<connection_factory> factory) + : odb::database (id_oracle), + port_ (0), + charset_ (charset), + ncharset_ (ncharset), + environment_ (environment), + factory_ (factory.transfer ()) + { + if (environment_ == 0) + { + sword s (OCIEnvNlsCreate (&environment_, + OCI_THREADED, + 0, 0, 0, 0, 0, 0, + charset, + ncharset)); + + if (s == OCI_ERROR) + translate_error (environment_); + + auto_environment_.reset (environment_); + } + + using namespace details; + + try + { + cli::argv_file_scanner scan (argc, argv, "--options-file", erase); + options ops (scan, cli::unknown_mode::skip, cli::unknown_mode::skip); + + if (ops.user_specified ()) + user_ = ops.user (); + + if (ops.password_specified ()) + password_ = ops.password (); + + if (ops.database_specified ()) + { + if (ops.host_specified () || + ops.port_specified () || + ops.service_specified ()) + + throw cli_exception ("--host, --port, or --service options " + "cannot be specified together with " + "--database option"); + db_ = ops.database (); + } + else + { + bool host_present (false); + ostringstream oss; + + if (ops.host_specified () && !ops.host ().empty ()) + { + host_present = true; + + host_ = ops.host (); + oss << "//" << host_; + + if (ops.port_specified ()) + { + port_ = ops.port (); + + if (port_ != 0) + oss << ":" << port_; + } + } + + if (ops.service_specified () && !ops.service ().empty ()) + { + service_ = ops.service (); + + if (host_present) + oss << "/" << service_; + else + oss << service_; + } + + db_ = oss.str (); + } + } + catch (const cli::exception& e) + { + ostringstream oss; + oss << e; + throw cli_exception (oss.str ()); + } + + if (!factory_) + factory_.reset (new connection_pool_factory ()); + + factory_->database (*this); + } + + void database:: + print_usage (ostream& os) + { + details::options::print_usage (os); + } + + database:: + ~database () + { + } + + transaction_impl* database:: + begin () + { + return new transaction_impl (*this); + } + + odb::connection* database:: + connection_ () + { + connection_ptr c (factory_->connect ()); + return c.release (); + } + + const database::schema_version_info& database:: + load_schema_version (const string& name) const + { + schema_version_info& svi (schema_version_map_[name]); + + // Construct the SELECT statement text. + // + string text ("SELECT \"version\", \"migration\" FROM "); + + if (!svi.version_table.empty ()) + text += svi.version_table; // Already quoted. + else if (!schema_version_table_.empty ()) + text += schema_version_table_; // Already quoted. + else + text += "\"schema_version\""; + + text += " WHERE \"name\" = :1"; + + // Bind parameters and results. If the schema name is empty, replace + // it with a single space to workaround the VARCHAR2 empty/NULL issue. + // + string n (name.empty () ? string (" ") : name); + ub2 psize[1] = {static_cast<ub2> (n.size ())}; + sb2 pind[1] = {0}; + bind pbind[1] = {{bind::string, + const_cast<char*> (n.c_str ()), + &psize[0], + psize[0], + &pind[0], + 0}}; + binding param (pbind, 1); + param.version++; + + char version[12]; + unsigned int migration; + ub2 rsize[1]; + sb2 rind[2]; + bind rbind[2] = { + {bind::number, + version, + &rsize[0], + static_cast<ub4> (sizeof (version)), + &rind[0], + 0}, + + {bind::uinteger, &migration, 0, 4, &rind[1], 0} + }; + binding result (rbind, 2); + result.version++; + + // If we are not in transaction, then OCI will start an implicit one + // but only if we try to modify anything. Since our statement is read- + // only, we can run without a transaction. + // + connection_ptr cp; + if (!transaction::has_current ()) + cp = factory_->connect (); + + oracle::connection& c ( + cp != 0 + ? *cp + : transaction::current ().connection (const_cast<database&> (*this))); + + try + { + select_statement st (c, + text, + false, // Don't process. + false, // Don't optimize. + param, + result); + st.execute (); + auto_result ar (st); + + switch (st.fetch ()) + { + case select_statement::success: + { + value_traits<unsigned long long, id_big_int>::set_value ( + svi.version, version, rsize[0], rind[0] == -1); + svi.migration = migration != 0; + assert (st.fetch () == select_statement::no_data); + break; + } + case select_statement::no_data: + { + svi.version = 0; // No schema. + break; + } + } + } + catch (const database_exception& e) + { + // Detect the case where there is no version table. + // + if (e.size () != 0 && e.begin ()->error () == 942) + svi.version = 0; // No schema. + else + throw; + } + + return svi; + } + } +} diff --git a/libodb-oracle/odb/oracle/database.hxx b/libodb-oracle/odb/oracle/database.hxx new file mode 100644 index 0000000..0b66999 --- /dev/null +++ b/libodb-oracle/odb/oracle/database.hxx @@ -0,0 +1,542 @@ +// file : odb/oracle/database.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_DATABASE_HXX +#define ODB_ORACLE_DATABASE_HXX + +#include <odb/pre.hxx> + +#include <string> +#include <iosfwd> // std::ostream + +#include <odb/database.hxx> +#include <odb/details/config.hxx> // ODB_CXX11 +#include <odb/details/unique-ptr.hxx> +#include <odb/details/transfer-ptr.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/forward.hxx> +#include <odb/oracle/query.hxx> +#include <odb/oracle/tracer.hxx> +#include <odb/oracle/connection.hxx> +#include <odb/oracle/connection-factory.hxx> +#include <odb/oracle/auto-handle.hxx> +#include <odb/oracle/oracle-fwd.hxx> + +#include <odb/oracle/details/export.hxx> + +namespace odb +{ + namespace oracle + { + class transaction_impl; + + class LIBODB_ORACLE_EXPORT database: public odb::database + { + public: + database (const std::string& user, + const std::string& password, + const std::string& db, + ub2 charset = 0, + ub2 ncharset = 0, + OCIEnv* environment = 0, + details::transfer_ptr<connection_factory> = + details::transfer_ptr<connection_factory> ()); + + database (const std::string& user, + const std::string& password, + const std::string& service, + const std::string& host, + unsigned int port = 0, + ub2 charset = 0, + ub2 ncharset = 0, + OCIEnv* environment = 0, + details::transfer_ptr<connection_factory> = + details::transfer_ptr<connection_factory> ()); + + // Extract the database parameters from the command line. The + // following options are recognized: + // + // --user + // --password + // --database + // --service + // --host + // --port + // --options-file + // + // For more information, see the output of the print_usage() function + // below. If erase is true, the above options are removed from the + // argv array and the argc count is updated accordingly. This + // constructor may throw the cli_exception exception. + // + database (int& argc, + char* argv[], + bool erase = false, + ub2 charset = 0, + ub2 ncharset = 0, + OCIEnv* environment = 0, + details::transfer_ptr<connection_factory> = + details::transfer_ptr<connection_factory> ()); + + // Move-constructible but not move-assignable. + // + // Note: noexcept is not specified since odb::database(odb::database&&) + // can throw. + // +#ifdef ODB_CXX11 + database (database&&); +#endif + + static void + print_usage (std::ostream&); + + // Object persistence API. + // + public: + + // Make the object persistent. + // + template <typename T> + typename object_traits<T>::id_type + persist (T& object); + + template <typename T> + typename object_traits<T>::id_type + persist (const T& object); + + template <typename T> + typename object_traits<T>::id_type + persist (T* obj_ptr); + + template <typename T, template <typename> class P> + typename object_traits<T>::id_type + persist (const P<T>& obj_ptr); + + template <typename T, typename A1, template <typename, typename> class P> + typename object_traits<T>::id_type + persist (const P<T, A1>& obj_ptr); + + template <typename T, template <typename> class P> + typename object_traits<T>::id_type + persist (P<T>& obj_ptr); + + template <typename T, typename A1, template <typename, typename> class P> + typename object_traits<T>::id_type + persist (P<T, A1>& obj_ptr); + + template <typename T> + typename object_traits<T>::id_type + persist (const typename object_traits<T>::pointer_type& obj_ptr); + + // Bulk persist. Can be a range of references or pointers (including + // smart pointers) to objects. + // + template <typename I> + void + persist (I begin, I end, bool continue_failed = true); + + // Load an object. Throw object_not_persistent if not found. + // + template <typename T> + typename object_traits<T>::pointer_type + load (const typename object_traits<T>::id_type& id); + + template <typename T> + void + load (const typename object_traits<T>::id_type& id, T& object); + + // Load (or reload, if it is already loaded) a section of an object. + // + template <typename T> + void + load (T& object, section&); + + // Reload an object. + // + template <typename T> + void + reload (T& object); + + template <typename T> + void + reload (T* obj_ptr); + + template <typename T, template <typename> class P> + void + reload (const P<T>& obj_ptr); + + template <typename T, typename A1, template <typename, typename> class P> + void + reload (const P<T, A1>& obj_ptr); + + template <typename T, template <typename> class P> + void + reload (P<T>& obj_ptr); + + template <typename T, typename A1, template <typename, typename> class P> + void + reload (P<T, A1>& obj_ptr); + + template <typename T> + void + reload (const typename object_traits<T>::pointer_type& obj_ptr); + + // Loan an object if found. Return NULL/false if not found. + // + template <typename T> + typename object_traits<T>::pointer_type + find (const typename object_traits<T>::id_type& id); + + template <typename T> + bool + find (const typename object_traits<T>::id_type& id, T& object); + + // Update the state of a modified objects. + // + template <typename T> + void + update (T& object); + + template <typename T> + void + update (T* obj_ptr); + + template <typename T, template <typename> class P> + void + update (const P<T>& obj_ptr); + + template <typename T, typename A1, template <typename, typename> class P> + void + update (const P<T, A1>& obj_ptr); + + template <typename T, template <typename> class P> + void + update (P<T>& obj_ptr); + + template <typename T, typename A1, template <typename, typename> class P> + void + update (P<T, A1>& obj_ptr); + + template <typename T> + void + update (const typename object_traits<T>::pointer_type& obj_ptr); + + // Bulk update. Can be a range of references or pointers (including + // smart pointers) to objects. + // + template <typename I> + void + update (I begin, I end, bool continue_failed = true); + + // Update a section of an object. Throws the section_not_loaded + // exception if the section is not loaded. Note also that this + // function does not clear the changed flag if it is set. + // + template <typename T> + void + update (const T& object, const section&); + + // Make the object transient. Throw object_not_persistent if not + // found. + // + template <typename T> + void + erase (const typename object_traits<T>::id_type& id); + + template <typename T> + void + erase (T& object); + + template <typename T> + void + erase (T* obj_ptr); + + template <typename T, template <typename> class P> + void + erase (const P<T>& obj_ptr); + + template <typename T, typename A1, template <typename, typename> class P> + void + erase (const P<T, A1>& obj_ptr); + + template <typename T, template <typename> class P> + void + erase (P<T>& obj_ptr); + + template <typename T, typename A1, template <typename, typename> class P> + void + erase (P<T, A1>& obj_ptr); + + template <typename T> + void + erase (const typename object_traits<T>::pointer_type& obj_ptr); + + // Bulk erase. + // + template <typename T, typename I> + void + erase (I id_begin, I id_end, bool continue_failed = true); + + // Can be a range of references or pointers (including smart pointers) + // to objects. + // + template <typename I> + void + erase (I obj_begin, I obj_end, bool continue_failed = true); + + // Erase multiple objects matching a query predicate. + // + template <typename T> + unsigned long long + erase_query (); + + template <typename T> + unsigned long long + erase_query (const char*); + + template <typename T> + unsigned long long + erase_query (const std::string&); + + template <typename T> + unsigned long long + erase_query (const oracle::query_base&); + + template <typename T> + unsigned long long + erase_query (const odb::query_base&); + + // Query API. + // + template <typename T> + result<T> + query (); + + template <typename T> + result<T> + query (const char*); + + template <typename T> + result<T> + query (const std::string&); + + template <typename T> + result<T> + query (const oracle::query_base&); + + template <typename T> + result<T> + query (const odb::query_base&); + + // Query one API. + // + template <typename T> + typename result<T>::pointer_type + query_one (); + + template <typename T> + bool + query_one (T& object); + + template <typename T> + T + query_value (); + + template <typename T> + typename result<T>::pointer_type + query_one (const char*); + + template <typename T> + bool + query_one (const char*, T& object); + + template <typename T> + T + query_value (const char*); + + template <typename T> + typename result<T>::pointer_type + query_one (const std::string&); + + template <typename T> + bool + query_one (const std::string&, T& object); + + template <typename T> + T + query_value (const std::string&); + + template <typename T> + typename result<T>::pointer_type + query_one (const oracle::query_base&); + + template <typename T> + bool + query_one (const oracle::query_base&, T& object); + + template <typename T> + T + query_value (const oracle::query_base&); + + template <typename T> + typename result<T>::pointer_type + query_one (const odb::query_base&); + + template <typename T> + bool + query_one (const odb::query_base&, T& object); + + template <typename T> + T + query_value (const odb::query_base&); + + // 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 oracle::query_base&); + + template <typename T> + prepared_query<T> + prepare_query (const char* name, const odb::query_base&); + + // Transactions. + // + public: + virtual transaction_impl* + begin (); + + public: + connection_ptr + connection (); + + public: + const std::string& + user () const + { + return user_; + } + + const std::string& + password () const + { + return password_; + } + + const std::string& + db () const + { + return db_; + } + + const std::string& + service () const + { + return service_; + } + + const std::string& + host () const + { + return host_; + } + + unsigned int + port () const + { + return port_; + } + + ub2 + charset () const + { + return charset_; + } + + ub2 + ncharset () const + { + return ncharset_; + } + + OCIEnv* + environment () + { + return environment_; + } + + // SQL statement tracing. + // + public: + typedef oracle::tracer tracer_type; + + void + tracer (tracer_type& t) + { + odb::database::tracer (t); + } + + void + tracer (tracer_type* t) + { + odb::database::tracer (t); + } + + using odb::database::tracer; + + // Database schema version. + // + protected: + virtual const schema_version_info& + load_schema_version (const std::string& schema_name) const; + + public: + // Database id constant (useful for meta-programming). + // + static const odb::database_id database_id = id_oracle; + + public: + virtual + ~database (); + + protected: + virtual odb::connection* + connection_ (); + + private: + // Note: remember to update move ctor if adding any new members. + // + std::string user_; + std::string password_; + + std::string db_; + + std::string service_; + std::string host_; + unsigned int port_; + + ub2 charset_; + ub2 ncharset_; + + auto_handle<OCIEnv> auto_environment_; + OCIEnv* environment_; + + details::unique_ptr<connection_factory> factory_; + }; + } +} + +#include <odb/oracle/database.ixx> + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_DATABASE_HXX diff --git a/libodb-oracle/odb/oracle/database.ixx b/libodb-oracle/odb/oracle/database.ixx new file mode 100644 index 0000000..ea41aca --- /dev/null +++ b/libodb-oracle/odb/oracle/database.ixx @@ -0,0 +1,640 @@ +// file : odb/oracle/database.ixx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <utility> // move() + +#include <odb/oracle/transaction.hxx> + +namespace odb +{ + namespace oracle + { +#ifdef ODB_CXX11 + inline database:: + database (database&& db) // Has to be inline. + : odb::database (std::move (db)), + user_ (std::move (db.user_)), + password_ (std::move (db.password_)), + db_ (std::move (db.db_)), + service_ (std::move (db.service_)), + host_ (std::move (db.host_)), + port_ (db.port_), + charset_ (db.charset_), + ncharset_ (db.ncharset_), + auto_environment_ (std::move (db.auto_environment_)), + environment_ (db.environment_), + factory_ (std::move (db.factory_)) + { + factory_->database (*this); // New database instance. + } +#endif + + inline connection_ptr database:: + connection () + { + // Go through the virtual connection_() function instead of + // directly to allow overriding. + // + return connection_ptr ( + static_cast<oracle::connection*> (connection_ ())); + } + + template <typename T> + inline typename object_traits<T>::id_type database:: + persist (T& obj) + { + return persist_<T, id_oracle> (obj); + } + + template <typename T> + inline typename object_traits<T>::id_type database:: + persist (const T& obj) + { + return persist_<const T, id_oracle> (obj); + } + + template <typename T> + inline typename object_traits<T>::id_type database:: + persist (T* p) + { + typedef typename object_traits<T>::pointer_type object_pointer; + + // The passed pointer should be the same or implicit-convertible + // to the object pointer. This way we make sure the object pointer + // does not assume ownership of the passed object. + // + const object_pointer& pobj (p); + + return persist_<T, id_oracle> (pobj); + } + + template <typename T, template <typename> class P> + inline typename object_traits<T>::id_type database:: + persist (const P<T>& p) + { + typedef typename object_traits<T>::pointer_type object_pointer; + + // The passed pointer should be the same or implicit-convertible + // to the object pointer. This way we make sure the object pointer + // does not assume ownership of the passed object. + // + const object_pointer& pobj (p); + + return persist_<T, id_oracle> (pobj); + } + + template <typename T, typename A1, template <typename, typename> class P> + inline typename object_traits<T>::id_type database:: + persist (const P<T, A1>& p) + { + typedef typename object_traits<T>::pointer_type object_pointer; + + // The passed pointer should be the same or implicit-convertible + // to the object pointer. This way we make sure the object pointer + // does not assume ownership of the passed object. + // + const object_pointer& pobj (p); + + return persist_<T, id_oracle> (pobj); + } + + template <typename T, template <typename> class P> + inline typename object_traits<T>::id_type database:: + persist (P<T>& p) + { + const P<T>& cr (p); + return persist<T, P> (cr); + } + + template <typename T, typename A1, template <typename, typename> class P> + inline typename object_traits<T>::id_type database:: + persist (P<T, A1>& p) + { + const P<T, A1>& cr (p); + return persist<T, A1, P> (cr); + } + + template <typename T> + inline typename object_traits<T>::id_type database:: + persist (const typename object_traits<T>::pointer_type& pobj) + { + return persist_<T, id_oracle> (pobj); + } + + template <typename I> + inline void database:: + persist (I b, I e, bool cont) + { + persist_<I, id_oracle> (b, e, cont); + } + + template <typename T> + inline typename object_traits<T>::pointer_type database:: + load (const typename object_traits<T>::id_type& id) + { + return load_<T, id_oracle> (id); + } + + template <typename T> + inline void database:: + load (const typename object_traits<T>::id_type& id, T& obj) + { + return load_<T, id_oracle> (id, obj); + } + + template <typename T> + inline void database:: + load (T& obj, section& s) + { + return load_<T, id_oracle> (obj, s); + } + + template <typename T> + inline typename object_traits<T>::pointer_type database:: + find (const typename object_traits<T>::id_type& id) + { + return find_<T, id_oracle> (id); + } + + template <typename T> + inline bool database:: + find (const typename object_traits<T>::id_type& id, T& obj) + { + return find_<T, id_oracle> (id, obj); + } + + template <typename T> + inline void database:: + reload (T& obj) + { + reload_<T, id_oracle> (obj); + } + + template <typename T> + inline void database:: + reload (T* p) + { + reload<T> (*p); + } + + template <typename T, template <typename> class P> + inline void database:: + reload (const P<T>& p) + { + reload (odb::pointer_traits< P<T> >::get_ref (p)); + } + + template <typename T, typename A1, template <typename, typename> class P> + inline void database:: + reload (const P<T, A1>& p) + { + reload (odb::pointer_traits< P<T, A1> >::get_ref (p)); + } + + template <typename T, template <typename> class P> + inline void database:: + reload (P<T>& p) + { + reload (odb::pointer_traits< P<T> >::get_ref (p)); + } + + template <typename T, typename A1, template <typename, typename> class P> + inline void database:: + reload (P<T, A1>& p) + { + reload (odb::pointer_traits< P<T, A1> >::get_ref (p)); + } + + template <typename T> + inline void database:: + reload (const typename object_traits<T>::pointer_type& pobj) + { + typedef typename object_traits<T>::pointer_type pointer_type; + + reload (odb::pointer_traits<pointer_type>::get_ref (pobj)); + } + + template <typename T> + inline void database:: + update (T& obj) + { + update_<T, id_oracle> (obj); + } + + template <typename T> + inline void database:: + update (T* p) + { + typedef typename object_traits<T>::pointer_type object_pointer; + + // The passed pointer should be the same or implicit-convertible + // to the object pointer. This way we make sure the object pointer + // does not assume ownership of the passed object. + // + const object_pointer& pobj (p); + + update_<T, id_oracle> (pobj); + } + + template <typename T, template <typename> class P> + inline void database:: + update (const P<T>& p) + { + typedef typename object_traits<T>::pointer_type object_pointer; + + // The passed pointer should be the same or implicit-convertible + // to the object pointer. This way we make sure the object pointer + // does not assume ownership of the passed object. + // + const object_pointer& pobj (p); + + update_<T, id_oracle> (pobj); + } + + template <typename T, typename A1, template <typename, typename> class P> + inline void database:: + update (const P<T, A1>& p) + { + typedef typename object_traits<T>::pointer_type object_pointer; + + // The passed pointer should be the same or implicit-convertible + // to the object pointer. This way we make sure the object pointer + // does not assume ownership of the passed object. + // + const object_pointer& pobj (p); + + update_<T, id_oracle> (pobj); + } + + template <typename T, template <typename> class P> + inline void database:: + update (P<T>& p) + { + const P<T>& cr (p); + update<T, P> (cr); + } + + template <typename T, typename A1, template <typename, typename> class P> + inline void database:: + update (P<T, A1>& p) + { + const P<T, A1>& cr (p); + update<T, A1, P> (cr); + } + + template <typename T> + inline void database:: + update (const typename object_traits<T>::pointer_type& pobj) + { + update_<T, id_oracle> (pobj); + } + + template <typename I> + inline void database:: + update (I b, I e, bool cont) + { + update_<I, id_oracle> (b, e, cont); + } + + template <typename T> + inline void database:: + update (const T& obj, const section& s) + { + update_<T, id_oracle> (obj, s); + } + + template <typename T> + inline void database:: + erase (const typename object_traits<T>::id_type& id) + { + return erase_<T, id_oracle> (id); + } + + template <typename T> + inline void database:: + erase (T& obj) + { + return erase_<T, id_oracle> (obj); + } + + template <typename T> + inline void database:: + erase (T* p) + { + typedef typename object_traits<T>::pointer_type object_pointer; + + // The passed pointer should be the same or implicit-convertible + // to the object pointer. This way we make sure the object pointer + // does not assume ownership of the passed object. + // + const object_pointer& pobj (p); + + erase_<T, id_oracle> (pobj); + } + + template <typename T, template <typename> class P> + inline void database:: + erase (const P<T>& p) + { + typedef typename object_traits<T>::pointer_type object_pointer; + + // The passed pointer should be the same or implicit-convertible + // to the object pointer. This way we make sure the object pointer + // does not assume ownership of the passed object. + // + const object_pointer& pobj (p); + + erase_<T, id_oracle> (pobj); + } + + template <typename T, typename A1, template <typename, typename> class P> + inline void database:: + erase (const P<T, A1>& p) + { + typedef typename object_traits<T>::pointer_type object_pointer; + + // The passed pointer should be the same or implicit-convertible + // to the object pointer. This way we make sure the object pointer + // does not assume ownership of the passed object. + // + const object_pointer& pobj (p); + + erase_<T, id_oracle> (pobj); + } + + template <typename T, template <typename> class P> + inline void database:: + erase (P<T>& p) + { + const P<T>& cr (p); + erase<T, P> (cr); + } + + template <typename T, typename A1, template <typename, typename> class P> + inline void database:: + erase (P<T, A1>& p) + { + const P<T, A1>& cr (p); + erase<T, A1, P> (cr); + } + + template <typename T> + inline void database:: + erase (const typename object_traits<T>::pointer_type& pobj) + { + erase_<T, id_oracle> (pobj); + } + + template <typename T, typename I> + inline void database:: + erase (I idb, I ide, bool cont) + { + erase_id_<I, T, id_oracle> (idb, ide, cont); + } + + template <typename I> + inline void database:: + erase (I ob, I oe, bool cont) + { + erase_object_<I, id_oracle> (ob, oe, cont); + } + + template <typename T> + inline unsigned long long database:: + erase_query () + { + // T is always object_type. + // + return erase_query<T> (oracle::query_base ()); + } + + template <typename T> + inline unsigned long long database:: + erase_query (const char* q) + { + // T is always object_type. + // + return erase_query<T> (oracle::query_base (q)); + } + + template <typename T> + inline unsigned long long database:: + erase_query (const std::string& q) + { + // T is always object_type. + // + return erase_query<T> (oracle::query_base (q)); + } + + template <typename T> + inline unsigned long long database:: + erase_query (const oracle::query_base& q) + { + // T is always object_type. + // + return object_traits_impl<T, id_oracle>::erase_query (*this, q); + } + + template <typename T> + inline unsigned long long database:: + erase_query (const odb::query_base& q) + { + // Translate to native query. + // + return erase_query<T> (oracle::query_base (q)); + } + + template <typename T> + inline result<T> database:: + query () + { + return query<T> (oracle::query_base ()); + } + + template <typename T> + inline result<T> database:: + query (const char* q) + { + return query<T> (oracle::query_base (q)); + } + + template <typename T> + inline result<T> database:: + query (const std::string& q) + { + return query<T> (oracle::query_base (q)); + } + + template <typename T> + inline result<T> database:: + query (const oracle::query_base& q) + { + // T is always object_type. We also don't need to check for transaction + // here; object_traits::query () does this. + // + return query_<T, id_oracle>::call (*this, q); + } + + template <typename T> + inline result<T> database:: + query (const odb::query_base& q) + { + // Translate to native query. + // + return query<T> (oracle::query_base (q)); + } + + template <typename T> + inline typename result<T>::pointer_type database:: + query_one () + { + return query_one<T> (oracle::query_base ()); + } + + template <typename T> + inline bool database:: + query_one (T& o) + { + return query_one<T> (oracle::query_base (), o); + } + + template <typename T> + inline T database:: + query_value () + { + return query_value<T> (oracle::query_base ()); + } + + template <typename T> + inline typename result<T>::pointer_type database:: + query_one (const char* q) + { + return query_one<T> (oracle::query_base (q)); + } + + template <typename T> + inline bool database:: + query_one (const char* q, T& o) + { + return query_one<T> (oracle::query_base (q), o); + } + + template <typename T> + inline T database:: + query_value (const char* q) + { + return query_value<T> (oracle::query_base (q)); + } + + template <typename T> + inline typename result<T>::pointer_type database:: + query_one (const std::string& q) + { + return query_one<T> (oracle::query_base (q)); + } + + template <typename T> + inline bool database:: + query_one (const std::string& q, T& o) + { + return query_one<T> (oracle::query_base (q), o); + } + + template <typename T> + inline T database:: + query_value (const std::string& q) + { + return query_value<T> (oracle::query_base (q)); + } + + template <typename T> + inline typename result<T>::pointer_type database:: + query_one (const oracle::query_base& q) + { + // T is always object_type. We also don't need to check for transaction + // here; object_traits::query () does this. + // + return query_one_<T, id_oracle> (q); + } + + template <typename T> + inline bool database:: + query_one (const oracle::query_base& q, T& o) + { + // T is always object_type. We also don't need to check for transaction + // here; object_traits::query () does this. + // + return query_one_<T, id_oracle> (q, o); + } + + template <typename T> + inline T database:: + query_value (const oracle::query_base& q) + { + // T is always object_type. We also don't need to check for transaction + // here; object_traits::query () does this. + // + return query_value_<T, id_oracle> (q); + } + + template <typename T> + inline typename result<T>::pointer_type database:: + query_one (const odb::query_base& q) + { + // Translate to native query. + // + return query_one<T> (oracle::query_base (q)); + } + + template <typename T> + inline bool database:: + query_one (const odb::query_base& q, T& o) + { + // Translate to native query. + // + return query_one<T> (oracle::query_base (q), o); + } + + template <typename T> + inline T database:: + query_value (const odb::query_base& q) + { + // Translate to native query. + // + return query_value<T> (oracle::query_base (q)); + } + + template <typename T> + inline prepared_query<T> database:: + prepare_query (const char* n, const char* q) + { + return prepare_query<T> (n, oracle::query_base (q)); + } + + template <typename T> + inline prepared_query<T> database:: + prepare_query (const char* n, const std::string& q) + { + return prepare_query<T> (n, oracle::query_base (q)); + } + + template <typename T> + inline prepared_query<T> database:: + prepare_query (const char* n, const oracle::query_base& q) + { + // Throws if not in transaction. + // + oracle::connection& c (transaction::current ().connection (*this)); + return c.prepare_query<T> (n, q); + } + + template <typename T> + inline prepared_query<T> database:: + prepare_query (const char* n, const odb::query_base& q) + { + // Translate to native query. + // + return prepare_query<T> (n, oracle::query_base (q)); + } + } +} diff --git a/libodb-oracle/odb/oracle/details/.gitignore b/libodb-oracle/odb/oracle/details/.gitignore new file mode 100644 index 0000000..b298f89 --- /dev/null +++ b/libodb-oracle/odb/oracle/details/.gitignore @@ -0,0 +1 @@ +/options.?xx diff --git a/libodb-oracle/odb/oracle/details/config.hxx b/libodb-oracle/odb/oracle/details/config.hxx new file mode 100644 index 0000000..a9330a4 --- /dev/null +++ b/libodb-oracle/odb/oracle/details/config.hxx @@ -0,0 +1,15 @@ +// file : odb/oracle/details/config.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_DETAILS_CONFIG_HXX +#define ODB_ORACLE_DETAILS_CONFIG_HXX + +// no pre + +#ifdef ODB_COMPILER +# error libodb-oracle header included in odb-compiled header +#endif + +// no post + +#endif // ODB_ORACLE_DETAILS_CONFIG_HXX diff --git a/libodb-oracle/odb/oracle/details/conversion.hxx b/libodb-oracle/odb/oracle/details/conversion.hxx new file mode 100644 index 0000000..c3c86cf --- /dev/null +++ b/libodb-oracle/odb/oracle/details/conversion.hxx @@ -0,0 +1,58 @@ +// file : odb/oracle/details/conversion.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_DETAILS_CONVERSION_HXX +#define ODB_ORACLE_DETAILS_CONVERSION_HXX + +#include <odb/oracle/traits.hxx> + +#include <odb/details/meta/answer.hxx> + +namespace odb +{ + // @@ Revise this. + // + namespace details {} + + namespace oracle + { + namespace details + { + using namespace odb::details; + + // Detect whether conversion is specified in type_traits. + // + template <typename T> + meta::yes + conversion_p_test (typename type_traits<T>::conversion*); + + template <typename T> + meta::no + conversion_p_test (...); + + template <typename T> + struct conversion_p + { + static const bool value = + sizeof (conversion_p_test<T> (0)) == sizeof (meta::yes); + }; + + template <typename T, bool = conversion_p<T>::value> + struct conversion; + + template <typename T> + struct conversion<T, true> + { + static const char* to () {return type_traits<T>::conversion::to ();} + }; + + template <typename T> + struct conversion<T, false> + { + static const char* to () {return 0;} + }; + } + } +} + +#endif // ODB_ORACLE_DETAILS_CONVERSION_HXX diff --git a/libodb-oracle/odb/oracle/details/date.hxx b/libodb-oracle/odb/oracle/details/date.hxx new file mode 100644 index 0000000..d6c1acb --- /dev/null +++ b/libodb-oracle/odb/oracle/details/date.hxx @@ -0,0 +1,59 @@ +// file : odb/oracle/details/date.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_DETAILS_DATE_HXX +#define ODB_ORACLE_DETAILS_DATE_HXX + +namespace odb +{ + // @@ Revise this. + // + namespace details + { + } + + namespace oracle + { + namespace details + { + inline void + set_date (char* b, + short year, + unsigned char month, + unsigned char day, + unsigned char hour, + unsigned char minute, + unsigned char second) + { + b[0] = static_cast<char> (year / 100 + 100); + b[1] = static_cast<char> (year % 100 + 100); + b[2] = static_cast<char> (month); + b[3] = static_cast<char> (day); + b[4] = static_cast<char> (hour + 1); + b[5] = static_cast<char> (minute + 1); + b[6] = static_cast<char> (second + 1); + } + + inline void + get_date (const char* b, + short& year, + unsigned char& month, + unsigned char& day, + unsigned char& hour, + unsigned char& minute, + unsigned char& second) + { + const unsigned char* ub (reinterpret_cast<const unsigned char*> (b)); + + year = 100 * ub[0] + ub[1] - 10100; + month = ub[2]; + day = ub[3]; + hour = ub[4] - 1; + minute = ub[5] - 1; + second = ub[6] - 1; + } + } + } +} + +#endif // ODB_ORACLE_DETAILS_DATE_HXX diff --git a/libodb-oracle/odb/oracle/details/export.hxx b/libodb-oracle/odb/oracle/details/export.hxx new file mode 100644 index 0000000..788948a --- /dev/null +++ b/libodb-oracle/odb/oracle/details/export.hxx @@ -0,0 +1,50 @@ +// file : odb/oracle/details/export.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_DETAILS_EXPORT_HXX +#define ODB_ORACLE_DETAILS_EXPORT_HXX + +#include <odb/pre.hxx> + +// Note: do this check directly instead of including config.hxx. +// +#ifdef ODB_COMPILER +# error libodb-oracle header included in odb-compiled header +#endif + +// Normally we don't export class templates (but do complete specializations), +// inline functions, and classes with only inline member functions. Exporting +// classes that inherit from non-exported/imported bases (e.g., std::string) +// will end up badly. The only known workarounds are to not inherit or to not +// export. Also, MinGW GCC doesn't like seeing non-exported function being +// used before their inline definition. The workaround is to reorder code. In +// the end it's all trial and error. + +#if defined(LIBODB_ORACLE_STATIC) // Using static. +# define LIBODB_ORACLE_EXPORT +#elif defined(LIBODB_ORACLE_STATIC_BUILD) // Building static. +# define LIBODB_ORACLE_EXPORT +#elif defined(LIBODB_ORACLE_SHARED) // Using shared. +# ifdef _WIN32 +# define LIBODB_ORACLE_EXPORT __declspec(dllimport) +# else +# define LIBODB_ORACLE_EXPORT +# endif +#elif defined(LIBODB_ORACLE_SHARED_BUILD) // Building shared. +# ifdef _WIN32 +# define LIBODB_ORACLE_EXPORT __declspec(dllexport) +# else +# define LIBODB_ORACLE_EXPORT +# endif +#else +// If none of the above macros are defined, then we assume we are being used +// by some third-party build system that cannot/doesn't signal the library +// type. Note that this fallback works for both static and shared but in case +// of shared will be sub-optimal compared to having dllimport. +// +# define LIBODB_ORACLE_EXPORT // Using static or shared. +#endif + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_DETAILS_EXPORT_HXX diff --git a/libodb-oracle/odb/oracle/details/number.cxx b/libodb-oracle/odb/oracle/details/number.cxx new file mode 100644 index 0000000..aeb2b96 --- /dev/null +++ b/libodb-oracle/odb/oracle/details/number.cxx @@ -0,0 +1,269 @@ +// file : odb/oracle/details/number.cxx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <cstddef> // std::size_t +#include <cassert> + +#include <odb/oracle/details/number.hxx> + +using namespace std; + +namespace odb +{ + namespace oracle + { + namespace details + { + // The NUMBER type's binary representation is made up of the following + // bit fields, ordered in increasing memory address. + // + // 000 to 007: The base-100 exponent bits. The most significant bit is + // the sign bit of the number, which is set for positive + // numbers and cleared for negative numbers. For positive + // numbers, the exponent has a bias of 65 added to it. + // + // 008 to 167: The mantissa bits. Each byte of this field represents a + // single base-100 value. + // + // + + long long + number_to_int64 (const char* b, size_t n) + { + // All bytes in the buffer are interpreted as unsigned. + // + const unsigned char* ub (reinterpret_cast<const unsigned char*> (b)); + + // Zero is represented by zero significant bits and an exponent + // set to 128. + // + if (n == 1) + { + assert (ub[0] == 128); + return 0; + } + + long long v (0); + + // Test the sign bit of the exponent. + // + if ((ub[0] & 0x80) != 0) + { + // The unbiased exponent of a positive number may be calculated as + // ub[0 - 128 - 65. For an integer, this is the order of magnitude + // of the number. Calculate the maximum weight, 100 ^ o, where o is + // the order of magnitude of the number. + // + long long w (1); + + for (size_t i (0), o (ub[0] - 193); i < o; ++i) + w *= 100; + + // Accumlate the sum of the significant base-100 terms. + // + for (const unsigned char* m (ub + 1), *e (ub + n); m < e; ++m) + { + v += (*m - 1) * w; + w /= 100; + } + } + else + { + // The unbiased exponent of a negative number is calculated as + // (~ub[0] & 0x7F) - 193. For an integer, this is the order of + // magnitude of the number. Calculate the maximum weight, 100 ^ o, + // where o is the order of magnitude of the number. + // + long long w (1); + + for (size_t i (0), o ((~ub[0] & 0x7F) - 65); i < o; ++i) + w *= 100; + + // Accumulate the sum of the significant base-100 terms. Note that + // negative values will have a terminator byte which is included + // in the length. This is ignored. + // + for (const unsigned char* m (ub + 1), *e (ub + n - 1); m < e; ++m) + { + v -= (101 - *m) * w; + w /= 100; + } + } + + return v; + } + + void + int64_to_number (char* b, size_t& n, long long v) + { + // We assume that b is long enough to contain a long long NUMBER + // representation, that being 12 bytes. + // + + // All bytes in the buffer are interpreted as unsigned. + // + unsigned char* ub (reinterpret_cast<unsigned char*> (b)); + + if (v == 0) + { + ub[0] = 128; + n = 1; + + return; + } + + bool sig (false); + unsigned char t[11], *m (t); + n = 0; + + if (v < 0) + { + // Termination marker for negative numbers. + // + *m++ = 102; + + while (v != 0) + { + int r (static_cast<int> (v % 100)); + sig = sig || r != 0; + + if (sig) + *m++ = static_cast<unsigned char> (101 + r); + + v /= 100; + ++n; + } + + // The exponent is one less than the number of base 100 digits. It is + // inverted for negative values. + // + ub[0] = static_cast<unsigned char> (~(n + 192)); + } + else + { + while (v != 0) + { + int r (static_cast<int> (v % 100)); + sig = sig || r != 0; + + if (sig) + *m++ = static_cast<unsigned char> (r + 1); + + v /= 100; + ++n; + } + + // Exponent is one less than the number of base 100 digits. + // + ub[0] = static_cast<unsigned char> (n + 192); + } + + // Set the length. + // + n = static_cast<unsigned char> (m - t + 1); + + // Set the significant digits in big-endian byte order and the + // terminator, if any. + // + for (size_t i (1); m > t; ++i) + ub[i] = *--m; + } + + unsigned long long + number_to_uint64 (const char* b, size_t n) + { + // All bytes in the buffer are interpreted as unsigned. + // + const unsigned char* ub (reinterpret_cast<const unsigned char*> (b)); + + // Zero is represented by zero significant bits and an exponent + // set to 128. + // + if (n == 1) + { + assert (ub[0] == 128); + return 0; + } + + unsigned long long v (0); + + // Test the sign bit of the exponent. + // + if ((ub[0] & 0x80) == 0) + { + assert (false); + return 0; + } + + // The unbiased exponent of a positive number may be calculated as + // ub[0] - 128 - 65. For an integer, this is the order of magnitude + // of the number. Calculate the maximum weight, 100 ^ o, where o is + // the order of magnitude of the number. + // + unsigned long long w (1); + + for (size_t i (0), o (ub[0] - 193); i < o; ++i) + w *= 100; + + // Accumlate the sum of the significant base-100 terms. + // + for (const unsigned char* m (ub + 1), *e (ub + n); m < e; ++m) + { + v += (*m - 1) * w; + w /= 100; + } + + return v; + } + + void + uint64_to_number (char* b, size_t& n, unsigned long long v) + { + // We assume that b is long enough to contain an unsigned long long + // NUMBER representation, that being 12 bytes. + // + + // All bytes in the buffer are interpreted as unsigned. + // + unsigned char* ub (reinterpret_cast<unsigned char*> (b)); + + if (v == 0) + { + ub[0] = 128; + n = 1; + + return; + } + + bool sig (false); + unsigned char t[11], *m (t); + n = 0; + + while (v != 0) + { + int r (static_cast<int> (v % 100)); + sig = sig || r != 0; + + if (sig) + *m++ = static_cast<unsigned char> (r + 1); + + v /= 100; + ++n; + } + + // Exponent is one less than the number of base 100 digits. + // + ub[0] = static_cast<unsigned char> (n + 192); + + // Set the length. + // + n = static_cast<unsigned char> (m - t + 1); + + // Set the significant digits in big-endian byte order. + // + for (size_t i (1); m > t; ++i) + ub[i] = *--m; + } + } + } +} diff --git a/libodb-oracle/odb/oracle/details/number.hxx b/libodb-oracle/odb/oracle/details/number.hxx new file mode 100644 index 0000000..8ab6a90 --- /dev/null +++ b/libodb-oracle/odb/oracle/details/number.hxx @@ -0,0 +1,44 @@ +// file : odb/oracle/details/number.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_NUMBER_HXX +#define ODB_ORACLE_NUMBER_HXX + +#include <odb/pre.hxx> + +#include <cstddef> // std::size_t + +#include <odb/oracle/details/export.hxx> + +namespace odb +{ + // @@ Revise this. + // + namespace details + { + } + + namespace oracle + { + namespace details + { + using namespace odb::details; + + LIBODB_ORACLE_EXPORT long long + number_to_int64 (const char* buffer, std::size_t n); + + LIBODB_ORACLE_EXPORT void + int64_to_number (char* buffer, std::size_t& n, long long val); + + LIBODB_ORACLE_EXPORT unsigned long long + number_to_uint64 (const char* buffer, std::size_t n); + + LIBODB_ORACLE_EXPORT void + uint64_to_number (char* buffer, std::size_t& n, unsigned long long val); + } + } +} + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_NUMBER_HXX diff --git a/libodb-oracle/odb/oracle/details/options.cli b/libodb-oracle/odb/oracle/details/options.cli new file mode 100644 index 0000000..82be308 --- /dev/null +++ b/libodb-oracle/odb/oracle/details/options.cli @@ -0,0 +1,61 @@ +// file : odb/oracle/details/options.cli +// license : ODB NCUEL; see accompanying LICENSE file + +include <string>; + +namespace odb +{ + namespace oracle + { + namespace details + { + class options + { + std::string --user + { + "<name>", + "Oracle database user." + }; + + std::string --password + { + "<str>", + "Oracle database password." + }; + + std::string --database + { + "<conn-id>", + "Oracle connect identifier." + }; + + std::string --service + { + "<name>", + "Oracle service name." + }; + + std::string --host + { + "<str>", + "Oracle database host name or address (localhost by default)." + }; + + unsigned int --port + { + "<integer>", + "Oracle database port number." + }; + + std::string --options-file + { + "<file>", + "Read additional options from <file>. Each option should appear on a + separate line optionally followed by space or equal sign (\cb{=}) + and an option value. Empty lines and lines starting with \cb{#} are + ignored." + }; + }; + } + } +} diff --git a/libodb-oracle/odb/oracle/details/pregenerated/odb/oracle/details/options.cxx b/libodb-oracle/odb/oracle/details/pregenerated/odb/oracle/details/options.cxx new file mode 100644 index 0000000..69ea3d7 --- /dev/null +++ b/libodb-oracle/odb/oracle/details/pregenerated/odb/oracle/details/options.cxx @@ -0,0 +1,1125 @@ +// -*- C++ -*- +// +// This file was generated by CLI, a command line interface +// compiler for C++. +// + +// Begin prologue. +// +// +// End prologue. + +#include <odb/oracle/details/options.hxx> + +#include <map> +#include <set> +#include <string> +#include <vector> +#include <utility> +#include <ostream> +#include <sstream> +#include <cstring> +#include <fstream> + +namespace odb +{ + namespace oracle + { + namespace details + { + namespace cli + { + // unknown_option + // + unknown_option:: + ~unknown_option () throw () + { + } + + void unknown_option:: + print (::std::ostream& os) const + { + os << "unknown option '" << option ().c_str () << "'"; + } + + const char* unknown_option:: + what () const throw () + { + return "unknown option"; + } + + // unknown_argument + // + unknown_argument:: + ~unknown_argument () throw () + { + } + + void unknown_argument:: + print (::std::ostream& os) const + { + os << "unknown argument '" << argument ().c_str () << "'"; + } + + const char* unknown_argument:: + what () const throw () + { + return "unknown argument"; + } + + // missing_value + // + missing_value:: + ~missing_value () throw () + { + } + + void missing_value:: + print (::std::ostream& os) const + { + os << "missing value for option '" << option ().c_str () << "'"; + } + + const char* missing_value:: + what () const throw () + { + return "missing option value"; + } + + // invalid_value + // + invalid_value:: + ~invalid_value () throw () + { + } + + void invalid_value:: + print (::std::ostream& os) const + { + os << "invalid value '" << value ().c_str () << "' for option '" + << option ().c_str () << "'"; + + if (!message ().empty ()) + os << ": " << message ().c_str (); + } + + const char* invalid_value:: + what () const throw () + { + return "invalid option value"; + } + + // eos_reached + // + void eos_reached:: + print (::std::ostream& os) const + { + os << what (); + } + + const char* eos_reached:: + what () const throw () + { + return "end of argument stream reached"; + } + + // file_io_failure + // + file_io_failure:: + ~file_io_failure () throw () + { + } + + void file_io_failure:: + print (::std::ostream& os) const + { + os << "unable to open file '" << file ().c_str () << "' or read failure"; + } + + const char* file_io_failure:: + what () const throw () + { + return "unable to open file or read failure"; + } + + // unmatched_quote + // + unmatched_quote:: + ~unmatched_quote () throw () + { + } + + void unmatched_quote:: + print (::std::ostream& os) const + { + os << "unmatched quote in argument '" << argument ().c_str () << "'"; + } + + const char* unmatched_quote:: + what () const throw () + { + return "unmatched quote"; + } + + // scanner + // + scanner:: + ~scanner () + { + } + + // argv_scanner + // + bool argv_scanner:: + more () + { + return i_ < argc_; + } + + const char* argv_scanner:: + peek () + { + if (i_ < argc_) + return argv_[i_]; + else + throw eos_reached (); + } + + const char* argv_scanner:: + next () + { + if (i_ < argc_) + { + const char* r (argv_[i_]); + + if (erase_) + { + for (int i (i_ + 1); i < argc_; ++i) + argv_[i - 1] = argv_[i]; + + --argc_; + argv_[argc_] = 0; + } + else + ++i_; + + ++start_position_; + return r; + } + else + throw eos_reached (); + } + + void argv_scanner:: + skip () + { + if (i_ < argc_) + { + ++i_; + ++start_position_; + } + else + throw eos_reached (); + } + + std::size_t argv_scanner:: + position () + { + return start_position_; + } + + // argv_file_scanner + // + int argv_file_scanner::zero_argc_ = 0; + std::string argv_file_scanner::empty_string_; + + bool argv_file_scanner:: + more () + { + if (!args_.empty ()) + return true; + + while (base::more ()) + { + // See if the next argument is the file option. + // + const char* a (base::peek ()); + const option_info* oi = 0; + const char* ov = 0; + + if (!skip_) + { + if ((oi = find (a)) != 0) + { + base::next (); + + if (!base::more ()) + throw missing_value (a); + + ov = base::next (); + } + else if (std::strncmp (a, "-", 1) == 0) + { + if ((ov = std::strchr (a, '=')) != 0) + { + std::string o (a, 0, ov - a); + if ((oi = find (o.c_str ())) != 0) + { + base::next (); + ++ov; + } + } + } + } + + if (oi != 0) + { + if (oi->search_func != 0) + { + std::string f (oi->search_func (ov, oi->arg)); + + if (!f.empty ()) + load (f); + } + else + load (ov); + + if (!args_.empty ()) + return true; + } + else + { + if (!skip_) + skip_ = (std::strcmp (a, "--") == 0); + + return true; + } + } + + return false; + } + + const char* argv_file_scanner:: + peek () + { + if (!more ()) + throw eos_reached (); + + return args_.empty () ? base::peek () : args_.front ().value.c_str (); + } + + const std::string& argv_file_scanner:: + peek_file () + { + if (!more ()) + throw eos_reached (); + + return args_.empty () ? empty_string_ : *args_.front ().file; + } + + std::size_t argv_file_scanner:: + peek_line () + { + if (!more ()) + throw eos_reached (); + + return args_.empty () ? 0 : args_.front ().line; + } + + const char* argv_file_scanner:: + next () + { + if (!more ()) + throw eos_reached (); + + if (args_.empty ()) + return base::next (); + else + { + hold_[i_ == 0 ? ++i_ : --i_].swap (args_.front ().value); + args_.pop_front (); + ++start_position_; + return hold_[i_].c_str (); + } + } + + void argv_file_scanner:: + skip () + { + if (!more ()) + throw eos_reached (); + + if (args_.empty ()) + return base::skip (); + else + { + args_.pop_front (); + ++start_position_; + } + } + + const argv_file_scanner::option_info* argv_file_scanner:: + find (const char* a) const + { + for (std::size_t i (0); i < options_count_; ++i) + if (std::strcmp (a, options_[i].option) == 0) + return &options_[i]; + + return 0; + } + + std::size_t argv_file_scanner:: + position () + { + return start_position_; + } + + void argv_file_scanner:: + load (const std::string& file) + { + using namespace std; + + ifstream is (file.c_str ()); + + if (!is.is_open ()) + throw file_io_failure (file); + + files_.push_back (file); + + arg a; + a.file = &*files_.rbegin (); + + for (a.line = 1; !is.eof (); ++a.line) + { + string line; + getline (is, line); + + if (is.fail () && !is.eof ()) + throw file_io_failure (file); + + string::size_type n (line.size ()); + + // Trim the line from leading and trailing whitespaces. + // + if (n != 0) + { + const char* f (line.c_str ()); + const char* l (f + n); + + const char* of (f); + while (f < l && (*f == ' ' || *f == '\t' || *f == '\r')) + ++f; + + --l; + + const char* ol (l); + while (l > f && (*l == ' ' || *l == '\t' || *l == '\r')) + --l; + + if (f != of || l != ol) + line = f <= l ? string (f, l - f + 1) : string (); + } + + // Ignore empty lines, those that start with #. + // + if (line.empty () || line[0] == '#') + continue; + + string::size_type p (string::npos); + if (line.compare (0, 1, "-") == 0) + { + p = line.find (' '); + + string::size_type q (line.find ('=')); + if (q != string::npos && q < p) + p = q; + } + + string s1; + if (p != string::npos) + { + s1.assign (line, 0, p); + + // Skip leading whitespaces in the argument. + // + if (line[p] == '=') + ++p; + else + { + n = line.size (); + for (++p; p < n; ++p) + { + char c (line[p]); + if (c != ' ' && c != '\t' && c != '\r') + break; + } + } + } + else if (!skip_) + skip_ = (line == "--"); + + string s2 (line, p != string::npos ? p : 0); + + // If the string (which is an option value or argument) is + // wrapped in quotes, remove them. + // + n = s2.size (); + char cf (s2[0]), cl (s2[n - 1]); + + if (cf == '"' || cf == '\'' || cl == '"' || cl == '\'') + { + if (n == 1 || cf != cl) + throw unmatched_quote (s2); + + s2 = string (s2, 1, n - 2); + } + + if (!s1.empty ()) + { + // See if this is another file option. + // + const option_info* oi; + if (!skip_ && (oi = find (s1.c_str ()))) + { + if (s2.empty ()) + throw missing_value (oi->option); + + if (oi->search_func != 0) + { + string f (oi->search_func (s2.c_str (), oi->arg)); + if (!f.empty ()) + load (f); + } + else + { + // If the path of the file being parsed is not simple and the + // path of the file that needs to be loaded is relative, then + // complete the latter using the former as a base. + // +#ifndef _WIN32 + string::size_type p (file.find_last_of ('/')); + bool c (p != string::npos && s2[0] != '/'); +#else + string::size_type p (file.find_last_of ("/\\")); + bool c (p != string::npos && s2[1] != ':'); +#endif + if (c) + s2.insert (0, file, 0, p + 1); + + load (s2); + } + + continue; + } + + a.value = s1; + args_.push_back (a); + } + + a.value = s2; + args_.push_back (a); + } + } + + template <typename X> + struct parser + { + static void + parse (X& x, bool& xs, scanner& s) + { + using namespace std; + + const char* o (s.next ()); + if (s.more ()) + { + string v (s.next ()); + istringstream is (v); + if (!(is >> x && is.peek () == istringstream::traits_type::eof ())) + throw invalid_value (o, v); + } + else + throw missing_value (o); + + xs = true; + } + }; + + template <> + struct parser<bool> + { + static void + parse (bool& x, bool& xs, scanner& s) + { + const char* o (s.next ()); + + if (s.more ()) + { + const char* v (s.next ()); + + if (std::strcmp (v, "1") == 0 || + std::strcmp (v, "true") == 0 || + std::strcmp (v, "TRUE") == 0 || + std::strcmp (v, "True") == 0) + x = true; + else if (std::strcmp (v, "0") == 0 || + std::strcmp (v, "false") == 0 || + std::strcmp (v, "FALSE") == 0 || + std::strcmp (v, "False") == 0) + x = false; + else + throw invalid_value (o, v); + } + else + throw missing_value (o); + + xs = true; + } + }; + + template <> + struct parser<std::string> + { + static void + parse (std::string& x, bool& xs, scanner& s) + { + const char* o (s.next ()); + + if (s.more ()) + x = s.next (); + else + throw missing_value (o); + + xs = true; + } + }; + + template <typename X> + struct parser<std::pair<X, std::size_t> > + { + static void + parse (std::pair<X, std::size_t>& x, bool& xs, scanner& s) + { + x.second = s.position (); + parser<X>::parse (x.first, xs, s); + } + }; + + template <typename X> + struct parser<std::vector<X> > + { + static void + parse (std::vector<X>& c, bool& xs, scanner& s) + { + X x; + bool dummy; + parser<X>::parse (x, dummy, s); + c.push_back (x); + xs = true; + } + }; + + template <typename X, typename C> + struct parser<std::set<X, C> > + { + static void + parse (std::set<X, C>& c, bool& xs, scanner& s) + { + X x; + bool dummy; + parser<X>::parse (x, dummy, s); + c.insert (x); + xs = true; + } + }; + + template <typename K, typename V, typename C> + struct parser<std::map<K, V, C> > + { + static void + parse (std::map<K, V, C>& m, bool& xs, scanner& s) + { + const char* o (s.next ()); + + if (s.more ()) + { + std::size_t pos (s.position ()); + std::string ov (s.next ()); + std::string::size_type p = ov.find ('='); + + K k = K (); + V v = V (); + std::string kstr (ov, 0, p); + std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ())); + + int ac (2); + char* av[] = + { + const_cast<char*> (o), + 0 + }; + + bool dummy; + if (!kstr.empty ()) + { + av[1] = const_cast<char*> (kstr.c_str ()); + argv_scanner s (0, ac, av, false, pos); + parser<K>::parse (k, dummy, s); + } + + if (!vstr.empty ()) + { + av[1] = const_cast<char*> (vstr.c_str ()); + argv_scanner s (0, ac, av, false, pos); + parser<V>::parse (v, dummy, s); + } + + m[k] = v; + } + else + throw missing_value (o); + + xs = true; + } + }; + + template <typename K, typename V, typename C> + struct parser<std::multimap<K, V, C> > + { + static void + parse (std::multimap<K, V, C>& m, bool& xs, scanner& s) + { + const char* o (s.next ()); + + if (s.more ()) + { + std::size_t pos (s.position ()); + std::string ov (s.next ()); + std::string::size_type p = ov.find ('='); + + K k = K (); + V v = V (); + std::string kstr (ov, 0, p); + std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ())); + + int ac (2); + char* av[] = + { + const_cast<char*> (o), + 0 + }; + + bool dummy; + if (!kstr.empty ()) + { + av[1] = const_cast<char*> (kstr.c_str ()); + argv_scanner s (0, ac, av, false, pos); + parser<K>::parse (k, dummy, s); + } + + if (!vstr.empty ()) + { + av[1] = const_cast<char*> (vstr.c_str ()); + argv_scanner s (0, ac, av, false, pos); + parser<V>::parse (v, dummy, s); + } + + m.insert (typename std::multimap<K, V, C>::value_type (k, v)); + } + else + throw missing_value (o); + + xs = true; + } + }; + + template <typename X, typename T, T X::*M> + void + thunk (X& x, scanner& s) + { + parser<T>::parse (x.*M, s); + } + + template <typename X, bool X::*M> + void + thunk (X& x, scanner& s) + { + s.next (); + x.*M = true; + } + + template <typename X, typename T, T X::*M, bool X::*S> + void + thunk (X& x, scanner& s) + { + parser<T>::parse (x.*M, x.*S, s); + } + } + } + } +} + +#include <map> + +namespace odb +{ + namespace oracle + { + namespace details + { + // options + // + + options:: + options () + : user_ (), + user_specified_ (false), + password_ (), + password_specified_ (false), + database_ (), + database_specified_ (false), + service_ (), + service_specified_ (false), + host_ (), + host_specified_ (false), + port_ (), + port_specified_ (false), + options_file_ (), + options_file_specified_ (false) + { + } + + options:: + options (int& argc, + char** argv, + bool erase, + ::odb::oracle::details::cli::unknown_mode opt, + ::odb::oracle::details::cli::unknown_mode arg) + : user_ (), + user_specified_ (false), + password_ (), + password_specified_ (false), + database_ (), + database_specified_ (false), + service_ (), + service_specified_ (false), + host_ (), + host_specified_ (false), + port_ (), + port_specified_ (false), + options_file_ (), + options_file_specified_ (false) + { + ::odb::oracle::details::cli::argv_scanner s (argc, argv, erase); + _parse (s, opt, arg); + } + + options:: + options (int start, + int& argc, + char** argv, + bool erase, + ::odb::oracle::details::cli::unknown_mode opt, + ::odb::oracle::details::cli::unknown_mode arg) + : user_ (), + user_specified_ (false), + password_ (), + password_specified_ (false), + database_ (), + database_specified_ (false), + service_ (), + service_specified_ (false), + host_ (), + host_specified_ (false), + port_ (), + port_specified_ (false), + options_file_ (), + options_file_specified_ (false) + { + ::odb::oracle::details::cli::argv_scanner s (start, argc, argv, erase); + _parse (s, opt, arg); + } + + options:: + options (int& argc, + char** argv, + int& end, + bool erase, + ::odb::oracle::details::cli::unknown_mode opt, + ::odb::oracle::details::cli::unknown_mode arg) + : user_ (), + user_specified_ (false), + password_ (), + password_specified_ (false), + database_ (), + database_specified_ (false), + service_ (), + service_specified_ (false), + host_ (), + host_specified_ (false), + port_ (), + port_specified_ (false), + options_file_ (), + options_file_specified_ (false) + { + ::odb::oracle::details::cli::argv_scanner s (argc, argv, erase); + _parse (s, opt, arg); + end = s.end (); + } + + options:: + options (int start, + int& argc, + char** argv, + int& end, + bool erase, + ::odb::oracle::details::cli::unknown_mode opt, + ::odb::oracle::details::cli::unknown_mode arg) + : user_ (), + user_specified_ (false), + password_ (), + password_specified_ (false), + database_ (), + database_specified_ (false), + service_ (), + service_specified_ (false), + host_ (), + host_specified_ (false), + port_ (), + port_specified_ (false), + options_file_ (), + options_file_specified_ (false) + { + ::odb::oracle::details::cli::argv_scanner s (start, argc, argv, erase); + _parse (s, opt, arg); + end = s.end (); + } + + options:: + options (::odb::oracle::details::cli::scanner& s, + ::odb::oracle::details::cli::unknown_mode opt, + ::odb::oracle::details::cli::unknown_mode arg) + : user_ (), + user_specified_ (false), + password_ (), + password_specified_ (false), + database_ (), + database_specified_ (false), + service_ (), + service_specified_ (false), + host_ (), + host_specified_ (false), + port_ (), + port_specified_ (false), + options_file_ (), + options_file_specified_ (false) + { + _parse (s, opt, arg); + } + + ::odb::oracle::details::cli::usage_para options:: + print_usage (::std::ostream& os, ::odb::oracle::details::cli::usage_para p) + { + CLI_POTENTIALLY_UNUSED (os); + + if (p != ::odb::oracle::details::cli::usage_para::none) + os << ::std::endl; + + os << "--user <name> Oracle database user." << ::std::endl; + + os << std::endl + << "--password <str> Oracle database password." << ::std::endl; + + os << std::endl + << "--database <conn-id> Oracle connect identifier." << ::std::endl; + + os << std::endl + << "--service <name> Oracle service name." << ::std::endl; + + os << std::endl + << "--host <str> Oracle database host name or address (localhost by" << ::std::endl + << " default)." << ::std::endl; + + os << std::endl + << "--port <integer> Oracle database port number." << ::std::endl; + + os << std::endl + << "--options-file <file> Read additional options from <file>. Each option should" << ::std::endl + << " appear on a separate line optionally followed by space or" << ::std::endl + << " equal sign (=) and an option value. Empty lines and lines" << ::std::endl + << " starting with # are ignored." << ::std::endl; + + p = ::odb::oracle::details::cli::usage_para::option; + + return p; + } + + typedef + std::map<std::string, void (*) (options&, ::odb::oracle::details::cli::scanner&)> + _cli_options_map; + + static _cli_options_map _cli_options_map_; + + struct _cli_options_map_init + { + _cli_options_map_init () + { + _cli_options_map_["--user"] = + &::odb::oracle::details::cli::thunk< options, std::string, &options::user_, + &options::user_specified_ >; + _cli_options_map_["--password"] = + &::odb::oracle::details::cli::thunk< options, std::string, &options::password_, + &options::password_specified_ >; + _cli_options_map_["--database"] = + &::odb::oracle::details::cli::thunk< options, std::string, &options::database_, + &options::database_specified_ >; + _cli_options_map_["--service"] = + &::odb::oracle::details::cli::thunk< options, std::string, &options::service_, + &options::service_specified_ >; + _cli_options_map_["--host"] = + &::odb::oracle::details::cli::thunk< options, std::string, &options::host_, + &options::host_specified_ >; + _cli_options_map_["--port"] = + &::odb::oracle::details::cli::thunk< options, unsigned int, &options::port_, + &options::port_specified_ >; + _cli_options_map_["--options-file"] = + &::odb::oracle::details::cli::thunk< options, std::string, &options::options_file_, + &options::options_file_specified_ >; + } + }; + + static _cli_options_map_init _cli_options_map_init_; + + bool options:: + _parse (const char* o, ::odb::oracle::details::cli::scanner& s) + { + _cli_options_map::const_iterator i (_cli_options_map_.find (o)); + + if (i != _cli_options_map_.end ()) + { + (*(i->second)) (*this, s); + return true; + } + + return false; + } + + bool options:: + _parse (::odb::oracle::details::cli::scanner& s, + ::odb::oracle::details::cli::unknown_mode opt_mode, + ::odb::oracle::details::cli::unknown_mode arg_mode) + { + bool r = false; + bool opt = true; + + while (s.more ()) + { + const char* o = s.peek (); + + if (std::strcmp (o, "--") == 0) + { + opt = false; + s.skip (); + r = true; + continue; + } + + if (opt) + { + if (_parse (o, s)) + { + r = true; + continue; + } + + if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0') + { + // Handle combined option values. + // + std::string co; + if (const char* v = std::strchr (o, '=')) + { + co.assign (o, 0, v - o); + ++v; + + int ac (2); + char* av[] = + { + const_cast<char*> (co.c_str ()), + const_cast<char*> (v) + }; + + ::odb::oracle::details::cli::argv_scanner ns (0, ac, av); + + if (_parse (co.c_str (), ns)) + { + // Parsed the option but not its value? + // + if (ns.end () != 2) + throw ::odb::oracle::details::cli::invalid_value (co, v); + + s.next (); + r = true; + continue; + } + else + { + // Set the unknown option and fall through. + // + o = co.c_str (); + } + } + + switch (opt_mode) + { + case ::odb::oracle::details::cli::unknown_mode::skip: + { + s.skip (); + r = true; + continue; + } + case ::odb::oracle::details::cli::unknown_mode::stop: + { + break; + } + case ::odb::oracle::details::cli::unknown_mode::fail: + { + throw ::odb::oracle::details::cli::unknown_option (o); + } + } + + break; + } + } + + switch (arg_mode) + { + case ::odb::oracle::details::cli::unknown_mode::skip: + { + s.skip (); + r = true; + continue; + } + case ::odb::oracle::details::cli::unknown_mode::stop: + { + break; + } + case ::odb::oracle::details::cli::unknown_mode::fail: + { + throw ::odb::oracle::details::cli::unknown_argument (o); + } + } + + break; + } + + return r; + } + } + } +} + +// Begin epilogue. +// +// +// End epilogue. + diff --git a/libodb-oracle/odb/oracle/details/pregenerated/odb/oracle/details/options.hxx b/libodb-oracle/odb/oracle/details/pregenerated/odb/oracle/details/options.hxx new file mode 100644 index 0000000..285c906 --- /dev/null +++ b/libodb-oracle/odb/oracle/details/pregenerated/odb/oracle/details/options.hxx @@ -0,0 +1,570 @@ +// -*- C++ -*- +// +// This file was generated by CLI, a command line interface +// compiler for C++. +// + +#ifndef LIBODB_ORACLE_DETAILS_OPTIONS_HXX +#define LIBODB_ORACLE_DETAILS_OPTIONS_HXX + +// Begin prologue. +// +// +// End prologue. + +#include <list> +#include <deque> +#include <iosfwd> +#include <string> +#include <cstddef> +#include <exception> + +#ifndef CLI_POTENTIALLY_UNUSED +# if defined(_MSC_VER) || defined(__xlC__) +# define CLI_POTENTIALLY_UNUSED(x) (void*)&x +# else +# define CLI_POTENTIALLY_UNUSED(x) (void)x +# endif +#endif + +namespace odb +{ + namespace oracle + { + namespace details + { + namespace cli + { + class usage_para + { + public: + enum value + { + none, + text, + option + }; + + usage_para (value); + + operator value () const + { + return v_; + } + + private: + value v_; + }; + + class unknown_mode + { + public: + enum value + { + skip, + stop, + fail + }; + + unknown_mode (value); + + operator value () const + { + return v_; + } + + private: + value v_; + }; + + // Exceptions. + // + + class exception: public std::exception + { + public: + virtual void + print (::std::ostream&) const = 0; + }; + + ::std::ostream& + operator<< (::std::ostream&, const exception&); + + class unknown_option: public exception + { + public: + virtual + ~unknown_option () throw (); + + unknown_option (const std::string& option); + + const std::string& + option () const; + + virtual void + print (::std::ostream&) const; + + virtual const char* + what () const throw (); + + private: + std::string option_; + }; + + class unknown_argument: public exception + { + public: + virtual + ~unknown_argument () throw (); + + unknown_argument (const std::string& argument); + + const std::string& + argument () const; + + virtual void + print (::std::ostream&) const; + + virtual const char* + what () const throw (); + + private: + std::string argument_; + }; + + class missing_value: public exception + { + public: + virtual + ~missing_value () throw (); + + missing_value (const std::string& option); + + const std::string& + option () const; + + virtual void + print (::std::ostream&) const; + + virtual const char* + what () const throw (); + + private: + std::string option_; + }; + + class invalid_value: public exception + { + public: + virtual + ~invalid_value () throw (); + + invalid_value (const std::string& option, + const std::string& value, + const std::string& message = std::string ()); + + const std::string& + option () const; + + const std::string& + value () const; + + const std::string& + message () const; + + virtual void + print (::std::ostream&) const; + + virtual const char* + what () const throw (); + + private: + std::string option_; + std::string value_; + std::string message_; + }; + + class eos_reached: public exception + { + public: + virtual void + print (::std::ostream&) const; + + virtual const char* + what () const throw (); + }; + + class file_io_failure: public exception + { + public: + virtual + ~file_io_failure () throw (); + + file_io_failure (const std::string& file); + + const std::string& + file () const; + + virtual void + print (::std::ostream&) const; + + virtual const char* + what () const throw (); + + private: + std::string file_; + }; + + class unmatched_quote: public exception + { + public: + virtual + ~unmatched_quote () throw (); + + unmatched_quote (const std::string& argument); + + const std::string& + argument () const; + + virtual void + print (::std::ostream&) const; + + virtual const char* + what () const throw (); + + private: + std::string argument_; + }; + + // Command line argument scanner interface. + // + // The values returned by next() are guaranteed to be valid + // for the two previous arguments up until a call to a third + // peek() or next(). + // + // The position() function returns a monotonically-increasing + // number which, if stored, can later be used to determine the + // relative position of the argument returned by the following + // call to next(). Note that if multiple scanners are used to + // extract arguments from multiple sources, then the end + // position of the previous scanner should be used as the + // start position of the next. + // + class scanner + { + public: + virtual + ~scanner (); + + virtual bool + more () = 0; + + virtual const char* + peek () = 0; + + virtual const char* + next () = 0; + + virtual void + skip () = 0; + + virtual std::size_t + position () = 0; + }; + + class argv_scanner: public scanner + { + public: + argv_scanner (int& argc, + char** argv, + bool erase = false, + std::size_t start_position = 0); + + argv_scanner (int start, + int& argc, + char** argv, + bool erase = false, + std::size_t start_position = 0); + + int + end () const; + + virtual bool + more (); + + virtual const char* + peek (); + + virtual const char* + next (); + + virtual void + skip (); + + virtual std::size_t + position (); + + protected: + std::size_t start_position_; + int i_; + int& argc_; + char** argv_; + bool erase_; + }; + + class argv_file_scanner: public argv_scanner + { + public: + argv_file_scanner (int& argc, + char** argv, + const std::string& option, + bool erase = false, + std::size_t start_position = 0); + + argv_file_scanner (int start, + int& argc, + char** argv, + const std::string& option, + bool erase = false, + std::size_t start_position = 0); + + argv_file_scanner (const std::string& file, + const std::string& option, + std::size_t start_position = 0); + + struct option_info + { + // If search_func is not NULL, it is called, with the arg + // value as the second argument, to locate the options file. + // If it returns an empty string, then the file is ignored. + // + const char* option; + std::string (*search_func) (const char*, void* arg); + void* arg; + }; + + argv_file_scanner (int& argc, + char** argv, + const option_info* options, + std::size_t options_count, + bool erase = false, + std::size_t start_position = 0); + + argv_file_scanner (int start, + int& argc, + char** argv, + const option_info* options, + std::size_t options_count, + bool erase = false, + std::size_t start_position = 0); + + argv_file_scanner (const std::string& file, + const option_info* options = 0, + std::size_t options_count = 0, + std::size_t start_position = 0); + + virtual bool + more (); + + virtual const char* + peek (); + + virtual const char* + next (); + + virtual void + skip (); + + virtual std::size_t + position (); + + // Return the file path if the peeked at argument came from a file and + // the empty string otherwise. The reference is guaranteed to be valid + // till the end of the scanner lifetime. + // + const std::string& + peek_file (); + + // Return the 1-based line number if the peeked at argument came from + // a file and zero otherwise. + // + std::size_t + peek_line (); + + private: + const option_info* + find (const char*) const; + + void + load (const std::string& file); + + typedef argv_scanner base; + + const std::string option_; + option_info option_info_; + const option_info* options_; + std::size_t options_count_; + + struct arg + { + std::string value; + const std::string* file; + std::size_t line; + }; + + std::deque<arg> args_; + std::list<std::string> files_; + + // Circular buffer of two arguments. + // + std::string hold_[2]; + std::size_t i_; + + bool skip_; + + static int zero_argc_; + static std::string empty_string_; + }; + + template <typename X> + struct parser; + } + } + } +} + +#include <string> + +namespace odb +{ + namespace oracle + { + namespace details + { + class options + { + public: + options (); + + options (int& argc, + char** argv, + bool erase = false, + ::odb::oracle::details::cli::unknown_mode option = ::odb::oracle::details::cli::unknown_mode::fail, + ::odb::oracle::details::cli::unknown_mode argument = ::odb::oracle::details::cli::unknown_mode::stop); + + options (int start, + int& argc, + char** argv, + bool erase = false, + ::odb::oracle::details::cli::unknown_mode option = ::odb::oracle::details::cli::unknown_mode::fail, + ::odb::oracle::details::cli::unknown_mode argument = ::odb::oracle::details::cli::unknown_mode::stop); + + options (int& argc, + char** argv, + int& end, + bool erase = false, + ::odb::oracle::details::cli::unknown_mode option = ::odb::oracle::details::cli::unknown_mode::fail, + ::odb::oracle::details::cli::unknown_mode argument = ::odb::oracle::details::cli::unknown_mode::stop); + + options (int start, + int& argc, + char** argv, + int& end, + bool erase = false, + ::odb::oracle::details::cli::unknown_mode option = ::odb::oracle::details::cli::unknown_mode::fail, + ::odb::oracle::details::cli::unknown_mode argument = ::odb::oracle::details::cli::unknown_mode::stop); + + options (::odb::oracle::details::cli::scanner&, + ::odb::oracle::details::cli::unknown_mode option = ::odb::oracle::details::cli::unknown_mode::fail, + ::odb::oracle::details::cli::unknown_mode argument = ::odb::oracle::details::cli::unknown_mode::stop); + + // Option accessors. + // + const std::string& + user () const; + + bool + user_specified () const; + + const std::string& + password () const; + + bool + password_specified () const; + + const std::string& + database () const; + + bool + database_specified () const; + + const std::string& + service () const; + + bool + service_specified () const; + + const std::string& + host () const; + + bool + host_specified () const; + + const unsigned int& + port () const; + + bool + port_specified () const; + + const std::string& + options_file () const; + + bool + options_file_specified () const; + + // Print usage information. + // + static ::odb::oracle::details::cli::usage_para + print_usage (::std::ostream&, + ::odb::oracle::details::cli::usage_para = ::odb::oracle::details::cli::usage_para::none); + + // Implementation details. + // + protected: + bool + _parse (const char*, ::odb::oracle::details::cli::scanner&); + + private: + bool + _parse (::odb::oracle::details::cli::scanner&, + ::odb::oracle::details::cli::unknown_mode option, + ::odb::oracle::details::cli::unknown_mode argument); + + public: + std::string user_; + bool user_specified_; + std::string password_; + bool password_specified_; + std::string database_; + bool database_specified_; + std::string service_; + bool service_specified_; + std::string host_; + bool host_specified_; + unsigned int port_; + bool port_specified_; + std::string options_file_; + bool options_file_specified_; + }; + } + } +} + +#include <odb/oracle/details/options.ixx> + +// Begin epilogue. +// +// +// End epilogue. + +#endif // LIBODB_ORACLE_DETAILS_OPTIONS_HXX diff --git a/libodb-oracle/odb/oracle/details/pregenerated/odb/oracle/details/options.ixx b/libodb-oracle/odb/oracle/details/pregenerated/odb/oracle/details/options.ixx new file mode 100644 index 0000000..a69d602 --- /dev/null +++ b/libodb-oracle/odb/oracle/details/pregenerated/odb/oracle/details/options.ixx @@ -0,0 +1,384 @@ +// -*- C++ -*- +// +// This file was generated by CLI, a command line interface +// compiler for C++. +// + +// Begin prologue. +// +// +// End prologue. + +#include <cassert> + +namespace odb +{ + namespace oracle + { + namespace details + { + namespace cli + { + // usage_para + // + inline usage_para:: + usage_para (value v) + : v_ (v) + { + } + + // unknown_mode + // + inline unknown_mode:: + unknown_mode (value v) + : v_ (v) + { + } + + // exception + // + inline ::std::ostream& + operator<< (::std::ostream& os, const exception& e) + { + e.print (os); + return os; + } + + // unknown_option + // + inline unknown_option:: + unknown_option (const std::string& option) + : option_ (option) + { + } + + inline const std::string& unknown_option:: + option () const + { + return option_; + } + + // unknown_argument + // + inline unknown_argument:: + unknown_argument (const std::string& argument) + : argument_ (argument) + { + } + + inline const std::string& unknown_argument:: + argument () const + { + return argument_; + } + + // missing_value + // + inline missing_value:: + missing_value (const std::string& option) + : option_ (option) + { + } + + inline const std::string& missing_value:: + option () const + { + return option_; + } + + // invalid_value + // + inline invalid_value:: + invalid_value (const std::string& option, + const std::string& value, + const std::string& message) + : option_ (option), + value_ (value), + message_ (message) + { + } + + inline const std::string& invalid_value:: + option () const + { + return option_; + } + + inline const std::string& invalid_value:: + value () const + { + return value_; + } + + inline const std::string& invalid_value:: + message () const + { + return message_; + } + + // file_io_failure + // + inline file_io_failure:: + file_io_failure (const std::string& file) + : file_ (file) + { + } + + inline const std::string& file_io_failure:: + file () const + { + return file_; + } + + // unmatched_quote + // + inline unmatched_quote:: + unmatched_quote (const std::string& argument) + : argument_ (argument) + { + } + + inline const std::string& unmatched_quote:: + argument () const + { + return argument_; + } + + // argv_scanner + // + inline argv_scanner:: + argv_scanner (int& argc, + char** argv, + bool erase, + std::size_t sp) + : start_position_ (sp + 1), + i_ (1), + argc_ (argc), + argv_ (argv), + erase_ (erase) + { + } + + inline argv_scanner:: + argv_scanner (int start, + int& argc, + char** argv, + bool erase, + std::size_t sp) + : start_position_ (sp + static_cast<std::size_t> (start)), + i_ (start), + argc_ (argc), + argv_ (argv), + erase_ (erase) + { + } + + inline int argv_scanner:: + end () const + { + return i_; + } + + // argv_file_scanner + // + inline argv_file_scanner:: + argv_file_scanner (int& argc, + char** argv, + const std::string& option, + bool erase, + std::size_t sp) + : argv_scanner (argc, argv, erase, sp), + option_ (option), + options_ (&option_info_), + options_count_ (1), + i_ (1), + skip_ (false) + { + option_info_.option = option_.c_str (); + option_info_.search_func = 0; + } + + inline argv_file_scanner:: + argv_file_scanner (int start, + int& argc, + char** argv, + const std::string& option, + bool erase, + std::size_t sp) + : argv_scanner (start, argc, argv, erase, sp), + option_ (option), + options_ (&option_info_), + options_count_ (1), + i_ (1), + skip_ (false) + { + option_info_.option = option_.c_str (); + option_info_.search_func = 0; + } + + inline argv_file_scanner:: + argv_file_scanner (const std::string& file, + const std::string& option, + std::size_t sp) + : argv_scanner (0, zero_argc_, 0, sp), + option_ (option), + options_ (&option_info_), + options_count_ (1), + i_ (1), + skip_ (false) + { + option_info_.option = option_.c_str (); + option_info_.search_func = 0; + + load (file); + } + + inline argv_file_scanner:: + argv_file_scanner (int& argc, + char** argv, + const option_info* options, + std::size_t options_count, + bool erase, + std::size_t sp) + : argv_scanner (argc, argv, erase, sp), + options_ (options), + options_count_ (options_count), + i_ (1), + skip_ (false) + { + } + + inline argv_file_scanner:: + argv_file_scanner (int start, + int& argc, + char** argv, + const option_info* options, + std::size_t options_count, + bool erase, + std::size_t sp) + : argv_scanner (start, argc, argv, erase, sp), + options_ (options), + options_count_ (options_count), + i_ (1), + skip_ (false) + { + } + + inline argv_file_scanner:: + argv_file_scanner (const std::string& file, + const option_info* options, + std::size_t options_count, + std::size_t sp) + : argv_scanner (0, zero_argc_, 0, sp), + options_ (options), + options_count_ (options_count), + i_ (1), + skip_ (false) + { + load (file); + } + } + } + } +} + +namespace odb +{ + namespace oracle + { + namespace details + { + // options + // + + inline const std::string& options:: + user () const + { + return this->user_; + } + + inline bool options:: + user_specified () const + { + return this->user_specified_; + } + + inline const std::string& options:: + password () const + { + return this->password_; + } + + inline bool options:: + password_specified () const + { + return this->password_specified_; + } + + inline const std::string& options:: + database () const + { + return this->database_; + } + + inline bool options:: + database_specified () const + { + return this->database_specified_; + } + + inline const std::string& options:: + service () const + { + return this->service_; + } + + inline bool options:: + service_specified () const + { + return this->service_specified_; + } + + inline const std::string& options:: + host () const + { + return this->host_; + } + + inline bool options:: + host_specified () const + { + return this->host_specified_; + } + + inline const unsigned int& options:: + port () const + { + return this->port_; + } + + inline bool options:: + port_specified () const + { + return this->port_specified_; + } + + inline const std::string& options:: + options_file () const + { + return this->options_file_; + } + + inline bool options:: + options_file_specified () const + { + return this->options_file_specified_; + } + } + } +} + +// Begin epilogue. +// +// +// End epilogue. diff --git a/libodb-oracle/odb/oracle/error.cxx b/libodb-oracle/odb/oracle/error.cxx new file mode 100644 index 0000000..3c9a7df --- /dev/null +++ b/libodb-oracle/odb/oracle/error.cxx @@ -0,0 +1,225 @@ +// file : odb/oracle/error.cxx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <oci.h> + +#include <cstring> // std::strlen +#include <cassert> + +#include <odb/details/buffer.hxx> + +#include <odb/oracle/error.hxx> +#include <odb/oracle/exceptions.hxx> +#include <odb/oracle/connection.hxx> + +using namespace std; + +namespace odb +{ + namespace oracle + { + static void + translate_error (void* h, ub4 htype, sword r, connection* conn, + size_t pos, multiple_exceptions* mex) + { + assert (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO); + + switch (r) + { + case OCI_STILL_EXECUTING: + { + throw database_exception (0, "statement still executing"); + break; + } + case OCI_NEED_DATA: + case OCI_NO_DATA: + { + throw database_exception (0, "unhandled OCI_*_DATA condition"); + break; + } + case OCI_INVALID_HANDLE: + { + throw invalid_oci_handle (); + break; + } + default: + { + break; + } + } + + sb4 e; + char b[512]; // Error message will be truncated if it does not fit. + + if (htype == OCI_HTYPE_ERROR) + { + // We need to translate certain Oracle error codes to special + // exceptions, such as deadlock, timeout, etc. The problem is we can + // have multiple records potentially with different error codes. If we + // have both, say, a deadlock code and some other code, then we should + // probably throw database_exception, which is more severe. To + // implement this we are going to pre-scan the records looking for the + // codes we are interested in. If in the process we see any other code, + // then we stop and go ahead to prepare and throw database_exception. + // + enum code + { + code_none, + code_deadlock, + code_timeout, + code_connection_lost + }; + + code c (code_none); + + for (sb4 i (1);; ++i) + { + r = OCIErrorGet (h, + i, + 0, + &e, + reinterpret_cast<OraText*> (b), + 512, + htype); + + if (r == OCI_NO_DATA) + break; + + code nc; + + if (e == 60 || // Deadlock detected while waiting for resource. + e == 104) // Deadlock detected; all public servers blocked. + nc = code_deadlock; + else if (e == 51 || // Timeout occurred while waiting for a resource. + e == 54 || // Resource busy and acquisition timeout expired. + e == 2049) // Distributed lock timeout. + nc = code_timeout; + else if (e == 28 || // Session has been killed. + e == 3113 || // End-of-file on communication channel. + e == 3135 || // Connection lost contact. + e == 3136 || // Inbound connection timed out. + e == 3138) // Connection terminated. + nc = code_connection_lost; + else + { + c = code_none; + break; + } + + if (c != code_none && c != nc) + { + // Several different codes. + // + c = code_none; + break; + } + + c = nc; + } + + // Check if the connection is lost. If code is connection_lost, + // then we know it is gone. If code is deadlock, then the + // connection is most likely ok. + // + if (conn != 0 && (c == code_none || c == code_timeout)) + { + OCIServer* server; + r = OCIAttrGet (conn->handle (), + OCI_HTYPE_SVCCTX, + &server, + 0, + OCI_ATTR_SERVER, + conn->error_handle ()); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + ub4 server_status; + r = OCIAttrGet (server, + OCI_HTYPE_SERVER, + &server_status, + 0, + OCI_ATTR_SERVER_STATUS, + conn->error_handle ()); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + if (server_status == OCI_SERVER_NOT_CONNECTED) + conn->mark_failed (); + } + + switch (c) + { + case code_deadlock: + throw deadlock (); + case code_timeout: + throw timeout (); + case code_connection_lost: + { + if (conn != 0) + conn->mark_failed (); + + throw connection_lost (); + } + case code_none: + break; + } + } + + // Some other error code. Prepare database_exception. + // + database_exception dbe; + + for (sb4 i (1);; ++i) + { + r = OCIErrorGet (h, + i, + 0, + &e, + reinterpret_cast<OraText*> (b), + 512, + htype); + + if (r == OCI_NO_DATA) + break; + + // Get rid of a trailing newline if there is one. + // + size_t n (strlen (b)); + if (n != 0 && b[n - 1] == '\n') + b[n - 1] = '\0'; + + dbe.append (e, b); + } + + if (mex == 0) + throw dbe; + else + // It could be that some of these errors are fatal. I guess we + // will just have to learn from experience which ones are. The + // client code can always treat specific error codes as fatal. + // + mex->insert (pos, dbe); + } + + void + translate_error (OCIError* h, sword r, connection* c, + size_t pos, multiple_exceptions* mex) + { + translate_error (h, OCI_HTYPE_ERROR, r, c, pos, mex); + } + + void + translate_error (connection& c, sword r) + { + translate_error (c.error_handle (), OCI_HTYPE_ERROR, r, &c, 0, 0); + } + + void + translate_error (OCIEnv* h) + { + translate_error (h, OCI_HTYPE_ENV, OCI_ERROR, 0, 0, 0); + } + } +} diff --git a/libodb-oracle/odb/oracle/error.hxx b/libodb-oracle/odb/oracle/error.hxx new file mode 100644 index 0000000..50092c8 --- /dev/null +++ b/libodb-oracle/odb/oracle/error.hxx @@ -0,0 +1,41 @@ +// file : odb/oracle/error.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_ERROR_HXX +#define ODB_ORACLE_ERROR_HXX + +#include <odb/pre.hxx> + +#include <cstddef> // std::size_t + +#include <odb/oracle/oracle-fwd.hxx> +#include <odb/oracle/forward.hxx> // connection, multiple_exceptions +#include <odb/oracle/version.hxx> + +#include <odb/oracle/details/export.hxx> + +namespace odb +{ + namespace oracle + { + // Translate OCI error given an error handle and throw (or return, + // in case multiple_exceptions is not NULL) an appropriate exception. + // + LIBODB_ORACLE_EXPORT void + translate_error (OCIError*, sword result, connection* = 0, + std::size_t pos = 0, multiple_exceptions* = 0); + + LIBODB_ORACLE_EXPORT void + translate_error (connection&, sword result); + + // Translate an OCI error given an environment handle error and throw + // an appropriate exception. + // + LIBODB_ORACLE_EXPORT void + translate_error (OCIEnv*); + } +} + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_ERROR_HXX diff --git a/libodb-oracle/odb/oracle/exceptions.cxx b/libodb-oracle/odb/oracle/exceptions.cxx new file mode 100644 index 0000000..532306e --- /dev/null +++ b/libodb-oracle/odb/oracle/exceptions.cxx @@ -0,0 +1,124 @@ +// file : odb/oracle/exceptions.cxx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <sstream> + +#include <odb/oracle/exceptions.hxx> + +using namespace std; + +namespace odb +{ + namespace oracle + { + // + // database_exception + // + + database_exception::record:: + record (sb4 e, const string& m) + : error_ (e), message_ (m) + { + } + + database_exception:: + ~database_exception () ODB_NOTHROW_NOEXCEPT + { + } + + database_exception:: + database_exception () + { + } + + database_exception:: + database_exception (sb4 e, const string& m) + { + append (e, m); + } + + void database_exception:: + append (sb4 e, const string& m) + { + records_.push_back (record (e, m)); + + if (!what_.empty ()) + what_ += '\n'; + + ostringstream ostr; + ostr << e << ": " << m; + what_ += ostr.str (); + } + + const char* database_exception:: + what () const ODB_NOTHROW_NOEXCEPT + { + return what_.c_str (); + } + + database_exception* database_exception:: + clone () const + { + return new database_exception (*this); + } + + // + // lob_comparison + // + + const char* lob_comparison:: + what () const ODB_NOTHROW_NOEXCEPT + { + return "comparison of LOB values in queries not supported"; + } + + lob_comparison* lob_comparison:: + clone () const + { + return new lob_comparison (*this); + } + + // + // cli_exception + // + + cli_exception:: + cli_exception (const string& what) + : what_ (what) + { + } + + cli_exception:: + ~cli_exception () ODB_NOTHROW_NOEXCEPT + { + } + + const char* cli_exception:: + what () const ODB_NOTHROW_NOEXCEPT + { + return what_.c_str (); + } + + cli_exception* cli_exception:: + clone () const + { + return new cli_exception (*this); + } + + // + // invalid_oci_handle + // + + const char* invalid_oci_handle:: + what () const ODB_NOTHROW_NOEXCEPT + { + return "invalid oci handle passed or unable to allocate handle"; + } + + invalid_oci_handle* invalid_oci_handle:: + clone () const + { + return new invalid_oci_handle (*this); + } + } +} diff --git a/libodb-oracle/odb/oracle/exceptions.hxx b/libodb-oracle/odb/oracle/exceptions.hxx new file mode 100644 index 0000000..d21b742 --- /dev/null +++ b/libodb-oracle/odb/oracle/exceptions.hxx @@ -0,0 +1,135 @@ +// file : odb/oracle/exceptions.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_EXCEPTIONS_HXX +#define ODB_ORACLE_EXCEPTIONS_HXX + +#include <odb/pre.hxx> + +#include <string> +#include <vector> + +#include <odb/exceptions.hxx> +#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT + +#include <odb/oracle/version.hxx> +#include <odb/oracle/forward.hxx> +#include <odb/oracle/oracle-fwd.hxx> +#include <odb/oracle/details/export.hxx> + +namespace odb +{ + namespace oracle + { + struct LIBODB_ORACLE_EXPORT database_exception: odb::database_exception + { + struct record + { + record (sb4 error, const std::string& message); + + sb4 + error () const + { + return error_; + } + + const std::string& + message () const + { + return message_; + } + + private: + sb4 error_; + std::string message_; + }; + + typedef std::vector<record> records; + + typedef records::size_type size_type; + typedef records::const_iterator iterator; + + iterator + begin () const + { + return records_.begin (); + } + + iterator + end () const + { + return records_.end (); + } + + size_type + size () const + { + return records_.size (); + } + + public: + ~database_exception () ODB_NOTHROW_NOEXCEPT; + + database_exception (); + database_exception (sb4 error, const std::string& message); + + virtual const char* + what () const ODB_NOTHROW_NOEXCEPT; + + virtual database_exception* + clone () const; + + void + append (sb4 error, const std::string& message); + + private: + records records_; + std::string what_; + }; + + struct LIBODB_ORACLE_EXPORT lob_comparison: odb::exception + { + virtual const char* + what () const ODB_NOTHROW_NOEXCEPT; + + virtual lob_comparison* + clone () const; + }; + + struct LIBODB_ORACLE_EXPORT cli_exception: odb::exception + { + cli_exception (const std::string& what); + ~cli_exception () ODB_NOTHROW_NOEXCEPT; + + virtual const char* + what () const ODB_NOTHROW_NOEXCEPT; + + virtual cli_exception* + clone () const; + + private: + std::string what_; + }; + + struct LIBODB_ORACLE_EXPORT invalid_oci_handle: odb::exception + { + virtual const char* + what () const ODB_NOTHROW_NOEXCEPT; + + virtual invalid_oci_handle* + clone () const; + }; + + namespace core + { + using oracle::database_exception; + using oracle::lob_comparison; + using oracle::cli_exception; + using oracle::invalid_oci_handle; + } + } +} + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_EXCEPTIONS_HXX diff --git a/libodb-oracle/odb/oracle/forward.hxx b/libodb-oracle/odb/oracle/forward.hxx new file mode 100644 index 0000000..ae4d3a0 --- /dev/null +++ b/libodb-oracle/odb/oracle/forward.hxx @@ -0,0 +1,92 @@ +// file : odb/oracle/forward.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_FORWARD_HXX +#define ODB_ORACLE_FORWARD_HXX + +#include <odb/pre.hxx> + +#include <odb/forward.hxx> + +namespace odb +{ + namespace oracle + { + namespace core + { + using namespace odb::common; + } + + // + // + class database; + class connection; + typedef details::shared_ptr<connection> connection_ptr; + class connection_factory; + class statement; + class transaction; + class tracer; + + namespace core + { + using oracle::database; + using oracle::connection; + using oracle::connection_ptr; + using oracle::transaction; + using oracle::statement; + } + + // Implementation details. + // + enum statement_kind + { + statement_select, + statement_insert, + statement_update, + statement_delete, + statement_generic + }; + + class binding; + class select_statement; + + template <typename T> + class object_statements; + + template <typename T> + class polymorphic_root_object_statements; + + template <typename T> + class polymorphic_derived_object_statements; + + template <typename T> + class no_id_object_statements; + + template <typename T> + class view_statements; + + template <typename T> + class container_statements; + + template <typename T> + class smart_container_statements; + + template <typename T, typename ST> + class section_statements; + + class query_base; + } + + namespace details + { + template <> + struct counter_type<oracle::connection> + { + typedef shared_base counter; + }; + } +} + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_FORWARD_HXX diff --git a/libodb-oracle/odb/oracle/no-id-object-result.hxx b/libodb-oracle/odb/oracle/no-id-object-result.hxx new file mode 100644 index 0000000..93e7e54 --- /dev/null +++ b/libodb-oracle/odb/oracle/no-id-object-result.hxx @@ -0,0 +1,84 @@ +// file : odb/oracle/no-id-object-result.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_NO_ID_OBJECT_RESULT_HXX +#define ODB_ORACLE_NO_ID_OBJECT_RESULT_HXX + +#include <odb/pre.hxx> + +#include <cstddef> // std::size_t + +#include <odb/schema-version.hxx> +#include <odb/no-id-object-result.hxx> + +#include <odb/details/shared-ptr.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/forward.hxx> // query_base +#include <odb/oracle/statement.hxx> +#include <odb/oracle/traits-calls.hxx> + +namespace odb +{ + namespace oracle + { + template <typename T> + class no_id_object_result_impl: public odb::no_id_object_result_impl<T> + { + public: + typedef odb::no_id_object_result_impl<T> base_type; + + typedef typename base_type::object_type object_type; + typedef typename base_type::pointer_type pointer_type; + + typedef object_traits_impl<object_type, id_oracle> object_traits; + typedef typename base_type::pointer_traits pointer_traits; + + typedef typename object_traits::statements_type statements_type; + + virtual + ~no_id_object_result_impl (); + + no_id_object_result_impl (const query_base&, + details::shared_ptr<select_statement>, + statements_type&, + const schema_version_migration*); + + virtual void + load (object_type&); + + virtual void + next (); + + virtual void + cache (); + + virtual std::size_t + size (); + + virtual void + invalidate (); + + using base_type::current; + + private: + typedef oracle::change_callback change_callback_type; + + static void + change_callback (void* context); + + private: + details::shared_ptr<select_statement> statement_; + statements_type& statements_; + object_traits_calls<object_type> tc_; + bool use_copy_; + typename object_traits::image_type* image_copy_; + }; + } +} + +#include <odb/oracle/no-id-object-result.txx> + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_NO_ID_OBJECT_RESULT_HXX diff --git a/libodb-oracle/odb/oracle/no-id-object-result.txx b/libodb-oracle/odb/oracle/no-id-object-result.txx new file mode 100644 index 0000000..7bac70b --- /dev/null +++ b/libodb-oracle/odb/oracle/no-id-object-result.txx @@ -0,0 +1,149 @@ +// file : odb/oracle/no-id-object-result.txx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <odb/callback.hxx> +#include <odb/exceptions.hxx> // result_not_cached + +#include <odb/oracle/no-id-object-statements.hxx> + +namespace odb +{ + namespace oracle + { + template <typename T> + no_id_object_result_impl<T>:: + ~no_id_object_result_impl () + { + invalidate (); + } + + template <typename T> + void no_id_object_result_impl<T>:: + invalidate () + { + change_callback_type& cc (statements_.image ().change_callback_); + + if (cc.context == this) + { + cc.callback = 0; + cc.context = 0; + } + + delete image_copy_; + image_copy_ = 0; + + if (!this->end_) + { + statement_->free_result (); + this->end_ = true; + } + + statement_.reset (); + } + + template <typename T> + no_id_object_result_impl<T>:: + no_id_object_result_impl (const query_base&, + details::shared_ptr<select_statement> statement, + statements_type& statements, + const schema_version_migration* svm) + : base_type (statements.connection ()), + statement_ (statement), + statements_ (statements), + tc_ (svm), + use_copy_ (false), + image_copy_ (0) + { + } + + template <typename T> + void no_id_object_result_impl<T>:: + load (object_type& obj) + { + object_traits::callback (this->db_, obj, callback_event::pre_load); + + tc_.init (obj, + use_copy_ ? *image_copy_ : statements_.image (), + &this->db_); + + // If we are using a copy, make sure the callback information for + // LOB data also comes from the copy. + // + statement_->stream_result ( + use_copy_ ? &statements_.image () : 0, + use_copy_ ? image_copy_ : 0); + + object_traits::callback (this->db_, obj, callback_event::post_load); + } + + template <typename T> + void no_id_object_result_impl<T>:: + next () + { + this->current (pointer_type ()); + + typename object_traits::image_type& im (statements_.image ()); + change_callback_type& cc (im.change_callback_); + + if (cc.context == this) + { + cc.callback = 0; + cc.context = 0; + } + + use_copy_ = false; + + if (im.version != statements_.select_image_version ()) + { + binding& b (statements_.select_image_binding ()); + tc_.bind (b.bind, im, statement_select); + statements_.select_image_version (im.version); + b.version++; + } + + if (statement_->fetch () == select_statement::no_data) + { + statement_->free_result (); + this->end_ = true; + } + else + { + cc.callback = &change_callback; + cc.context = this; + } + } + + template <typename T> + void no_id_object_result_impl<T>:: + cache () + { + } + + template <typename T> + std::size_t no_id_object_result_impl<T>:: + size () + { + throw result_not_cached (); + } + + template <typename T> + void no_id_object_result_impl<T>:: + change_callback (void* c) + { + no_id_object_result_impl<T>* r ( + static_cast<no_id_object_result_impl<T>*> (c)); + + typename object_traits::image_type im (r->statements_.image ()); + + if (r->image_copy_ == 0) + r->image_copy_ = new typename object_traits::image_type (im); + else + *r->image_copy_ = im; + + im.change_callback_.callback = 0; + im.change_callback_.context = 0; + + r->use_copy_ = true; + } + } +} diff --git a/libodb-oracle/odb/oracle/no-id-object-statements.hxx b/libodb-oracle/odb/oracle/no-id-object-statements.hxx new file mode 100644 index 0000000..2e5a033 --- /dev/null +++ b/libodb-oracle/odb/oracle/no-id-object-statements.hxx @@ -0,0 +1,134 @@ +// file : odb/oracle/no-id-object-statements.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_NO_ID_OBJECT_STATEMENTS_HXX +#define ODB_ORACLE_NO_ID_OBJECT_STATEMENTS_HXX + +#include <odb/pre.hxx> + +#include <cstddef> // std::size_t + +#include <odb/forward.hxx> +#include <odb/traits.hxx> + +#include <odb/details/shared-ptr.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/forward.hxx> +#include <odb/oracle/oracle-types.hxx> +#include <odb/oracle/binding.hxx> +#include <odb/oracle/statement.hxx> +#include <odb/oracle/statements-base.hxx> + +namespace odb +{ + namespace oracle + { + // + // Implementation for objects without object id. + // + + template <typename T> + class no_id_object_statements: public statements_base + { + public: + typedef T object_type; + typedef object_traits_impl<object_type, id_oracle> object_traits; + typedef typename object_traits::pointer_type pointer_type; + typedef typename object_traits::image_type image_type; + + typedef oracle::insert_statement insert_statement_type; + + public: + no_id_object_statements (connection_type&); + + virtual + ~no_id_object_statements (); + + // Object image. + // + image_type& + image (std::size_t i = 0) + { + return image_[i]; + } + + // Insert binding. + // + std::size_t + insert_image_version () const { return insert_image_version_;} + + void + insert_image_version (std::size_t v) {insert_image_version_ = v;} + + binding& + insert_image_binding () {return insert_image_binding_;} + + // Select binding (needed for query support). + // + std::size_t + select_image_version () const { return select_image_version_;} + + void + select_image_version (std::size_t v) {select_image_version_ = v;} + + binding& + select_image_binding () {return select_image_binding_;} + + // Statements. + // + insert_statement_type& + persist_statement () + { + if (persist_ == 0) + persist_.reset ( + new (details::shared) insert_statement_type ( + conn_, + object_traits::persist_statement, + object_traits::versioned, // Process if versioned. + insert_image_binding_, + 0)); + + return *persist_; + } + + public: + // select = total + // insert = total - inverse; inverse == 0 for object without id + // + static const std::size_t insert_column_count = + object_traits::column_count; + + static const std::size_t select_column_count = + object_traits::column_count; + + private: + no_id_object_statements (const no_id_object_statements&); + no_id_object_statements& operator= (const no_id_object_statements&); + + private: + image_type image_[object_traits::batch]; + sb4 status_[object_traits::batch]; + + // Select binding. + // + std::size_t select_image_version_; + binding select_image_binding_; + bind select_image_bind_[select_column_count]; + + // Insert binding. + // + std::size_t insert_image_version_; + binding insert_image_binding_; + bind insert_image_bind_[insert_column_count]; + + details::shared_ptr<insert_statement_type> persist_; + }; + } +} + +#include <odb/oracle/no-id-object-statements.txx> + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_NO_ID_OBJECT_STATEMENTS_HXX diff --git a/libodb-oracle/odb/oracle/no-id-object-statements.txx b/libodb-oracle/odb/oracle/no-id-object-statements.txx new file mode 100644 index 0000000..23d330f --- /dev/null +++ b/libodb-oracle/odb/oracle/no-id-object-statements.txx @@ -0,0 +1,39 @@ +// file : odb/oracle/no-id-object-statements.txx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <cstring> // std::memset + +namespace odb +{ + namespace oracle + { + template <typename T> + no_id_object_statements<T>:: + ~no_id_object_statements () + { + } + + template <typename T> + no_id_object_statements<T>:: + no_id_object_statements (connection_type& conn) + : statements_base (conn), + select_image_binding_ (select_image_bind_, select_column_count), + insert_image_binding_ (insert_image_bind_, + insert_column_count, + object_traits::batch, + sizeof (image_type), + status_) + { + image_[0].version = 0; // Only version in the first element used. + select_image_version_ = 0; + insert_image_version_ = 0; + + // SELECT statements only use the first element (no batches). + // + select_image_binding_.change_callback = image_[0].change_callback (); + + std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_)); + std::memset (select_image_bind_, 0, sizeof (select_image_bind_)); + } + } +} diff --git a/libodb-oracle/odb/oracle/oracle-fwd.hxx b/libodb-oracle/odb/oracle/oracle-fwd.hxx new file mode 100644 index 0000000..cbc107a --- /dev/null +++ b/libodb-oracle/odb/oracle/oracle-fwd.hxx @@ -0,0 +1,35 @@ +// file : odb/oracle/oracle-fwd.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_ORACLE_FWD_HXX +#define ODB_ORACLE_ORACLE_FWD_HXX + +#include <odb/pre.hxx> + +// Forward declaration for some of the types defined in oci.h. This +// allows us to avoid having to include oci.h in public headers. +// +typedef signed int sword; + +typedef unsigned char ub1; +typedef signed char sb1; +typedef signed short sb2; +typedef unsigned short ub2; +typedef signed int sb4; +typedef unsigned int ub4; + +typedef struct OCIEnv OCIEnv; +typedef struct OCISvcCtx OCISvcCtx; +typedef struct OCIError OCIError; +typedef struct OCIStmt OCIStmt; +typedef struct OCIAuthInfo OCIAuthInfo; +typedef struct OCITrans OCITrans; + +typedef struct OCIParam OCIParam; +typedef struct OCILobLocator OCILobLocator; +typedef struct OCIDateTime OCIDateTime; +typedef struct OCIInterval OCIInterval; + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_ORACLE_FWD_HXX diff --git a/libodb-oracle/odb/oracle/oracle-types.cxx b/libodb-oracle/odb/oracle/oracle-types.cxx new file mode 100644 index 0000000..1ef531f --- /dev/null +++ b/libodb-oracle/odb/oracle/oracle-types.cxx @@ -0,0 +1,374 @@ +// file : odb/oracle/oracle-types.cxx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <oci.h> + +#include <odb/oracle/oracle-types.hxx> +#include <odb/oracle/exceptions.hxx> +#include <odb/oracle/error.hxx> + +namespace odb +{ + namespace oracle + { + // + // lob + // + + lob:: + ~lob () + { + if (locator != 0) + OCIDescriptorFree (locator, OCI_DTYPE_LOB); + } + + lob:: + lob (const lob& x) + : environment (x.environment), + error (x.error), + locator (0), + buffer (x.buffer), + position (x.position) + { + // Watch out for exception safety. + // + if (x.locator != 0) + clone (x); + } + + lob& lob:: + operator= (const lob& x) + { + // Watch out for exception safety. + // + if (this != &x) + { + if (x.locator != 0) + clone (x); + else + { + if (locator != 0) + { + OCIDescriptorFree (locator, OCI_DTYPE_LOB); + locator = 0; + } + } + + environment = x.environment; + error = x.error; + buffer = x.buffer; + position = x.position; + } + + return *this; + } + + void lob:: + clone (const lob& x) + { + // Watch out for exception safety. + // + sword r; + bool alloc (locator == 0); + + if (alloc) + { + void* d (0); + r = OCIDescriptorAlloc (x.environment, &d, OCI_DTYPE_LOB, 0, 0); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + locator = static_cast<OCILobLocator*> (d); + } + + r = OCILobAssign (x.environment, x.error, x.locator, &locator); + + if (r != OCI_SUCCESS) + { + if (alloc) + { + OCIDescriptorFree (locator, OCI_DTYPE_LOB); + locator = 0; + } + + translate_error (x.error, r); + } + } + + // + // datetime + // + + datetime:: + ~datetime () + { + if (descriptor != 0 && (flags & descriptor_free)) + OCIDescriptorFree (descriptor, OCI_DTYPE_TIMESTAMP); + } + + datetime:: + datetime (const datetime& x) + : descriptor (0), flags (x.flags) + { + x.get (year_, month_, day_, hour_, minute_, second_, nanosecond_); + } + + datetime& datetime:: + operator= (const datetime& x) + { + if (this != &x) + { + if (descriptor != 0 && (flags & descriptor_free)) + { + OCIDescriptorFree (descriptor, OCI_DTYPE_TIMESTAMP); + descriptor = 0; + } + + flags = x.flags; + x.get (year_, month_, day_, hour_, minute_, second_, nanosecond_); + } + + return *this; + } + + void datetime:: + get (sb2& y, ub1& m, ub1& d, ub1& h, ub1& mi, ub1& s, ub4& ns) const + { + if (descriptor != 0) + { + sword r (OCIDateTimeGetDate (environment, + error, + descriptor, + &y, + &m, + &d)); + + if (r != OCI_SUCCESS) + translate_error (error, r); + + r = OCIDateTimeGetTime (environment, + error, + descriptor, + &h, + &mi, + &s, + &ns); + + if (r != OCI_SUCCESS) + translate_error (error, r); + } + else + { + y = year_; + m = month_; + d = day_; + h = hour_; + mi = minute_; + s = second_; + ns = nanosecond_; + } + } + + void datetime:: + set (sb2 y, ub1 m, ub1 d, ub1 h, ub1 minute, ub1 s, ub4 ns) + { + if (descriptor != 0) + { + sword r (OCIDateTimeConstruct (environment, + error, + descriptor, + y, + m, + d, + h, + minute, + s, + ns, + 0, + 0)); + + if (r != OCI_SUCCESS) + translate_error (error, r); + } + else + { + year_ = y; + month_ = m; + day_ = d; + hour_ = h; + minute_ = minute; + second_ = s; + nanosecond_ = ns; + } + } + + // + // interval_ym + // + + interval_ym:: + ~interval_ym () + { + if (descriptor != 0 && (flags & descriptor_free)) + OCIDescriptorFree (descriptor, OCI_DTYPE_INTERVAL_YM); + } + + interval_ym:: + interval_ym (const interval_ym& x) + : descriptor (0), flags (x.flags) + { + x.get (year_, month_); + } + + interval_ym& interval_ym:: + operator= (const interval_ym& x) + { + if (this != &x) + { + if (descriptor != 0 && (flags & descriptor_free)) + { + OCIDescriptorFree (descriptor, OCI_DTYPE_INTERVAL_YM); + descriptor = 0; + } + + flags = x.flags; + x.get (year_, month_); + } + + return *this; + } + + void interval_ym:: + get (sb4& y, sb4& m) const + { + if (descriptor != 0) + { + sword r (OCIIntervalGetYearMonth (environment, + error, + &y, + &m, + descriptor)); + + if (r != OCI_SUCCESS) + translate_error (error, r); + } + else + { + y = year_; + m = month_; + } + } + + void interval_ym:: + set (sb4 y, sb4 m) + { + if (descriptor != 0) + { + sword r (OCIIntervalSetYearMonth (environment, + error, + y, + m, + descriptor)); + + if (r != OCI_SUCCESS) + translate_error (error, r); + } + else + { + year_ = y; + month_ = m; + } + } + + // + // interval_ds + // + + interval_ds:: + ~interval_ds () + { + if (descriptor != 0 && (flags & descriptor_free)) + OCIDescriptorFree (descriptor, OCI_DTYPE_INTERVAL_DS); + } + + interval_ds:: + interval_ds (const interval_ds& x) + : descriptor (0), flags (x.flags) + { + x.get (day_, hour_, minute_, second_, nanosecond_); + } + + interval_ds& interval_ds:: + operator= (const interval_ds& x) + { + if (this != &x) + { + if (descriptor != 0 && (flags & descriptor_free)) + { + OCIDescriptorFree (descriptor, OCI_DTYPE_TIMESTAMP); + descriptor = 0; + } + + flags = x.flags; + x.get (day_, hour_, minute_, second_, nanosecond_); + } + + return *this; + } + + void interval_ds:: + get (sb4& d, sb4& h, sb4& m, sb4& s, sb4& ns) const + { + if (descriptor != 0) + { + sword r (OCIIntervalGetDaySecond (environment, + error, + &d, + &h, + &m, + &s, + &ns, + descriptor)); + + if (r != OCI_SUCCESS) + translate_error (error, r); + } + else + { + d = day_; + h = hour_; + m = minute_; + s = second_; + ns = nanosecond_; + } + } + + void interval_ds:: + set (sb4 d, sb4 h, sb4 m, sb4 s, sb4 ns) + { + if (descriptor != 0) + { + sword r (OCIIntervalSetDaySecond (environment, + error, + d, + h, + m, + s, + ns, + descriptor)); + + if (r != OCI_SUCCESS) + translate_error (error, r); + } + else + { + day_ = d; + hour_ = h; + minute_ = m; + second_ = s; + nanosecond_ = ns; + } + } + } +} diff --git a/libodb-oracle/odb/oracle/oracle-types.hxx b/libodb-oracle/odb/oracle/oracle-types.hxx new file mode 100644 index 0000000..a9553b2 --- /dev/null +++ b/libodb-oracle/odb/oracle/oracle-types.hxx @@ -0,0 +1,300 @@ +// file : odb/oracle/oracle-types.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_ORACLE_TYPES_HXX +#define ODB_ORACLE_ORACLE_TYPES_HXX + +#include <odb/pre.hxx> + +#include <odb/details/buffer.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/forward.hxx> // binding +#include <odb/oracle/oracle-fwd.hxx> + +#include <odb/oracle/details/export.hxx> + +namespace odb +{ + namespace oracle + { + enum chunk_position + { + chunk_one, + chunk_first, + chunk_next, + chunk_last + }; + + // Callback function signature used to specify LOB parameters that are + // passed to the database. If false is returned from the callback, + // statement execution is aborted. + // + typedef bool (*param_callback_type) ( + const void* context, // [in] The user context. + ub4* position_context, // [in] A position context. A callback is free to + // use this to track position information. This is + // initialized to zero before the callback is + // invoked for the first time. + const void** buffer, // [out] On return, a pointer to a buffer + // containing parameter data. + ub4* size, // [out] The parameter data size in bytes. + chunk_position*, // [out] The position of the chunk of data in + // buffer. + void* temp_buffer, // [in] A temporary buffer that may be used if + // required. The buffer argument should specify + // this buffer on return if it is used. + ub4 capacity); // [in] The temporary buffer size in bytes. + + // Callback function signature used to specify LOB values returned from + // the database. If false is returned, database_exception is thrown. + // + typedef bool (*result_callback_type) ( + void* context, // [in] The user context. + ub4* position_context, // [in] A position context. A callback is free to + // use this to track position information. This is + // initialized to zero before the callback is + // invoked for the first time. + void* buffer, // [in] A buffer containing the result data. + ub4 size, // [in] The result data size in bytes. + chunk_position); // [in] The position of this chunk. + + struct lob_callback + { + union + { + param_callback_type param; + result_callback_type result; + } callback; + + union + { + const void* param; + void* result; + } context; + }; + + struct bind + { + // This enumeration identifies the possible buffer types that can be + // bound to a bind instance. In most cases, these map directly to + // SQLT_XXX codes, identifying an external OCI type. nstring and nclob + // however have no equivalent OCI typecode. These additional identifiers + // allow for a consistent interface across all types. Note that these + // values are mapped to their corresponding external OCI typecodes (if + // any) using their integer values, and should therefore not be + // rearranged or explicitly assigned without also adjusting the + // sqlt_lookup array in odb/oracle/statement.cxx. + // + enum buffer_type + { + integer, // Buffer is an integer type of size specified by size. + uinteger, // Buffer is an unsigned integer of size specified by + // size. + binary_float, // Buffer is a float. + binary_double, // Buffer is a double. + number, // Buffer is a variable length char array. + date, // Buffer is a 7-byte char array. + timestamp, // Buffer is a datetime. + interval_ym, // Buffer is an interval_ym. + interval_ds, // Buffer is an interval_ds. + string, // Buffer is a variable length char array. + nstring, // Buffer is a variable length char array. + raw, // Buffer is a variable length char array. + blob, // Bind is a callback. + clob, // Bind is a callback. + nclob, // Bind is a callback. + last // Used as an end of list marker. + }; + + buffer_type type; // The type stored by buffer. + void* buffer; // Data buffer pointer. For LOB type bindings, this is + // interpreted as an oracle::lob*. + ub2* size; // The number of bytes in buffer. + ub4 capacity; // The maximum number of bytes that can be stored in + // the buffer. For LOBs, it used to store array skip + // size. + sb2* indicator; // Pointer to an OCI indicator variable. + + lob_callback* callback; + }; + + // An instance of this structure specifies the function to invoke and + // the context to pass when the object/view image is about to be + // modified. This mechanism is used by the query machinery to save the + // image between result iteration and dereferencing if something gets + // executed between these two operations that would overwrite the + // image. + // + struct change_callback + { + change_callback (): callback (0), context (0) {}; + + void (*callback) (void*); + void* context; + }; + + // The lob structure wraps data required for both parameter and result + // LOB type bindings. + // + struct LIBODB_ORACLE_EXPORT lob + { + ~lob (); + lob (): locator (0), buffer (0), position (0) {} + + lob (const lob&); + lob& operator= (const lob&); + + private: + void + clone (const lob&); + + public: + OCIEnv* environment; + OCIError* error; + OCILobLocator* locator; + + details::buffer* buffer; + ub4 position; + }; + + // + // The OCIDateTime and OCIInterval APIs require that an environment and + // error handle be passed to any function that manipulates an OCIDateTime + // or OCIInterval descriptor. It is however impossible to obtain these + // handles at the time a temporal data image is first initialized. The + // following structures allow ODB generated code to interact with the OCI + // temporal descriptor types indirectly via C++ primitives. The wrapped OCI + // descriptor is then set using these primitives at a time when the + // required data is available. A symmetric get interface is provided for + // consistency. + // + + // Descriptor management flags. + // + const unsigned short descriptor_cache = 0x01; + const unsigned short descriptor_free = 0x02; + + struct LIBODB_ORACLE_EXPORT datetime + { + void + get (sb2& year, + ub1& month, + ub1& day, + ub1& hour, + ub1& minute, + ub1& second, + ub4& nanosecond) const; + + void + set (sb2 year, + ub1 month, + ub1 day, + ub1 hour, + ub1 minute, + ub1 second, + ub4 nanosecond); + + ~datetime (); + datetime (unsigned short f = descriptor_cache | descriptor_free) + : descriptor (0), flags (f) {} + + datetime (const datetime&); + datetime& operator= (const datetime&); + + // Use the get() and set() functions above unless you know what you + // are doing and understand how copying of datetime works. + // + public: + OCIEnv* environment; + OCIError* error; + OCIDateTime* descriptor; + + unsigned short flags; + + public: + sb2 year_; + ub1 month_; + ub1 day_; + ub1 hour_; + ub1 minute_; + ub1 second_; + ub4 nanosecond_; + }; + + struct LIBODB_ORACLE_EXPORT interval_ym + { + void + get (sb4& year, sb4& month) const; + + void + set (sb4 year, sb4 month); + + ~interval_ym (); + interval_ym (unsigned short f = descriptor_cache | descriptor_free) + : descriptor (0), flags (f) {} + + interval_ym (const interval_ym&); + interval_ym& operator= (const interval_ym&); + + // Use the get() and set() functions above unless you know what you + // are doing and understand how copying of interval_ym works. + // + public: + OCIEnv* environment; + OCIError* error; + OCIInterval* descriptor; + + unsigned short flags; + + public: + sb4 year_; + sb4 month_; + }; + + struct LIBODB_ORACLE_EXPORT interval_ds + { + void + get (sb4& day, + sb4& hour, + sb4& minute, + sb4& second, + sb4& nanosecond) const; + + void + set (sb4 day, + sb4 hour, + sb4 minute, + sb4 second, + sb4 nanosecond); + + ~interval_ds (); + interval_ds (unsigned short f = descriptor_cache | descriptor_free) + : descriptor (0), flags (f) {} + + interval_ds (const interval_ds&); + interval_ds& operator= (const interval_ds&); + + // Use the get() and set() functions above unless you know what you + // are doing and understand how copying of interval_ds works. + // + public: + OCIEnv* environment; + OCIError* error; + OCIInterval* descriptor; + + unsigned short flags; + + public: + sb4 day_; + sb4 hour_; + sb4 minute_; + sb4 second_; + sb4 nanosecond_; + }; + } +} + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_ORACLE_TYPES_HXX diff --git a/libodb-oracle/odb/oracle/polymorphic-object-result.hxx b/libodb-oracle/odb/oracle/polymorphic-object-result.hxx new file mode 100644 index 0000000..ddb3055 --- /dev/null +++ b/libodb-oracle/odb/oracle/polymorphic-object-result.hxx @@ -0,0 +1,98 @@ +// file : odb/oracle/polymorphic-object-result.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_POLYMORPHIC_OBJECT_RESULT_HXX +#define ODB_ORACLE_POLYMORPHIC_OBJECT_RESULT_HXX + +#include <odb/pre.hxx> + +#include <cstddef> // std::size_t + +#include <odb/schema-version.hxx> +#include <odb/polymorphic-object-result.hxx> + +#include <odb/details/shared-ptr.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/forward.hxx> // query_base +#include <odb/oracle/statement.hxx> +#include <odb/oracle/traits-calls.hxx> + +namespace odb +{ + namespace oracle + { + template <typename T> + class polymorphic_object_result_impl: + public odb::polymorphic_object_result_impl<T> + { + public: + typedef odb::polymorphic_object_result_impl<T> base_type; + + typedef typename base_type::id_type id_type; + typedef typename base_type::object_type object_type; + typedef typename base_type::pointer_type pointer_type; + + typedef object_traits_impl<object_type, id_oracle> object_traits; + typedef typename base_type::pointer_traits pointer_traits; + + typedef typename base_type::root_type root_type; + typedef typename base_type::discriminator_type discriminator_type; + + typedef object_traits_impl<root_type, id_oracle> root_traits; + + typedef typename object_traits::image_type image_type; + typedef typename object_traits::statements_type statements_type; + + virtual + ~polymorphic_object_result_impl (); + + polymorphic_object_result_impl (const query_base&, + details::shared_ptr<select_statement>, + statements_type&, + const schema_version_migration*); + + virtual void + load (object_type*, bool fetch); + + virtual id_type + load_id (); + + virtual discriminator_type + load_discriminator (); + + virtual void + next (); + + virtual void + cache (); + + virtual std::size_t + size (); + + virtual void + invalidate (); + + using base_type::current; + + private: + typedef oracle::change_callback change_callback_type; + + static void + change_callback (void* context); + + private: + details::shared_ptr<select_statement> statement_; + statements_type& statements_; + object_traits_calls<object_type> tc_; + bool use_copy_; + image_type* image_copy_; + }; + } +} + +#include <odb/oracle/polymorphic-object-result.txx> + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_POLYMORPHIC_OBJECT_RESULT_HXX diff --git a/libodb-oracle/odb/oracle/polymorphic-object-result.txx b/libodb-oracle/odb/oracle/polymorphic-object-result.txx new file mode 100644 index 0000000..b810b29 --- /dev/null +++ b/libodb-oracle/odb/oracle/polymorphic-object-result.txx @@ -0,0 +1,320 @@ +// file : odb/oracle/polymorphic-object-result.txx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <cassert> + +#include <odb/callback.hxx> +#include <odb/exceptions.hxx> // result_not_cached + +#include <odb/oracle/polymorphic-object-statements.hxx> + +namespace odb +{ + namespace oracle + { + template <typename T> + polymorphic_object_result_impl<T>:: + ~polymorphic_object_result_impl () + { + invalidate (); + } + + template <typename T> + void polymorphic_object_result_impl<T>:: + invalidate () + { + change_callback_type& cc ( + statements_.root_statements ().image ().change_callback_); + + if (cc.context == this) + { + cc.context = 0; + cc.callback = 0; + } + + if (image_copy_ != 0) + { + object_traits::free_image (image_copy_); + image_copy_ = 0; + } + + if (!this->end_) + { + statement_->free_result (); + this->end_ = true; + } + + statement_.reset (); + } + + template <typename T> + polymorphic_object_result_impl<T>:: + polymorphic_object_result_impl (const query_base&, + details::shared_ptr<select_statement> st, + statements_type& sts, + const schema_version_migration* svm) + : base_type (sts.connection ()), + statement_ (st), + statements_ (sts), + tc_ (svm), + use_copy_ (false), + image_copy_ (0) + { + } + + template <typename T> + void polymorphic_object_result_impl<T>:: + load (object_type* pobj, bool) + { + typename statements_type::root_statements_type& rsts ( + statements_.root_statements ()); + + // This is a top-level call so the statements cannot be locked. + // + assert (!rsts.locked ()); + typename statements_type::auto_lock l (rsts); + + image_type& i (use_copy_ ? *image_copy_ : statements_.image ()); + typename root_traits::image_type& ri ( + use_copy_ ? object_traits::root_image (i) : rsts.image ()); + + id_type id (root_traits::id (ri)); + + // Determine this object's dynamic type. + // + typedef typename root_traits::info_type info_type; + discriminator_type d (root_traits::discriminator (ri)); + + // Use the polymorphic_info() helper to get concrete_info if + // object_type is concrete and NULL if it is abstract. + // + const info_type* spi (polymorphic_info (object_traits::info)); + const info_type& pi ( + spi != 0 && spi->discriminator == d + ? *spi + : root_traits::map->find (d)); + + typedef typename root_traits::pointer_type root_pointer_type; + typedef typename root_traits::pointer_traits root_pointer_traits; + + typename object_traits::pointer_cache_traits::insert_guard ig; + + if (pobj == 0) + { + // Need to create a new instance of the dynamic type. + // + root_pointer_type rp (pi.create ()); + pointer_type p ( + root_pointer_traits::template static_pointer_cast<object_type> (rp)); + + // Insert it as a root pointer (for non-unique pointers, rp should + // still be valid and for unique pointers this is a no-op). + // + ig.reset ( + object_traits::pointer_cache_traits::insert (this->db_, id, rp)); + + pobj = &pointer_traits::get_ref (p); + current (p); + } + else + { + // We are loading into an existing instance. If the static and + // dynamic types differ, then make sure the instance is at least + // of the dynamic type. + // + if (&pi != &object_traits::info) + { + const info_type& dpi (root_traits::map->find (typeid (*pobj))); + + if (&dpi != &pi && dpi.derived (pi)) + throw object_not_persistent (); // @@ type_mismatch ? + } + } + + callback_event ce (callback_event::pre_load); + pi.dispatch (info_type::call_callback, this->db_, pobj, &ce); + + tc_.init (*pobj, i, &this->db_); + + // If we are using a copy, make sure the callback information for + // LOB data also comes from the copy. + // + statement_->stream_result ( + use_copy_ ? &statements_.image () : 0, + use_copy_ ? image_copy_ : 0); + + // Initialize the id image and binding and load the rest of the object + // (containers, dynamic part, etc). + // + typename object_traits::id_image_type& idi (statements_.id_image ()); + root_traits::init (idi, id); + + binding& idb (statements_.id_image_binding ()); + if (idi.version != statements_.id_image_version () || idb.version == 0) + { + object_traits::bind (idb.bind, idi); + statements_.id_image_version (idi.version); + idb.version++; + } + + tc_.load_ (statements_, *pobj, false); + + // Load the dynamic part of the object unless static and dynamic + // types are the same. + // + if (&pi != &object_traits::info) + { + std::size_t d (object_traits::depth); + pi.dispatch (info_type::call_load, this->db_, pobj, &d); + }; + + rsts.load_delayed (tc_.version ()); + l.unlock (); + + ce = callback_event::post_load; + pi.dispatch (info_type::call_callback, this->db_, pobj, &ce); + object_traits::pointer_cache_traits::load (ig.position ()); + ig.release (); + } + + template <typename T> + typename polymorphic_object_result_impl<T>::id_type + polymorphic_object_result_impl<T>:: + load_id () + { + typename root_traits::image_type& i ( + use_copy_ + ? object_traits::root_image (*image_copy_) + : statements_.root_statements ().image ()); + + return root_traits::id (i); + } + + template <typename T> + typename polymorphic_object_result_impl<T>::discriminator_type + polymorphic_object_result_impl<T>:: + load_discriminator () + { + typename root_traits::image_type& i ( + use_copy_ + ? object_traits::root_image (*image_copy_) + : statements_.root_statements ().image ()); + + return root_traits::discriminator (i); + } + + template <typename T, typename R> + struct polymorphic_image_rebind + { + // Derived type version. + // + typedef object_traits_impl<T, id_oracle> traits; + + static void + rebind (typename traits::statements_type& sts, + const schema_version_migration* svm) + { + typename traits::image_type& im (sts.image ()); + + if (traits::check_version (sts.select_image_versions (), im)) + { + binding& b (sts.select_image_binding (traits::depth)); + object_traits_calls<T> tc (svm); + tc.bind (b.bind, 0, 0, im, statement_select); + traits::update_version ( + sts.select_image_versions (), im, sts.select_image_bindings ()); + } + } + }; + + template <typename R> + struct polymorphic_image_rebind<R, R> + { + // Root type version. + // + typedef object_traits_impl<R, id_oracle> traits; + + static void + rebind (typename traits::statements_type& sts, + const schema_version_migration* svm) + { + typename traits::image_type& im (sts.image ()); + + if (im.version != sts.select_image_version ()) + { + binding& b (sts.select_image_binding ()); + object_traits_calls<R> tc (svm); + tc.bind (b.bind, im, statement_select); + sts.select_image_version (im.version); + b.version++; + } + } + }; + + template <typename T> + void polymorphic_object_result_impl<T>:: + next () + { + this->current (pointer_type ()); + + change_callback_type& cc ( + statements_.root_statements ().image ().change_callback_); + + if (cc.context == this) + { + cc.callback = 0; + cc.context = 0; + } + + use_copy_ = false; + polymorphic_image_rebind<object_type, root_type>::rebind ( + statements_, tc_.version ()); + + if (statement_->fetch () == select_statement::no_data) + { + statement_->free_result (); + this->end_ = true; + } + else + { + cc.callback = &change_callback; + cc.context = this; + } + } + + template <typename T> + void polymorphic_object_result_impl<T>:: + cache () + { + } + + template <typename T> + std::size_t polymorphic_object_result_impl<T>:: + size () + { + throw result_not_cached (); + } + + template <typename T> + void polymorphic_object_result_impl<T>:: + change_callback (void* c) + { + polymorphic_object_result_impl<T>* r ( + static_cast<polymorphic_object_result_impl<T>*> (c)); + image_type& im (r->statements_.image ()); + + if (r->image_copy_ == 0) + r->image_copy_ = object_traits::clone_image (im); + else + object_traits::copy_image (*r->image_copy_, im); + + typename root_traits::image_type& rim ( + r->statements_.root_statements ().image ()); + + rim.change_callback_.callback = 0; + rim.change_callback_.context = 0; + + r->use_copy_ = true; + } + } +} diff --git a/libodb-oracle/odb/oracle/polymorphic-object-statements.hxx b/libodb-oracle/odb/oracle/polymorphic-object-statements.hxx new file mode 100644 index 0000000..fbb8fcf --- /dev/null +++ b/libodb-oracle/odb/oracle/polymorphic-object-statements.hxx @@ -0,0 +1,462 @@ +// file : odb/oracle/polymorphic-object-statements.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_POLYMORPHIC_OBJECT_STATEMENTS_HXX +#define ODB_ORACLE_POLYMORPHIC_OBJECT_STATEMENTS_HXX + +#include <odb/pre.hxx> + +#include <cstddef> // std::size_t + +#include <odb/forward.hxx> +#include <odb/traits.hxx> + +#include <odb/details/shared-ptr.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/forward.hxx> +#include <odb/oracle/oracle-types.hxx> +#include <odb/oracle/binding.hxx> +#include <odb/oracle/statement.hxx> +#include <odb/oracle/statements-base.hxx> +#include <odb/oracle/simple-object-statements.hxx> + +namespace odb +{ + namespace oracle + { + // + // Implementation for polymorphic objects. + // + + template <typename T> + class polymorphic_root_object_statements: public object_statements<T> + { + public: + typedef typename object_statements<T>::connection_type connection_type; + typedef typename object_statements<T>::object_traits object_traits; + typedef typename object_statements<T>::id_image_type id_image_type; + + typedef + typename object_traits::discriminator_image_type + discriminator_image_type; + + typedef + typename object_statements<T>::select_statement_type + select_statement_type; + + public: + // Interface compatibility with derived_object_statements. + // + typedef polymorphic_root_object_statements root_statements_type; + + root_statements_type& + root_statements () + { + return *this; + } + + public: + // Discriminator binding. + // + discriminator_image_type& + discriminator_image () {return discriminator_image_;} + + std::size_t + discriminator_image_version () const + {return discriminator_image_version_;} + + void + discriminator_image_version (std::size_t v) + {discriminator_image_version_ = v;} + + binding& + discriminator_image_binding () {return discriminator_image_binding_;} + + // Id binding for discriminator retrieval. + // + id_image_type& + discriminator_id_image () {return discriminator_id_image_;} + + std::size_t + discriminator_id_image_version () const + {return discriminator_id_image_version_;} + + void + discriminator_id_image_version (std::size_t v) + {discriminator_id_image_version_ = v;} + + binding& + discriminator_id_image_binding () + {return discriminator_id_image_binding_;} + + // + // + select_statement_type& + find_discriminator_statement () + { + if (find_discriminator_ == 0) + find_discriminator_.reset ( + new (details::shared) select_statement_type ( + this->conn_, + object_traits::find_discriminator_statement, + false, // Doesn't need to be processed. + false, // Don't optimize. + discriminator_id_image_binding_, + discriminator_image_binding_, + 0)); // No LOB prefetch (discriminator cannot be LOB). + + return *find_discriminator_; + } + + public: + polymorphic_root_object_statements (connection_type&); + + virtual + ~polymorphic_root_object_statements (); + + // Static "override" (statements type). + // + void + load_delayed (const schema_version_migration* svm) + { + assert (this->locked ()); + + if (!this->delayed_.empty ()) + this->template load_delayed_<polymorphic_root_object_statements> ( + svm); + } + + public: + static const std::size_t id_column_count = + object_statements<T>::id_column_count; + + static const std::size_t discriminator_column_count = + object_traits::discriminator_column_count; + + static const std::size_t managed_optimistic_column_count = + object_traits::managed_optimistic_column_count; + + private: + // Discriminator image. + // + discriminator_image_type discriminator_image_; + std::size_t discriminator_image_version_; + binding discriminator_image_binding_; + bind discriminator_image_bind_[discriminator_column_count + + managed_optimistic_column_count]; + + // Id image for discriminator retrieval (only used as a parameter). + // + id_image_type discriminator_id_image_; + std::size_t discriminator_id_image_version_; + binding discriminator_id_image_binding_; + bind discriminator_id_image_bind_[id_column_count]; + + details::shared_ptr<select_statement_type> find_discriminator_; + }; + + template <typename T> + class polymorphic_derived_object_statements: public statements_base + { + public: + typedef T object_type; + typedef object_traits_impl<object_type, id_oracle> object_traits; + typedef typename object_traits::id_type id_type; + typedef typename object_traits::pointer_type pointer_type; + typedef typename object_traits::id_image_type id_image_type; + typedef typename object_traits::image_type image_type; + + typedef typename object_traits::root_type root_type; + typedef + polymorphic_root_object_statements<root_type> + root_statements_type; + + typedef typename object_traits::base_type base_type; + typedef + typename object_traits::base_traits::statements_type + base_statements_type; + + typedef + typename object_traits::extra_statement_cache_type + extra_statement_cache_type; + + typedef oracle::insert_statement insert_statement_type; + typedef oracle::select_statement select_statement_type; + typedef oracle::update_statement update_statement_type; + typedef oracle::delete_statement delete_statement_type; + + typedef typename root_statements_type::auto_lock auto_lock; + + public: + polymorphic_derived_object_statements (connection_type&); + + virtual + ~polymorphic_derived_object_statements (); + + public: + // Delayed loading. + // + static void + delayed_loader (odb::database&, + const id_type&, + root_type&, + const schema_version_migration*); + + public: + // Root and immediate base statements. + // + root_statements_type& + root_statements () + { + return root_statements_; + } + + base_statements_type& + base_statements () + { + return base_statements_; + } + + public: + // Object image. + // + image_type& + image () + { + return image_; + } + + // Insert binding. + // + std::size_t + insert_image_version () const { return insert_image_version_;} + + void + insert_image_version (std::size_t v) {insert_image_version_ = v;} + + std::size_t + insert_id_binding_version () const { return insert_id_binding_version_;} + + void + insert_id_binding_version (std::size_t v) {insert_id_binding_version_ = v;} + + binding& + insert_image_binding () {return insert_image_binding_;} + + // Update binding. + // + std::size_t + update_image_version () const { return update_image_version_;} + + void + update_image_version (std::size_t v) {update_image_version_ = v;} + + std::size_t + update_id_binding_version () const { return update_id_binding_version_;} + + void + update_id_binding_version (std::size_t v) {update_id_binding_version_ = v;} + + binding& + update_image_binding () {return update_image_binding_;} + + // Select binding. + // + std::size_t* + select_image_versions () { return select_image_versions_;} + + binding* + select_image_bindings () {return select_image_bindings_;} + + binding& + select_image_binding (std::size_t d) + { + return select_image_bindings_[object_traits::depth - d]; + } + + // Object id binding (comes from the root statements). + // + id_image_type& + id_image () {return root_statements_.id_image ();} + + std::size_t + id_image_version () const {return root_statements_.id_image_version ();} + + void + id_image_version (std::size_t v) {root_statements_.id_image_version (v);} + + binding& + id_image_binding () {return root_statements_.id_image_binding ();} + + binding& + optimistic_id_image_binding () { + return root_statements_.optimistic_id_image_binding ();} + + // Statements. + // + insert_statement_type& + persist_statement () + { + if (persist_ == 0) + persist_.reset ( + new (details::shared) insert_statement_type ( + conn_, + object_traits::persist_statement, + object_traits::versioned, // Process if versioned. + insert_image_binding_, + 0)); + + return *persist_; + } + + select_statement_type& + find_statement (std::size_t d) + { + std::size_t i (object_traits::depth - d); + details::shared_ptr<select_statement_type>& p (find_[i]); + + if (p == 0) + p.reset ( + new (details::shared) select_statement_type ( + conn_, + object_traits::find_statements[i], + object_traits::versioned, // Process if versioned. + false, // Don't optimize. + root_statements_.id_image_binding (), + select_image_bindings_[i], + 4096)); // Hardcode a 4kB LOB prefetch size. + + return *p; + } + + update_statement_type& + update_statement () + { + if (update_ == 0) + update_.reset ( + new (details::shared) update_statement_type ( + conn_, + object_traits::update_statement, + object_traits::versioned, // Process if versioned. + update_image_binding_)); + + return *update_; + } + + delete_statement_type& + erase_statement () + { + if (erase_ == 0) + erase_.reset ( + new (details::shared) delete_statement_type ( + conn_, + object_traits::erase_statement, + root_statements_.id_image_binding ())); + + return *erase_; + } + + // Extra (container, section) statement cache. + // + extra_statement_cache_type& + extra_statement_cache () + { + return extra_statement_cache_.get ( + conn_, + image_, + id_image (), + id_image_binding (), + &id_image_binding ()); // Note, not id+version. + } + + public: + // select = total - id - separate_load + base::select + // insert = total - inverse + // update = total - inverse - id - readonly - separate_update + // + static const std::size_t id_column_count = + object_traits::id_column_count; + + static const std::size_t select_column_count = + object_traits::column_count - + id_column_count - + object_traits::separate_load_column_count + + base_statements_type::select_column_count; + + static const std::size_t insert_column_count = + object_traits::column_count - + object_traits::inverse_column_count; + + static const std::size_t update_column_count = insert_column_count - + object_traits::id_column_count - + object_traits::readonly_column_count - + object_traits::separate_update_column_count; + + private: + polymorphic_derived_object_statements ( + const polymorphic_derived_object_statements&); + + polymorphic_derived_object_statements& + operator= (const polymorphic_derived_object_statements&); + + private: + root_statements_type& root_statements_; + base_statements_type& base_statements_; + + extra_statement_cache_ptr<extra_statement_cache_type, + image_type, + id_image_type> extra_statement_cache_; + + image_type image_; + + // Select binding. Here we are have an array of statements/bindings + // one for each depth. In other words, if we have classes root, base, + // and derived, then we have the following array of statements: + // + // [0] d + b + r + // [1] d + b + // [2] d + // + // Also, because we have a chain of images bound to these statements, + // we have an array of versions, one entry for each base plus one for + // our own image. + // + // A poly-abstract class only needs the first statement and in this + // case we have only one entry in the the bindings and statements + // arrays (but not versions; we still have a chain of images). + // + std::size_t select_image_versions_[object_traits::depth]; + binding select_image_bindings_[ + object_traits::abstract ? 1 : object_traits::depth]; + bind select_image_bind_[select_column_count]; + + // Insert binding. The id binding is copied from the hierarchy root. + // + std::size_t insert_image_version_; + std::size_t insert_id_binding_version_; + binding insert_image_binding_; + bind insert_image_bind_[insert_column_count]; + + // Update binding. The id suffix binding is copied from the hierarchy + // root. + // + std::size_t update_image_version_; + std::size_t update_id_binding_version_; + binding update_image_binding_; + bind update_image_bind_[update_column_count + id_column_count]; + + details::shared_ptr<insert_statement_type> persist_; + details::shared_ptr<select_statement_type> find_[ + object_traits::abstract ? 1 : object_traits::depth]; + details::shared_ptr<update_statement_type> update_; + details::shared_ptr<delete_statement_type> erase_; + }; + } +} + +#include <odb/oracle/polymorphic-object-statements.txx> + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_POLYMORPHIC_OBJECT_STATEMENTS_HXX diff --git a/libodb-oracle/odb/oracle/polymorphic-object-statements.txx b/libodb-oracle/odb/oracle/polymorphic-object-statements.txx new file mode 100644 index 0000000..9d190e5 --- /dev/null +++ b/libodb-oracle/odb/oracle/polymorphic-object-statements.txx @@ -0,0 +1,137 @@ +// file : odb/oracle/polymorphic-object-statements.txx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <cstring> // std::memset + +#include <odb/callback.hxx> +#include <odb/exceptions.hxx> + +#include <odb/oracle/connection.hxx> +#include <odb/oracle/transaction.hxx> +#include <odb/oracle/statement-cache.hxx> +#include <odb/oracle/traits-calls.hxx> + +namespace odb +{ + namespace oracle + { + // + // polymorphic_root_object_statements + // + + template <typename T> + polymorphic_root_object_statements<T>:: + ~polymorphic_root_object_statements () + { + } + + template <typename T> + polymorphic_root_object_statements<T>:: + polymorphic_root_object_statements (connection_type& conn) + : object_statements<T> (conn), + discriminator_image_binding_ (discriminator_image_bind_, + discriminator_column_count + + managed_optimistic_column_count), + discriminator_id_image_binding_ (discriminator_id_image_bind_, + id_column_count) + { + discriminator_image_.version = 0; + discriminator_id_image_.version = 0; + + discriminator_image_version_ = 0; + discriminator_id_image_version_ = 0; + + std::memset ( + discriminator_image_bind_, 0, sizeof (discriminator_image_bind_)); + std::memset ( + discriminator_id_image_bind_, 0, sizeof (discriminator_id_image_bind_)); + } + + // + // polymorphic_derived_object_statements + // + + template <typename T> + polymorphic_derived_object_statements<T>:: + ~polymorphic_derived_object_statements () + { + } + + template <typename T> + polymorphic_derived_object_statements<T>:: + polymorphic_derived_object_statements (connection_type& conn) + : statements_base (conn), + root_statements_ (conn.statement_cache ().find_object<root_type> ()), + base_statements_ (conn.statement_cache ().find_object<base_type> ()), + insert_image_binding_ (insert_image_bind_, insert_column_count), + update_image_binding_ (update_image_bind_, + update_column_count + id_column_count) + { + image_.base = &base_statements_.image (); + image_.version = 0; + + for (std::size_t i (0); i < object_traits::depth; ++i) + select_image_versions_[i] = 0; + + for (std::size_t i (0); + i < (object_traits::abstract ? 1 : object_traits::depth); + ++i) + { + select_image_bindings_[i].bind = select_image_bind_; + select_image_bindings_[i].count = object_traits::find_column_counts[i]; + select_image_bindings_[i].change_callback = 0; + } + + // Statements other than the first one (which goes all the way to + // the root) can never override the image because they are used to + // load up the dynamic part of the object only after the static + // part has been loaded (and triggered the callback if necessary). + // + select_image_bindings_[0].change_callback = + root_statements_.image ().change_callback (); + + insert_image_version_ = 0; + insert_id_binding_version_ = 0; + update_image_version_ = 0; + update_id_binding_version_ = 0; + + std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_)); + std::memset (update_image_bind_, 0, sizeof (update_image_bind_)); + std::memset (select_image_bind_, 0, sizeof (select_image_bind_)); + } + + template <typename T> + void polymorphic_derived_object_statements<T>:: + delayed_loader (odb::database& db, + const id_type& id, + root_type& robj, + const schema_version_migration* svm) + { + connection_type& conn (transaction::current ().connection (db)); + polymorphic_derived_object_statements& sts ( + conn.statement_cache ().find_object<object_type> ()); + root_statements_type& rsts (sts.root_statements ()); + + object_type& obj (static_cast<object_type&> (robj)); + + // The same code as in object_statements::load_delayed_(). + // + object_traits_calls<T> tc (svm); + + if (!tc.find_ (sts, &id)) + throw object_not_persistent (); + + object_traits::callback (db, obj, callback_event::pre_load); + tc.init (obj, sts.image (), &db); + sts.find_[0]->stream_result (); + tc.load_ (sts, obj, false); // Load containers, etc. + + rsts.load_delayed (svm); + + { + typename root_statements_type::auto_unlock u (rsts); + object_traits::callback (db, obj, callback_event::post_load); + } + } + } +} diff --git a/libodb-oracle/odb/oracle/prepared-query.cxx b/libodb-oracle/odb/oracle/prepared-query.cxx new file mode 100644 index 0000000..7fa11be --- /dev/null +++ b/libodb-oracle/odb/oracle/prepared-query.cxx @@ -0,0 +1,15 @@ +// file : odb/oracle/prepared-query.cxx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <odb/oracle/prepared-query.hxx> + +namespace odb +{ + namespace oracle + { + prepared_query_impl:: + ~prepared_query_impl () + { + } + } +} diff --git a/libodb-oracle/odb/oracle/prepared-query.hxx b/libodb-oracle/odb/oracle/prepared-query.hxx new file mode 100644 index 0000000..c449a3a --- /dev/null +++ b/libodb-oracle/odb/oracle/prepared-query.hxx @@ -0,0 +1,34 @@ +// file : odb/oracle/prepared-query.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_PREPARED_QUERY_HXX +#define ODB_ORACLE_PREPARED_QUERY_HXX + +#include <odb/pre.hxx> + +#include <odb/prepared-query.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/query.hxx> + +#include <odb/oracle/details/export.hxx> + +namespace odb +{ + namespace oracle + { + struct LIBODB_ORACLE_EXPORT prepared_query_impl: odb::prepared_query_impl + { + virtual + ~prepared_query_impl (); + + prepared_query_impl (odb::connection& c): odb::prepared_query_impl (c) {} + + oracle::query_base query; + }; + } +} + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_PREPARED_QUERY_HXX diff --git a/libodb-oracle/odb/oracle/query-const-expr.cxx b/libodb-oracle/odb/oracle/query-const-expr.cxx new file mode 100644 index 0000000..5395f1f --- /dev/null +++ b/libodb-oracle/odb/oracle/query-const-expr.cxx @@ -0,0 +1,14 @@ +// file : odb/oracle/query-const-expr.cxx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <odb/oracle/query.hxx> + +namespace odb +{ + namespace oracle + { + // Sun CC cannot handle this in query.cxx. + // + const query_base query_base::true_expr (true); + } +} diff --git a/libodb-oracle/odb/oracle/query-dynamic.cxx b/libodb-oracle/odb/oracle/query-dynamic.cxx new file mode 100644 index 0000000..8ee4964 --- /dev/null +++ b/libodb-oracle/odb/oracle/query-dynamic.cxx @@ -0,0 +1,163 @@ +// file : odb/oracle/query-dynamic.cxx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <cstddef> // std::size_t + +#include <odb/oracle/query-dynamic.hxx> +#include <odb/oracle/exceptions.hxx> // lob_comparison + +using namespace std; + +namespace odb +{ + namespace oracle + { + static const char* logic_operators[] = {") AND (", ") OR ("}; + static const char* comp_operators[] = {"=", "!=", "<", ">", "<=", ">="}; + + static void + translate (query_base& q, const odb::query_base& s, size_t p) + { + typedef odb::query_base::clause_part part; + + const part& x (s.clause ()[p]); + + switch (x.kind) + { + case part::kind_column: + { + const query_column_base* c ( + static_cast<const query_column_base*> ( + x.native_info[id_oracle].column)); + + q.append (c->table (), c->column ()); + break; + } + case part::kind_param_val: + case part::kind_param_ref: + { + const query_column_base& qc ( + *static_cast<const query_column_base*> ( + x.native_info[id_oracle].column)); + + query_param_factory f ( + reinterpret_cast<query_param_factory> ( + x.native_info[id_oracle].param_factory)); + + // If factory is NULL, then this is a LOB value. + // + if (f == 0) + throw lob_comparison (); + + const odb::query_param* p ( + reinterpret_cast<const odb::query_param*> (x.data)); + + q.append (f (p->value, qc, x.kind == part::kind_param_ref), + qc.conversion ()); + break; + } + case part::kind_native: + { + q.append (s.strings ()[x.data]); + break; + } + case part::kind_true: + case part::kind_false: + { + q.append (x.kind == part::kind_true); + break; + } + case part::op_add: + { + translate (q, s, x.data); + translate (q, s, p - 1); + break; + } + case part::op_and: + case part::op_or: + { + q += "("; + translate (q, s, x.data); + q += logic_operators[x.kind - part::op_and]; + translate (q, s, p - 1); + q += ")"; + break; + } + case part::op_not: + { + q += "NOT ("; + translate (q, s, p - 1); + q += ")"; + break; + } + case part::op_null: + case part::op_not_null: + { + translate (q, s, p - 1); + q += (x.kind == part::op_null ? "IS NULL" : "IS NOT NULL"); + break; + } + case part::op_in: + { + if (x.data != 0) + { + size_t b (p- x.data); + + translate (q, s, b - 1); // column + q += "IN ("; + + for (size_t i (b); i != p; ++i) + { + if (i != b) + q += ","; + + translate (q, s, i); + } + + q += ")"; + } + else + q.append (false); + + break; + } + case part::op_like: + { + translate (q, s, p - 2); // column + q += "LIKE"; + translate (q, s, p - 1); // pattern + break; + } + case part::op_like_escape: + { + translate (q, s, p - 3); // column + q += "LIKE"; + translate (q, s, p - 2); // pattern + q += "ESCAPE"; + translate (q, s, p - 1); // escape + break; + } + case part::op_eq: + case part::op_ne: + case part::op_lt: + case part::op_gt: + case part::op_le: + case part::op_ge: + { + translate (q, s, x.data); + q += comp_operators[x.kind - part::op_eq]; + translate (q, s, p - 1); + break; + } + } + } + + query_base:: + query_base (const odb::query_base& q) + : binding_ (0, 0) + { + if (!q.empty ()) + translate (*this, q, q.clause ().size () - 1); + } + } +} diff --git a/libodb-oracle/odb/oracle/query-dynamic.hxx b/libodb-oracle/odb/oracle/query-dynamic.hxx new file mode 100644 index 0000000..eceac44 --- /dev/null +++ b/libodb-oracle/odb/oracle/query-dynamic.hxx @@ -0,0 +1,32 @@ +// file : odb/oracle/query-dynamic.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_QUERY_DYNAMIC_HXX +#define ODB_ORACLE_QUERY_DYNAMIC_HXX + +#include <odb/pre.hxx> + +#include <odb/query.hxx> +#include <odb/query-dynamic.hxx> + +#include <odb/oracle/query.hxx> + +namespace odb +{ + namespace oracle + { + typedef details::shared_ptr<query_param> (*query_param_factory) ( + const void* val, const query_column_base&, bool by_ref); + + template <typename T, database_type_id ID> + details::shared_ptr<query_param> + query_param_factory_impl (const void*, const query_column_base&, bool); + } +} + +#include <odb/oracle/query-dynamic.ixx> +#include <odb/oracle/query-dynamic.txx> + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_QUERY_DYNAMIC_HXX diff --git a/libodb-oracle/odb/oracle/query-dynamic.ixx b/libodb-oracle/odb/oracle/query-dynamic.ixx new file mode 100644 index 0000000..154f5cb --- /dev/null +++ b/libodb-oracle/odb/oracle/query-dynamic.ixx @@ -0,0 +1,72 @@ +// file : odb/oracle/query-dynamic.ixx +// license : ODB NCUEL; see accompanying LICENSE file + +namespace odb +{ + namespace oracle + { + // + // + template <typename T, database_type_id ID> + inline query_column<T, ID>:: + query_column (odb::query_column<T>& qc, + const char* table, + const char* column, + const char* conv, + unsigned short prec, + short scale) + : query_column_base (table, column, conv, prec, scale) + { + native_column_info& ci (qc.native_info[id_oracle]); + ci.column = static_cast<query_column_base*> (this); + + // For some reason GCC needs this statically-typed pointer in + // order to instantiate the functions. + // + query_param_factory f (&query_param_factory_impl<T, ID>); + ci.param_factory = reinterpret_cast<void*> (f); + } + + template <typename T> + inline query_column<T, id_blob>:: + query_column (odb::query_column<T>& qc, + const char* table, const char* column, const char*) + : lob_query_column (table, column) + { + native_column_info& ci (qc.native_info[id_oracle]); + ci.column = static_cast<query_column_base*> (this); + + // In Oracle LOBs cannot be compared. + // + ci.param_factory = 0; + } + + template <typename T> + inline query_column<T, id_clob>:: + query_column (odb::query_column<T>& qc, + const char* table, const char* column, const char*) + : lob_query_column (table, column) + { + native_column_info& ci (qc.native_info[id_oracle]); + ci.column = static_cast<query_column_base*> (this); + + // In Oracle LOBs cannot be compared. + // + ci.param_factory = 0; + } + + template <typename T> + inline query_column<T, id_nclob>:: + query_column (odb::query_column<T>& qc, + const char* table, const char* column, const char*) + : lob_query_column (table, column) + { + native_column_info& ci (qc.native_info[id_oracle]); + ci.column = static_cast<query_column_base*> (this); + + // In Oracle LOBs cannot be compared. + // + ci.param_factory = 0; + } + } +} diff --git a/libodb-oracle/odb/oracle/query-dynamic.txx b/libodb-oracle/odb/oracle/query-dynamic.txx new file mode 100644 index 0000000..a19b5c8 --- /dev/null +++ b/libodb-oracle/odb/oracle/query-dynamic.txx @@ -0,0 +1,25 @@ +// file : odb/oracle/query-dynamic.txx +// license : ODB NCUEL; see accompanying LICENSE file + +namespace odb +{ + namespace oracle + { + template <typename T, database_type_id ID> + details::shared_ptr<query_param> + query_param_factory_impl (const void* val, + const query_column_base& qc, + bool by_ref) + { + const T& v (*static_cast<const T*> (val)); + + unsigned short p (qc.prec ()); + short s (qc.scale ()); + + return details::shared_ptr<query_param> ( + by_ref + ? new (details::shared) query_param_impl<T, ID> (ref_bind<T> (v, p, s)) + : new (details::shared) query_param_impl<T, ID> (val_bind<T> (v, p, s))); + } + } +} diff --git a/libodb-oracle/odb/oracle/query.cxx b/libodb-oracle/odb/oracle/query.cxx new file mode 100644 index 0000000..890e1db --- /dev/null +++ b/libodb-oracle/odb/oracle/query.cxx @@ -0,0 +1,356 @@ +// file : odb/oracle/query.cxx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <cstddef> // std::size_t +#include <cstring> // std::memset +#include <sstream> // std::ostringstream + +#include <odb/oracle/query.hxx> + +using namespace std; + +namespace odb +{ + namespace oracle + { + // query_param + // + query_param:: + ~query_param () + { + } + + // query_base + // + query_base:: + query_base (const query_base& q) + : clause_ (q.clause_), + parameters_ (q.parameters_), + bind_ (q.bind_), + binding_ (0, 0) + { + // Here and below we want to maintain up to date binding info so + // that the call to parameters_binding() below is an immutable + // operation, provided the query does not have any by-reference + // parameters. This way a by-value-only query can be shared + // between multiple threads without the need for synchronization. + // + if (size_t n = bind_.size ()) + { + binding_.bind = &bind_[0]; + binding_.count = n; + binding_.version++; + } + } + + query_base& query_base:: + operator= (const query_base& q) + { + if (this != &q) + { + clause_ = q.clause_; + parameters_ = q.parameters_; + bind_ = q.bind_; + + size_t n (bind_.size ()); + binding_.bind = n != 0 ? &bind_[0] : 0; + binding_.count = n; + binding_.version++; + } + + return *this; + } + + query_base& query_base:: + operator+= (const query_base& q) + { + clause_.insert (clause_.end (), q.clause_.begin (), q.clause_.end ()); + + size_t n (bind_.size ()); + + parameters_.insert ( + parameters_.end (), q.parameters_.begin (), q.parameters_.end ()); + + bind_.insert ( + bind_.end (), q.bind_.begin (), q.bind_.end ()); + + if (n != bind_.size ()) + { + binding_.bind = &bind_[0]; + binding_.count = bind_.size (); + binding_.version++; + } + + return *this; + } + + void query_base:: + append (const string& q) + { + if (!clause_.empty () && + clause_.back ().kind == clause_part::kind_native) + { + string& s (clause_.back ().part); + + char first (!q.empty () ? q[0] : ' '); + char last (!s.empty () ? s[s.size () - 1] : ' '); + + // We don't want extra spaces after '(' as well as before ',' + // and ')'. + // + if (last != ' ' && last != '\n' && last != '(' && + first != ' ' && first != '\n' && first != ',' && first != ')') + s += ' '; + + s += q; + } + else + clause_.push_back (clause_part (clause_part::kind_native, q)); + } + + void query_base:: + append (const char* table, const char* column) + { + string s (table); + s += '.'; + s += column; + + clause_.push_back (clause_part (clause_part::kind_column, s)); + } + + void query_base:: + append (details::shared_ptr<query_param> p, const char* conv) + { + clause_.push_back (clause_part (clause_part::kind_param)); + + if (conv != 0) + clause_.back ().part = conv; + + parameters_.push_back (p); + bind_.push_back (bind ()); + binding_.bind = &bind_[0]; + binding_.count = bind_.size (); + binding_.version++; + + bind* b (&bind_.back ()); + memset (b, 0, sizeof (bind)); + p->bind (b); + } + + void query_base:: + init_parameters () const + { + bool inc_ver (false); + + for (size_t i (0); i < parameters_.size (); ++i) + { + query_param& p (*parameters_[i]); + + if (p.reference ()) + { + if (p.init ()) + { + p.bind (&bind_[i]); + inc_ver = true; + } + } + } + + if (inc_ver) + binding_.version++; + } + + static bool + check_prefix (const string& s) + { + string::size_type n; + + // It is easier to compare to upper and lower-case versions + // rather than getting involved with the portable case- + // insensitive string comparison mess. + // + if (s.compare (0, (n = 5), "WHERE") == 0 || + s.compare (0, (n = 5), "where") == 0 || + s.compare (0, (n = 6), "SELECT") == 0 || + s.compare (0, (n = 6), "select") == 0 || + s.compare (0, (n = 8), "ORDER BY") == 0 || + s.compare (0, (n = 8), "order by") == 0 || + s.compare (0, (n = 8), "GROUP BY") == 0 || + s.compare (0, (n = 8), "group by") == 0 || + s.compare (0, (n = 6), "HAVING") == 0 || + s.compare (0, (n = 6), "having") == 0 || + s.compare (0, (n = 4), "CALL") == 0 || + s.compare (0, (n = 4), "call") == 0) + { + // It either has to be an exact match, or there should be + // a whitespace following the keyword. + // + if (s.size () == n || s[n] == ' ' || s[n] == '\n' || s[n] =='\t') + return true; + } + + return false; + } + + void query_base:: + optimize () + { + // Remove a single TRUE literal or one that is followe by one of + // the other clauses. This avoids useless WHERE clauses like + // + // WHERE TRUE GROUP BY foo + // + clause_type::iterator i (clause_.begin ()), e (clause_.end ()); + + if (i != e && i->kind == clause_part::kind_bool && i->bool_part) + { + clause_type::iterator j (i + 1); + + if (j == e || + (j->kind == clause_part::kind_native && check_prefix (j->part))) + clause_.erase (i); + } + } + + const char* query_base:: + clause_prefix () const + { + if (!clause_.empty ()) + { + const clause_part& p (clause_.front ()); + + if (p.kind == clause_part::kind_native && check_prefix (p.part)) + return ""; + + return "WHERE "; + } + + return ""; + } + + string query_base:: + clause () const + { + string r; + size_t param (1); + + for (clause_type::const_iterator i (clause_.begin ()), + end (clause_.end ()); + i != end; + ++i) + { + char last (!r.empty () ? r[r.size () - 1] : ' '); + + switch (i->kind) + { + case clause_part::kind_column: + { + if (last != ' ' && last != '\n' && last != '(') + r += ' '; + + r += i->part; + break; + } + case clause_part::kind_param: + { + if (last != ' ' && last != '\n' && last != '(') + r += ' '; + + ostringstream os; + os << param++; + + // Add the conversion expression, if any. + // + string::size_type p (0); + if (!i->part.empty ()) + { + p = i->part.find ("(?)"); + r.append (i->part, 0, p); + } + + r += ':'; + r += os.str (); + + if (!i->part.empty ()) + r.append (i->part, p + 3, string::npos); + + break; + } + case clause_part::kind_native: + { + // We don't want extra spaces after '(' as well as before ',' + // and ')'. + // + const string& p (i->part); + char first (!p.empty () ? p[0] : ' '); + + if (last != ' ' && last != '\n' && last != '(' && + first != ' ' && first != '\n' && first != ',' && first != ')') + r += ' '; + + r += p; + break; + } + case clause_part::kind_bool: + { + if (last != ' ' && last != '\n' && last != '(') + r += ' '; + + // Oracle does not have TRUE and FALSE boolean literals (these + // are available in PL/SQL). Boolean values seem to only be + // created as the result of boolean expressions. + // + r += i->bool_part ? "1 = 1" : "1 = 0"; + break; + } + } + } + + return clause_prefix () + r; + } + + query_base + operator&& (const query_base& x, const query_base& y) + { + // Optimize cases where one or both sides are constant truth. + // + bool xt (x.const_true ()), yt (y.const_true ()); + + if (xt && yt) + return x; + + if (xt) + return y; + + if (yt) + return x; + + query_base r ("("); + r += x; + r += ") AND ("; + r += y; + r += ")"; + return r; + } + + query_base + operator|| (const query_base& x, const query_base& y) + { + query_base r ("("); + r += x; + r += ") OR ("; + r += y; + r += ")"; + return r; + } + + query_base + operator! (const query_base& x) + { + query_base r ("NOT ("); + r += x; + r += ")"; + return r; + } + } +} diff --git a/libodb-oracle/odb/oracle/query.hxx b/libodb-oracle/odb/oracle/query.hxx new file mode 100644 index 0000000..a1cbd80 --- /dev/null +++ b/libodb-oracle/odb/oracle/query.hxx @@ -0,0 +1,2261 @@ +// file : odb/oracle/query.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_QUERY_HXX +#define ODB_ORACLE_QUERY_HXX + +#include <odb/pre.hxx> + +#include <string> +#include <vector> +#include <cstddef> // std::size_t + +#include <odb/forward.hxx> // odb::query_column +#include <odb/query.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/forward.hxx> +#include <odb/oracle/traits.hxx> +#include <odb/oracle/binding.hxx> + +#include <odb/details/buffer.hxx> +#include <odb/details/shared-ptr.hxx> + +#include <odb/oracle/details/export.hxx> +#include <odb/oracle/details/conversion.hxx> + +namespace odb +{ + namespace oracle + { + // For both precision and scale 0 is a valid value. Furthermore, + // scale can be negative. To indicate that these values are not + // specified, we will use 0xFFF which is out of range for both + // precision (0 to 4000) and scale (-84 to 127). Note also that + // string size (stored in precision) is always in bytes. If a + // national string size is specified as a number of characters + // and not bytes, then this will be a conservative estimate (4 + // bytes per character). + // + template <typename T> + struct val_bind + { + typedef const T& type; + + explicit + val_bind (type v, unsigned short p = 0xFFF, short s = 0xFFF) + : val (v), prec (p), scale (s) {} + + type val; + + unsigned short prec; + short scale; + }; + + template <typename T, std::size_t N> + struct val_bind<T[N]> + { + typedef const T* type; + + explicit + val_bind (type v, unsigned short p = 0xFFF, short s = 0xFFF) + : val (v), prec (p), scale (s) {} + + type val; + + unsigned short prec; + short scale; + }; + + template <typename T> + struct ref_bind + { + typedef const T& type; + + explicit + ref_bind (type r, unsigned short p = 0xFFF, short s = 0xFFF) + : ref (r), prec (p), scale (s) {} + + const void* + ptr () const {return &ref;} + + type ref; + + unsigned short prec; + short scale; + }; + + template <typename T, std::size_t N> + struct ref_bind<T[N]> + { + typedef const T* type; + + explicit + ref_bind (type r, unsigned short p = 0xFFF, short s = 0xFFF) + : ref (r), prec (p), scale (s) {} + + // Allow implicit conversion from decayed ref_bind's. + // + ref_bind (ref_bind<T*> r): ref (r.ref), prec (r.prec), scale (r.scale) {} + ref_bind (ref_bind<const T*> r) + : ref (r.ref), prec (r.prec), scale (r.scale) {} + + const void* + ptr () const {return ref;} + + type ref; + + unsigned short prec; + short scale; + }; + + template <typename T, database_type_id ID> + struct val_bind_typed: val_bind<T> + { + explicit + val_bind_typed (typename val_bind<T>::type v, + unsigned short p = 0xFFF, + short s = 0xFFF): val_bind<T> (v, p, s) {} + }; + + template <typename T, database_type_id ID> + struct ref_bind_typed: ref_bind<T> + { + explicit + ref_bind_typed (typename ref_bind<T>::type r, + unsigned short p = 0xFFF, + short s = 0xFFF): ref_bind<T> (r, p, s) {} + }; + + struct LIBODB_ORACLE_EXPORT query_param: details::shared_base + { + typedef oracle::bind bind_type; + + virtual + ~query_param (); + + bool + reference () const + { + return value_ != 0; + } + + virtual bool + init () = 0; + + virtual void + bind (bind_type*) = 0; + + protected: + query_param (const void* value) + : value_ (value) + { + } + + protected: + const void* value_; + }; + + // + // + template <typename T, database_type_id ID> + struct query_column; + + class LIBODB_ORACLE_EXPORT query_base + { + public: + struct clause_part + { + enum kind_type + { + kind_column, + kind_param, + kind_native, + kind_bool + }; + + clause_part (kind_type k): kind (k), bool_part (false) {} + clause_part (kind_type k, const std::string& p) + : kind (k), part (p), bool_part (false) {} + clause_part (bool p): kind (kind_bool), bool_part (p) {} + + kind_type kind; + std::string part; // If kind is param, then part is conversion expr. + bool bool_part; + }; + + query_base () + : binding_ (0, 0) + { + } + + // True or false literal. + // + explicit + query_base (bool v) + : binding_ (0, 0) + { + append (v); + } + + explicit + query_base (const char* native) + : binding_ (0, 0) + { + clause_.push_back (clause_part (clause_part::kind_native, native)); + } + + explicit + query_base (const std::string& native) + : binding_ (0, 0) + { + clause_.push_back (clause_part (clause_part::kind_native, native)); + } + + query_base (const char* table, const char* column) + : binding_ (0, 0) + { + append (table, column); + } + + template <typename T> + explicit + query_base (val_bind<T> v) + : binding_ (0, 0) + { + *this += v; + } + + template <typename T, database_type_id ID> + explicit + query_base (val_bind_typed<T, ID> v) + : binding_ (0, 0) + { + *this += v; + } + + template <typename T> + explicit + query_base (ref_bind<T> r) + : binding_ (0, 0) + { + *this += r; + } + + template <typename T, database_type_id ID> + explicit + query_base (ref_bind_typed<T, ID> r) + : binding_ (0, 0) + { + *this += r; + } + + template <database_type_id ID> + query_base (const query_column<bool, ID>&); + + // Translate common query representation to Oracle native. Defined + // in query-dynamic.cxx + // + query_base (const odb::query_base&); + + // Copy c-tor and assignment. + // + query_base (const query_base&); + + query_base& + operator= (const query_base&); + + public: + std::string + clause () const; + + const char* + clause_prefix () const; + + // Initialize the by-reference parameters from bound variables. + // + void + init_parameters () const; + + binding& + parameters_binding () const; + + public: + bool + empty () const + { + return clause_.empty (); + } + + static const query_base true_expr; + + bool + const_true () const + { + return clause_.size () == 1 && + clause_.front ().kind == clause_part::kind_bool && + clause_.front ().bool_part; + } + + void + optimize (); + + public: + template <typename T> + static val_bind<T> + _val (const T& x, unsigned short prec = 0xFFF, short scale = 0xFFF) + { + return val_bind<T> (x, prec, scale); + } + + template <database_type_id ID, typename T> + static val_bind_typed<T, ID> + _val (const T& x, unsigned short prec = 0xFFF, short scale = 0xFFF) + { + return val_bind_typed<T, ID> (x, prec, scale); + } + + template <typename T> + static ref_bind<T> + _ref (const T& x, unsigned short prec = 0xFFF, short scale = 0xFFF) + { + return ref_bind<T> (x, prec, scale); + } + + template <database_type_id ID, typename T> + static ref_bind_typed<T, ID> + _ref (const T& x, unsigned short prec = 0xFFF, short scale = 0xFFF) + { + return ref_bind_typed<T, ID> (x, prec, scale); + } + + // Some compilers (notably VC++), when deducing const T& from const + // array do not strip const from the array type. As a result, in the + // above signatures we get, for example, T = const char[4] instead + // of T = char[4], which is what we want. So to "fix" such compilers, + // we will have to provide the following specializations of the above + // functions. + // + template <typename T, std::size_t N> + static val_bind<T[N]> + _val (const T (&x) [N], unsigned short prec = 0xFFF, short scale = 0xFFF) + { + return val_bind<T[N]> (x, prec, scale); + } + + template <database_type_id ID, typename T, std::size_t N> + static val_bind_typed<T[N], ID> + _val (const T (&x) [N], unsigned short prec = 0xFFF, short scale = 0xFFF) + { + return val_bind_typed<T[N], ID> (x, prec, scale); + } + + template <typename T, std::size_t N> + static ref_bind<T[N]> + _ref (const T (&x) [N], unsigned short prec = 0xFFF, short scale = 0xFFF) + { + return ref_bind<T[N]> (x, prec, scale); + } + + template <database_type_id ID, typename T, std::size_t N> + static ref_bind_typed<T[N], ID> + _ref (const T (&x) [N], unsigned short prec = 0xFFF, short scale = 0xFFF) + { + return ref_bind_typed<T[N], ID> (x, prec, scale); + } + + public: + query_base& + operator+= (const query_base&); + + query_base& + operator+= (const std::string& q) + { + append (q); + return *this; + } + + template <typename T> + query_base& + operator+= (val_bind<T> v) + { + append<T, type_traits<T>::db_type_id> ( + v, details::conversion<T>::to ()); + return *this; + } + + template <typename T, database_type_id ID> + query_base& + operator+= (val_bind_typed<T, ID> v) + { + // We are not using default type_traits so no default conversion + // either. + // + append<T, ID> (v, 0); + return *this; + } + + template <typename T> + query_base& + operator+= (ref_bind<T> r) + { + append<T, type_traits<T>::db_type_id> ( + r, details::conversion<T>::to ()); + return *this; + } + + template <typename T, database_type_id ID> + query_base& + operator+= (ref_bind_typed<T, ID> r) + { + // We are not using default type_traits so no default conversion + // either. + // + append<T, ID> (r, 0); + return *this; + } + + // Implementation details. + // + public: + template <typename T, database_type_id ID> + void + append (val_bind<T>, const char* conv); + + template <typename T, database_type_id ID> + void + append (ref_bind<T>, const char* conv); + + void + append (details::shared_ptr<query_param>, const char* conv); + + void + append (bool v) + { + clause_.push_back (clause_part (v)); + } + + void + append (const std::string& native); + + void + append (const char* native) // Clashes with append(bool). + { + append (std::string (native)); + } + + void + append (const char* table, const char* column); + + private: + typedef std::vector<clause_part> clause_type; + typedef std::vector<details::shared_ptr<query_param> > parameters_type; + + clause_type clause_; + parameters_type parameters_; + mutable std::vector<bind> bind_; + mutable binding binding_; + }; + + inline query_base + operator+ (const query_base& x, const query_base& y) + { + query_base r (x); + r += y; + return r; + } + + template <typename T> + inline query_base + operator+ (const query_base& q, val_bind<T> b) + { + query_base r (q); + r += b; + return r; + } + + template <typename T> + inline query_base + operator+ (val_bind<T> b, const query_base& q) + { + query_base r; + r += b; + r += q; + return r; + } + + template <typename T, database_type_id ID> + inline query_base + operator+ (const query_base& q, val_bind_typed<T, ID> b) + { + query_base r (q); + r += b; + return r; + } + + template <typename T, database_type_id ID> + inline query_base + operator+ (val_bind_typed<T, ID> b, const query_base& q) + { + query_base r; + r += b; + r += q; + return r; + } + + template <typename T> + inline query_base + operator+ (const query_base& q, ref_bind<T> b) + { + query_base r (q); + r += b; + return r; + } + + template <typename T> + inline query_base + operator+ (ref_bind<T> b, const query_base& q) + { + query_base r; + r += b; + r += q; + return r; + } + + template <typename T, database_type_id ID> + inline query_base + operator+ (const query_base& q, ref_bind_typed<T, ID> b) + { + query_base r (q); + r += b; + return r; + } + + template <typename T, database_type_id ID> + inline query_base + operator+ (ref_bind_typed<T, ID> b, const query_base& q) + { + query_base r; + r += b; + r += q; + return r; + } + + inline query_base + operator+ (const query_base& q, const std::string& s) + { + query_base r (q); + r += s; + return r; + } + + inline query_base + operator+ (const std::string& s, const query_base& q) + { + query_base r (s); + r += q; + return r; + } + + template <typename T> + inline query_base + operator+ (const std::string& s, val_bind<T> b) + { + query_base r (s); + r += b; + return r; + } + + template <typename T> + inline query_base + operator+ (val_bind<T> b, const std::string& s) + { + query_base r; + r += b; + r += s; + return r; + } + + template <typename T, database_type_id ID> + inline query_base + operator+ (const std::string& s, val_bind_typed<T, ID> b) + { + query_base r (s); + r += b; + return r; + } + + template <typename T, database_type_id ID> + inline query_base + operator+ (val_bind_typed<T, ID> b, const std::string& s) + { + query_base r; + r += b; + r += s; + return r; + } + + template <typename T> + inline query_base + operator+ (const std::string& s, ref_bind<T> b) + { + query_base r (s); + r += b; + return r; + } + + template <typename T> + inline query_base + operator+ (ref_bind<T> b, const std::string& s) + { + query_base r; + r += b; + r += s; + return r; + } + + template <typename T, database_type_id ID> + inline query_base + operator+ (const std::string& s, ref_bind_typed<T, ID> b) + { + query_base r (s); + r += b; + return r; + } + + template <typename T, database_type_id ID> + inline query_base + operator+ (ref_bind_typed<T, ID> b, const std::string& s) + { + query_base r; + r += b; + r += s; + return r; + } + + LIBODB_ORACLE_EXPORT query_base + operator&& (const query_base& x, const query_base& y); + + LIBODB_ORACLE_EXPORT query_base + operator|| (const query_base& x, const query_base& y); + + LIBODB_ORACLE_EXPORT query_base + operator! (const query_base& x); + + // query_column + // + struct LIBODB_ORACLE_EXPORT query_column_base + { + // Note that we keep shallow copies of the table, column, and conversion + // expression. The latter can be NULL. + // + query_column_base (const char* table, + const char* column, + const char* conv, + unsigned short prec, + short scale) + : table_ (table), column_ (column), conversion_ (conv), + prec_ (prec), scale_ (scale) + { + } + + const char* + table () const + { + return table_; + } + + const char* + column () const + { + return column_; + } + + // Can be NULL. + // + const char* + conversion () const + { + return conversion_; + } + + unsigned short + prec () const + { + return prec_; + } + + short + scale () const + { + return scale_; + } + + protected: + const char* table_; + const char* column_; + const char* conversion_; + + unsigned short prec_; + short scale_; + }; + + template <typename T, database_type_id ID> + struct query_column: query_column_base + { + typedef typename decay_traits<T>::type decayed_type; + + // Note that we keep shalow copies of the table, column, and conversion + // expression. The latter can be NULL. + // + query_column (const char* table, + const char* column, + const char* conv, + unsigned short prec = 0xFFF, + short scale = 0xFFF) + : query_column_base (table, column, conv, prec, scale) {} + + // Implementation is in query-dynamic.ixx. + // + query_column (odb::query_column<T>&, + const char* table, + const char* column, + const char* conv, + unsigned short prec = 0xFFF, + short scale = 0xFFF); + + // is_null, is_not_null + // + public: + query_base + is_null () const + { + query_base q (table_, column_); + q += "IS NULL"; + return q; + } + + query_base + is_not_null () const + { + query_base q (table_, column_); + q += "IS NOT NULL"; + return q; + } + + // in + // + public: + query_base + in (decayed_type, decayed_type) const; + + query_base + in (decayed_type, decayed_type, decayed_type) const; + + query_base + in (decayed_type, decayed_type, decayed_type, decayed_type) const; + + query_base + in (decayed_type, decayed_type, decayed_type, decayed_type, + decayed_type) const; + + template <typename I> + query_base + in_range (I begin, I end) const; + + // like + // + public: + query_base + like (decayed_type pattern) const + { + return like (val_bind<T> (pattern)); + } + + query_base + like (val_bind<T> pattern) const; + + template <typename T2> + query_base + like (val_bind<T2> pattern) const + { + return like (val_bind<T> (decayed_type (pattern.val))); + } + + query_base + like (ref_bind<T> pattern) const; + + query_base + like (decayed_type pattern, decayed_type escape) const + { + return like (val_bind<T> (pattern), escape); + } + + query_base + like (val_bind<T> pattern, decayed_type escape) const; + + template <typename T2> + query_base + like (val_bind<T2> pattern, decayed_type escape) const + { + return like (val_bind<T> (decayed_type (pattern.val)), escape); + } + + query_base + like (ref_bind<T> pattern, decayed_type escape) const; + + // = + // + public: + query_base + equal (decayed_type v) const + { + return equal (val_bind<T> (v)); + } + + query_base + equal (val_bind<T> v) const + { + v.prec = prec_; + v.scale = scale_; + + query_base q (table_, column_); + q += "="; + q.append<T, ID> (v, conversion_); + return q; + } + + template <typename T2> + query_base + equal (val_bind<T2> v) const + { + return equal (val_bind<T> (decayed_type (v.val))); + } + + query_base + equal (ref_bind<T> r) const + { + r.prec = prec_; + r.scale = scale_; + + query_base q (table_, column_); + q += "="; + q.append<T, ID> (r, conversion_); + return q; + } + + friend query_base + operator== (const query_column& c, decayed_type v) + { + return c.equal (v); + } + + friend query_base + operator== (decayed_type v, const query_column& c) + { + return c.equal (v); + } + + friend query_base + operator== (const query_column& c, val_bind<T> v) + { + return c.equal (v); + } + + friend query_base + operator== (val_bind<T> v, const query_column& c) + { + return c.equal (v); + } + + template <typename T2> + friend query_base + operator== (const query_column& c, val_bind<T2> v) + { + return c.equal (v); + } + + template <typename T2> + friend query_base + operator== (val_bind<T2> v, const query_column& c) + { + return c.equal (v); + } + + friend query_base + operator== (const query_column& c, ref_bind<T> r) + { + return c.equal (r); + } + + friend query_base + operator== (ref_bind<T> r, const query_column& c) + { + return c.equal (r); + } + + // != + // + public: + query_base + unequal (decayed_type v) const + { + return unequal (val_bind<T> (v)); + } + + query_base + unequal (val_bind<T> v) const + { + v.prec = prec_; + v.scale = scale_; + + query_base q (table_, column_); + q += "!="; + q.append<T, ID> (v, conversion_); + return q; + } + + template <typename T2> + query_base + unequal (val_bind<T2> v) const + { + return unequal (val_bind<T> (decayed_type (v.val))); + } + + query_base + unequal (ref_bind<T> r) const + { + r.prec = prec_; + r.scale = scale_; + + query_base q (table_, column_); + q += "!="; + q.append<T, ID> (r, conversion_); + return q; + } + + friend query_base + operator!= (const query_column& c, decayed_type v) + { + return c.unequal (v); + } + + friend query_base + operator!= (decayed_type v, const query_column& c) + { + return c.unequal (v); + } + + friend query_base + operator!= (const query_column& c, val_bind<T> v) + { + return c.unequal (v); + } + + friend query_base + operator!= (val_bind<T> v, const query_column& c) + { + return c.unequal (v); + } + + template <typename T2> + friend query_base + operator!= (const query_column& c, val_bind<T2> v) + { + return c.unequal (v); + } + + template <typename T2> + friend query_base + operator!= (val_bind<T2> v, const query_column& c) + { + return c.unequal (v); + } + + friend query_base + operator!= (const query_column& c, ref_bind<T> r) + { + return c.unequal (r); + } + + friend query_base + operator!= (ref_bind<T> r, const query_column& c) + { + return c.unequal (r); + } + + // < + // + public: + query_base + less (decayed_type v) const + { + return less (val_bind<T> (v)); + } + + query_base + less (val_bind<T> v) const + { + v.prec = prec_; + v.scale = scale_; + + query_base q (table_, column_); + q += "<"; + q.append<T, ID> (v, conversion_); + return q; + } + + template <typename T2> + query_base + less (val_bind<T2> v) const + { + return less (val_bind<T> (decayed_type (v.val))); + } + + query_base + less (ref_bind<T> r) const + { + r.prec = prec_; + r.scale = scale_; + + query_base q (table_, column_); + q += "<"; + q.append<T, ID> (r, conversion_); + return q; + } + + friend query_base + operator< (const query_column& c, decayed_type v) + { + return c.less (v); + } + + friend query_base + operator< (decayed_type v, const query_column& c) + { + return c.greater (v); + } + + friend query_base + operator< (const query_column& c, val_bind<T> v) + { + return c.less (v); + } + + friend query_base + operator< (val_bind<T> v, const query_column& c) + { + return c.greater (v); + } + + template <typename T2> + friend query_base + operator< (const query_column& c, val_bind<T2> v) + { + return c.less (v); + } + + template <typename T2> + friend query_base + operator< (val_bind<T2> v, const query_column& c) + { + return c.greater (v); + } + + friend query_base + operator< (const query_column& c, ref_bind<T> r) + { + return c.less (r); + } + + friend query_base + operator< (ref_bind<T> r, const query_column& c) + { + return c.greater (r); + } + + // > + // + public: + query_base + greater (decayed_type v) const + { + return greater (val_bind<T> (v)); + } + + query_base + greater (val_bind<T> v) const + { + v.prec = prec_; + v.scale = scale_; + + query_base q (table_, column_); + q += ">"; + q.append<T, ID> (v, conversion_); + return q; + } + + template <typename T2> + query_base + greater (val_bind<T2> v) const + { + return greater (val_bind<T> (decayed_type (v.val))); + } + + query_base + greater (ref_bind<T> r) const + { + r.prec = prec_; + r.scale = scale_; + + query_base q (table_, column_); + q += ">"; + q.append<T, ID> (r, conversion_); + return q; + } + + friend query_base + operator> (const query_column& c, decayed_type v) + { + return c.greater (v); + } + + friend query_base + operator> (decayed_type v, const query_column& c) + { + return c.less (v); + } + + friend query_base + operator> (const query_column& c, val_bind<T> v) + { + return c.greater (v); + } + + friend query_base + operator> (val_bind<T> v, const query_column& c) + { + return c.less (v); + } + + template <typename T2> + friend query_base + operator> (const query_column& c, val_bind<T2> v) + { + return c.greater (v); + } + + template <typename T2> + friend query_base + operator> (val_bind<T2> v, const query_column& c) + { + return c.less (v); + } + + friend query_base + operator> (const query_column& c, ref_bind<T> r) + { + return c.greater (r); + } + + friend query_base + operator> (ref_bind<T> r, const query_column& c) + { + return c.less (r); + } + + // <= + // + public: + query_base + less_equal (decayed_type v) const + { + return less_equal (val_bind<T> (v)); + } + + query_base + less_equal (val_bind<T> v) const + { + v.prec = prec_; + v.scale = scale_; + + query_base q (table_, column_); + q += "<="; + q.append<T, ID> (v, conversion_); + return q; + } + + template <typename T2> + query_base + less_equal (val_bind<T2> v) const + { + return less_equal (val_bind<T> (decayed_type (v.val))); + } + + query_base + less_equal (ref_bind<T> r) const + { + r.prec = prec_; + r.scale = scale_; + + query_base q (table_, column_); + q += "<="; + q.append<T, ID> (r, conversion_); + return q; + } + + friend query_base + operator<= (const query_column& c, decayed_type v) + { + return c.less_equal (v); + } + + friend query_base + operator<= (decayed_type v, const query_column& c) + { + return c.greater_equal (v); + } + + friend query_base + operator<= (const query_column& c, val_bind<T> v) + { + return c.less_equal (v); + } + + friend query_base + operator<= (val_bind<T> v, const query_column& c) + { + return c.greater_equal (v); + } + + template <typename T2> + friend query_base + operator<= (const query_column& c, val_bind<T2> v) + { + return c.less_equal (v); + } + + template <typename T2> + friend query_base + operator<= (val_bind<T2> v, const query_column& c) + { + return c.greater_equal (v); + } + + friend query_base + operator<= (const query_column& c, ref_bind<T> r) + { + return c.less_equal (r); + } + + friend query_base + operator<= (ref_bind<T> r, const query_column& c) + { + return c.greater_equal (r); + } + + // >= + // + public: + query_base + greater_equal (decayed_type v) const + { + return greater_equal (val_bind<T> (v)); + } + + query_base + greater_equal (val_bind<T> v) const + { + v.prec = prec_; + v.scale = scale_; + + query_base q (table_, column_); + q += ">="; + q.append<T, ID> (v, conversion_); + return q; + } + + template <typename T2> + query_base + greater_equal (val_bind<T2> v) const + { + return greater_equal (val_bind<T> (decayed_type (v.val))); + } + + query_base + greater_equal (ref_bind<T> r) const + { + r.prec = prec_; + r.scale = scale_; + + query_base q (table_, column_); + q += ">="; + q.append<T, ID> (r, conversion_); + return q; + } + + friend query_base + operator>= (const query_column& c, decayed_type v) + { + return c.greater_equal (v); + } + + friend query_base + operator>= (decayed_type v, const query_column& c) + { + return c.less_equal (v); + } + + friend query_base + operator>= (const query_column& c, val_bind<T> v) + { + return c.greater_equal (v); + } + + friend query_base + operator>= (val_bind<T> v, const query_column& c) + { + return c.less_equal (v); + } + + template <typename T2> + friend query_base + operator>= (const query_column& c, val_bind<T2> v) + { + return c.greater_equal (v); + } + + template <typename T2> + friend query_base + operator>= (val_bind<T2> v, const query_column& c) + { + return c.less_equal (v); + } + + friend query_base + operator>= (const query_column& c, ref_bind<T> r) + { + return c.greater_equal (r); + } + + friend query_base + operator>= (ref_bind<T> r, const query_column& c) + { + return c.less_equal (r); + } + + // Column comparison. + // + public: + template <typename T2, database_type_id ID2> + query_base + operator== (const query_column<T2, ID2>& c) const + { + // We can compare columns only if we can compare their C++ types. + // + (void) (sizeof (decay_traits<T>::instance () == + decay_traits<T2>::instance ())); + + query_base q (table_, column_); + q += "="; + q.append (c.table (), c.column ()); + return q; + } + + template <typename T2, database_type_id ID2> + query_base + operator!= (const query_column<T2, ID2>& c) const + { + // We can compare columns only if we can compare their C++ types. + // + (void) (sizeof (decay_traits<T>::instance () != + decay_traits<T2>::instance ())); + + query_base q (table_, column_); + q += "!="; + q.append (c.table (), c.column ()); + return q; + } + + template <typename T2, database_type_id ID2> + query_base + operator< (const query_column<T2, ID2>& c) const + { + // We can compare columns only if we can compare their C++ types. + // + (void) (sizeof (decay_traits<T>::instance () < + decay_traits<T2>::instance ())); + + query_base q (table_, column_); + q += "<"; + q.append (c.table (), c.column ()); + return q; + } + + template <typename T2, database_type_id ID2> + query_base + operator> (const query_column<T2, ID2>& c) const + { + // We can compare columns only if we can compare their C++ types. + // + (void) (sizeof (decay_traits<T>::instance () > + decay_traits<T2>::instance ())); + + query_base q (table_, column_); + q += ">"; + q.append (c.table (), c.column ()); + return q; + } + + template <typename T2, database_type_id ID2> + query_base + operator<= (const query_column<T2, ID2>& c) const + { + // We can compare columns only if we can compare their C++ types. + // + (void) (sizeof (decay_traits<T>::instance () <= + decay_traits<T2>::instance ())); + + query_base q (table_, column_); + q += "<="; + q.append (c.table (), c.column ()); + return q; + } + + template <typename T2, database_type_id ID2> + query_base + operator>= (const query_column<T2, ID2>& c) const + { + // We can compare columns only if we can compare their C++ types. + // + (void) (sizeof (decay_traits<T>::instance () >= + decay_traits<T2>::instance ())); + + query_base q (table_, column_); + q += ">="; + q.append (c.table (), c.column ()); + return q; + } + }; + + // + // Oracle does not support comparison operations between LOB columns. + // query_column therefore only supports the IS NULL and IS NOT NULL + // predicates for these types. + // + + struct LIBODB_ORACLE_EXPORT lob_query_column: query_column_base + { + // Note that we keep shallow copies of the table and column names. + // There is also no need for conversion expression since the only + // valid tests are is IS NULL/IS NOT NULL. + // + lob_query_column (const char* table, const char* column) + : query_column_base (table, column, 0, 0xFFF, 0xFFF) + { + } + + // is_null, is_not_null + // + public: + query_base + is_null () const + { + query_base q (table_, column_); + q += "IS NULL"; + return q; + } + + query_base + is_not_null () const + { + query_base q (table_, column_); + q += "IS NOT NULL"; + return q; + } + }; + + template <typename T> + struct query_column<T, id_blob>: lob_query_column + { + query_column (const char* table, const char* column, const char*) + : lob_query_column (table, column) + { + } + + // Implementation is in query-dynamic.ixx. + // + query_column (odb::query_column<T>&, + const char* table, const char* column, const char*); + }; + + template <typename T> + struct query_column<T, id_clob>: lob_query_column + { + query_column (const char* table, const char* column, const char*) + : lob_query_column (table, column) + { + } + + // Implementation is in query-dynamic.ixx. + // + query_column (odb::query_column<T>&, + const char* table, const char* column, const char*); + }; + + template <typename T> + struct query_column<T, id_nclob>: lob_query_column + { + query_column (const char* table, const char* column, const char*) + : lob_query_column (table, column) + { + } + + // Implementation is in query-dynamic.ixx. + // + query_column (odb::query_column<T>&, + const char* table, const char* column, const char*); + }; + + // Provide operator+() for using columns to construct native + // query fragments (e.g., ORDER BY). + // + template <typename T, database_type_id ID> + inline query_base + operator+ (const query_column<T, ID>& c, const std::string& s) + { + query_base q (c.table (), c.column ()); + q += s; + return q; + } + + template <typename T, database_type_id ID> + inline query_base + operator+ (const std::string& s, const query_column<T, ID>& c) + { + query_base q (s); + q.append (c.table (), c.column ()); + return q; + } + + template <typename T, database_type_id ID> + inline query_base + operator+ (const query_column<T, ID>& c, const query_base& q) + { + query_base r (c.table (), c.column ()); + r += q; + return r; + } + + template <typename T, database_type_id ID> + inline query_base + operator+ (const query_base& q, const query_column<T, ID>& c) + { + query_base r (q); + r.append (c.table (), c.column ()); + return r; + } + + // + // + template <typename T, database_type_id> + struct query_param_impl; + + // id_int32. + // + template <typename T> + struct query_param_impl<T, id_int32>: query_param + { + query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {} + query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);} + + virtual bool + init () + { + init (*static_cast<const T*> (value_)); + return false; + } + + virtual void + bind (bind_type* b) + { + b->type = image_traits<T, id_int32>::buffer_type; + b->buffer = &image_; + b->capacity = sizeof (image_); + b->size = 0; + } + + private: + void + init (typename decay_traits<T>::type v) + { + bool is_null (false); // Can't be NULL. + value_traits<T, id_int32>::set_image (image_, is_null, v); + } + + private: + typename image_traits<T, id_int32>::image_type image_; + }; + + // id_int64. + // + template <typename T> + struct query_param_impl<T, id_int64>: query_param + { + query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {} + query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);} + + virtual bool + init () + { + init (*static_cast<const T*> (value_)); + return false; + } + + virtual void + bind (bind_type* b) + { + b->type = image_traits<T, id_int64>::buffer_type; + b->buffer = &image_; + b->capacity = sizeof (image_); + b->size = 0; + } + + private: + void + init (typename decay_traits<T>::type v) + { + bool is_null (false); // Can't be NULL. + value_traits<T, id_int64>::set_image (image_, is_null, v); + } + + private: + typename image_traits<T, id_int64>::image_type image_; + }; + + // id_big_int. + // + template <typename T> + struct query_param_impl<T, id_big_int>: query_param + { + query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {} + query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);} + + virtual bool + init () + { + init (*static_cast<const T*> (value_)); + return false; + } + + virtual void + bind (bind_type* b) + { + b->type = bind_type::number; + b->buffer = &image_; + b->capacity = sizeof (image_); + b->size = &size_; + } + + private: + void + init (typename decay_traits<T>::type v) + { + bool is_null (false); // Can't be NULL. + std::size_t size (0); + value_traits<T, id_big_int>::set_image (image_, size, is_null, v); + size_ = static_cast<ub2> (size); + } + + private: + char image_[21]; + ub2 size_; + }; + + // id_float. + // + template <typename T> + struct query_param_impl<T, id_float>: query_param + { + query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {} + query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);} + + virtual bool + init () + { + init (*static_cast<const T*> (value_)); + return false; + } + + virtual void + bind (bind_type* b) + { + b->type = bind_type::binary_float; + b->buffer = &image_; + b->capacity = sizeof (image_); + b->size = 0; + } + + private: + void + init (typename decay_traits<T>::type v) + { + bool is_null (false); // Can't be NULL. + value_traits<T, id_float>::set_image (image_, is_null, v); + } + + private: + float image_; + }; + + // id_double. + // + template <typename T> + struct query_param_impl<T, id_double>: query_param + { + query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {} + query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);} + + virtual bool + init () + { + init (*static_cast<const T*> (value_)); + return false; + } + + virtual void + bind (bind_type* b) + { + b->type = bind_type::binary_double; + b->buffer = &image_; + b->capacity = sizeof (image_); + b->size = 0; + } + + private: + void + init (typename decay_traits<T>::type v) + { + bool is_null (false); // Can't be NULL. + value_traits<T, id_double>::set_image (image_, is_null, v); + } + + private: + double image_; + }; + + // id_big_float. + // + template <typename T> + struct query_param_impl<T, id_big_float>: query_param + { + query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {} + query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);} + + virtual bool + init () + { + init (*static_cast<const T*> (value_)); + return false; + } + + virtual void + bind (bind_type* b) + { + b->type = bind_type::number; + b->buffer = &image_; + b->capacity = sizeof (image_); + b->size = &size_; + } + + private: + void + init (typename decay_traits<T>::type v) + { + bool is_null (false); // Can't be NULL. + std::size_t size (0); + value_traits<T, id_big_float>::set_image (image_, size, is_null, v); + size_ = static_cast<ub2> (size); + } + + private: + char image_[21]; + ub2 size_; + }; + + // id_date. + // + template <typename T> + struct query_param_impl<T, id_date>: query_param + { + query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {} + query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);} + + virtual bool + init () + { + init (*static_cast<const T*> (value_)); + return false; + } + + virtual void + bind (bind_type* b) + { + b->type = bind_type::date; + b->buffer = &image_; + b->capacity = sizeof (image_); + b->size = 0; + } + + private: + void + init (typename decay_traits<T>::type v) + { + bool is_null (false); // Can't be NULL. + value_traits<T, id_date>::set_image (image_, is_null, v); + } + + private: + char image_[7]; + }; + + // id_timestamp + // + template <typename T> + struct query_param_impl<T, id_timestamp>: query_param + { + query_param_impl (ref_bind<T> r) + : query_param (r.ptr ()), + image_ (descriptor_cache) // Cache, don't free. + { + } + + query_param_impl (val_bind<T> v) + : query_param (0), + image_ (0) // Don't cache, don't free. + { + init (v.val); + } + + virtual bool + init () + { + init (*static_cast<const T*> (value_)); + return false; + } + + virtual void + bind (bind_type* b) + { + b->type = bind_type::timestamp; + b->buffer = &image_; + b->capacity = sizeof (OCIDateTime*); + b->size = 0; + } + + private: + void + init (typename decay_traits<T>::type v) + { + bool is_null (false); // Can't be NULL. + value_traits<T, id_timestamp>::set_image (image_, is_null, v); + } + + private: + datetime image_; + }; + + // id_interval_ym + // + template <typename T> + struct query_param_impl<T, id_interval_ym>: query_param + { + query_param_impl (ref_bind<T> r) + : query_param (r.ptr ()), + image_ (descriptor_cache) // Cache, don't free. + { + } + + query_param_impl (val_bind<T> v) + : query_param (0), + image_ (0) // Don't cache, don't free. + { + init (v.val); + } + + virtual bool + init () + { + init (*static_cast<const T*> (value_)); + return false; + } + + virtual void + bind (bind_type* b) + { + b->type = bind_type::interval_ym; + b->buffer = &image_; + b->capacity = sizeof (OCIInterval*); + b->size = 0; + } + + private: + void + init (typename decay_traits<T>::type v) + { + bool is_null (false); // Can't be NULL. + value_traits<T, id_interval_ym>::set_image (image_, is_null, v); + } + + private: + interval_ym image_; + }; + + // id_interval_ds + // + template <typename T> + struct query_param_impl<T, id_interval_ds>: query_param + { + query_param_impl (ref_bind<T> r) + : query_param (r.ptr ()), + image_ (descriptor_cache) // Cache, don't free. + { + } + + query_param_impl (val_bind<T> v) + : query_param (0), + image_ (0) // Don't cache, don't free. + + { + init (v.val); + } + + virtual bool + init () + { + init (*static_cast<const T*> (value_)); + return false; + } + + virtual void + bind (bind_type* b) + { + b->type = bind_type::interval_ds; + b->buffer = &image_; + b->capacity = sizeof (OCIInterval*); + b->size = 0; + } + + private: + void + init (typename decay_traits<T>::type v) + { + bool is_null (false); // Can't be NULL. + value_traits<T, id_interval_ds>::set_image (image_, is_null, v); + } + + private: + interval_ds image_; + }; + + // id_string. + // + template <typename T> + struct query_param_impl<T, id_string>: query_param + { + query_param_impl (ref_bind<T> r) + : query_param (r.ptr ()), + // Default to max (4000). + buf_ (r.prec != 0xFFF ? r.prec : 4000) + { + } + + query_param_impl (val_bind<T> v) + : query_param (0), + // Default to max (4000). + buf_ (v.prec != 0xFFF ? v.prec : 4000) + + { + init (v.val); + } + + virtual bool + init () + { + init (*static_cast<const T*> (value_)); + return false; + } + + virtual void + bind (bind_type* b) + { + b->type = bind_type::string; + b->buffer = buf_.data (); + b->capacity = static_cast<ub4> (buf_.capacity ()); + b->size = &size_; + } + + private: + void + init (typename decay_traits<T>::type v) + { + bool is_null (false); // Can't be NULL. + std::size_t size (0); + value_traits<T, id_string>::set_image ( + buf_.data (), buf_.capacity (), size, is_null, v); + size_ = static_cast<ub2> (size); + } + + private: + details::buffer buf_; + ub2 size_; + }; + + // id_nstring + // + template <typename T> + struct query_param_impl<T, id_nstring>: query_param + { + query_param_impl (ref_bind<T> r) + : query_param (r.ptr ()), + // Default to max (4000). + buf_ (r.prec != 0xFFF ? r.prec : 4000) + { + } + + query_param_impl (val_bind<T> v) + : query_param (0), + // Default to max (4000). + buf_ (v.prec != 0xFFF ? v.prec : 4000) + + { + init (v.val); + } + + virtual bool + init () + { + init (*static_cast<const T*> (value_)); + return false; + } + + virtual void + bind (bind_type* b) + { + b->type = bind_type::nstring; + b->buffer = buf_.data (); + b->capacity = static_cast<ub4> (buf_.capacity ()); + b->size = &size_; + } + + private: + void + init (typename decay_traits<T>::type v) + { + bool is_null (false); // Can't be NULL. + std::size_t size (0); + value_traits<T, id_nstring>::set_image ( + buf_.data (), buf_.capacity (), size, is_null, v); + size_ = static_cast<ub2> (size); + } + + private: + details::buffer buf_; + ub2 size_; + }; + + // id_raw + // + template <typename T> + struct query_param_impl<T, id_raw>: query_param + { + query_param_impl (ref_bind<T> r) + : query_param (r.ptr ()), + // Default to max (2000). + buf_ (r.prec != 0xFFF ? r.prec : 2000) + { + } + + query_param_impl (val_bind<T> v) + : query_param (0), + // Default to max (2000). + buf_ (v.prec != 0xFFF ? v.prec : 2000) + + { + init (v.val); + } + + virtual bool + init () + { + init (*static_cast<const T*> (value_)); + return false; + } + + virtual void + bind (bind_type* b) + { + b->type = bind_type::raw; + b->buffer = buf_.data (); + b->capacity = static_cast<ub4> (buf_.capacity ()); + b->size = &size_; + } + + private: + void + init (typename decay_traits<T>::type v) + { + bool is_null (false); // Can't be NULL. + std::size_t size (0); + value_traits<T, id_raw>::set_image ( + buf_.data (), buf_.capacity (), size, is_null, v); + size_ = static_cast<ub2> (size); + } + + private: + details::buffer buf_; + ub2 size_; + }; + } +} + +// odb::oracle::query and odb::query specialization for Oracle. +// +namespace odb +{ + namespace oracle + { + template <typename T> + class query: public query_base, + public query_selector<T, id_oracle>::columns_type + { + public: + // We don't define any typedefs here since they may clash with + // column names defined by our base type. + // + + query () + { + } + + explicit + query (bool v) + : query_base (v) + { + } + + explicit + query (const char* q) + : query_base (q) + { + } + + explicit + query (const std::string& q) + : query_base (q) + { + } + + template <typename T2> + explicit + query (val_bind<T2> v) + : query_base (v) + { + } + + template <typename T2> + explicit + query (ref_bind<T2> r) + : query_base (r) + { + } + + query (const query_base& q) + : query_base (q) + { + } + + template <database_type_id ID> + query (const query_column<bool, ID>& qc) + : query_base (qc) + { + } + + query (const odb::query_base& q) + : query_base (q) + { + } + }; + + namespace core + { + using oracle::query; + } + } + + // Derive odb::query from odb::oracle::query so that it can be + // implicitly converted in oracle::database::query() calls. + // + template <typename T> + class query<T, oracle::query_base>: public oracle::query<T> + { + public: + // We don't define any typedefs here since they may clash with + // column names defined by our base type. + // + + query () + { + } + + explicit + query (bool v) + : oracle::query<T> (v) + { + } + + explicit + query (const char* q) + : oracle::query<T> (q) + { + } + + explicit + query (const std::string& q) + : oracle::query<T> (q) + { + } + + template <typename T2> + explicit + query (oracle::val_bind<T2> v) + : oracle::query<T> (v) + { + } + + template <typename T2> + explicit + query (oracle::ref_bind<T2> r) + : oracle::query<T> (r) + { + } + + query (const oracle::query_base& q) + : oracle::query<T> (q) + { + } + + template <oracle::database_type_id ID> + query (const oracle::query_column<bool, ID>& qc) + : oracle::query<T> (qc) + { + } + }; +} + +#include <odb/oracle/query.ixx> +#include <odb/oracle/query.txx> + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_QUERY_HXX diff --git a/libodb-oracle/odb/oracle/query.ixx b/libodb-oracle/odb/oracle/query.ixx new file mode 100644 index 0000000..48caec3 --- /dev/null +++ b/libodb-oracle/odb/oracle/query.ixx @@ -0,0 +1,34 @@ +// file : odb/oracle/query.ixx +// license : ODB NCUEL; see accompanying LICENSE file + +namespace odb +{ + namespace oracle + { + inline binding& query_base:: + parameters_binding () const + { + return binding_; + } + + template <typename T, database_type_id ID> + inline void query_base:: + append (val_bind<T> v, const char* conv) + { + append ( + details::shared_ptr<query_param> ( + new (details::shared) query_param_impl<T, ID> (v)), + conv); + } + + template <typename T, database_type_id ID> + inline void query_base:: + append (ref_bind<T> r, const char* conv) + { + append ( + details::shared_ptr<query_param> ( + new (details::shared) query_param_impl<T, ID> (r)), + conv); + } + } +} diff --git a/libodb-oracle/odb/oracle/query.txx b/libodb-oracle/odb/oracle/query.txx new file mode 100644 index 0000000..65f2858 --- /dev/null +++ b/libodb-oracle/odb/oracle/query.txx @@ -0,0 +1,169 @@ +// file : odb/oracle/query.txx +// license : ODB NCUEL; see accompanying LICENSE file + +namespace odb +{ + namespace oracle + { + // + // query_base + // + + template <database_type_id ID> + query_base:: + query_base (const query_column<bool, ID>& c) + : binding_ (0, 0) + { + // Cannot use IS TRUE here since database type can be a non- + // integral type. + // + append (c.table (), c.column ()); + append ("="); + append<bool, ID> (val_bind<bool> (true, c.prec (), c.scale ()), + c.conversion ()); + } + + // + // query_column + // + + // in + // + template <typename T, database_type_id ID> + query_base query_column<T, ID>:: + in (decayed_type v1, decayed_type v2) const + { + query_base q (table_, column_); + q += "IN ("; + q.append<T, ID> (val_bind<T> (v1, prec_, scale_), conversion_); + q += ","; + q.append<T, ID> (val_bind<T> (v2, prec_, scale_), conversion_); + q += ")"; + return q; + } + + template <typename T, database_type_id ID> + query_base query_column<T, ID>:: + in (decayed_type v1, decayed_type v2, decayed_type v3) const + { + query_base q (table_, column_); + q += "IN ("; + q.append<T, ID> (val_bind<T> (v1, prec_, scale_), conversion_); + q += ","; + q.append<T, ID> (val_bind<T> (v2, prec_, scale_), conversion_); + q += ","; + q.append<T, ID> (val_bind<T> (v3, prec_, scale_), conversion_); + q += ")"; + return q; + } + + template <typename T, database_type_id ID> + query_base query_column<T, ID>:: + in (decayed_type v1, decayed_type v2, decayed_type v3, + decayed_type v4) const + { + query_base q (table_, column_); + q += "IN ("; + q.append<T, ID> (val_bind<T> (v1, prec_, scale_), conversion_); + q += ","; + q.append<T, ID> (val_bind<T> (v2, prec_, scale_), conversion_); + q += ","; + q.append<T, ID> (val_bind<T> (v3, prec_, scale_), conversion_); + q += ","; + q.append<T, ID> (val_bind<T> (v4, prec_, scale_), conversion_); + q += ")"; + return q; + } + + template <typename T, database_type_id ID> + query_base query_column<T, ID>:: + in (decayed_type v1, decayed_type v2, decayed_type v3, decayed_type v4, + decayed_type v5) const + { + query_base q (table_, column_); + q += "IN ("; + q.append<T, ID> (val_bind<T> (v1, prec_, scale_), conversion_); + q += ","; + q.append<T, ID> (val_bind<T> (v2, prec_, scale_), conversion_); + q += ","; + q.append<T, ID> (val_bind<T> (v3, prec_, scale_), conversion_); + q += ","; + q.append<T, ID> (val_bind<T> (v4, prec_, scale_), conversion_); + q += ","; + q.append<T, ID> (val_bind<T> (v5, prec_, scale_), conversion_); + q += ")"; + return q; + } + + template <typename T, database_type_id ID> + template <typename I> + query_base query_column<T, ID>:: + in_range (I begin, I end) const + { + if (begin != end) + { + query_base q (table_, column_); + q += "IN ("; + + for (I i (begin); i != end; ++i) + { + if (i != begin) + q += ","; + + q.append<T, ID> (val_bind<T> (*i, prec_, scale_), conversion_); + } + + q += ")"; + return q; + } + else + return query_base (false); + } + + // like + // + template <typename T, database_type_id ID> + query_base query_column<T, ID>:: + like (val_bind<T> p) const + { + query_base q (table_, column_); + q += "LIKE"; + q.append<T, ID> (p, conversion_); + return q; + } + + template <typename T, database_type_id ID> + query_base query_column<T, ID>:: + like (ref_bind<T> p) const + { + query_base q (table_, column_); + q += "LIKE"; + q.append<T, ID> (p, conversion_); + return q; + } + + template <typename T, database_type_id ID> + query_base query_column<T, ID>:: + like (val_bind<T> p, decayed_type e) const + { + query_base q (table_, column_); + q += "LIKE"; + q.append<T, ID> (p, conversion_); + q += "ESCAPE"; + q.append<T, ID> (val_bind<T> (e), conversion_); + return q; + } + + template <typename T, database_type_id ID> + query_base query_column<T, ID>:: + like (ref_bind<T> p, decayed_type e) const + { + query_base q (table_, column_); + q += "LIKE"; + q.append<T, ID> (p, conversion_); + q += "ESCAPE"; + q.append<T, ID> (val_bind<T> (e), conversion_); + return q; + } + } +} diff --git a/libodb-oracle/odb/oracle/section-statements.hxx b/libodb-oracle/odb/oracle/section-statements.hxx new file mode 100644 index 0000000..1819fb1 --- /dev/null +++ b/libodb-oracle/odb/oracle/section-statements.hxx @@ -0,0 +1,195 @@ +// file : odb/oracle/section-statements.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_SECTION_STATEMENTS_HXX +#define ODB_ORACLE_SECTION_STATEMENTS_HXX + +#include <odb/pre.hxx> + +#include <cstddef> // std::size_t + +#include <odb/forward.hxx> +#include <odb/schema-version.hxx> +#include <odb/traits.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/oracle-types.hxx> +#include <odb/oracle/binding.hxx> +#include <odb/oracle/statement.hxx> +#include <odb/oracle/connection.hxx> +#include <odb/oracle/database.hxx> +#include <odb/oracle/details/export.hxx> + +namespace odb +{ + namespace oracle + { + class connection; + + // Template argument is the section traits type. + // + template <typename T, typename ST> + class section_statements + { + public: + typedef ST traits; + + typedef typename traits::image_type image_type; + typedef typename traits::id_image_type id_image_type; + + typedef oracle::select_statement select_statement_type; + typedef oracle::update_statement update_statement_type; + + typedef oracle::connection connection_type; + + section_statements (connection_type&, + image_type&, id_image_type&, + binding& id, binding& idv); + + connection_type& + connection () {return conn_;} + + const schema_version_migration& + version_migration (const char* name = "") const + { + if (svm_ == 0) + svm_ = &conn_.database ().schema_version_migration (name); + + return *svm_; + } + + image_type& + image () {return image_;} + + const binding& + id_binding () {return id_binding_;} + + // Id and optimistic concurrency version (if any). + // + const binding& + idv_binding () {return idv_binding_;} + + // Select binding. + // + std::size_t + select_image_version () const { return select_image_version_;} + + void + select_image_version (std::size_t v) {select_image_version_ = v;} + + binding& + select_image_binding () {return select_image_binding_;} + + // Update binding. + // + std::size_t + update_image_version () const { return update_image_version_;} + + void + update_image_version (std::size_t v) {update_image_version_ = v;} + + std::size_t + update_id_binding_version () const { return update_id_binding_version_;} + + void + update_id_binding_version (std::size_t v) {update_id_binding_version_ = v;} + + binding& + update_image_binding () {return update_image_binding_;} + + // + // Statements. + // + + select_statement_type& + select_statement () + { + if (select_ == 0) + select_.reset ( + new (details::shared) select_statement_type ( + conn_, + traits::select_statement, + traits::versioned, // Process if versioned. + false, // Don't optimize. + id_binding_, + select_image_binding_, + 4096)); // Hardcode a 4kB LOB prefetch size. + + return *select_; + } + + update_statement_type& + update_statement () + { + if (update_ == 0) + update_.reset ( + new (details::shared) update_statement_type ( + conn_, + traits::update_statement, + traits::versioned, // Process if versioned. + update_image_binding_)); + + return *update_; + } + + public: + static const std::size_t id_column_count = traits::id_column_count; + static const std::size_t managed_optimistic_load_column_count = + traits::managed_optimistic_load_column_count; + static const std::size_t managed_optimistic_update_column_count = + traits::managed_optimistic_update_column_count; + static const std::size_t select_column_count = traits::load_column_count; + static const std::size_t update_column_count = + traits::update_column_count; + + private: + section_statements (const section_statements&); + section_statements& operator= (const section_statements&); + + protected: + connection_type& conn_; + mutable const schema_version_migration* svm_; + + // These come from object_statements. + // + image_type& image_; + binding& id_binding_; + binding& idv_binding_; + + // Select binding. + // + std::size_t select_image_version_; + + static const std::size_t select_bind_count = + select_column_count != 0 || managed_optimistic_load_column_count != 0 + ? select_column_count + managed_optimistic_load_column_count + : 1; + + binding select_image_binding_; + bind select_image_bind_[select_bind_count]; + + // Update binding. + // + std::size_t update_image_version_; + std::size_t update_id_binding_version_; + + static const std::size_t update_bind_count = + update_column_count != 0 || managed_optimistic_update_column_count != 0 + ? update_column_count + id_column_count + + managed_optimistic_update_column_count + : 1; + + binding update_image_binding_; + bind update_image_bind_[update_bind_count]; + + details::shared_ptr<select_statement_type> select_; + details::shared_ptr<update_statement_type> update_; + }; + } +} + +#include <odb/oracle/section-statements.txx> + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_SECTION_STATEMENTS_HXX diff --git a/libodb-oracle/odb/oracle/section-statements.txx b/libodb-oracle/odb/oracle/section-statements.txx new file mode 100644 index 0000000..45606d5 --- /dev/null +++ b/libodb-oracle/odb/oracle/section-statements.txx @@ -0,0 +1,49 @@ +// file : odb/oracle/section-statements.txx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <cstring> // std::memset + +namespace odb +{ + namespace oracle + { + template <typename T, typename ST> + section_statements<T, ST>:: + section_statements (connection_type& conn, + image_type& im, id_image_type&, + binding& id, binding& idv) + : conn_ (conn), + svm_ (0), + image_ (im), + id_binding_ (id), + idv_binding_ (idv), + select_image_binding_ (select_image_bind_, + select_column_count + + managed_optimistic_load_column_count), + update_image_binding_ (update_image_bind_, + update_column_count + id_column_count + + managed_optimistic_update_column_count) + { + select_image_version_ = 0; + update_image_version_ = 0; + update_id_binding_version_ = 0; + + // If we are using optimistic concurrency, then the select statement + // will override the version member in the object image. + // + if (managed_optimistic_load_column_count != 0) + { + // Getting to the root image in polymorphic case is tricky. + // + typedef object_traits_impl<T, id_oracle> object_traits; + + select_image_binding_.change_callback = + root_image<object_traits, object_traits::polymorphic>::get ( + image_).change_callback (); + } + + std::memset (select_image_bind_, 0, sizeof (select_image_bind_)); + std::memset (update_image_bind_, 0, sizeof (update_image_bind_)); + } + } +} diff --git a/libodb-oracle/odb/oracle/simple-object-result.hxx b/libodb-oracle/odb/oracle/simple-object-result.hxx new file mode 100644 index 0000000..df828d2 --- /dev/null +++ b/libodb-oracle/odb/oracle/simple-object-result.hxx @@ -0,0 +1,88 @@ +// file : odb/oracle/simple-object-result.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_SIMPLE_OBJECT_RESULT_HXX +#define ODB_ORACLE_SIMPLE_OBJECT_RESULT_HXX + +#include <odb/pre.hxx> + +#include <cstddef> // std::size_t + +#include <odb/schema-version.hxx> +#include <odb/simple-object-result.hxx> + +#include <odb/details/shared-ptr.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/forward.hxx> // query_base +#include <odb/oracle/statement.hxx> +#include <odb/oracle/traits-calls.hxx> + +namespace odb +{ + namespace oracle + { + template <typename T> + class object_result_impl: public odb::object_result_impl<T> + { + public: + typedef odb::object_result_impl<T> base_type; + + typedef typename base_type::id_type id_type; + typedef typename base_type::object_type object_type; + typedef typename base_type::pointer_type pointer_type; + + typedef object_traits_impl<object_type, id_oracle> object_traits; + typedef typename base_type::pointer_traits pointer_traits; + + typedef typename object_traits::statements_type statements_type; + + virtual + ~object_result_impl (); + + object_result_impl (const query_base&, + details::shared_ptr<select_statement>, + statements_type&, + const schema_version_migration*); + + virtual void + load (object_type&, bool fetch); + + virtual id_type + load_id (); + + virtual void + next (); + + virtual void + cache (); + + virtual std::size_t + size (); + + virtual void + invalidate (); + + using base_type::current; + + private: + typedef oracle::change_callback change_callback_type; + + static void + change_callback (void* context); + + private: + details::shared_ptr<select_statement> statement_; + statements_type& statements_; + object_traits_calls<object_type> tc_; + bool use_copy_; + typename object_traits::image_type* image_copy_; + }; + } +} + +#include <odb/oracle/simple-object-result.txx> + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_SIMPLE_OBJECT_RESULT_HXX diff --git a/libodb-oracle/odb/oracle/simple-object-result.txx b/libodb-oracle/odb/oracle/simple-object-result.txx new file mode 100644 index 0000000..436c5a9 --- /dev/null +++ b/libodb-oracle/odb/oracle/simple-object-result.txx @@ -0,0 +1,181 @@ +// file : odb/oracle/simple-object-result.txx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <cassert> + +#include <odb/callback.hxx> +#include <odb/exceptions.hxx> // result_not_cached + +#include <odb/oracle/simple-object-statements.hxx> + +namespace odb +{ + namespace oracle + { + template <typename T> + object_result_impl<T>:: + ~object_result_impl () + { + invalidate (); + } + + template <typename T> + void object_result_impl<T>:: + invalidate () + { + change_callback_type& cc (statements_.image ().change_callback_); + + if (cc.context == this) + { + cc.callback = 0; + cc.context = 0; + } + + delete image_copy_; + image_copy_ = 0; + + if (!this->end_) + { + statement_->free_result (); + this->end_ = true; + } + + statement_.reset (); + } + + template <typename T> + object_result_impl<T>:: + object_result_impl (const query_base&, + details::shared_ptr<select_statement> statement, + statements_type& statements, + const schema_version_migration* svm) + : base_type (statements.connection ()), + statement_ (statement), + statements_ (statements), + tc_ (svm), + use_copy_ (false), + image_copy_ (0) + { + } + + template <typename T> + void object_result_impl<T>:: + load (object_type& obj, bool) + { + // This is a top-level call so the statements cannot be locked. + // + assert (!statements_.locked ()); + typename statements_type::auto_lock l (statements_); + + object_traits::callback (this->db_, obj, callback_event::pre_load); + + typename object_traits::image_type& i ( + use_copy_ ? *image_copy_ : statements_.image ()); + + tc_.init (obj, i, &this->db_); + + // If we are using a copy, make sure the callback information for + // LOB data also comes from the copy. + // + statement_->stream_result ( + use_copy_ ? &statements_.image () : 0, + use_copy_ ? image_copy_ : 0); + + // Initialize the id image and binding and load the rest of the object + // (containers, etc). + // + typename object_traits::id_image_type& idi (statements_.id_image ()); + object_traits::init (idi, object_traits::id (i)); + + binding& idb (statements_.id_image_binding ()); + if (idi.version != statements_.id_image_version () || idb.version == 0) + { + object_traits::bind (idb.bind, idi); + statements_.id_image_version (idi.version); + idb.version++; + } + + tc_.load_ (statements_, obj, false); + statements_.load_delayed (tc_.version ()); + l.unlock (); + object_traits::callback (this->db_, obj, callback_event::post_load); + } + + template <typename T> + typename object_result_impl<T>::id_type + object_result_impl<T>:: + load_id () + { + return object_traits::id ( + use_copy_ ? *image_copy_ : statements_.image ()); + } + + template <typename T> + void object_result_impl<T>:: + next () + { + this->current (pointer_type ()); + + typename object_traits::image_type& im (statements_.image ()); + change_callback_type& cc (im.change_callback_); + + if (cc.context == this) + { + cc.callback = 0; + cc.context = 0; + } + + use_copy_ = false; + + if (im.version != statements_.select_image_version ()) + { + binding& b (statements_.select_image_binding ()); + tc_.bind (b.bind, im, statement_select); + statements_.select_image_version (im.version); + b.version++; + } + + if (statement_->fetch () == select_statement::no_data) + { + statement_->free_result (); + this->end_ = true; + } + else + { + cc.callback = &change_callback; + cc.context = this; + } + } + + template <typename T> + void object_result_impl<T>:: + cache () + { + } + + template <typename T> + std::size_t object_result_impl<T>:: + size () + { + throw result_not_cached (); + } + + template <typename T> + void object_result_impl<T>:: + change_callback (void* c) + { + object_result_impl<T>* r (static_cast<object_result_impl<T>*> (c)); + typename object_traits::image_type& im (r->statements_.image ()); + + if (r->image_copy_ == 0) + r->image_copy_ = new typename object_traits::image_type (im); + else + *r->image_copy_ = im; + + im.change_callback_.callback = 0; + im.change_callback_.context = 0; + + r->use_copy_ = true; + } + } +} diff --git a/libodb-oracle/odb/oracle/simple-object-statements.cxx b/libodb-oracle/odb/oracle/simple-object-statements.cxx new file mode 100644 index 0000000..330627a --- /dev/null +++ b/libodb-oracle/odb/oracle/simple-object-statements.cxx @@ -0,0 +1,15 @@ +// file : odb/oracle/simple-object-statements.cxx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <odb/oracle/simple-object-statements.hxx> + +namespace odb +{ + namespace oracle + { + object_statements_base:: + ~object_statements_base () + { + } + } +} diff --git a/libodb-oracle/odb/oracle/simple-object-statements.hxx b/libodb-oracle/odb/oracle/simple-object-statements.hxx new file mode 100644 index 0000000..98e60b2 --- /dev/null +++ b/libodb-oracle/odb/oracle/simple-object-statements.hxx @@ -0,0 +1,594 @@ +// file : odb/oracle/simple-object-statements.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_SIMPLE_OBJECT_STATEMENTS_HXX +#define ODB_ORACLE_SIMPLE_OBJECT_STATEMENTS_HXX + +#include <odb/pre.hxx> + +#include <vector> +#include <cassert> +#include <cstddef> // std::size_t + +#include <odb/forward.hxx> +#include <odb/traits.hxx> + +#include <odb/details/shared-ptr.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/forward.hxx> +#include <odb/oracle/oracle-types.hxx> +#include <odb/oracle/binding.hxx> +#include <odb/oracle/statement.hxx> +#include <odb/oracle/statements-base.hxx> + +#include <odb/oracle/details/export.hxx> + +namespace odb +{ + namespace oracle + { + // The extra_statement_cache class is only defined (and used) in + // the generated source file. However, object_statements may be + // referenced from another source file in the case of a polymorphic + // hierarchy (though in this case the extra statement cache is + // not used). As a result, we cannot have a by-value member and + // instead will store a pointer and lazily allocate the cache if + // and when needed. We will also need to store a pointer to the + // deleter function which will be initialized during allocation + // (at that point we know that the cache class is defined). + // + template <typename T, typename I, typename ID> + struct extra_statement_cache_ptr + { + typedef I image_type; + typedef ID id_image_type; + typedef oracle::connection connection_type; + + extra_statement_cache_ptr (): p_ (0) {} + ~extra_statement_cache_ptr () + { + if (p_ != 0) + (this->*deleter_) (0, 0, 0, 0, 0); + } + + T& + get (connection_type& c, + image_type& im, id_image_type& idim, + binding& id, binding* idv) + { + if (p_ == 0) + allocate (&c, &im, &idim, &id, (idv != 0 ? idv : &id)); + + return *p_; + } + + private: + void + allocate (connection_type*, + image_type*, id_image_type*, + binding*, binding*); + + private: + T* p_; + void (extra_statement_cache_ptr::*deleter_) ( + connection_type*, image_type*, id_image_type*, binding*, binding*); + }; + + template <typename T, typename I, typename ID> + void extra_statement_cache_ptr<T, I, ID>:: + allocate (connection_type* c, + image_type* im, id_image_type* idim, + binding* id, binding* idv) + { + // To reduce object code size, this function acts as both allocator + // and deleter. + // + if (p_ == 0) + { + p_ = new T (*c, *im, *idim, *id, *idv); + deleter_ = &extra_statement_cache_ptr<T, I, ID>::allocate; + } + else + delete p_; + } + + // + // Implementation for objects with object id. + // + + class LIBODB_ORACLE_EXPORT object_statements_base: public statements_base + { + // Locking. + // + public: + void + lock () + { + assert (!locked_); + locked_ = true; + } + + void + unlock () + { + assert (locked_); + locked_ = false; + } + + bool + locked () const + { + return locked_; + } + + struct auto_unlock + { + // Unlocks the statement on construction and re-locks it on + // destruction. + // + auto_unlock (object_statements_base&); + ~auto_unlock (); + + private: + auto_unlock (const auto_unlock&); + auto_unlock& operator= (const auto_unlock&); + + private: + object_statements_base& s_; + }; + + public: + virtual + ~object_statements_base (); + + protected: + object_statements_base (connection_type& conn) + : statements_base (conn), locked_ (false) + { + } + + protected: + bool locked_; + }; + + template <typename T, bool optimistic> + struct optimistic_data; + + template <typename T> + struct optimistic_data<T, true> + { + typedef T object_type; + typedef object_traits_impl<object_type, id_oracle> object_traits; + + optimistic_data (bind*, std::size_t skip, sb4* status); + + binding* + id_image_binding () {return &id_image_binding_;} + + // The id + optimistic column binding. + // + binding id_image_binding_; + + details::shared_ptr<delete_statement> erase_; + }; + + template <typename T> + struct optimistic_data<T, false> + { + optimistic_data (bind*, std::size_t, sb4*) {} + + binding* + id_image_binding () {return 0;} + }; + + template <typename T> + class object_statements: public object_statements_base + { + public: + typedef T object_type; + typedef object_traits_impl<object_type, id_oracle> object_traits; + typedef typename object_traits::id_type id_type; + typedef typename object_traits::pointer_type pointer_type; + typedef typename object_traits::image_type image_type; + typedef typename object_traits::id_image_type id_image_type; + + typedef + typename object_traits::pointer_cache_traits + pointer_cache_traits; + + typedef + typename object_traits::extra_statement_cache_type + extra_statement_cache_type; + + typedef oracle::insert_statement insert_statement_type; + typedef oracle::select_statement select_statement_type; + typedef oracle::update_statement update_statement_type; + typedef oracle::delete_statement delete_statement_type; + + // Automatic lock. + // + struct auto_lock + { + // Lock the statements unless they are already locked in which + // case subsequent calls to locked() will return false. + // + auto_lock (object_statements&); + + // Unlock the statements if we are holding the lock and clear + // the delayed loads. This should only happen in case an + // exception is thrown. In normal circumstances, the user + // should call unlock() explicitly. + // + ~auto_lock (); + + // Return true if this auto_lock instance holds the lock. + // + bool + locked () const; + + // Unlock the statements. + // + void + unlock (); + + private: + auto_lock (const auto_lock&); + auto_lock& operator= (const auto_lock&); + + private: + object_statements& s_; + bool locked_; + }; + + public: + object_statements (connection_type&); + + virtual + ~object_statements (); + + // Delayed loading. + // + typedef void (*loader_function) (odb::database&, + const id_type&, + object_type&, + const schema_version_migration*); + + void + delay_load (const id_type& id, + object_type& obj, + const typename pointer_cache_traits::position_type& p, + loader_function l = 0) + { + delayed_.push_back (delayed_load (id, obj, p, l)); + } + + void + load_delayed (const schema_version_migration* svm) + { + assert (locked ()); + + if (!delayed_.empty ()) + load_delayed_<object_statements> (svm); + } + + void + clear_delayed () + { + if (!delayed_.empty ()) + clear_delayed_ (); + } + + // Object image. + // + image_type& + image (std::size_t i = 0) {return images_[i].obj;} + + // Insert binding. + // + std::size_t + insert_image_version () const { return insert_image_version_;} + + void + insert_image_version (std::size_t v) {insert_image_version_ = v;} + + binding& + insert_image_binding () {return insert_image_binding_;} + + // Update binding. + // + std::size_t + update_image_version () const { return update_image_version_;} + + void + update_image_version (std::size_t v) {update_image_version_ = v;} + + std::size_t + update_id_image_version () const { return update_id_image_version_;} + + void + update_id_image_version (std::size_t v) {update_id_image_version_ = v;} + + binding& + update_image_binding () {return update_image_binding_;} + + // Select binding. + // + std::size_t + select_image_version () const { return select_image_version_;} + + void + select_image_version (std::size_t v) {select_image_version_ = v;} + + binding& + select_image_binding () {return select_image_binding_;} + + // Object id image and binding. + // + id_image_type& + id_image (std::size_t i = 0) {return images_[i].id;} + + std::size_t + id_image_version () const {return id_image_version_;} + + void + id_image_version (std::size_t v) {id_image_version_ = v;} + + binding& + id_image_binding () {return id_image_binding_;} + + // Optimistic id + managed column image binding. It points to + // the same suffix as id binding and they are always updated + // at the same time. + // + binding& + optimistic_id_image_binding () {return od_.id_image_binding_;} + + // Statements. + // + insert_statement_type& + persist_statement () + { + if (persist_ == 0) + persist_.reset ( + new (details::shared) insert_statement_type ( + conn_, + object_traits::persist_statement, + object_traits::versioned, // Process if versioned. + insert_image_binding_, + object_traits::auto_id ? &id_image_binding_ : 0)); + + return *persist_; + } + + select_statement_type& + find_statement () + { + if (find_ == 0) + find_.reset ( + new (details::shared) select_statement_type ( + conn_, + object_traits::find_statement, + object_traits::versioned, // Process if versioned. + false, // Don't optimize. + id_image_binding_, + select_image_binding_, + 4096)); // Hardcode a 4kB LOB prefetch size. + + return *find_; + } + + update_statement_type& + update_statement () + { + if (update_ == 0) + update_.reset ( + new (details::shared) update_statement_type ( + conn_, + object_traits::update_statement, + true, // Unique (0 or 1). + object_traits::versioned, // Process if versioned. + update_image_binding_)); + + return *update_; + } + + delete_statement_type& + erase_statement () + { + if (erase_ == 0) + erase_.reset ( + new (details::shared) delete_statement_type ( + conn_, + object_traits::erase_statement, + true, // Unique (0 or 1 affected rows). + id_image_binding_)); + + return *erase_; + } + + delete_statement_type& + optimistic_erase_statement () + { + if (od_.erase_ == 0) + { + od_.erase_.reset ( + new (details::shared) delete_statement_type ( + conn_, + object_traits::optimistic_erase_statement, + true, // Unique (0 or 1 affected rows). + od_.id_image_binding_)); + } + + return *od_.erase_; + } + + // Extra (container, section) statement cache. + // + extra_statement_cache_type& + extra_statement_cache () + { + return extra_statement_cache_.get ( + conn_, + images_[0].obj, images_[0].id, + id_image_binding_, od_.id_image_binding ()); + } + + public: + // select = total - separate_load + // insert = total - inverse - managed_optimistic - auto_id + // update = total - inverse - managed_optimistic - id - readonly + // - separate_update + // + static const std::size_t id_column_count = + object_traits::id_column_count; + + static const std::size_t managed_optimistic_column_count = + object_traits::managed_optimistic_column_count; + + static const std::size_t select_column_count = + object_traits::column_count - + object_traits::separate_load_column_count; + + static const std::size_t insert_column_count = + object_traits::column_count - + object_traits::inverse_column_count - + object_traits::managed_optimistic_column_count - + (object_traits::auto_id ? id_column_count : 0); + + static const std::size_t update_column_count = + insert_column_count - + (object_traits::auto_id ? 0 : id_column_count) - + object_traits::readonly_column_count - + object_traits::separate_update_column_count; + + private: + object_statements (const object_statements&); + object_statements& operator= (const object_statements&); + + protected: + template <typename STS> + void + load_delayed_ (const schema_version_migration*); + + void + clear_delayed_ (); + + protected: + template <typename T1> + friend class polymorphic_derived_object_statements; + + extra_statement_cache_ptr<extra_statement_cache_type, + image_type, + id_image_type> extra_statement_cache_; + + // The UPDATE statement uses both the object and id image. Keep + // them next to each other so that the same skip distance can + // be used in batch binding. + // + struct images + { + image_type obj; + id_image_type id; + }; + + images images_[object_traits::batch]; + sb4 status_[object_traits::batch]; + + // Select binding. + // + std::size_t select_image_version_; + binding select_image_binding_; + bind select_image_bind_[select_column_count]; + + // Insert binding. + // + std::size_t insert_image_version_; + binding insert_image_binding_; + bind insert_image_bind_[ + insert_column_count != 0 ? insert_column_count : 1]; + + // Update binding. Note that the id suffix is bound to id_image_ + // below instead of image_ which makes this binding effectively + // bound to two images. As a result, we have to track versions + // for both of them. If this object uses optimistic concurrency, + // then the binding for the managed column (version, timestamp, + // etc) comes after the id and the image for such a column is + // stored as part of the id image. + // + std::size_t update_image_version_; + std::size_t update_id_image_version_; + binding update_image_binding_; + bind update_image_bind_[update_column_count + id_column_count + + managed_optimistic_column_count]; + + // Id image binding (only used as a parameter or in RETURNING for + // auto ids). Uses the suffix in the update bind. + // + std::size_t id_image_version_; + binding id_image_binding_; + + // Extra data for objects with optimistic concurrency support. + // + optimistic_data<T, managed_optimistic_column_count != 0> od_; + + details::shared_ptr<insert_statement_type> persist_; + details::shared_ptr<select_statement_type> find_; + details::shared_ptr<update_statement_type> update_; + details::shared_ptr<delete_statement_type> erase_; + + // Delayed loading. + // + struct delayed_load + { + typedef typename pointer_cache_traits::position_type position_type; + + delayed_load () {} + delayed_load (const id_type& i, + object_type& o, + const position_type& p, + loader_function l) + : id (i), obj (&o), pos (p), loader (l) + { + } + + id_type id; + object_type* obj; + position_type pos; + loader_function loader; + }; + + typedef std::vector<delayed_load> delayed_loads; + delayed_loads delayed_; + + // Delayed vectors swap guard. See the load_delayed_() function for + // details. + // + struct swap_guard + { + swap_guard (object_statements& os, delayed_loads& dl) + : os_ (os), dl_ (dl) + { + dl_.swap (os_.delayed_); + } + + ~swap_guard () + { + os_.clear_delayed (); + dl_.swap (os_.delayed_); + } + + private: + object_statements& os_; + delayed_loads& dl_; + }; + }; + } +} + +#include <odb/oracle/simple-object-statements.ixx> +#include <odb/oracle/simple-object-statements.txx> + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_SIMPLE_OBJECT_STATEMENTS_HXX diff --git a/libodb-oracle/odb/oracle/simple-object-statements.ixx b/libodb-oracle/odb/oracle/simple-object-statements.ixx new file mode 100644 index 0000000..4631d69 --- /dev/null +++ b/libodb-oracle/odb/oracle/simple-object-statements.ixx @@ -0,0 +1,68 @@ +// file : odb/oracle/simple-object-statements.ixx +// license : ODB NCUEL; see accompanying LICENSE file + +namespace odb +{ + namespace oracle + { + // + // auto_unlock + // + inline object_statements_base::auto_unlock:: + auto_unlock (object_statements_base& s) + : s_ (s) + { + s_.unlock (); + } + + inline object_statements_base::auto_unlock:: + ~auto_unlock () + { + s_.lock (); + } + + // + // auto_lock + // + template <typename T> + inline object_statements<T>::auto_lock:: + auto_lock (object_statements& s) + : s_ (s) + { + if (!s_.locked ()) + { + s_.lock (); + locked_ = true; + } + else + locked_ = false; + } + + template <typename T> + inline object_statements<T>::auto_lock:: + ~auto_lock () + { + if (locked_) + { + s_.unlock (); + s_.clear_delayed (); + } + } + + template <typename T> + inline bool object_statements<T>::auto_lock:: + locked () const + { + return locked_; + } + + template <typename T> + inline void object_statements<T>::auto_lock:: + unlock () + { + assert (locked_); + s_.unlock (); + locked_ = false; + } + } +} diff --git a/libodb-oracle/odb/oracle/simple-object-statements.txx b/libodb-oracle/odb/oracle/simple-object-statements.txx new file mode 100644 index 0000000..99de084 --- /dev/null +++ b/libodb-oracle/odb/oracle/simple-object-statements.txx @@ -0,0 +1,164 @@ +// file : odb/oracle/simple-object-statements.txx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <cstring> // std::memset + +#include <odb/callback.hxx> +#include <odb/exceptions.hxx> + +#include <odb/oracle/connection.hxx> +#include <odb/oracle/traits-calls.hxx> + +namespace odb +{ + namespace oracle + { + // + // optimistic_data + // + + template <typename T> + optimistic_data<T, true>:: + optimistic_data (bind* b, std::size_t skip, sb4* status) + : id_image_binding_ ( + b, + object_traits::id_column_count + + object_traits::managed_optimistic_column_count, + object_traits::batch, + skip, + status) + { + } + + // + // object_statements + // + + template <typename T> + object_statements<T>:: + ~object_statements () + { + } + + template <typename T> + object_statements<T>:: + object_statements (connection_type& conn) + : object_statements_base (conn), + select_image_binding_ (select_image_bind_, select_column_count), + insert_image_binding_ (insert_image_bind_, + insert_column_count, + object_traits::batch, + sizeof (images), + status_), + update_image_binding_ (update_image_bind_, + update_column_count + id_column_count + + managed_optimistic_column_count, + object_traits::batch, + sizeof (images), + status_), + id_image_binding_ (update_image_bind_ + update_column_count, + id_column_count, + object_traits::batch, + sizeof (images), + status_), + od_ (update_image_bind_ + update_column_count, + sizeof (images), + status_) + { + // Only versions in the first element used. + // + images_[0].obj.version = 0; + images_[0].id.version = 0; + + select_image_version_ = 0; + insert_image_version_ = 0; + update_image_version_ = 0; + update_id_image_version_ = 0; + id_image_version_ = 0; + + // SELECT statements only use the first element (no batches). + // + select_image_binding_.change_callback = + images_[0].obj.change_callback (); + + std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_)); + std::memset (update_image_bind_, 0, sizeof (update_image_bind_)); + std::memset (select_image_bind_, 0, sizeof (select_image_bind_)); + } + + template <typename T> + template <typename STS> + void object_statements<T>:: + load_delayed_ (const schema_version_migration* svm) + { + database& db (connection ().database ()); + + delayed_loads dls; + swap_guard sg (*this, dls); + + while (!dls.empty ()) + { + delayed_load l (dls.back ()); + typename pointer_cache_traits::insert_guard ig (l.pos); + dls.pop_back (); + + if (l.loader == 0) + { + object_traits_calls<T> tc (svm); + + if (!tc.find_ (static_cast<STS&> (*this), &l.id)) + throw object_not_persistent (); + + object_traits::callback (db, *l.obj, callback_event::pre_load); + + // Our calls to init/load below can result in additional delayed + // loads being added to the delayed_ vector. We need to process + // those before we call the post callback. + // + tc.init (*l.obj, image (), &db); + find_->stream_result (); + + // Load containers, etc. + // + tc.load_ (static_cast<STS&> (*this), *l.obj, false); + + if (!delayed_.empty ()) + load_delayed_<STS> (svm); + + // Temporarily unlock the statement for the post_load call so that + // it can load objects of this type recursively. This is safe to do + // because we have completely loaded the current object. Also the + // delayed_ list is clear before the unlock and should be clear on + // re-lock (since a callback can only call public API functions + // which will make sure all the delayed loads are processed before + // returning). + // + { + auto_unlock u (*this); + object_traits::callback (db, *l.obj, callback_event::post_load); + } + } + else + l.loader (db, l.id, *l.obj, svm); + + pointer_cache_traits::load (ig.position ()); + ig.release (); + } + } + + template <typename T> + void object_statements<T>:: + clear_delayed_ () + { + // Remove the objects from the session cache. + // + for (typename delayed_loads::iterator i (delayed_.begin ()), + e (delayed_.end ()); i != e; ++i) + { + pointer_cache_traits::erase (i->pos); + } + + delayed_.clear (); + } + } +} diff --git a/libodb-oracle/odb/oracle/statement-cache.hxx b/libodb-oracle/odb/oracle/statement-cache.hxx new file mode 100644 index 0000000..2dba01f --- /dev/null +++ b/libodb-oracle/odb/oracle/statement-cache.hxx @@ -0,0 +1,59 @@ +// file : odb/oracle/statement-cache.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_STATEMENT_CACHE_HXX +#define ODB_ORACLE_STATEMENT_CACHE_HXX + +#include <odb/pre.hxx> + +#include <map> +#include <typeinfo> + +#include <odb/forward.hxx> +#include <odb/traits.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/forward.hxx> +#include <odb/oracle/statements-base.hxx> + +#include <odb/details/shared-ptr.hxx> +#include <odb/details/type-info.hxx> + +#include <odb/oracle/details/export.hxx> + +namespace odb +{ + namespace oracle + { + class LIBODB_ORACLE_EXPORT statement_cache + { + public: + statement_cache (connection& conn) + : conn_ (conn), + version_seq_ (conn_.database ().schema_version_sequence ()) {} + + template <typename T> + typename object_traits_impl<T, id_oracle>::statements_type& + find_object (); + + template <typename T> + view_statements<T>& + find_view (); + + private: + typedef std::map<const std::type_info*, + details::shared_ptr<statements_base>, + details::type_info_comparator> map; + + connection& conn_; + unsigned int version_seq_; + map map_; + }; + } +} + +#include <odb/oracle/statement-cache.txx> + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_STATEMENT_CACHE_HXX diff --git a/libodb-oracle/odb/oracle/statement-cache.txx b/libodb-oracle/odb/oracle/statement-cache.txx new file mode 100644 index 0000000..23d9504 --- /dev/null +++ b/libodb-oracle/odb/oracle/statement-cache.txx @@ -0,0 +1,60 @@ +// file : odb/oracle/statement-cache.txx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <odb/oracle/database.hxx> + +namespace odb +{ + namespace oracle + { + template <typename T> + typename object_traits_impl<T, id_oracle>::statements_type& + statement_cache:: + find_object () + { + typedef + typename object_traits_impl<T, id_oracle>::statements_type + statements_type; + + // Clear the cache if the database version has changed. This + // makes sure we don't re-use statements that correspond to + // the old schema. + // + if (version_seq_ != conn_.database ().schema_version_sequence ()) + { + map_.clear (); + version_seq_ = conn_.database ().schema_version_sequence (); + } + + map::iterator i (map_.find (&typeid (T))); + + if (i != map_.end ()) + return static_cast<statements_type&> (*i->second); + + details::shared_ptr<statements_type> p ( + new (details::shared) statements_type (conn_)); + + map_.insert (map::value_type (&typeid (T), p)); + return *p; + } + + template <typename T> + view_statements<T>& statement_cache:: + find_view () + { + // We don't cache any statements for views so no need to clear + // the cache. + + map::iterator i (map_.find (&typeid (T))); + + if (i != map_.end ()) + return static_cast<view_statements<T>&> (*i->second); + + details::shared_ptr<view_statements<T> > p ( + new (details::shared) view_statements<T> (conn_)); + + map_.insert (map::value_type (&typeid (T), p)); + return *p; + } + } +} diff --git a/libodb-oracle/odb/oracle/statement.cxx b/libodb-oracle/odb/oracle/statement.cxx new file mode 100644 index 0000000..8c09d3a --- /dev/null +++ b/libodb-oracle/odb/oracle/statement.cxx @@ -0,0 +1,2053 @@ +// file : odb/oracle/statement.cxx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <oci.h> + +#include <cstring> // std::strlen, std::memset +#include <cassert> + +#include <odb/tracer.hxx> +#include <odb/exceptions.hxx> // object_not_persistent +#include <odb/details/unused.hxx> + +#include <odb/oracle/database.hxx> +#include <odb/oracle/statement.hxx> +#include <odb/oracle/connection.hxx> +#include <odb/oracle/auto-descriptor.hxx> +#include <odb/oracle/error.hxx> +#include <odb/oracle/exceptions.hxx> + +#include <odb/oracle/details/number.hxx> + +using namespace std; + +namespace odb +{ + namespace oracle + { + // Mapping of bind::buffer_type values for parameter buffers to their + // equivalent external OCI typecode identifiers. + // + static const ub4 param_sqlt_lookup[bind::last] = + { + SQLT_INT, // bind::integer + SQLT_UIN, // bind::uinteger + SQLT_BFLOAT, // bind::binary_float + SQLT_BDOUBLE, // bind::binary_double + SQLT_NUM, // bind::number + SQLT_DAT, // bind::date + SQLT_TIMESTAMP, // bind::timestamp + SQLT_INTERVAL_YM, // bind::interval_ym + SQLT_INTERVAL_DS, // bind::interval_ds + SQLT_CHR, // bind::string + SQLT_CHR, // bind::nstring + SQLT_BIN, // bind::raw + SQLT_LBI, // bind::blob + SQLT_LNG, // bind::clob + SQLT_LNG // bind::nclob + }; + + // Mapping of bind::buffer_type values for result buffers to their + // equivalent external OCI typecode identifiers. + // + static const ub4 result_sqlt_lookup[bind::last] = + { + SQLT_INT, // bind::integer + SQLT_UIN, // bind::uinteger + SQLT_BFLOAT, // bind::binary_float + SQLT_BDOUBLE, // bind::binary_double + SQLT_NUM, // bind::number + SQLT_DAT, // bind::date + SQLT_TIMESTAMP, // bind::timestamp + SQLT_INTERVAL_YM, // bind::interval_ym + SQLT_INTERVAL_DS, // bind::interval_ds + SQLT_CHR, // bind::string + SQLT_CHR, // bind::nstring + SQLT_BIN, // bind::raw + SQLT_BLOB, // bind::blob + SQLT_CLOB, // bind::clob + SQLT_CLOB // bind::nclob + }; + + template <typename T> + static inline T* + offset (T* base, size_t count, size_t size) + { + return reinterpret_cast<T*> ( + reinterpret_cast<char*> (base) + count * size); + } + + extern "C" sb4 + odb_oracle_param_callback_proxy (void* context, + OCIBind*, + ub4 it, // iteration + ub4, // index + void** buffer, + ub4* size, + ub1* piece, + void** indicator) + { + bind& b (*static_cast<bind*> (context)); + + // Offset the data based on the current iteration and skip size (stored + // in capacity). + // + sb2* ind (offset (b.indicator, it, b.capacity)); + + // Only call the callback if the parameter is not NULL. + // + if (*ind != -1) + { + lob* l (static_cast<lob*> (offset (b.buffer, it, b.capacity))); + lob_callback* cb ( + static_cast<lob_callback*> (offset (b.callback, it, b.capacity))); + + chunk_position pos; + if (!(*cb->callback.param) ( + cb->context.param, + &l->position, + const_cast<const void**> (buffer), + size, + &pos, + l->buffer->data (), + static_cast<ub4> (l->buffer->capacity ()))) + return OCI_ERROR; + + switch (pos) + { + case chunk_one: + { + *piece = OCI_ONE_PIECE; + break; + } + case chunk_first: + { + *piece = OCI_FIRST_PIECE; + break; + } + case chunk_next: + { + *piece = OCI_NEXT_PIECE; + break; + } + case chunk_last: + { + *piece = OCI_LAST_PIECE; + break; + } + } + } + else + *piece = OCI_ONE_PIECE; + + *indicator = ind; + + return OCI_CONTINUE; + } + + // + // statement + // + + statement:: + ~statement () + { + if (stmt_ == 0) + return; + + { + odb::tracer* t; + if ((t = conn_.transaction_tracer ()) || + (t = conn_.tracer ()) || + (t = conn_.database ().tracer ())) + t->deallocate (conn_, *this); + } + + // Unbind (free) parameter descriptors. + // + for (size_t i (0); i < usize_; ++i) + { + ub4 t; + bind* b (udata_[i].bind); + + switch (udata_[i].type) + { + case bind::timestamp: + { + if (b != 0) + static_cast<datetime*> (b->buffer)->descriptor = 0; + + t = OCI_DTYPE_TIMESTAMP; + break; + } + case bind::interval_ym: + { + if (b != 0) + static_cast<interval_ym*> (b->buffer)->descriptor = 0; + + t = OCI_DTYPE_INTERVAL_YM; + break; + } + case bind::interval_ds: + { + if (b != 0) + static_cast<interval_ds*> (b->buffer)->descriptor = 0; + + t = OCI_DTYPE_INTERVAL_DS; + break; + } + default: + { + assert (false); + return; + } + } + + OCIDescriptorFree (udata_[i].value, t); + } + + delete[] udata_; + } + + statement:: + statement (connection_type& conn, + const string& text, + statement_kind sk, + const binding* process, + bool optimize) + : conn_ (conn), udata_ (0), usize_ (0) + { + init (text.c_str (), text.size (), sk, process, optimize); + } + + statement:: + statement (connection_type& conn, + const char* text, + statement_kind sk, + const binding* process, + bool optimize) + : conn_ (conn), udata_ (0), usize_ (0) + { + init (text, strlen (text), sk, process, optimize); + } + + void statement:: + init (const char* text, + size_t text_size, + statement_kind sk, + const binding* proc, + bool optimize) + { + string tmp; + if (proc != 0) + { + switch (sk) + { + case statement_select: + process_select (tmp, + text, + &proc->bind->buffer, proc->count, sizeof (bind), + '"', '"', + optimize, + false); // No AS in JOINs. + break; + case statement_insert: + process_insert (tmp, + text, + &proc->bind->buffer, proc->count, sizeof (bind), + ':'); + break; + case statement_update: + process_update (tmp, + text, + &proc->bind->buffer, proc->count, sizeof (bind), + ':'); + break; + case statement_delete: + case statement_generic: + assert (false); + } + + text = tmp.c_str (); + text_size = tmp.size (); + } + + // Empty statement. + // + if (*text == '\0') + return; + + { + odb::tracer* t; + if ((t = conn_.transaction_tracer ()) || + (t = conn_.tracer ()) || + (t = conn_.database ().tracer ())) + { + // Temporarily store the statement text in unbind data so that + // text() which may be called by the tracer can access it. + // + udata_ = reinterpret_cast<unbind*> (const_cast<char*> (text)); + t->prepare (conn_, *this); + udata_ = 0; + } + } + + OCIError* err (conn_.error_handle ()); + OCIStmt* handle (0); + + sword r (OCIStmtPrepare2 (conn_.handle (), + &handle, + err, + reinterpret_cast<const OraText*> (text), + static_cast<ub4> (text_size), + 0, + 0, + OCI_NTV_SYNTAX, + OCI_DEFAULT)); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (conn_, r); + + stmt_.reset (handle, OCI_STRLS_CACHE_DELETE, err); + } + + const char* statement:: + text () const + { + if (stmt_ == 0) + // See init() above for details on what's going on here. + // + return udata_ != 0 ? reinterpret_cast<const char*> (udata_) : ""; + + OCIError* err (conn_.error_handle ()); + + OraText* s (0); + sword r (OCIAttrGet (stmt_, + OCI_HTYPE_STMT, + &s, + 0, + OCI_ATTR_STATEMENT, + err)); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + + return reinterpret_cast<char*> (s); + } + + ub4 statement:: + bind_param (bind* b, size_t n, size_t batch, size_t skip) + { + // Figure out how many unbind elements we will need and allocate them. + // + { + size_t un (0); + + for (size_t i (0); i < n; ++i) + { + if (b[i].buffer == 0) // Skip NULL entries. + continue; + + switch (b[i].type) + { + case bind::timestamp: + { + datetime* dt (static_cast<datetime*> (b[i].buffer)); + if (dt->descriptor == 0 && (dt->flags & descriptor_free) == 0) + un++; + break; + } + case bind::interval_ym: + { + interval_ym* iym (static_cast<interval_ym*> (b[i].buffer)); + if (iym->descriptor == 0 && (iym->flags & descriptor_free) == 0) + un++; + break; + } + case bind::interval_ds: + { + interval_ds* ids (static_cast<interval_ds*> (b[i].buffer)); + if (ids->descriptor == 0 && (ids->flags & descriptor_free) == 0) + un++; + break; + } + default: + break; + } + } + + // Unbind is only used in queries which means there should be no + // batches. + // + assert (un == 0 || batch == 1); + + if (un != 0) + udata_ = new unbind[un]; + } + + bool seen_lob (false); + sword r; + OCIError* err (conn_.error_handle ()); + OCIEnv* env (conn_.database ().environment ()); + + ub4 i (0); + for (bind* end (b + n); b != end; ++b) + { + if (b->buffer == 0) // Skip NULL entries. + continue; + + i++; // Column index is 1-based. + + void* value (0); + sb4 capacity; + ub2* size (0); + bool callback (b->callback != 0); + + switch (b->type) + { + case bind::timestamp: + { + for (size_t i (0); i != batch; ++i) + { + datetime* dt ( + static_cast<datetime*> (offset (b->buffer, i, skip))); + + void* pd (0); // Pointer to descriptor. + + if (dt->descriptor == 0) + { + void* d (0); + r = OCIDescriptorAlloc (env, + &d, + OCI_DTYPE_TIMESTAMP, + 0, + 0); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + if (dt->flags & descriptor_cache) + { + dt->descriptor = static_cast<OCIDateTime*> (d); + dt->environment = env; + dt->error = err; + } + + // If the datetime instance is not responsible for the + // descriptor, then we have to arrange to have it freed + // using the unbind machinery. + // + if ((dt->flags & descriptor_free) == 0) + { + unbind& u (udata_[usize_++]); + + u.type = bind::timestamp; + u.bind = (dt->flags & descriptor_cache) ? b : 0; + u.value = d; + pd = &u.value; + } + else + pd = &dt->descriptor; + + // Initialize the descriptor from the cached data. + // + if (b->indicator == 0 || *b->indicator != -1) + r = OCIDateTimeConstruct (env, + err, + static_cast<OCIDateTime*> (d), + dt->year_, + dt->month_, + dt->day_, + dt->hour_, + dt->minute_, + dt->second_, + dt->nanosecond_, + 0, + 0); + + if (r != OCI_SUCCESS) + translate_error (err, r); + } + else + pd = &dt->descriptor; + + if (i == 0) + value = pd; + } + + capacity = static_cast<sb4> (sizeof (OCIDateTime*)); + break; + } + case bind::interval_ym: + { + for (size_t i (0); i != batch; ++i) + { + interval_ym* iym ( + static_cast<interval_ym*> (offset (b->buffer, i, skip))); + + void* pd (0); // Pointer to descriptor. + + if (iym->descriptor == 0) + { + void* d (0); + r = OCIDescriptorAlloc (env, + &d, + OCI_DTYPE_INTERVAL_YM, + 0, + 0); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + if (iym->flags & descriptor_cache) + { + iym->descriptor = static_cast<OCIInterval*> (d); + iym->environment = env; + iym->error = err; + } + + // If the interval_ym instance is not responsible for the + // descriptor, then we have to arrange to have it freed + // using the unbind machinery. + // + if ((iym->flags & descriptor_free) == 0) + { + unbind& u (udata_[usize_++]); + + u.type = bind::interval_ym; + u.bind = (iym->flags & descriptor_cache) ? b : 0; + u.value = d; + pd = &u.value; + } + else + pd = &iym->descriptor; + + // Initialize the descriptor from the cached data. + // + if (b->indicator == 0 || *b->indicator != -1) + r = OCIIntervalSetYearMonth (env, + err, + iym->year_, + iym->month_, + static_cast<OCIInterval*> (d)); + + if (r != OCI_SUCCESS) + translate_error (err, r); + } + else + pd = &iym->descriptor; + + if (i == 0) + value = pd; + } + + capacity = static_cast<sb4> (sizeof (OCIInterval*)); + break; + } + case bind::interval_ds: + { + for (size_t i (0); i != batch; ++i) + { + interval_ds* ids ( + static_cast<interval_ds*> (offset (b->buffer, i, skip))); + + void* pd (0); // Pointer to descriptor. + + if (ids->descriptor == 0) + { + void* d (0); + r = OCIDescriptorAlloc (env, + &d, + OCI_DTYPE_INTERVAL_DS, + 0, + 0); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + if (ids->flags & descriptor_cache) + { + ids->descriptor = static_cast<OCIInterval*> (d); + ids->environment = env; + ids->error = err; + } + + // If the interval_ds instance is not responsible for the + // descriptor, then we have to arrange to have it freed + // using the unbind machinery. + // + if ((ids->flags & descriptor_free) == 0) + { + unbind& u (udata_[usize_++]); + + u.type = bind::interval_ds; + u.bind = (ids->flags & descriptor_cache) ? b : 0; + u.value = d; + pd = &u.value; + } + else + pd = &ids->descriptor; + + // Initialize the descriptor from the cached data. + // + if (b->indicator == 0 || *b->indicator != -1) + r = OCIIntervalSetDaySecond (env, + err, + ids->day_, + ids->hour_, + ids->minute_, + ids->second_, + ids->nanosecond_, + static_cast<OCIInterval*> (d)); + + if (r != OCI_SUCCESS) + translate_error (err, r); + } + else + pd = &ids->descriptor; + + if (i == 0) + value = pd; + } + + capacity = static_cast<sb4> (sizeof (OCIInterval*)); + break; + } + case bind::blob: + case bind::clob: + case bind::nclob: + { + seen_lob = true; + + lob* l (static_cast<lob*> (b->buffer)); + + if (l->buffer == 0) + { + details::buffer& lob_buffer (conn_.lob_buffer ()); + + if (lob_buffer.capacity () == 0) + lob_buffer.capacity (4096); + + // Generally, we should not modify the image since that would + // break the thread-safety guarantee of the query expression. + // However, in Oracle, LOBs cannot be used in queries so we can + // make an exception here. + // + for (size_t i (0); i != batch;) + { + l->buffer = &lob_buffer; + l = static_cast<lob*> (offset (b->buffer, ++i, skip)); + } + } + + assert (callback); + value = 0; + + // When binding LOB parameters, let's set the capacity to the + // minimum between the LOB maximum size (4GB) and the maximum + // value that can be stored in the variable of sb4 type. + // + capacity = SB4MAXVAL < OCI_LOBMAXSIZE ? SB4MAXVAL : OCI_LOBMAXSIZE; + + // Store skip in capacity so that the callback can offset the + // values based on the iteration number. + // + b->capacity = static_cast<ub4> (skip); + + break; + } + default: + { +#if OCI_MAJOR_VERSION < 11 || \ + (OCI_MAJOR_VERSION == 11 && OCI_MINOR_VERSION < 2) + // Assert if a 64 bit integer buffer type is provided and the OCI + // version is unable to implicitly convert the NUMBER binary data + // to the relevant type. + // + assert ((b->type != bind::integer && + b->type != bind::uinteger) || b->capacity <= 4); +#endif + value = callback ? 0 : b->buffer; + capacity = static_cast<sb4> (b->capacity); + size = b->size; + + break; + } + } + + OCIBind* h (0); + r = OCIBindByPos (stmt_, + &h, + err, + i, + value, + capacity, + param_sqlt_lookup[b->type], + b->indicator, + size, + 0, + 0, + 0, + callback ? OCI_DATA_AT_EXEC : OCI_DEFAULT); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + + // Set the character set form for national strings. + // + if (b->type == bind::nstring || b->type == bind::nclob) + { + ub1 form (SQLCS_NCHAR); + r = OCIAttrSet (h, + OCI_HTYPE_BIND, + &form, + 0, + OCI_ATTR_CHARSET_FORM, + err); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + } + + if (seen_lob && (b->type == bind::string || b->type == bind::nstring)) + { + // Set the maximum data size for all string types. If this is not set + // Oracle server will implicitly calculate this maximum size. If the + // calculated size exceeds 4000 bytes (which may occur if a character + // set conversion is required) and the string is bound after a LOB + // binding, the server will return an ORA-24816 error. + // + sb4 n (4000); + r = OCIAttrSet (h, + OCI_HTYPE_BIND, + &n, + 0, + OCI_ATTR_MAXDATA_SIZE, + err); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + } + + if (callback) + { + r = OCIBindDynamic ( + h, err, b, &odb_oracle_param_callback_proxy, 0, 0); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + } + + // Set array information if we have a batch. + // + if (batch != 1) + { + ub4 s (static_cast<ub4> (skip)); + + r = OCIBindArrayOfStruct (h, + err, + (value != 0 ? s : 0), // value + (b->indicator != 0 ? s : 0), // indicator + (size != 0 ? s : 0), // length + 0); // return code + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + } + } + + return i; + } + + ub4 statement:: + bind_result (bind* b, size_t c, size_t p) + { + ODB_POTENTIALLY_UNUSED (p); + + sword r; + OCIError* err (conn_.error_handle ()); + OCIEnv* env (conn_.database ().environment ()); + + ub4 i (0); + for (bind* end (b + c); b != end; ++b) + { + if (b->buffer == 0) // Skip NULL entries. + continue; + + i++; // Column index is 1-based. + + void* value; + sb4 capacity; + ub2* size (0); + + switch (b->type) + { + case bind::timestamp: + { + datetime* dt (static_cast<datetime*> (b->buffer)); + + if (dt->descriptor == 0) + { + assert ((dt->flags & descriptor_cache) && + (dt->flags & descriptor_free)); + + void* d (0); + r = OCIDescriptorAlloc (env, &d, OCI_DTYPE_TIMESTAMP, 0, 0); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + dt->descriptor = static_cast<OCIDateTime*> (d); + dt->environment = env; + dt->error = err; + } + + value = &dt->descriptor; + capacity = static_cast<sb4> (sizeof (OCIDateTime*)); + + break; + } + case bind::interval_ym: + { + interval_ym* iym (static_cast<interval_ym*> (b->buffer)); + + if (iym->descriptor == 0) + { + assert ((iym->flags & descriptor_cache) && + (iym->flags & descriptor_free)); + + void* d (0); + r = OCIDescriptorAlloc (env, &d, OCI_DTYPE_INTERVAL_YM, 0, 0); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + iym->descriptor = static_cast<OCIInterval*> (d); + iym->environment = env; + iym->error = err; + } + + value = &iym->descriptor; + capacity = static_cast<sb4> (sizeof (OCIInterval*)); + + break; + } + case bind::interval_ds: + { + interval_ds* ids (static_cast<interval_ds*> (b->buffer)); + + if (ids->descriptor == 0) + { + assert ((ids->flags & descriptor_cache) && + (ids->flags & descriptor_free)); + + void* d (0); + r = OCIDescriptorAlloc (env, &d, OCI_DTYPE_INTERVAL_DS, 0, 0); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + ids->descriptor = static_cast<OCIInterval*> (d); + ids->environment = env; + ids->error = err; + } + + value = &ids->descriptor; + capacity = static_cast<sb4> (sizeof (OCIInterval*)); + + break; + } + case bind::blob: + case bind::clob: + case bind::nclob: + { + lob* l (static_cast<lob*> (b->buffer)); + + if (l->locator == 0) + { + void* d (0); + r = OCIDescriptorAlloc (env, &d, OCI_DTYPE_LOB, 0, 0); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + l->locator = static_cast<OCILobLocator*> (d); + l->environment = env; + l->error = err; + } + + value = &l->locator; + capacity = static_cast<sb4> (sizeof (OCILobLocator*)); + + break; + } + default: + { +#if OCI_MAJOR_VERSION < 11 || \ + (OCI_MAJOR_VERSION == 11 && OCI_MINOR_VERSION < 2) + // Assert if a 64 bit integer buffer type is provided and the OCI + // version is unable to implicitly convert the NUMBER binary data + // to the relevant type. + // + assert ((b->type != bind::integer && b->type != bind::uinteger) || + b->capacity <= 4); +#endif + value = b->buffer; + capacity = static_cast<sb4> (b->capacity); + size = b->size; + + break; + } + } + + OCIDefine* h (0); + r = OCIDefineByPos (stmt_, + &h, + err, + i, + value, + capacity, + result_sqlt_lookup[b->type], + b->indicator, + size, + 0, + OCI_DEFAULT); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + + // LOB prefetching is only supported in OCI version 11.1 and greater + // and in Oracle server 11.1 and greater. If this code is called + // against a pre 11.1 server, the call to OCIAttrSet will return an + // error code. + // +#if (OCI_MAJOR_VERSION == 11 && OCI_MINOR_VERSION >= 1) \ + || OCI_MAJOR_VERSION > 11 + if (b->type == bind::blob || + b->type == bind::clob || + b->type == bind::nclob) + { + + if (p != 0) + { + ub4 n (static_cast<ub4> (p)); + + r = OCIAttrSet (h, + OCI_HTYPE_DEFINE, + &n, + 0, + OCI_ATTR_LOBPREFETCH_SIZE, + err); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + } + } + else +#endif + if (b->type == bind::nstring) + { + ub1 form (SQLCS_NCHAR); + + r = OCIAttrSet (h, + OCI_HTYPE_DEFINE, + &form, + 0, + OCI_ATTR_CHARSET_FORM, + err); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + } + } + + return i; + } + + void statement:: + stream_result (bind* b, size_t c, void* obase, void* nbase) + { + OCIError* err (conn_.error_handle ()); + + for (bind* end (b + c); b != end; ++b) + { + if (b->buffer == 0) // Skip NULL entries. + continue; + + // Only stream if the bind specifies a LOB type. + // + if (b->type == bind::blob || + b->type == bind::clob || + b->type == bind::nclob) + { + lob* l; + sb2* ind; + lob_callback* cb; + + if (obase == 0) + { + l = static_cast<lob*> (b->buffer); + ind = b->indicator; + cb = b->callback; + } + else + { + // Re-base the pointers. + // + char* ob (static_cast<char*> (obase)); + char* nb (static_cast<char*> (nbase)); + + char* p (static_cast<char*> (b->buffer)); + assert (ob <= p); + l = reinterpret_cast<lob*> (nb + (p - ob)); + + if (b->indicator == 0) + ind = 0; + else + { + p = reinterpret_cast<char*> (b->indicator); + assert (ob <= p); + ind = reinterpret_cast<sb2*> (nb + (p - ob)); + } + + p = reinterpret_cast<char*> (b->callback); + assert (ob <= p); + cb = reinterpret_cast<lob_callback*> (nb + (p - ob)); + } + + // Nothing to do if the LOB value is NULL or the result callback + // hasn't been provided. + // + if ((ind != 0 && *ind == -1) || cb->callback.result == 0) + continue; + + ub4 position (0); // Position context. + ub1 piece (OCI_FIRST_PIECE); + + // Setting the value pointed to by the byte_amt argument to 0 on the + // first call to OCILobRead2 instructs OCI to remain in a polling + // state until the EOF is reached, at which point OCILobRead2 will + // return OCI_SUCCESS. + // + ub8 read (0); + ub1 cs_form (b->type == bind::nclob ? SQLCS_NCHAR : SQLCS_IMPLICIT); + + // Allocate buffer space if necessary. + // + details::buffer& lob_buffer (conn_.lob_buffer ()); + + if (lob_buffer.capacity () == 0) + lob_buffer.capacity (4096); + + sword r; + do + { + r = OCILobRead2 (conn_.handle (), + err, + l->locator, + &read, + 0, + 1, + lob_buffer.data (), + lob_buffer.capacity (), + piece, + 0, + 0, + 0, + cs_form); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (conn_, r); + + chunk_position cp; + + if (piece == OCI_FIRST_PIECE) + cp = r == OCI_SUCCESS ? chunk_one : chunk_first; + else if (r == OCI_NEED_DATA) + cp = chunk_next; + else + cp = chunk_last; + + piece = OCI_NEXT_PIECE; + + // OCI generates and ORA-24343 error when an error code is + // returned from a user callback. We simulate this. + // + if (!(*cb->callback.result) ( + cb->context.result, + &position, + lob_buffer.data (), + static_cast<ub4> (read), + cp)) + throw database_exception (24343, "user defined callback error"); + + } while (r == OCI_NEED_DATA); + } + } + } + + // + // bulk_statement + // + + bulk_statement:: + ~bulk_statement () {} + + sword bulk_statement:: + execute (size_t n, multiple_exceptions* mex, sb4 ignore_code) + { + { + odb::tracer* t; + if ((t = conn_.transaction_tracer ()) || + (t = conn_.tracer ()) || + (t = conn_.database ().tracer ())) + t->execute (conn_, *this); + } + + mex_ = mex; + + OCIError* err (conn_.error_handle ()); + + // We use OCI_BATCH_ERRORS for n == 1 in order to get the batch + // error reporting even for a single parameter set. This makes + // it easier to populate mex since otherwise we would have two + // cases to worry about: batch and non-batch (statement fails + // as a whole). + // + sword r (OCIStmtExecute (conn_.handle (), + stmt_, + err, + static_cast<ub4> (n), + 0, + 0, + 0, + status_ == 0 ? OCI_DEFAULT : OCI_BATCH_ERRORS)); + + // If the statement failed as a whole, assume no parameter sets + // were attempted in case of a batch. Otherwise, in the batch + // errors mode, all the sets are always attempted (let's hope + // this is actually true). + // + i_ = 0; + n_ = (r == OCI_ERROR || r == OCI_INVALID_HANDLE + ? (status_ == 0 ? 1 : 0) + : n); + + if (mex_ != 0) + { + mex_->current (i_); + mex_->attempted (n_); + } + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + { + if (mex_ != 0) + mex_->fatal (true); // An incomplete batch is always fatal. + + return r; + } + + // Initialize the batch status array and extract error information + // for failed parameter sets. + // + if (status_ != 0) + { + sword r; // Our own return code. + + // Clear the status array. + // + memset (status_, 0, n * sizeof (status_[0])); + + if (err1_ == 0) + { + OCIError* e (0); + r = OCIHandleAlloc (conn_.database ().environment (), + reinterpret_cast<void**> (&e), + OCI_HTYPE_ERROR, + 0, + 0); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + err1_.reset (e); + } + + ub4 errors; + r = OCIAttrGet (stmt_, + OCI_HTYPE_STMT, + &errors, + 0, + OCI_ATTR_NUM_DML_ERRORS, + err1_); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err1_, r); + + errors_ = errors; + + if (errors != 0) + { + auto_handle<OCIError> err2; + + { + OCIError* e (0); + r = OCIHandleAlloc (conn_.database ().environment (), + reinterpret_cast<void**> (&e), + OCI_HTYPE_ERROR, + 0, + 0); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + err2.reset (e); + } + + for (ub4 i (0); i != errors; ++i) + { + { + OCIError* tmp (err2); + r = OCIParamGet (err, // from + OCI_HTYPE_ERROR, + err1_, // diagnostics + reinterpret_cast<void**> (&tmp), // to + i); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err1_, r); + } + + ub4 row; + r = OCIAttrGet (err2, + OCI_HTYPE_ERROR, + &row, + 0, + OCI_ATTR_DML_ROW_OFFSET, + err1_); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err1_, r); + + OCIErrorGet (err2, 1, 0, &status_[row], 0, 0, OCI_HTYPE_ERROR); + + if (status_[row] != ignore_code) + translate_error (err2, OCI_ERROR, &conn_, row, mex_); + } + } + } + + return r; + } + + unsigned long long bulk_statement:: + affected (bool unique) + { + unsigned long long rows; + { + ub4 n (0); + OCIError* err (conn_.error_handle ()); + sword r (OCIAttrGet (stmt_, + OCI_HTYPE_STMT, + &n, + 0, + OCI_ATTR_ROW_COUNT, + err)); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + + rows = static_cast<unsigned long long> (n); + } + + if (n_ > 1) // Batch. + { + if (rows != 0) // Some rows did get affected. + { + // Subtract the parameter sets that failed since they haven't + // affected any rows. + // + size_t p (n_ - errors_); + + if (p > 1) // True batch. + { + if (unique) // Each can affect 0 or 1 row. + { + rows = (p == static_cast<size_t> (rows) + ? 1 + : result_unknown); + } + else + rows = result_unknown; + } + } + } + + return rows; + } + + // + // generic_statement + // + + generic_statement:: + generic_statement (connection_type& conn, const string& text) + : statement (conn, + text, statement_generic, + 0, false), + bound_ (false) + { + init (); + } + + generic_statement:: + generic_statement (connection_type& conn, const char* text) + : statement (conn, + text, statement_generic, + 0, false), + bound_ (false) + { + init (); + } + + void generic_statement:: + init () + { + OCIError* err (conn_.error_handle ()); + + sword r (OCIAttrGet (stmt_, + OCI_HTYPE_STMT, + &stmt_type_, + 0, + OCI_ATTR_STMT_TYPE, + err)); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + } + + generic_statement:: + ~generic_statement () + { + } + + unsigned long long generic_statement:: + execute () + { + { + odb::tracer* t; + if ((t = conn_.transaction_tracer ()) || + (t = conn_.tracer ()) || + (t = conn_.database ().tracer ())) + t->execute (conn_, *this); + } + + sword r (0); + + OCISvcCtx* handle (conn_.handle ()); + OCIError* err (conn_.error_handle ()); + + if (stmt_type_ == OCI_STMT_SELECT) + { + // Do not prefetch any rows. + // + r = OCIStmtExecute (handle, stmt_, err, 0, 0, 0, 0, OCI_DEFAULT); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (conn_, r); + + // In order to successfully execute a select statement, OCI/Oracle + // requires that there be OCIDefine handles provided for all select + // list columns. Since we are not interested in any data returned by + // the select statement, all buffer pointers, indicator variable + // pointers, and data length pointers are specified as NULL (we still + // specify a valid data type identifier; not doing so results in + // undefined behavior). This results in truncation errors being + // returned for all attempted row fetches. However, cursor behaves + // normally allowing us to return the row count for a select + // statement. Note also that we only need to do this once. + // + if (!bound_) + { + for (ub4 i (1); ; ++i) + { + auto_descriptor<OCIParam> param; + { + OCIParam* p (0); + r = OCIParamGet (stmt_, + OCI_HTYPE_STMT, + err, + reinterpret_cast<void**> (&p), + i); + + if (r == OCI_ERROR) // No more result columns. + break; + + param.reset (p); + } + + ub2 data_type; + r = OCIAttrGet (param, + OCI_DTYPE_PARAM, + &data_type, + 0, + OCI_ATTR_DATA_TYPE, + err); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + + // No need to keep track of the OCIDefine handles - these will + // be deallocated with the statement. + // + OCIDefine* define (0); + r = OCIDefineByPos (stmt_, + &define, + err, + i, + 0, // NULL value buffer pointer + 0, // zero length value buffer + data_type, + 0, // NULL indicator pointer + 0, // NULL length data pointer + 0, // NULL column level return code pointer + OCI_DEFAULT); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + } + + bound_ = true; + } + + for (;;) + { + r = OCIStmtFetch2 (stmt_, err, 1, OCI_FETCH_NEXT, 0, OCI_DEFAULT); + + if (r == OCI_NO_DATA) + break; + else if (r == OCI_ERROR) + { + sb4 e; + r = OCIErrorGet (err, 1, 0, &e, 0, 0, OCI_HTYPE_ERROR); + + // ORA-01406 is returned if there is a truncation error. We expect + // and ignore this error. + // + if (e != 1406) + translate_error (conn_, r); + } + else if (r == OCI_INVALID_HANDLE) + translate_error (err, r); + } + } + else + { + // OCIStmtExecute requires a non-zero iters param for DML statements. + // + r = OCIStmtExecute (handle, stmt_, err, 1, 0, 0, 0, OCI_DEFAULT); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (conn_, r); + } + + ub4 row_count (0); + r = OCIAttrGet (stmt_, + OCI_HTYPE_STMT, + &row_count, + 0, + OCI_ATTR_ROW_COUNT, + err); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + + return row_count; + } + + // + // select_statement + // + + select_statement:: + ~select_statement () + { + } + + select_statement:: + select_statement (connection_type& conn, + const string& text, + bool process, + bool optimize, + binding& param, + binding& result, + size_t lob_prefetch_size) + : statement (conn, + text, statement_select, + (process ? &result : 0), optimize), + result_ (result), + done_ (true) + { + if (!empty ()) + { + bind_param (param.bind, param.count); + result_count_ = bind_result ( + result.bind, result.count, lob_prefetch_size); + } + } + + select_statement:: + select_statement (connection_type& conn, + const char* text, + bool process, + bool optimize, + binding& param, + binding& result, + size_t lob_prefetch_size) + : statement (conn, + text, statement_select, + (process ? &result : 0), optimize), + result_ (result), + done_ (true) + { + if (!empty ()) + { + bind_param (param.bind, param.count); + result_count_ = bind_result ( + result.bind, result.count, lob_prefetch_size); + } + } + + select_statement:: + select_statement (connection_type& conn, + const string& text, + bool process, + bool optimize, + binding& result, + size_t lob_prefetch_size) + : statement (conn, + text, statement_select, + (process ? &result : 0), optimize), + result_ (result), + done_ (true) + { + if (!empty ()) + { + result_count_ = bind_result ( + result.bind, result.count, lob_prefetch_size); + } + } + + select_statement:: + select_statement (connection_type& conn, + const char* text, + bool process, + bool optimize, + binding& result, + size_t lob_prefetch_size) + : statement (conn, + text, statement_select, + (process ? &result : 0), optimize), + result_ (result), + done_ (true) + { + if (!empty ()) + { + result_count_ = bind_result ( + result.bind, result.count, lob_prefetch_size); + } + } + + void select_statement:: + execute () + { + { + odb::tracer* t; + if ((t = conn_.transaction_tracer ()) || + (t = conn_.tracer ()) || + (t = conn_.database ().tracer ())) + t->execute (conn_, *this); + } + + OCIError* err (conn_.error_handle ()); + + // @@ Retrieve a single row into the already bound output buffers as an + // optimization? This will avoid multiple server round-trips in the case + // of a single object load. + // + sword r (OCIStmtExecute (conn_.handle (), + stmt_, + err, + 0, + 0, + 0, + 0, + OCI_DEFAULT)); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (conn_, r); + + done_ = r == OCI_NO_DATA; + +#ifndef NDEBUG + ub4 n (0); + r = OCIAttrGet (stmt_, OCI_HTYPE_STMT, &n, 0, OCI_ATTR_PARAM_COUNT, err); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + + // Make sure that the number of columns in the result returned by + // the database matches the number that we expect. A common cause + // of this assertion is a native view with a number of data members + // not matching the number of columns in the SELECT-list. + // + assert (n == result_count_); +#endif + } + + select_statement::result select_statement:: + fetch () + { + if (!done_) + { + change_callback* cc (result_.change_callback); + + if (cc != 0 && cc->callback != 0) + (cc->callback) (cc->context); + + sword r (OCIStmtFetch2 (stmt_, + conn_.error_handle (), + 1, + OCI_FETCH_NEXT, + 0, + OCI_DEFAULT)); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (conn_, r); + else if (r == OCI_NO_DATA) + done_ = true; + } + + return done_ ? no_data : success; + } + + void select_statement:: + free_result () + { + if (!done_) + { + sword r (OCIStmtFetch2 (stmt_, + conn_.error_handle (), + 0, + OCI_FETCH_NEXT, + 0, + OCI_DEFAULT)); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (conn_, r); + + done_ = true; + } + } + + // + // insert_statement + // + + extern "C" sb4 + odb_oracle_returning_in (void* context, + OCIBind*, // bind + ub4 it, // iter + ub4, // index + void** buffer, + ub4* size, + ub1* piece, + void** indicator) + { + binding& ret (*static_cast<insert_statement*> (context)->ret_); + + // Offset the data based on the current iteration and skip size. + // The id is always first. + // + *buffer = 0; + *size = 0; + *piece = OCI_ONE_PIECE; + + sb2* ind (offset (ret.bind[0].indicator, it, ret.skip)); + *ind = -1; + *indicator = ind; + + return OCI_CONTINUE; + } + + extern "C" sb4 + odb_oracle_returning_out (void* context, + OCIBind*, // bind + ub4 it, // iter + ub4, // index + void** buffer, + ub4** size, + ub1* piece, + void** indicator, + ub2** rcode) + { + insert_statement& st (*static_cast<insert_statement*> (context)); + bind& b (st.ret_->bind[0]); // The id is always first. + size_t skip (st.ret_->skip); + + // Offset the data based on the current iteration and skip size. + // + *buffer = offset (b.buffer, it, skip); + + if (b.type == bind::number) + { + // So the straightforward way to handle this would have been to + // set size to the properly offset pointer to b.size, just like + // we do for the buffer and indicator. The problem is that in + // OCI size is ub2 everywhere except in the *Dynamic() callbacks. + // Here it is expected to be ub4 and, as a result, we cannot use + // our ub2 size that we use throughout (I know you are tempted + // to just cast ub2* to ub4* and forget about this mess, but, + // trust me, this won't end up well). + // + // So what we will do instead is this: have a temporary ub4 buffer + // that we return to OCI so that it can store the size for us. But + // the callback can be called multiple times (batch operations) so + // on each subsequent call we will have to save the size from the + // previous call into our ub2 array. We will also have to handle + // the last extracted size after OCIStmtExecute() below. Thanks, + // Oracle! + // + if (st.ret_prev_ != 0) + *st.ret_prev_ = static_cast<ub2> (st.ret_size_); + + st.ret_prev_ = offset (b.size, it, skip); + *size = &st.ret_size_; + } + + // For some reason we have to set the out size to the (presumably) + // maximum buffer size. + // + **size = b.capacity; + + *indicator = offset (b.indicator, it, skip); + *rcode = 0; + *piece = OCI_ONE_PIECE; + + return OCI_CONTINUE; + } + + insert_statement:: + ~insert_statement () + { + } + + insert_statement:: + insert_statement (connection_type& conn, + const string& text, + bool process, + binding& param, + binding* returning) + : bulk_statement (conn, + text, statement_insert, + (process ? ¶m : 0), false, + param.batch, param.status), + ret_ (returning) + { + init (param); + } + + insert_statement:: + insert_statement (connection_type& conn, + const char* text, + bool process, + binding& param, + binding* returning) + : bulk_statement (conn, + text, statement_insert, + (process ? ¶m : 0), false, + param.batch, param.status), + ret_ (returning) + { + init (param); + } + + void insert_statement:: + init (binding& param) + { + ub4 param_count (bind_param (param.bind, param.count, + param.batch, param.skip)); + if (ret_ != 0) + { + OCIError* err (conn_.error_handle ()); + OCIBind* h (0); + + bind* b (ret_->bind); + +#if OCI_MAJOR_VERSION < 11 || \ + (OCI_MAJOR_VERSION == 11 && OCI_MINOR_VERSION < 2) + // Assert if a 64 bit integer buffer type is provided and the OCI + // version is unable to implicitly convert the NUMBER binary data + // to the relevant type. + // + assert ((b->type != bind::integer && b->type != bind::uinteger) || + b->capacity <= 4); +#endif + sword r (OCIBindByPos (stmt_, + &h, + err, + param_count + 1, + 0, + b->capacity, + param_sqlt_lookup[b->type], + 0, + 0, + 0, + 0, + 0, + OCI_DATA_AT_EXEC)); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + + r = OCIBindDynamic (h, + err, + this, + &odb_oracle_returning_in, + this, + &odb_oracle_returning_out); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + } + } + + size_t insert_statement:: + execute (size_t n, multiple_exceptions* mex) + { + OCIError* err (conn_.error_handle ()); + + if (ret_ != 0) + ret_prev_ = 0; + + // Ignore ORA-00001 error code, see fetch() below for details. + // + sword r (bulk_statement::execute (n, mex, (ret_ == 0 ? 1 : 0))); + + // Statement failed as a whole, assume no parameter sets were + // attempted in case of a batch. + // + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + { + sb4 e; + OCIErrorGet (err, 1, 0, &e, 0, 0, OCI_HTYPE_ERROR); + fetch (r, e); + + if (result_) // If fetch() hasn't translated the error. + translate_error (err, r, &conn_, 0, mex_); // Can return. + + return n_; + } + + // Store the last returned id size (see odb_oracle_returning_out() + // for details). + // + if (ret_ != 0 && ret_prev_ != 0) + *ret_prev_ = static_cast<ub2> (ret_size_); + + if (status_ == 0) // Non-batch mode. + fetch (OCI_SUCCESS, 0); + else + { + fetch (status_[i_] == 0 ? OCI_SUCCESS : OCI_ERROR, status_[i_]); + } + + return n_; + } + + // + // update_statement + // + + update_statement:: + ~update_statement () + { + } + + update_statement:: + update_statement (connection_type& conn, + const string& text, + bool process, + binding& param) + : bulk_statement (conn, + text, statement_update, + (process ? ¶m : 0), false, + param.batch, param.status), + unique_ (false) + { + assert (param.batch == 1); // Specify unique_hint explicitly. + + if (!empty ()) + bind_param (param.bind, param.count, param.batch, param.skip); + } + + update_statement:: + update_statement (connection_type& conn, + const string& text, + bool unique, + bool process, + binding& param) + : bulk_statement (conn, + text, statement_update, + (process ? ¶m : 0), false, + param.batch, param.status), + unique_ (unique) + { + if (!empty ()) + bind_param (param.bind, param.count, param.batch, param.skip); + } + + update_statement:: + update_statement (connection_type& conn, + const char* text, + bool process, + binding& param) + : bulk_statement (conn, + text, statement_update, + (process ? ¶m : 0), false, + param.batch, param.status), + unique_ (false) + { + assert (param.batch == 1); // Specify unique_hint explicitly. + + if (!empty ()) + bind_param (param.bind, param.count, param.batch, param.skip); + } + + update_statement:: + update_statement (connection_type& conn, + const char* text, + bool unique, + bool process, + binding& param) + : bulk_statement (conn, + text, statement_update, + (process ? ¶m : 0), false, + param.batch, param.status), + unique_ (unique) + { + if (!empty ()) + bind_param (param.bind, param.count, param.batch, param.skip); + } + + size_t update_statement:: + execute (size_t n, multiple_exceptions* mex) + { + OCIError* err (conn_.error_handle ()); + sword r (bulk_statement::execute (n, mex)); + + // Statement failed as a whole, assume no parameter sets were + // attempted in case of a batch. + // + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + { + translate_error (err, r, &conn_, 0, mex_); // Can return. + return n_; + } + + // Figure out the affected (matched, not necessarily updated) + // row count. + // + result_ = affected (unique_); + + return n_; + } + + // + // delete_statement + // + + delete_statement:: + ~delete_statement () + { + } + + delete_statement:: + delete_statement (connection_type& conn, + const string& text, + binding& param) + : bulk_statement (conn, + text, statement_delete, + 0, false, + param.batch, param.status), + unique_ (false) + { + assert (param.batch == 1); // Specify unique_hint explicitly. + bind_param (param.bind, param.count, param.batch, param.skip); + } + + delete_statement:: + delete_statement (connection_type& conn, + const string& text, + bool unique, + binding& param) + : bulk_statement (conn, + text, statement_delete, + 0, false, + param.batch, param.status), + unique_ (unique) + { + bind_param (param.bind, param.count, param.batch, param.skip); + } + + delete_statement:: + delete_statement (connection_type& conn, + const char* text, + binding& param) + : bulk_statement (conn, + text, statement_delete, + 0, false, + param.batch, param.status), + unique_ (false) + { + assert (param.batch == 1); // Specify unique_hint explicitly. + bind_param (param.bind, param.count, param.batch, param.skip); + } + + delete_statement:: + delete_statement (connection_type& conn, + const char* text, + bool unique, + binding& param) + : bulk_statement (conn, + text, statement_delete, + 0, false, + param.batch, param.status), + unique_ (unique) + { + bind_param (param.bind, param.count, param.batch, param.skip); + } + + size_t delete_statement:: + execute (size_t n, multiple_exceptions* mex) + { + sword r (bulk_statement::execute (n, mex)); + OCIError* err (conn_.error_handle ()); + + // Statement failed as a whole, assume no parameter sets were + // attempted in case of a batch. + // + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + { + translate_error (err, r, &conn_, 0, mex_); // Can return. + return n_; + } + + // Figure out the affected row count. + // + result_ = affected (unique_); + + return n_; + } + } +} diff --git a/libodb-oracle/odb/oracle/statement.hxx b/libodb-oracle/odb/oracle/statement.hxx new file mode 100644 index 0000000..d435286 --- /dev/null +++ b/libodb-oracle/odb/oracle/statement.hxx @@ -0,0 +1,538 @@ +// file : odb/oracle/statement.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_STATEMENT_HXX +#define ODB_ORACLE_STATEMENT_HXX + +#include <odb/pre.hxx> + +#include <string> +#include <cstddef> // std::size_t + +#include <odb/statement.hxx> +#include <odb/exceptions.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/forward.hxx> +#include <odb/oracle/binding.hxx> +#include <odb/oracle/connection.hxx> +#include <odb/oracle/oracle-fwd.hxx> +#include <odb/oracle/auto-handle.hxx> + +#include <odb/oracle/details/export.hxx> + +namespace odb +{ + namespace oracle + { + class LIBODB_ORACLE_EXPORT statement: public odb::statement + { + public: + typedef oracle::connection connection_type; + + virtual + ~statement () = 0; + + OCIStmt* + handle () const + { + return stmt_; + } + + virtual const char* + text () const; + + virtual connection_type& + connection () + { + return conn_; + } + + // A statement can be empty. This is used to handle situations + // where a SELECT or UPDATE statement ends up not having any + // columns after processing. An empty statement cannot be + // executed. + // + bool + empty () const + { + return stmt_ == 0; + } + + protected: + // We keep two versions to take advantage of std::string COW. + // + statement (connection_type&, + const std::string& text, + statement_kind, + const binding* process, + bool optimize); + + statement (connection_type&, + const char* text, + statement_kind, + const binding* process, + bool optimize); + + private: + void + init (const char* text, + std::size_t text_size, + statement_kind, + const binding* process, + bool optimize); + + protected: + struct unbind + { + oracle::bind::buffer_type type; // Bind type. + oracle::bind* bind; // Corresponding bind entry. + void* value; // Actual value passed to OCIBindByPos. + }; + + // Bind parameters for this statement. This function must only + // be called once. Multiple calls to it will result in memory + // leaks due to lost OCIBind resources. Return the actual number + // of columns bound. + // + ub4 + bind_param (bind*, std::size_t count, + size_t batch = 1, std::size_t skip = 0); + + // Bind results for this statement. This function must only be + // called once. Multiple calls to it will result in memory leaks + // due to lost OCIDefine resources. Return the actual number of + // columns bound. + // + ub4 + bind_result (bind*, + std::size_t count, + std::size_t lob_prefetch_size = 0); + + // Stream the result LOBs, calling user callbacks where necessary. + // The old_base and new_base arguments can be used to "re-base" the + // lob_callback struct pointer (stored in bind::callback), the lob + // struct pointer (stored in bind::buffer), and the indicator value + // pointer (stored in bind::indicator). This is used by the query + // machinery to cause stream_result() to use the callback information + // from a copy of the image instead of the bound image. + // + void + stream_result (bind*, + std::size_t count, + void* old_base = 0, + void* new_base = 0); + + protected: + connection_type& conn_; + auto_handle<OCIStmt> stmt_; + + unbind* udata_; + std::size_t usize_; + }; + + class LIBODB_ORACLE_EXPORT bulk_statement: public statement + { + public: + virtual + ~bulk_statement () = 0; + + protected: + bulk_statement (connection_type&, + const std::string& text, + statement_kind, + const binding* process, + bool optimize, + std::size_t batch, + sb4* status); + + bulk_statement (connection_type&, + const char* text, + statement_kind, + const binding* process, + bool optimize, + std::size_t batch, + sb4* status); + + // Call OCIStmtExecute() and set up the batch tracking variables (see + // below). The ignore_code argument specifies optional error code that + // should not be treated as an error. + // + sword + execute (std::size_t n, multiple_exceptions*, sb4 ignore_code = 0); + + static const unsigned long long result_unknown = ~0ULL; + + unsigned long long + affected (bool unique); + + protected: + auto_handle<OCIError> err1_; + sb4* status_; // Parameter sets status array. + std::size_t n_; // Actual batch size. + std::size_t i_; // Position in result. + std::size_t errors_; // Number of parameter sets that failed. + multiple_exceptions* mex_; + }; + + class LIBODB_ORACLE_EXPORT generic_statement: public statement + { + public: + virtual + ~generic_statement (); + + generic_statement (connection_type&, const std::string& text); + generic_statement (connection_type&, const char* text); + + unsigned long long + execute (); + + private: + generic_statement (const generic_statement&); + generic_statement& operator= (const generic_statement&); + + private: + void + init (); + + private: + ub2 stmt_type_; + bool bound_; + }; + + class LIBODB_ORACLE_EXPORT select_statement: public statement + { + public: + virtual + ~select_statement (); + + select_statement (connection_type& conn, + const std::string& text, + bool process_text, + bool optimize_text, + binding& param, + binding& result, + std::size_t lob_prefetch_size = 0); + + select_statement (connection_type& conn, + const char* text, + bool process_text, + bool optimize_text, + binding& param, + binding& result, + std::size_t lob_prefetch_size = 0); + + select_statement (connection_type& conn, + const std::string& text, + bool process_text, + bool optimize_text, + binding& result, + std::size_t lob_prefetch_size = 0); + + select_statement (connection_type& conn, + const char* text, + bool process_text, + bool optimize_text, + binding& result, + std::size_t lob_prefetch_size = 0); + + enum result + { + success, + no_data + }; + + void + execute (); + + result + fetch (); + + void + stream_result (void* old_base = 0, void* new_base = 0) + { + statement::stream_result (result_.bind, + result_.count, + old_base, + new_base); + } + + void + free_result (); + + private: + select_statement (const select_statement&); + select_statement& operator= (const select_statement&); + + private: + binding& result_; + ub4 result_count_; // Actual number of bound columns. + bool done_; + }; + + struct LIBODB_ORACLE_EXPORT auto_result + { + explicit auto_result (select_statement& s): s_ (s) {} + ~auto_result () {s_.free_result ();} + + private: + auto_result (const auto_result&); + auto_result& operator= (const auto_result&); + + private: + select_statement& s_; + }; + + class LIBODB_ORACLE_EXPORT insert_statement: public bulk_statement + { + public: + virtual + ~insert_statement (); + + insert_statement (connection_type& conn, + const std::string& text, + bool process_text, + binding& param, + binding* returning); + + insert_statement (connection_type& conn, + const char* text, + bool process_text, + binding& param, + binding* returning); + + // Return the number of parameter sets (out of n) that were attempted. + // + std::size_t + execute (std::size_t n, multiple_exceptions& mex) + { + return execute (n, &mex); + } + + // Return true if successful and false if this row is a duplicate. + // All other errors are reported via exceptions. + // + bool + result (std::size_t i) + { + // Get to the next parameter set if necessary. + // + if (i != i_) + { + mex_->current (++i_); // mex cannot be NULL since this is a batch. + fetch (status_[i_] == 0 ? 0 /*OCI_SUCCESS*/ : -1 /*OCI_ERROR*/, + status_[i_]); + } + + return result_; + } + + bool + execute () + { + execute (1, 0); + return result (0); + } + + private: + insert_statement (const insert_statement&); + insert_statement& operator= (const insert_statement&); + + private: + void + init (binding& param); + + std::size_t + execute (std::size_t, multiple_exceptions*); + + void + fetch (sword r, sb4 code); + + public: // For odb_oracle_returning_*(). + binding* ret_; + ub4 ret_size_; // You don't want to know (see statement.cxx). + ub2* ret_prev_; + + private: + bool result_; + }; + + class LIBODB_ORACLE_EXPORT update_statement: public bulk_statement + { + public: + virtual + ~update_statement (); + + // OCI does not expose individual affected row counts for batch + // operations. Instead, it adds them all up and returns a single + // count. This is bad news for us. + // + // In case of updating by primary key (the affected row count is + // either 1 or 0), we can recognize the presumably successful case + // where the total affected row count is equal to the batch size + // (we can also recognize the "all unsuccessful" case where the + // total affected row count is 0). The unique_hint argument in the + // constructors below indicates whether this is a "0 or 1" UPDATE + // statement. + // + // In all other situations (provided this is a batch), the result() + // function below returns the special result_unknown value. + // + update_statement (connection_type& conn, + const std::string& text, + bool process_text, + binding& param); + + update_statement (connection_type& conn, + const std::string& text, + bool unique_hint, + bool process_text, + binding& param); + + update_statement (connection_type& conn, + const char* text, + bool process_text, + binding& param); + + update_statement (connection_type& conn, + const char* text, + bool unique_hint, + bool process_text, + binding& param); + + // Return the number of parameter sets (out of n) that were attempted. + // + std::size_t + execute (std::size_t n, multiple_exceptions& mex) + { + return execute (n, &mex); + } + + // Return the number of rows affected (updated) by the parameter + // set. If this is a batch (n > 1 in execute() call above) and it + // is impossible to determine the affected row count for each + // parameter set, then this function returns result_unknown. All + // other errors are reported by throwing exceptions. + // + using bulk_statement::result_unknown; + + unsigned long long + result (std::size_t i) + { + if (i != i_) + mex_->current (++i_); // mex cannot be NULL since this is a batch. + + return result_; + } + + unsigned long long + execute () + { + execute (1, 0); + return result (0); + } + + private: + update_statement (const update_statement&); + update_statement& operator= (const update_statement&); + + private: + std::size_t + execute (std::size_t, multiple_exceptions*); + + private: + bool unique_; + unsigned long long result_; + }; + + class LIBODB_ORACLE_EXPORT delete_statement: public bulk_statement + { + public: + virtual + ~delete_statement (); + + // OCI does not expose individual affected row counts for batch + // operations. Instead, it adds them all up and returns a single + // count. This is bad news for us. + // + // In case of deleting by primary key (the affected row count is + // either 1 or 0), we can recognize the presumably successful case + // where the total affected row count is equal to the batch size + // (we can also recognize the "all unsuccessful" case where the + // total affected row count is 0). The unique_hint argument in the + // constructors below indicates whether this is a "0 or 1" DELETE + // statement. + // + // In all other situations (provided this is a batch), the result() + // function below returns the special result_unknown value. + // + delete_statement (connection_type& conn, + const std::string& text, + binding& param); + + delete_statement (connection_type& conn, + const std::string& text, + bool unique_hint, + binding& param); + + delete_statement (connection_type& conn, + const char* text, + binding& param); + + delete_statement (connection_type& conn, + const char* text, + bool unique_hint, + binding& param); + + // Return the number of parameter sets (out of n) that were attempted. + // + std::size_t + execute (std::size_t n, multiple_exceptions& mex) + { + return execute (n, &mex); + } + + // Return the number of rows affected (deleted) by the parameter + // set. If this is a batch (n > 1 in execute() call above) and it + // is impossible to determine the affected row count for each + // parameter set, then this function returns result_unknown. All + // other errors are reported by throwing exceptions. + // + using bulk_statement::result_unknown; + + unsigned long long + result (std::size_t i) + { + if (i != i_) + mex_->current (++i_); // mex cannot be NULL since this is a batch. + + return result_; + } + + unsigned long long + execute () + { + execute (1, 0); + return result (0); + } + + private: + delete_statement (const delete_statement&); + delete_statement& operator= (const delete_statement&); + + private: + std::size_t + execute (std::size_t, multiple_exceptions*); + + private: + bool unique_; + unsigned long long result_; + }; + } +} + +#include <odb/oracle/statement.ixx> + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_STATEMENT_HXX diff --git a/libodb-oracle/odb/oracle/statement.ixx b/libodb-oracle/odb/oracle/statement.ixx new file mode 100644 index 0000000..ef0fa64 --- /dev/null +++ b/libodb-oracle/odb/oracle/statement.ixx @@ -0,0 +1,62 @@ +// file : odb/oracle/statement.ixx +// license : ODB NCUEL; see accompanying LICENSE file + +using namespace std; + +namespace odb +{ + namespace oracle + { + // bulk_statement + // + inline bulk_statement:: + bulk_statement (connection_type& c, + const std::string& text, + statement_kind k, + const binding* process, + bool optimize, + std::size_t batch, + sb4* status) + : statement (c, text, k, process, optimize), + status_ (batch == 1 ? 0 : status) + { + } + + inline bulk_statement:: + bulk_statement (connection_type& c, + const char* text, + statement_kind k, + const binding* process, + bool optimize, + std::size_t batch, + sb4* status) + : statement (c, text, k, process, optimize), + status_ (batch == 1 ? 0 : status) + { + } + + // insert_statement + // + inline void insert_statement:: + fetch (sword r, sb4 code) + { + result_ = true; + + if (r != 0 /*OCI_SUCCESS*/) + { + // An auto-assigned object id should never cause a duplicate primary + // key. + // + if (ret_ == 0) + { + // The Oracle error code ORA-00001 indicates unique constraint + // violation, which covers more than just a duplicate primary key. + // Unfortunately, there is nothing more precise that we can use. + // + if (code == 1) + result_ = false; + } + } + } + } +} diff --git a/libodb-oracle/odb/oracle/statements-base.cxx b/libodb-oracle/odb/oracle/statements-base.cxx new file mode 100644 index 0000000..e25d851 --- /dev/null +++ b/libodb-oracle/odb/oracle/statements-base.cxx @@ -0,0 +1,15 @@ +// file : odb/oracle/statements-base.cxx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <odb/oracle/statements-base.hxx> + +namespace odb +{ + namespace oracle + { + statements_base:: + ~statements_base () + { + } + } +} diff --git a/libodb-oracle/odb/oracle/statements-base.hxx b/libodb-oracle/odb/oracle/statements-base.hxx new file mode 100644 index 0000000..61b34ae --- /dev/null +++ b/libodb-oracle/odb/oracle/statements-base.hxx @@ -0,0 +1,63 @@ +// file : odb/oracle/statements-base.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_STATEMENTS_BASE_HXX +#define ODB_ORACLE_STATEMENTS_BASE_HXX + +#include <odb/pre.hxx> + +#include <odb/schema-version.hxx> +#include <odb/details/shared-ptr.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/connection.hxx> +#include <odb/oracle/database.hxx> + +#include <odb/oracle/details/export.hxx> + +namespace odb +{ + namespace oracle + { + class LIBODB_ORACLE_EXPORT statements_base: public details::shared_base + { + public: + typedef oracle::connection connection_type; + + connection_type& + connection () + { + return conn_; + } + + // Schema version. database::schema_version_migration() is thread- + // safe which means it is also slow. Cache the result in statements + // so we can avoid the mutex lock. This is thread-safe since if the + // version is updated, then the statements cache will be expired. + // + const schema_version_migration& + version_migration (const char* name = "") const + { + if (svm_ == 0) + svm_ = &conn_.database ().schema_version_migration (name); + + return *svm_; + } + + public: + virtual + ~statements_base (); + + protected: + statements_base (connection_type& conn): conn_ (conn), svm_ (0) {} + + protected: + connection_type& conn_; + mutable const schema_version_migration* svm_; + }; + } +} + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_STATEMENTS_BASE_HXX diff --git a/libodb-oracle/odb/oracle/tracer.cxx b/libodb-oracle/odb/oracle/tracer.cxx new file mode 100644 index 0000000..ca56d05 --- /dev/null +++ b/libodb-oracle/odb/oracle/tracer.cxx @@ -0,0 +1,60 @@ +// file : odb/oracle/tracer.cxx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <odb/oracle/tracer.hxx> +#include <odb/oracle/connection.hxx> +#include <odb/oracle/statement.hxx> + +namespace odb +{ + namespace oracle + { + tracer:: + ~tracer () + { + } + + void tracer:: + prepare (connection&, const statement&) + { + } + + void tracer:: + execute (connection& c, const statement& s) + { + execute (c, s.text ()); + } + + void tracer:: + deallocate (connection&, const statement&) + { + } + + void tracer:: + prepare (odb::connection& c, const odb::statement& s) + { + prepare (static_cast<connection&> (c), + static_cast<const statement&> (s)); + } + + void tracer:: + execute (odb::connection& c, const odb::statement& s) + { + execute (static_cast<connection&> (c), + static_cast<const statement&> (s)); + } + + void tracer:: + execute (odb::connection& c, const char* s) + { + execute (static_cast<connection&> (c), s); + } + + void tracer:: + deallocate (odb::connection& c, const odb::statement& s) + { + deallocate (static_cast<connection&> (c), + static_cast<const statement&> (s)); + } + } +} diff --git a/libodb-oracle/odb/oracle/tracer.hxx b/libodb-oracle/odb/oracle/tracer.hxx new file mode 100644 index 0000000..0627926 --- /dev/null +++ b/libodb-oracle/odb/oracle/tracer.hxx @@ -0,0 +1,61 @@ +// file : odb/oracle/tracer.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_TRACER_HXX +#define ODB_ORACLE_TRACER_HXX + +#include <odb/pre.hxx> + +#include <odb/tracer.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/forward.hxx> +#include <odb/oracle/details/export.hxx> + +namespace odb +{ + namespace oracle + { + class LIBODB_ORACLE_EXPORT tracer: private odb::tracer + { + public: + virtual + ~tracer (); + + virtual void + prepare (connection&, const statement&); + + virtual void + execute (connection&, const statement&); + + virtual void + execute (connection&, const char* statement) = 0; + + virtual void + deallocate (connection&, const statement&); + + private: + // Allow these classes to convert oracle::tracer to odb::tracer. + // + friend class database; + friend class connection; + friend class transaction; + + virtual void + prepare (odb::connection&, const odb::statement&); + + virtual void + execute (odb::connection&, const odb::statement&); + + virtual void + execute (odb::connection&, const char* statement); + + virtual void + deallocate (odb::connection&, const odb::statement&); + }; + } +} + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_TRACER_HXX diff --git a/libodb-oracle/odb/oracle/traits-calls.hxx b/libodb-oracle/odb/oracle/traits-calls.hxx new file mode 100644 index 0000000..7b9d6f3 --- /dev/null +++ b/libodb-oracle/odb/oracle/traits-calls.hxx @@ -0,0 +1,190 @@ +// file : odb/oracle/traits-calls.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_TRAITS_CALLS_HXX +#define ODB_ORACLE_TRAITS_CALLS_HXX + +#include <odb/pre.hxx> + +#include <cstddef> // std::size_t + +#include <odb/forward.hxx> +#include <odb/schema-version.hxx> +#include <odb/traits.hxx> + +#include <odb/oracle/forward.hxx> +#include <odb/oracle/oracle-types.hxx> + +namespace odb +{ + namespace oracle + { + // + // object_traits_calls + // + + template <typename T, + bool versioned = object_traits_impl<T, id_oracle>::versioned> + struct object_traits_calls; + + template <typename T> + struct object_traits_calls<T, false> + { + typedef object_traits_impl<T, id_oracle> traits; + typedef typename traits::image_type image_type; + typedef oracle::bind bind_type; + + object_traits_calls (const schema_version_migration*) {} + + const schema_version_migration* + version () const {return 0;} + + static void + bind (bind_type* b, image_type& i, statement_kind sk) + { + traits::bind (b, i, sk); + } + + // Poly-derived version. + // + static void + bind (bind_type* b, + const bind_type* id, std::size_t id_size, + image_type& i, + statement_kind sk) + { + traits::bind (b, id, id_size, i, sk); + } + + static void + init (T& o, const image_type& i, odb::database* db) + { + traits::init (o, i, db); + } + + static bool + find_ (typename traits::statements_type& sts, + const typename traits::id_type* id) + { + return traits::find_ (sts, id); + } + + static void + load_ (typename traits::statements_type& sts, T& o, bool reload) + { + return traits::load_ (sts, o, reload); + } + }; + + template <typename T> + struct object_traits_calls<T, true> + { + typedef object_traits_impl<T, id_oracle> traits; + typedef typename traits::image_type image_type; + typedef oracle::bind bind_type; + + object_traits_calls (const schema_version_migration* svm): svm_ (*svm) {} + + const schema_version_migration* + version () const {return &svm_;} + + void + bind (bind_type* b, image_type& i, statement_kind sk) const + { + traits::bind (b, i, sk, svm_); + } + + // Poly-derived version. + // + void + bind (bind_type* b, + const bind_type* id, std::size_t id_size, + image_type& i, + statement_kind sk) const + { + traits::bind (b, id, id_size, i, sk, svm_); + } + + void + init (T& o, const image_type& i, odb::database* db) const + { + traits::init (o, i, db, svm_); + } + + bool + find_ (typename traits::statements_type& sts, + const typename traits::id_type* id) const + { + return traits::find_ (sts, id, svm_); + } + + void + load_ (typename traits::statements_type& sts, T& o, bool reload) const + { + return traits::load_ (sts, o, reload, svm_); + } + + private: + const schema_version_migration& svm_; + }; + + // + // view_traits_calls + // + + template <typename T, + bool versioned = view_traits_impl<T, id_oracle>::versioned> + struct view_traits_calls; + + template <typename T> + struct view_traits_calls<T, false> + { + typedef view_traits_impl<T, id_oracle> traits; + typedef typename traits::image_type image_type; + typedef oracle::bind bind_type; + + view_traits_calls (const schema_version_migration*) {} + + static void + bind (bind_type* b, image_type& i) + { + traits::bind (b, i); + } + + static void + init (T& o, const image_type& i, odb::database* db) + { + traits::init (o, i, db); + } + }; + + template <typename T> + struct view_traits_calls<T, true> + { + typedef view_traits_impl<T, id_oracle> traits; + typedef typename traits::image_type image_type; + typedef oracle::bind bind_type; + + view_traits_calls (const schema_version_migration* svm): svm_ (*svm) {} + + void + bind (bind_type* b, image_type& i) const + { + traits::bind (b, i, svm_); + } + + void + init (T& o, const image_type& i, odb::database* db) const + { + traits::init (o, i, db, svm_); + } + + private: + const schema_version_migration& svm_; + }; + } +} + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_TRAITS_CALLS_HXX diff --git a/libodb-oracle/odb/oracle/traits.cxx b/libodb-oracle/odb/oracle/traits.cxx new file mode 100644 index 0000000..6c7b46e --- /dev/null +++ b/libodb-oracle/odb/oracle/traits.cxx @@ -0,0 +1,201 @@ +// file : odb/oracle/traits.cxx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <odb/oracle/traits.hxx> + +using namespace std; + +namespace odb +{ + namespace oracle + { + // + // c_array_value_traits_base + // + void c_array_value_traits_base:: + set_value (char* const& v, + const char* b, + size_t n, + bool is_null, + size_t N) + { + if (!is_null) + { + n = n < N ? n : N; + + if (n != 0) + memcpy (v, b, n); + } + else + n = 0; + + if (n != N) // Append '\0' if there is space. + v[n] = '\0'; + } + + void c_array_value_traits_base:: + set_image (char* b, + size_t c, + size_t& n, + bool& is_null, + const char* v, + size_t N) + { + is_null = false; + + // Figure out the length. We cannot use strlen since it may + // not be 0-terminated (strnlen is not standard). + // + for (n = 0; n != N && v[n] != '\0'; ++n) ; + + if (n > c) + n = c; + + if (n != 0) + memcpy (b, v, n); + } + + // + // string_lob_value_traits + // + bool string_lob_value_traits:: + result_callback (void* c, ub4*, void* b, ub4 s, chunk_position p) + { + string& v (*static_cast<string*> (c)); + + switch (p) + { + case chunk_one: + case chunk_first: + { + v.clear (); + } + // Fall through. + case chunk_next: + case chunk_last: + { + v.append (static_cast<char*> (b), s); + break; + } + } + + return true; + } + + bool string_lob_value_traits:: + param_callback (const void* c, + ub4*, + const void** b, + ub4* s, + chunk_position* p, + void*, + ub4) + { + const string& v (*static_cast<const string*> (c)); + + *p = chunk_one; + *s = static_cast<ub4> (v.size ()); + *b = v.c_str (); + + return true; + } + + // + // default_value_traits<std::vector<char>, id_blob> + // + // std::vector has to be qualified for Sun CC. + // + bool default_value_traits<std::vector<char>, id_blob>:: + result_callback (void* c, ub4*, void* b, ub4 s, chunk_position p) + { + value_type& v (*static_cast<value_type*> (c)); + + switch (p) + { + case chunk_one: + case chunk_first: + { + v.clear (); + } + // Fall through. + case chunk_next: + case chunk_last: + { + char* cb (static_cast<char*> (b)); + v.insert (v.end (), cb, cb + s); + + break; + } + } + + return true; + } + + bool default_value_traits<std::vector<char>, id_blob>:: + param_callback (const void* c, + ub4*, + const void** b, + ub4* s, + chunk_position* p, + void*, + ub4) + { + const value_type& v (*static_cast<const value_type*> (c)); + + *p = chunk_one; + *s = static_cast<ub4> (v.size ()); + *b = v.empty () ? 0 : &v.front (); + + return true; + } + + // + // default_value_traits<std::vector<unsigned char>, id_blob> + // + // std::vector has to be qualified for Sun CC. + // + bool default_value_traits<std::vector<unsigned char>, id_blob>:: + result_callback (void* c, ub4*, void* b, ub4 s, chunk_position p) + { + value_type& v (*static_cast<value_type*> (c)); + + switch (p) + { + case chunk_one: + case chunk_first: + { + v.clear (); + } + // Fall through. + case chunk_next: + case chunk_last: + { + unsigned char* cb (static_cast<unsigned char*> (b)); + v.insert (v.end (), cb, cb + s); + + break; + } + } + + return true; + } + + bool default_value_traits<std::vector<unsigned char>, id_blob>:: + param_callback (const void* c, + ub4*, + const void** b, + ub4* s, + chunk_position* p, + void*, + ub4) + { + const value_type& v (*static_cast<const value_type*> (c)); + + *p = chunk_one; + *s = static_cast<ub4> (v.size ()); + *b = v.empty () ? 0 : &v.front (); + + return true; + } + } +} diff --git a/libodb-oracle/odb/oracle/traits.hxx b/libodb-oracle/odb/oracle/traits.hxx new file mode 100644 index 0000000..8a1673c --- /dev/null +++ b/libodb-oracle/odb/oracle/traits.hxx @@ -0,0 +1,1491 @@ +// file : odb/oracle/traits.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_TRAITS_HXX +#define ODB_ORACLE_TRAITS_HXX + +#include <odb/pre.hxx> + +#include <odb/details/config.hxx> // ODB_CXX11 + +#include <string> +#include <vector> +#include <cstddef> // std::size_t +#include <cstring> // std::memcpy, std::memset, std::strlen + +#ifdef ODB_CXX11 +# include <array> +#endif + +#include <odb/traits.hxx> +#include <odb/wrapper-traits.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/oracle-types.hxx> + +#include <odb/details/buffer.hxx> +#include <odb/details/wrapper-p.hxx> + +#include <odb/oracle/details/export.hxx> +#include <odb/oracle/details/number.hxx> + +namespace odb +{ + namespace oracle + { + enum database_type_id + { + id_int32, + id_int64, + + id_big_int, + + id_float, + id_double, + + // Both large fixed-point and large floating point NUMBER and FLOAT + // values are mapped to this id. + // + id_big_float, + + id_date, + id_timestamp, + id_interval_ym, + id_interval_ds, + + id_string, + id_nstring, + + id_raw, + + id_blob, + id_clob, + id_nclob + }; + + // + // int_traits + // + + // Only mark fundamental unsigned integers as unsigned. In particular, + // treat enums as signed since in most cases and on most platforms the + // underlying integer type will be signed. On Windows with VC9 and up + // and with GCC, the __intN types are simply aliases for the respective + // standard integers so the below code will cover them as well. Also + // note that the ODB compiler performs a similar test, so if you change + // anything below you will probably also need to make a similar change + // there. + // + template <typename T> + struct int_traits {static const bool unsign = false;}; + + template <> + struct int_traits<bool> {static const bool unsign = true;}; + template <> + struct int_traits<unsigned char> {static const bool unsign = true;}; + template <> + struct int_traits<unsigned short> {static const bool unsign = true;}; + template <> + struct int_traits<unsigned int> {static const bool unsign = true;}; + template <> + struct int_traits<unsigned long> {static const bool unsign = true;}; + template <> + struct int_traits<unsigned long long> + { + static const bool unsign = true; + }; + + // + // image_traits + // + + template <typename T, database_type_id> + struct image_traits; + + // int32 + // + template <bool unsign> + struct int32_image_traits; + + template <> + struct int32_image_traits<false> + { + static const bind::buffer_type buffer_type = bind::integer; + typedef int image_type; + }; + + template <> + struct int32_image_traits<true> + { + static const bind::buffer_type buffer_type = bind::uinteger; + typedef unsigned int image_type; + }; + + template <typename T> + struct image_traits<T, id_int32>: int32_image_traits<int_traits<T>::unsign> + { + }; + + // int64 + // + template <bool unsign> + struct int64_image_traits; + + template <> + struct int64_image_traits<false> + { + static const bind::buffer_type buffer_type = bind::integer; + typedef long long image_type; + }; + + template <> + struct int64_image_traits<true> + { + static const bind::buffer_type buffer_type = bind::uinteger; + typedef unsigned long long image_type; + }; + + template <typename T> + struct image_traits<T, id_int64>: int64_image_traits<int_traits<T>::unsign> + { + }; + + // big_int + // + template <typename T> + struct image_traits<T, id_big_int> + { + // Image is a buffer containing native OCI NUMBER representation. + // + typedef char* image_type; + }; + + template <typename T> + struct image_traits<T, id_float> {typedef float image_type;}; + + template <typename T> + struct image_traits<T, id_double> {typedef double image_type;}; + + template <typename T> + struct image_traits<T, id_big_float> + { + // Image is a buffer containing the native OCI NUMBER representation. + // + typedef char* image_type; + }; + + template <typename T> + struct image_traits<T, id_date> + { + // Image is a buffer containing the native OCI DATE representation. This + // buffer has a fixed length of 7 bytes. + // + typedef char* image_type; + }; + + template <typename T> + struct image_traits<T, id_timestamp> + { + typedef datetime image_type; + }; + + template <typename T> + struct image_traits<T, id_interval_ym> + { + typedef interval_ym image_type; + }; + + template <typename T> + struct image_traits<T, id_interval_ds> + { + typedef interval_ds image_type; + }; + + template <typename T> + struct image_traits<T, id_string> {typedef char* image_type;}; + + template <typename T> + struct image_traits<T, id_nstring> {typedef char* image_type;}; + + template <typename T> + struct image_traits<T, id_raw> {typedef char* image_type;}; + + template <typename T> + struct image_traits<T, id_blob> {typedef lob_callback image_type;}; + + template <typename T> + struct image_traits<T, id_clob> {typedef lob_callback image_type;}; + + template <typename T> + struct image_traits<T, id_nclob> {typedef lob_callback image_type;}; + + // + // value_traits + // + + template <typename W, database_type_id, bool null_handler> + struct wrapped_value_traits; + + template <typename T, database_type_id> + struct default_value_traits; + + template <typename T, database_type_id, bool w = details::wrapper_p<T>::r> + struct select_traits; + + template <typename T, database_type_id ID> + struct select_traits<T, ID, false> + { + typedef default_value_traits<T, ID> type; + }; + + template <typename W, database_type_id ID> + struct select_traits<W, ID, true> + { + typedef + wrapped_value_traits<W, ID, wrapper_traits<W>::null_handler> + type; + }; + + template <typename T, database_type_id ID> + class value_traits: public select_traits<T, ID>::type + { + }; + + // The wrapped_value_traits specializations should be able to handle + // any value type which means we have to have every possible signature + // of the set_value() and set_image() functions. + // + template <typename W, database_type_id ID> + struct wrapped_value_traits<W, ID, false> + { + typedef wrapper_traits<W> wtraits; + typedef typename wtraits::unrestricted_wrapped_type wrapped_type; + + typedef W value_type; + typedef wrapped_type query_type; + typedef typename image_traits<wrapped_type, ID>::image_type image_type; + + typedef value_traits<wrapped_type, ID> vtraits; + + static void + set_value (W& v, const image_type& i, bool is_null) + { + vtraits::set_value (wtraits::set_ref (v), i, is_null); + } + + static void + set_image (image_type& i, bool& is_null, const W& v) + { + vtraits::set_image (i, is_null, wtraits::get_ref (v)); + } + + // big_int, big_float, string, nstring, raw. + // + static void + set_value (W& v, const char* i, std::size_t n, bool is_null) + { + vtraits::set_value (wtraits::set_ref (v), i, n, is_null); + } + + // string, nstring, raw. + // + static void + set_image (char* i, + std::size_t c, + std::size_t& n, + bool& is_null, + const W& v) + { + vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v)); + } + + // big_int, big_float. + // + static void + set_image (char* i, std::size_t& n, bool& is_null, const W& v) + { + vtraits::set_image (i, n, is_null, wtraits::get_ref (v)); + } + + // blob, clob, nclob. + // + static void + set_value (W& v, result_callback_type& cb, void*& context, bool is_null) + { + vtraits::set_value (wtraits::set_ref (v), cb, context, is_null); + } + + static void + set_image (param_callback_type& cb, + const void*& context, + bool& is_null, + const W& v) + { + vtraits::set_image (cb, context, is_null, wtraits::get_ref (v)); + } + }; + + template <typename W, database_type_id ID> + struct wrapped_value_traits<W, ID, true> + { + typedef wrapper_traits<W> wtraits; + typedef typename wtraits::unrestricted_wrapped_type wrapped_type; + + typedef W value_type; + typedef wrapped_type query_type; + typedef typename image_traits<wrapped_type, ID>::image_type image_type; + + typedef value_traits<wrapped_type, ID> vtraits; + + static void + set_value (W& v, const image_type& i, bool is_null) + { + if (is_null) + wtraits::set_null (v); + else + vtraits::set_value (wtraits::set_ref (v), i, is_null); + } + + static void + set_image (image_type& i, bool& is_null, const W& v) + { + is_null = wtraits::get_null (v); + + if (!is_null) + vtraits::set_image (i, is_null, wtraits::get_ref (v)); + } + + // big_int, big_float, string, nstring, raw. + // + static void + set_value (W& v, const char* i, std::size_t n, bool is_null) + { + if (is_null) + wtraits::set_null (v); + else + vtraits::set_value (wtraits::set_ref (v), i, n, is_null); + } + + // string, nstring, raw. + // + static void + set_image (char* i, + std::size_t c, + std::size_t& n, + bool& is_null, + const W& v) + { + is_null = wtraits::get_null (v); + + if (!is_null) + vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v)); + } + + // big_int, big_float + // + static void + set_image (char* i, std::size_t& n, bool& is_null, const W& v) + { + is_null = wtraits::get_null (v); + + if (!is_null) + vtraits::set_image (i, n, is_null, wtraits::get_ref (v)); + } + + // blob, clob, nclob. + // + static void + set_value (W& v, result_callback_type& cb, void*& context, bool is_null) + { + if (is_null) + wtraits::set_null (v); + else + vtraits::set_value (wtraits::set_ref (v), cb, context, is_null); + } + + static void + set_image (param_callback_type& cb, + const void*& context, + bool& is_null, + const W& v) + { + is_null = wtraits::get_null (v); + + if (!is_null) + vtraits::set_image (cb, context, is_null, wtraits::get_ref (v)); + } + }; + + template <typename T, database_type_id ID> + struct default_value_traits + { + typedef T value_type; + typedef T query_type; + typedef typename image_traits<T, ID>::image_type image_type; + + static void + set_value (T& v, const image_type& i, bool is_null) + { + if (!is_null) + v = T (i); + else + v = T (); + } + + static void + set_image (image_type& i, bool& is_null, T v) + { + is_null = false; + i = image_type (v); + } + }; + + // id_big_int partial specialization. + // + template <typename T, bool unsign> + struct big_int_value_traits; + + template <typename T> + struct big_int_value_traits<T, false> + { + static void + set_value (T& v, const char* b, std::size_t n, bool is_null) + { + if (!is_null) + v = static_cast<T> (details::number_to_int64 (b, n)); + else + v = T (); + } + + static void + set_image (char* b, std::size_t& n, bool& is_null, T v) + { + is_null = false; + details::int64_to_number (b, n, static_cast<long long> (v)); + } + }; + + template <typename T> + struct big_int_value_traits<T, true> + { + static void + set_value (T& v, const char* b, std::size_t n, bool is_null) + { + if (!is_null) + v = static_cast<T> (details::number_to_uint64 (b, n)); + else + v = T (); + } + + static void + set_image (char* b, std::size_t& n, bool& is_null, T v) + { + is_null = false; + details::uint64_to_number (b, n, static_cast<unsigned long long> (v)); + } + }; + + template <typename T> + struct default_value_traits<T, id_big_int>: + big_int_value_traits<T, int_traits<T>::unsign> + { + typedef T value_type; + typedef T query_type; + typedef typename image_traits<T, id_big_int>::image_type image_type; + }; + + // std::string specialization. + // + class LIBODB_ORACLE_EXPORT string_value_traits + { + public: + typedef std::string value_type; + typedef std::string query_type; + typedef char* image_type; + + static void + set_value (std::string& v, + const char* b, + std::size_t n, + bool is_null) + { + if (!is_null) + v.assign (b, n); + else + v.erase (); + } + + static void + set_image (char* b, + std::size_t c, + std::size_t& n, + bool& is_null, + const std::string& v) + { + is_null = false; + n = v.size (); + + if (n > c) + n = c; + + if (n != 0) + std::memcpy (b, v.c_str (), n); + } + }; + + template <> + struct LIBODB_ORACLE_EXPORT default_value_traits<std::string, id_string>: + string_value_traits + { + }; + + template <> + struct LIBODB_ORACLE_EXPORT default_value_traits<std::string, id_nstring>: + string_value_traits + { + }; + + // char*/const char* specialization. + // + // Specialization for const char* which only supports initialization + // of an image from the value but not the other way around. This way + // we can pass such values to the queries. + // + class LIBODB_ORACLE_EXPORT c_string_value_traits + { + public: + typedef const char* value_type; + typedef char* image_type; + + static void + set_image (char* b, + std::size_t c, + std::size_t& n, + bool& is_null, + const char* v) + { + is_null = false; + n = std::strlen (v); + + if (n > c) + n = c; + + if (n != 0) + std::memcpy (b, v, n); + } + }; + + template <> + struct LIBODB_ORACLE_EXPORT default_value_traits<char*, id_string>: + c_string_value_traits {}; + + template <> + struct LIBODB_ORACLE_EXPORT default_value_traits<char*, id_nstring>: + c_string_value_traits {}; + + template <> + struct LIBODB_ORACLE_EXPORT default_value_traits<const char*, id_string>: + c_string_value_traits {}; + + template <> + struct LIBODB_ORACLE_EXPORT default_value_traits<const char*, id_nstring>: + c_string_value_traits {}; + + // char[N] specializations. + // + struct LIBODB_ORACLE_EXPORT c_array_value_traits_base + { + static void + set_value (char* const& v, + const char* b, + std::size_t n, + bool is_null, + std::size_t N); + + static void + set_image (char* b, + std::size_t c, + std::size_t& n, + bool& is_null, + const char* v, + std::size_t N); + }; + + template <std::size_t N> + struct c_array_value_traits + { + typedef char* value_type; + typedef char query_type[N]; + typedef details::buffer image_type; + + static void + set_value (char* const& v, + const char* b, + std::size_t n, + bool is_null) + { + c_array_value_traits_base::set_value (v, b, n, is_null, N); + } + + static void + set_image (char* b, + std::size_t c, + std::size_t& n, + bool& is_null, + const char* v) + { + c_array_value_traits_base::set_image (b, c, n, is_null, v, N); + } + }; + + template <std::size_t N> + struct default_value_traits<char[N], id_string>: + c_array_value_traits<N> {}; + + template <std::size_t N> + struct default_value_traits<char[N], id_nstring>: + c_array_value_traits<N> {}; + + // std::array<char, N> (string) specialization. + // +#ifdef ODB_CXX11 + template <std::size_t N> + struct std_array_value_traits + { + typedef std::array<char, N> value_type; + typedef std::array<char, N> query_type; + typedef details::buffer image_type; + + static void + set_value (value_type& v, + const char* b, + std::size_t n, + bool is_null) + { + c_array_value_traits_base::set_value (v.data (), b, n, is_null, N); + } + + static void + set_image (char* b, + std::size_t c, + std::size_t& n, + bool& is_null, + const value_type& v) + { + c_array_value_traits_base::set_image (b, c, n, is_null, v.data (), N); + } + }; + + template <std::size_t N> + struct default_value_traits<std::array<char, N>, id_string>: + std_array_value_traits<N> {}; + + template <std::size_t N> + struct default_value_traits<std::array<char, N>, id_nstring>: + std_array_value_traits<N> {}; +#endif + + // char specialization. + // + struct LIBODB_ORACLE_EXPORT char_value_traits + { + typedef char value_type; + typedef char query_type; + typedef details::buffer image_type; + + static void + set_value (char& v, + const char* b, + std::size_t n, + bool is_null) + { + c_array_value_traits_base::set_value (&v, b, n, is_null, 1); + } + + static void + set_image (char* b, + std::size_t c, + std::size_t& n, + bool& is_null, + char v) + { + c_array_value_traits_base::set_image (b, c, n, is_null, &v, 1); + } + }; + + template <> + struct LIBODB_ORACLE_EXPORT default_value_traits<char, id_string>: + char_value_traits {}; + + template <> + struct LIBODB_ORACLE_EXPORT default_value_traits<char, id_nstring>: + char_value_traits {}; + + // std::vector<char> (buffer) specialization for RAW. + // + template <> + struct LIBODB_ORACLE_EXPORT default_value_traits<std::vector<char>, id_raw> + { + public: + typedef std::vector<char> value_type; + typedef std::vector<char> query_type; + typedef char* image_type; + + static void + set_value (value_type& v, const char* b, std::size_t n, bool is_null) + { + if (!is_null) + v.assign (b, b + n); + else + v.clear (); + } + + static void + set_image (char* b, + std::size_t c, + std::size_t& n, + bool& is_null, + const value_type& v) + { + is_null = false; + n = v.size (); + + if (n > c) + n = c; + + // std::vector::data() may not be available in older compilers. + // + if (n != 0) + std::memcpy (b, &v.front (), n); + } + }; + + // std::vector<unsigned char> (buffer) specialization for RAW. + // + template <> + struct LIBODB_ORACLE_EXPORT + default_value_traits<std::vector<unsigned char>, id_raw> + { + typedef std::vector<unsigned char> value_type; + typedef std::vector<unsigned char> query_type; + typedef char* image_type; + + static void + set_value (value_type& v, const char* b, std::size_t n, bool is_null) + { + if (!is_null) + { + const unsigned char* ub (reinterpret_cast<const unsigned char*> (b)); + v.assign (ub, ub + n); + } + else + v.clear (); + } + + static void + set_image (char* b, + std::size_t c, + std::size_t& n, + bool& is_null, + const value_type& v) + { + is_null = false; + n = v.size (); + + if (n > c) + n = c; + + // std::vector::data() may not be available in older compilers. + // + if (n != 0) + std::memcpy (b, &v.front (), n); + } + }; + + // char[N] (buffer) specialization for RAW. + // + template <std::size_t N> + struct default_value_traits<char[N], id_raw> + { + typedef char* value_type; + typedef char query_type[N]; + typedef char* image_type; + + static void + set_value (char* const& v, const char* b, std::size_t n, bool is_null) + { + if (!is_null) + std::memcpy (v, b, (n < N ? n : N)); + else + std::memset (v, 0, N); + } + + static void + set_image (char* b, + std::size_t c, + std::size_t& n, + bool& is_null, + const char* v) + { + is_null = false; + n = N < c ? N : c; + std::memcpy (b, v, n); + } + }; + + // unsigned char[N] (buffer) specialization for RAW. + // + template <std::size_t N> + struct default_value_traits<unsigned char[N], id_raw> + { + typedef unsigned char* value_type; + typedef unsigned char query_type[N]; + typedef char* image_type; + + static void + set_value (unsigned char* const& v, + const char* b, + std::size_t n, + bool is_null) + { + if (!is_null) + std::memcpy (v, b, (n < N ? n : N)); + else + std::memset (v, 0, N); + } + + static void + set_image (char* b, + std::size_t c, + std::size_t& n, + bool& is_null, + const unsigned char* v) + { + is_null = false; + n = N < c ? N : c; + std::memcpy (b, v, n); + } + }; + +#ifdef ODB_CXX11 + // std::array<char, N> (buffer) specialization for RAW. + // + template <std::size_t N> + struct default_value_traits<std::array<char, N>, id_raw> + { + public: + typedef std::array<char, N> value_type; + typedef value_type query_type; + typedef char* image_type; + + static void + set_value (value_type& v, const char* b, std::size_t n, bool is_null) + { + if (!is_null) + std::memcpy (v.data (), b, (n < N ? n : N)); + else + std::memset (v.data (), 0, N); + } + + static void + set_image (char* b, + std::size_t c, + std::size_t& n, + bool& is_null, + const value_type& v) + { + is_null = false; + n = N < c ? N : c; + std::memcpy (b, v.data (), n); + } + }; + + // std::array<unsigned char, N> (buffer) specialization for RAW. + // + template <std::size_t N> + struct default_value_traits<std::array<unsigned char, N>, id_raw> + { + public: + typedef std::array<unsigned char, N> value_type; + typedef value_type query_type; + typedef char* image_type; + + static void + set_value (value_type& v, const char* b, std::size_t n, bool is_null) + { + if (!is_null) + std::memcpy (v.data (), b, (n < N ? n : N)); + else + std::memset (v.data (), 0, N); + } + + static void + set_image (char* b, + std::size_t c, + std::size_t& n, + bool& is_null, + const value_type& v) + { + is_null = false; + n = N < c ? N : c; + std::memcpy (b, v.data (), n); + } + }; +#endif + + // std::string specialization for LOBs. + // + class LIBODB_ORACLE_EXPORT string_lob_value_traits + { + public: + typedef std::string value_type; + typedef std::string query_type; + typedef lob_callback image_type; + + static void + set_value (std::string& v, + result_callback_type& cb, + void*& context, + bool is_null) + { + if (!is_null) + { + cb = &result_callback; + context = &v; + } + else + v.erase (); + } + + static void + set_image (param_callback_type& cb, + const void*& context, + bool& is_null, + const std::string& v) + { + is_null = false; + cb = ¶m_callback; + context = &v; + } + + static bool + result_callback (void* context, + ub4* position_context, + void* buffer, + ub4 size, + chunk_position); + + static bool + param_callback (const void* context, + ub4* position_context, + const void** buffer, + ub4* size, + chunk_position*, + void* temp_buffer, + ub4 capacity); + }; + + template <> + struct LIBODB_ORACLE_EXPORT default_value_traits<std::string, id_clob>: + string_lob_value_traits + { + }; + + template <> + struct LIBODB_ORACLE_EXPORT default_value_traits<std::string, id_nclob>: + string_lob_value_traits + { + }; + + // std::vector<char> (buffer) specialization for BLOBs. + // + template <> + struct LIBODB_ORACLE_EXPORT default_value_traits<std::vector<char>, + id_blob> + { + public: + typedef std::vector<char> value_type; + typedef std::vector<char> query_type; + typedef lob_callback image_type; + + static void + set_value (value_type& v, + result_callback_type& cb, + void*& context, + bool is_null) + { + if (!is_null) + { + cb = &result_callback; + context = &v; + } + else + v.clear (); + } + + static void + set_image (param_callback_type& cb, + const void*& context, + bool& is_null, + const value_type& v) + { + is_null = false; + cb = ¶m_callback; + context = &v; + } + + static bool + result_callback (void* context, + ub4* position_context, + void* buffer, + ub4 size, + chunk_position); + + static bool + param_callback (const void* context, + ub4* position_context, + const void** buffer, + ub4* size, + chunk_position*, + void* temp_buffer, + ub4 capacity); + }; + + // std::vector<unsigned char> (buffer) specialization for BLOBs. + // + template <> + struct LIBODB_ORACLE_EXPORT + default_value_traits<std::vector<unsigned char>, id_blob> + { + public: + typedef std::vector<unsigned char> value_type; + typedef std::vector<unsigned char> query_type; + typedef lob_callback image_type; + + static void + set_value (value_type& v, + result_callback_type& cb, + void*& context, + bool is_null) + { + if (!is_null) + { + cb = &result_callback; + context = &v; + } + else + v.clear (); + } + + static void + set_image (param_callback_type& cb, + const void*& context, + bool& is_null, + const value_type& v) + { + is_null = false; + cb = ¶m_callback; + context = &v; + } + + static bool + result_callback (void* context, + ub4* position_context, + void* buffer, + ub4 size, + chunk_position); + + static bool + param_callback (const void* context, + ub4* position_context, + const void** buffer, + ub4* size, + chunk_position*, + void* temp_buffer, + ub4 capacity); + }; + + // char[N] (buffer) specialization for BLOBs. + // + template <std::size_t N> + struct default_value_traits<char[N], id_blob> + { + public: + typedef char* value_type; + typedef char query_type[N]; + typedef lob_callback image_type; + + static void + set_value (char* const& v, + result_callback_type& cb, + void*& context, + bool is_null) + { + if (!is_null) + { + cb = &result_callback; + context = v; + } + else + std::memset (v, 0, N); + } + + static void + set_image (param_callback_type& cb, + const void*& context, + bool& is_null, + const char* v) + { + is_null = false; + cb = ¶m_callback; + context = v; + } + + static bool + result_callback (void* context, + ub4* position_context, + void* buffer, + ub4 size, + chunk_position); + + static bool + param_callback (const void* context, + ub4* position_context, + const void** buffer, + ub4* size, + chunk_position*, + void* temp_buffer, + ub4 capacity); + }; + + // unsigned char[N] (buffer) specialization for BLOBs. + // + template <std::size_t N> + struct default_value_traits<unsigned char[N], id_blob> + { + public: + typedef unsigned char* value_type; + typedef unsigned char query_type[N]; + typedef lob_callback image_type; + + static void + set_value (unsigned char* const& v, + result_callback_type& cb, + void*& context, + bool is_null) + { + if (!is_null) + { + cb = &result_callback; + context = v; + } + else + std::memset (v, 0, N); + } + + static void + set_image (param_callback_type& cb, + const void*& context, + bool& is_null, + const unsigned char* v) + { + is_null = false; + cb = ¶m_callback; + context = v; + } + + static bool + result_callback (void* context, + ub4* position_context, + void* buffer, + ub4 size, + chunk_position); + + static bool + param_callback (const void* context, + ub4* position_context, + const void** buffer, + ub4* size, + chunk_position*, + void* temp_buffer, + ub4 capacity); + }; + +#ifdef ODB_CXX11 + // std::array<char, N> (buffer) specialization for BLOBS. + // + template <std::size_t N> + struct default_value_traits<std::array<char, N>, id_blob> + { + public: + typedef std::array<char, N> value_type; + typedef value_type query_type; + typedef lob_callback image_type; + + static void + set_value (value_type& v, + result_callback_type& cb, + void*& context, + bool is_null) + { + if (!is_null) + { + cb = &result_callback; + context = v.data (); + } + else + std::memset (v.data (), 0, N); + } + + static void + set_image (param_callback_type& cb, + const void*& context, + bool& is_null, + const value_type& v) + { + is_null = false; + cb = ¶m_callback; + context = v.data (); + } + + static bool + result_callback (void* context, + ub4* position_context, + void* buffer, + ub4 size, + chunk_position); + + static bool + param_callback (const void* context, + ub4* position_context, + const void** buffer, + ub4* size, + chunk_position*, + void* temp_buffer, + ub4 capacity); + }; + + // std::array<unsigned char, N> (buffer) specialization for BLOBS. + // + template <std::size_t N> + struct default_value_traits<std::array<unsigned char, N>, id_blob> + { + public: + typedef std::array<unsigned char, N> value_type; + typedef value_type query_type; + typedef lob_callback image_type; + + static void + set_value (value_type& v, + result_callback_type& cb, + void*& context, + bool is_null) + { + if (!is_null) + { + cb = &result_callback; + context = v.data (); + } + else + std::memset (v.data (), 0, N); + } + + static void + set_image (param_callback_type& cb, + const void*& context, + bool& is_null, + const value_type& v) + { + is_null = false; + cb = ¶m_callback; + context = v.data (); + } + + static bool + result_callback (void* context, + ub4* position_context, + void* buffer, + ub4 size, + chunk_position); + + static bool + param_callback (const void* context, + ub4* position_context, + const void** buffer, + ub4* size, + chunk_position*, + void* temp_buffer, + ub4 capacity); + }; +#endif + + // + // type_traits + // + + template <typename T> + struct default_type_traits; + + template <typename T> + class type_traits: public default_type_traits<T> + { + }; + + // Integral types. + // + template <> + struct default_type_traits<bool> + { + static const database_type_id db_type_id = id_int32; + }; + + template <> + struct default_type_traits<signed char> + { + static const database_type_id db_type_id = id_int32; + }; + + template <> + struct default_type_traits<unsigned char> + { + static const database_type_id db_type_id = id_int32; + }; + + template <> + struct default_type_traits<short> + { + static const database_type_id db_type_id = id_int32; + }; + + template <> + struct default_type_traits<unsigned short> + { + static const database_type_id db_type_id = id_int32; + }; + + template <> + struct default_type_traits<int> + { + static const database_type_id db_type_id = id_int32; + }; + + template <> + struct default_type_traits<unsigned int> + { + static const database_type_id db_type_id = id_int32; + }; + + template <> + struct default_type_traits<long> + { + static const database_type_id db_type_id = id_big_int; + }; + + template <> + struct default_type_traits<unsigned long> + { + static const database_type_id db_type_id = id_big_int; + }; + + template <> + struct default_type_traits<long long> + { + static const database_type_id db_type_id = id_big_int; + }; + + template <> + struct default_type_traits<unsigned long long> + { + static const database_type_id db_type_id = id_big_int; + }; + + // Float types. + // + template <> + struct default_type_traits<float> + { + static const database_type_id db_type_id = id_float; + }; + + template <> + struct default_type_traits<double> + { + static const database_type_id db_type_id = id_double; + }; + + // String types. + // + template <> + struct default_type_traits<std::string> + { + static const database_type_id db_type_id = id_string; + }; + + template <> + struct default_type_traits<char*> + { + static const database_type_id db_type_id = id_string; + }; + + template <> + struct default_type_traits<const char*> + { + static const database_type_id db_type_id = id_string; + }; + + template <std::size_t N> + struct default_type_traits<char[N]> + { + static const database_type_id db_type_id = id_string; + }; + +#ifdef ODB_CXX11 + template <std::size_t N> + struct default_type_traits<std::array<char, N> > + { + static const database_type_id db_type_id = id_string; + }; +#endif + + template <> + struct default_type_traits<char> + { + static const database_type_id db_type_id = id_string; + }; + + // Binary types. Assume RAW since LOBs cannot be compared in + // Oracle and this is only used in queries. + // + template <std::size_t N> + struct default_type_traits<unsigned char[N]> + { + static const database_type_id db_type_id = id_raw; + }; + + template <> + struct default_type_traits<std::vector<char> > + { + static const database_type_id db_type_id = id_raw; + }; + + template <> + struct default_type_traits<std::vector<unsigned char> > + { + static const database_type_id db_type_id = id_raw; + }; + +#ifdef ODB_CXX11 + template <std::size_t N> + struct default_type_traits<std::array<unsigned char, N> > + { + static const database_type_id db_type_id = id_raw; + }; +#endif + } +} + +#include <odb/oracle/traits.txx> + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_TRAITS_HXX diff --git a/libodb-oracle/odb/oracle/traits.txx b/libodb-oracle/odb/oracle/traits.txx new file mode 100644 index 0000000..22d8f9e --- /dev/null +++ b/libodb-oracle/odb/oracle/traits.txx @@ -0,0 +1,130 @@ +// file : odb/oracle/traits.txx +// license : ODB NCUEL; see accompanying LICENSE file + +namespace odb +{ + namespace oracle + { + // + // default_value_traits<char[N], id_blob> + // + + template <std::size_t N> + bool default_value_traits<char[N], id_blob>:: + result_callback (void* c, ub4* position, void* b, ub4 s, chunk_position) + { + ub4 n (*position + s < N ? s : N - *position); + std::memcpy (static_cast<char*> (c) + *position, b, n); + *position += n; + return true; + } + + template <std::size_t N> + bool default_value_traits<char[N], id_blob>:: + param_callback (const void* c, + ub4*, + const void** b, + ub4* s, + chunk_position* p, + void*, + ub4) + { + *p = chunk_one; + *s = static_cast<ub4> (N); + *b = c; + return true; + } + + // + // default_value_traits<unsigned char[N], id_blob> + // + + template <std::size_t N> + bool default_value_traits<unsigned char[N], id_blob>:: + result_callback (void* c, ub4* position, void* b, ub4 s, chunk_position) + { + ub4 n (*position + s < N ? s : N - *position); + std::memcpy (static_cast<unsigned char*> (c) + *position, b, n); + *position += n; + return true; + } + + template <std::size_t N> + bool default_value_traits<unsigned char[N], id_blob>:: + param_callback (const void* c, + ub4*, + const void** b, + ub4* s, + chunk_position* p, + void*, + ub4) + { + *p = chunk_one; + *s = static_cast<ub4> (N); + *b = c; + return true; + } + +#ifdef ODB_CXX11 + // + // default_value_traits<std::array<char, N>, id_blob> + // + + template <std::size_t N> + bool default_value_traits<std::array<char, N>, id_blob>:: + result_callback (void* c, ub4* position, void* b, ub4 s, chunk_position) + { + ub4 n (*position + s < N ? s : N - *position); + std::memcpy (static_cast<char*> (c) + *position, b, n); + *position += n; + return true; + } + + template <std::size_t N> + bool default_value_traits<std::array<char, N>, id_blob>:: + param_callback (const void* c, + ub4*, + const void** b, + ub4* s, + chunk_position* p, + void*, + ub4) + { + *p = chunk_one; + *s = static_cast<ub4> (N); + *b = c; + return true; + } + + // + // default_value_traits<std::array<unsigned char, N>, id_blob> + // + + template <std::size_t N> + bool default_value_traits<std::array<unsigned char, N>, id_blob>:: + result_callback (void* c, ub4* position, void* b, ub4 s, chunk_position) + { + ub4 n (*position + s < N ? s : N - *position); + std::memcpy (static_cast<unsigned char*> (c) + *position, b, n); + *position += n; + return true; + } + + template <std::size_t N> + bool default_value_traits<std::array<unsigned char, N>, id_blob>:: + param_callback (const void* c, + ub4*, + const void** b, + ub4* s, + chunk_position* p, + void*, + ub4) + { + *p = chunk_one; + *s = static_cast<ub4> (N); + *b = c; + return true; + } +#endif + } +} diff --git a/libodb-oracle/odb/oracle/transaction-impl.cxx b/libodb-oracle/odb/oracle/transaction-impl.cxx new file mode 100644 index 0000000..377e409 --- /dev/null +++ b/libodb-oracle/odb/oracle/transaction-impl.cxx @@ -0,0 +1,160 @@ +// file : odb/oracle/transaction-impl.cxx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <oci.h> + +#include <cassert> + +#include <odb/tracer.hxx> + +#include <odb/oracle/database.hxx> +#include <odb/oracle/connection.hxx> +#include <odb/oracle/error.hxx> +#include <odb/oracle/exceptions.hxx> +#include <odb/oracle/transaction-impl.hxx> + +namespace odb +{ + namespace oracle + { + transaction_impl:: + transaction_impl (database_type& db) + : odb::transaction_impl (db) + { + } + + transaction_impl:: + transaction_impl (connection_ptr c) + : odb::transaction_impl (c->database (), *c), connection_ (c) + { + } + + transaction_impl:: + ~transaction_impl () + { + } + + void transaction_impl:: + start () + { + database_type& db (static_cast<database_type&> (database_)); + + if (connection_ == 0) + { + connection_ = db.connection (); + odb::transaction_impl::connection_ = connection_.get (); + } + + OCISvcCtx* h (connection_->handle ()); + OCIError* err (connection_->error_handle ()); + + // Allocate a transaction handle if there is none associated with + // the connection. + // + OCITrans* t (0); + sword s (OCIAttrGet (h, + OCI_HTYPE_SVCCTX, + reinterpret_cast<void*> (&t), + 0, + OCI_ATTR_TRANS, + err)); + + if (s == OCI_ERROR || s == OCI_INVALID_HANDLE) + translate_error (err, s); + else if (t == 0) + { + auto_handle<OCITrans> auto_t; + + s = OCIHandleAlloc (db.environment (), + reinterpret_cast<void**> (&t), + OCI_HTYPE_TRANS, + 0, + 0); + + if (s != OCI_SUCCESS) + throw invalid_oci_handle (); + + auto_t.reset (t); + + s = OCIAttrSet (h, + OCI_HTYPE_SVCCTX, + reinterpret_cast<void*> (t), + 0, + OCI_ATTR_TRANS, + err); + + if (s == OCI_ERROR || s == OCI_INVALID_HANDLE) + translate_error (err, s); + + auto_t.release (); + } + + { + odb::tracer* t; + if ((t = connection_->tracer ()) || (t = database_.tracer ())) + t->execute (*connection_, "BEGIN"); + } + + // We never use OCITransDetach so the timeout parameter is + // of no consequence. + // + s = OCITransStart (h, + err, + 0, + OCI_TRANS_NEW); + + if (s == OCI_ERROR || s == OCI_INVALID_HANDLE) + translate_error (*connection_, s); + } + + void transaction_impl:: + commit () + { + // Invalidate query results. + // + connection_->invalidate_results (); + + { + odb::tracer* t; + if ((t = connection_->tracer ()) || (t = database_.tracer ())) + t->execute (*connection_, "COMMIT"); + } + + sword s (OCITransCommit (connection_->handle (), + connection_->error_handle (), + OCI_DEFAULT)); + + if (s == OCI_ERROR || s == OCI_INVALID_HANDLE) + translate_error (*connection_, s); + + // Release the connection. + // + connection_.reset (); + } + + void transaction_impl:: + rollback () + { + // Invalidate query results. + // + connection_->invalidate_results (); + + { + odb::tracer* t; + if ((t = connection_->tracer ()) || (t = database_.tracer ())) + t->execute (*connection_, "ROLLBACK"); + } + + sword s (OCITransRollback (connection_->handle (), + connection_->error_handle (), + OCI_DEFAULT)); + + if (s == OCI_ERROR || s == OCI_INVALID_HANDLE) + translate_error (*connection_, s); + + // Release the connection. + // + connection_.reset (); + } + } +} diff --git a/libodb-oracle/odb/oracle/transaction-impl.hxx b/libodb-oracle/odb/oracle/transaction-impl.hxx new file mode 100644 index 0000000..0b7e9bf --- /dev/null +++ b/libodb-oracle/odb/oracle/transaction-impl.hxx @@ -0,0 +1,50 @@ +// file : odb/oracle/transaction-impl.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_TRANSACTION_IMPL_HXX +#define ODB_ORACLE_TRANSACTION_IMPL_HXX + +#include <odb/pre.hxx> + +#include <odb/transaction.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/forward.hxx> +#include <odb/oracle/oracle-fwd.hxx> + +#include <odb/oracle/details/export.hxx> + +namespace odb +{ + namespace oracle + { + class LIBODB_ORACLE_EXPORT transaction_impl: public odb::transaction_impl + { + public: + typedef oracle::database database_type; + typedef oracle::connection connection_type; + + transaction_impl (database_type&); + transaction_impl (connection_ptr); + + virtual + ~transaction_impl (); + + virtual void + start (); + + virtual void + commit (); + + virtual void + rollback (); + + private: + connection_ptr connection_; + }; + } +} + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_TRANSACTION_IMPL_HXX diff --git a/libodb-oracle/odb/oracle/transaction.cxx b/libodb-oracle/odb/oracle/transaction.cxx new file mode 100644 index 0000000..6dff1a0 --- /dev/null +++ b/libodb-oracle/odb/oracle/transaction.cxx @@ -0,0 +1,26 @@ +// file : odb/oracle/transaction.cxx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <cassert> + +#include <odb/oracle/transaction.hxx> + +namespace odb +{ + namespace oracle + { + transaction& transaction:: + current () + { + // While the impl type can be of the concrete type, the transaction + // object can be created as either odb:: or odb::oracle:: type. To + // work around that we are going to hard-cast one to the other + // relying on the fact that they have the same representation and + // no virtual functions. The former is checked in the tests. + // + odb::transaction& b (odb::transaction::current ()); + assert (dynamic_cast<transaction_impl*> (&b.implementation ()) != 0); + return reinterpret_cast<transaction&> (b); + } + } +} diff --git a/libodb-oracle/odb/oracle/transaction.hxx b/libodb-oracle/odb/oracle/transaction.hxx new file mode 100644 index 0000000..8d06b2f --- /dev/null +++ b/libodb-oracle/odb/oracle/transaction.hxx @@ -0,0 +1,88 @@ +// file : odb/oracle/transaction.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_TRANSACTION_HXX +#define ODB_ORACLE_TRANSACTION_HXX + +#include <odb/pre.hxx> + +#include <odb/transaction.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/forward.hxx> +#include <odb/oracle/tracer.hxx> + +#include <odb/oracle/details/export.hxx> + +namespace odb +{ + namespace oracle + { + class transaction_impl; + + class LIBODB_ORACLE_EXPORT transaction: public odb::transaction + { + public: + typedef oracle::database database_type; + typedef oracle::connection connection_type; + + explicit + transaction (transaction_impl*, bool make_current = true); + + transaction (); + + // Return the database this transaction is on. + // + database_type& + database (); + + // Return the underlying database connection for this transaction. + // + connection_type& + connection (); + + connection_type& + connection (odb::database&); + + // Return current transaction or throw if there is no transaction + // in effect. + // + static transaction& + current (); + + // Set the current thread's transaction. + // + static void + current (transaction&); + + // SQL statement tracing. + // + public: + typedef oracle::tracer tracer_type; + + void + tracer (tracer_type& t) + { + odb::transaction::tracer (t); + } + + void + tracer (tracer_type* t) + { + odb::transaction::tracer (t); + } + + using odb::transaction::tracer; + + public: + transaction_impl& + implementation (); + }; + } +} + +#include <odb/oracle/transaction.ixx> + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_TRANSACTION_HXX diff --git a/libodb-oracle/odb/oracle/transaction.ixx b/libodb-oracle/odb/oracle/transaction.ixx new file mode 100644 index 0000000..d75a399 --- /dev/null +++ b/libodb-oracle/odb/oracle/transaction.ixx @@ -0,0 +1,57 @@ +// file : odb/oracle/transaction.ixx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <odb/oracle/database.hxx> +#include <odb/oracle/transaction-impl.hxx> + +namespace odb +{ + namespace oracle + { + inline transaction:: + transaction (transaction_impl* impl, bool make_current) + : odb::transaction (impl, make_current) + { + } + + inline transaction:: + transaction () + : odb::transaction () + { + } + + inline transaction_impl& transaction:: + implementation () + { + // We can use static_cast here since we have an instance of + // oracle::transaction. + // + return static_cast<transaction_impl&> ( + odb::transaction::implementation ()); + } + + inline transaction::database_type& transaction:: + database () + { + return static_cast<database_type&> (odb::transaction::database ()); + } + + inline transaction::connection_type& transaction:: + connection () + { + return static_cast<connection_type&> (odb::transaction::connection ()); + } + + inline transaction::connection_type& transaction:: + connection (odb::database& db) + { + return static_cast<connection_type&> (odb::transaction::connection (db)); + } + + inline void transaction:: + current (transaction& t) + { + odb::transaction::current (t); + } + } +} diff --git a/libodb-oracle/odb/oracle/version.hxx b/libodb-oracle/odb/oracle/version.hxx new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/libodb-oracle/odb/oracle/version.hxx diff --git a/libodb-oracle/odb/oracle/version.hxx.in b/libodb-oracle/odb/oracle/version.hxx.in new file mode 100644 index 0000000..85a5052 --- /dev/null +++ b/libodb-oracle/odb/oracle/version.hxx.in @@ -0,0 +1,61 @@ +// file : odb/oracle/version.hxx.in +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef LIBODB_ORACLE_VERSION // Note: using the version macro itself. + +// New numeric version format is AAAAABBBBBCCCCCDDDE where: +// +// AAAAA - major version number +// BBBBB - minor version number +// CCCCC - bugfix version number +// DDD - alpha / beta (DDD + 500) version number +// E - final (0) / snapshot (1) +// +// When DDDE is not 0, 1 is subtracted from AAAAABBBBBCCCCC. For example: +// +// Version AAAAABBBBBCCCCCDDDE +// +// 0.1.0 0000000001000000000 +// 0.1.2 0000000001000020000 +// 1.2.3 0000100002000030000 +// 2.2.0-a.1 0000200001999990010 +// 3.0.0-b.2 0000299999999995020 +// 2.2.0-a.1.z 0000200001999990011 +// +#define LIBODB_ORACLE_VERSION_FULL $libodb_oracle.version.project_number$ULL +#define LIBODB_ORACLE_VERSION_STR "$libodb_oracle.version.project$" +#define LIBODB_ORACLE_VERSION_ID "$libodb_oracle.version.project_id$" + +#define LIBODB_ORACLE_VERSION_MAJOR $libodb_oracle.version.major$ +#define LIBODB_ORACLE_VERSION_MINOR $libodb_oracle.version.minor$ +#define LIBODB_ORACLE_VERSION_PATCH $libodb_oracle.version.patch$ + +#define LIBODB_ORACLE_PRE_RELEASE $libodb_oracle.version.pre_release$ + +#define LIBODB_ORACLE_SNAPSHOT $libodb_oracle.version.snapshot_sn$ULL +#define LIBODB_ORACLE_SNAPSHOT_ID "$libodb_oracle.version.snapshot_id$" + +#include <odb/version.hxx> + +$libodb.check(LIBODB_VERSION_FULL, LIBODB_SNAPSHOT)$ + + +// Old/deprecated numeric version format is AABBCCDD where: +// +// AA - major version number +// BB - minor version number +// CC - bugfix version number +// DD - alpha / beta (DD + 50) version number +// +// When DD is not 00, 1 is subtracted from AABBCC. For example: +// +// Version AABBCCDD +// 2.0.0 02000000 +// 2.1.0 02010000 +// 2.1.1 02010100 +// 2.2.0.a1 02019901 +// 3.0.0.b2 02999952 +// +#define LIBODB_ORACLE_VERSION 2049977 + +#endif // LIBODB_ORACLE_VERSION diff --git a/libodb-oracle/odb/oracle/view-result.hxx b/libodb-oracle/odb/oracle/view-result.hxx new file mode 100644 index 0000000..15d1cee --- /dev/null +++ b/libodb-oracle/odb/oracle/view-result.hxx @@ -0,0 +1,84 @@ +// file : odb/oracle/view-result.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_VIEW_RESULT_HXX +#define ODB_ORACLE_VIEW_RESULT_HXX + +#include <odb/pre.hxx> + +#include <cstddef> // std::size_t + +#include <odb/schema-version.hxx> +#include <odb/view-result.hxx> + +#include <odb/details/shared-ptr.hxx> + +#include <odb/oracle/version.hxx> +#include <odb/oracle/forward.hxx> // query_base, view_statements +#include <odb/oracle/statement.hxx> +#include <odb/oracle/traits-calls.hxx> + +namespace odb +{ + namespace oracle + { + template <typename T> + class view_result_impl: public odb::view_result_impl<T> + { + public: + typedef odb::view_result_impl<T> base_type; + + typedef typename base_type::view_type view_type; + typedef typename base_type::pointer_type pointer_type; + + typedef view_traits_impl<view_type, id_oracle> view_traits; + typedef typename base_type::pointer_traits pointer_traits; + + typedef view_statements<view_type> statements_type; + + virtual + ~view_result_impl (); + + view_result_impl (const query_base&, + details::shared_ptr<select_statement>, + statements_type&, + const schema_version_migration*); + + virtual void + load (view_type&); + + virtual void + next (); + + virtual void + cache (); + + virtual std::size_t + size (); + + virtual void + invalidate (); + + using base_type::current; + + private: + typedef oracle::change_callback change_callback_type; + + static void + change_callback (void* context); + + private: + details::shared_ptr<select_statement> statement_; + statements_type& statements_; + view_traits_calls<view_type> tc_; + bool use_copy_; + typename view_traits::image_type* image_copy_; + }; + } +} + +#include <odb/oracle/view-result.txx> + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_VIEW_RESULT_HXX diff --git a/libodb-oracle/odb/oracle/view-result.txx b/libodb-oracle/odb/oracle/view-result.txx new file mode 100644 index 0000000..8ae25aa --- /dev/null +++ b/libodb-oracle/odb/oracle/view-result.txx @@ -0,0 +1,148 @@ +// file : odb/oracle/view-result.txx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <odb/callback.hxx> +#include <odb/exceptions.hxx> + +#include <odb/oracle/view-statements.hxx> + +namespace odb +{ + namespace oracle + { + template <typename T> + view_result_impl<T>:: + ~view_result_impl () + { + invalidate (); + } + + template <typename T> + void view_result_impl<T>:: + invalidate () + { + change_callback_type& cc (statements_.image ().change_callback_); + + if (cc.context == this) + { + cc.callback = 0; + cc.context = 0; + } + + delete image_copy_; + image_copy_ = 0; + + if (!this->end_) + { + statement_->free_result (); + this->end_ = true; + } + + statement_.reset (); + } + + template <typename T> + view_result_impl<T>:: + view_result_impl (const query_base&, + details::shared_ptr<select_statement> statement, + statements_type& statements, + const schema_version_migration* svm) + : base_type (statements.connection ()), + statement_ (statement), + statements_ (statements), + tc_ (svm), + use_copy_ (false), + image_copy_ (0) + { + } + + template <typename T> + void view_result_impl<T>:: + load (view_type& view) + { + view_traits::callback (this->db_, view, callback_event::pre_load); + + tc_.init (view, + use_copy_ ? *image_copy_ : statements_.image (), + &this->db_); + + // If we are using a copy, make sure the callback information for + // LOB data also comes from the copy. + // + statement_->stream_result ( + use_copy_ ? &statements_.image () : 0, + use_copy_ ? image_copy_ : 0); + + view_traits::callback (this->db_, view, callback_event::post_load); + } + + template <typename T> + void view_result_impl<T>:: + next () + { + this->current (pointer_type ()); + + typename view_traits::image_type& im (statements_.image ()); + change_callback_type& cc (im.change_callback_); + + if (cc.context == this) + { + cc.callback = 0; + cc.context = 0; + } + + use_copy_ = false; + + if (im.version != statements_.image_version ()) + { + binding& b (statements_.image_binding ()); + tc_.bind (b.bind, im); + statements_.image_version (im.version); + b.version++; + } + + if (statement_->fetch () == select_statement::no_data) + { + statement_->free_result (); + this->end_ = true; + } + else + { + cc.callback = &change_callback; + cc.context = this; + } + } + + template <typename T> + void view_result_impl<T>:: + cache () + { + } + + template <typename T> + std::size_t view_result_impl<T>:: + size () + { + throw result_not_cached (); + } + + template <typename T> + void view_result_impl<T>:: + change_callback (void* c) + { + view_result_impl<T>* r (static_cast<view_result_impl<T>*> (c)); + + typename view_traits::image_type& im (r->statements_.image ()); + + if (r->image_copy_ == 0) + r->image_copy_ = new typename view_traits::image_type (im); + else + *r->image_copy_ = im; + + im.change_callback_.callback = 0; + im.change_callback_.context = 0; + + r->use_copy_ = true; + } + } +} diff --git a/libodb-oracle/odb/oracle/view-statements.hxx b/libodb-oracle/odb/oracle/view-statements.hxx new file mode 100644 index 0000000..272352f --- /dev/null +++ b/libodb-oracle/odb/oracle/view-statements.hxx @@ -0,0 +1,81 @@ +// file : odb/oracle/view-statements.hxx +// license : ODB NCUEL; see accompanying LICENSE file + +#ifndef ODB_ORACLE_VIEW_STATEMENTS_HXX +#define ODB_ORACLE_VIEW_STATEMENTS_HXX + +#include <odb/pre.hxx> + +#include <cstddef> // std::size_t + +#include <odb/forward.hxx> +#include <odb/traits.hxx> + +#include <odb/oracle/oracle-types.hxx> +#include <odb/oracle/version.hxx> +#include <odb/oracle/statement.hxx> +#include <odb/oracle/statements-base.hxx> + +namespace odb +{ + namespace oracle + { + template <typename T> + class view_statements: public statements_base + { + public: + typedef T view_type; + typedef view_traits_impl<view_type, id_oracle> view_traits; + typedef typename view_traits::pointer_type pointer_type; + typedef typename view_traits::image_type image_type; + + public: + view_statements (connection_type&); + + virtual + ~view_statements (); + + // View image. + // + image_type& + image () + { + return image_; + } + + std::size_t + image_version () const + { + return image_version_; + } + + void + image_version (std::size_t v) + { + image_version_ = v; + } + + binding& + image_binding () + { + return image_binding_; + } + + private: + view_statements (const view_statements&); + view_statements& operator= (const view_statements&); + + private: + image_type image_; + std::size_t image_version_; + binding image_binding_; + bind image_bind_[view_traits::column_count]; + }; + } +} + +#include <odb/oracle/view-statements.txx> + +#include <odb/post.hxx> + +#endif // ODB_ORACLE_VIEW_STATEMENTS_HXX diff --git a/libodb-oracle/odb/oracle/view-statements.txx b/libodb-oracle/odb/oracle/view-statements.txx new file mode 100644 index 0000000..3a5d31e --- /dev/null +++ b/libodb-oracle/odb/oracle/view-statements.txx @@ -0,0 +1,30 @@ +// file : odb/oracle/view-statements.txx +// license : ODB NCUEL; see accompanying LICENSE file + +#include <cstring> // std::memset + +namespace odb +{ + namespace oracle + { + template <typename T> + view_statements<T>:: + ~view_statements () + { + } + + template <typename T> + view_statements<T>:: + view_statements (connection_type& conn) + : statements_base (conn), + image_binding_ (image_bind_, view_traits::column_count) + { + image_.version = 0; + image_version_ = 0; + + image_binding_.change_callback = image_.change_callback (); + + std::memset (image_bind_, 0, sizeof (image_bind_)); + } + } +} |