aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--odb/oracle/binding.hxx22
-rw-r--r--odb/oracle/error.cxx21
-rw-r--r--odb/oracle/error.hxx12
-rw-r--r--odb/oracle/exceptions.cxx24
-rw-r--r--odb/oracle/exceptions.hxx12
-rw-r--r--odb/oracle/oracle-types.hxx3
-rw-r--r--odb/oracle/simple-object-statements.hxx17
-rw-r--r--odb/oracle/simple-object-statements.txx17
-rw-r--r--odb/oracle/statement.cxx381
-rw-r--r--odb/oracle/statement.hxx39
10 files changed, 428 insertions, 120 deletions
diff --git a/odb/oracle/binding.hxx b/odb/oracle/binding.hxx
index 94e7128..be201a7 100644
--- a/odb/oracle/binding.hxx
+++ b/odb/oracle/binding.hxx
@@ -11,6 +11,7 @@
#include <odb/oracle/version.hxx>
#include <odb/oracle/oracle-types.hxx>
+#include <odb/oracle/auto-handle.hxx>
#include <odb/oracle/details/export.hxx>
@@ -24,10 +25,23 @@ namespace odb
typedef oracle::bind bind_type;
typedef oracle::change_callback change_callback_type;
- binding (): bind (0), count (0), version (0), change_callback (0) {}
+ 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), change_callback (0)
+ : 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, auto_handle<OCIError>* st)
+ : bind (b), count (n), version (0),
+ batch (bt), skip (s), status (st),
+ change_callback (0)
{
}
@@ -35,6 +49,10 @@ namespace odb
std::size_t count;
std::size_t version;
+ std::size_t batch;
+ std::size_t skip;
+ auto_handle<OCIError>* status; // Batch status array.
+
change_callback_type* change_callback;
private:
diff --git a/odb/oracle/error.cxx b/odb/oracle/error.cxx
index afab2c7..68bdc44 100644
--- a/odb/oracle/error.cxx
+++ b/odb/oracle/error.cxx
@@ -19,7 +19,8 @@ namespace odb
namespace oracle
{
static void
- translate_error (void* h, ub4 htype, sword r, connection* conn)
+ 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);
@@ -186,25 +187,33 @@ namespace odb
dbe.append (e, b);
}
- throw dbe;
+ 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)
+ translate_error (OCIError* h, sword r, connection* c,
+ size_t pos, multiple_exceptions* mex)
{
- translate_error (h, OCI_HTYPE_ERROR, r, 0);
+ 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);
+ 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);
+ translate_error (h, OCI_HTYPE_ENV, OCI_ERROR, 0, 0, 0);
}
}
}
diff --git a/odb/oracle/error.hxx b/odb/oracle/error.hxx
index fdb0537..accfc3f 100644
--- a/odb/oracle/error.hxx
+++ b/odb/oracle/error.hxx
@@ -7,20 +7,24 @@
#include <odb/pre.hxx>
+#include <cstddef> // std::size_t
+
#include <odb/oracle/oracle-fwd.hxx>
-#include <odb/oracle/forward.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 an appropriate
- // exception.
+ // 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);
+ translate_error (OCIError*, sword result, connection* = 0,
+ std::size_t pos = 0, multiple_exceptions* = 0);
LIBODB_ORACLE_EXPORT void
translate_error (connection&, sword result);
diff --git a/odb/oracle/exceptions.cxx b/odb/oracle/exceptions.cxx
index b6375e7..4676843 100644
--- a/odb/oracle/exceptions.cxx
+++ b/odb/oracle/exceptions.cxx
@@ -57,6 +57,12 @@ namespace odb
return what_.c_str ();
}
+ database_exception* database_exception::
+ clone () const
+ {
+ return new database_exception (*this);
+ }
+
//
// lob_comparison
//
@@ -67,6 +73,12 @@ namespace odb
return "comparison of LOB values in queries not supported";
}
+ lob_comparison* lob_comparison::
+ clone () const
+ {
+ return new lob_comparison (*this);
+ }
+
//
// cli_exception
//
@@ -88,6 +100,12 @@ namespace odb
return what_.c_str ();
}
+ cli_exception* cli_exception::
+ clone () const
+ {
+ return new cli_exception (*this);
+ }
+
//
// invalid_oci_handle
//
@@ -97,5 +115,11 @@ namespace odb
{
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/odb/oracle/exceptions.hxx b/odb/oracle/exceptions.hxx
index d8ca14a..87327b9 100644
--- a/odb/oracle/exceptions.hxx
+++ b/odb/oracle/exceptions.hxx
@@ -76,6 +76,9 @@ namespace odb
virtual const char*
what () const throw ();
+ virtual database_exception*
+ clone () const;
+
void
append (sb4 error, const std::string& message);
@@ -88,6 +91,9 @@ namespace odb
{
virtual const char*
what () const throw ();
+
+ virtual lob_comparison*
+ clone () const;
};
struct LIBODB_ORACLE_EXPORT cli_exception: odb::exception
@@ -98,6 +104,9 @@ namespace odb
virtual const char*
what () const throw ();
+ virtual cli_exception*
+ clone () const;
+
private:
std::string what_;
};
@@ -106,6 +115,9 @@ namespace odb
{
virtual const char*
what () const throw ();
+
+ virtual invalid_oci_handle*
+ clone () const;
};
namespace core
diff --git a/odb/oracle/oracle-types.hxx b/odb/oracle/oracle-types.hxx
index ec53425..ffc974a 100644
--- a/odb/oracle/oracle-types.hxx
+++ b/odb/oracle/oracle-types.hxx
@@ -115,7 +115,8 @@ namespace odb
// bindings, this is interpreted as the OCIDefine
// handle associated with the LOB result parameter.
ub4 capacity; // The maximum number of bytes that can be stored in
- // the buffer.
+ // the buffer. For LOBs, it used to store array skip
+ // size.
sb2* indicator; // Pointer to an OCI indicator variable.
lob_callback* callback;
diff --git a/odb/oracle/simple-object-statements.hxx b/odb/oracle/simple-object-statements.hxx
index ff76e91..2b550e5 100644
--- a/odb/oracle/simple-object-statements.hxx
+++ b/odb/oracle/simple-object-statements.hxx
@@ -276,9 +276,9 @@ namespace odb
// Object image.
//
image_type&
- image ()
+ image (std::size_t i = 0)
{
- return image_;
+ return image_[i];
}
// Insert binding.
@@ -323,7 +323,7 @@ namespace odb
// Object id image and binding.
//
id_image_type&
- id_image () {return id_image_;}
+ id_image (std::size_t i = 0) {return id_image_[i];}
std::size_t
id_image_version () const {return id_image_version_;}
@@ -353,7 +353,7 @@ namespace odb
object_traits::persist_statement,
object_traits::versioned, // Process if versioned.
insert_image_binding_,
- object_traits::auto_id));
+ object_traits::auto_id ? &id_image_binding_ : 0));
return *persist_;
}
@@ -473,7 +473,8 @@ namespace odb
extra_statement_cache_ptr<extra_statement_cache_type, image_type>
extra_statement_cache_;
- image_type image_;
+ image_type image_[object_traits::batch];
+ auto_handle<OCIError> status_[object_traits::batch];
// Select binding.
//
@@ -502,10 +503,10 @@ namespace odb
bind update_image_bind_[update_column_count + id_column_count +
managed_optimistic_column_count];
- // Id image binding (only used as a parameter). Uses the suffix in
- // the update bind.
+ // Id image binding (only used as a parameter or in RETURNING for
+ // auto ids). Uses the suffix in the update bind.
//
- id_image_type id_image_;
+ id_image_type id_image_[object_traits::batch];
std::size_t id_image_version_;
binding id_image_binding_;
diff --git a/odb/oracle/simple-object-statements.txx b/odb/oracle/simple-object-statements.txx
index 61bbc5f..32be8bd 100644
--- a/odb/oracle/simple-object-statements.txx
+++ b/odb/oracle/simple-object-statements.txx
@@ -43,24 +43,31 @@ namespace odb
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),
+ insert_image_binding_ (insert_image_bind_,
+ insert_column_count,
+ object_traits::batch,
+ sizeof (image_type),
+ status_),
update_image_binding_ (update_image_bind_,
update_column_count + id_column_count +
managed_optimistic_column_count),
id_image_binding_ (update_image_bind_ + update_column_count,
- id_column_count),
+ id_column_count,
+ object_traits::batch,
+ sizeof (id_image_type),
+ 0),
od_ (update_image_bind_ + update_column_count)
{
- image_.version = 0;
+ image_[0].version = 0; // @@ TODO
select_image_version_ = 0;
insert_image_version_ = 0;
update_image_version_ = 0;
update_id_image_version_ = 0;
- id_image_.version = 0;
+ id_image_[0].version = 0; // @@ TODO
id_image_version_ = 0;
- select_image_binding_.change_callback = image_.change_callback ();
+ select_image_binding_.change_callback = image_[0].change_callback ();
std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
diff --git a/odb/oracle/statement.cxx b/odb/oracle/statement.cxx
index 4032138..10fa3a6 100644
--- a/odb/oracle/statement.cxx
+++ b/odb/oracle/statement.cxx
@@ -4,8 +4,9 @@
#include <oci.h>
-#include <cstring> // std::strlen
+#include <cstring> // std::strlen, std::memset
#include <cassert>
+#include <iostream> // @@ tmp
#include <odb/tracer.hxx>
#include <odb/exceptions.hxx> // object_not_persistent
@@ -73,7 +74,7 @@ namespace odb
extern "C" sb4
odb_oracle_param_callback_proxy (void* context,
OCIBind*,
- ub4, // iteration
+ ub4 it, // iteration
ub4, // index
void** buffer,
ub4* size,
@@ -81,15 +82,27 @@ namespace odb
void** indicator)
{
bind& b (*static_cast<bind*> (context));
- lob* l (static_cast<lob*> (b.buffer));
+
+ // Offset the data based on the current iteration and skip size (stored
+ // in capacity).
+ //
+ sb2* ind (reinterpret_cast<sb2*> (
+ reinterpret_cast<char*> (b.indicator) + it * b.capacity));
// Only call the callback if the parameter is not NULL.
//
- if (*b.indicator != -1)
+ if (*ind != -1)
{
+ lob* l (reinterpret_cast<lob*> (
+ static_cast<char*> (b.buffer) + it * b.capacity));
+
+ lob_callback* cb (
+ reinterpret_cast<lob_callback*> (
+ reinterpret_cast<char*> (b.callback) + it * b.capacity));
+
chunk_position pos;
- if (!(*b.callback->callback.param) (
- b.callback->context.param,
+ if (!(*cb->callback.param) (
+ cb->context.param,
&l->position,
const_cast<const void**> (buffer),
size,
@@ -125,7 +138,7 @@ namespace odb
else
*piece = OCI_ONE_PIECE;
- *indicator = b.indicator;
+ *indicator = ind;
return OCI_CONTINUE;
}
@@ -321,7 +334,7 @@ namespace odb
}
ub4 statement::
- bind_param (bind* b, size_t n)
+ 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.
//
@@ -589,7 +602,12 @@ namespace odb
// However, in Oracle, LOBs cannot be used in queries so we can
// make an exception here.
//
- l->buffer = &lob_buffer;
+ for (size_t i (0); i != batch;)
+ {
+ l->buffer = &lob_buffer;
+ l = reinterpret_cast<lob*> (
+ static_cast<char*> (b->buffer) + ++i * skip);
+ }
}
assert (callback);
@@ -603,6 +621,11 @@ namespace odb
//
capacity = 4096;
+ // 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:
@@ -686,6 +709,23 @@ namespace odb
if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
translate_error (err, r);
}
+
+ // Set array information if we have a batch.
+ //
+ if (skip != 0)
+ {
+ 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;
@@ -1559,22 +1599,27 @@ namespace odb
extern "C" sb4
odb_oracle_returning_in (void* context,
OCIBind*, // bind
- ub4, // iter
+ ub4 it, // iter
ub4, // index
void** buffer,
ub4* size,
ub1* piece,
void** indicator)
{
- typedef insert_statement::id_bind_type bind;
+ binding& ret (*static_cast<insert_statement*> (context)->ret_);
- bind& b (*static_cast<bind*> (context));
+ // Offset the data based on the current iteration and skip size.
+ // The id is always first.
+ //
+ sb2* ind (reinterpret_cast<sb2*> (
+ reinterpret_cast<char*> (
+ ret.bind[0].indicator) + it * ret.skip));
*buffer = 0;
*size = 0;
*piece = OCI_ONE_PIECE;
- b.indicator = -1;
- *indicator = &b.indicator;
+ *ind = -1;
+ *indicator = ind;
return OCI_CONTINUE;
}
@@ -1582,30 +1627,61 @@ namespace odb
extern "C" sb4
odb_oracle_returning_out (void* context,
OCIBind*, // bind
- ub4, // iter
+ ub4 it, // iter
ub4, // index
void** buffer,
ub4** size,
- ub1*, // piece
+ ub1* piece,
void** indicator,
ub2** rcode)
{
- typedef insert_statement::id_bind_type bind;
+ 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);
- bind& b (*static_cast<bind*> (context));
+ // Offset the data based on the current iteration and skip size.
+ //
+ *buffer = static_cast<char*> (b.buffer) + it * skip;
-#if (OCI_MAJOR_VERSION == 11 && OCI_MINOR_VERSION >=2) \
- || OCI_MAJOR_VERSION > 11
- *buffer = &b.id.integer;
- **size = sizeof (unsigned long long);
-#else
- *buffer = b.id.number.buffer;
- *size = &b.id.number.size;
- b.id.number.size = 21;
-#endif
+ 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 (it != 0)
+ {
+ *reinterpret_cast<ub2*> (
+ reinterpret_cast<char*> (b.size) + (it - 1) * skip) =
+ static_cast<ub2> (st.ret_size_);
+ }
+
+ *size = &st.ret_size_;
+ }
+
+ // For some reason we have to set the out size to the (presumably)
+ // maximum buffer size.
+ //
+ **size = b.capacity;
+
+ *indicator = reinterpret_cast<sb2*> (
+ reinterpret_cast<char*> (b.indicator) + it * skip);
- *indicator = &b.indicator;
*rcode = 0;
+ *piece = OCI_ONE_PIECE;
return OCI_CONTINUE;
}
@@ -1620,12 +1696,13 @@ namespace odb
const string& text,
bool process,
binding& param,
- bool returning)
+ binding* returning)
: statement (conn,
text, statement_insert,
- (process ? &param : 0), false)
+ (process ? &param : 0), false),
+ ret_ (returning), status_ (param.status)
{
- init (param, returning);
+ init (param);
}
insert_statement::
@@ -1633,37 +1710,43 @@ namespace odb
const char* text,
bool process,
binding& param,
- bool returning)
+ binding* returning)
: statement (conn,
text, statement_insert,
- (process ? &param : 0), false)
+ (process ? &param : 0), false),
+ ret_ (returning), status_ (param.status)
{
- init (param, returning);
+ init (param);
}
void insert_statement::
- init (binding& param, bool returning)
+ init (binding& param)
{
- ub4 param_count (bind_param (param.bind, param.count));
-
- if (returning)
+ 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,
-#if (OCI_MAJOR_VERSION == 11 && OCI_MINOR_VERSION >=2) \
- || OCI_MAJOR_VERSION > 11
- sizeof (unsigned long long),
- SQLT_UIN,
-#else
- 21,
- SQLT_NUM,
-#endif
+ b->capacity,
+ param_sqlt_lookup[b->type],
0,
0,
0,
@@ -1676,9 +1759,9 @@ namespace odb
r = OCIBindDynamic (h,
err,
- &id_bind_,
+ this,
&odb_oracle_returning_in,
- &id_bind_,
+ this,
&odb_oracle_returning_out);
if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
@@ -1686,8 +1769,8 @@ namespace odb
}
}
- bool insert_statement::
- execute ()
+ size_t insert_statement::
+ execute (size_t n, multiple_exceptions* mex)
{
{
odb::tracer* t;
@@ -1697,60 +1780,192 @@ namespace odb
t->execute (conn_, *this);
}
+ mex_ = mex;
OCIError* err (conn_.error_handle ());
sword r (OCIStmtExecute (conn_.handle (),
stmt_,
err,
- 1,
+ static_cast<ub4> (n),
0,
0,
0,
- OCI_DEFAULT));
+ n == 1 ? OCI_DEFAULT : OCI_BATCH_ERRORS));
+
+ // If the statement failed as a whole, assume only the first row
+ // was attempted (and failed). Otherwise, in the batch errors mode,
+ // all the rows are always attempted (let's hope this is true).
+ //
+ i_ = 0;
+ n_ = (r == OCI_ERROR || r == OCI_INVALID_HANDLE ? 1 : n);
+
+ if (mex_ != 0)
+ {
+ mex_->current (i_);
+ mex_->attempted (n_);
+ }
if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
{
- sb4 e;
- OCIErrorGet (err, 1, 0, &e, 0, 0, OCI_HTYPE_ERROR);
+ if (mex_ != 0)
+ mex_->fatal (true); // An incomplete batch is always fatal.
+
+ fetch (r, err);
+ return n_;
+ }
- // 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.
+ // Initialize the batch status array.
+ //
+ if (n_ != 1)
+ {
+ // Clear the status array.
//
- if (e == 1)
- return false;
- else
- translate_error (conn_, r);
+ for (size_t i (0); i != n_; ++i)
+ status_[i].reset ();
+
+ // @@ TODO allocate per batch stmt (maybe lazily here if NULL).
+ //
+ auto_handle<OCIError> err1;
+ {
+ 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 en;
+ r = OCIAttrGet (stmt_,
+ OCI_HTYPE_STMT,
+ &en,
+ 0,
+ OCI_ATTR_NUM_DML_ERRORS,
+ err1);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err1, r);
+
+ cerr << "_NUM_DML_ERRORS: " << en << endl;
+
+ for (ub4 i (0); i != en; ++i)
+ {
+ 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);
+ }
+
+ 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);
+
+ status_[row].reset (err2.release ());
+
+ cerr << "[" << row << "]" << endl;
+ }
}
- ub4 row_count (0);
- r = OCIAttrGet (stmt_,
- OCI_HTYPE_STMT,
- &row_count,
- 0,
- OCI_ATTR_ROW_COUNT,
- err);
+ // Store the last returned id size (see odb_oracle_returning_out()
+ // for details).
+ //
+ if (ret_ != 0)
+ {
+ size_t n (n_);
+
+ // Find the index of the last sucessefully processed row.
+ //
+ if (n != 1)
+ for (; n != 0 && status_[n - 1] != 0; --n) ;
+
+ if (n != 0) // They all might have failed.
+ {
+ *reinterpret_cast<ub2*> (
+ reinterpret_cast<char*> (
+ ret_->bind[0].size) + (n - 1) * ret_->skip) =
+ static_cast<ub2> (ret_size_);
+ }
+ }
+
+ if (n_ == 1)
+ fetch (OCI_SUCCESS, 0);
+ else
+ fetch (status_[i_] == 0 ? OCI_SUCCESS : OCI_ERROR, status_[i_]);
+
+ return n_;
+ }
+
+ void insert_statement::
+ fetch (sword r, OCIError* err)
+ {
+ result_ = true;
if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
- translate_error (err, r);
+ {
+ // An auto-assigned object id should never cause a duplicate primary
+ // key.
+ //
+ if (ret_ == 0)
+ {
+ sb4 e;
+ OCIErrorGet (err, 1, 0, &e, 0, 0, OCI_HTYPE_ERROR);
- // The value of the OCI_ATTR_ROW_COUNT attribute associated with an
- // INSERT statment represents the number of rows inserted.
- //
- return row_count != 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 (e == 1)
+ result_ = false;
+ }
+
+ if (result_)
+ translate_error (err, r, &conn_, i_, mex_); // Can return.
+ }
}
- unsigned long long insert_statement::
- id ()
+ bool insert_statement::
+ result (std::size_t i)
{
-#if (OCI_MAJOR_VERSION == 11 && OCI_MINOR_VERSION >=2) \
- || OCI_MAJOR_VERSION > 11
- return id_bind_.id.integer;
-#else
- return details::number_to_uint64 (
- id_bind_.id.number.buffer,
- static_cast <std::size_t> (id_bind_.id.number.size));
-#endif
+ assert ((i_ == i || i_ + 1 == i) && i < n_);
+
+ // Get to the next row if necessary.
+ //
+ if (i != i_)
+ {
+ mex_->current (++i_); // It cannot be NULL since this is a batch.
+ fetch (status_[i_] == 0 ? OCI_SUCCESS : OCI_ERROR, status_[i_]);
+ }
+
+ return result_;
}
//
diff --git a/odb/oracle/statement.hxx b/odb/oracle/statement.hxx
index cf336c4..93213d1 100644
--- a/odb/oracle/statement.hxx
+++ b/odb/oracle/statement.hxx
@@ -11,6 +11,7 @@
#include <cstddef> // std::size_t
#include <odb/statement.hxx>
+#include <odb/exceptions.hxx>
#include <odb/oracle/version.hxx>
#include <odb/oracle/forward.hxx>
@@ -96,7 +97,8 @@ namespace odb
// of columns bound.
//
ub4
- bind_param (bind*, std::size_t count);
+ 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
@@ -261,27 +263,30 @@ namespace odb
const std::string& text,
bool process_text,
binding& param,
- bool returning);
+ binding* returning);
insert_statement (connection_type& conn,
const char* text,
bool process_text,
binding& param,
- bool returning);
+ binding* returning);
- // Return true if successful and false if the row is a duplicate. All
- // other errors are reported by throwing exceptions.
+ // Return the number of rows (out of n) that were attempted.
//
- bool
- execute ();
+ std::size_t
+ execute (std::size_t n = 1, multiple_exceptions* = 0);
- unsigned long long
- id ();
+ // 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 = 0);
private:
insert_statement (const insert_statement&);
insert_statement& operator= (const insert_statement&);
+ /*
// Only OCI versions 11.2 and greater support conversion of the internal
// Oracle type NUMBER to an external 64-bit integer type. If we detect
// version 11.2 or greater we provide an unsigned long long image.
@@ -304,13 +309,25 @@ namespace odb
sb2 indicator;
};
+ */
private:
void
- init (binding& param, bool returning);
+ init (binding& param);
+
+ void
+ fetch (sword r, OCIError*);
+
+ public: // For odb_oracle_returning_*().
+ binding* ret_;
+ ub4 ret_size_; // You don't want to know (see statement.cxx).
private:
- id_bind_type id_bind_;
+ multiple_exceptions* mex_;
+ std::size_t n_;
+ std::size_t i_;
+ auto_handle<OCIError>* status_;
+ bool result_;
};
class LIBODB_ORACLE_EXPORT update_statement: public statement