From 655fc5c0827d56b6eaa86f80c904d9a607530fcb Mon Sep 17 00:00:00 2001 From: Constantin Michael Date: Mon, 7 Nov 2011 15:02:14 +0200 Subject: Implement support for Oracle temporal types --- odb/oracle/auto-descriptor.cxx | 16 +- odb/oracle/auto-descriptor.hxx | 63 +++-- odb/oracle/details/date.hxx | 61 +++++ odb/oracle/makefile | 1 + odb/oracle/oracle-fwd.hxx | 5 + odb/oracle/oracle-types.cxx | 135 ++++++++++ odb/oracle/oracle-types.hxx | 213 +++++++++++++++- odb/oracle/statement.cxx | 562 ++++++++++++++++++++++++++++------------- odb/oracle/traits.hxx | 28 +- 9 files changed, 865 insertions(+), 219 deletions(-) create mode 100644 odb/oracle/details/date.hxx create mode 100644 odb/oracle/oracle-types.cxx diff --git a/odb/oracle/auto-descriptor.cxx b/odb/oracle/auto-descriptor.cxx index 522d1e9..b332c16 100644 --- a/odb/oracle/auto-descriptor.cxx +++ b/odb/oracle/auto-descriptor.cxx @@ -11,13 +11,19 @@ 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, ub4 type) + oci_descriptor_free (void* d, descriptor_type type) { - OCIDescriptorFree (d, type); + OCIDescriptorFree (d, oci_descriptor_types[type - 1]); } - - const ub4 descriptor_type_traits::dtype = OCI_DTYPE_PARAM; - const ub4 descriptor_type_traits::dtype = OCI_DTYPE_LOB; } } diff --git a/odb/oracle/auto-descriptor.hxx b/odb/oracle/auto-descriptor.hxx index f55925d..6bc97d6 100644 --- a/odb/oracle/auto-descriptor.hxx +++ b/odb/oracle/auto-descriptor.hxx @@ -17,41 +17,64 @@ namespace odb { namespace oracle { + enum descriptor_type + { + dt_default = 0, + dt_param, + dt_lob, + dt_timestamp, + dt_interval_ym, + dt_interval_ds + }; + + LIBODB_ORACLE_EXPORT void + oci_descriptor_free (void* descriptor, descriptor_type type); + // // descriptor_type_traits // template - struct descriptor_type_traits; + struct default_descriptor_type_traits; template <> - struct descriptor_type_traits - { static const ub4 dtype; }; + struct default_descriptor_type_traits + { static const descriptor_type dtype = dt_param; }; template <> - struct descriptor_type_traits - { static const ub4 dtype; }; - + struct default_descriptor_type_traits + { static const descriptor_type dtype = dt_lob; }; // - // descriptor_traits + // auto_descriptor_base // - LIBODB_ORACLE_EXPORT void - oci_descriptor_free (void* descriptor, ub4 type); + template + struct auto_descriptor_base + { + static void + release (D* d) + { + oci_descriptor_free (d, Type); + } + }; template - struct descriptor_traits + struct auto_descriptor_base { static void release (D* d) { - oci_descriptor_free (d, descriptor_type_traits::dtype); + oci_descriptor_free (d, default_descriptor_type_traits::dtype); } }; - template - class auto_descriptor + // + // auto_descriptor + // + + template + class auto_descriptor: auto_descriptor_base { public: auto_descriptor (D* d = 0) @@ -62,25 +85,31 @@ namespace odb ~auto_descriptor () { if (d_ != 0) - descriptor_traits::release (d_); + release (d_); } - operator D* () + operator D* () const { return d_; } - D* + D*& get () { return d_; } + D* + get () const + { + return d_; + } + void reset (D* d = 0) { if (d_ != 0) - descriptor_traits::release (d_); + release (d_); d_ = d; } diff --git a/odb/oracle/details/date.hxx b/odb/oracle/details/date.hxx new file mode 100644 index 0000000..47137f6 --- /dev/null +++ b/odb/oracle/details/date.hxx @@ -0,0 +1,61 @@ +// file : odb/oracle/details/date.hxx +// author : Constantin Michael +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// 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 (year / 100 + 100); + b[1] = static_cast (year % 100 + 100); + b[2] = static_cast (month); + b[3] = static_cast (day); + b[4] = static_cast (hour + 1); + b[5] = static_cast (minute + 1); + b[6] = static_cast (second + 1); + } + + inline void + get_date (short& year, + unsigned char& month, + unsigned char& day, + unsigned char& hour, + unsigned char& minute, + unsigned char& second, + const char* b) + { + const unsigned char* ub (reinterpret_cast (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/odb/oracle/makefile b/odb/oracle/makefile index cf1db15..f173b95 100644 --- a/odb/oracle/makefile +++ b/odb/oracle/makefile @@ -14,6 +14,7 @@ database.cxx \ error.cxx \ exceptions.cxx \ object-statements.cxx \ +oracle-types.cxx \ query.cxx \ query-const-expr.cxx \ statement.cxx \ diff --git a/odb/oracle/oracle-fwd.hxx b/odb/oracle/oracle-fwd.hxx index 813414c..dee5736 100644 --- a/odb/oracle/oracle-fwd.hxx +++ b/odb/oracle/oracle-fwd.hxx @@ -12,6 +12,9 @@ // 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; @@ -26,6 +29,8 @@ typedef struct OCITrans OCITrans; typedef struct OCIParam OCIParam; typedef struct OCILobLocator OCILobLocator; +typedef struct OCIDateTime OCIDateTime; +typedef struct OCIInterval OCIInterval; #include diff --git a/odb/oracle/oracle-types.cxx b/odb/oracle/oracle-types.cxx new file mode 100644 index 0000000..17ae479 --- /dev/null +++ b/odb/oracle/oracle-types.cxx @@ -0,0 +1,135 @@ +// file : odb/oracle/oracle-types.hxx +// author : Constantin Michael +// copyright : Copyright (c) 2005-2011 Code Synthesis Tools CC +// license : ODB NCUEL; see accompanying LICENSE file + +#include + +#include + +#include +#include + +namespace odb +{ + namespace oracle + { + void datetime:: + get () const + { + assert (descriptor.get () != 0); + + sword r (OCIDateTimeGetDate (descriptor.environment, + descriptor.error, + descriptor, + &fields.year, + &fields.month, + &fields.day)); + + if (r != OCI_SUCCESS) + translate_error (descriptor.error, r); + + r = OCIDateTimeGetTime (descriptor.environment, + descriptor.error, + descriptor, + &fields.hour, + &fields.minute, + &fields.second, + &fields.nanosecond); + + if (r != OCI_SUCCESS) + translate_error (descriptor.error, r); + } + + void datetime:: + set () + { + if (descriptor.get () != 0) + { + sword r (OCIDateTimeConstruct (descriptor.environment, + descriptor.error, + descriptor, + fields.year, + fields.month, + fields.day, + fields.hour, + fields.minute, + fields.second, + fields.nanosecond, + 0, + 0)); + + if (r != OCI_SUCCESS) + translate_error (descriptor.error, r); + } + } + + void interval_ym:: + get () const + { + assert (descriptor.get () != 0); + + sword r (OCIIntervalGetYearMonth (descriptor.environment, + descriptor.error, + &fields.year, + &fields.month, + descriptor)); + + if (r != OCI_SUCCESS) + translate_error (descriptor.error, r); + } + + void interval_ym:: + set () + { + if (descriptor.get () != 0) + { + sword r (OCIIntervalSetYearMonth (descriptor.environment, + descriptor.error, + fields.year, + fields.month, + descriptor)); + + if (r != OCI_SUCCESS) + translate_error (descriptor.error, r); + } + } + + void interval_ds:: + get () const + { + assert (descriptor.get () != 0); + + sword r (OCIIntervalGetDaySecond (descriptor.environment, + descriptor.error, + &fields.day, + &fields.hour, + &fields.minute, + &fields.second, + &fields.nanosecond, + descriptor)); + + if (r != OCI_SUCCESS) + translate_error (descriptor.error, r); + } + + void interval_ds:: + set () + { + if (descriptor.get () != 0) + { + sword r (OCIIntervalSetDaySecond (descriptor.environment, + descriptor.error, + fields.day, + fields.hour, + fields.minute, + fields.second, + fields.nanosecond, + descriptor)); + + if (r != OCI_SUCCESS) + translate_error (descriptor.error, r); + } + } + } +} diff --git a/odb/oracle/oracle-types.hxx b/odb/oracle/oracle-types.hxx index 3327c57..f12caeb 100644 --- a/odb/oracle/oracle-types.hxx +++ b/odb/oracle/oracle-types.hxx @@ -90,7 +90,9 @@ namespace odb 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 variable length 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. @@ -132,22 +134,28 @@ namespace odb void* context; }; - // The LOB specialization of auto_descriptor allows for transparent - // transferal of LOB descriptors between auto_descriptor instances. This + // + // These specialized auto_descriptor classes allows for transparent + // transferal of descriptors between auto_descriptor instances. This // simplifies the implementation of a private copy of the shared image - // associated with queries. + // associated with queries. The specializations for OCIDateTime and + // OCIInterval also wrap OCI handles that are required for manipulation + // of the descriptor data. // - class LIBODB_ORACLE_EXPORT lob_auto_descriptor - : auto_descriptor + + class LIBODB_ORACLE_EXPORT lob_auto_descriptor: + public auto_descriptor { + typedef auto_descriptor base; + public: lob_auto_descriptor (OCILobLocator* l = 0) - : auto_descriptor (l) + : base (l) { } lob_auto_descriptor (lob_auto_descriptor& x) - : auto_descriptor (x.d_) + : base (x.d_) { x.d_ = 0; } @@ -162,6 +170,195 @@ namespace odb return *this; } }; + + class LIBODB_ORACLE_EXPORT datetime_auto_descriptor: + public auto_descriptor + { + typedef auto_descriptor base; + + public: + datetime_auto_descriptor (OCIDateTime* d = 0): + base (d), + environment (0), + error (0) + { + } + + datetime_auto_descriptor (datetime_auto_descriptor& x): + base (x.d_), + environment (x.environment), + error (x.error) + { + x.d_ = 0; + } + + datetime_auto_descriptor& + operator= (datetime_auto_descriptor& x) + { + OCIDateTime* l (x.d_); + x.d_ = 0; + reset (l); + + environment = x.environment; + error = x.error; + + return *this; + } + + OCIEnv* environment; + OCIError* error; + }; + + class LIBODB_ORACLE_EXPORT interval_ym_auto_descriptor: + public auto_descriptor + { + typedef auto_descriptor base; + + public: + interval_ym_auto_descriptor (OCIInterval* d = 0): + base (d), + environment (0), + error (0) + { + } + + interval_ym_auto_descriptor (interval_ym_auto_descriptor& x): + base (x.d_), + environment (x.environment), + error (x.error) + { + x.d_ = 0; + } + + interval_ym_auto_descriptor& + operator= (interval_ym_auto_descriptor& x) + { + OCIInterval* l (x.d_); + x.d_ = 0; + reset (l); + + environment = x.environment; + error = x.error; + + return *this; + } + + OCIEnv* environment; + OCIError* error; + }; + + class LIBODB_ORACLE_EXPORT interval_ds_auto_descriptor: + public auto_descriptor + { + typedef auto_descriptor base; + + public: + interval_ds_auto_descriptor (OCIInterval* d = 0): + base (d), + environment (0), + error (0) + { + } + + interval_ds_auto_descriptor (interval_ds_auto_descriptor& x): + base (x.d_), + environment (x.environment), + error (x.error) + { + x.d_ = 0; + } + + interval_ds_auto_descriptor& + operator= (interval_ds_auto_descriptor& x) + { + OCIInterval* l (x.d_); + x.d_ = 0; + reset (l); + + environment = x.environment; + error = x.error; + + return *this; + } + + OCIEnv* environment; + OCIError* error; + }; + + // + // The OCIDateTime and OCIInterval APIs require that an environment and + // error handle be passed any function that manipulates an OCIDateTime or + // OCIInterval descriptor. It is however impossible to obtain these handles + // the first time any temporal data image is 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 all the + // required data is available. A symmetric get interface is provided for + // consistency. + // + + struct LIBODB_ORACLE_EXPORT datetime + { + struct fields_type + { + sb2 year; + ub1 month; + ub1 day; + ub1 hour; + ub1 minute; + ub1 second; + ub4 nanosecond; + }; + + mutable fields_type fields; + datetime_auto_descriptor descriptor; + + void + get () const; + + void + set (); + + }; + + struct LIBODB_ORACLE_EXPORT interval_ym + { + struct fields_type + { + sb4 year; + sb4 month; + }; + + mutable fields_type fields; + interval_ym_auto_descriptor descriptor; + + void + get () const; + + void + set (); + }; + + struct LIBODB_ORACLE_EXPORT interval_ds + { + struct fields_type + { + sb4 day; + sb4 hour; + sb4 minute; + sb4 second; + sb4 nanosecond; + }; + + mutable fields_type fields; + interval_ds_auto_descriptor descriptor; + + void + get () const; + + void + set (); + }; } } diff --git a/odb/oracle/statement.cxx b/odb/oracle/statement.cxx index c1747a3..efbaf72 100644 --- a/odb/oracle/statement.cxx +++ b/odb/oracle/statement.cxx @@ -27,41 +27,45 @@ namespace odb // Mapping of bind::buffer_type values for parameter buffers to their // equivalent external OCI typecode identifiers. // - static ub4 param_sqlt_lookup[bind::last] = + 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_CHR, // bind::string - SQLT_CHR, // bind::nstring - SQLT_BIN, // bind::raw - SQLT_LBI, // bind::blob - SQLT_LNG, // bind::clob - SQLT_LNG // bind::nclob + 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 ub4 result_sqlt_lookup[bind::last] = + 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_CHR, // bind::string - SQLT_CHR, // bind::nstring - SQLT_BIN, // bind::raw - SQLT_BLOB, // bind::blob - SQLT_CLOB, // bind::clob - SQLT_CLOB // bind::nclob + 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 }; static sb4 @@ -156,41 +160,139 @@ namespace odb bind_param (bind* b, size_t n) { OCIError* err (conn_.error_handle ()); + OCIEnv* env (conn_.database ().environment ()); // The parameter position in OCIBindByPos is specified as a 1-based // index. // n++; + for (ub4 i (1); i < n; ++i, ++b) { -#if OCI_MAJOR_VERSION < 11 || \ + void* value (0); + bool callback (b->callback != 0); + + switch (b->type) + { + case bind::timestamp: + { + datetime* dt (static_cast (b->buffer)); + + if (dt->descriptor.get () == 0) + { + void* d (0); + sword r (OCIDescriptorAlloc (env, + &d, + OCI_DTYPE_TIMESTAMP, + 0, + 0)); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + dt->descriptor.reset (static_cast (d)); + dt->descriptor.environment = env; + dt->descriptor.error = err; + dt->set (); + } + + value = &dt->descriptor.get (); + break; + } + case bind::interval_ym: + { + interval_ym* iym (static_cast (b->buffer)); + + if (iym->descriptor.get () == 0) + { + void* d (0); + sword r (OCIDescriptorAlloc (env, + &d, + OCI_DTYPE_INTERVAL_YM, + 0, + 0)); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + iym->descriptor.reset (static_cast (d)); + iym->descriptor.environment = env; + iym->descriptor.error = err; + iym->set (); + } + + value = &iym->descriptor.get (); + break; + } + case bind::interval_ds: + { + interval_ds* ids (static_cast (b->buffer)); + + if (ids->descriptor.get () == 0) + { + void* d (0); + sword r (OCIDescriptorAlloc (env, + &d, + OCI_DTYPE_INTERVAL_DS, + 0, + 0)); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + ids->descriptor.reset (static_cast (d)); + ids->descriptor.environment = env; + ids->descriptor.error = err; + ids->set (); + } + + value = &ids->descriptor.get (); + break; + } + case bind::blob: + case bind::clob: + case bind::nclob: + { + details::buffer& lob_buffer (conn_.lob_buffer ()); + + if (lob_buffer.capacity () == 0) + lob_buffer.capacity (4096); + + b->buffer = &lob_buffer; + + 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); + // 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 + if (!callback) + value = b->buffer; + + break; + } + } - bool callback (b->callback != 0); OCIBind* h (0); sword r (OCIBindByPos (stmt_, &h, err, i, - callback ? 0 : b->buffer, - // When parameter callbacks are in use, set the - // allowable data length to the maximum - // possible. - // + value, callback - ? std::numeric_limits::max () - : static_cast (b->capacity), + ? std::numeric_limits::max () + : static_cast (b->capacity), param_sqlt_lookup[b->type], - callback ? 0 : b->indicator, - callback ? 0 : b->size, + b->indicator, + b->size, 0, 0, 0, @@ -199,6 +301,8 @@ namespace odb 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); @@ -216,13 +320,6 @@ namespace odb if (callback) { - details::buffer& lob_buffer (conn_.lob_buffer ()); - - if (lob_buffer.capacity () == 0) - lob_buffer.capacity (4096); - - b->buffer = &lob_buffer; - r = OCIBindDynamic (h, err, b, ¶m_callback_proxy, 0, 0); if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) @@ -237,56 +334,146 @@ namespace odb ODB_POTENTIALLY_UNUSED (p); OCIError* err (conn_.error_handle ()); + OCIEnv* env (conn_.database ().environment ()); for (size_t i (1); i <= c; ++i, ++b) { - OCIDefine* h (0); + void* value (0); + ub2* size (0); - if (b->type == bind::blob || - b->type == bind::clob || - b->type == bind::nclob) + switch (b->type) { - // When binding a LOB result, the bind::buffer member is - // reinterpreted as a pointer to an auto_descriptor. - // If the descriptor has not yet been allocated, it is allocated now. - // - auto_descriptor* lob ( - reinterpret_cast*> (b->buffer)); - - if (lob->get () == 0) + case bind::timestamp: + { + datetime* dt (static_cast (b->buffer)); + + if (dt->descriptor.get () == 0) + { + void* d (0); + sword r (OCIDescriptorAlloc (env, + &d, + OCI_DTYPE_TIMESTAMP, + 0, + 0)); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + dt->descriptor.reset (static_cast (d)); + dt->descriptor.environment = env; + dt->descriptor.error = err; + } + + value = &dt->descriptor.get (); + break; + } + case bind::interval_ym: { - OCILobLocator* h (0); + interval_ym* iym (static_cast (b->buffer)); + + if (iym->descriptor.get () == 0) + { + void* d (0); + sword r (OCIDescriptorAlloc (env, + &d, + OCI_DTYPE_INTERVAL_YM, + 0, + 0)); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + iym->descriptor.reset (static_cast (d)); + iym->descriptor.environment = env; + iym->descriptor.error = err; + } + + value = &iym->descriptor.get (); + break; + } + case bind::interval_ds: + { + interval_ds* ids (static_cast (b->buffer)); + + if (ids->descriptor.get () == 0) + { + void* d (0); + sword r (OCIDescriptorAlloc (env, + &d, + OCI_DTYPE_INTERVAL_DS, + 0, + 0)); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + ids->descriptor.reset (static_cast (d)); + ids->descriptor.environment = env; + ids->descriptor.error = err; + } + + value = &ids->descriptor.get (); + break; + } + case bind::blob: + case bind::clob: + case bind::nclob: + { + auto_descriptor* lob ( + static_cast*> (b->buffer)); + + if (lob->get () == 0) + { + void* d (0); + sword r (OCIDescriptorAlloc (env, &d, OCI_DTYPE_LOB, 0, 0)); - sword r (OCIDescriptorAlloc (conn_.database ().environment (), - reinterpret_cast (&h), - OCI_DTYPE_LOB, - 0, - 0)); + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); - // OCIDescriptorAlloc will return OCI_SUCCESS on success, or - // OCI_INVALID_HANDLE on an out-of-memory condition. + lob->reset (static_cast (d)); + } + + value = &lob->get (); + 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. // - if (r != OCI_SUCCESS) - throw invalid_oci_handle (); + assert ((b->type != bind::integer && b->type != bind::uinteger) || + b->capacity <= 4); +#endif + value = b->buffer; + size = b->size; - lob->reset (h); + break; } + } - sword r (OCIDefineByPos (stmt_, - &h, - err, - i, - lob, - sizeof (OCILobLocator*), - result_sqlt_lookup[b->type], - b->indicator, - 0, - 0, - OCI_DEFAULT)); + OCIDefine* h (0); + sword r (OCIDefineByPos (stmt_, + &h, + err, + i, + value, + static_cast (b->capacity), + result_sqlt_lookup[b->type], + b->indicator, + size, + 0, + OCI_DEFAULT)); - if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) - translate_error (err, r); + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + if (b->type == bind::blob || + b->type == bind::clob || + b->type == bind::nclob) + { // The OCIDefine handle is stored in the size member of the bind in // case the LOB parameter is rebound. If rebinding is necessary, the // same OCIDefine handle is used. @@ -316,47 +503,19 @@ namespace odb } #endif } - else + else if (b->type == bind::nstring) { -#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 + ub1 form (SQLCS_NCHAR); - sword r (OCIDefineByPos (stmt_, - &h, - err, - i, - b->buffer, - static_cast (b->capacity), - result_sqlt_lookup[b->type], - b->indicator, - b->size, - 0, - OCI_DEFAULT)); + 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); - - 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); - } } } } @@ -366,49 +525,119 @@ namespace odb { ODB_POTENTIALLY_UNUSED (p); - OCIError* err (conn_.error_handle ()); + OCIEnv* env (conn_.database ().environment ()); for (size_t i (1); i <= c; ++i, ++b) { - if (!(b->type == bind::blob || - b->type == bind::clob || - b->type == bind::nclob)) - continue; - - // When binding a LOB result, the bind::buffer member is - // reinterpreted as a pointer to an auto_descriptor. - // If the descriptor has been reset, it is re-allocated now. - // - auto_descriptor* lob ( - reinterpret_cast*> (b->buffer)); + void* value (0); - if (lob->get () == 0) + switch (b->type) { - OCILobLocator* h (0); + case bind::timestamp: + { + datetime* dt (static_cast (b->buffer)); - sword r (OCIDescriptorAlloc (conn_.database ().environment (), - reinterpret_cast (&h), - OCI_DTYPE_LOB, - 0, - 0)); + if (dt->descriptor.get () == 0) + { + void* d (0); + sword r (OCIDescriptorAlloc (env, + &d, + OCI_DTYPE_TIMESTAMP, + 0, + 0)); - // OCIDescriptorAlloc will return OCI_SUCCESS on success, or - // OCI_INVALID_HANDLE on an out-of-memory condition. - // - if (r != OCI_SUCCESS) - throw invalid_oci_handle (); + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); - lob->reset (h); + dt->descriptor.reset (static_cast (d)); + } + + value = &dt->descriptor.get (); + break; + } + case bind::interval_ym: + { + interval_ym* iym (static_cast (b->buffer)); + + if (iym->descriptor.get () == 0) + { + void* d (0); + sword r (OCIDescriptorAlloc (env, + &d, + OCI_DTYPE_INTERVAL_YM, + 0, + 0)); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + iym->descriptor.reset (static_cast (d)); + } + + value = &iym->descriptor.get (); + break; + } + case bind::interval_ds: + { + interval_ds* ids (static_cast (b->buffer)); + + if (ids->descriptor.get () == 0) + { + void* d (0); + sword r (OCIDescriptorAlloc (env, + &d, + OCI_DTYPE_INTERVAL_DS, + 0, + 0)); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + ids->descriptor.reset (static_cast (d)); + } + + value = &ids->descriptor.get (); + break; + } + case bind::blob: + case bind::clob: + case bind::nclob: + { + auto_descriptor* lob ( + reinterpret_cast*> (b->buffer)); + + if (lob->get () == 0) + { + void* d (0); + sword r (OCIDescriptorAlloc (env, &d, OCI_DTYPE_LOB, 0, 0)); + + if (r != OCI_SUCCESS) + throw invalid_oci_handle (); + + lob->reset (static_cast (d)); + } + + value = &lob->get (); + break; + } + default: + { + continue; + } } + // The bind::size member of bind instances associated with LOB and + // TIMESTAMP type is interpreted as the OCIDefine* returned by the + // initial call to OCIDefineByPos when binding for the first time. + // OCIDefine* h (reinterpret_cast (b->size)); sword r (OCIDefineByPos (stmt_, &h, - err, + conn_.error_handle (), i, - lob, - sizeof (OCILobLocator*), + value, + static_cast (b->capacity), result_sqlt_lookup[b->type], b->indicator, 0, @@ -416,34 +645,7 @@ namespace odb 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. - // - // Note that even though we are re-binding the same handle, we still - // have to reset this attribute. Failing to do so will result in the - // mysterious ORA-03106 fatal two-task communication protocol error. - // -#if (OCI_MAJOR_VERSION == 11 && OCI_MINOR_VERSION >= 1) \ - || OCI_MAJOR_VERSION > 11 - if (p != 0) - { - ub4 n (static_cast (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); - } -#endif + translate_error (conn_.error_handle (), r); } } diff --git a/odb/oracle/traits.hxx b/odb/oracle/traits.hxx index e8c3ec2..2ad3054 100644 --- a/odb/oracle/traits.hxx +++ b/odb/oracle/traits.hxx @@ -47,6 +47,8 @@ namespace odb id_date, id_timestamp, + id_interval_ym, + id_interval_ds, id_string, id_nstring, @@ -181,11 +183,19 @@ namespace odb template struct image_traits { - // Image is a buffer containing the native OCI TIMESTAMP representation. - // This buffer has varying length, depending on the microsecond - // precision of the TIMESTAMP value. - // - typedef char* image_type; + typedef datetime image_type; + }; + + template + struct image_traits + { + typedef interval_ym image_type; + }; + + template + struct image_traits + { + typedef interval_ds image_type; }; template @@ -266,7 +276,7 @@ namespace odb vtraits::set_image (i, is_null, wtraits::get_ref (v)); } - // big_int, big_float, timestamp, string, nstring, raw. + // big_int, big_float, string, nstring, raw. // static void set_value (W& v, const char* i, std::size_t n, bool is_null) @@ -274,7 +284,7 @@ namespace odb vtraits::set_value (wtraits::set_ref (v), i, n, is_null); } - // timestamp, string, nstring, raw. + // string, nstring, raw. // static void set_image (char* i, @@ -342,7 +352,7 @@ namespace odb vtraits::set_image (i, is_null, wtraits::get_ref (v)); } - // big_int, big_float, timestamp, string, nstring, raw. + // big_int, big_float, string, nstring, raw. // static void set_value (W& v, const char* i, std::size_t n, bool is_null) @@ -353,7 +363,7 @@ namespace odb vtraits::set_value (wtraits::set_ref (v), i, n, is_null); } - // timestamp, string, nstring, raw. + // string, nstring, raw. // static void set_image (char* i, -- cgit v1.1