From a3372ca4e3eb1ba4e87dfa9ccb0c78c379935441 Mon Sep 17 00:00:00 2001
From: Boris Kolpackov <boris@codesynthesis.com>
Date: Tue, 10 Sep 2013 14:10:45 +0200
Subject: Schema versioning support

---
 odb/oracle/container-statements.hxx          |  25 ++-
 odb/oracle/container-statements.txx          |   5 +-
 odb/oracle/database.cxx                      |   7 +-
 odb/oracle/forward.hxx                       |   4 +-
 odb/oracle/no-id-object-result.hxx           |   6 +-
 odb/oracle/no-id-object-result.txx           |  12 +-
 odb/oracle/no-id-object-statements.hxx       |   1 +
 odb/oracle/polymorphic-object-result.hxx     |   6 +-
 odb/oracle/polymorphic-object-result.txx     |  25 +--
 odb/oracle/polymorphic-object-statements.hxx |  23 ++-
 odb/oracle/polymorphic-object-statements.txx |  16 +-
 odb/oracle/query.cxx                         |  16 +-
 odb/oracle/section-statements.hxx            |   3 +
 odb/oracle/simple-object-result.hxx          |   6 +-
 odb/oracle/simple-object-result.txx          |  12 +-
 odb/oracle/simple-object-statements.hxx      |  21 ++-
 odb/oracle/simple-object-statements.txx      |  19 ++-
 odb/oracle/statement-cache.hxx               |   5 +-
 odb/oracle/statement-cache.txx               |  15 ++
 odb/oracle/statement.cxx                     | 223 +++++++++++++++++++++------
 odb/oracle/statement.hxx                     |  55 ++++++-
 odb/oracle/traits-calls.hxx                  | 191 +++++++++++++++++++++++
 odb/oracle/view-result.hxx                   |   6 +-
 odb/oracle/view-result.txx                   |  12 +-
 24 files changed, 599 insertions(+), 115 deletions(-)
 create mode 100644 odb/oracle/traits-calls.hxx

diff --git a/odb/oracle/container-statements.hxx b/odb/oracle/container-statements.hxx
index 4da7af4..ef1733d 100644
--- a/odb/oracle/container-statements.hxx
+++ b/odb/oracle/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/oracle/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,11 @@ namespace odb
         if (insert_ == 0)
           insert_.reset (
             new (details::shared) insert_statement_type (
-              conn_, insert_text_, insert_image_binding_, false));
+              conn_,
+              insert_text_,
+              versioned_,   // Process if versioned.
+              insert_image_binding_,
+              false));
 
         return *insert_;
       }
@@ -122,6 +135,8 @@ namespace odb
             new (details::shared) select_statement_type (
               conn_,
               select_text_,
+              versioned_,   // Process if versioned.
+              false,        // Don't optimize.
               id_binding_,
               select_image_binding_,
               4096));  // Hardcode a 4kB LOB prefetch size.
@@ -161,6 +176,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_;
@@ -260,7 +278,10 @@ namespace odb
         if (update_ == 0)
           update_.reset (
             new (details::shared) update_statement_type (
-              this->conn_, update_text_, update_image_binding_));
+              this->conn_,
+              update_text_,
+              this->versioned_, // Process if versioned.
+              update_image_binding_));
 
         return *update_;
       }
diff --git a/odb/oracle/container-statements.txx b/odb/oracle/container-statements.txx
index 814414a..f872c05 100644
--- a/odb/oracle/container-statements.txx
+++ b/odb/oracle/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/oracle/database.cxx b/odb/oracle/database.cxx
index 60dc4f4..a16ac82 100644
--- a/odb/oracle/database.cxx
+++ b/odb/oracle/database.cxx
@@ -309,7 +309,12 @@ namespace odb
 
       try
       {
-        select_statement st (c, text, param, result);
+        select_statement st (c,
+                             text,
+                             false, // Don't process.
+                             false, // Don't optimize.
+                             param,
+                             result);
         st.execute ();
         auto_result ar (st);
 
diff --git a/odb/oracle/forward.hxx b/odb/oracle/forward.hxx
index 6163fdf..1f7fafb 100644
--- a/odb/oracle/forward.hxx
+++ b/odb/oracle/forward.hxx
@@ -43,7 +43,9 @@ namespace odb
     {
       statement_select,
       statement_insert,
-      statement_update
+      statement_update,
+      statement_delete,
+      statement_generic
     };
 
     class binding;
diff --git a/odb/oracle/no-id-object-result.hxx b/odb/oracle/no-id-object-result.hxx
index 150db3d..f3a695b 100644
--- a/odb/oracle/no-id-object-result.hxx
+++ b/odb/oracle/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/oracle/version.hxx>
 #include <odb/oracle/forward.hxx> // query_base
 #include <odb/oracle/statement.hxx>
+#include <odb/oracle/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 use_copy_;
       typename object_traits::image_type* image_copy_;
     };
