From ec1683514402dfed20b8a08824e2aa1bb017d6e7 Mon Sep 17 00:00:00 2001 From: Constantin Michael Date: Wed, 28 Sep 2011 09:57:42 +0200 Subject: Implement custom bind buffer type identifiers. This allows for the association of a character set with a character data buffer, something that is impossible using only OCI external typecodes. --- odb/oracle/oracle-fwd.hxx | 9 ------ odb/oracle/oracle-types.hxx | 50 +++++++++++++++++++++++------- odb/oracle/statement.cxx | 75 ++++++++++++++++++++++++++++++++++++++------- 3 files changed, 103 insertions(+), 31 deletions(-) diff --git a/odb/oracle/oracle-fwd.hxx b/odb/oracle/oracle-fwd.hxx index a0cd2ed..bc400d1 100644 --- a/odb/oracle/oracle-fwd.hxx +++ b/odb/oracle/oracle-fwd.hxx @@ -24,15 +24,6 @@ typedef struct OCIStmt OCIStmt; typedef struct OCIAuthInfo OCIAuthInfo; typedef struct OCITrans OCITrans; -// Define an external type identifier for the national character types. -// These are used exclusively for identifying national character encoded -// buffers while setting the character set form of an OCIBind or OCIDefine -// handle. They are never passed to the OCI API. -// -extern ub2 SQLT_NCHAR; -extern ub2 SQLT_NVARCHAR2; -extern ub2 SQLT_NCLOB; - #include #endif // ODB_ORACLE_ORACLE_FWD_HXX diff --git a/odb/oracle/oracle-types.hxx b/odb/oracle/oracle-types.hxx index c027e5d..d21b948 100644 --- a/odb/oracle/oracle-types.hxx +++ b/odb/oracle/oracle-types.hxx @@ -58,17 +58,45 @@ namespace odb struct bind { - ub2 type; // The type stored by buffer. This must be an external - // OCI type identifier of the form SQLT_XXX. - void* buffer; // Data buffer pointer. - ub2* size; // The number of bytes in buffer. When parameter - // callbacks are in use, this is interpreted as a ub4* - // indicating the current position. When result - // callbacks are in use, this is interpreted as an - // OCILobLocator*. - ub4 capacity; // The maximum number of bytes that can be stored in - // buffer. - sb2* indicator; // Pointer to an OCI indicator variable. + // 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 variable length char array. + string, // Buffer is a variable length char array. + nstring, // 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. This must be an external + // OCI type identifier of the form SQLT_XXX. + void* buffer; // Data buffer pointer. + ub2* size; // The number of bytes in buffer. When parameter + // callbacks are in use, this is interpreted as a ub4* + // indicating the current position. When result + // callbacks are in use, this is interpreted as an + // OCILobLocator*. + ub4 capacity; // The maximum number of bytes that can be stored in + // buffer. + sb2* indicator; // Pointer to an OCI indicator variable. lob_callback callback; diff --git a/odb/oracle/statement.cxx b/odb/oracle/statement.cxx index 4e35fe5..f923da6 100644 --- a/odb/oracle/statement.cxx +++ b/odb/oracle/statement.cxx @@ -23,6 +23,25 @@ namespace odb { namespace oracle { + // Mapping of bind::buffer_type values to there equivalent external + // OCI typecode identifiers. + // + static ub4 sqlt_lookup[bind::last] = + { + SQLT_INT, + SQLT_UIN, + SQLT_BFLOAT, + SQLT_BDOUBLE, + SQLT_VNU, + SQLT_DAT, + SQLT_TIMESTAMP, + SQLT_CHR, + SQLT_CHR, + SQLT_BLOB, + SQLT_CLOB, + SQLT_CLOB + }; + static sb4 param_callback_proxy (void* context, OCIBind*, @@ -126,8 +145,7 @@ namespace odb // version is unable to implicitly convert the NUMBER binary data // to the relevant type. // - assert ((b->type != SQLT_INT && b->type != SQLT_UIN) || - b->capacity <= 4); + assert (b->type != bind::integer || b->capacity <= 4); #endif OCIBind* h (0); @@ -137,7 +155,7 @@ namespace odb o, b->buffer, static_cast (b->capacity), - b->type, + sqlt_lookup[b->type], b->indicator, b->size, 0, @@ -149,6 +167,21 @@ namespace odb if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) translate_error (err, r); + 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 (b->callback.param != 0) { r = OCIBindDynamic (h, err, b, ¶m_callback_proxy, 0, 0); @@ -168,16 +201,19 @@ namespace odb for (size_t i (1); i <= c; ++i, ++b) { - if (b->type == SQLT_BLOB || b->type == SQLT_CLOB) + OCIDefine* h (0); + + if (b->type == bind::blob || + b->type == bind::clob || + b->type == bind::nclob) { - OCIDefine* h (0); sword r (OCIDefineByPos (stmt_, &h, err, i, reinterpret_cast (b->size), sizeof (OCILobLocator*), - b->type, + sqlt_lookup[b->type], b->indicator, 0, 0, @@ -215,18 +251,17 @@ namespace odb // version is unable to implicitly convert the NUMBER binary data // to the relevant type. // - assert ((b->type != SQLT_INT && b->type != SQLT_UIN) || + assert ((b->type != bind::integer || b->type != bind::uinteger) && b->capacity <= 4); #endif - OCIDefine* h (0); sword r (OCIDefineByPos (stmt_, &h, err, i, b->buffer, static_cast (b->capacity), - b->type, + sqlt_lookup[b->type], b->indicator, b->size, 0, @@ -234,6 +269,21 @@ namespace odb 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_BIND, + &form, + 0, + OCI_ATTR_CHARSET_FORM, + err); + + if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) + translate_error (err, r); + } } } } @@ -248,7 +298,9 @@ namespace odb // Only stream if the bind specifies a LOB type, and the LOB value is // not NULL, and a result callback has been provided. // - if ((b->type == SQLT_BLOB || b->type == SQLT_CLOB) && + if ((b->type == bind::blob || + b->type == bind::clob || + b->type == bind::nclob) && *b->indicator != -1 && b->callback.result != 0) { @@ -266,6 +318,7 @@ namespace odb // return OCI_SUCCESS. // ub8 read (0); + ub1 cs_form (b->type == bind::nclob ? SQLCS_NCHAR : SQLCS_IMPLICIT); sword r; do @@ -282,7 +335,7 @@ namespace odb 0, 0, 0, - SQLCS_IMPLICIT); + cs_form); if (r == OCI_ERROR || r == OCI_INVALID_HANDLE) translate_error (err, r); -- cgit v1.1