aboutsummaryrefslogtreecommitdiff
path: root/odb/oracle
diff options
context:
space:
mode:
authorConstantin Michael <constantin@codesynthesis.com>2011-09-16 09:32:57 +0200
committerConstantin Michael <constantin@codesynthesis.com>2011-09-16 10:03:12 +0200
commitee99d2c795d9b6afc263f7ae5c15a62abd258e4e (patch)
tree896c28be19cd8c1bf2682edcd83258e8f9ca9c78 /odb/oracle
parente601ea00bf044fd1ac48db859c2c8491f45fda3f (diff)
Corrections to LOB parameter and results callback implementation
Diffstat (limited to 'odb/oracle')
-rw-r--r--odb/oracle/oracle-types.hxx74
-rw-r--r--odb/oracle/statement.cxx233
-rw-r--r--odb/oracle/statement.hxx22
3 files changed, 161 insertions, 168 deletions
diff --git a/odb/oracle/oracle-types.hxx b/odb/oracle/oracle-types.hxx
index 4d347d0..45485c2 100644
--- a/odb/oracle/oracle-types.hxx
+++ b/odb/oracle/oracle-types.hxx
@@ -15,6 +15,41 @@ namespace odb
{
namespace oracle
{
+ enum chunk_position
+ {
+ one_chunk,
+ first_chunk,
+ next_chunk,
+ last_chunk
+ };
+
+ // 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) (
+ void* context, // [in] The user context.
+ ub4* position_context, // [in] A positional context. A callback is free
+ // to use this to track positional information.
+ 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 length 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.
+ void* buffer, // [in] A buffer containing the result data.
+ ub4 size, // [in] The result data length in bytes.
+ chunk_position); // [in] The position of this chunk.
+
struct bind
{
ub2 type; // The type stored by buffer. This must be an external
@@ -22,53 +57,20 @@ namespace odb
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 'type'
- // specifies a LOB type, this is interpreted as an
+ // 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.
- enum piece
- {
- whole,
- first,
- next,
- last
- };
-
- // Callback function signature used to specify LOB parameters that are
- // passed to the database.
- //
- typedef bool (*param_callback_type) (
- void* context, // [in/out] The user context.
- void** buffer, // [out] On return, a pointer to a buffer containing
- // parameter data.
- ub4* size, // [out] The parameter data length in bytes.
- ub4* position, // [in/out] A position context. This value remains
- // unchanged between callback invocations.
- piece*, // [out] The piece type for this segment of data.
- void* temp_buffer, // [in] A temporary buffer that may be used if
- // required. The 'buffer' parameter should specify
- // this buffer on return if it is used.
- ub4 capacity); // [in] The temporary buffer length in bytes.
-
- // Callback function signature used to specify LOB values returned from
- // the database.
- //
- typedef bool (*result_callback_type) (
- void* context, // [in/out] The user context.
- void* buffer, // [in] A buffer containing the result data.
- ub4 size, // [in] The result data length in bytes.
- piece); // [in] The piece type for this segment of data.
-
union
{
param_callback_type param;
result_callback_type result;
} callback;
- // This pointer is provided to the user through the 'context' parameter
+ // This pointer is provided to the user through the context argument
// in both parameter and result callback functions.
//
void* callback_context;
diff --git a/odb/oracle/statement.cxx b/odb/oracle/statement.cxx
index 32b015a..70859a4 100644
--- a/odb/oracle/statement.cxx
+++ b/odb/oracle/statement.cxx
@@ -13,8 +13,9 @@
#include <odb/oracle/database.hxx>
#include <odb/oracle/statement.hxx>
#include <odb/oracle/connection.hxx>
-#include <odb/oracle/error.hxx>
#include <odb/oracle/auto-descriptor.hxx>
+#include <odb/oracle/error.hxx>
+#include <odb/oracle/exceptions.hxx>
using namespace std;
@@ -28,52 +29,48 @@ namespace odb
ub4, // iteration
ub4, // index
void** buffer,
- ub4* len,
+ ub4* size,
ub1* piece,
void** indicator)
{
bind& b (*static_cast<bind*> (context));
- // Only callback to user if the parameter is not NULL.
+ // Only call callback if the parameter is not NULL.
//
if (*b.indicator != -1)
{
- bind::piece p;
-
+ chunk_position pos;
if (!(*b.callback.param) (&b.callback_context,
- buffer,
- len,
reinterpret_cast<ub4*> (b.size),
- &p,
+ buffer,
+ size,
+ &pos,
b.buffer,
b.capacity))
return OCI_ERROR;
- switch (p)
+ switch (pos)
{
- case bind::whole:
+ case one_chunk:
{
*piece = OCI_ONE_PIECE;
break;
}
- case bind::first:
+ case first_chunk:
{
*piece = OCI_FIRST_PIECE;
break;
}
- case bind::next:
+ case next_chunk:
{
*piece = OCI_NEXT_PIECE;
break;
}
- case bind::last:
+ case last_chunk:
{
*piece = OCI_LAST_PIECE;
break;
}
- default:
- assert (0);
- return OCI_ERROR;
}
}
else
@@ -117,17 +114,13 @@ namespace odb
OCIError* err (conn_.error_handle ());
// The parameter position in OCIBindByPos is specified as a 1-based
- // index. Convert 'o' to a 1-based offset.
+ // index. Convert o to a 1-based offset.
//
++o;
for (size_t e (o + c); o < e; ++c, ++b)
{
- bool callback ((b->type == SQLT_BLOB || b->type == SQLT_CLOB) &&
- b->callback.param != 0);
-
OCIBind* h (0);
-
sword r (OCIBindByPos (stmt_,
&h,
err,
@@ -140,12 +133,13 @@ namespace odb
0,
0,
0,
- callback ? OCI_DATA_AT_EXEC : OCI_DEFAULT));
+ b->callback.param != 0 ?
+ OCI_DATA_AT_EXEC : OCI_DEFAULT));
if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
translate_error (err, r);
- if (callback)
+ if (b->callback.param != 0)
{
r = OCIBindDynamic (h, err, b, &param_callback_proxy, 0, 0);
@@ -162,9 +156,9 @@ namespace odb
OCIError* err (conn_.error_handle ());
- // The parameter position in OCIDefineByPos is specified as a 1-based
- // index.
- //
+ result_binds_ = b;
+ count_ = c;
+
for (size_t i (1); i <= c; ++i, ++b)
{
if (b->type == SQLT_BLOB || b->type == SQLT_CLOB)
@@ -185,10 +179,10 @@ namespace odb
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.
+ // 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
@@ -204,7 +198,7 @@ namespace odb
if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
translate_error (err, r);
- bool b (true);
+ boolean b (true);
r = OCIAttrSet (h,
OCI_HTYPE_DEFINE,
&b,
@@ -238,6 +232,81 @@ namespace odb
}
}
+ void statement::
+ stream_result_lobs ()
+ {
+ OCIError* err (conn_.error_handle());
+
+ for (size_t i (0); i < count_; ++i)
+ {
+ bind& b (result_binds_[i]);
+
+ // 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) &&
+ *b.indicator != -1 &&
+ b.callback.result != 0)
+ {
+ // If b.capacity is 0, we will be stuck in an infinite loop.
+ //
+ assert (b.capacity != 0);
+
+ OCILobLocator* locator (reinterpret_cast<OCILobLocator*> (b.size));
+
+ ub8 size (0);
+ sword r (OCILobGetLength2(conn_.handle (),
+ err,
+ locator,
+ &size));
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+
+ ub1 piece (OCI_FIRST_PIECE);
+ ub8 read (size);
+
+ do
+ {
+ r = OCILobRead2 (conn_.handle (),
+ err,
+ locator,
+ &read,
+ 0,
+ 1,
+ b.buffer,
+ b.capacity,
+ piece,
+ 0,
+ 0,
+ 0,
+ SQLCS_IMPLICIT);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+
+ chunk_position cp;
+
+ if (piece == OCI_FIRST_PIECE)
+ cp = read == size ? one_chunk : first_chunk;
+ else if (r == OCI_NEED_DATA)
+ cp = next_chunk;
+ else
+ cp = last_chunk;
+
+ if (!(*b.callback.result) (b.callback_context,
+ b.buffer,
+ static_cast<ub4> (read),
+ cp))
+ throw database_exception (24343, "user defined callback error");
+
+ piece = OCI_NEXT_PIECE;
+
+ } while (r == OCI_NEED_DATA);
+ }
+ }
+ }
+
statement::
~statement ()
{
@@ -257,13 +326,12 @@ namespace odb
const string& s,
binding& cond,
binding& data,
- std::size_t lob_prefetch_len)
+ std::size_t lob_prefetch_size)
: statement (conn, s),
- data_ (data),
done_ (false)
{
bind_param (cond.bind, cond.count, 0);
- bind_result (data.bind, data.count, lob_prefetch_len);
+ bind_result (data.bind, data.count, lob_prefetch_size);
}
void select_statement::
@@ -272,10 +340,10 @@ namespace odb
if (!done_)
free_result ();
- // @@ 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. Keep in mind the implications
- // on LOB prefetch.
+ // @@ 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. Keep the implications on LOB prefetch in
+ // mind.
//
sword r (OCIStmtExecute (conn_.handle (),
stmt_,
@@ -330,85 +398,6 @@ namespace odb
}
}
- void select_statement::
- stream_result_lobs ()
- {
- OCIError* err (conn_.error_handle());
-
- for (size_t i (0); i < data_.count; ++i)
- {
- // Only stream if the bind specifies a LOB type, and the LOB
- // value is not NULL, and a result callback has been provided.
- //
- if ((data_.bind[i].type == SQLT_BLOB ||
- data_.bind[i].type == SQLT_CLOB) &&
- *data_.bind[i].indicator != -1 &&
- data_.bind[i].callback.result != 0)
- {
- // @@ If data_.bind[i].capacity is 0, we will be stuck in an
- // infinite loop.
- //
- assert (data_.bind[i].capacity != 0);
-
- OCILobLocator* locator (
- reinterpret_cast<OCILobLocator*> (data_.bind[i].size));
-
- ub8 length (0);
- sword r (OCILobGetLength2(conn_.handle (),
- err,
- locator,
- &length));
-
- if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
- translate_error (err, r);
-
- for (ub8 total (0), read (0); total < length; total += read)
- {
- read = length - total;
- read = read > data_.bind[i].capacity ?
- data_.bind[i].capacity : read;
-
- // The call to OCILobRead2 does not need to know when the last
- // piece is being requested.
- //
- ub1 oci_piece (total == 0 ? OCI_FIRST_PIECE : OCI_NEXT_PIECE);
-
- r = OCILobRead2 (conn_.handle (),
- err,
- locator,
- &read,
- 0,
- 1, // Starting offset. The first position is 1.
- // This parameter is only used by OCI on the
- // first call when polling.
- data_.bind[i].buffer,
- data_.bind[i].capacity,
- oci_piece,
- 0,
- 0,
- 0,
- SQLCS_IMPLICIT);
-
- if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
- translate_error (err, r);
-
- bind::piece user_piece;
- if (oci_piece == OCI_FIRST_PIECE)
- user_piece = read == length ? bind::whole : bind::first;
- else
- user_piece = total + read < length ? bind::next : bind::last;
-
- if (!(*data_.bind[i].callback.result) (
- data_.bind[i].callback_context,
- data_.bind[i].buffer,
- read,
- user_piece))
- break;
- }
- }
- }
- }
-
//
// insert_statement
//
@@ -419,7 +408,7 @@ namespace odb
ub4, // iter
ub4, // index
void** buffer,
- ub4* len,
+ ub4* size,
ub1* piece,
void** indicator)
{
@@ -428,7 +417,7 @@ namespace odb
bind& b (*static_cast<bind*> (context));
*buffer = 0;
- *len = 0;
+ *size = 0;
*piece = OCI_ONE_PIECE;
b.indicator = -1;
*indicator = &b.indicator;
@@ -442,7 +431,7 @@ namespace odb
ub4, // iter
ub4, // index
void** buffer,
- ub4** len,
+ ub4** size,
ub1*, // piece
void** indicator,
ub2** rcode)
@@ -454,10 +443,10 @@ namespace odb
#if (OCI_MAJOR_VERSION == 11 && OCI_MINOR_VERSION >=2) \
|| OCI_MAJOR_VERSION > 11
*buffer = &b.id.value64;
- **len = sizeof (unsigned long long);
+ **size = sizeof (unsigned long long);
#else
*buffer = &b.id.value32;
- **len = sizeof (unsigned int);
+ **size = sizeof (unsigned int);
#endif
*indicator = &b.indicator;
diff --git a/odb/oracle/statement.hxx b/odb/oracle/statement.hxx
index b4e6601..add263a 100644
--- a/odb/oracle/statement.hxx
+++ b/odb/oracle/statement.hxx
@@ -48,13 +48,21 @@ namespace odb
// lost OCIDefine resources.
//
void
- bind_result (bind*,
- std::size_t count,
- std::size_t lob_prefetch_len = 0);
+ bind_result (bind*,
+ std::size_t count,
+ std::size_t lob_prefetch_size = 0);
+
+ // Stream the result LOBs, calling user callbacks where necessary.
+ //
+ void
+ stream_result_lobs ();
protected:
connection& conn_;
auto_handle<OCIStmt> stmt_;
+
+ bind* result_binds_;
+ std::size_t count_;
};
class LIBODB_ORACLE_EXPORT select_statement: public statement
@@ -67,7 +75,7 @@ namespace odb
const std::string& statement,
binding& cond,
binding& data,
- std::size_t lob_prefetch_len = 0);
+ std::size_t lob_prefetch_size = 0);
enum result
{
success,
@@ -83,17 +91,11 @@ namespace odb
void
free_result ();
- // Stream the result lobs, invoking user callbacks where necessary.
- //
- void
- stream_result_lobs ();
-
private:
select_statement (const select_statement&);
select_statement& operator= (const select_statement&);
private:
- binding& data_;
bool done_;
};