diff --git a/odb/oracle/no-id-object-result.txx b/odb/oracle/no-id-object-result.txx
index c70661c..c58e0af 100644
--- a/odb/oracle/no-id-object-result.txx
+++ b/odb/oracle/no-id-object-result.txx
@@ -46,10 +46,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)
     {
@@ -61,9 +63,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
       // LOB data also comes from the copy.
@@ -95,7 +97,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/oracle/no-id-object-statements.hxx b/odb/oracle/no-id-object-statements.hxx
index 0510283..0be8dac 100644
--- a/odb/oracle/no-id-object-statements.hxx
+++ b/odb/oracle/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));
 
diff --git a/odb/oracle/polymorphic-object-result.hxx b/odb/oracle/polymorphic-object-result.hxx
index f23a199..6879645 100644
--- a/odb/oracle/polymorphic-object-result.hxx
+++ b/odb/oracle/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/oracle/version.hxx>
 #include <odb/oracle/forward.hxx> // query_base
 #include <odb/oracle/statement.hxx>
+#include <odb/oracle/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 use_copy_;
       image_type* image_copy_;
     };
diff --git a/odb/oracle/polymorphic-object-result.txx b/odb/oracle/polymorphic-object-result.txx
index a67dce4..19b4fa3 100644
--- a/odb/oracle/polymorphic-object-result.txx
+++ b/odb/oracle/polymorphic-object-result.txx
@@ -52,10 +52,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)
     {
@@ -133,7 +135,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
       // LOB data also comes from the copy.
@@ -156,7 +158,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.
@@ -167,7 +169,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;
@@ -210,14 +212,16 @@ namespace odb
       typedef object_traits_impl<T, id_oracle> 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 ());
         }
@@ -242,14 +246,16 @@ namespace odb
       typedef object_traits_impl<R, id_oracle> 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++;
         }
@@ -278,7 +284,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/oracle/polymorphic-object-statements.hxx b/odb/oracle/polymorphic-object-statements.hxx
index 3a3cceb..81ea3c3 100644
--- a/odb/oracle/polymorphic-object-statements.hxx
+++ b/odb/oracle/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_,
               0)); // No LOB prefetch (discriminator cannot be LOB).
@@ -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.
@@ -287,6 +304,7 @@ namespace odb
             new (details::shared) insert_statement_type (
               conn_,
               object_traits::persist_statement,
+              object_traits::versioned, // Process if versioned.
               insert_image_binding_,
               false));
 
@@ -304,6 +322,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],
               4096));  // Hardcode a 4kB LOB prefetch size.
@@ -319,6 +339,7 @@ namespace odb
             new (details::shared) update_statement_type (
               conn_,
               object_traits::update_statement,
+              object_traits::versioned, // Process if versioned.
               update_image_binding_));
 
         return *update_;
diff --git a/odb/oracle/polymorphic-object-statements.txx b/odb/oracle/polymorphic-object-statements.txx
index 83e88de..54ea3a8 100644
--- a/odb/oracle/polymorphic-object-statements.txx
+++ b/odb/oracle/polymorphic-object-statements.txx
@@ -10,6 +10,7 @@
 #include <odb/oracle/connection.hxx>
 #include <odb/oracle/transaction.hxx>
 #include <odb/oracle/statement-cache.hxx>
+#include <odb/oracle/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,15 +117,17 @@ 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 ();
 
       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 ();
-      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/oracle/query.cxx b/odb/oracle/query.cxx
index 9fbdc63..439d2c7 100644
--- a/odb/oracle/query.cxx
+++ b/odb/oracle/query.cxx
@@ -99,8 +99,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;
@@ -184,7 +184,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;
       }
 
