aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2013-09-10 14:10:45 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2013-09-10 14:10:45 +0200
commitd3ec68714365046a30379a410cdff52e5f5ae066 (patch)
tree36b971dd06626cc39b51eb0ec6e7f4f3ca40fcba
parent88f3f0282bad25c962783defa1e34dcbec60a07c (diff)
Schema versioning support
-rw-r--r--odb/mssql/container-statements.hxx35
-rw-r--r--odb/mssql/container-statements.txx5
-rw-r--r--odb/mssql/database.cxx9
-rw-r--r--odb/mssql/forward.hxx3
-rw-r--r--odb/mssql/makefile1
-rw-r--r--odb/mssql/no-id-object-result.hxx6
-rw-r--r--odb/mssql/no-id-object-result.txx12
-rw-r--r--odb/mssql/no-id-object-statements.hxx1
-rw-r--r--odb/mssql/polymorphic-object-result.hxx6
-rw-r--r--odb/mssql/polymorphic-object-result.txx28
-rw-r--r--odb/mssql/polymorphic-object-statements.hxx23
-rw-r--r--odb/mssql/polymorphic-object-statements.txx16
-rw-r--r--odb/mssql/query.cxx16
-rw-r--r--odb/mssql/section-statements.hxx3
-rw-r--r--odb/mssql/simple-object-result.hxx6
-rw-r--r--odb/mssql/simple-object-result.txx12
-rw-r--r--odb/mssql/simple-object-statements.hxx21
-rw-r--r--odb/mssql/simple-object-statements.txx19
-rw-r--r--odb/mssql/statement-cache.hxx5
-rw-r--r--odb/mssql/statement-cache.txx15
-rw-r--r--odb/mssql/statement-processing.cxx348
-rw-r--r--odb/mssql/statement.cxx259
-rw-r--r--odb/mssql/statement.hxx77
-rw-r--r--odb/mssql/traits-calls.hxx191
-rw-r--r--odb/mssql/view-result.hxx6
-rw-r--r--odb/mssql/view-result.txx12
26 files changed, 998 insertions, 137 deletions
diff --git a/odb/mssql/container-statements.hxx b/odb/mssql/container-statements.hxx
index 8653afe..5a7e52c 100644
--- a/odb/mssql/container-statements.hxx
+++ b/odb/mssql/container-statements.hxx
@@ -10,6 +10,7 @@
#include <cstddef> // std::size_t
#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
#include <odb/traits.hxx>
#include <odb/mssql/version.hxx>
@@ -58,6 +59,14 @@ namespace odb
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&
@@ -109,7 +118,13 @@ namespace odb
if (insert_ == 0)
insert_.reset (
new (details::shared) insert_statement_type (
- conn_, insert_text_, insert_image_binding_, false, false, false));
+ conn_,
+ insert_text_,
+ versioned_, // Process if versioned.
+ insert_image_binding_,
+ false,
+ false,
+ false));
return *insert_;
}
@@ -120,7 +135,13 @@ namespace odb
if (select_ == 0)
select_.reset (
new (details::shared) select_statement_type (
- conn_, select_text_, id_binding_, select_image_binding_, false));
+ conn_,
+ select_text_,
+ versioned_, // Process if versioned.
+ false, // Don't optimize.
+ id_binding_,
+ select_image_binding_,
+ false));
return *select_;
}
@@ -157,6 +178,9 @@ namespace odb
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_;
@@ -259,7 +283,12 @@ namespace odb
if (update_ == 0)
update_.reset (
new (details::shared) update_statement_type (
- this->conn_, update_text_, update_image_binding_, false, false));
+ this->conn_,
+ update_text_,
+ this->versioned_, // Process if versioned.
+ update_image_binding_,
+ false,
+ false));
return *update_;
}
diff --git a/odb/mssql/container-statements.txx b/odb/mssql/container-statements.txx
index 33c76fe..d989bfa 100644
--- a/odb/mssql/container-statements.txx
+++ b/odb/mssql/container-statements.txx
@@ -18,7 +18,8 @@ namespace odb
id_binding_ (id),
functions_ (this),
insert_image_binding_ (0, 0), // Initialized by impl.
- select_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;
@@ -69,6 +70,8 @@ namespace odb
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
diff --git a/odb/mssql/database.cxx b/odb/mssql/database.cxx
index b5e2fab..3c7075f 100644
--- a/odb/mssql/database.cxx
+++ b/odb/mssql/database.cxx
@@ -533,7 +533,14 @@ namespace odb
: t.connection ());
try
{
- select_statement st (c, text.c_str (), param, result, false);
+ select_statement st (c,
+ text.c_str (),
+ false, // Don't process.
+ false, // Don't optimize.
+ param,
+ result,
+ false);
+
st.execute ();
auto_result ar (st);
diff --git a/odb/mssql/forward.hxx b/odb/mssql/forward.hxx
index 124cdbe..8e5b06f 100644
--- a/odb/mssql/forward.hxx
+++ b/odb/mssql/forward.hxx
@@ -43,7 +43,8 @@ namespace odb
{
statement_select,
statement_insert,
- statement_update
+ statement_update,
+ statement_delete
};
class binding;
diff --git a/odb/mssql/makefile b/odb/mssql/makefile
index 23b92cd..e713718 100644
--- a/odb/mssql/makefile
+++ b/odb/mssql/makefile
@@ -17,6 +17,7 @@ query-dynamic.cxx \
query-const-expr.cxx \
simple-object-statements.cxx \
statement.cxx \
+statement-processing.cxx \
statements-base.cxx \
tracer.cxx \
traits.cxx \
diff --git a/odb/mssql/no-id-object-result.hxx b/odb/mssql/no-id-object-result.hxx
index 9bb726d..706e507 100644
--- a/odb/mssql/no-id-object-result.hxx
+++ b/odb/mssql/no-id-object-result.hxx
@@ -9,6 +9,7 @@
#include <cstddef> // std::size_t
+#include <odb/schema-version.hxx>
#include <odb/no-id-object-result.hxx>
#include <odb/details/shared-ptr.hxx>
@@ -16,6 +17,7 @@
#include <odb/mssql/version.hxx>
#include <odb/mssql/forward.hxx> // query_base
#include <odb/mssql/statement.hxx>
+#include <odb/mssql/traits-calls.hxx>
namespace odb
{
@@ -40,7 +42,8 @@ namespace odb
no_id_object_result_impl (const query_base&,
details::shared_ptr<select_statement>,
- statements_type&);
+ statements_type&,
+ const schema_version_migration*);
virtual void
load (object_type&);
@@ -68,6 +71,7 @@ namespace odb
private:
details::shared_ptr<select_statement> statement_;
statements_type& statements_;
+ object_traits_calls<object_type> tc_;
bool can_load_;
bool use_copy_;
typename object_traits::image_type* image_copy_;
diff --git a/odb/mssql/no-id-object-result.txx b/odb/mssql/no-id-object-result.txx
index 6629fa1..1b9edd3 100644
--- a/odb/mssql/no-id-object-result.txx
+++ b/odb/mssql/no-id-object-result.txx
@@ -47,10 +47,12 @@ namespace odb
no_id_object_result_impl<T>::
no_id_object_result_impl (const query_base&,
details::shared_ptr<select_statement> statement,
- statements_type& statements)
+ statements_type& statements,
+ const schema_version_migration* svm)
: base_type (statements.connection ()),
statement_ (statement),
statements_ (statements),
+ tc_ (svm),
use_copy_ (false),
image_copy_ (0)
{
@@ -65,9 +67,9 @@ namespace odb
object_traits::callback (this->db_, obj, callback_event::pre_load);
- object_traits::init (obj,
- use_copy_ ? *image_copy_ : statements_.image (),
- &this->db_);
+ tc_.init (obj,
+ use_copy_ ? *image_copy_ : statements_.image (),
+ &this->db_);
// If we are using a copy, make sure the callback information for
// long data also comes from the copy.
@@ -100,7 +102,7 @@ namespace odb
if (im.version != statements_.select_image_version ())
{
binding& b (statements_.select_image_binding ());
- object_traits::bind (b.bind, im, statement_select);
+ tc_.bind (b.bind, im, statement_select);
statements_.select_image_version (im.version);
b.version++;
}
diff --git a/odb/mssql/no-id-object-statements.hxx b/odb/mssql/no-id-object-statements.hxx
index adc071f..981bc9f 100644
--- a/odb/mssql/no-id-object-statements.hxx
+++ b/odb/mssql/no-id-object-statements.hxx
@@ -86,6 +86,7 @@ namespace odb
new (details::shared) insert_statement_type (
conn_,
object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
insert_image_binding_,
false,
object_traits::rowversion,
diff --git a/odb/mssql/polymorphic-object-result.hxx b/odb/mssql/polymorphic-object-result.hxx
index 91b5bfe..bb353b5 100644
--- a/odb/mssql/polymorphic-object-result.hxx
+++ b/odb/mssql/polymorphic-object-result.hxx
@@ -9,6 +9,7 @@
#include <cstddef> // std::size_t
+#include <odb/schema-version.hxx>
#include <odb/polymorphic-object-result.hxx>
#include <odb/details/shared-ptr.hxx>
@@ -16,6 +17,7 @@
#include <odb/mssql/version.hxx>
#include <odb/mssql/forward.hxx> // query_base
#include <odb/mssql/statement.hxx>
+#include <odb/mssql/traits-calls.hxx>
namespace odb
{
@@ -48,7 +50,8 @@ namespace odb
polymorphic_object_result_impl (const query_base&,
details::shared_ptr<select_statement>,
- statements_type&);
+ statements_type&,
+ const schema_version_migration*);
virtual void
load (object_type*, bool fetch);
@@ -82,6 +85,7 @@ namespace odb
private:
details::shared_ptr<select_statement> statement_;
statements_type& statements_;
+ object_traits_calls<object_type> tc_;
bool can_load_;
bool use_copy_;
image_type* image_copy_;
diff --git a/odb/mssql/polymorphic-object-result.txx b/odb/mssql/polymorphic-object-result.txx
index 14c7c75..a9c78c1 100644
--- a/odb/mssql/polymorphic-object-result.txx
+++ b/odb/mssql/polymorphic-object-result.txx
@@ -41,7 +41,10 @@ namespace odb
}
if (!this->end_)
+ {
statement_->free_result ();
+ this->end_ = true;
+ }
statement_.reset ();
}
@@ -50,10 +53,12 @@ namespace odb
polymorphic_object_result_impl<T>::
polymorphic_object_result_impl (const query_base&,
details::shared_ptr<select_statement> st,
- statements_type& sts)
+ statements_type& sts,
+ const schema_version_migration* svm)
: base_type (sts.connection ()),
statement_ (st),
statements_ (sts),
+ tc_ (svm),
use_copy_ (false),
image_copy_ (0)
{
@@ -134,7 +139,7 @@ namespace odb
callback_event ce (callback_event::pre_load);
pi.dispatch (info_type::call_callback, this->db_, pobj, &ce);
- object_traits::init (*pobj, i, &this->db_);
+ tc_.init (*pobj, i, &this->db_);
// If we are using a copy, make sure the callback information for
// long data also comes from the copy.
@@ -157,7 +162,7 @@ namespace odb
idb.version++;
}
- object_traits::load_ (statements_, *pobj);
+ tc_.load_ (statements_, *pobj, false);
// Load the dynamic part of the object unless static and dynamic
// types are the same.
@@ -168,7 +173,7 @@ namespace odb
pi.dispatch (info_type::call_load, this->db_, pobj, &d);
};
- rsts.load_delayed ();
+ rsts.load_delayed (tc_.version ());
l.unlock ();
ce = callback_event::post_load;
@@ -211,14 +216,16 @@ namespace odb
typedef object_traits_impl<T, id_mssql> traits;
static void
- rebind (typename traits::statements_type& sts)
+ 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));
- traits::bind (b.bind, 0, 0, im, statement_select);
+ 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 ());
}
@@ -233,14 +240,16 @@ namespace odb
typedef object_traits_impl<R, id_mssql> traits;
static void
- rebind (typename traits::statements_type& sts)
+ 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 ());
- traits::bind (b.bind, im, statement_select);
+ object_traits_calls<R> tc (svm);
+ tc.bind (b.bind, im, statement_select);
sts.select_image_version (im.version);
b.version++;
}
@@ -264,7 +273,8 @@ namespace odb
}
use_copy_ = false;
- polymorphic_image_rebind<object_type, root_type>::rebind (statements_);
+ polymorphic_image_rebind<object_type, root_type>::rebind (
+ statements_, tc_.version ());
if (statement_->fetch () == select_statement::no_data)
{
diff --git a/odb/mssql/polymorphic-object-statements.hxx b/odb/mssql/polymorphic-object-statements.hxx
index 2e435d9..381c607 100644
--- a/odb/mssql/polymorphic-object-statements.hxx
+++ b/odb/mssql/polymorphic-object-statements.hxx
@@ -101,6 +101,8 @@ namespace odb
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_,
false));
@@ -114,6 +116,18 @@ namespace odb
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;
@@ -185,7 +199,10 @@ namespace odb
// Delayed loading.
//
static void
- delayed_loader (odb::database&, const id_type&, root_type&);
+ delayed_loader (odb::database&,
+ const id_type&,
+ root_type&,
+ const schema_version_migration*);
public:
// Root and immediate base statements.
@@ -289,6 +306,7 @@ namespace odb
new (details::shared) insert_statement_type (
conn_,
object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
insert_image_binding_,
false,
false,
@@ -308,6 +326,8 @@ namespace odb
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],
false));
@@ -323,6 +343,7 @@ namespace odb
new (details::shared) update_statement_type (
conn_,
object_traits::update_statement,
+ object_traits::versioned, // Process if versioned.
update_image_binding_,
false,
false));
diff --git a/odb/mssql/polymorphic-object-statements.txx b/odb/mssql/polymorphic-object-statements.txx
index fa4756d..234af30 100644
--- a/odb/mssql/polymorphic-object-statements.txx
+++ b/odb/mssql/polymorphic-object-statements.txx
@@ -10,6 +10,7 @@
#include <odb/mssql/connection.hxx>
#include <odb/mssql/transaction.hxx>
#include <odb/mssql/statement-cache.hxx>
+#include <odb/mssql/traits-calls.hxx>
namespace odb
{
@@ -102,7 +103,10 @@ namespace odb
template <typename T>
void polymorphic_derived_object_statements<T>::
- delayed_loader (odb::database& db, const id_type& id, root_type& robj)
+ delayed_loader (odb::database& db,
+ const id_type& id,
+ root_type& robj,
+ const schema_version_migration* svm)
{
connection_type& conn (transaction::current ().connection ());
polymorphic_derived_object_statements& sts (
@@ -113,18 +117,20 @@ namespace odb
// The same code as in object_statements::load_delayed_().
//
- if (!object_traits::find_ (sts, &id))
+ object_traits_calls<T> tc (svm);
+
+ if (!tc.find_ (sts, &id))
throw object_not_persistent ();
auto_result ar (*sts.find_[0]);
object_traits::callback (db, obj, callback_event::pre_load);
- object_traits::init (obj, sts.image (), &db);
+ tc.init (obj, sts.image (), &db);
sts.find_[0]->stream_result ();
ar.free ();
- object_traits::load_ (sts, obj); // Load containers, etc.
+ tc.load_ (sts, obj, false); // Load containers, etc.
- rsts.load_delayed ();
+ rsts.load_delayed (svm);
{
typename root_statements_type::auto_unlock u (rsts);
diff --git a/odb/mssql/query.cxx b/odb/mssql/query.cxx
index 10e97ba..faef6f7 100644
--- a/odb/mssql/query.cxx
+++ b/odb/mssql/query.cxx
@@ -98,8 +98,8 @@ namespace odb
// We don't want extra spaces after '(' as well as before ','
// and ')'.
//
- if (last != ' ' && last != '(' &&
- first != ' ' && first != ',' && first != ')')
+ if (last != ' ' && last != '\n' && last != '(' &&
+ first != ' ' && first != '\n' && first != ',' && first != ')')
s += ' ';
s += q;
@@ -183,7 +183,7 @@ namespace odb
// 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] =='\t')
+ if (s.size () == n || s[n] == ' ' || s[n] == '\n' || s[n] =='\t')
return true;
}
@@ -242,7 +242,7 @@ namespace odb
{
case clause_part::kind_column:
{
- if (last != ' ' && last != '(')
+ if (last != ' ' && last != '\n' && last != '(')
r += ' ';
r += i->part;
@@ -250,7 +250,7 @@ namespace odb
}
case clause_part::kind_param:
{
- if (last != ' ' && last != '(')
+ if (last != ' ' && last != '\n' && last != '(')
r += ' ';
// Add the conversion expression, if any.
@@ -277,8 +277,8 @@ namespace odb
const string& p (i->part);
char first (!p.empty () ? p[0] : ' ');
- if (last != ' ' && last != '(' &&
- first != ' ' && first != ',' && first != ')')
+ if (last != ' ' && last != '\n' && last != '(' &&
+ first != ' ' && first != '\n' && first != ',' && first != ')')
r += ' ';
r += p;
@@ -286,7 +286,7 @@ namespace odb
}
case clause_part::kind_bool:
{
- if (last != ' ' && last != '(')
+ if (last != ' ' && last != '\n' && last != '(')
r += ' ';
// SQL Server does not have "true" TRUE and FALSE boolean
diff --git a/odb/mssql/section-statements.hxx b/odb/mssql/section-statements.hxx
index bf26dba..d6ac777 100644
--- a/odb/mssql/section-statements.hxx
+++ b/odb/mssql/section-statements.hxx
@@ -97,6 +97,8 @@ namespace odb
new (details::shared) select_statement_type (
conn_,
traits::select_statement,
+ traits::versioned, // Process if versioned.
+ false, // Don't optimize.
id_binding_,
select_image_binding_,
false));
@@ -112,6 +114,7 @@ namespace odb
new (details::shared) update_statement_type (
conn_,
traits::update_statement,
+ traits::versioned, // Process if versioned.
update_image_binding_,
traits::rowversion,
false));
diff --git a/odb/mssql/simple-object-result.hxx b/odb/mssql/simple-object-result.hxx
index 4240c29..e719db2 100644
--- a/odb/mssql/simple-object-result.hxx
+++ b/odb/mssql/simple-object-result.hxx
@@ -9,6 +9,7 @@
#include <cstddef> // std::size_t
+#include <odb/schema-version.hxx>
#include <odb/simple-object-result.hxx>
#include <odb/details/shared-ptr.hxx>
@@ -16,6 +17,7 @@
#include <odb/mssql/version.hxx>
#include <odb/mssql/forward.hxx> // query_base
#include <odb/mssql/statement.hxx>
+#include <odb/mssql/traits-calls.hxx>
namespace odb
{
@@ -41,7 +43,8 @@ namespace odb
object_result_impl (const query_base&,
details::shared_ptr<select_statement>,
- statements_type&);
+ statements_type&,
+ const schema_version_migration*);
virtual void
load (object_type&, bool fetch);
@@ -72,6 +75,7 @@ namespace odb
private:
details::shared_ptr<select_statement> statement_;
statements_type& statements_;
+ object_traits_calls<object_type> tc_;
bool can_load_;
bool use_copy_;
typename object_traits::image_type* image_copy_;
diff --git a/odb/mssql/simple-object-result.txx b/odb/mssql/simple-object-result.txx
index a7fb191..510494a 100644
--- a/odb/mssql/simple-object-result.txx
+++ b/odb/mssql/simple-object-result.txx
@@ -49,10 +49,12 @@ namespace odb
object_result_impl<T>::
object_result_impl (const query_base&,
details::shared_ptr<select_statement> statement,
- statements_type& statements)
+ statements_type& statements,
+ const schema_version_migration* svm)
: base_type (statements.connection ()),
statement_ (statement),
statements_ (statements),
+ tc_ (svm),
use_copy_ (false),
image_copy_ (0)
{
@@ -75,7 +77,7 @@ namespace odb
typename object_traits::image_type& i (
use_copy_ ? *image_copy_ : statements_.image ());
- object_traits::init (obj, i, &this->db_);
+ tc_.init (obj, i, &this->db_);
// If we are using a copy, make sure the callback information for
// long data also comes from the copy.
@@ -98,8 +100,8 @@ namespace odb
idb.version++;
}
- object_traits::load_ (statements_, obj);
- statements_.load_delayed ();
+ tc_.load_ (statements_, obj, false);
+ statements_.load_delayed (tc_.version ());
l.unlock ();
object_traits::callback (this->db_, obj, callback_event::post_load);
}
@@ -134,7 +136,7 @@ namespace odb
if (im.version != statements_.select_image_version ())
{
binding& b (statements_.select_image_binding ());
- object_traits::bind (b.bind, im, statement_select);
+ tc_.bind (b.bind, im, statement_select);
statements_.select_image_version (im.version);
b.version++;
}
diff --git a/odb/mssql/simple-object-statements.hxx b/odb/mssql/simple-object-statements.hxx
index 9cbd69c..ab43439 100644
--- a/odb/mssql/simple-object-statements.hxx
+++ b/odb/mssql/simple-object-statements.hxx
@@ -243,8 +243,10 @@ namespace odb
// Delayed loading.
//
- typedef void (*loader_function) (
- odb::database&, const id_type&, object_type&);
+ typedef void (*loader_function) (odb::database&,
+ const id_type&,
+ object_type&,
+ const schema_version_migration*);
void
delay_load (const id_type& id,
@@ -256,12 +258,12 @@ namespace odb
}
void
- load_delayed ()
+ load_delayed (const schema_version_migration* svm)
{
assert (locked ());
if (!delayed_.empty ())
- load_delayed_ ();
+ load_delayed_<object_statements> (svm);
}
void
@@ -349,6 +351,7 @@ namespace odb
new (details::shared) insert_statement_type (
conn_,
object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
insert_image_binding_,
object_traits::auto_id,
object_traits::rowversion,
@@ -365,6 +368,8 @@ namespace odb
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_,
false));
@@ -380,6 +385,7 @@ namespace odb
new (details::shared) update_statement_type (
conn_,
object_traits::update_statement,
+ object_traits::versioned, // Process if versioned.
update_image_binding_,
object_traits::rowversion,
false));
@@ -458,14 +464,15 @@ namespace odb
object_statements (const object_statements&);
object_statements& operator= (const object_statements&);
- private:
+ protected:
+ template <typename STS>
void
- load_delayed_ ();
+ load_delayed_ (const schema_version_migration*);
void
clear_delayed_ ();
- private:
+ protected:
template <typename T1>
friend class polymorphic_derived_object_statements;
diff --git a/odb/mssql/simple-object-statements.txx b/odb/mssql/simple-object-statements.txx
index 989f178..84ed5db 100644
--- a/odb/mssql/simple-object-statements.txx
+++ b/odb/mssql/simple-object-statements.txx
@@ -8,6 +8,7 @@
#include <odb/exceptions.hxx>
#include <odb/mssql/connection.hxx>
+#include <odb/mssql/traits-calls.hxx>
namespace odb
{
@@ -67,8 +68,9 @@ namespace odb
}
template <typename T>
+ template <typename STS>
void object_statements<T>::
- load_delayed_ ()
+ load_delayed_ (const schema_version_migration* svm)
{
database& db (connection ().database ());
@@ -83,7 +85,9 @@ namespace odb
if (l.loader == 0)
{
- if (!object_traits::find_ (*this, &l.id))
+ object_traits_calls<T> tc (svm);
+
+ if (!tc.find_ (static_cast<STS&> (*this), &l.id))
throw object_not_persistent ();
// Our find_() version delays result freeing.
@@ -96,13 +100,16 @@ namespace odb
// loads being added to the delayed_ vector. We need to process
// those before we call the post callback.
//
- object_traits::init (*l.obj, image (), &db);
+ tc.init (*l.obj, image (), &db);
find_->stream_result ();
ar.free ();
- object_traits::load_ (*this, *l.obj); // Load containers, etc.
+
+ // Load containers, etc.
+ //
+ tc.load_ (static_cast<STS&> (*this), *l.obj, false);
if (!delayed_.empty ())
- load_delayed_ ();
+ 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
@@ -118,7 +125,7 @@ namespace odb
}
}
else
- l.loader (db, l.id, *l.obj);
+ l.loader (db, l.id, *l.obj, svm);
pointer_cache_traits::load (ig.position ());
ig.release ();
diff --git a/odb/mssql/statement-cache.hxx b/odb/mssql/statement-cache.hxx
index 5754ba3..b09741f 100644
--- a/odb/mssql/statement-cache.hxx
+++ b/odb/mssql/statement-cache.hxx
@@ -29,7 +29,9 @@ namespace odb
class LIBODB_MSSQL_EXPORT statement_cache
{
public:
- statement_cache (connection& conn): conn_ (conn) {}
+ statement_cache (connection& conn)
+ : conn_ (conn),
+ version_seq_ (conn.database ().schema_version_sequence ()) {}
template <typename T>
typename object_traits_impl<T, id_mssql>::statements_type&
@@ -45,6 +47,7 @@ namespace odb
details::type_info_comparator> map;
connection& conn_;
+ unsigned int version_seq_;
map map_;
};
}
diff --git a/odb/mssql/statement-cache.txx b/odb/mssql/statement-cache.txx
index 5ab2918..7840227 100644
--- a/odb/mssql/statement-cache.txx
+++ b/odb/mssql/statement-cache.txx
@@ -2,6 +2,8 @@
// copyright : Copyright (c) 2005-2013 Code Synthesis Tools CC
// license : ODB NCUEL; see accompanying LICENSE file
+#include <odb/mssql/database.hxx>
+
namespace odb
{
namespace mssql
@@ -15,6 +17,16 @@ namespace odb
typename object_traits_impl<T, id_mssql>::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 ())
@@ -31,6 +43,9 @@ namespace odb
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 ())
diff --git a/odb/mssql/statement-processing.cxx b/odb/mssql/statement-processing.cxx
new file mode 100644
index 0000000..e591385
--- /dev/null
+++ b/odb/mssql/statement-processing.cxx
@@ -0,0 +1,348 @@
+// file : odb/mssql/statement-processing.cxx
+// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/statement-processing-common.hxx>
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+# include <iostream>
+#endif
+
+#include <odb/mssql/statement.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mssql
+ {
+ typedef bind bind_type;
+
+ void statement::
+ process_select (const char* s,
+ const bind_type* bind,
+ std::size_t bind_size,
+#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING
+ bool optimize,
+#else
+ bool,
+#endif
+ std::string& r)
+ {
+ // This implementation is pretty much the same as the generic one
+ // except for two things:
+ //
+ // 1. When checking for the fast case, take into account long data
+ // columns which we may have to re-arrange.
+ //
+ // 2. Create the column list in two passes, ordinary columns first
+ // followed by the long data columns.
+ //
+
+ bool empty (true); // Empty case (if none present).
+ bool fast (true); // Fast case (if all present and none are long data).
+ for (size_t i (0); i != bind_size && (empty || fast); ++i)
+ {
+ const bind_type& b (bind[i]);
+
+ if (b.buffer != 0)
+ empty = false;
+ else
+ fast = false;
+
+ if (b.type == bind_type::long_string ||
+ b.type == bind_type::long_nstring ||
+ b.type == bind_type::long_binary)
+ fast = false;
+ }
+
+ // Empty.
+ //
+ if (empty)
+ {
+ r.clear ();
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+ if (*s != '\0')
+ cerr << endl
+ << "old: '" << s << "'" << endl << endl
+ << "new: '" << r << "'" << endl << endl;
+#endif
+ return;
+ }
+
+ // Fast path: just remove the "structure".
+ //
+#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING
+ if (fast && !optimize)
+ {
+ process_fast (s, r);
+ return;
+ }
+#endif
+
+ // Scan the statement and store the positions of various parts.
+ //
+ size_t n (traits::length (s));
+ const char* e (s + n);
+
+ // Header.
+ //
+ const char* p (find (s, e, '\n'));
+ assert (p != 0);
+ size_t header_size (p - s);
+ p++;
+
+ // Column list.
+ //
+ const char* columns_begin (p);
+ for (const char* ce (comma_begin (p, e)); ce != 0; comma_next (p, ce, e))
+ ;
+
+ // FROM.
+ assert (traits::compare (p, "FROM ", 5) == 0);
+ const char* from_begin (p);
+ p = find (p, e, '\n'); // May not end with '\n'.
+ if (p == 0)
+ p = e;
+ size_t from_size (p - from_begin);
+ if (p != e)
+ p++;
+
+ // JOIN list.
+ //
+ const char* joins_begin (0), *joins_end (0);
+ if (e - p > 5 && traits::compare (p, "LEFT JOIN ", 10) == 0)
+ {
+ joins_begin = p;
+
+ // Find the end of the JOIN list.
+ //
+ for (const char* je (newline_begin (p, e));
+ je != 0; newline_next (p, je, e, "LEFT JOIN ", 10))
+ ;
+
+ joins_end = (p != e ? p - 1 : p);
+ }
+
+#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING
+ if (fast && joins_begin == 0)
+ {
+ // No JOINs to optimize so can still take the fast path.
+ //
+ process_fast (s, r);
+ return;
+ }
+#endif
+
+ // Trailer (WHERE, ORDER BY, etc).
+ //
+ const char* trailer_begin (0);
+ size_t trailer_size (0);
+ if (e - p != 0)
+ {
+ trailer_begin = p;
+ trailer_size = e - p;
+ }
+
+ // Assume the same size as the original. It can only shrink, and in
+ // most cases only slightly. So this is a good approximation.
+ //
+ r.reserve (n);
+ r.assign (s, header_size);
+
+ // Column list.
+ //
+ {
+ r += ' ';
+
+ size_t i (0);
+ bool need_second (false);
+
+ // First pass: non-long data columns.
+ //
+ {
+ size_t bi (0);
+ for (const char *c (columns_begin), *ce (comma_begin (c, e));
+ ce != 0; comma_next (c, ce, e))
+ {
+ const bind_type& b (bind[bi++]);
+
+ // See if the column is present in the bind array and if it
+ // is of the right kind.
+ //
+ if (b.buffer == 0)
+ continue;
+
+ if (b.type == bind_type::long_string ||
+ b.type == bind_type::long_nstring ||
+ b.type == bind_type::long_binary)
+ {
+ need_second = true;
+ continue;
+ }
+
+ // Append the column.
+ //
+ if (i++ != 0)
+ r += ", "; // Add the space for consistency with the fast path.
+
+ r.append (c, ce - c);
+ }
+ }
+
+ // Second pass: long data columns.
+ //
+ if (need_second)
+ {
+ size_t bi (0);
+ for (const char *c (columns_begin), *ce (comma_begin (c, e));
+ ce != 0; comma_next (c, ce, e))
+ {
+ const bind_type& b (bind[bi++]);
+
+ // See if the column is present in the bind array and if it
+ // is of the right kind.
+ //
+ if (b.buffer == 0 ||
+ (b.type != bind_type::long_string &&
+ b.type != bind_type::long_nstring &&
+ b.type != bind_type::long_binary))
+ continue;
+
+ // Append the column.
+ //
+ if (i++ != 0)
+ r += ", "; // Add the space for consistency with the fast path.
+
+ r.append (c, ce - c);
+ }
+ }
+ }
+
+ // From.
+ //
+ r += ' ';
+ r.append (from_begin, from_size);
+
+ // JOIN list, pass 1.
+ //
+ size_t join_pos (0);
+ if (joins_begin != 0)
+ {
+ // Fill in the JOIN "area" with spaces.
+ //
+ r.resize (r.size () + joins_end - joins_begin + 1, ' ');
+ join_pos = r.size () + 1; // End of the last JOIN.
+ }
+
+ // Trailer.
+ //
+ if (trailer_size != 0)
+ {
+ r += ' ';
+ r.append (trailer_begin, trailer_size);
+ }
+
+ // JOIN list, pass 2.
+ //
+ if (joins_begin != 0)
+ {
+ // Splice the JOINs into the pre-allocated area.
+ //
+ for (const char* je (joins_end), *j (newline_rbegin (je, joins_begin));
+ j != 0; newline_rnext (j, je, joins_begin))
+ {
+ size_t n (je - j);
+
+ // Get the alias or, if none used, the table name.
+ //
+ p = find (j + 10, je, ' '); // 10 for "LEFT JOIN ".
+ const char* table_end (p);
+ p++; // Skip space.
+
+ // We may or may not have the AS keyword.
+ //
+ const char* alias_begin (0);
+ size_t alias_size (0);
+ if (je - p > 3 && traits::compare (p, "ON ", 3) != 0)
+ {
+ p += 3;
+ alias_begin = p;
+ alias_size = find (p, je, ' ') - alias_begin;
+ }
+ else
+ {
+ alias_begin = j + 10;
+ alias_size = table_end - alias_begin;
+ }
+
+ // The alias must be quoted.
+ //
+ assert (*alias_begin == '[' &&
+ *(alias_begin + alias_size - 1) == ']');
+
+ // We now need to see if the alias is used in either the SELECT
+ // list, the WHERE conditions, or the ON condition of any of the
+ // JOINs that we have already processed and decided to keep.
+ //
+ // Instead of re-parsing the whole thing again, we are going to
+ // take a shortcut and simply search for the alias in the statement
+ // we have constructed so far (that's why we have have added the
+ // trailer before filling in the JOINs). To make it more robust,
+ // we are going to do a few extra sanity checks, specifically,
+ // that the alias is a top level identifier and is followed by
+ // only a single identifer (column). This will catch cases like
+ // [s].[t].[c] where [s] is also used as an alias or LEFT JOIN [t]
+ // where [t] is also used as an alias in another JOIN.
+ //
+ bool found (false);
+ for (size_t p (r.find (alias_begin, 0, alias_size));
+ p != string::npos;
+ p = r.find (alias_begin, p + alias_size, alias_size))
+ {
+ size_t e (p + alias_size);
+
+ // If we are not a top-level qualifier or not a bottom-level,
+ // then we are done (3 is for at least "[a]").
+ //
+ if ((p != 0 && r[p - 1] == '.') ||
+ (e + 3 >= r.size () || (r[e] != '.' || r[e + 1] != '[')))
+ continue;
+
+ // The only way to distinguish the [a].[c] from FROM [a].[c] or
+ // LEFT JOIN [a].[c] is by checking the prefix.
+ //
+ if ((p > 5 && r.compare (p - 5, 5, "FROM ") == 0) ||
+ (p > 10 && r.compare (p - 10, 10, "LEFT JOIN ") == 0))
+ continue;
+
+ // Check that we are followed by a single identifier.
+ //
+ e = r.find (']', e + 2);
+ if (e == string::npos || (e + 1 != r.size () && r[e + 1] == '.'))
+ continue;
+
+ found = true;
+ break;
+ }
+
+ join_pos -= n + 1; // Extra one for space.
+ if (found)
+ r.replace (join_pos, n, j, n);
+ else
+ r.erase (join_pos - 1, n + 1); // Extra one for space.
+ }
+ }
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+ if (r.size () != n)
+ cerr << endl
+ << "old: '" << s << "'" << endl << endl
+ << "new: '" << r << "'" << endl << endl;
+#endif
+ }
+ }
+}
diff --git a/odb/mssql/statement.cxx b/odb/mssql/statement.cxx
index 4283b55..e2022fa 100644
--- a/odb/mssql/statement.cxx
+++ b/odb/mssql/statement.cxx
@@ -95,19 +95,36 @@ namespace odb
//
statement::
- statement (connection_type& conn, const string& text)
- : conn_ (conn), text_copy_ (text), text_ (text_copy_.c_str ())
+ statement (connection_type& conn,
+ const string& text,
+ statement_kind sk,
+ const binding* process,
+ bool optimize)
+ : conn_ (conn)
{
- init (text_copy_.size ());
+ if (process == 0)
+ {
+ text_copy_ = text;
+ text_ = text_copy_.c_str ();
+ }
+ else
+ text_ = text.c_str (); // Temporary, see init().
+
+ init (text.size (), sk, process, optimize);
}
statement::
- statement (connection_type& conn, const char* text, bool copy)
+ statement (connection_type& conn,
+ const char* text,
+ statement_kind sk,
+ const binding* process,
+ bool optimize,
+ bool copy)
: conn_ (conn)
{
size_t n;
- if (copy)
+ if (process == 0 && copy)
{
text_copy_ = text;
text_ = text_copy_.c_str ();
@@ -116,15 +133,53 @@ namespace odb
else
{
text_ = text;
- n = strlen (text_);
+ n = strlen (text_); // Potentially temporary, see init().
}
- init (n);
+ init (n, sk, process, optimize);
}
void statement::
- init (size_t text_size)
+ init (size_t text_size,
+ statement_kind sk,
+ const binding* proc,
+ bool optimize)
{
+ if (proc != 0)
+ {
+ switch (sk)
+ {
+ case statement_select:
+ process_select (text_,
+ proc->bind, proc->count,
+ optimize,
+ text_copy_);
+ break;
+ case statement_insert:
+ process_insert (text_,
+ &proc->bind->buffer, proc->count, sizeof (bind),
+ '?',
+ text_copy_);
+ break;
+ case statement_update:
+ process_update (text_,
+ &proc->bind->buffer, proc->count, sizeof (bind),
+ '?',
+ text_copy_);
+ break;
+ case statement_delete:
+ assert (false);
+ }
+
+ text_ = text_copy_.c_str ();
+ text_size = text_copy_.size ();
+ }
+
+ // Empty statement.
+ //
+ if (*text_ == '\0')
+ return;
+
SQLRETURN r;
// Allocate the handle.
@@ -188,10 +243,14 @@ namespace odb
{
SQLRETURN r;
- n++; // Parameters are counted from 1.
-
- for (size_t i (1); i < n; ++i, ++b)
+ SQLUSMALLINT i (0);
+ for (bind* end (b + n); b != end; ++b)
{
+ if (b->buffer == 0) // Skip NULL entries.
+ continue;
+
+ i++; // Column index is 1-based.
+
SQLULEN col_size (0);
SQLSMALLINT digits (0);
SQLPOINTER buf;
@@ -342,7 +401,7 @@ namespace odb
r = SQLBindParameter (
stmt_,
- (SQLUSMALLINT) i,
+ i,
SQL_PARAM_INPUT,
c_type_lookup[b->type],
sql_type_lookup[b->type],
@@ -357,14 +416,18 @@ namespace odb
}
}
- size_t statement::
- bind_result (bind* b, size_t n)
+ SQLUSMALLINT statement::
+ bind_result (bind* b, size_t n, SQLUSMALLINT& long_count)
{
+ long_count = 0;
SQLRETURN r;
- size_t i (0);
+ SQLUSMALLINT i (0);
for (bind* end (b + n); b != end; ++b)
{
+ if (b->buffer == 0) // Skip NULL entries.
+ continue;
+
SQLLEN cap (0);
switch (b->type)
@@ -428,6 +491,7 @@ namespace odb
{
// Long data is not bound.
//
+ long_count++;
continue;
}
case bind::date:
@@ -468,7 +532,7 @@ namespace odb
}
r = SQLBindCol (stmt_,
- (SQLUSMALLINT) (i + 1), // Results are counted from 1.
+ ++i, // Column index is 1-based.
c_type_lookup[b->type],
(SQLPOINTER) b->buffer,
cap,
@@ -476,8 +540,6 @@ namespace odb
if (!SQL_SUCCEEDED (r))
translate_error (r, conn_, stmt_);
-
- ++i;
}
return i;
@@ -557,7 +619,7 @@ namespace odb
}
void statement::
- stream_result (bind* b, size_t i, size_t n, void* obase, void* nbase)
+ stream_result (SQLUSMALLINT i, bind* b, size_t n, void* obase, void* nbase)
{
details::buffer& tmp_buf (conn_.long_data_buffer ());
@@ -568,8 +630,10 @@ namespace odb
for (bind* end (b + n); b != end; ++b)
{
- bool char_data;
+ if (b->buffer == 0) // Skip NULL entries.
+ continue;
+ bool char_data;
switch (b->type)
{
case bind::long_string:
@@ -612,7 +676,7 @@ namespace odb
//
SQLLEN si;
r = SQLGetData (stmt_,
- (SQLUSMALLINT) (i + 1),
+ ++i,
c_type_lookup[b->type],
tmp_buf.data (), // Dummy value.
0,
@@ -655,7 +719,7 @@ namespace odb
// contain a valid value.
//
r = SQLGetData (stmt_,
- (SQLUSMALLINT) (i + 1),
+ i,
c_type_lookup[b->type],
(SQLPOINTER) buf,
(SQLLEN) size,
@@ -686,8 +750,6 @@ namespace odb
if (size_left != 0)
size_left -= size;
}
-
- ++i;
}
}
@@ -701,42 +763,74 @@ namespace odb
select_statement::
select_statement (connection_type& conn,
- const string& t,
+ const string& text,
+ bool process,
+ bool optimize,
binding& param,
binding& result)
- : statement (conn, t), result_ (result)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ result_ (result)
{
- bind_param (param.bind, param.count);
- first_long_ = bind_result (result.bind, result.count);
+ if (!empty ())
+ {
+ bind_param (param.bind, param.count);
+ result_count_ = bind_result (result.bind, result.count, long_count_);
+ }
}
select_statement::
select_statement (connection_type& conn,
- const char* t,
+ const char* text,
+ bool process,
+ bool optimize,
binding& param,
binding& result,
- bool ct)
- : statement (conn, t, ct), result_ (result)
+ bool copy_text)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize,
+ copy_text),
+ result_ (result)
{
- bind_param (param.bind, param.count);
- first_long_ = bind_result (result.bind, result.count);
+ if (!empty ())
+ {
+ bind_param (param.bind, param.count);
+ result_count_ = bind_result (result.bind, result.count, long_count_);
+ }
}
select_statement::
- select_statement (connection_type& conn, const string& t, binding& result)
- : statement (conn, t), result_ (result)
+ select_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ bool optimize,
+ binding& result)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ result_ (result)
{
- first_long_ = bind_result (result.bind, result.count);
+ if (!empty ())
+ result_count_ = bind_result (result.bind, result.count, long_count_);
}
select_statement::
select_statement (connection_type& conn,
- const char* t,
+ const char* text,
+ bool process,
+ bool optimize,
binding& result,
- bool ct)
- : statement (conn, t, ct), result_ (result)
+ bool copy_text)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize,
+ copy_text),
+ result_ (result)
{
- first_long_ = bind_result (result.bind, result.count);
+ if (!empty ())
+ result_count_ = bind_result (result.bind, result.count, long_count_);
}
void select_statement::
@@ -746,6 +840,21 @@ namespace odb
if (!SQL_SUCCEEDED (r))
translate_error (r, conn_, stmt_);
+
+#ifndef NDEBUG
+ SQLSMALLINT cols;
+ r = SQLNumResultCols (stmt_, &cols);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ // 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 (static_cast<SQLUSMALLINT> (cols) == result_count_ + long_count_);
+#endif
}
select_statement::result select_statement::
@@ -793,11 +902,14 @@ namespace odb
insert_statement::
insert_statement (connection_type& conn,
- const string& t,
+ const string& text,
+ bool process,
binding& param,
bool returning_id,
bool returning_version)
- : statement (conn, t),
+ : statement (conn,
+ text, statement_insert,
+ (process ? &param : 0), false),
returning_id_ (returning_id),
returning_version_ (returning_version)
{
@@ -809,12 +921,16 @@ namespace odb
insert_statement::
insert_statement (connection_type& conn,
- const char* t,
+ const char* text,
+ bool process,
binding& param,
bool returning_id,
bool returning_version,
- bool ct)
- : statement (conn, t, ct),
+ bool copy_text)
+ : statement (conn,
+ text, statement_insert,
+ (process ? &param : 0), false,
+ copy_text),
returning_id_ (returning_id),
returning_version_ (returning_version)
{
@@ -980,30 +1096,44 @@ namespace odb
update_statement::
update_statement (connection_type& conn,
- const string& t,
+ const string& text,
+ bool process,
binding& param,
bool returning_version)
- : statement (conn, t), returning_version_ (returning_version)
+ : statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false),
+ returning_version_ (returning_version)
{
- bind_param (param.bind, param.count);
+ if (!empty ())
+ {
+ bind_param (param.bind, param.count);
- if (returning_version_)
- init_result ();
+ if (returning_version_)
+ init_result ();
+ }
}
update_statement::
update_statement (connection_type& conn,
- const char* t,
+ const char* text,
+ bool process,
binding& param,
bool returning_version,
- bool ct)
- : statement (conn, t, ct),
+ bool copy_text)
+ : statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false,
+ copy_text),
returning_version_ (returning_version)
{
- bind_param (param.bind, param.count);
+ if (!empty ())
+ {
+ bind_param (param.bind, param.count);
- if (returning_version_)
- init_result ();
+ if (returning_version_)
+ init_result ();
+ }
}
void update_statement::
@@ -1080,18 +1210,25 @@ namespace odb
}
delete_statement::
- delete_statement (connection_type& conn, const string& t, binding& param)
- : statement (conn, t)
+ delete_statement (connection_type& conn,
+ const string& text,
+ binding& param)
+ : statement (conn,
+ text, statement_delete,
+ 0, false)
{
bind_param (param.bind, param.count);
}
delete_statement::
delete_statement (connection_type& conn,
- const char* t,
+ const char* text,
binding& param,
- bool ct)
- : statement (conn, t, ct)
+ bool copy_text)
+ : statement (conn,
+ text, statement_delete,
+ 0, false,
+ copy_text)
{
bind_param (param.bind, param.count);
}
diff --git a/odb/mssql/statement.hxx b/odb/mssql/statement.hxx
index add996c..1c012ce 100644
--- a/odb/mssql/statement.hxx
+++ b/odb/mssql/statement.hxx
@@ -50,20 +50,58 @@ namespace odb
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:
- statement (connection_type&, const std::string& text);
- statement (connection_type&, const char* text, bool copy_text);
+ // 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,
+ bool copy_text);
private:
void
- init (std::size_t text_size);
+ init (std::size_t text_size,
+ statement_kind,
+ const binding* process,
+ bool optimize);
+
+ // Custom implementation for SQL Server that also moves long data
+ // columns to the end.
+ //
+ static void
+ process_select (const char* statement,
+ const bind*,
+ std::size_t bind_size,
+ bool optimize,
+ std::string& result);
protected:
void
bind_param (bind*, std::size_t count);
- std::size_t
- bind_result (bind*, std::size_t count);
+ // Return the actual number of columns bound.
+ //
+ SQLUSMALLINT
+ bind_result (bind*, std::size_t count, SQLUSMALLINT& long_count);
SQLRETURN
execute ();
@@ -75,8 +113,8 @@ namespace odb
// instead of the bound image.
//
void
- stream_result (bind*,
- std::size_t start,
+ stream_result (SQLUSMALLINT start_col,
+ bind*,
std::size_t count,
void* old_base = 0,
void* new_base = 0);
@@ -100,21 +138,29 @@ namespace odb
//
select_statement (connection_type& conn,
const std::string& text,
+ bool process_text,
+ bool optimize_text,
binding& param,
binding& result);
select_statement (connection_type& conn,
const char* text,
+ bool process_text,
+ bool optimize_text,
binding& param,
binding& result,
bool copy_text = true);
select_statement (connection_type& conn,
const std::string& text,
+ bool process_text,
+ bool optimize_text,
binding& result);
select_statement (connection_type& conn,
const char* text,
+ bool process_text,
+ bool optimize_text,
binding& result,
bool copy_text = true);
@@ -135,15 +181,13 @@ namespace odb
bool
stream_result (void* old_base = 0, void* new_base = 0)
{
- bool ld (first_long_ != result_.count);
-
- if (ld)
- statement::stream_result (result_.bind,
- first_long_,
+ if (long_count_ != 0)
+ statement::stream_result (result_count_,
+ result_.bind,
result_.count,
old_base,
new_base);
- return ld;
+ return long_count_ != 0;
}
void
@@ -155,7 +199,8 @@ namespace odb
private:
binding& result_;
- std::size_t first_long_; // First long data column.
+ SQLUSMALLINT result_count_; // Actual number of columns bound.
+ SQLUSMALLINT long_count_; // Number of long data columns.
};
struct LIBODB_MSSQL_EXPORT auto_result
@@ -199,12 +244,14 @@ namespace odb
insert_statement (connection_type& conn,
const std::string& text,
+ bool process_text,
binding& param,
bool returning_id,
bool returning_version);
insert_statement (connection_type& conn,
const char* text,
+ bool process_text,
binding& param,
bool returning_id,
bool returning_version,
@@ -253,11 +300,13 @@ namespace odb
update_statement (connection_type& conn,
const std::string& text,
+ bool process,
binding& param,
bool returning_version);
update_statement (connection_type& conn,
const char* text,
+ bool process,
binding& param,
bool returning_version,
bool copy_text = true);
diff --git a/odb/mssql/traits-calls.hxx b/odb/mssql/traits-calls.hxx
new file mode 100644
index 0000000..88665f7
--- /dev/null
+++ b/odb/mssql/traits-calls.hxx
@@ -0,0 +1,191 @@
+// file : odb/mssql/traits-calls.hxx
+// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_TRAITS_CALLS_HXX
+#define ODB_MSSQL_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/mssql/forward.hxx>
+#include <odb/mssql/mssql-types.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ // object_traits_calls
+ //
+
+ template <typename T,
+ bool versioned = object_traits_impl<T, id_mssql>::versioned>
+ struct object_traits_calls;
+
+ template <typename T>
+ struct object_traits_calls<T, false>
+ {
+ typedef object_traits_impl<T, id_mssql> traits;
+ typedef typename traits::image_type image_type;
+ typedef mssql::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_mssql> traits;
+ typedef typename traits::image_type image_type;
+ typedef mssql::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_mssql>::versioned>
+ struct view_traits_calls;
+
+ template <typename T>
+ struct view_traits_calls<T, false>
+ {
+ typedef view_traits_impl<T, id_mssql> traits;
+ typedef typename traits::image_type image_type;
+ typedef mssql::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_mssql> traits;
+ typedef typename traits::image_type image_type;
+ typedef mssql::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_MSSQL_TRAITS_CALLS_HXX
diff --git a/odb/mssql/view-result.hxx b/odb/mssql/view-result.hxx
index 0d96ce0..472d2d4 100644
--- a/odb/mssql/view-result.hxx
+++ b/odb/mssql/view-result.hxx
@@ -9,6 +9,7 @@
#include <cstddef> // std::size_t
+#include <odb/schema-version.hxx>
#include <odb/view-result.hxx>
#include <odb/details/shared-ptr.hxx>
@@ -16,6 +17,7 @@
#include <odb/mssql/version.hxx>
#include <odb/mssql/forward.hxx> // query_base, view_statements
#include <odb/mssql/statement.hxx>
+#include <odb/mssql/traits-calls.hxx>
namespace odb
{
@@ -40,7 +42,8 @@ namespace odb
view_result_impl (const query_base&,
details::shared_ptr<select_statement>,
- statements_type&);
+ statements_type&,
+ const schema_version_migration*);
virtual void
load (view_type&);
@@ -68,6 +71,7 @@ namespace odb
private:
details::shared_ptr<select_statement> statement_;
statements_type& statements_;
+ view_traits_calls<view_type> tc_;
bool can_load_;
bool use_copy_;
typename view_traits::image_type* image_copy_;
diff --git a/odb/mssql/view-result.txx b/odb/mssql/view-result.txx
index 4842c68..641f57e 100644
--- a/odb/mssql/view-result.txx
+++ b/odb/mssql/view-result.txx
@@ -47,10 +47,12 @@ namespace odb
view_result_impl<T>::
view_result_impl (const query_base&,
details::shared_ptr<select_statement> statement,
- statements_type& statements)
+ statements_type& statements,
+ const schema_version_migration* svm)
: base_type (statements.connection ()),
statement_ (statement),
statements_ (statements),
+ tc_ (svm),
use_copy_ (false),
image_copy_ (0)
{
@@ -65,9 +67,9 @@ namespace odb
view_traits::callback (this->db_, view, callback_event::pre_load);
- view_traits::init (view,
- use_copy_ ? *image_copy_ : statements_.image (),
- &this->db_);
+ tc_.init (view,
+ use_copy_ ? *image_copy_ : statements_.image (),
+ &this->db_);
// If we are using a copy, make sure the callback information for
// long data also comes from the copy.
@@ -100,7 +102,7 @@ namespace odb
if (im.version != statements_.image_version ())
{
binding& b (statements_.image_binding ());
- view_traits::bind (b.bind, im);
+ tc_.bind (b.bind, im);
statements_.image_version (im.version);
b.version++;
}