@@ -244,7 +244,7 @@ namespace odb
         {
         case clause_part::kind_column:
           {
-            if (last != ' ' && last != '(')
+            if (last != ' ' && last != '\n' && last != '(')
               r += ' ';
 
             r += i->part;
@@ -252,7 +252,7 @@ namespace odb
           }
         case clause_part::kind_param:
           {
-            if (last != ' ' && last != '(')
+            if (last != ' ' && last != '\n' && last != '(')
               r += ' ';
 
             ostringstream os;
@@ -283,8 +283,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;
@@ -292,7 +292,7 @@ namespace odb
           }
         case clause_part::kind_bool:
           {
-            if (last != ' ' && last != '(')
+            if (last != ' ' && last != '\n' && last != '(')
               r += ' ';
 
             // Oracle does not have TRUE and FALSE boolean literals (these
diff --git a/odb/oracle/section-statements.hxx b/odb/oracle/section-statements.hxx
index 8579be5..47e3bb7 100644
--- a/odb/oracle/section-statements.hxx
+++ b/odb/oracle/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_,
               4096));  // Hardcode a 4kB LOB prefetch size.
@@ -112,6 +114,7 @@ namespace odb
             new (details::shared) update_statement_type (
               conn_,
               traits::update_statement,
+              traits::versioned, // Process if versioned.
               update_image_binding_));
 
         return *update_;
diff --git a/odb/oracle/simple-object-result.hxx b/odb/oracle/simple-object-result.hxx
index 19837e7..6ee56ba 100644
--- a/odb/oracle/simple-object-result.hxx
+++ b/odb/oracle/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/oracle/version.hxx>
 #include <odb/oracle/forward.hxx> // query_base
 #include <odb/oracle/statement.hxx>
+#include <odb/oracle/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 use_copy_;
       typename object_traits::image_type* image_copy_;
     };
diff --git a/odb/oracle/simple-object-result.txx b/odb/oracle/simple-object-result.txx
index 53d752d..114c919 100644
--- a/odb/oracle/simple-object-result.txx
+++ b/odb/oracle/simple-object-result.txx
@@ -48,10 +48,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)
     {
@@ -71,7 +73,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
       // LOB data also comes from the copy.
@@ -94,8 +96,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);
     }
@@ -129,7 +131,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/oracle/simple-object-statements.hxx b/odb/oracle/simple-object-statements.hxx
index 17e6085..ff76e91 100644
--- a/odb/oracle/simple-object-statements.hxx
+++ b/odb/oracle/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));
 
@@ -363,6 +366,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_,
               4096));  // Hardcode a 4kB LOB prefetch size.
@@ -378,6 +383,7 @@ namespace odb
             new (details::shared) update_statement_type (
               conn_,
               object_traits::update_statement,
+              object_traits::versioned, // Process if versioned.
               update_image_binding_));
 
         return *update_;
@@ -452,14 +458,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/oracle/simple-object-statements.txx b/odb/oracle/simple-object-statements.txx
index 6723b1c..61bbc5f 100644
--- a/odb/oracle/simple-object-statements.txx
+++ b/odb/oracle/simple-object-statements.txx
@@ -8,6 +8,7 @@
 #include <odb/exceptions.hxx>
 
 #include <odb/oracle/connection.hxx>
+#include <odb/oracle/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 ();
 
           object_traits::callback (db, *l.obj, callback_event::pre_load);
@@ -92,12 +96,15 @@ 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 ();
-          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
@@ -113,7 +120,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/oracle/statement-cache.hxx b/odb/oracle/statement-cache.hxx
index 2795fa4..10f08ea 100644
--- a/odb/oracle/statement-cache.hxx
+++ b/odb/oracle/statement-cache.hxx
@@ -29,7 +29,9 @@ namespace odb
     class LIBODB_ORACLE_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_oracle>::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/oracle/statement-cache.txx b/odb/oracle/statement-cache.txx
index a593674..ba747e2 100644
--- a/odb/oracle/statement-cache.txx
+++ b/odb/oracle/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/oracle/database.hxx>
+
 namespace odb
 {
   namespace oracle
@@ -15,6 +17,16 @@ namespace odb
       typename object_traits_impl<T, id_oracle>::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/oracle/statement.cxx b/odb/oracle/statement.cxx
index 13b9553..5ad867a 100644
--- a/odb/oracle/statement.cxx
+++ b/odb/oracle/statement.cxx
@@ -137,6 +137,9 @@ namespace odb
     statement::
     ~statement ()
     {
+      if (empty ())
+        return;
+
       {
         odb::tracer* t;
         if ((t = conn_.transaction_tracer ()) ||
@@ -192,22 +195,73 @@ namespace odb
     }
 
     statement::
-    statement (connection_type& conn, const string& text)
+    statement (connection_type& conn,
+               const string& text,
+               statement_kind sk,
+               const binding* process,
+               bool optimize)
         : conn_ (conn), udata_ (0), usize_ (0)
     {
-      init (text.c_str (), text.size ());
+      init (text.c_str (), text.size (), sk, process, optimize);
     }
 
     statement::
-    statement (connection_type& conn, const char* text)
+    statement (connection_type& conn,
+               const char* text,
+               statement_kind sk,
+               const binding* process,
+               bool optimize)
         : conn_ (conn), udata_ (0), usize_ (0)
     {
-      init (text, strlen (text));
+      init (text, strlen (text), sk, process, optimize);
     }
 
     void statement::
-    init (const char* text, size_t text_size)
+    init (const char* text,
+          size_t text_size,
+          statement_kind sk,
+          const binding* proc,
+          bool optimize)
     {
+      string tmp;
+      if (proc != 0)
+      {
+        switch (sk)
+        {
+        case statement_select:
+          process_select (text,
+                          &proc->bind->buffer, proc->count, sizeof (bind),
+                          '"', '"',
+                          optimize,
+                          tmp,
+                          false); // No AS in JOINs.
+          break;
+        case statement_insert:
+          process_insert (text,
+                          &proc->bind->buffer, proc->count, sizeof (bind),
+                          ':',
+                          tmp);
+          break;
+        case statement_update:
+          process_update (text,
+                          &proc->bind->buffer, proc->count, sizeof (bind),
+                          ':',
+                          tmp);
+          break;
+        case statement_delete:
+        case statement_generic:
+          assert (false);
+        }
+
+        text = tmp.c_str ();
+        text_size = tmp.size ();
+      }
+
+      // Empty statement.
+      //
+      if (*text == '\0')
+        return;
+
       OCIError* err (conn_.error_handle ());
       OCIStmt* handle (0);
 
@@ -254,7 +308,7 @@ namespace odb
       return reinterpret_cast<char*> (s);
     }
 
-    void statement::
+    ub4 statement::
     bind_param (bind* b, size_t n)
     {
       // Figure out how many unbind elements we will need and allocate them.
@@ -264,6 +318,9 @@ namespace odb
 
         for (size_t i (0); i < n; ++i)
         {
+          if (b[i].buffer == 0) // Skip NULL entries.
+            continue;
+
           switch (b[i].type)
           {
           case bind::timestamp:
@@ -301,13 +358,14 @@ namespace odb
       OCIError* err (conn_.error_handle ());
       OCIEnv* env (conn_.database ().environment ());
 
-      // The parameter position in OCIBindByPos is specified as a 1-based
-      // index.
-      //
-      n++;
-
-      for (ub4 i (1); i < n; ++i, ++b)
+      ub4 i (0);
+      for (bind* end (b + n); b != end; ++b)
       {
+        if (b->buffer == 0) // Skip NULL entries.
+          continue;
+
+        i++; // Column index is 1-based.
+
         void* value;
         sb4 capacity;
         ub2* size (0);
@@ -617,9 +675,11 @@ namespace odb
             translate_error (err, r);
         }
       }
+
+      return i;
     }
 
-    void statement::
+    ub4 statement::
     bind_result (bind* b, size_t c, size_t p)
     {
       ODB_POTENTIALLY_UNUSED (p);
@@ -628,8 +688,14 @@ namespace odb
       OCIError* err (conn_.error_handle ());
       OCIEnv* env (conn_.database ().environment ());
 
-      for (size_t i (1); i <= c; ++i, ++b)
+      ub4 i (0);
+      for (bind* end (b + c); b != end; ++b)
       {
+        if (b->buffer == 0) // Skip NULL entries.
+          continue;
+
+        i++; // Column index is 1-based.
+
         void* value;
         sb4 capacity;
         ub2* size (0);
@@ -816,6 +882,8 @@ namespace odb
             translate_error (err, r);
         }
       }
+
+      return i;
     }
 
     void statement::
@@ -826,8 +894,14 @@ namespace odb
       sword r;
       OCIEnv* env (conn_.database ().environment ());
 
-      for (size_t i (1); i <= c; ++i, ++b)
+      ub4 i (0);
+      for (bind* end (b + c); b != end; ++b)
       {
+        if (b->buffer == 0) // Skip NULL entries.
+          continue;
+
+        i++; // Column index is 1-based.
+
         void* value;
 
         switch (b->type)
@@ -971,8 +1045,11 @@ namespace odb
     {
       OCIError* err (conn_.error_handle ());
 
-      for (size_t i (0); i < c; ++i, ++b)
+      for (bind* end (b + c); b != end; ++b)
       {
+        if (b->buffer == 0) // Skip NULL entries.
+          continue;
+
         // Only stream if the bind specifies a LOB type.
         //
         if (b->type == bind::blob ||
@@ -1091,14 +1168,20 @@ namespace odb
 
     generic_statement::
     generic_statement (connection_type& conn, const string& text)
-        : statement (conn, text), bound_ (false)
+        : statement (conn,
+                     text, statement_generic,
+                     0, false),
+          bound_ (false)
     {
       init ();
     }
 
     generic_statement::
     generic_statement (connection_type& conn, const char* text)
-        : statement (conn, text), bound_ (false)
+        : statement (conn,
+                     text, statement_generic,
+                     0, false),
+          bound_ (false)
     {
       init ();
     }
@@ -1270,65 +1353,93 @@ namespace odb
     select_statement::
     select_statement (connection_type& conn,
                       const string& text,
+                      bool process,
+                      bool optimize,
                       binding& param,
                       binding& result,
                       size_t lob_prefetch_size)
-        : statement (conn, text),
+        : statement (conn,
+                     text, statement_select,
+                     (process ? &result : 0), optimize),
           result_ (result),
-          result_version_ (0),
           lob_prefetch_size_ (lob_prefetch_size),
           done_ (true)
     {
-      bind_param (param.bind, param.count);
-      bind_result (result.bind, result.count, lob_prefetch_size);
-      result_version_ = result_.version;
+      if (!empty ())
+      {
+        bind_param (param.bind, param.count);
+        result_count_ = bind_result (
+          result.bind, result.count, lob_prefetch_size);
+        result_version_ = result_.version;
+      }
     }
 
     select_statement::
     select_statement (connection_type& conn,
                       const char* text,
+                      bool process,
+                      bool optimize,
                       binding& param,
                       binding& result,
                       size_t lob_prefetch_size)
-        : statement (conn, text),
+        : statement (conn,
+                     text, statement_select,
+                     (process ? &result : 0), optimize),
           result_ (result),
-          result_version_ (0),
           lob_prefetch_size_ (lob_prefetch_size),
           done_ (true)
     {
-      bind_param (param.bind, param.count);
-      bind_result (result.bind, result.count, lob_prefetch_size);
-      result_version_ = result_.version;
+      if (!empty ())
+      {
+        bind_param (param.bind, param.count);
+        result_count_ = bind_result (
+          result.bind, result.count, lob_prefetch_size);
+        result_version_ = result_.version;
+      }
     }
 
     select_statement::
     select_statement (connection_type& conn,
                       const string& text,
+                      bool process,
+                      bool optimize,
                       binding& result,
                       size_t lob_prefetch_size)
-        : statement (conn, text),
+        : statement (conn,
+                     text, statement_select,
+                     (process ? &result : 0), optimize),
           result_ (result),
-          result_version_ (0),
           lob_prefetch_size_ (lob_prefetch_size),
           done_ (true)
     {
-      bind_result (result.bind, result.count, lob_prefetch_size);
-      result_version_ = result_.version;
+      if (!empty ())
+      {
+        result_count_ = bind_result (
+          result.bind, result.count, lob_prefetch_size);
+        result_version_ = result_.version;
+      }
     }
 
     select_statement::
     select_statement (connection_type& conn,
                       const char* text,
+                      bool process,
+                      bool optimize,
                       binding& result,
                       size_t lob_prefetch_size)
-        : statement (conn, text),
+        : statement (conn,
+                     text, statement_select,
+                     (process ? &result : 0), optimize),
           result_ (result),
-          result_version_ (0),
           lob_prefetch_size_ (lob_prefetch_size),
           done_ (true)
     {
-      bind_result (result.bind, result.count, lob_prefetch_size);
-      result_version_ = result_.version;
+      if (!empty ())
+      {
+        result_count_ = bind_result (
+          result.bind, result.count, lob_prefetch_size);
+        result_version_ = result_.version;
+      }
     }
 
     void select_statement::
@@ -1374,7 +1485,7 @@ namespace odb
       // of this assertion is a native view with a number of data members
       // not matching the number of columns in the SELECT-list.
       //
-      assert (n == result_.count);
+      assert (n == result_count_);
 #endif
     }
 
@@ -1495,9 +1606,12 @@ namespace odb
     insert_statement::
     insert_statement (connection_type& conn,
                       const string& text,
+                      bool process,
                       binding& param,
                       bool returning)
-        : statement (conn, text)
+        : statement (conn,
+                     text, statement_insert,
+                     (process ? &param : 0), false)
     {
       init (param, returning);
     }
@@ -1505,9 +1619,12 @@ namespace odb
     insert_statement::
     insert_statement (connection_type& conn,
                       const char* text,
+                      bool process,
                       binding& param,
                       bool returning)
-        : statement (conn, text)
+        : statement (conn,
+                     text, statement_insert,
+                     (process ? &param : 0), false)
     {
       init (param, returning);
     }
@@ -1515,7 +1632,7 @@ namespace odb
     void insert_statement::
     init (binding& param, bool returning)
     {
-      bind_param (param.bind, param.count);
+      ub4 param_count (bind_param (param.bind, param.count));
 
       if (returning)
       {
@@ -1525,7 +1642,7 @@ namespace odb
         sword r (OCIBindByPos (stmt_,
                                &h,
                                err,
-                               param.count + 1,
+                               param_count + 1,
                                0,
 #if (OCI_MAJOR_VERSION == 11 && OCI_MINOR_VERSION >=2) \
   || OCI_MAJOR_VERSION > 11
@@ -1636,19 +1753,27 @@ namespace odb
     update_statement::
     update_statement (connection_type& conn,
                       const string& text,
+                      bool process,
                       binding& param)
-        : statement (conn, text)
+        : statement (conn,
+                     text, statement_update,
+                     (process ? &param : 0), false)
     {
-      bind_param (param.bind, param.count);
+      if (!empty ())
+        bind_param (param.bind, param.count);
     }
 
     update_statement::
     update_statement (connection_type& conn,
                       const char* text,
+                      bool process,
                       binding& param)
-        : statement (conn, text)
+        : statement (conn,
+                     text, statement_update,
+                     (process ? &param : 0), false)
     {
-      bind_param (param.bind, param.count);
+      if (!empty ())
+        bind_param (param.bind, param.count);
     }
 
     unsigned long long update_statement::
@@ -1707,7 +1832,9 @@ namespace odb
     delete_statement (connection_type& conn,
                       const string& text,
                       binding& param)
-        : statement (conn, text)
+        : statement (conn,
+                     text, statement_delete,
+                     0, false)
     {
       bind_param (param.bind, param.count);
     }
@@ -1716,7 +1843,9 @@ namespace odb
     delete_statement (connection_type& conn,
                       const char* text,
                       binding& param)
-        : statement (conn, text)
+        : statement (conn,
+                     text, statement_delete,
+                     0, false)
     {
       bind_param (param.bind, param.count);
     }
diff --git a/odb/oracle/statement.hxx b/odb/oracle/statement.hxx
index d246992..cf336c4 100644
--- a/odb/oracle/statement.hxx
+++ b/odb/oracle/statement.hxx
@@ -48,13 +48,39 @@ 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);
+      // 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);
 
     private:
       void
-      init (const char* text, std::size_t text_size);
+      init (const char* text,
+            std::size_t text_size,
+            statement_kind,
+            const binding* process,
+            bool optimize);
 
     protected:
       struct unbind
@@ -66,16 +92,18 @@ namespace odb
 
       // Bind parameters for this statement. This function must only
       // be called once. Multiple calls to it will result in memory
-      // leaks due to lost OCIBind resources.
+      // leaks due to lost OCIBind resources. Return the actual number
+      // of columns bound.
       //
-      void
+      ub4
       bind_param (bind*, std::size_t count);
 
       // Bind results for this statement. This function must only be
       // called once. Multiple calls to it will result in memory leaks
-      // due to lost OCIDefine resources.
+      // due to lost OCIDefine resources. Return the actual number of
+      // columns bound.
       //
-      void
+      ub4
       bind_result (bind*,
                    std::size_t count,
                    std::size_t lob_prefetch_size = 0);
@@ -146,23 +174,31 @@ namespace odb
 
       select_statement (connection_type& conn,
                         const std::string& text,
+                        bool process_text,
+                        bool optimize_text,
                         binding& param,
                         binding& result,
                         std::size_t lob_prefetch_size = 0);
 
       select_statement (connection_type& conn,
                         const char* text,
+                        bool process_text,
+                        bool optimize_text,
                         binding& param,
                         binding& result,
                         std::size_t lob_prefetch_size = 0);
 
       select_statement (connection_type& conn,
                         const std::string& text,
+                        bool process_text,
+                        bool optimize_text,
                         binding& result,
                         std::size_t lob_prefetch_size = 0);
 
       select_statement (connection_type& conn,
                         const char* text,
+                        bool process_text,
+                        bool optimize_text,
                         binding& result,
                         std::size_t lob_prefetch_size = 0);
 
@@ -197,6 +233,7 @@ namespace odb
     private:
       binding& result_;
       std::size_t result_version_;
+      ub4 result_count_; // Actual number of bound columns.
       const std::size_t lob_prefetch_size_;
       bool done_;
     };
@@ -222,11 +259,13 @@ namespace odb
 
       insert_statement (connection_type& conn,
                         const std::string& text,
+                        bool process_text,
                         binding& param,
                         bool returning);
 
       insert_statement (connection_type& conn,
                         const char* text,
+                        bool process_text,
                         binding& param,
                         bool returning);
 
@@ -282,10 +321,12 @@ namespace odb
 
       update_statement (connection_type& conn,
                         const std::string& text,
+                        bool process_text,
                         binding& param);
 
       update_statement (connection_type& conn,
                         const char* text,
+                        bool process_text,
                         binding& param);
 
       unsigned long long
diff --git a/odb/oracle/traits-calls.hxx b/odb/oracle/traits-calls.hxx
new file mode 100644
index 0000000..e15e616
--- /dev/null
+++ b/odb/oracle/traits-calls.hxx
@@ -0,0 +1,191 @@
+// file      : odb/oracle/traits-calls.hxx
+// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
+// license   : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_TRAITS_CALLS_HXX
+#define ODB_ORACLE_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/oracle/forward.hxx>
+#include <odb/oracle/oracle-types.hxx>
+
+namespace odb
+{
+  namespace oracle
+  {
+    //
+    // object_traits_calls
+    //
+
+    template <typename T,
+              bool versioned = object_traits_impl<T, id_oracle>::versioned>
+    struct object_traits_calls;
+
+    template <typename T>
+    struct object_traits_calls<T, false>
+    {
+      typedef object_traits_impl<T, id_oracle> traits;
+      typedef typename traits::image_type image_type;
+      typedef oracle::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_oracle> traits;
+      typedef typename traits::image_type image_type;
+      typedef oracle::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_oracle>::versioned>
+    struct view_traits_calls;
+
+    template <typename T>
+    struct view_traits_calls<T, false>
+    {
+      typedef view_traits_impl<T, id_oracle> traits;
+      typedef typename traits::image_type image_type;
+      typedef oracle::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_oracle> traits;
+      typedef typename traits::image_type image_type;
+      typedef oracle::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_ORACLE_TRAITS_CALLS_HXX
diff --git a/odb/oracle/view-result.hxx b/odb/oracle/view-result.hxx
index 0adf0fe..424728b 100644
--- a/odb/oracle/view-result.hxx
+++ b/odb/oracle/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/oracle/version.hxx>
 #include <odb/oracle/forward.hxx> // query_base, view_statements
 #include <odb/oracle/statement.hxx>
+#include <odb/oracle/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 use_copy_;
       typename view_traits::image_type* image_copy_;
     };
diff --git a/odb/oracle/view-result.txx b/odb/oracle/view-result.txx
index df2a833..402a571 100644
--- a/odb/oracle/view-result.txx
+++ b/odb/oracle/view-result.txx
@@ -46,10 +46,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)
     {
@@ -61,9 +63,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
       // LOB data also comes from the copy.
@@ -95,7 +97,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++;
       }
-- 
cgit v1.1