aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2012-03-21 08:36:21 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2012-04-23 16:50:56 +0200
commit8d89bf37dd4ef3cb7373e1841ff57a53916fff0d (patch)
tree704b7de0338eba0e008a96a09b6d57e86846d1b1
parent68ff21c56fb287790440208907ad2a851a108f89 (diff)
Polymorphic inheritance support
-rw-r--r--odb/common.cxx37
-rw-r--r--odb/common.hxx37
-rw-r--r--odb/context.cxx75
-rw-r--r--odb/context.hxx62
-rw-r--r--odb/features.hxx24
-rw-r--r--odb/generator.cxx48
-rw-r--r--odb/generator.hxx6
-rw-r--r--odb/header.cxx23
-rw-r--r--odb/plugin.cxx11
-rw-r--r--odb/pragma.cxx15
-rw-r--r--odb/processor.cxx7
-rw-r--r--odb/processor.hxx6
-rw-r--r--odb/relational/common.cxx178
-rw-r--r--odb/relational/common.hxx30
-rw-r--r--odb/relational/context.cxx1
-rw-r--r--odb/relational/context.hxx1
-rw-r--r--odb/relational/header.cxx805
-rw-r--r--odb/relational/header.hxx732
-rw-r--r--odb/relational/inline.cxx3
-rw-r--r--odb/relational/inline.hxx259
-rw-r--r--odb/relational/model.cxx2
-rw-r--r--odb/relational/model.hxx18
-rw-r--r--odb/relational/mssql/context.cxx4
-rw-r--r--odb/relational/mssql/context.hxx1
-rw-r--r--odb/relational/mssql/header.cxx33
-rw-r--r--odb/relational/mssql/source.cxx168
-rw-r--r--odb/relational/mysql/context.cxx4
-rw-r--r--odb/relational/mysql/context.hxx1
-rw-r--r--odb/relational/mysql/source.cxx134
-rw-r--r--odb/relational/oracle/context.cxx4
-rw-r--r--odb/relational/oracle/context.hxx1
-rw-r--r--odb/relational/oracle/header.cxx33
-rw-r--r--odb/relational/oracle/source.cxx154
-rw-r--r--odb/relational/pgsql/context.cxx4
-rw-r--r--odb/relational/pgsql/context.hxx1
-rw-r--r--odb/relational/pgsql/header.cxx21
-rw-r--r--odb/relational/pgsql/source.cxx238
-rw-r--r--odb/relational/processor.cxx426
-rw-r--r--odb/relational/schema-source.hxx10
-rw-r--r--odb/relational/source.cxx3245
-rw-r--r--odb/relational/source.hxx2560
-rw-r--r--odb/relational/sqlite/context.cxx4
-rw-r--r--odb/relational/sqlite/context.hxx1
-rw-r--r--odb/relational/sqlite/source.cxx124
-rw-r--r--odb/semantics/class.hxx2
-rw-r--r--odb/semantics/elements.cxx29
-rw-r--r--odb/semantics/elements.hxx71
-rw-r--r--odb/semantics/relational/elements.cxx6
-rw-r--r--odb/semantics/relational/elements.hxx3
-rw-r--r--odb/semantics/unit.hxx2
-rw-r--r--odb/traversal/elements.cxx20
-rw-r--r--odb/traversal/elements.hxx14
-rw-r--r--odb/validator.cxx130
-rw-r--r--odb/validator.hxx2
54 files changed, 6228 insertions, 3602 deletions
diff --git a/odb/common.cxx b/odb/common.cxx
index 35bfc87..28b80cb 100644
--- a/odb/common.cxx
+++ b/odb/common.cxx
@@ -85,12 +85,23 @@ traverse (semantics::class_& c)
return;
}
- semantics::class_* prev (context::cur_object);
- context::cur_object = &c;
+ if (top_level_)
+ top_level_ = false;
+ else
+ {
+ // Unless requested otherwise, don't go into bases if we are a derived
+ // type in a polymorphic hierarchy.
+ //
+ if (!traverse_poly_base_ && polymorphic (c))
+ return;
+ }
if (context::top_object == 0)
context::top_object = &c;
+ semantics::class_* prev (context::cur_object);
+ context::cur_object = &c;
+
if (member_scope_.empty ())
member_scope_.push_back (class_inheritance_chain ());
@@ -138,10 +149,10 @@ traverse (semantics::class_& c)
if (member_scope_.back ().empty ())
member_scope_.pop_back ();
+ context::cur_object = prev;
+
if (prev == 0)
context::top_object = 0;
-
- context::cur_object = prev;
}
void object_members_base::
@@ -363,15 +374,23 @@ traverse (semantics::class_& c)
if (top_level_)
top_level_ = false;
+ else
+ {
+ // Unless requested otherwise, don't go into bases if we are a derived
+ // type in a polymorphic hierarchy.
+ //
+ if (!traverse_poly_base_ && polymorphic (c))
+ return;
+ }
semantics::class_* prev (0);
if (k == class_object || k == class_view)
{
- prev = context::cur_object;
- context::cur_object = &c;
-
if (context::top_object == 0)
context::top_object = &c;
+
+ prev = context::cur_object;
+ context::cur_object = &c;
}
if (member_scope_.empty ())
@@ -393,10 +412,10 @@ traverse (semantics::class_& c)
if (k == class_object || k == class_view)
{
+ context::cur_object = prev;
+
if (prev == 0)
context::top_object = 0;
-
- context::cur_object = prev;
}
if (f && !first_)
diff --git a/odb/common.hxx b/odb/common.hxx
index ffbd2d6..bc45d00 100644
--- a/odb/common.hxx
+++ b/odb/common.hxx
@@ -67,25 +67,33 @@ struct object_members_base: traversal::class_, virtual context
traverse_view (semantics::class_&);
public:
- object_members_base ()
- : member_ (*this)
+ object_members_base (bool traverse_poly_base = false)
+ : top_level_ (true), member_ (*this)
{
- init (false, false, false);
+ init (false, false, false, traverse_poly_base);
}
object_members_base (bool build_flat_prefix,
bool build_table_prefix,
- bool build_member_prefix)
- : member_ (*this)
+ bool build_member_prefix,
+ bool traverse_poly_base = false)
+ : top_level_ (true), member_ (*this)
{
- init (build_flat_prefix, build_table_prefix, build_member_prefix);
+ init (build_flat_prefix,
+ build_table_prefix,
+ build_member_prefix,
+ traverse_poly_base);
}
object_members_base (object_members_base const& x)
: context (), //@@ -Wextra
+ top_level_ (true),
member_ (*this)
{
- init (x.build_flat_prefix_, x.build_table_prefix_, x.build_member_prefix_);
+ init (x.build_flat_prefix_,
+ x.build_table_prefix_,
+ x.build_member_prefix_,
+ x.traverse_poly_base_);
}
virtual void
@@ -117,11 +125,13 @@ private:
void
init (bool build_flat_prefix,
bool build_table_prefix,
- bool build_member_prefix)
+ bool build_member_prefix,
+ bool traverse_poly_base)
{
build_flat_prefix_ = build_flat_prefix;
build_table_prefix_ = build_table_prefix;
build_member_prefix_ = build_member_prefix;
+ traverse_poly_base_ = traverse_poly_base;
*this >> names_ >> member_;
*this >> inherits_ >> *this;
@@ -149,6 +159,10 @@ private:
bool build_table_prefix_;
bool build_member_prefix_;
+ bool traverse_poly_base_;
+
+ bool top_level_;
+
member member_;
traversal::names names_;
traversal::inherits inherits_;
@@ -204,9 +218,11 @@ struct object_columns_base: traversal::class_, virtual context
public:
object_columns_base (bool first = true,
- string const& column_prefix = string ())
+ string const& column_prefix = string (),
+ bool traverse_poly_base = false)
: column_prefix_ (column_prefix),
root_ (0),
+ traverse_poly_base_ (traverse_poly_base),
first_ (first),
top_level_ (true),
member_ (*this)
@@ -218,6 +234,7 @@ public:
: context (), //@@ -Wextra
column_prefix_ (x.column_prefix_),
root_ (0),
+ traverse_poly_base_ (x.traverse_poly_base_),
first_ (x.first_),
top_level_ (true),
member_ (*this)
@@ -321,6 +338,8 @@ private:
object_columns_base& oc_;
};
+ bool traverse_poly_base_;
+
bool first_;
bool top_level_;
diff --git a/odb/context.cxx b/odb/context.cxx
index 265d4ca..f699f38 100644
--- a/odb/context.cxx
+++ b/odb/context.cxx
@@ -122,6 +122,7 @@ auto_ptr<context>
create_context (ostream& os,
semantics::unit& unit,
options const& ops,
+ features& f,
semantics::relational::model* m)
{
auto_ptr<context> r;
@@ -130,27 +131,27 @@ create_context (ostream& os,
{
case database::mssql:
{
- r.reset (new relational::mssql::context (os, unit, ops, m));
+ r.reset (new relational::mssql::context (os, unit, ops, f, m));
break;
}
case database::mysql:
{
- r.reset (new relational::mysql::context (os, unit, ops, m));
+ r.reset (new relational::mysql::context (os, unit, ops, f, m));
break;
}
case database::oracle:
{
- r.reset (new relational::oracle::context (os, unit, ops, m));
+ r.reset (new relational::oracle::context (os, unit, ops, f, m));
break;
}
case database::pgsql:
{
- r.reset (new relational::pgsql::context (os, unit, ops, m));
+ r.reset (new relational::pgsql::context (os, unit, ops, f, m));
break;
}
case database::sqlite:
{
- r.reset (new relational::sqlite::context (os, unit, ops, m));
+ r.reset (new relational::sqlite::context (os, unit, ops, f, m));
break;
}
}
@@ -169,11 +170,13 @@ context::
context (ostream& os_,
semantics::unit& u,
options_type const& ops,
+ features_type& f,
data_ptr d)
: data_ (d ? d : data_ptr (new (shared) data (os_))),
os (data_->os_),
unit (u),
options (ops),
+ features (f),
db (options.database ()),
keyword_set (data_->keyword_set_),
include_regex (data_->include_regex_),
@@ -201,6 +204,7 @@ context ()
os (current ().os),
unit (current ().unit),
options (current ().options),
+ features (current ().features),
db (current ().db),
keyword_set (current ().keyword_set),
include_regex (current ().include_regex),
@@ -428,6 +432,28 @@ null (semantics::data_member& m, string const& kp)
}
}
+size_t context::
+polymorphic_depth (semantics::class_& c)
+{
+ if (c.count ("polymorphic-depth"))
+ return c.get<size_t> ("polymorphic-depth");
+
+ // Calculate our hierarchy depth (number of classes).
+ //
+ using semantics::class_;
+
+ class_* root (polymorphic (c));
+ assert (root != 0);
+
+ size_t r (1); // One for the root.
+
+ for (class_* b (&c); b != root; b = &polymorphic_base (*b))
+ ++r;
+
+ c.set ("polymorphic-depth", r);
+ return r;
+}
+
context::class_kind_type context::
class_kind (semantics::class_& c)
{
@@ -1180,6 +1206,28 @@ escape (string const& name) const
return r;
}
+string context::
+make_guard (string const& s) const
+{
+ // Split words, e.g., "FooBar" to "Foo_Bar" and convert everything
+ // to upper case.
+ //
+ string r;
+ for (string::size_type i (0), n (s.size ()); i < n - 1; ++i)
+ {
+ char c1 (s[i]);
+ char c2 (s[i + 1]);
+
+ r += toupper (c1);
+
+ if (isalpha (c1) && isalpha (c2) && islower (c1) && isupper (c2))
+ r += "_";
+ }
+ r += toupper (s[s.size () - 1]);
+
+ return escape (r);
+}
+
static string
charlit (unsigned int u)
{
@@ -1351,6 +1399,11 @@ namespace
c_.readonly++;
else if (context::version (m))
c_.optimistic_managed++;
+
+ // For now discriminator can only be a simple value.
+ //
+ if (discriminator (m))
+ c_.discriminator++;
}
context::column_count_type c_;
@@ -1375,7 +1428,9 @@ namespace
struct has_a_impl: object_members_base
{
has_a_impl (unsigned short flags)
- : r_ (0), flags_ (flags)
+ : object_members_base ((flags & context::include_base) != 0),
+ r_ (0),
+ flags_ (flags)
{
}
@@ -1386,8 +1441,14 @@ namespace
}
virtual void
- traverse_pointer (semantics::data_member&, semantics::class_&)
+ traverse_pointer (semantics::data_member& m, semantics::class_&)
{
+ // Ignore polymorphic id references; they are represented as
+ // pointers but are normally handled in a special way.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
if (context::is_a (member_path_, member_scope_, flags_))
r_++;
diff --git a/odb/context.hxx b/odb/context.hxx
index b590ea4..cdb49ac 100644
--- a/odb/context.hxx
+++ b/odb/context.hxx
@@ -21,6 +21,7 @@
#include <cutl/shared-ptr.hxx>
#include <odb/options.hxx>
+#include <odb/features.hxx>
#include <odb/cxx-token.hxx>
#include <odb/semantics.hxx>
#include <odb/semantics/relational/name.hxx>
@@ -442,6 +443,42 @@ public:
return m.count ("version");
}
+ // Polymorphic inheritance. Return root of the hierarchy or NULL if
+ // not polymorphic.
+ //
+ static semantics::class_*
+ polymorphic (semantics::class_& c)
+ {
+ // Set by the validator.
+ //
+ return c.get<semantics::class_*> ("polymorphic-root", 0);
+ }
+
+ static semantics::class_&
+ polymorphic_base (semantics::class_& c)
+ {
+ // Set by the validator.
+ //
+ return *c.get<semantics::class_*> ("polymorphic-base");
+ }
+
+ static size_t
+ polymorphic_depth (semantics::class_&);
+
+ static bool
+ discriminator (semantics::data_member& m)
+ {
+ return m.count ("discriminator");
+ }
+
+ static semantics::data_member*
+ discriminator (semantics::class_& c)
+ {
+ // Set by type processor.
+ //
+ return c.get<semantics::data_member*> ("discriminator", 0);
+ }
+
//
//
typedef ::class_kind class_kind_type;
@@ -556,6 +593,12 @@ public:
string
escape (string const&) const;
+ // Make C++ include guard name by split words, e.g., "FooBar" to
+ // "Foo_Bar" and converting everything to upper case.
+ //
+ string
+ make_guard (string const&) const;
+
// Return a string literal that can be used in C++ source code. It
// includes "".
//
@@ -572,7 +615,8 @@ public:
id (0),
inverse (0),
readonly (0),
- optimistic_managed (0)
+ optimistic_managed (0),
+ discriminator (0)
{
}
@@ -581,6 +625,7 @@ public:
size_t inverse;
size_t readonly;
size_t optimistic_managed;
+ size_t discriminator;
};
static column_count_type
@@ -589,7 +634,8 @@ public:
static semantics::data_member*
id_member (semantics::class_& c)
{
- // Set by the validator. May not be there for abstract objects.
+ // Set by the validator. May not be there for reuse-abstract
+ // classes or classes without object id.
//
return c.get<semantics::data_member*> ("id-member", 0);
}
@@ -695,7 +741,12 @@ public:
static unsigned short const test_inverse_container = 0x20;
static unsigned short const test_readonly_container = 0x40;
- static unsigned short const exclude_base = 0x8000;
+ // By default the test goes into bases for non-polymorphic
+ // hierarchies and doesn't go for polymorphic. The following
+ // flags can be used to alter this behavior.
+ //
+ static unsigned short const exclude_base = 0x4000;
+ static unsigned short const include_base = 0x8000;
bool
is_a (data_member_path const& mp,
@@ -808,9 +859,12 @@ protected:
data_ptr data_;
public:
+ typedef ::features features_type;
+
std::ostream& os;
semantics::unit& unit;
options_type const& options;
+ features_type& features;
database const db;
keyword_set_type const& keyword_set;
@@ -855,6 +909,7 @@ public:
context (std::ostream&,
semantics::unit&,
options_type const&,
+ features_type&,
data_ptr = data_ptr ());
static context&
@@ -877,6 +932,7 @@ std::auto_ptr<context>
create_context (std::ostream&,
semantics::unit&,
options const&,
+ features&,
semantics::relational::model*);
// Checks if scope Y names any of X.
diff --git a/odb/features.hxx b/odb/features.hxx
new file mode 100644
index 0000000..0468acb
--- /dev/null
+++ b/odb/features.hxx
@@ -0,0 +1,24 @@
+// file : odb/features.hxx
+// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_FEATURES_HXX
+#define ODB_FEATURES_HXX
+
+#include <cstring> // std::memset
+
+// Optional features used by client code that affect generated code.
+//
+struct features
+{
+ features () {std::memset (this, 0, sizeof (features));}
+
+ bool tr1_pointer;
+ bool boost_pointer;
+ bool simple_object;
+ bool polymorphic_object;
+ bool no_id_object;
+ bool view;
+};
+
+#endif // ODB_FEATURES_HXX
diff --git a/odb/generator.cxx b/odb/generator.cxx
index c63b52f..ffed9d9 100644
--- a/odb/generator.cxx
+++ b/odb/generator.cxx
@@ -33,30 +33,6 @@ namespace
"// compiler for C++.\n"
"//\n\n";
- string
- make_guard (string const& file, context& ctx)
- {
- string g (file);
-
- // Split words, e.g., "FooBar" to "Foo_Bar" and convert everything
- // to upper case.
- //
- string r;
- for (string::size_type i (0), n (g.size ()); i < n - 1; ++i)
- {
- char c1 (g[i]);
- char c2 (g[i + 1]);
-
- r += toupper (c1);
-
- if (isalpha (c1) && isalpha (c2) && islower (c1) && isupper (c2))
- r += "_";
- }
- r += toupper (g[g.size () - 1]);
-
- return ctx.escape (r);
- }
-
void
open (ifstream& ifs, string const& path)
{
@@ -88,7 +64,10 @@ namespace
}
void generator::
-generate (options const& ops, semantics::unit& unit, path const& p)
+generate (options const& ops,
+ features& fts,
+ semantics::unit& unit,
+ path const& p)
{
try
{
@@ -98,7 +77,7 @@ generate (options const& ops, semantics::unit& unit, path const& p)
if (ops.generate_schema ())
{
- auto_ptr<context> ctx (create_context (cerr, unit, ops, 0));
+ auto_ptr<context> ctx (create_context (cerr, unit, ops, fts, 0));
switch (ops.database ())
{
@@ -249,9 +228,10 @@ generate (options const& ops, semantics::unit& unit, path const& p)
sloc_filter sloc (hxx);
ind_filter ind (hxx);
- auto_ptr<context> ctx (create_context (hxx, unit, ops, model.get ()));
+ auto_ptr<context> ctx (
+ create_context (hxx, unit, ops, fts, model.get ()));
- string guard (make_guard (gp + hxx_name, *ctx));
+ string guard (ctx->make_guard (gp + hxx_name));
hxx << "#ifndef " << guard << endl
<< "#define " << guard << endl
@@ -328,7 +308,8 @@ generate (options const& ops, semantics::unit& unit, path const& p)
sloc_filter sloc (ixx);
ind_filter ind (ixx);
- auto_ptr<context> ctx (create_context (ixx, unit, ops, model.get ()));
+ auto_ptr<context> ctx (
+ create_context (ixx, unit, ops, fts, model.get ()));
// Copy prologue.
//
@@ -376,7 +357,8 @@ generate (options const& ops, semantics::unit& unit, path const& p)
sloc_filter sloc (cxx);
ind_filter ind (cxx);
- auto_ptr<context> ctx (create_context (cxx, unit, ops, model.get ()));
+ auto_ptr<context> ctx (
+ create_context (cxx, unit, ops, fts, model.get ()));
cxx << "#include <odb/pre.hxx>" << endl
<< endl;
@@ -432,7 +414,8 @@ generate (options const& ops, semantics::unit& unit, path const& p)
sloc_filter sloc (sch);
ind_filter ind (sch);
- auto_ptr<context> ctx (create_context (sch, unit, ops, model.get ()));
+ auto_ptr<context> ctx (
+ create_context (sch, unit, ops, fts, model.get ()));
// Copy prologue.
//
@@ -487,7 +470,8 @@ generate (options const& ops, semantics::unit& unit, path const& p)
//
if (sql_schema)
{
- auto_ptr<context> ctx (create_context (sql, unit, ops, model.get ()));
+ auto_ptr<context> ctx (
+ create_context (sql, unit, ops, fts, model.get ()));
// Copy prologue.
//
diff --git a/odb/generator.hxx b/odb/generator.hxx
index 4911883..5a1bbdf 100644
--- a/odb/generator.hxx
+++ b/odb/generator.hxx
@@ -6,6 +6,7 @@
#define ODB_GENERATOR_HXX
#include <odb/options.hxx>
+#include <odb/features.hxx>
#include <odb/semantics/unit.hxx>
class generator
@@ -14,7 +15,10 @@ public:
class failed {};
void
- generate (options const&, semantics::unit&, semantics::path const&);
+ generate (options const&,
+ features&,
+ semantics::unit&,
+ semantics::path const&);
generator () {}
diff --git a/odb/header.cxx b/odb/header.cxx
index d316036..a2b3b96 100644
--- a/odb/header.cxx
+++ b/odb/header.cxx
@@ -31,14 +31,12 @@ namespace header
// the latter is just a using-declaration for the former. To resolve
// this we will include TR1 traits if the Boost TR1 header is included.
//
- if (ctx.unit.count ("tr1-pointer-used") &&
- ctx.unit.get<bool> ("tr1-pointer-used"))
+ if (ctx.features.tr1_pointer)
{
os << "#include <odb/tr1/wrapper-traits.hxx>" << endl
<< "#include <odb/tr1/pointer-traits.hxx>" << endl;
}
- else if (ctx.unit.count ("boost-pointer-used") &&
- ctx.unit.get<bool> ("boost-pointer-used"))
+ else if (ctx.features.boost_pointer)
{
os << "#ifdef BOOST_TR1_MEMORY_HPP_INCLUDED" << endl
<< "# include <odb/tr1/wrapper-traits.hxx>" << endl
@@ -48,9 +46,26 @@ namespace header
os << "#include <odb/container-traits.hxx>" << endl;
+ if (ctx.features.polymorphic_object)
+ os << "#include <odb/polymorphic-info.hxx>" << endl;
+
if (ctx.options.generate_query ())
+ {
os << "#include <odb/result.hxx>" << endl;
+ if (ctx.features.simple_object)
+ os << "#include <odb/simple-object-result.hxx>" << endl;
+
+ if (ctx.features.polymorphic_object)
+ os << "#include <odb/polymorphic-object-result.hxx>" << endl;
+
+ if (ctx.features.no_id_object)
+ os << "#include <odb/no-id-object-result.hxx>" << endl;
+
+ if (ctx.features.view)
+ os << "#include <odb/view-result.hxx>" << endl;
+ }
+
os << endl;
}
}
diff --git a/odb/plugin.cxx b/odb/plugin.cxx
index 599760f..73ada7a 100644
--- a/odb/plugin.cxx
+++ b/odb/plugin.cxx
@@ -17,6 +17,7 @@
#include <odb/parser.hxx>
#include <odb/options.hxx>
#include <odb/option-functions.hxx>
+#include <odb/features.hxx>
#include <odb/profile.hxx>
#include <odb/version.hxx>
#include <odb/validator.hxx>
@@ -115,24 +116,26 @@ gate_callback (void*, void*)
parser p (*options_, loc_pragmas_, ns_loc_pragmas_, decl_pragmas_);
auto_ptr<unit> u (p.parse (global_namespace, file_));
+ features f;
+
// Validate, pass 1.
//
validator v;
- v.validate (*options_, *u, file_, 1);
+ v.validate (*options_, f, *u, file_, 1);
// Process.
//
processor pr;
- pr.process (*options_, *u, file_);
+ pr.process (*options_, f, *u, file_);
// Validate, pass 2.
//
- v.validate (*options_, *u, file_, 2);
+ v.validate (*options_, f, *u, file_, 2);
// Generate.
//
generator g;
- g.generate (*options_, *u, file_);
+ g.generate (*options_, f, *u, file_);
}
catch (pragmas_failed const&)
{
diff --git a/odb/pragma.cxx b/odb/pragma.cxx
index 080de98..698551a 100644
--- a/odb/pragma.cxx
+++ b/odb/pragma.cxx
@@ -460,7 +460,8 @@ check_spec_decl_type (tree d,
p == "callback" ||
p == "query" ||
p == "object" ||
- p == "optimistic")
+ p == "optimistic" ||
+ p == "polymorphic")
{
if (tc != RECORD_TYPE)
{
@@ -881,6 +882,18 @@ handle_pragma (cpp_reader* reader,
tt = pragma_lex (&t);
}
+ else if (p == "polymorphic")
+ {
+ // polymorphic
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl != 0 && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = pragma_lex (&t);
+ }
else if (p == "callback")
{
// callback (name)
diff --git a/odb/processor.cxx b/odb/processor.cxx
index 981cb31..0299ad5 100644
--- a/odb/processor.cxx
+++ b/odb/processor.cxx
@@ -12,13 +12,16 @@
using namespace std;
void processor::
-process (options const& ops, semantics::unit& unit, semantics::path const&)
+process (options const& ops,
+ features& f,
+ semantics::unit& unit,
+ semantics::path const&)
{
try
{
// Process types.
//
- auto_ptr<context> ctx (create_context (cerr, unit, ops, 0));
+ auto_ptr<context> ctx (create_context (cerr, unit, ops, f, 0));
relational::process ();
}
catch (operation_failed const&)
diff --git a/odb/processor.hxx b/odb/processor.hxx
index fdf8938..22f2d86 100644
--- a/odb/processor.hxx
+++ b/odb/processor.hxx
@@ -6,6 +6,7 @@
#define ODB_PROCESSOR_HXX
#include <odb/options.hxx>
+#include <odb/features.hxx>
#include <odb/semantics/unit.hxx>
class processor
@@ -14,7 +15,10 @@ public:
class failed {};
void
- process (options const&, semantics::unit&, semantics::path const&);
+ process (options const&,
+ features&,
+ semantics::unit&,
+ semantics::path const&);
processor () {}
diff --git a/odb/relational/common.cxx b/odb/relational/common.cxx
index 381d77f..914a778 100644
--- a/odb/relational/common.cxx
+++ b/odb/relational/common.cxx
@@ -14,6 +14,122 @@ using namespace std;
namespace relational
{
+ // query_alias_traits
+ //
+
+ void query_alias_traits::
+ traverse_object (semantics::class_& c)
+ {
+ // We don't want to traverse bases.
+ //
+ names (c);
+ }
+
+ void query_alias_traits::
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore polymorphic id references.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ // Come up with a table alias. Generally, we want it to be based
+ // on the column name. This is straightforward for single-column
+ // references. In case of a composite id, we will need to use the
+ // column prefix which is based on the data member name, unless
+ // overridden by the user. In the latter case the prefix can be
+ // empty, in which case we will just fall back on the member's
+ // public name.
+ //
+ string alias;
+
+ if (composite_wrapper (utype ((*id_member (c)))))
+ {
+ string p (column_prefix (m, key_prefix_, default_name_));
+
+ if (p.empty ())
+ p = public_name_db (m);
+ else
+ p.resize (p.size () - 1); // Remove trailing underscore.
+
+ alias = column_prefix_ + p;
+ }
+ else
+ alias = column_prefix_ + column_name (m, key_prefix_, default_name_);
+
+ generate (alias, c);
+ }
+
+ void query_alias_traits::
+ generate (string const& alias, semantics::class_& c)
+ {
+ string tag (escape (alias + "_alias_tag"));
+
+ if (tags_.find (tag) == tags_.end ())
+ {
+ os << "class " << tag << ";"
+ << endl;
+
+ tags_.insert (tag);
+ }
+
+ // Generate the alias_traits specialization.
+ //
+ generate_specialization (alias, tag, c);
+ }
+
+ void query_alias_traits::
+ generate_specialization (string const& alias,
+ string const& tag,
+ semantics::class_& c)
+ {
+ string const& fq_name (class_fq_name (c));
+ string guard (
+ make_guard (
+ "ODB_ALIAS_TRAITS_" + alias + "_FOR_" + flat_name (fq_name)));
+
+ if (specs_.find (guard) != specs_.end ())
+ return;
+ else
+ specs_.insert (guard);
+
+ semantics::class_* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+ semantics::class_* poly_base (poly_derived ? &polymorphic_base (c) : 0);
+
+ if (poly_derived)
+ generate_specialization (alias, tag, *poly_base);
+
+ os << "#ifndef " << guard << endl
+ << "#define " << guard << endl;
+
+ os << "template <bool d>" << endl
+ << "struct alias_traits< " << fq_name << ", " << tag << ", d >"
+ << "{"
+ << "static const char table_name[];";
+
+ if (poly_derived)
+ os << "typedef alias_traits< " << class_fq_name (*poly_base) << ", " <<
+ tag << " > base_traits;";
+
+ os << "};";
+
+ os << "template <bool d>" << endl
+ << "const char alias_traits< " << fq_name << ", " << tag <<
+ ", d >::" << endl
+ << "table_name[] = ";
+
+ if (poly_root != 0)
+ os << strlit (quote_id (alias + "_" + table_name (c).uname ()));
+ else
+ os << strlit (quote_id (alias));
+
+ os << ";"
+ << "#endif // " << guard << endl
+ << endl;
+ }
+
+
// query_columns_base
//
@@ -81,31 +197,19 @@ namespace relational
void query_columns_base::
traverse_pointer (semantics::data_member& m, semantics::class_& c)
{
+ // Ignore polymorphic id references.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
string name (public_name (m));
bool inv (inverse (m, key_prefix_));
if (decl_)
{
os << "// " << name << endl
- << "//" << endl
- << "static const char " << name << "_alias_[];"
- << endl;
+ << "//" << endl;
- if (inv)
- {
- os << "typedef" << endl
- << "odb::query_pointer<" << endl
- << " odb::pointer_query_columns<" << endl
- << " " << class_fq_name (c) << "," << endl
- << " " << name << "_alias_ > >" << endl
- << name << "_type_ ;"
- << endl
- << "static const " << name << "_type_ " << name << ";"
- << endl;
- }
- }
- else
- {
// Come up with a table alias. Generally, we want it to be based
// on the column name. This is straightforward for single-column
// references. In case of a composite id, we will need to use the
@@ -130,11 +234,30 @@ namespace relational
else
alias = column_prefix_ + column_name (m, key_prefix_, default_name_);
- os << "const char " << scope_ << "::" << name << "_alias_[] = " <<
- strlit (quote_id (alias)) << ";"
+ string tag (escape (alias + "_alias_tag"));
+ string const& fq_name (class_fq_name (c));
+
+ os << "typedef" << endl
+ << "odb::alias_traits< " << fq_name << ", " << tag << " >" << endl
+ << name << "_alias_;"
<< endl;
if (inv)
+ {
+ os << "typedef" << endl
+ << "odb::query_pointer<" << endl
+ << " odb::pointer_query_columns<" << endl
+ << " " << fq_name << "," << endl
+ << " " << name << "_alias_ > >" << endl
+ << name << "_type_ ;"
+ << endl
+ << "static const " << name << "_type_ " << name << ";"
+ << endl;
+ }
+ }
+ else
+ {
+ if (inv)
os << "const " << scope_ << "::" << name << "_type_" << endl
<< scope_ << "::" << name << ";"
<< endl;
@@ -155,7 +278,7 @@ namespace relational
: ptr_ (ptr), decl_ (false), in_ptr_ (false)
{
scope_ = ptr ? "pointer_query_columns" : "query_columns";
- scope_ += "< " + class_fq_name (c) + ", table >";
+ scope_ += "< " + class_fq_name (c) + ", A >";
}
void query_columns::
@@ -217,7 +340,7 @@ namespace relational
// Composite member. Note that here we don't use suffix.
//
- os << "template <const char* table>" << endl
+ os << "template <typename A>" << endl
<< "const typename " << scope_ << "::" << name << "_type_" << endl
<< scope_ << "::" << name << ";"
<< endl;
@@ -261,10 +384,10 @@ namespace relational
{
// Note that here we don't use suffix.
//
- os << "template <const char* table>" << endl
+ os << "template <typename A>" << endl
<< "const typename " << scope_ << "::" << name << "_type_" << endl
- << scope_ << "::" << name << " (" << "table, " <<
- strlit (quote_id (column));
+ << scope_ << "::" << endl
+ << name << " (A::" << "table_name, " << strlit (quote_id (column));
column_ctor_extra (m);
@@ -295,6 +418,11 @@ namespace relational
void query_columns::
traverse_pointer (semantics::data_member& m, semantics::class_& c)
{
+ // Ignore polymorphic id references.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
// If this is for the pointer_query_columns and the member is not
// inverse, then create the normal member corresponding to the id
// column. This will allow the user to check it for NULL or to
diff --git a/odb/relational/common.hxx b/odb/relational/common.hxx
index 276868c..26e8197 100644
--- a/odb/relational/common.hxx
+++ b/odb/relational/common.hxx
@@ -6,6 +6,7 @@
#define ODB_RELATIONAL_COMMON_HXX
#include <map>
+#include <set>
#include <cstddef> // std::size_t
#include <cassert>
#include <typeinfo>
@@ -221,6 +222,35 @@ namespace relational
}
};
+ // Generate alias tags and alias_traits specializations for pointers
+ // in this objects.
+ //
+ struct query_alias_traits: object_columns_base, virtual context
+ {
+ typedef query_alias_traits base;
+
+ query_alias_traits (std::set<string>& tags, std::set<string>& specs)
+ : tags_ (tags), specs_ (specs) {}
+
+ virtual void
+ traverse_object (semantics::class_&);
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&);
+
+ virtual void
+ generate (string const& alias, semantics::class_&);
+
+ private:
+ void
+ generate_specialization (string const& alias,
+ string const& tag,
+ semantics::class_&);
+
+ std::set<string>& tags_;
+ std::set<string>& specs_;
+ };
+
//
//
struct query_columns_base: object_columns_base, virtual context
diff --git a/odb/relational/context.cxx b/odb/relational/context.cxx
index 98bbfe4..8630a30 100644
--- a/odb/relational/context.cxx
+++ b/odb/relational/context.cxx
@@ -27,6 +27,7 @@ namespace relational
need_alias_as (current ().need_alias_as),
insert_send_auto_id (current ().insert_send_auto_id),
delay_freeing_statement_result (current ().delay_freeing_statement_result),
+ need_image_clone (current ().need_image_clone),
bind_vector (data_->bind_vector_),
truncated_vector (data_->truncated_vector_)
{
diff --git a/odb/relational/context.hxx b/odb/relational/context.hxx
index 52cb9c0..60ed934 100644
--- a/odb/relational/context.hxx
+++ b/odb/relational/context.hxx
@@ -159,6 +159,7 @@ namespace relational
bool need_alias_as;
bool insert_send_auto_id;
bool delay_freeing_statement_result;
+ bool need_image_clone;
string const& bind_vector;
string const& truncated_vector;
diff --git a/odb/relational/header.cxx b/odb/relational/header.cxx
index ccd73bf..46ba722 100644
--- a/odb/relational/header.cxx
+++ b/odb/relational/header.cxx
@@ -7,65 +7,790 @@
using namespace std;
-namespace relational
+void relational::header::class1::
+traverse_object (type& c)
{
- namespace header
+ using semantics::data_member;
+
+ data_member* id (id_member (c));
+ bool auto_id (id ? id->count ("auto") : false);
+ bool base_id (id ? &id->scope () != &c : false); // Comes from base.
+
+ data_member* optimistic (context::optimistic (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+ type* poly_base (poly_derived ? &polymorphic_base (c) : 0);
+ data_member* discriminator (poly ? context::discriminator (*poly_root) : 0);
+
+ bool abst (abstract (c));
+ bool reuse_abst (abst && !poly);
+
+ string const& type (class_fq_name (c));
+ column_count_type const& cc (column_count (c));
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ // class_traits
+ //
+ os << "template <>" << endl
+ << "struct class_traits< " << type << " >"
+ << "{"
+ << "static const class_kind kind = class_object;"
+ << "};";
+
+ // pointer_query_columns & query_columns
+ //
+ if (options.generate_query ())
+ {
+ // If we don't have object pointers, then also generate
+ // query_columns (in this case pointer_query_columns and
+ // query_columns are the same and the former inherits from
+ // the latter). Otherwise we have to postpone query_columns
+ // generation until the second pass to deal with forward-
+ // declared objects.
+ //
+ if (!has_a (c, test_pointer | include_base))
+ query_columns_type_->traverse (c);
+
+ pointer_query_columns_type_->traverse (c);
+ }
+
+ // object_traits
+ //
+ os << "template <>" << endl
+ << "class access::object_traits< " << type << " >"
+ << "{"
+ << "public:" << endl;
+
+ object_public_extra_pre (c);
+
+ // object_type & pointer_type
+ //
+ os << "typedef " << type << " object_type;"
+ << "typedef " << c.get<string> ("object-pointer") << " pointer_type;"
+ << "typedef odb::pointer_traits<pointer_type> pointer_traits;"
+ << endl;
+
+ // polymorphic, root_type, base_type, etc.
+ //
+ os << "static const bool polymorphic = " << (poly ? "true" : "false") << ";";
+
+ if (poly)
{
- void
- generate ()
+ os << "typedef " << class_fq_name (*poly_root) << " root_type;"
+ << "typedef object_traits<root_type> root_traits;";
+
+ if (poly_derived)
{
- context ctx;
- ostream& os (ctx.os);
+ os << "typedef " << class_fq_name (*poly_base) << " base_type;"
+ << "typedef object_traits<base_type> base_traits;"
+ << "typedef root_traits::discriminator_type discriminator_type;"
+ << "typedef odb::polymorphic_concrete_info<root_type> info_type;";
+
+ if (abst)
+ os << "typedef odb::polymorphic_abstract_info<root_type> " <<
+ "abstract_info_type;";
+ else
+ os << "typedef odb::polymorphic_entry<object_type> entry_type;";
+
+ // Calculate our hierarchy depth (number of classes).
+ //
+ size_t depth (polymorphic_depth (c));
+
+ os << "static const std::size_t depth = " << depth << "UL;"
+ << endl;
- instance<include> i;
- i->generate ();
+ os << "static " << (abst ? "abstract_" : "") << "info_type info;";
- os << "namespace odb"
+ if (!abst)
+ os << "static entry_type entry;";
+ }
+ else
+ {
+ semantics::names* hint;
+ semantics::type& t (utype (*discriminator, hint));
+
+ os << "typedef " << t.fq_name (hint) << " discriminator_type;"
+ << endl;
+
+ os << "struct discriminator_image_type"
<< "{";
- {
- traversal::unit unit;
- traversal::defines unit_defines;
- typedefs unit_typedefs (false);
- traversal::namespace_ ns;
- instance<class1> c;
+ discriminator_image_member_->traverse (*discriminator);
+
+ if (optimistic != 0)
+ version_image_member_->traverse (*optimistic);
- unit >> unit_defines >> ns;
- unit_defines >> c;
- unit >> unit_typedefs >> c;
+ os << "std::size_t version;"
+ << "};";
- traversal::defines ns_defines;
- typedefs ns_typedefs (false);
+ os << "typedef odb::polymorphic_map<object_type> map_type;"
+ << "typedef odb::polymorphic_concrete_info<object_type> info_type;";
- ns >> ns_defines >> ns;
- ns_defines >> c;
- ns >> ns_typedefs >> c;
+ if (abst)
+ os << "typedef odb::polymorphic_abstract_info<object_type> " <<
+ "abstract_info_type;";
+ else
+ os << "typedef odb::polymorphic_entry<object_type> entry_type;";
- unit.dispatch (ctx.unit);
+ os << "static const std::size_t depth = 1UL;"
+ << endl;
+
+ os << "static map_type* map;"
+ << "static " << (abst ? "abstract_" : "") << "info_type info;";
+
+ if (!abst)
+ os << "static entry_type entry;";
+ }
+ }
+
+ os << endl;
+
+ // id_type & id_image_type
+ //
+ if (id != 0)
+ {
+ if (base_id)
+ {
+ semantics::class_& b (
+ dynamic_cast<semantics::class_&> (id->scope ()));
+ string const& type (class_fq_name (b));
+
+ os << "typedef object_traits< " << type << " >::id_type id_type;";
+
+ if (optimistic != 0)
+ os << "typedef object_traits< " << type << " >::version_type " <<
+ "version_type;";
+
+ if (poly_derived)
+ os << "typedef object_traits< " << type << " >:: id_image_type " <<
+ "id_image_type;"
+ << "static const bool auto_id = false;"
+ << endl;
+ else
+ os << "typedef object_traits< " << type << " >::id_image_type " <<
+ "id_image_type;"
+ << "static const bool auto_id = object_traits< " << type <<
+ " >::auto_id;"
+ << endl;
+ }
+ else
+ {
+ {
+ semantics::names* hint;
+ semantics::type& t (utype (*id, hint));
+ os << "typedef " << t.fq_name (hint) << " id_type;";
}
+ if (optimistic != 0)
{
- traversal::unit unit;
- traversal::defines unit_defines;
- typedefs unit_typedefs (false);
- traversal::namespace_ ns;
- instance<class2> c;
+ semantics::names* hint;
+ semantics::type& t (utype (*optimistic, hint));
+ os << "typedef " << t.fq_name (hint) << " version_type;";
+ }
+
+ os << "static const bool auto_id = " << (auto_id ? "true;" : "false;")
+ << endl;
- unit >> unit_defines >> ns;
- unit_defines >> c;
- unit >> unit_typedefs >> c;
+ os << "struct id_image_type"
+ << "{";
- traversal::defines ns_defines;
- typedefs ns_typedefs (false);
+ id_image_member_->traverse (*id);
- ns >> ns_defines >> ns;
- ns_defines >> c;
- ns >> ns_typedefs >> c;
+ if (optimistic != 0)
+ version_image_member_->traverse (*optimistic);
- unit.dispatch (ctx.unit);
- }
+ os << "std::size_t version;"
+ << "};";
+ }
+ }
+ else if (!reuse_abst)
+ {
+ // Object without id.
+ //
+ os << "typedef void id_type;"
+ << "static const bool auto_id = false;"
+ << endl;
+ }
+
+ // abstract
+ //
+ os << "static const bool abstract = " << (abst ? "true" : "false") << ";"
+ << endl;
+
+ // image_type
+ //
+ image_type_->traverse (c);
+
+ //
+ // Containers (abstract and concrete).
+ //
+
+ {
+ instance<container_traits> t (c);
+ t->traverse (c);
+ }
+
+ //
+ // Functions (abstract and concrete).
+ //
+
+ // id ()
+ //
+ if (id != 0 || !reuse_abst)
+ {
+ // We want to generate a dummy void id() accessor even if this
+ // object has no id to help us in the runtime. This way we can
+ // write generic code that will work for both void and non-void
+ // ids.
+ //
+ os << "static id_type" << endl
+ << "id (const object_type&);"
+ << endl;
+ }
+
+ if (!poly_derived && id != 0)
+ {
+ if (options.generate_query ())
+ os << "static id_type" << endl
+ << "id (const image_type&);"
+ << endl;
+
+ if (optimistic != 0)
+ os << "static version_type" << endl
+ << "version (const image_type&);"
+ << endl;
+ }
+
+ // discriminator()
+ //
+ if (poly && !poly_derived)
+ os << "static discriminator_type" << endl
+ << "discriminator (const image_type&);"
+ << endl;
+
+ // grow ()
+ //
+ if (generate_grow)
+ {
+ // For derived classes in a polymorphic hierarchy, grow() will
+ // check bases up to the specified depth. If one of the base
+ // images has grown, then it will increment its version. But
+ // the return value only indicates the state of this image,
+ // excluding polymorphic bases (in other words, it is possible
+ // that one of the bases has grown but this function returns
+ // false).
+ //
+ os << "static bool" << endl
+ << "grow (image_type&, " << truncated_vector;
+
+ if (poly_derived)
+ os << ", std::size_t = depth";
+
+ os << ");"
+ << endl;
+ }
+
+ // bind (image_type)
+ //
+ os << "static void" << endl
+ << "bind (" << bind_vector << "," << endl;
+
+ // If we are a derived type in a polymorphic hierarchy, then
+ // we get the the external id binding.
+ //
+ if (poly_derived)
+ os << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl;
+
+ os << "image_type&," << endl
+ << db << "::statement_kind);"
+ << endl;
+
+ // bind (id_image_type)
+ //
+ if (id != 0)
+ {
+ os << "static void" << endl
+ << "bind (" << bind_vector << ", id_image_type&" <<
+ (optimistic != 0 ? ", bool bind_version = true" : "") << ");"
+ << endl;
+ }
+
+ // init (image, object)
+ //
+ os << "static bool" << endl
+ << "init (image_type&, const object_type&, " << db << "::statement_kind);"
+ << endl;
+
+ // init (object, image)
+ //
+ os << "static void" << endl
+ << "init (object_type&, const image_type&, database*";
+
+ if (poly_derived)
+ os << ", std::size_t = depth";
+
+ os << ");"
+ << endl;
+
+ // init (id_image, id)
+ //
+ if (id != 0)
+ {
+ os << "static void" << endl
+ << "init (id_image_type&, const id_type&" <<
+ (optimistic != 0 ? ", const version_type* = 0" : "") << ");"
+ << endl;
+ }
+
+ if (poly_derived)
+ {
+ // check_version
+ //
+ os << "static bool" << endl
+ << "check_version (const std::size_t*, const image_type&);"
+ << endl;
+
+ // update_version
+ //
+ os << "static void" << endl
+ << "update_version (std::size_t*, const image_type&, " <<
+ db << "::binding*);"
+ << endl;
+ }
+
+ // The rest does not apply to reuse-abstract objects.
+ //
+ if (reuse_abst)
+ {
+ object_public_extra_post (c);
+ os << "};";
+ return;
+ }
+
+ // Cache traits typedefs.
+ //
+ if (id == 0)
+ {
+ os << "typedef" << endl
+ << "odb::no_id_pointer_cache_traits<pointer_type>" << endl
+ << "pointer_cache_traits;"
+ << "typedef" << endl
+ << "odb::no_id_reference_cache_traits<object_type>" << endl
+ << "reference_cache_traits;"
+ << endl;
+ }
+ else if (poly_derived)
+ {
+ os << "typedef" << endl
+ << "odb::pointer_cache_traits<root_traits::pointer_type>" << endl
+ << "pointer_cache_traits;"
+ << "typedef" << endl
+ << "odb::reference_cache_traits<root_type>" << endl
+ << "reference_cache_traits;"
+ << endl;
+ }
+ else
+ {
+ os << "typedef" << endl
+ << "odb::pointer_cache_traits<pointer_type>" << endl
+ << "pointer_cache_traits;"
+ << "typedef" << endl
+ << "odb::reference_cache_traits<object_type>" << endl
+ << "reference_cache_traits;"
+ << endl;
+ }
+
+ // Statements typedefs.
+ //
+ if (poly)
+ {
+ if (poly_derived)
+ os << "typedef" << endl
+ << db << "::polymorphic_derived_object_statements" <<
+ "<object_type>" << endl
+ << "statements_type;"
+ << "typedef" << endl
+ << db << "::polymorphic_root_object_statements<root_type>" << endl
+ << "root_statements_type;"
+ << endl;
+ else
+ os << "typedef" << endl
+ << db << "::polymorphic_root_object_statements<object_type>" << endl
+ << "statements_type;"
+ << "typedef statements_type root_statements_type;"
+ << "typedef " << db << "::object_statements<object_type> " <<
+ "base_statements_type;"
+ << endl;
+ }
+ else
+ {
+ if (id != 0)
+ os << "typedef " << db << "::object_statements<object_type> " <<
+ "statements_type;"
+ << endl;
+ else
+ os << "typedef " << db << "::no_id_object_statements<object_type> " <<
+ "statements_type;"
+ << endl;
+ }
+
+ //
+ // Query (concrete).
+ //
+
+ if (options.generate_query ())
+ {
+ // query_base_type
+ //
+ os << "typedef " << db << "::query query_base_type;"
+ << endl;
+
+ // query_type
+ //
+ os << "struct query_type;";
+ }
+
+ //
+ // Containers (concrete).
+ //
+
+ // Statement cache (forward declaration).
+ //
+ if (id != 0)
+ os << "struct container_statement_cache_type;"
+ << endl;
+
+ // column_count
+ //
+ os << "static const std::size_t column_count = " << cc.total << "UL;"
+ << "static const std::size_t id_column_count = " << cc.id << "UL;"
+ << "static const std::size_t inverse_column_count = " <<
+ cc.inverse << "UL;"
+ << "static const std::size_t readonly_column_count = " <<
+ cc.readonly << "UL;"
+ << "static const std::size_t managed_optimistic_column_count = " <<
+ cc.optimistic_managed << "UL;";
+
+ if (poly && !poly_derived)
+ os << "static const std::size_t discriminator_column_count = " <<
+ cc.discriminator << "UL;";
+
+ os << endl;
+
+ // Statements.
+ //
+ os << "static const char persist_statement[];";
+
+ if (id != 0)
+ {
+ if (poly_derived)
+ {
+ char const* n (abst ? "1" : "depth");
+
+ os << "static const char* const find_statements[" << n << "];"
+ << "static const std::size_t find_column_counts[" << n << "];";
+ }
+ else
+ {
+ os << "static const char find_statement[];";
- os << "}";
+ if (poly)
+ os << "static const char find_discriminator_statement[];";
}
+
+ if (cc.total != cc.id + cc.inverse + cc.readonly)
+ os << "static const char update_statement[];";
+
+ os << "static const char erase_statement[];";
+
+ if (optimistic != 0 && !poly_derived)
+ os << "static const char optimistic_erase_statement[];";
+ }
+
+ if (options.generate_query ())
+ {
+ os << "static const char query_statement[];"
+ << "static const char erase_query_statement[];"
+ << endl
+ << "static const char table_name[];";
+ }
+
+ os << endl;
+
+ //
+ // Functions (concrete).
+ //
+
+ // callback ()
+ //
+ os << "static void" << endl
+ << "callback (database&, object_type&, callback_event);"
+ << endl;
+
+ os << "static void" << endl
+ << "callback (database&, const object_type&, callback_event);"
+ << endl;
+
+ // persist ()
+ //
+ os << "static void" << endl
+ << "persist (database&, " << (auto_id ? "" : "const ") << "object_type&";
+
+ if (poly)
+ os << ", bool top = true, bool dyn = true";
+
+ os << ");"
+ << endl;
+
+ if (id != 0)
+ {
+ // find (id)
+ //
+ if (c.default_ctor ())
+ os << "static pointer_type" << endl
+ << "find (database&, const id_type&);"
+ << endl;
+
+ // find (id, obj)
+ //
+ os << "static bool" << endl
+ << "find (database&, const id_type&, object_type&";
+
+ if (poly)
+ os << ", bool dyn = true";
+
+ os << ");"
+ << endl;
+
+ // reload ()
+ //
+ os << "static bool" << endl
+ << "reload (database&, object_type&";
+
+ if (poly)
+ os << ", bool dyn = true";
+
+ os << ");"
+ << endl;
+
+ // update ()
+ //
+ // In case of a polymorphic object, we generate update() even if it is
+ // readonly since the potentially-readwrite base will rely on it to
+ // initialize the id image.
+ //
+ //
+ if (!readonly (c) || poly)
+ {
+ os << "static void" << endl
+ << "update (database&, const object_type&";
+
+ if (poly)
+ os << ", bool top = true, bool dyn = true";
+
+ os << ");"
+ << endl;
+ }
+
+ // erase ()
+ //
+ os << "static void" << endl
+ << "erase (database&, const id_type&";
+
+ if (poly)
+ os << ", bool top = true, bool dyn = true";
+
+ os << ");"
+ << endl;
+
+ os << "static void" << endl
+ << "erase (database&, const object_type&";
+
+ if (poly)
+ os << ", bool top = true, bool dyn = true";
+
+ os << ");"
+ << endl;
+ }
+
+ // query ()
+ //
+ if (options.generate_query ())
+ {
+ os << "static result<object_type>" << endl
+ << "query (database&, const query_base_type&);"
+ << endl;
+
+ os << "static unsigned long long" << endl
+ << "erase_query (database&, const query_base_type&);"
+ << endl;
+ }
+
+ // create_schema ()
+ //
+ if (embedded_schema || separate_schema)
+ {
+ os << "static bool" << endl
+ << "create_schema (database&, unsigned short pass, bool drop);"
+ << endl;
+ }
+
+ object_public_extra_post (c);
+
+ // Implementation details.
+ //
+ os << "public:" << endl;
+
+ if (id != 0)
+ {
+ // Load the object image.
+ //
+ os << "static bool" << endl
+ << "find_ (";
+
+ if (poly && !poly_derived)
+ os << "base_statements_type&, ";
+ else
+ os << "statements_type&, ";
+
+ os << "const id_type*";
+
+ if (poly_derived && !abst)
+ os << ", std::size_t = depth";
+
+ os << ");"
+ << endl;
+
+ // Load the rest of the object (containers, etc). Expects the id
+ // image in the object statements to be initialized to the object
+ // id.
+ //
+ os << "static void" << endl
+ << "load_ (";
+
+ if (poly && !poly_derived)
+ os << "base_statements_type&, ";
+ else
+ os << "statements_type&, ";
+
+ os << "object_type&";
+
+ if (poly_derived)
+ os << ", std::size_t = depth";
+
+ os << ");"
+ << endl;
}
+
+ // discriminator_ ()
+ //
+ if (poly && !poly_derived)
+ {
+ os << "static void" << endl
+ << "discriminator_ (statements_type&," << endl
+ << "const id_type&," << endl
+ << "discriminator_type*";
+
+ if (optimistic != 0)
+ os << "," << endl
+ << "version_type* = 0";
+
+ os << ");"
+ << endl;
+ }
+
+ // Load the dynamic part of the object. Depth inidicates where
+ // the dynamic part starts. Expects the id image in the object
+ // statements to be initialized to the object id. We don't need
+ // it if we are poly-abstract.
+ //
+ if (poly_derived && !abst)
+ os << "static void" << endl
+ << "load_ (database&, root_type&, std::size_t);"
+ << endl;
+
+ // Image chain manipulation.
+ //
+ if (poly && need_image_clone && options.generate_query ())
+ {
+ os << "static root_traits::image_type&" << endl
+ << "root_image (image_type&);"
+ << endl;
+
+ os << "static image_type*" << endl
+ << "clone_image (const image_type&);"
+ << endl;
+
+ os << "static void" << endl
+ << "copy_image (image_type&, const image_type&);"
+ << endl;
+
+ os << "static void" << endl
+ << "free_image (image_type*);"
+ << endl;
+ }
+
+ os << "};";
+}
+
+void relational::header::
+generate ()
+{
+ context ctx;
+ ostream& os (ctx.os);
+
+ instance<include> i;
+ i->generate ();
+
+ os << "namespace odb"
+ << "{";
+
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ instance<class1> c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx.unit);
+ }
+
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ instance<class2> c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx.unit);
+ }
+
+ os << "}";
}
diff --git a/odb/relational/header.hxx b/odb/relational/header.hxx
index 250a07c..3d3189e 100644
--- a/odb/relational/header.hxx
+++ b/odb/relational/header.hxx
@@ -96,20 +96,36 @@ namespace relational
virtual void
traverse (type& c)
{
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
os << "struct image_type";
if (!view (c))
{
- instance<image_base> b;
- traversal::inherits i (*b);
- inherits (c, i);
+ // Don't go into the base if we are a derived type in a
+ // polymorphic hierarchy.
+ //
+ if (!poly_derived)
+ {
+ instance<image_base> b;
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
}
os << "{";
+ if (poly_derived)
+ os << "object_traits<base_type>::image_type* base;"
+ << endl;
+
names (c);
- if (!(composite (c) || abstract (c)))
+ // We don't need a version if this is a composite value type
+ // or reuse-abstract object.
+ //
+ if (!(composite (c) || (abstract (c) && !polymorphic (c))))
os << "std::size_t version;"
<< endl;
@@ -146,7 +162,8 @@ namespace relational
if (first_)
{
- os << ": ";
+ os << ":" << endl
+ << " ";
first_ = false;
}
else
@@ -155,8 +172,17 @@ namespace relational
<< " ";
}
- os << (ptr_ ? "pointer_query_columns" : "query_columns") <<
- "< " << class_fq_name (c) << ", table >";
+ os << (ptr_ ? "pointer_query_columns" : "query_columns") << "< " <<
+ class_fq_name (c) << ", ";
+
+ // If our base is polymorphic, then it has its own table/alias.
+ //
+ if (polymorphic (c))
+ os << "typename A::base_traits";
+ else
+ os << "A";
+
+ os << " >";
}
private:
@@ -185,10 +211,17 @@ namespace relational
os << "// " << name << endl
<< "//" << endl
- << "typedef " <<
- (ptr_ ? "pointer_query_columns" : "query_columns") <<
- "< " << class_fq_name (c) << ", table > " << name << ";"
- << endl;
+ << "typedef " <<
+ (ptr_ ? "pointer_query_columns" : "query_columns") << "< " <<
+ class_fq_name (c) << ", ";
+
+ if (polymorphic (c))
+ os << "typename A::base_traits";
+ else
+ os << "A";
+
+ os << " > " << name << ";"
+ << endl;
}
private:
@@ -212,16 +245,16 @@ namespace relational
if (ptr_)
{
- os << "template <const char* table>" << endl
- << "struct pointer_query_columns< " << type << ", table >";
+ os << "template <typename A>" << endl
+ << "struct pointer_query_columns< " << type << ", A >";
// If we don't have pointers (in the whole hierarchy), then
// pointer_query_columns and query_columns are the same.
//
- if (!has_a (c, test_pointer))
+ if (!has_a (c, test_pointer | include_base))
{
os << ":" << endl
- << " query_columns< " << type << ", table >"
+ << " query_columns< " << type << ", A >"
<< "{"
<< "};";
}
@@ -260,6 +293,10 @@ namespace relational
if (has_ptr)
{
+ // This class contains everything for inverse pointers and
+ // aliases for non-inverse ones. It doesn't depend on the
+ // table alias (A) template argument.
+ //
os << "template <>" << endl
<< "struct query_columns_base< " << type << " >"
<< "{";
@@ -270,8 +307,8 @@ namespace relational
os << "};";
}
- os << "template <const char* table>" << endl
- << "struct query_columns< " << type << ", table >";
+ os << "template <typename A>" << endl
+ << "struct query_columns< " << type << ", A >";
if (has_ptr)
os << ":" << endl
@@ -352,20 +389,20 @@ namespace relational
using semantics::class_;
// Figure out if this member is from a base object or composite
- // value and whether it is abstract.
+ // value and if it's from an object, whether it is reuse-abstract.
//
- bool base, abst;
+ bool base, reuse_abst;
if (object (c_))
{
base = cur_object != &c_ ||
!object (dynamic_cast<type&> (m.scope ()));
- abst = abstract (c_);
+ reuse_abst = abstract (c_) && !polymorphic (c_);
}
else
{
- base = false; // We don't go into bases.
- abst = true; // Always abstract.
+ base = false; // We don't go into bases.
+ reuse_abst = true; // Always abstract.
}
container_kind_type ck (container_kind (c));
@@ -407,7 +444,7 @@ namespace relational
//
size_t id_columns, data_columns, cond_columns;
- if (!abst)
+ if (!reuse_abst)
{
type& idt (container_idt (m));
@@ -513,7 +550,7 @@ namespace relational
container_public_extra_pre (m);
- if (!abst)
+ if (!reuse_abst)
{
// column_count
//
@@ -572,8 +609,9 @@ namespace relational
os << " container_type;";
- os << "typedef odb::access::container_traits< container_type > " <<
- "container_traits_type;";
+ os << "typedef" << endl
+ << "odb::access::container_traits<container_type>" << endl
+ << "container_traits_type;";
switch (ck)
{
@@ -912,6 +950,7 @@ namespace relational
class1 ()
: id_image_member_ ("id_"),
version_image_member_ ("version_"),
+ discriminator_image_member_ ("discriminator_"),
query_columns_type_ (false),
pointer_query_columns_type_ (true)
{
@@ -922,6 +961,7 @@ namespace relational
context (),
id_image_member_ ("id_"),
version_image_member_ ("version_"),
+ discriminator_image_member_ ("discriminator_"),
query_columns_type_ (false),
pointer_query_columns_type_ (true)
{
@@ -952,396 +992,7 @@ namespace relational
}
virtual void
- traverse_object (type& c)
- {
- bool abstract (context::abstract (c));
- string const& type (class_fq_name (c));
-
- semantics::data_member* id (id_member (c));
- bool auto_id (id ? id->count ("auto") : false);
- bool base_id (id ? &id->scope () != &c : false); // Comes from base.
-
- semantics::data_member* optimistic (context::optimistic (c));
-
- column_count_type const& cc (column_count (c));
-
- os << "// " << class_name (c) << endl
- << "//" << endl;
-
- // class_traits
- //
- os << "template <>" << endl
- << "struct class_traits< " << type << " >"
- << "{"
- << "static const class_kind kind = class_object;"
- << "};";
-
- // pointer_query_columns & query_columns
- //
- if (options.generate_query ())
- {
- // If we don't have object pointers, then also generate
- // query_columns (in this case pointer_query_columns and
- // query_columns are the same and the former inherits from
- // the latter). Otherwise we have to postpone query_columns
- // generation until the second pass to deal with forward-
- // declared objects.
- //
- if (!has_a (c, test_pointer))
- query_columns_type_->traverse (c);
-
- pointer_query_columns_type_->traverse (c);
- }
-
- // object_traits
- //
- os << "template <>" << endl
- << "class access::object_traits< " << type << " >"
- << "{"
- << "public:" << endl;
-
- object_public_extra_pre (c);
-
- // object_type & pointer_type
- //
- os << "typedef " << type << " object_type;"
- << "typedef " << c.get<string> ("object-pointer") << " pointer_type;";
-
- // id_type & id_image_type
- //
- if (id != 0)
- {
- if (base_id)
- {
- semantics::class_& b (
- dynamic_cast<semantics::class_&> (id->scope ()));
- string const& type (class_fq_name (b));
-
- os << "typedef object_traits< " << type << " >::id_type id_type;";
-
- if (optimistic != 0)
- os << "typedef object_traits< " << type << " >::version_type " <<
- "version_type;";
-
- os << endl
- << "static const bool auto_id = object_traits< " << type <<
- " >::auto_id;"
- << endl
- << "typedef object_traits< " << type << " >::id_image_type " <<
- "id_image_type;"
- << endl;
- }
- else
- {
- {
- semantics::names* hint;
- semantics::type& t (utype (*id, hint));
-
- os << "typedef " << t.fq_name (hint) << " id_type;";
- }
-
- if (optimistic != 0)
- {
- semantics::names* hint;
- semantics::type& t (utype (*optimistic, hint));
-
- os << "typedef " << t.fq_name (hint) << " version_type;";
- }
-
- os << endl
- << "static const bool auto_id = " <<
- (auto_id ? "true" : "false") << ";"
- << endl;
-
- os << "struct id_image_type"
- << "{";
-
- id_image_member_->traverse (*id);
-
- if (optimistic != 0)
- version_image_member_->traverse (*optimistic);
-
- os << "std::size_t version;"
- << "};";
- }
- }
- else if (!abstract)
- {
- // Object without id.
- //
- os << "typedef void id_type;"
- << endl
- << "static const bool auto_id = false;"
- << endl;
- }
-
- // image_type
- //
- image_type_->traverse (c);
-
- //
- // Containers (abstract and concrete).
- //
-
- {
- instance<container_traits> t (c);
- t->traverse (c);
- }
-
- //
- // Functions (abstract and concrete).
- //
-
- // id ()
- //
- if (id != 0 || !abstract)
- // We want to generate a dummy void id() accessor even if this
- // object has no id to help us in the runtime. This way we can
- // write generic code that will work for both void and non-void
- // ids.
- //
- os << "static id_type" << endl
- << "id (const object_type&);"
- << endl;
-
- if (id != 0 && options.generate_query ())
- os << "static id_type" << endl
- << "id (const image_type&);"
- << endl;
-
- if (id != 0 && optimistic != 0)
- os << "static version_type" << endl
- << "version (const image_type&);"
- << endl;
-
- // grow ()
- //
- if (generate_grow)
- {
- os << "static bool" << endl
- << "grow (image_type&, " << truncated_vector << ");"
- << endl;
- }
-
- // bind (image_type)
- //
- os << "static void" << endl
- << "bind (" << bind_vector << ", image_type&, "
- << db << "::statement_kind);"
- << endl;
-
- // bind (id_image_type)
- //
- if (id != 0)
- {
- os << "static void" << endl
- << "bind (" << bind_vector << ", id_image_type&);"
- << endl;
- }
-
- // init (image, object)
- //
- os << "static bool" << endl
- << "init (image_type&, const object_type&, " <<
- db << "::statement_kind);"
- << endl;
-
- // init (object, image)
- //
- os << "static void" << endl
- << "init (object_type&, const image_type&, database*);"
- << endl;
-
- // init (id_image, id)
- //
- if (id != 0)
- {
- os << "static void" << endl
- << "init (id_image_type&, const id_type&" <<
- (optimistic != 0 ? ", const version_type* = 0" : "") << ");"
- << endl;
- }
-
- //
- // The rest only applies to concrete objects.
- //
- if (abstract)
- {
- object_public_extra_post (c);
- os << "};";
- return;
- }
-
- //
- // Query (concrete).
- //
-
- if (options.generate_query ())
- {
- // query_base_type
- //
- os << "typedef " << db << "::query query_base_type;"
- << endl;
-
- // query_type
- //
- os << "struct query_type;";
- }
-
- //
- // Containers (concrete).
- //
-
- // Statement cache (forward declaration).
- //
- if (id != 0)
- os << "struct container_statement_cache_type;"
- << endl;
-
- // column_count
- //
- os << "static const std::size_t column_count = " << cc.total << "UL;"
- << "static const std::size_t id_column_count = " << cc.id << "UL;"
- << "static const std::size_t inverse_column_count = " <<
- cc.inverse << "UL;"
- << "static const std::size_t readonly_column_count = " <<
- cc.readonly << "UL;"
- << "static const std::size_t managed_optimistic_column_count = " <<
- cc.optimistic_managed << "UL;"
- << endl;
-
- // Statements.
- //
- os << "static const char persist_statement[];";
-
- if (id != 0)
- {
- os << "static const char find_statement[];";
-
- if (cc.total != cc.id + cc.inverse + cc.readonly)
- os << "static const char update_statement[];";
-
- os << "static const char erase_statement[];";
-
- if (optimistic != 0)
- os << "static const char optimistic_erase_statement[];";
- }
-
- if (options.generate_query ())
- {
- os << "static const char query_statement[];"
- << "static const char erase_query_statement[];"
- << endl
- << "static const char table_name[];";
- }
-
- os << endl;
-
- //
- // Functions (concrete).
- //
-
- // callback ()
- //
- os << "static void" << endl
- << "callback (database&, object_type&, callback_event);"
- << endl;
-
- os << "static void" << endl
- << "callback (database&, const object_type&, callback_event);"
- << endl;
-
- // persist ()
- //
- os << "static void" << endl
- << "persist (database&, " << (auto_id ? "" : "const ") <<
- "object_type&);"
- << endl;
-
- if (id != 0)
- {
- // find ()
- //
- if (c.default_ctor ())
- os << "static pointer_type" << endl
- << "find (database&, const id_type&);"
- << endl;
-
- os << "static bool" << endl
- << "find (database&, const id_type&, object_type&);"
- << endl;
-
- os << "static bool" << endl
- << "reload (database&, object_type&);"
- << endl;
-
- // update ()
- //
- if (!readonly (c))
- os << "static void" << endl
- << "update (database&, const object_type&);"
- << endl;
-
- // erase ()
- //
- os << "static void" << endl
- << "erase (database&, const id_type&);"
- << endl;
-
- os << "static void" << endl
- << "erase (database&, const object_type&);"
- << endl;
- }
-
- // query ()
- //
- if (options.generate_query ())
- {
- os << "static result<object_type>" << endl
- << "query (database&, const query_base_type&);"
- << endl;
-
- os << "static unsigned long long" << endl
- << "erase_query (database&, const query_base_type&);"
- << endl;
- }
-
- // create_schema ()
- //
- if (embedded_schema || separate_schema)
- {
- os << "static bool" << endl
- << "create_schema (database&, unsigned short pass, bool drop);"
- << endl;
- }
-
- object_public_extra_post (c);
-
- // Implementation details.
- //
- os << "public:" << endl;
-
- if (id != 0)
- {
- // Load the object image.
- //
- os << "static bool" << endl
- << "find_ (" << db << "::object_statements< object_type >&, " <<
- "const id_type&);"
- << endl;
-
- // Load the rest of the object (containers, etc). Expects the id
- // image in the object statements to be initialized to the object
- // id.
- //
- os << "static void" << endl
- << "load_ (" << db << "::object_statements< object_type >&, " <<
- "object_type&);"
- << endl;
- }
-
- os << "};";
- }
+ traverse_object (type& c);
virtual void
view_public_extra_pre (type&)
@@ -1393,100 +1044,17 @@ namespace relational
os << "typedef " << db << "::query query_base_type;"
<< endl;
- // query_type
+ // query_type (definition generated by class2).
//
size_t obj_count (c.get<size_t> ("object-count"));
if (obj_count != 0)
{
- view_objects& objs (c.get<view_objects> ("objects"));
-
if (obj_count > 1)
- {
- os << "struct query_columns"
- << "{";
-
- for (view_objects::const_iterator i (objs.begin ());
- i < objs.end ();
- ++i)
- {
- if (i->kind != view_object::object)
- continue; // Skip tables.
+ os << "struct query_columns;";
- bool alias (!i->alias.empty ());
- semantics::class_& o (*i->obj);
- string const& name (alias ? i->alias : class_name (o));
- string const& type (class_fq_name (o));
- qname const& table (table_name (o));
-
- os << "// " << name << endl
- << "//" << endl;
-
- if (alias && (table.qualified () || i->alias != table.uname ()))
- os << "static const char " << name << "_alias_[];"
- << endl
- << "typedef" << endl
- << "odb::pointer_query_columns< " << type << ", " <<
- name << "_alias_ >" << endl
- << name << ";"
- << endl;
- else
- os << "typedef" << endl
- << "odb::pointer_query_columns<" << endl
- << " " << type << "," << endl
- << " " << "odb::access::object_traits< " << type <<
- " >::table_name >" << endl
- << name << ";"
- << endl;
- }
-
- os << "};"
- << "struct query_type: query_base_type, query_columns"
- << "{";
- }
- else
- {
- // For a single object view we generate a shortcut without
- // an intermediate typedef.
- //
- view_object const* vo (0);
- for (view_objects::const_iterator i (objs.begin ());
- vo == 0 && i < objs.end ();
- ++i)
- {
- if (i->kind == view_object::object)
- vo = &*i;
- }
-
- bool alias (!vo->alias.empty ());
- semantics::class_& o (*vo->obj);
- string const& type (class_fq_name (o));
- qname const& table (table_name (o));
-
- if (alias && (table.qualified () || vo->alias != table.uname ()))
- os << "static const char query_alias[];"
- << endl
- << "struct query_type:" << endl
- << " query_base_type," << endl
- << " odb::pointer_query_columns< " << type <<
- ", query_alias >"
- << "{";
- else
- os << "struct query_type:" << endl
- << " query_base_type," << endl
- << " odb::pointer_query_columns<" << endl
- << " " << type << "," << endl
- << " odb::access::object_traits< " << type <<
- " >::table_name >"
- << "{";
- }
-
- os << "query_type ();"
- << "query_type (bool);"
- << "query_type (const char*);"
- << "query_type (const std::string&);"
- << "query_type (const query_base_type&);"
- << "};";
+ os << "struct query_type;"
+ << endl;
}
else
os << "typedef query_base_type query_type;"
@@ -1626,6 +1194,7 @@ namespace relational
instance<image_type> image_type_;
instance<image_member> id_image_member_;
instance<image_member> version_image_member_;
+ instance<image_member> discriminator_image_member_;
instance<query_columns_type> query_columns_type_;
instance<query_columns_type> pointer_query_columns_type_;
@@ -1663,14 +1232,15 @@ namespace relational
virtual void
traverse_object (type& c)
{
- bool abst (abstract (c));
+ bool reuse_abst (abstract (c) && !polymorphic (c));
+
string const& type (class_fq_name (c));
if (options.generate_query ())
{
- bool has_ptr (has_a (c, test_pointer));
+ bool has_ptr (has_a (c, test_pointer | include_base));
- if (has_ptr || !abst)
+ if (has_ptr || !reuse_abst)
os << "// " << class_name (c) << endl
<< "//" << endl;
@@ -1680,15 +1250,22 @@ namespace relational
// in pass 1 (see the comment in class1 for details).
//
if (has_ptr)
+ {
+ instance<query_alias_traits> t (alias_tags_, alias_specs_);
+ t->traverse (c);
+
query_columns_type_->traverse (c);
+ }
// query_type
//
- if (!abst)
+ if (!reuse_abst)
os << "struct access::object_traits< " << type << " >::" <<
"query_type:" << endl
<< " query_base_type," << endl
- << " query_columns< " << type << ", table_name >"
+ << " query_columns<" << endl
+ << " " << type << "," << endl
+ << " access::object_traits< " << type << "> >"
<< "{"
<< "query_type ();"
<< "query_type (bool);"
@@ -1702,8 +1279,141 @@ namespace relational
}
virtual void
- traverse_view (type&)
+ traverse_view (type& c)
{
+ string const& type (class_fq_name (c));
+
+ // query_type
+ //
+ size_t obj_count (c.get<size_t> ("object-count"));
+
+ if (obj_count != 0)
+ {
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ // Generate the alias tags and alias_traits specializations.
+ //
+ {
+ instance<query_alias_traits> at (alias_tags_, alias_specs_);
+
+ for (view_objects::const_iterator i (objs.begin ());
+ i < objs.end ();
+ ++i)
+ {
+ if (i->kind != view_object::object)
+ continue; // Skip tables.
+
+ if (i->alias.empty ())
+ continue;
+
+ semantics::class_& o (*i->obj);
+ qname const& t (table_name (o));
+
+ // Check that the alias is not the same as the table name
+ // (if this is a polymorphic object, then the alias is just
+ // a prefix).
+ //
+ if (polymorphic (o) || t.qualified () || i->alias != t.uname ())
+ at->generate (i->alias, o);
+ }
+ }
+
+ if (obj_count > 1)
+ {
+ os << "struct access::view_traits< " << type << " >::query_columns"
+ << "{";
+
+ for (view_objects::const_iterator i (objs.begin ());
+ i < objs.end ();
+ ++i)
+ {
+ if (i->kind != view_object::object)
+ continue; // Skip tables.
+
+ bool alias (!i->alias.empty ());
+ semantics::class_& o (*i->obj);
+ string const& oname (alias ? i->alias : class_name (o));
+ string const& otype (class_fq_name (o));
+ qname const& table (table_name (o));
+
+ os << "// " << oname << endl
+ << "//" << endl
+ << "typedef" << endl
+ << "odb::pointer_query_columns<" << endl
+ << " " << otype << "," << endl;
+
+ if (alias && (polymorphic (o) ||
+ table.qualified () ||
+ i->alias != table.uname ()))
+ {
+ string tag (escape (i->alias + "_alias_tag"));
+ os << " odb::alias_traits< " << otype << ", " <<
+ tag << " > >" << endl;
+ }
+ else
+ os << " odb::access::object_traits< " << otype <<
+ " > >" << endl;
+
+ os << oname << ";"
+ << endl;
+ }
+
+ os << "};";
+
+ os << "struct access::view_traits< " << type << " >::" <<
+ "query_type:" << endl
+ << " query_base_type, query_columns";
+ }
+ else
+ {
+ // For a single object view we generate a shortcut without
+ // an intermediate typedef.
+ //
+ view_object const* vo (0);
+ for (view_objects::const_iterator i (objs.begin ());
+ vo == 0 && i < objs.end ();
+ ++i)
+ {
+ if (i->kind == view_object::object)
+ vo = &*i;
+ }
+
+ bool alias (!vo->alias.empty ());
+ semantics::class_& o (*vo->obj);
+ string const& otype (class_fq_name (o));
+ qname const& table (table_name (o));
+
+ os << "struct access::view_traits< " << type << " >::" <<
+ "query_type:" << endl
+ << " query_base_type," << endl
+ << " odb::pointer_query_columns<" << endl
+ << " " << otype << "," << endl;
+
+ if (alias && (polymorphic (o) ||
+ table.qualified () ||
+ vo->alias != table.uname ()))
+ {
+ string tag (escape (vo->alias + "_alias_tag"));
+ os << " odb::alias_traits< " << otype << ", " <<
+ tag << " > >";
+ }
+ else
+ os << " odb::access::object_traits< " << otype << " > >";
+ }
+
+ os << "{"
+ << "query_type ();"
+ << "query_type (bool);"
+ << "query_type (const char*);"
+ << "query_type (const std::string&);"
+ << "query_type (const query_base_type&);"
+ << "};";
+ }
+
+ // Move header comment out of if-block if adding any code here.
}
virtual void
@@ -1713,6 +1423,9 @@ namespace relational
private:
instance<query_columns_type> query_columns_type_;
+
+ std::set<string> alias_tags_;
+ std::set<string> alias_specs_;
};
struct include: virtual context
@@ -1728,6 +1441,7 @@ namespace relational
os << "#include <odb/" << db << "/version.hxx>" << endl
<< "#include <odb/" << db << "/forward.hxx>" << endl
+ << "#include <odb/" << db << "/binding.hxx>" << endl
<< "#include <odb/" << db << "/" << db << "-types.hxx>" << endl;
if (options.generate_query ())
diff --git a/odb/relational/inline.cxx b/odb/relational/inline.cxx
index d1626d8..ca37e9d 100644
--- a/odb/relational/inline.cxx
+++ b/odb/relational/inline.cxx
@@ -17,6 +17,9 @@ namespace relational
context ctx;
ostream& os (ctx.os);
+ instance<include> i;
+ i->generate ();
+
traversal::unit unit;
traversal::defines unit_defines;
typedefs unit_typedefs (false);
diff --git a/odb/relational/inline.hxx b/odb/relational/inline.hxx
index 5691d3e..0c585b8 100644
--- a/odb/relational/inline.hxx
+++ b/odb/relational/inline.hxx
@@ -102,10 +102,6 @@ namespace relational
virtual void
traverse_object (type& c)
{
- bool abstract (context::abstract (c));
- string const& type (class_fq_name (c));
- string traits ("access::object_traits< " + type + " >");
-
semantics::data_member* id (id_member (c));
bool base_id (id ? &id->scope () != &c : false); // Comes from base.
@@ -114,10 +110,18 @@ namespace relational
// Base class the contains the object id and version for optimistic
// concurrency.
//
- semantics::class_* base (
- id != 0 && base_id
- ? dynamic_cast<semantics::class_*> (&id->scope ())
- : 0);
+ type* base (
+ id != 0 && base_id ? dynamic_cast<type*> (&id->scope ()) : 0);
+
+ type* poly_root (context::polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ bool abst (abstract (c));
+ bool reuse_abst (abst && !poly);
+
+ string const& type (class_fq_name (c));
+ string traits ("access::object_traits< " + type + " >");
os << "// " << class_name (c) << endl
<< "//" << endl
@@ -127,7 +131,7 @@ namespace relational
// id (object_type)
//
- if (id != 0 || !abstract)
+ if (id != 0 || !reuse_abst)
{
os << "inline" << endl
<< traits << "::id_type" << endl
@@ -147,77 +151,128 @@ namespace relational
os << "}";
}
- if (id != 0)
+ if (id != 0 && base_id)
{
- // id (image_type)
- //
- if (options.generate_query () && base_id)
+ if (!poly_derived)
{
- os << "inline" << endl
- << traits << "::id_type" << endl
- << traits << "::" << endl
- << "id (const image_type& i)"
- << "{"
- << "return object_traits< " << class_fq_name (*base) <<
- " >::id (i);"
- << "}";
+ // id (image_type)
+ //
+ if (options.generate_query ())
+ {
+ os << "inline" << endl
+ << traits << "::id_type" << endl
+ << traits << "::" << endl
+ << "id (const image_type& i)"
+ << "{"
+ << "return object_traits< " << class_fq_name (*base) <<
+ " >::id (i);"
+ << "}";
+ }
+
+ // version (image_type)
+ //
+ if (optimistic != 0)
+ {
+ os << "inline" << endl
+ << traits << "::version_type" << endl
+ << traits << "::" << endl
+ << "version (const image_type& i)"
+ << "{"
+ << "return object_traits< " << class_fq_name (*base) <<
+ " >::version (i);"
+ << "}";
+ }
}
- // version (image_type)
+ // bind (id_image_type)
//
- if (optimistic != 0 && base_id)
- {
- os << "inline" << endl
- << traits << "::version_type" << endl
- << traits << "::" << endl
- << "version (const image_type& i)"
- << "{"
- << "return object_traits< " << class_fq_name (*base) <<
- " >::version (i);"
- << "}";
- }
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "bind (" << bind_vector << " b, id_image_type& i" <<
+ (optimistic != 0 ? ", bool bv" : "") << ")"
+ << "{"
+ << "object_traits< " << class_fq_name (*base) <<
+ " >::bind (b, i" << (optimistic != 0 ? ", bv" : "") << ");"
+ << "}";
- // bind (id_image_type)
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "init (id_image_type& i, const id_type& id" <<
+ (optimistic != 0 ? ", const version_type* v" : "") << ")"
+ << "{"
+ << "object_traits< " << class_fq_name (*base) <<
+ " >::init (i, id" << (optimistic != 0 ? ", v" : "") << ");"
+ << "}";
+ }
+
+ if (poly_derived)
+ {
+ size_t depth (polymorphic_depth (c));
+
+ // check_version
//
- if (base_id)
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "check_version (const std::size_t* v, const image_type& i)"
+ << "{"
+ << "return ";
+
+ string image ("i.");
+ for (size_t i (0); i < depth; ++i)
{
- os << "inline" << endl
- << "void " << traits << "::" << endl
- << "bind (" << bind_vector << " b, id_image_type& i)"
- << "{"
- << "object_traits< " << class_fq_name (*base) <<
- " >::bind (b, i);"
- << "}";
+ os << (i == 0 ? "" : " ||") << endl
+ << " v[" << i << "UL] != " << image << "version";
+
+ image += "base->";
}
- if (base_id)
+ os << ";"
+ << "}";
+
+ // update_version
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "update_version (std::size_t* v, const image_type& i, " <<
+ db << "::binding* b)"
+ << "{";
+
+ image = "i.";
+ for (size_t i (0); i < depth; ++i)
{
- os << "inline" << endl
- << "void " << traits << "::" << endl
- << "init (id_image_type& i, const id_type& id" <<
- (optimistic != 0 ? ", const version_type* v" : "") << ")"
- << "{"
- << "object_traits< " << class_fq_name (*base) <<
- " >::init (i, id" << (optimistic != 0 ? ", v" : "") << ");"
- << "}";
+ os << "v[" << i << "UL] = " << image << "version;";
+ image += "base->";
}
+
+ // A poly-abstract class always has only one entry in the
+ // bindings array.
+ //
+ if (abst)
+ os << "b[0].version++;";
+ else
+ for (size_t i (0); i < depth; ++i)
+ os << "b[" << i << "UL].version++;";
+
+ os << "}";
}
//
- // The rest only applies to concrete objects.
+ // The rest does not apply to reuse-abstract objects.
//
- if (abstract)
+ if (reuse_abst)
return;
// erase (object_type)
//
- if (id != 0 && optimistic == 0)
+ if (id != 0 && !poly && optimistic == 0)
{
os << "inline" << endl
<< "void " << traits << "::" << endl
<< "erase (database& db, const object_type& obj)"
<< "{"
+ << "callback (db, obj, callback_event::pre_erase);"
<< "erase (db, id (obj));"
+ << "callback (db, obj, callback_event::post_erase);"
<< "}";
}
@@ -287,15 +342,92 @@ namespace relational
// load_()
//
- if (id != 0 && !has_a (c, test_container))
+ if (id != 0 && !(poly_derived || has_a (c, test_container)))
{
os << "inline" << endl
<< "void " << traits << "::" << endl
- << "load_ (" << db << "::object_statements< object_type >&, " <<
- "object_type&)"
+ << "load_ (";
+
+ if (poly && !poly_derived)
+ os << "base_statements_type&, ";
+ else
+ os << "statements_type&, ";
+
+ os << "object_type&)"
<< "{"
<< "}";
}
+
+ if (poly && need_image_clone && options.generate_query ())
+ {
+ // root_image ()
+ //
+ os << "inline" << endl
+ << traits << "::root_traits::image_type&" << endl
+ << traits << "::" << endl
+ << "root_image (image_type& i)"
+ << "{";
+
+ if (poly_derived)
+ os << "return object_traits<base_type>::root_image (*i.base);";
+ else
+ os << "return i;";
+
+ os << "}";
+
+ // clone_image ()
+ //
+ os << "inline" << endl
+ << traits << "::image_type*" << endl
+ << traits << "::" << endl
+ << "clone_image (const image_type& i)"
+ << "{";
+
+ if (poly_derived)
+ os << "typedef object_traits<base_type> base_traits;"
+ << endl
+ << "details::unique_ptr<base_traits::image_type> p (" << endl
+ << "base_traits::clone_image (*i.base));"
+ << "image_type* c (new image_type (i));"
+ << "c->base = p.release ();"
+ << "return c;";
+ else
+ os << "return new image_type (i);";
+
+ os << "}";
+
+ // copy_image ()
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "copy_image (image_type& d, const image_type& s)"
+ << "{";
+
+ if (poly_derived)
+ os << "typedef object_traits<base_type> base_traits;"
+ << endl
+ << "base_traits::image_type* b (d.base);"
+ << "base_traits::copy_image (*b, *s.base);"
+ << "d = s;" // Overwrites the base pointer.
+ << "d.base = b;";
+ else
+ os << "d = s;";
+
+ os << "}";
+
+ // free_image ()
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "free_image (image_type* i)"
+ << "{";
+
+ if (poly_derived)
+ os << "object_traits<base_type>::free_image (i->base);";
+
+ os << "delete i;"
+ << "}";
+ }
}
virtual void
@@ -379,6 +511,19 @@ namespace relational
private:
instance<callback_calls> callback_calls_;
};
+
+ struct include: virtual context
+ {
+ typedef include base;
+
+ virtual void
+ generate ()
+ {
+ if (features.polymorphic_object && options.generate_query ())
+ os << "#include <odb/details/unique-ptr.hxx>" << endl
+ << endl;
+ }
+ };
}
}
diff --git a/odb/relational/model.cxx b/odb/relational/model.cxx
index 235350d..bd91b3c 100644
--- a/odb/relational/model.cxx
+++ b/odb/relational/model.cxx
@@ -147,7 +147,7 @@ namespace relational
semantics::node& d (*e.dup.get<semantics::node*> ("cxx-node"));
cerr << d.file () << ":" << d.line () << ":" << d.column ()
- << ": error: " << e.dup.kind () << " name '" << e.orig_name
+ << ": error: " << e.dup.kind () << " name '" << e.name
<< "' conflicts with an already defined " << e.orig.kind ()
<< " name"
<< endl;
diff --git a/odb/relational/model.hxx b/odb/relational/model.hxx
index 66db3d6..afaa90d 100644
--- a/odb/relational/model.hxx
+++ b/odb/relational/model.hxx
@@ -218,6 +218,7 @@ namespace relational
traverse_pointer (semantics::data_member& m, semantics::class_& c)
{
using sema_rel::column;
+ using sema_rel::foreign_key;
// Ignore inverse object pointers.
//
@@ -227,11 +228,13 @@ namespace relational
string id (id_prefix_ +
(key_prefix_.empty () ? m.name () : key_prefix_));
- sema_rel::foreign_key& fk (
- model_.new_node<sema_rel::foreign_key> (
- id,
- table_name (c),
- true)); // deferred
+ bool deferred (m.get<bool> ("deferred", true));
+ foreign_key::action on_delete (
+ m.get<foreign_key::action> ("on-delete", foreign_key::no_action));
+
+ foreign_key& fk (
+ model_.new_node<foreign_key> (
+ id, table_name (c), deferred, on_delete));
fk.set ("cxx-node", static_cast<semantics::node*> (&m));
@@ -529,7 +532,10 @@ namespace relational
if (class_file (c) != unit.file ())
return;
- if (!object (c) || abstract (c))
+ if (!object (c))
+ return;
+
+ if (abstract (c) && !polymorphic (c))
return;
qname const& name (table_name (c));
diff --git a/odb/relational/mssql/context.cxx b/odb/relational/mssql/context.cxx
index 1fddbe9..0b36366 100644
--- a/odb/relational/mssql/context.cxx
+++ b/odb/relational/mssql/context.cxx
@@ -73,8 +73,9 @@ namespace relational
context (ostream& os,
semantics::unit& u,
options_type const& ops,
+ features_type& f,
sema_rel::model* m)
- : root_context (os, u, ops, data_ptr (new (shared) data (os))),
+ : root_context (os, u, ops, f, data_ptr (new (shared) data (os))),
base_context (static_cast<data*> (root_context::data_.get ()), m),
data_ (static_cast<data*> (base_context::data_))
{
@@ -85,6 +86,7 @@ namespace relational
need_alias_as = true;
insert_send_auto_id = false;
delay_freeing_statement_result = true;
+ need_image_clone = true;
data_->bind_vector_ = "mssql::bind*";
// Populate the C++ type to DB type map.
diff --git a/odb/relational/mssql/context.hxx b/odb/relational/mssql/context.hxx
index fe30746..b0cba1e 100644
--- a/odb/relational/mssql/context.hxx
+++ b/odb/relational/mssql/context.hxx
@@ -118,6 +118,7 @@ namespace relational
context (std::ostream&,
semantics::unit&,
options_type const&,
+ features_type&,
sema_rel::model*);
static context&
diff --git a/odb/relational/mssql/header.cxx b/odb/relational/mssql/header.cxx
index 08ef306..957f880 100644
--- a/odb/relational/mssql/header.cxx
+++ b/odb/relational/mssql/header.cxx
@@ -22,24 +22,31 @@ namespace relational
virtual void
image_extra (type& c)
{
- if (!(composite (c) || abstract (c)))
+ if (!(composite (c) || (abstract (c) && !polymorphic (c))))
{
- bool gc (options.generate_query ());
+ type* poly_root (polymorphic (c));
- if (gc)
- os << "mssql::change_callback change_callback_;"
- << endl;
+ // If this is a polymorphic type, only add callback to the root.
+ //
+ if (poly_root == 0 || poly_root == &c)
+ {
+ bool gc (options.generate_query ());
- os << "mssql::change_callback*" << endl
- << "change_callback ()"
- << "{";
+ if (gc)
+ os << "mssql::change_callback change_callback_;"
+ << endl;
- if (gc)
- os << "return &change_callback_;";
- else
- os << "return 0;";
+ os << "mssql::change_callback*" << endl
+ << "change_callback ()"
+ << "{";
- os << "}";
+ if (gc)
+ os << "return &change_callback_;";
+ else
+ os << "return 0;";
+
+ os << "}";
+ }
}
}
};
diff --git a/odb/relational/mssql/source.cxx b/odb/relational/mssql/source.cxx
index 2d8d41d..af43d12 100644
--- a/odb/relational/mssql/source.cxx
+++ b/odb/relational/mssql/source.cxx
@@ -2,8 +2,6 @@
// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC
// license : GNU GPL v3; see accompanying LICENSE file
-#include <sstream>
-
#include <odb/relational/source.hxx>
#include <odb/relational/mssql/common.hxx>
@@ -69,134 +67,17 @@ namespace relational
"mssql::bind::bigint"
};
- struct bind_member: relational::bind_member, member_base
+ struct bind_member: relational::bind_member_impl<sql_type>,
+ member_base
{
bind_member (base const& x)
- : member_base::base (x), // virtual base
- base (x),
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
member_base (x)
{
}
- virtual bool
- pre (member_info& mi)
- {
- if (container (mi))
- return false;
-
- ostringstream ostr;
- ostr << "b[n]";
- b = ostr.str ();
-
- arg = arg_override_.empty () ? string ("i") : arg_override_;
-
- if (var_override_.empty ())
- {
- os << "// " << mi.m.name () << endl
- << "//" << endl;
-
- if (id (mi.m) && auto_ (mi.m))
- // For SQL Server we don't send auto id in INSERT.
- //
- os << "if (sk != statement_insert && sk != statement_update)"
- << "{";
- else if (inverse (mi.m, key_prefix_) || version (mi.m))
- os << "if (sk == statement_select)"
- << "{";
- // If the whole class is readonly, then we will never be
- // called with sk == statement_update.
- //
- else if (!readonly (*context::top_object))
- {
- semantics::class_* c;
-
- if (id (mi.m) ||
- readonly (mi.m) ||
- ((c = composite (mi.t)) && readonly (*c)))
- os << "if (sk != statement_update)"
- << "{";
- }
- }
-
- return true;
- }
-
- virtual void
- post (member_info& mi)
- {
- if (var_override_.empty ())
- {
- semantics::class_* c;
-
- if ((c = composite (mi.t)))
- {
- bool ro (readonly (*c));
- column_count_type const& cc (column_count (*c));
-
- os << "n += " << cc.total << "UL";
-
- // select = total
- // insert = total - inverse
- // update = total - inverse - readonly
- //
- if (cc.inverse != 0 || (!ro && cc.readonly != 0))
- {
- os << " - (" << endl
- << "sk == statement_select ? 0 : ";
-
- if (cc.inverse != 0)
- os << cc.inverse << "UL";
-
- if (!ro && cc.readonly != 0)
- {
- if (cc.inverse != 0)
- os << " + ";
-
- os << "(" << endl
- << "sk == statement_insert ? 0 : " <<
- cc.readonly << "UL)";
- }
-
- os << ")";
- }
-
- os << ";";
- }
- else
- os << "n++;";
-
- bool block (false);
-
- // The same logic as in pre().
- //
- if (id (mi.m) && auto_ (mi.m))
- block = true;
- else if (inverse (mi.m, key_prefix_) || version (mi.m))
- block = true;
- else if (!readonly (*context::top_object))
- {
- semantics::class_* c;
-
- if (id (mi.m) ||
- readonly (mi.m) ||
- ((c = composite (mi.t)) && readonly (*c)))
- block = true;
- }
-
- if (block)
- os << "}";
- else
- os << endl;
- }
- }
-
- virtual void
- traverse_composite (member_info& mi)
- {
- os << "composite_value_traits< " << mi.fq_type () <<
- " >::bind (b + n, " << arg << "." << mi.var << "value, sk);";
- }
-
virtual void
traverse_integer (member_info& mi)
{
@@ -401,10 +282,6 @@ namespace relational
<< b << ".buffer = &" << arg << "." << mi.var << "value;"
<< b << ".size_ind = &" << arg << "." << mi.var << "size_ind;";
}
-
- private:
- string b;
- string arg;
};
entry<bind_member> bind_member_;
@@ -958,16 +835,34 @@ namespace relational
virtual void
init_image_pre (type& c)
{
- if (options.generate_query () && !(composite (c) || abstract (c)))
- os << "if (i.change_callback_.callback != 0)" << endl
- << "(i.change_callback_.callback) (i.change_callback_.context);"
- << endl;
+ if (options.generate_query () &&
+ !(composite (c) || (abstract (c) && !polymorphic (c))))
+ {
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ if (poly_derived)
+ os << "{"
+ << "root_traits::image_type& ri (root_image (i));"
+ << endl;
+
+ string i (poly_derived ? "ri" : "i");
+
+ os << "if (" << i << ".change_callback_.callback != 0)" << endl
+ << "(" << i << ".change_callback_.callback) (" <<
+ i << ".change_callback_.context);";
+
+ if (poly_derived)
+ os << "}";
+ else
+ os << endl;
+ }
}
virtual void
init_value_extra ()
{
- os << "sts.find_statement ().stream_result ();";
+ os << "st.stream_result ();";
}
virtual void
@@ -980,6 +875,13 @@ namespace relational
if (id == 0 || !auto_ (*id))
return;
+ // If we are a derived type in a polymorphic hierarchy, then
+ // auto id is handled by the root.
+ //
+ if (type* root = polymorphic (c))
+ if (root != &c)
+ return;
+
// SQL Server 2005 has a bug that causes it to fail on an
// INSERT statement with the OUTPUT clause if data for one
// of the inserted columns is supplied at execution (long
diff --git a/odb/relational/mysql/context.cxx b/odb/relational/mysql/context.cxx
index 5d45934..ec1a967 100644
--- a/odb/relational/mysql/context.cxx
+++ b/odb/relational/mysql/context.cxx
@@ -69,8 +69,9 @@ namespace relational
context (ostream& os,
semantics::unit& u,
options_type const& ops,
+ features_type& f,
sema_rel::model* m)
- : root_context (os, u, ops, data_ptr (new (shared) data (os))),
+ : root_context (os, u, ops, f, data_ptr (new (shared) data (os))),
base_context (static_cast<data*> (root_context::data_.get ()), m),
data_ (static_cast<data*> (base_context::data_))
{
@@ -81,6 +82,7 @@ namespace relational
need_alias_as = true;
insert_send_auto_id = true;
delay_freeing_statement_result = false;
+ need_image_clone = false;
data_->bind_vector_ = "MYSQL_BIND*";
data_->truncated_vector_ = "my_bool*";
diff --git a/odb/relational/mysql/context.hxx b/odb/relational/mysql/context.hxx
index 294b5d1..8759987 100644
--- a/odb/relational/mysql/context.hxx
+++ b/odb/relational/mysql/context.hxx
@@ -123,6 +123,7 @@ namespace relational
context (std::ostream&,
semantics::unit&,
options_type const&,
+ features_type&,
sema_rel::model*);
static context&
diff --git a/odb/relational/mysql/source.cxx b/odb/relational/mysql/source.cxx
index 3a784af..8591aca 100644
--- a/odb/relational/mysql/source.cxx
+++ b/odb/relational/mysql/source.cxx
@@ -142,7 +142,7 @@ namespace relational
r += ")";
sc_.push_back (
- relational::statement_column (r, type, m, key_prefix_));
+ relational::statement_column (table, r, type, m, key_prefix_));
}
};
entry<object_columns> object_columns_;
@@ -152,7 +152,9 @@ namespace relational
view_columns (base const& x): base (x) {}
virtual void
- column (semantics::data_member& m, string const& column)
+ column (semantics::data_member& m,
+ string const& table,
+ string const& column)
{
// The same idea as in object_columns.
//
@@ -160,7 +162,7 @@ namespace relational
if (parse_sql_type (type, m).type != sql_type::ENUM)
{
- base::column (m, column);
+ base::column (m, table, column);
return;
}
@@ -172,7 +174,7 @@ namespace relational
r += column;
r += ")";
- sc_.push_back (relational::statement_column (r, type, m));
+ sc_.push_back (relational::statement_column (table, r, type, m));
}
};
entry<view_columns> view_columns_;
@@ -181,127 +183,17 @@ namespace relational
// bind
//
- struct bind_member: relational::bind_member, member_base
+ struct bind_member: relational::bind_member_impl<sql_type>,
+ member_base
{
bind_member (base const& x)
- : member_base::base (x), // virtual base
- base (x),
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
member_base (x)
{
}
- virtual bool
- pre (member_info& mi)
- {
- if (container (mi))
- return false;
-
- ostringstream ostr;
- ostr << "b[n]";
- b = ostr.str ();
-
- arg = arg_override_.empty () ? string ("i") : arg_override_;
-
- if (var_override_.empty ())
- {
- os << "// " << mi.m.name () << endl
- << "//" << endl;
-
- if (inverse (mi.m, key_prefix_) || version (mi.m))
- os << "if (sk == statement_select)"
- << "{";
- // If the whole class is readonly, then we will never be
- // called with sk == statement_update.
- //
- else if (!readonly (*context::top_object))
- {
- semantics::class_* c;
-
- if (id (mi.m) ||
- readonly (mi.m) ||
- ((c = composite (mi.t)) && readonly (*c)))
- os << "if (sk != statement_update)"
- << "{";
- }
- }
-
- return true;
- }
-
- virtual void
- post (member_info& mi)
- {
- if (var_override_.empty ())
- {
- semantics::class_* c;
-
- if ((c = composite (mi.t)))
- {
- bool ro (readonly (*c));
- column_count_type const& cc (column_count (*c));
-
- os << "n += " << cc.total << "UL";
-
- // select = total
- // insert = total - inverse
- // update = total - inverse - readonly
- //
- if (cc.inverse != 0 || (!ro && cc.readonly != 0))
- {
- os << " - (" << endl
- << "sk == statement_select ? 0 : ";
-
- if (cc.inverse != 0)
- os << cc.inverse << "UL";
-
- if (!ro && cc.readonly != 0)
- {
- if (cc.inverse != 0)
- os << " + ";
-
- os << "(" << endl
- << "sk == statement_insert ? 0 : " <<
- cc.readonly << "UL)";
- }
-
- os << ")";
- }
-
- os << ";";
- }
- else
- os << "n++;";
-
- bool block (false);
-
- // The same logic as in pre().
- //
- if (inverse (mi.m, key_prefix_) || version (mi.m))
- block = true;
- else if (!readonly (*context::top_object))
- {
- semantics::class_* c;
-
- if (id (mi.m) ||
- readonly (mi.m) ||
- ((c = composite (mi.t)) && readonly (*c)))
- block = true;
- }
-
- if (block)
- os << "}";
- else
- os << endl;
- }
- }
-
- virtual void
- traverse_composite (member_info& mi)
- {
- os << "composite_value_traits< " << mi.fq_type () <<
- " >::bind (b + n, " << arg << "." << mi.var << "value, sk);";
- }
-
virtual void
traverse_integer (member_info& mi)
{
@@ -415,10 +307,6 @@ namespace relational
<< b << ".length = &" << arg << "." << mi.var << "size;"
<< b << ".is_null = &" << arg << "." << mi.var << "null;";
}
-
- private:
- string b;
- string arg;
};
entry<bind_member> bind_member_;
diff --git a/odb/relational/oracle/context.cxx b/odb/relational/oracle/context.cxx
index 2fa566c..a44e3ee 100644
--- a/odb/relational/oracle/context.cxx
+++ b/odb/relational/oracle/context.cxx
@@ -68,8 +68,9 @@ namespace relational
context (ostream& os,
semantics::unit& u,
options_type const& ops,
+ features_type& f,
sema_rel::model* m)
- : root_context (os, u, ops, data_ptr (new (shared) data (os))),
+ : root_context (os, u, ops, f, data_ptr (new (shared) data (os))),
base_context (static_cast<data*> (root_context::data_.get ()), m),
data_ (static_cast<data*> (base_context::data_))
{
@@ -80,6 +81,7 @@ namespace relational
need_alias_as = false;
insert_send_auto_id = true;
delay_freeing_statement_result = false;
+ need_image_clone = true;
data_->bind_vector_ = "oracle::bind*";
// Populate the C++ type to DB type map.
diff --git a/odb/relational/oracle/context.hxx b/odb/relational/oracle/context.hxx
index 37716f0..6b7e968 100644
--- a/odb/relational/oracle/context.hxx
+++ b/odb/relational/oracle/context.hxx
@@ -112,6 +112,7 @@ namespace relational
context (std::ostream&,
semantics::unit&,
options_type const&,
+ features_type&,
sema_rel::model*);
static context&
diff --git a/odb/relational/oracle/header.cxx b/odb/relational/oracle/header.cxx
index 91f1d93..3eca2e2 100644
--- a/odb/relational/oracle/header.cxx
+++ b/odb/relational/oracle/header.cxx
@@ -22,24 +22,31 @@ namespace relational
virtual void
image_extra (type& c)
{
- if (!(composite (c) || abstract (c)))
+ if (!(composite (c) || (abstract (c) && !polymorphic (c))))
{
- bool gc (options.generate_query ());
+ type* poly_root (polymorphic (c));
- if (gc)
- os << "oracle::change_callback change_callback_;"
- << endl;
+ // If this is a polymorphic type, only add callback to the root.
+ //
+ if (poly_root == 0 || poly_root == &c)
+ {
+ bool gc (options.generate_query ());
- os << "oracle::change_callback*" << endl
- << "change_callback ()"
- << "{";
+ if (gc)
+ os << "oracle::change_callback change_callback_;"
+ << endl;
- if (gc)
- os << "return &change_callback_;";
- else
- os << "return 0;";
+ os << "oracle::change_callback*" << endl
+ << "change_callback ()"
+ << "{";
- os << "}";
+ if (gc)
+ os << "return &change_callback_;";
+ else
+ os << "return 0;";
+
+ os << "}";
+ }
}
}
};
diff --git a/odb/relational/oracle/source.cxx b/odb/relational/oracle/source.cxx
index 1d7d81f..4ce6e2a 100644
--- a/odb/relational/oracle/source.cxx
+++ b/odb/relational/oracle/source.cxx
@@ -2,8 +2,6 @@
// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC
// license : GNU GPL v3; see accompanying LICENSE file
-#include <sstream>
-
#include <odb/relational/source.hxx>
#include <odb/relational/oracle/common.hxx>
@@ -63,127 +61,17 @@ namespace relational
// bind
//
- struct bind_member: relational::bind_member, member_base
+ struct bind_member: relational::bind_member_impl<sql_type>,
+ member_base
{
bind_member (base const& x)
- : member_base::base (x), // virtual base
- base (x),
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
member_base (x)
{
}
- virtual bool
- pre (member_info& mi)
- {
- if (container (mi))
- return false;
-
- ostringstream ostr;
- ostr << "b[n]";
- b = ostr.str ();
-
- arg = arg_override_.empty () ? string ("i") : arg_override_;
-
- if (var_override_.empty ())
- {
- os << "// " << mi.m.name () << endl
- << "//" << endl;
-
- if (inverse (mi.m, key_prefix_) || version (mi.m))
- os << "if (sk == statement_select)"
- << "{";
- // If the whole class is readonly, then we will never be
- // called with sk == statement_update.
- //
- else if (!readonly (*context::top_object))
- {
- semantics::class_* c;
-
- if (id (mi.m) ||
- readonly (mi.m) ||
- ((c = composite (mi.t)) && readonly (*c)))
- os << "if (sk != statement_update)"
- << "{";
- }
- }
-
- return true;
- }
-
- virtual void
- post (member_info& mi)
- {
- if (var_override_.empty ())
- {
- semantics::class_* c;
-
- if ((c = composite (mi.t)))
- {
- bool ro (readonly (*c));
- column_count_type const& cc (column_count (*c));
-
- os << "n += " << cc.total << "UL";
-
- // select = total
- // insert = total - inverse
- // update = total - inverse - readonly
- //
- if (cc.inverse != 0 || (!ro && cc.readonly != 0))
- {
- os << " - (" << endl
- << "sk == statement_select ? 0 : ";
-
- if (cc.inverse != 0)
- os << cc.inverse << "UL";
-
- if (!ro && cc.readonly != 0)
- {
- if (cc.inverse != 0)
- os << " + ";
-
- os << "(" << endl
- << "sk == statement_insert ? 0 : " <<
- cc.readonly << "UL)";
- }
-
- os << ")";
- }
-
- os << ";";
- }
- else
- os << "n++;";
-
- bool block (false);
-
- // The same logic as in pre().
- //
- if (inverse (mi.m, key_prefix_) || version (mi.m))
- block = true;
- else if (!readonly (*context::top_object))
- {
- semantics::class_* c;
-
- if (id (mi.m) ||
- readonly (mi.m) ||
- ((c = composite (mi.t)) && readonly (*c)))
- block = true;
- }
-
- if (block)
- os << "}";
- else
- os << endl;
- }
- }
-
- virtual void
- traverse_composite (member_info& mi)
- {
- os << "composite_value_traits< " << mi.fq_type () <<
- " >::bind (b + n, " << arg << "." << mi.var << "value, sk);";
- }
-
virtual void
traverse_int32 (member_info& mi)
{
@@ -305,10 +193,6 @@ namespace relational
<< b << ".callback = &" << arg << "." << mi.var << "callback;"
<< endl;
}
-
- private:
- string b;
- string arg;
};
entry<bind_member> bind_member_;
@@ -632,16 +516,34 @@ namespace relational
virtual void
init_image_pre (type& c)
{
- if (options.generate_query () && !(composite (c) || abstract (c)))
- os << "if (i.change_callback_.callback != 0)" << endl
- << "(i.change_callback_.callback) (i.change_callback_.context);"
- << endl;
+ if (options.generate_query () &&
+ !(composite (c) || (abstract (c) && !polymorphic (c))))
+ {
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ if (poly_derived)
+ os << "{"
+ << "root_traits::image_type& ri (root_image (i));"
+ << endl;
+
+ string i (poly_derived ? "ri" : "i");
+
+ os << "if (" << i << ".change_callback_.callback != 0)" << endl
+ << "(" << i << ".change_callback_.callback) (" <<
+ i << ".change_callback_.context, 0);";
+
+ if (poly_derived)
+ os << "}";
+ else
+ os << endl;
+ }
}
virtual void
init_value_extra ()
{
- os << "sts.find_statement ().stream_result ();";
+ os << "st.stream_result ();";
}
virtual void
diff --git a/odb/relational/pgsql/context.cxx b/odb/relational/pgsql/context.cxx
index 9ce333e..ca2b3a5 100644
--- a/odb/relational/pgsql/context.cxx
+++ b/odb/relational/pgsql/context.cxx
@@ -69,8 +69,9 @@ namespace relational
context (ostream& os,
semantics::unit& u,
options_type const& ops,
+ features_type& f,
sema_rel::model* m)
- : root_context (os, u, ops, data_ptr (new (shared) data (os))),
+ : root_context (os, u, ops, f, data_ptr (new (shared) data (os))),
base_context (static_cast<data*> (root_context::data_.get ()), m),
data_ (static_cast<data*> (base_context::data_))
{
@@ -81,6 +82,7 @@ namespace relational
need_alias_as = true;
insert_send_auto_id = true;
delay_freeing_statement_result = false;
+ need_image_clone = false;
data_->bind_vector_ = "pgsql::bind*";
data_->truncated_vector_ = "bool*";
diff --git a/odb/relational/pgsql/context.hxx b/odb/relational/pgsql/context.hxx
index 1da353a..c572b01 100644
--- a/odb/relational/pgsql/context.hxx
+++ b/odb/relational/pgsql/context.hxx
@@ -110,6 +110,7 @@ namespace relational
context (std::ostream&,
semantics::unit&,
options_type const&,
+ features_type&,
sema_rel::model*);
static context&
diff --git a/odb/relational/pgsql/header.cxx b/odb/relational/pgsql/header.cxx
index 0d5a10a..5141c6c 100644
--- a/odb/relational/pgsql/header.cxx
+++ b/odb/relational/pgsql/header.cxx
@@ -22,7 +22,13 @@ namespace relational
virtual void
object_public_extra_post (type& c)
{
- if (abstract (c))
+ bool abst (abstract (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ if (abst && !poly)
return;
semantics::data_member* id (id_member (c));
@@ -36,7 +42,14 @@ namespace relational
if (id != 0)
{
- os << "static const char find_statement_name[];";
+ if (poly_derived)
+ os << "static const char* const find_statement_names[" <<
+ (abst ? "1" : "depth") << "];";
+ else
+ os << "static const char find_statement_name[];";
+
+ if (poly && !poly_derived)
+ os << "static const char find_discriminator_statement_name[];";
if (cc.total != cc.id + cc.inverse + cc.readonly)
os << "static const char update_statement_name[];";
@@ -66,8 +79,6 @@ namespace relational
if (cc.total != cc.id + cc.inverse + cc.readonly)
os << "static const unsigned int update_statement_types[];";
- os << "static const unsigned int erase_statement_types[];";
-
if (optimistic != 0)
os << "static const unsigned int " <<
"optimistic_erase_statement_types[];";
@@ -94,7 +105,7 @@ namespace relational
virtual void
container_public_extra_pre (semantics::data_member&)
{
- if (!object (c_) || abstract (c_))
+ if (!object (c_) || (abstract (c_) && !polymorphic (c_)))
return;
// Container statement names.
diff --git a/odb/relational/pgsql/source.cxx b/odb/relational/pgsql/source.cxx
index ae19e05..c868df2 100644
--- a/odb/relational/pgsql/source.cxx
+++ b/odb/relational/pgsql/source.cxx
@@ -152,127 +152,17 @@ namespace relational
// bind
//
- struct bind_member: relational::bind_member, member_base
+ struct bind_member: relational::bind_member_impl<sql_type>,
+ member_base
{
bind_member (base const& x)
- : member_base::base (x), // virtual base
- base (x),
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
member_base (x)
{
}
- virtual bool
- pre (member_info& mi)
- {
- if (container (mi))
- return false;
-
- ostringstream ostr;
- ostr << "b[n]";
- b = ostr.str ();
-
- arg = arg_override_.empty () ? string ("i") : arg_override_;
-
- if (var_override_.empty ())
- {
- os << "// " << mi.m.name () << endl
- << "//" << endl;
-
- if (inverse (mi.m, key_prefix_) || version (mi.m))
- os << "if (sk == statement_select)"
- << "{";
- // If the whole class is readonly, then we will never be
- // called with sk == statement_update.
- //
- else if (!readonly (*context::top_object))
- {
- semantics::class_* c;
-
- if (id (mi.m) ||
- readonly (mi.m) ||
- ((c = composite (mi.t)) && readonly (*c)))
- os << "if (sk != statement_update)"
- << "{";
- }
- }
-
- return true;
- }
-
- virtual void
- post (member_info& mi)
- {
- if (var_override_.empty ())
- {
- semantics::class_* c;
-
- if ((c = composite (mi.t)))
- {
- bool ro (readonly (*c));
- column_count_type const& cc (column_count (*c));
-
- os << "n += " << cc.total << "UL";
-
- // select = total
- // insert = total - inverse
- // update = total - inverse - readonly
- //
- if (cc.inverse != 0 || (!ro && cc.readonly != 0))
- {
- os << " - (" << endl
- << "sk == statement_select ? 0 : ";
-
- if (cc.inverse != 0)
- os << cc.inverse << "UL";
-
- if (!ro && cc.readonly != 0)
- {
- if (cc.inverse != 0)
- os << " + ";
-
- os << "(" << endl
- << "sk == statement_insert ? 0 : " <<
- cc.readonly << "UL)";
- }
-
- os << ")";
- }
-
- os << ";";
- }
- else
- os << "n++;";
-
- bool block (false);
-
- // The same logic as in pre().
- //
- if (inverse (mi.m, key_prefix_) || version (mi.m))
- block = true;
- else if (!readonly (*context::top_object))
- {
- semantics::class_* c;
-
- if (id (mi.m) ||
- readonly (mi.m) ||
- ((c = composite (mi.t)) && readonly (*c)))
- block = true;
- }
-
- if (block)
- os << "}";
- else
- os << endl;
- }
- }
-
- virtual void
- traverse_composite (member_info& mi)
- {
- os << "composite_value_traits< " << mi.fq_type () <<
- " >::bind (b + n, " << arg << "." << mi.var << "value, sk);";
- }
-
virtual void
traverse_integer (member_info& mi)
{
@@ -351,10 +241,6 @@ namespace relational
<< b << ".buffer = " << arg << "." << mi.var << "value;"
<< b << ".is_null = &" << arg << "." << mi.var << "null;";
}
-
- private:
- string b;
- string arg;
};
entry<bind_member> bind_member_;
@@ -735,7 +621,13 @@ namespace relational
virtual void
object_extra (type& c)
{
- if (abstract (c))
+ bool abst (abstract (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ if (abst && !poly)
return;
semantics::data_member* id (id_member (c));
@@ -743,33 +635,58 @@ namespace relational
column_count_type const& cc (column_count (c));
string const& n (class_fq_name (c));
- string traits ("access::object_traits< " + n + " >::");
string const& fn (flat_name (n));
- string name_decl ("const char " + traits);
+ string traits ("access::object_traits< " + n + " >");
- os << name_decl << endl
- << "persist_statement_name[] = " << strlit (fn + "_persist") <<
- ";"
+ os << "const char " << traits << "::" << endl
+ << "persist_statement_name[] = " << strlit (fn + "_persist") << ";"
<< endl;
if (id != 0)
{
- os << name_decl << endl
- << "find_statement_name[] = " << strlit (fn + "_find") << ";"
- << endl;
+ if (poly_derived)
+ {
+ os << "const char* const " << traits << "::" << endl
+ << "find_statement_names[] ="
+ << "{";
+
+ for (size_t i (0), n (abst ? 1 : polymorphic_depth (c));
+ i < n;
+ ++i)
+ {
+ if (i != 0)
+ os << "," << endl;
+
+ ostringstream ostr;
+ ostr << fn << "_find_" << i;
+ os << strlit (ostr.str ());
+ }
+
+ os << "};";
+ }
+ else
+ os << "const char " << traits << "::" << endl
+ << "find_statement_name[] = " << strlit (fn + "_find") << ";"
+ << endl;
+
+ if (poly && !poly_derived)
+ os << "const char " << traits << "::" << endl
+ << "find_discriminator_statement_name[] = " <<
+ strlit (fn + "_find_discriminator") << ";"
+ << endl;
if (cc.total != cc.id + cc.inverse + cc.readonly)
- os << name_decl << endl
+ os << "const char " << traits << "::" << endl
<< "update_statement_name[] = " << strlit (fn + "_update") <<
";"
<< endl;
- os << name_decl << endl
+ os << "const char " << traits << "::" << endl
<< "erase_statement_name[] = " << strlit (fn + "_erase") << ";"
<< endl;
if (optimistic != 0)
- os << name_decl << endl
+ os << "const char " << traits << "::" << endl
<< "optimistic_erase_statement_name[] = " <<
strlit (fn + "_optimistic_erase") << ";"
<< endl;
@@ -779,10 +696,10 @@ namespace relational
//
if (options.generate_query ())
{
- os << name_decl << endl
+ os << "const char " << traits << "::" << endl
<< "query_statement_name[] = " << strlit (fn + "_query") << ";"
<< endl
- << name_decl << endl
+ << "const char " << traits << "::" << endl
<< "erase_query_statement_name[] = " <<
strlit (fn + "_erase_query") << ";"
<< endl;
@@ -790,12 +707,11 @@ namespace relational
// Statement types.
//
- string oid_decl ("const unsigned int " + traits);
// persist_statement_types.
//
{
- os << oid_decl << endl
+ os << "const unsigned int " << traits << "::" << endl
<< "persist_statement_types[] ="
<< "{";
@@ -809,7 +725,7 @@ namespace relational
//
if (id != 0)
{
- os << oid_decl << endl
+ os << "const unsigned int " << traits << "::" << endl
<< "find_statement_types[] ="
<< "{";
@@ -823,7 +739,7 @@ namespace relational
//
if (id != 0 && cc.total != cc.id + cc.inverse + cc.readonly)
{
- os << oid_decl << endl
+ os << "const unsigned int " << traits << "::" << endl
<< "update_statement_types[] ="
<< "{";
@@ -844,23 +760,9 @@ namespace relational
os << "};";
}
- // erase_statement_types.
- //
- if (id != 0)
- {
- os << oid_decl << endl
- << "erase_statement_types[] ="
- << "{";
-
- instance<statement_oids> st (statement_where);
- st->traverse (*id);
-
- os << "};";
- }
-
if (id != 0 && optimistic != 0)
{
- os << oid_decl << endl
+ os << "const unsigned int " << traits << "::" << endl
<< "optimistic_erase_statement_types[] ="
<< "{";
@@ -876,11 +778,10 @@ namespace relational
view_extra (type& c)
{
string const& n (class_fq_name (c));
- string traits ("access::view_traits< " + n + " >::");
string const& fn (flat_name (n));
- string name_decl ("const char " + traits);
+ string traits ("access::view_traits< " + n + " >");
- os << name_decl << endl
+ os << "const char " << traits << "::" << endl
<< "query_statement_name[] = " << strlit (fn + "_query") << ";"
<< endl;
}
@@ -935,7 +836,7 @@ namespace relational
virtual void
container_extra (semantics::data_member& m, semantics::type& t)
{
- if (!object (c_) || abstract (c_))
+ if (!object (c_) || (abstract (c_) && !polymorphic (c_)))
return;
string scope (scope_ + "::" + flat_prefix_ + public_name (m) +
@@ -943,29 +844,24 @@ namespace relational
// Statment names.
//
- string stmt_decl ("const char " + scope + "::");
// Prefix top-object name to avoid conflicts with inherited
// member statement names.
//
- string stmt_prefix (class_fq_name (*top_object) + m.fq_name ());
+ string fn (flat_name (class_fq_name (*top_object) + m.fq_name ()));
- os << stmt_decl << endl
- << "select_all_name[] = " <<
- strlit (stmt_prefix + "_select_all") << ";"
+ os << "const char " << scope << "::" << endl
+ << "select_all_name[] = " << strlit (fn + "_select_all") << ";"
<< endl
- << stmt_decl << endl
- << "insert_one_name[] = " <<
- strlit (stmt_prefix + "_insert_one") << ";"
+ << "const char " << scope << "::" << endl
+ << "insert_one_name[] = " << strlit (fn + "_insert_one") << ";"
<< endl
- << stmt_decl << endl
- << "delete_all_name[] = " <<
- strlit (stmt_prefix + "_delete_all") << ";"
+ << "const char " << scope << "::" << endl
+ << "delete_all_name[] = " << strlit (fn + "_delete_all") << ";"
<< endl;
// Statement types.
//
- string type_decl ("const unsigned int " + scope + "::");
semantics::data_member* inv_m (inverse (m, "value"));
bool inv (inv_m != 0);
@@ -976,7 +872,7 @@ namespace relational
// select_all statement types.
//
{
- os << type_decl << endl
+ os << "const unsigned int " << scope << "::" << endl
<< "select_all_types[] ="
<< "{";
@@ -1003,7 +899,7 @@ namespace relational
// insert_one statement types.
//
{
- os << type_decl << endl
+ os << "const unsigned int " << scope << "::" << endl
<< "insert_one_types[] ="
<< "{";
@@ -1048,7 +944,7 @@ namespace relational
// delete_all statement types.
//
{
- os << type_decl << endl
+ os << "const unsigned int " << scope << "::" << endl
<< "delete_all_types[] ="
<< "{";
diff --git a/odb/relational/processor.cxx b/odb/relational/processor.cxx
index e354b0f..cdacfd4 100644
--- a/odb/relational/processor.cxx
+++ b/odb/relational/processor.cxx
@@ -247,7 +247,9 @@ namespace relational
if (type.empty () && wt != 0)
type = database_type (*wt, whint, false);
- if (id (m))
+ // Use id mapping for discriminators.
+ //
+ if (id (m) || discriminator (m))
type = id_type;
}
@@ -768,14 +770,8 @@ namespace relational
while (DECL_TEMPLATE_INFO (decl))
decl = DECL_TI_TEMPLATE (decl);
- if (!unit.count ("tr1-pointer-used"))
- {
- unit.set ("tr1-pointer-used", false);
- unit.set ("boost-pointer-used", false);
- }
-
- bool& tr1 (unit.get<bool> ("tr1-pointer-used"));
- bool& boost (unit.get<bool> ("boost-pointer-used"));
+ bool& tr1 (features.tr1_pointer);
+ bool& boost (features.boost_pointer);
string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER));
@@ -918,9 +914,9 @@ namespace relational
throw operation_failed ();
}
- // Make sure the pointed-to class is not abstract.
+ // Make sure the pointed-to class is not reuse-abstract.
//
- if (context::abstract (*c))
+ if (abstract (*c) && !polymorphic (*c))
{
os << m.file () << ":" << m.line () << ":" << m.column () << ": "
<< "error: pointed-to class '" << class_fq_name (*c) << "' "
@@ -935,7 +931,7 @@ namespace relational
// Make sure the pointed-to class has object id.
//
- if (context::id_member (*c) == 0)
+ if (id_member (*c) == 0)
{
os << m.file () << ":" << m.line () << ":" << m.column () << ": "
<< "error: pointed-to class '" << class_fq_name (*c) << "' "
@@ -1285,7 +1281,7 @@ namespace relational
string name;
tree decl (0);
- semantics::class_* obj (0);
+ view_object* vo (0);
// Check if this is an alias.
//
@@ -1295,8 +1291,7 @@ namespace relational
if (j != amap_.end ())
{
- i->table = j->first;
- obj = j->second->obj;
+ vo = j->second;
// Skip '::'.
//
@@ -1312,13 +1307,13 @@ namespace relational
cpp_ttype ptt; // Not used.
decl = lookup::resolve_scoped_name (
- t, tt, ptt, lex_, obj->tree_node (), name, false);
+ t, tt, ptt, lex_, vo->obj->tree_node (), name, false);
}
}
// If it is not an alias, do the normal lookup.
//
- if (obj == 0)
+ if (vo == 0)
{
// Also get the object type. We need to do it so that
// we can get the correct (derived) table name (the
@@ -1342,8 +1337,7 @@ namespace relational
throw operation_failed ();
}
- obj = j->second->obj;
- i->table = table_name (*obj);
+ vo = j->second;
}
// Check that we have a data member.
@@ -1358,6 +1352,40 @@ namespace relational
data_member* m (dynamic_cast<data_member*> (unit.find (decl)));
i->member_path.push_back (m);
+ // Figure out the table name/alias for this member.
+ //
+ using semantics::class_;
+
+ if (class_* root = polymorphic (*vo->obj))
+ {
+ // If the object is polymorphic, then figure out which of the
+ // bases this member comes from and use the corresponding
+ // table.
+ //
+ class_* c (&static_cast<class_&> (m->scope ()));
+
+ // If this member's class is not polymorphic (root uses reuse
+ // inheritance), then use the root table.
+ //
+ if (!polymorphic (*c))
+ c = root;
+
+ // In a polymorphic hierarchy we have several tables and the
+ // provided alias is used as a prefix together with the table
+ // name to form the actual alias.
+ //
+ qname const& t (table_name (*c));
+
+ if (vo->alias.empty ())
+ i->table = t;
+ else
+ i->table = qname (vo->alias + "_" + t.uname ());
+ }
+ else
+ i->table = vo->alias.empty ()
+ ? table_name (*vo->obj)
+ : qname (vo->alias);
+
// Finally, resolve nested members if any.
//
for (; tt == CPP_DOT; tt = lex_.next (t))
@@ -1440,7 +1468,7 @@ namespace relational
}
}
}
- // This member has no column information. If we are generting our
+ // This member has no column information. If we are generating our
// own query, try to find a member with the same (or similar) name
// in one of the associated objects.
//
@@ -1508,9 +1536,39 @@ namespace relational
column_expr_part& ep (e.back ());
ep.kind = column_expr_part::reference;
- ep.table = am.vo->alias.empty ()
- ? table_name (*am.vo->obj)
- : qname (am.vo->alias);
+
+
+ // If this object is polymorphic, then figure out which of the
+ // bases this member comes from and use the corresponding table.
+ //
+ using semantics::class_;
+
+ if (class_* root = polymorphic (*am.vo->obj))
+ {
+ class_* c (&static_cast<class_&> (am.m->scope ()));
+
+ // If this member's class is not polymorphic (root uses reuse
+ // inheritance), then use the root table.
+ //
+ if (!polymorphic (*c))
+ c = root;
+
+ // In a polymorphic hierarchy we have several tables and the
+ // provided alias is used as a prefix together with the table
+ // name to form the actual alias.
+ //
+ qname const& t (table_name (*c));
+
+ if (am.vo->alias.empty ())
+ ep.table = t;
+ else
+ ep.table = qname (am.vo->alias + "_" + t.uname ());
+ }
+ else
+ ep.table = am.vo->alias.empty ()
+ ? table_name (*am.vo->obj)
+ : qname (am.vo->alias);
+
ep.member_path.push_back (am.m);
src_m = am.m;
@@ -1553,7 +1611,23 @@ namespace relational
traverse (view_object& vo)
{
member_.vo_ = &vo;
- traverse (*vo.obj);
+
+ // First look for an exact match.
+ //
+ {
+ member_.exact_ = true;
+ member_.found_ = false;
+ traverse (*vo.obj);
+ }
+
+ // If we didn't find an exact match, then look for a public
+ // name match.
+ //
+ if (!member_.found_)
+ {
+ member_.exact_ = false;
+ traverse (*vo.obj);
+ }
}
virtual void
@@ -1563,7 +1637,12 @@ namespace relational
return; // Ignore transient bases.
names (c);
- inherits (c);
+
+ // If we already found a match in one of the derived classes,
+ // don't go into bases to get the standard "hiding" behavior.
+ //
+ if (!member_.found_)
+ inherits (c);
}
public:
@@ -1614,37 +1693,27 @@ namespace relational
virtual void
traverse (type& m)
{
- // First see if we have the exact match.
- //
- if (name_ == m.name ())
+ if (exact_)
{
- if (check (m))
+ if (name_ == m.name () && check (m))
{
assoc_member am;
am.m = &m;
am.vo = vo_;
members_.push_back (am);
+ found_ = true;
}
-
- return;
}
-
- // Don't bother with public name matching if we already
- // have an exact match.
- //
- if (members_.empty ())
+ else
{
- if (pub_name_ == context::current ().public_name (m))
+ if (pub_name_ == context::current ().public_name (m) &&
+ check (m))
{
- if (check (m))
- {
- assoc_member am;
- am.m = &m;
- am.vo = vo_;
- pub_members_.push_back (am);
- }
-
- return;
+ assoc_member am;
+ am.m = &m;
+ am.vo = vo_;
+ pub_members_.push_back (am);
+ found_ = true;
}
}
}
@@ -1654,7 +1723,9 @@ namespace relational
{
// Make sure that the found node can possibly match.
//
- if (context::transient (m) || context::inverse (m))
+ if (context::transient (m) ||
+ context::inverse (m) ||
+ m.count ("polymorphic-ref"))
return false;
return check_types (utype (m), type_);
@@ -1668,6 +1739,8 @@ namespace relational
semantics::type& type_;
view_object* vo_;
+ bool exact_;
+ bool found_;
};
traversal::names names_;
@@ -1686,7 +1759,31 @@ namespace relational
struct class_: traversal::class_, context
{
class_ ()
+ : std_string_ (0), std_string_hint_ (0)
{
+ // Resolve the std::string type node.
+ //
+ using semantics::scope;
+
+ for (scope::names_iterator_pair ip (unit.find ("std"));
+ ip.first != ip.second; ++ip.first)
+ {
+ if (scope* ns = dynamic_cast<scope*> (&ip.first->named ()))
+ {
+ scope::names_iterator_pair jp (ns->find ("string"));
+
+ if (jp.first != jp.second)
+ {
+ std_string_ = dynamic_cast<semantics::type*> (
+ &jp.first->named ());
+ std_string_hint_ = &*jp.first;
+ break;
+ }
+ }
+ }
+
+ assert (std_string_ != 0); // No std::string?
+
*this >> member_names_ >> member_;
}
@@ -1698,17 +1795,115 @@ namespace relational
if (k == class_other)
return;
- names (c);
-
// Assign pointer.
//
if (k == class_object || k == class_view)
assign_pointer (c);
- // Do some additional processing for views.
+ // Do some additional pre-processing for objects.
+ //
+ if (k == class_object)
+ traverse_object_pre (c);
+
+ names (c);
+
+ // Do some additional post-processing for views.
//
if (k == class_view)
- traverse_view (c);
+ traverse_view_post (c);
+ }
+
+ //
+ // Object.
+ //
+
+ virtual void
+ traverse_object_pre (type& c)
+ {
+ if (semantics::class_* root = polymorphic (c))
+ {
+ using namespace semantics;
+
+ semantics::data_member& idm (*id_member (*root));
+
+ if (root != &c)
+ {
+ // If we are a derived class in the polymorphic persistent
+ // class hierarchy, then add a synthesized virtual pointer
+ // member that points back to the root.
+ //
+ path const& f (idm.file ());
+ size_t l (idm.line ()), col (idm.column ());
+
+ semantics::data_member& m (
+ unit.new_node<virtual_data_member> (f, l, col));
+
+ // Make it the first member in the class.
+ //
+ node_position<type, scope::names_iterator> np (c, c.names_end ());
+ unit.new_edge<semantics::names> (
+ np, m, idm.name (), access::public_);
+
+ // Use the raw pointer as this member's type.
+ //
+ if (!root->pointed_p ())
+ {
+ // Create the pointer type in the graph. The pointer node
+ // in GCC seems to always be present, even if not explicitly
+ // used in the translation unit.
+ //
+ tree t (root->tree_node ());
+ tree ptr (TYPE_POINTER_TO (t));
+ assert (ptr != 0);
+ ptr = TYPE_MAIN_VARIANT (ptr);
+ pointer& p (unit.new_node<pointer> (f, l, col, ptr));
+ unit.insert (ptr, p);
+ unit.new_edge<points> (p, *root);
+ assert (root->pointed_p ());
+ }
+
+ unit.new_edge<belongs> (m, root->pointed ().pointer ());
+
+ m.set ("not-null", true);
+ m.set ("deferred", false);
+ m.set ("on-delete", sema_rel::foreign_key::cascade);
+
+ // Mark it as a special kind of id.
+ //
+ m.set ("id", true);
+ m.set ("polymorphic-ref", true);
+ }
+ else
+ {
+ // If we are a root of the polymorphic persistent class hierarchy,
+ // then add a synthesized virtual member for the discriminator.
+ // Use the location of the polymorphic pragma as the location of
+ // this member.
+ //
+ location_t loc (c.get<location_t> ("polymorphic-location"));
+ semantics::data_member& m (
+ unit.new_node<virtual_data_member> (
+ path (LOCATION_FILE (loc)),
+ LOCATION_LINE (loc),
+ LOCATION_COLUMN (loc)));
+
+ // Insert it after the id member (or first if this id comes
+ // from reuse-base).
+ //
+ node_position<type, scope::names_iterator> np (
+ c, c.find (idm.named ()));
+ unit.new_edge<semantics::names> (
+ np, m, "typeid_", access::public_);
+
+ belongs& edge (unit.new_edge<belongs> (m, *std_string_));
+ edge.hint (*std_string_hint_);
+
+ m.set ("readonly", true);
+ m.set ("discriminator", true);
+
+ c.set ("discriminator", &m);
+ }
+ }
}
//
@@ -1726,7 +1921,7 @@ namespace relational
typedef vector<relationship> relationships;
virtual void
- traverse_view (type& c)
+ traverse_view_post (type& c)
{
bool has_q (c.count ("query"));
bool has_o (c.count ("objects"));
@@ -1859,12 +2054,46 @@ namespace relational
<< "persistent class '" << i->obj_name << "' is used in "
<< "the view more than once" << endl;
+ error (omap[n]->loc)
+ << "previously used here" << endl;
+
info (i->loc)
<< "use the alias clause to assign it a different name"
<< endl;
throw operation_failed ();
}
+
+ // Also add the bases of a polymorphic object.
+ //
+ class_* poly_root (polymorphic (o));
+
+ if (poly_root != 0 && poly_root != &o)
+ {
+ for (class_* b (&polymorphic_base (o));;
+ b = &polymorphic_base (*b))
+ {
+ view_object_map::value_type v (b->tree_node (), &*i);
+ if (!omap.insert (v).second)
+ {
+ error (i->loc)
+ << "base class '" << class_name (*b) << "' is "
+ << "used in the view more than once" << endl;
+
+ error (omap[v.first]->loc)
+ << "previously used here" << endl;
+
+ info (i->loc)
+ << "use the alias clause to assign it a different name"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ if (b == poly_root)
+ break;
+ }
+ }
}
else
{
@@ -1908,9 +2137,7 @@ namespace relational
}
// Now see if this object points to any of the objects
- // specified prior to it. Ignore self-references if any,
- // since they were already added to the list in the
- // previous pass.
+ // specified prior to it.
//
{
relationship_resolver r (rs, *j, false);
@@ -2002,10 +2229,20 @@ namespace relational
{
relationship_resolver (relationships& rs,
view_object& pointee,
- bool self_pointer)
- : object_members_base (false, false, true),
+ bool forward)
+ // Look in polymorphic bases only for previously-associated
+ // objects since backward pointers from bases will result in
+ // the pathological case (we will have to join the base table
+ // first, which means we will get both bases and derived objects
+ // instead of just derived).
+ //
+ : object_members_base (false, false, true, forward),
relationships_ (rs),
- self_pointer_ (self_pointer),
+ // Ignore self-references if we are looking for backward
+ // pointers since they were already added to the list in
+ // the previous pass.
+ //
+ self_pointer_ (forward),
pointer_ (0),
pointee_ (pointee)
{
@@ -2021,6 +2258,11 @@ namespace relational
virtual void
traverse_pointer (semantics::data_member& m, semantics::class_& c)
{
+ // Ignore polymorphic id references.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
// Ignore inverse sides of the same relationship to avoid
// phony conflicts caused by the direct side that will end
// up in the relationship list as well.
@@ -2089,13 +2331,31 @@ namespace relational
string decl_name; // User-provided template name.
tree resolve_scope (0); // Scope in which we resolve names.
+ class_pointer const* cp (0);
+ bool cp_template (false);
+
if (c.count ("pointer"))
{
- class_pointer const& cp (c.get<class_pointer> ("pointer"));
- string const& p (cp.name);
+ cp = &c.get<class_pointer> ("pointer");
+ }
+ // If we are a derived type in polymorphic hierarchy, then use
+ // our root's pointer type by default.
+ //
+ else if (semantics::class_* r = polymorphic (c))
+ {
+ if (&c != r && r->count ("pointer-template"))
+ cp = r->get<class_pointer const*> ("pointer-template");
+ }
+
+ if (cp != 0)
+ {
+ string const& p (cp->name);
if (p == "*")
+ {
ptr = type + "*";
+ cp_template = true;
+ }
else if (p[p.size () - 1] == '*')
ptr = p;
else if (p.find ('<') != string::npos)
@@ -2110,7 +2370,7 @@ namespace relational
// This is not a template-id. Resolve it and see if it is a
// template or a type.
//
- decl = resolve_name (p, cp.scope, true);
+ decl = resolve_name (p, cp->scope, true);
int tc (TREE_CODE (decl));
if (tc == TYPE_DECL)
@@ -2135,10 +2395,11 @@ namespace relational
{
ptr = p + "< " + type + " >";
decl_name = p;
+ cp_template = true;
}
else
{
- error (cp.loc)
+ error (cp->loc)
<< "name '" << p << "' specified with db pragma pointer "
<< "does not name a type or a template" << endl;
@@ -2148,8 +2409,8 @@ namespace relational
// Resolve scope is the scope of the pragma.
//
- resolve_scope = cp.scope;
- loc = cp.loc;
+ resolve_scope = cp->scope;
+ loc = cp->loc;
}
else
{
@@ -2176,8 +2437,8 @@ namespace relational
continue;
}
- class_pointer const& cp (ns->get<class_pointer> ("pointer"));
- string const& p (cp.name);
+ cp = &ns->get<class_pointer> ("pointer");
+ string const& p (cp->name);
// Namespace-specified pointer can only be '*' or are template.
//
@@ -2185,13 +2446,13 @@ namespace relational
ptr = type + "*";
else if (p[p.size () - 1] == '*')
{
- error (cp.loc)
+ error (cp->loc)
<< "name '" << p << "' specified with db pragma pointer "
<< "at namespace level cannot be a raw pointer" << endl;
}
else if (p.find ('<') != string::npos)
{
- error (cp.loc)
+ error (cp->loc)
<< "name '" << p << "' specified with db pragma pointer "
<< "at namespace level cannot be a template-id" << endl;
}
@@ -2199,7 +2460,7 @@ namespace relational
{
// Resolve this name and make sure it is a template.
//
- decl = resolve_name (p, cp.scope, true);
+ decl = resolve_name (p, cp->scope, true);
int tc (TREE_CODE (decl));
if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl))
@@ -2209,7 +2470,7 @@ namespace relational
}
else
{
- error (cp.loc)
+ error (cp->loc)
<< "name '" << p << "' specified with db pragma pointer "
<< "does not name a template" << endl;
}
@@ -2218,10 +2479,12 @@ namespace relational
if (ptr.empty ())
throw operation_failed ();
+ cp_template = true;
+
// Resolve scope is the scope of the pragma.
//
- resolve_scope = cp.scope;
- loc = cp.loc;
+ resolve_scope = cp->scope;
+ loc = cp->loc;
break;
}
@@ -2245,18 +2508,18 @@ namespace relational
}
}
+ // If this class is a root of a polymorphic hierarchy, then cache
+ // the pointer template so that we can use it for derived classes.
+ //
+ if (cp != 0 && cp_template && polymorphic (c) == &c)
+ c.set ("pointer-template", cp);
+
// Check if we are using TR1.
//
if (decl != 0 || !decl_name.empty ())
{
- if (!unit.count ("tr1-pointer-used"))
- {
- unit.set ("tr1-pointer-used", false);
- unit.set ("boost-pointer-used", false);
- }
-
- bool& tr1 (unit.get<bool> ("tr1-pointer-used"));
- bool& boost (unit.get<bool> ("boost-pointer-used"));
+ bool& tr1 (features.tr1_pointer);
+ bool& boost (features.boost_pointer);
// First check the user-supplied name.
//
@@ -2459,6 +2722,9 @@ namespace relational
data_member member_;
traversal::names member_names_;
+
+ semantics::type* std_string_;
+ semantics::names* std_string_hint_;
};
}
diff --git a/odb/relational/schema-source.hxx b/odb/relational/schema-source.hxx
index a72b55b..82b7e71 100644
--- a/odb/relational/schema-source.hxx
+++ b/odb/relational/schema-source.hxx
@@ -21,18 +21,20 @@ namespace relational
virtual void
traverse (type& c)
{
- if (class_file (c) != unit.file () || !object (c) || abstract (c))
+ if (class_file (c) != unit.file ())
return;
- context::top_object = context::cur_object = &c;
+ if (!object (c))
+ return;
+
+ if (abstract (c) && !polymorphic (c))
+ return;
os << "// " << class_name (c) << endl
<< "//" << endl
<< endl;
schema_->traverse (c);
-
- context::top_object = context::cur_object = 0;
}
private:
diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx
index 6067e63..5bf14f1 100644
--- a/odb/relational/source.cxx
+++ b/odb/relational/source.cxx
@@ -12,6 +12,3251 @@
using namespace std;
+void relational::source::class_::
+traverse_object (type& c)
+{
+ using semantics::data_member;
+
+ data_member* id (id_member (c));
+ bool auto_id (id ? id->count ("auto") : false);
+ bool base_id (id ? &id->scope () != &c : false); // Comes from base.
+
+ bool has_ptr (has_a (c, test_pointer));
+
+ data_member* optimistic (context::optimistic (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+ type* poly_base (poly_derived ? &polymorphic_base (c) : 0);
+ size_t poly_depth (poly_derived ? polymorphic_depth (c) : 1);
+ data_member* discriminator (poly ? context::discriminator (*poly_root) : 0);
+
+ bool abst (abstract (c));
+ bool reuse_abst (abst && !poly);
+
+ bool grow (false);
+ bool grow_id (false);
+
+ if (generate_grow)
+ {
+ grow = context::grow (c);
+ grow_id = (id ? context::grow (*id) : false) ||
+ (optimistic ? context::grow (*optimistic) : false);
+ }
+
+ string const& type (class_fq_name (c));
+ string traits ("access::object_traits< " + type + " >");
+ column_count_type const& cc (column_count (c));
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ object_extra (c);
+
+ //
+ // Query.
+ //
+
+ if (options.generate_query ())
+ {
+ // query_columns_base
+ //
+ if (has_ptr)
+ {
+ instance<query_columns_base> t (c);
+ t->traverse (c);
+ }
+ }
+
+ //
+ // Containers (abstract and concrete).
+ //
+ bool containers (has_a (c, test_container));
+ bool straight_containers (false);
+ bool straight_readwrite_containers (false);
+
+ if (containers)
+ {
+ containers = true;
+ size_t scn (has_a (c, test_straight_container));
+
+ if (scn != 0)
+ {
+ straight_containers = true;
+
+ // Inverse containers cannot be marked readonly.
+ //
+ straight_readwrite_containers = scn > has_a (c, test_readonly_container);
+ }
+ }
+
+ if (containers)
+ {
+ instance<container_traits> t (c);
+ t->traverse (c);
+ }
+
+ //
+ // Functions (abstract and concrete).
+ //
+
+ // id (image_type)
+ //
+ if (!poly_derived && id != 0 && !base_id)
+ {
+ if (options.generate_query ())
+ {
+ os << traits << "::id_type" << endl
+ << traits << "::" << endl
+ << "id (const image_type& i)"
+ << "{"
+ << db << "::database* db (0);"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl
+ << "id_type id;";
+ init_id_value_member_->traverse (*id);
+ os << "return id;"
+ << "}";
+ }
+
+ if (optimistic != 0)
+ {
+ os << traits << "::version_type" << endl
+ << traits << "::" << endl
+ << "version (const image_type& i)"
+ << "{"
+ << "version_type v;";
+ init_version_value_member_->traverse (*optimistic);
+ os << "return v;"
+ << "}";
+ }
+ }
+
+ // discriminator()
+ //
+ if (poly && !poly_derived)
+ {
+ os << traits << "::discriminator_type" << endl
+ << traits << "::" << endl
+ << "discriminator (const image_type& i)"
+ << "{"
+ << db << "::database* db (0);"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl
+ << "discriminator_type d;";
+ init_discriminator_value_member_->traverse (*discriminator);
+ os << "return d;"
+ << "}";
+ }
+
+ // grow ()
+ //
+ if (generate_grow)
+ {
+ os << "bool " << traits << "::" << endl
+ << "grow (image_type& i, " << truncated_vector << " t";
+
+ if (poly_derived)
+ os << ", std::size_t d";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (t);"
+ << endl
+ << "bool grew (false);"
+ << endl;
+
+ index_ = 0;
+
+ if (poly_derived)
+ {
+ // Select column count for this class.
+ //
+ size_t cols (cc.total - cc.id);
+
+ os << "// " << class_name (*poly_base) << " base" << endl
+ << "//" << endl
+ << "if (--d != 0)"
+ << "{"
+ << "if (object_traits<base_type>::grow (*i.base, " <<
+ "t + " << cols << "UL" <<
+ (poly_base != poly_root ? ", d" : "") << "))" << endl
+ << "i.base->version++;"
+ << "}";
+ }
+ else
+ inherits (c, grow_base_inherits_);
+
+ names (c, grow_member_names_);
+
+ os << "return grew;"
+ << "}";
+ }
+
+ // bind (image_type)
+ //
+ os << "void " << traits << "::" << endl
+ << "bind (" << bind_vector << " b," << endl;
+
+ // If we are a derived type in a polymorphic hierarchy, then
+ // we get the the external id binding.
+ //
+ if (poly_derived)
+ os << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl;
+
+ os << "image_type& i," << endl
+ << db << "::statement_kind sk)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (readonly (c))
+ os << "assert (sk != statement_update);"
+ << endl;
+
+ os << "std::size_t n (0);"
+ << endl;
+
+ if (poly_derived)
+ {
+ // The id reference comes first in the insert statement.
+ //
+ os << "// " << id->name () << endl
+ << "//" << endl
+ << "if (sk == statement_insert)"
+ << "{"
+ << "if (id != 0)" << endl
+ << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
+ << "n += id_size;"
+ << "}";
+ }
+ else
+ inherits (c, bind_base_inherits_);
+
+ names (c, bind_member_names_);
+
+ if (poly_derived)
+ {
+ // The id reference comes last in the update statement.
+ //
+ if (!readonly (c))
+ os << "// " << id->name () << endl
+ << "//" << endl
+ << "if (sk == statement_update)"
+ << "{"
+ << "if (id != 0)" << endl
+ << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
+ << "n += id_size;"
+ << "}";
+
+ // Bind the image chain for the select statement. Seeing that
+ // this is the last statement in the function, we don't care
+ // about updating n.
+ //
+ os << "// " << class_name (*poly_base) << " base" << endl
+ << "//" << endl
+ << "if (sk == statement_select)" << endl
+ << "object_traits<base_type>::";
+
+ if (poly_base == poly_root)
+ os << "bind (b + n, *i.base, sk);";
+ else
+ os << "bind (b + n, id, id_size, *i.base, sk);";
+ }
+
+ os << "}";
+
+ // bind (id_image_type)
+ //
+ if (!poly_derived && id != 0 && !base_id)
+ {
+ os << "void " << traits << "::" << endl
+ << "bind (" << bind_vector << " b, id_image_type& i" <<
+ (optimistic != 0 ? ", bool bv" : "") << ")"
+ << "{"
+ << "std::size_t n (0);";
+
+ if (composite_wrapper (utype (*id)))
+ os << db << "::statement_kind sk (" << db << "::statement_select);";
+
+ bind_id_member_->traverse (*id);
+
+ if (optimistic != 0)
+ {
+ os << "if (bv)"
+ << "{"
+ << "n += " << column_count (c).id << ";"
+ << endl;
+
+ bind_version_member_->traverse (*optimistic);
+ os << "}";
+ }
+
+ os << "}";
+ }
+
+ // init (image, object)
+ //
+ os << "bool " << traits << "::" << endl
+ << "init (image_type& i, const object_type& o, " <<
+ db << "::statement_kind sk)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (o);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (readonly (c))
+ os << "assert (sk != statement_update);"
+ << endl;
+
+ init_image_pre (c);
+
+ os << "bool grew (false);"
+ << endl;
+
+ if (!poly_derived)
+ inherits (c, init_image_base_inherits_);
+
+ names (c, init_image_member_names_);
+
+ os << "return grew;"
+ << "}";
+
+ // init (object, image)
+ //
+ os << "void " << traits << "::" << endl
+ << "init (object_type& o, const image_type& i, database* db";
+
+ if (poly_derived)
+ os << ", std::size_t d";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (o);"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl;
+
+ if (poly_derived)
+ {
+ os << "// " << class_name (*poly_base) << " base" << endl
+ << "//" << endl
+ << "if (--d != 0)" << endl
+ << "object_traits<base_type>::init (o, *i.base, db" <<
+ (poly_base != poly_root ? ", d" : "") << ");"
+ << endl;
+ }
+ else
+ inherits (c, init_value_base_inherits_);
+
+ names (c, init_value_member_names_);
+
+ os << "}";
+
+ // init (id_image, id)
+ //
+ if (id != 0 && !base_id)
+ {
+ os << "void " << traits << "::" << endl
+ << "init (id_image_type& i, const id_type& id" <<
+ (optimistic != 0 ? ", const version_type* v" : "") << ")"
+ << "{";
+
+ if (grow_id)
+ os << "bool grew (false);";
+
+ if (composite_wrapper (utype (*id)))
+ os << db << "::statement_kind sk (" << db << "::statement_select);";
+
+ init_id_image_member_->traverse (*id);
+
+ if (optimistic != 0)
+ {
+ // Here we rely on the fact that init_image_member
+ // always wraps the statements in a block.
+ //
+ os << "if (v != 0)";
+ init_version_image_member_->traverse (*optimistic);
+ }
+
+ if (grow_id)
+ os << "if (grew)" << endl
+ << "i.version++;";
+
+ os << "}";
+ }
+
+ // The rest does not apply to reuse-abstract objects.
+ //
+ if (reuse_abst)
+ return;
+
+ //
+ // Containers (concrete).
+ //
+
+ // Statement cache (definition).
+ //
+ if (id != 0)
+ {
+ os << "struct " << traits << "::container_statement_cache_type"
+ << "{";
+
+ instance<container_cache_members> cm;
+ cm->traverse (c);
+
+ os << (containers ? "\n" : "")
+ << "container_statement_cache_type (" << db << "::connection&" <<
+ (containers ? " c" : "") << ")";
+
+ instance<container_cache_init_members> im;
+ im->traverse (c);
+
+ os << "{"
+ << "}"
+ << "};";
+ }
+
+ // Polymorphic map.
+ //
+ if (poly)
+ {
+ if (!poly_derived)
+ os << traits << "::map_type*" << endl
+ << traits << "::map;"
+ << endl;
+
+ os << traits << "::" << (abst ? "abstract_" : "") << "info_type" << endl
+ << traits << "::info (" << endl
+ << "typeid (" << type << ")," << endl;
+
+ if (poly_derived)
+ os << "&object_traits< " << class_fq_name (*poly_base) << " >::info";
+ else
+ os << "0";
+
+ if (!abst)
+ {
+ string n (class_fq_name (c));
+
+ os << "," << endl
+ << strlit (string (n, 2, string::npos)) << "," << endl
+ << "&odb::create_impl< " << type << " >," << endl
+ << "&odb::dispatch_impl< " << type << " >," << endl;
+
+ if (poly_derived)
+ os << "&statements_type::delayed_loader";
+ else
+ os << "0";
+ }
+
+ os << ");"
+ << endl;
+
+ if (!abst)
+ os << traits << "::entry_type" << endl
+ << traits << "::entry;"
+ << endl;
+ }
+
+ //
+ // Statements.
+ //
+
+ string const& table (table_qname (c));
+
+ // persist_statement
+ //
+ {
+ statement_columns sc;
+ {
+ statement_kind sk (statement_insert); // Imperfect forwarding.
+ instance<object_columns> ct (sk, sc);
+ ct->traverse (c);
+ process_statement_columns (sc, statement_insert);
+ }
+
+ bool dv (sc.empty ()); // The DEFAULT VALUES syntax.
+
+ os << "const char " << traits << "::persist_statement[] =" << endl
+ << strlit ("INSERT INTO " + table_qname(c) + (dv ? "" : " (")) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : ")")) << endl;
+ }
+
+ instance<query_parameters> qp;
+
+ persist_statement_extra (c, *qp, persist_after_columns);
+
+ if (!dv)
+ {
+ string values;
+ instance<persist_statement_params> pt (values, *qp);
+ pt->traverse (c);
+ os << strlit (" VALUES (" + values + ")");
+ }
+ else
+ os << strlit (" DEFAULT VALUES");
+
+ persist_statement_extra (c, *qp, persist_after_values);
+
+ os << ";"
+ << endl;
+ }
+
+ if (id != 0)
+ {
+ instance<object_columns_list> id_cols;
+ id_cols->traverse (*id);
+
+ std::vector<size_t> find_column_counts (abst ? 1 : poly_depth);
+
+ // find_statement
+ //
+ if (poly_derived)
+ os << "const char* const " << traits << "::find_statements[] ="
+ << "{";
+ else
+ os << "const char " << traits << "::find_statement[] =" << endl;
+
+ for (size_t d (poly_depth); d != 0;)
+ {
+ statement_columns sc;
+ {
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ instance<object_columns> t (table, sk, sc, d);
+ t->traverse (c);
+ process_statement_columns (sc, statement_select);
+ find_column_counts[poly_depth - d] = sc.size ();
+ }
+
+ os << strlit ("SELECT ") << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "")) << endl;
+ }
+
+ os << strlit (" FROM " + table) << endl;
+
+ if (poly_derived)
+ {
+ instance<polymorphic_object_joins> j (c, d);
+ j->traverse (c);
+ }
+
+ bool f (false); // @@ (im)perfect forwarding
+ instance<object_joins> j (c, f, d); // @@ (im)perfect forwarding
+ j->traverse (c);
+
+ instance<query_parameters> qp;
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ os << endl;
+
+ os << strlit ((i == b ? " WHERE " : " AND ") + table + "." +
+ quote_id (i->name) + "=" + qp->next ());
+ }
+
+ if (abst)
+ break;
+
+ if (--d != 0)
+ os << "," << endl
+ << endl;
+ }
+
+ if (poly_derived)
+ os << "};";
+ else
+ os << ";"
+ << endl;
+
+ // find_column_counts
+ //
+ if (poly_derived)
+ {
+ os << "const std::size_t " << traits << "::find_column_counts[] ="
+ << "{";
+
+ for (std::vector<size_t>::iterator i (find_column_counts.begin ()),
+ e (find_column_counts.end ()); i != e;)
+ {
+ os << *i << "UL";
+
+ if (++i != e)
+ os << ',' << endl;
+ }
+
+ os << "};";
+ }
+
+ // find_discriminator_statement
+ //
+ if (poly && !poly_derived)
+ {
+ statement_columns sc;
+ {
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ instance<object_columns> t (table, sk, sc);
+ t->traverse (*discriminator);
+
+ if (optimistic != 0)
+ t->traverse (*optimistic);
+
+ process_statement_columns (sc, statement_select);
+ }
+
+ os << "const char " << traits << "::" << endl
+ << "find_discriminator_statement[] =" << endl
+ << strlit ("SELECT ") << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "")) << endl;
+ }
+
+ os << strlit (" FROM " + table) << endl;
+
+ instance<query_parameters> qp;
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ os << endl;
+
+ os << strlit ((i == b ? " WHERE " : " AND ") + table + "." +
+ quote_id (i->name) + "=" + qp->next ());
+ }
+
+ os << ";"
+ << endl;
+ }
+
+ // update_statement
+ //
+ if (cc.total != cc.id + cc.inverse + cc.readonly)
+ {
+ instance<query_parameters> qp;
+
+ statement_columns sc;
+ {
+ query_parameters* p (qp.get ()); // Imperfect forwarding.
+ statement_kind sk (statement_update); // Imperfect forwarding.
+ instance<object_columns> t (sk, sc, p);
+ t->traverse (c);
+ process_statement_columns (sc, statement_update);
+ }
+
+ os << "const char " << traits << "::update_statement[] =" << endl
+ << strlit ("UPDATE " + table + " SET ") << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "")) << endl;
+ }
+
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ os << endl;
+
+ os << strlit ((i == b ? " WHERE " : " AND ") +
+ quote_id (i->name) + "=" + qp->next ());
+ }
+
+ if (optimistic != 0 && !poly_derived)
+ os << endl
+ << strlit (" AND " + column_qname (*optimistic) +
+ "=" + qp->next ());
+ os << ";"
+ << endl;
+ }
+
+ // erase_statement
+ //
+ {
+ instance<query_parameters> qp;
+ os << "const char " << traits << "::erase_statement[] =" << endl
+ << strlit ("DELETE FROM " + table);
+
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+
+ os << endl
+ << strlit ((i == b ? " WHERE " : " AND ") +
+ quote_id (i->name) + "=" + qp->next ());
+ }
+
+ os << ";"
+ << endl;
+ }
+
+ if (optimistic != 0 && !poly_derived)
+ {
+ instance<query_parameters> qp;
+
+ os << "const char " << traits << "::optimistic_erase_statement[] " <<
+ "=" << endl
+ << strlit ("DELETE FROM " + table);
+
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ os << endl
+ << strlit ((i == b ? " WHERE " : " AND ") +
+ quote_id (i->name) + "=" + qp->next ());
+ }
+
+ os << endl
+ << strlit (" AND " + column_qname (*optimistic) +
+ "=" + qp->next ()) << ";"
+ << endl;
+ }
+ }
+
+ if (options.generate_query ())
+ {
+ // query_statement
+ //
+ statement_columns sc;
+ {
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ instance<object_columns> oc (table, sk, sc, poly_depth);
+ oc->traverse (c);
+ process_statement_columns (sc, statement_select);
+ }
+
+ os << "const char " << traits << "::query_statement[] =" << endl
+ << strlit ("SELECT ") << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "")) << endl;
+ }
+
+ os << strlit (" FROM " + table) << endl;
+
+ if (poly_derived)
+ {
+ instance<polymorphic_object_joins> j (c, poly_depth);
+ j->traverse (c);
+ }
+
+ if (id != 0)
+ {
+ bool t (true); //@@ (im)perfect forwarding
+ instance<object_joins> oj (c, t, poly_depth); //@@ (im)perfect forwarding
+ oj->traverse (c);
+ }
+
+ os << strlit (" ") << ";"
+ << endl;
+
+ // erase_query_statement
+ //
+ os << "const char " << traits << "::erase_query_statement[] =" << endl
+ << strlit ("DELETE FROM " + table) << endl;
+
+ // DELETE JOIN:
+ //
+ // MySQL:
+ // << strlit ("DELETE FROM " + table + " USING " + table) << endl;
+ // << strlit ("DELETE " + table + " FROM " + table) << endl;
+ // oj->write ();
+ //
+
+ os << strlit (" ") << ";"
+ << endl;
+
+ // table_name
+ //
+ os << "const char " << traits << "::table_name[] =" << endl
+ << strlit (table_qname (c)) << ";" // Use quoted name.
+ << endl;
+ }
+
+ // persist ()
+ //
+ os << "void " << traits << "::" << endl
+ << "persist (database& db, " << (auto_id ? "" : "const ") <<
+ "object_type& obj";
+
+ if (poly)
+ os << ", bool top, bool dyn";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (top);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (poly)
+ os << "if (dyn)" << endl
+ << "{"
+ << "const std::type_info& t (typeid (obj));"
+ << endl
+ << "if (t != info.type)"
+ << "{"
+ << "const info_type& pi (root_traits::map->find (t));"
+ << "pi.dispatch (info_type::call_persist, db, &obj, 0);"
+ << "return;"
+ << "}"
+ << "}";
+
+ // If we are database-poly-abstract but not C++-abstract, then make
+ // sure we are not trying to persist an instance of an abstract class.
+ //
+ if (abst && !c.abstract ())
+ os << "if (top)" << endl
+ << "throw abstract_class ();"
+ << endl;
+
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection ());"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+
+ // Call callback (pre_persist).
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "callback (db," << endl
+ << (auto_id ? "static_cast<const object_type&> (obj)," : "obj,") << endl
+ << "callback_event::pre_persist);"
+ << endl;
+ }
+
+ // Call our base if we are a derived type in a polymorphic
+ // hierarchy.
+ //
+ if (poly_derived)
+ os << "object_traits<base_type>::persist (db, obj, false, false);"
+ << endl;
+
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.insert_image_binding ());";
+
+ if (poly_derived)
+ os << "const binding& idb (sts.id_image_binding ());";
+
+ os << endl
+ << "if (init (im, obj, statement_insert))" << endl
+ << "im.version++;"
+ << endl;
+
+ if (!poly_derived && auto_id && insert_send_auto_id)
+ {
+ string const& n (id->name ());
+ string var ("im." + n + (n[n.size () - 1] == '_' ? "" : "_"));
+ init_auto_id (*id, var);
+ os << endl;
+ }
+
+ os << "if (";
+
+ if (poly_derived)
+ os << "idb.version != sts.insert_id_binding_version () ||" << endl;
+
+ os << "im.version != sts.insert_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, ";
+
+ if (poly_derived)
+ os << "idb.bind, idb.count, ";
+
+ os << "im, statement_insert);";
+
+ if (poly_derived)
+ os << "sts.insert_id_binding_version (idb.version);";
+
+ os << "sts.insert_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+
+ os << "insert_statement& st (sts.persist_statement ());"
+ << "if (!st.execute ())" << endl
+ << "throw object_already_persistent ();"
+ << endl;
+
+ if (!poly_derived && auto_id)
+ {
+ if (const_type (id->type ()))
+ os << "const_cast< id_type& > (obj." << id->name () << ")";
+ else
+ os << "obj." << id->name ();
+
+ os << " = static_cast< id_type > (st.id ());"
+ << endl;
+ }
+
+ if (optimistic != 0 && !poly_derived)
+ {
+ // Set the version in the object member.
+ //
+ if (!auto_id || const_type (optimistic->type ()))
+ os << "const_cast< version_type& > (obj." << optimistic->name () <<
+ ") = 1;";
+ else
+ os << "obj." << optimistic->name () << " = 1;";
+
+ os << endl;
+ }
+
+ // Initialize id_image and binding if we are a root of a polymorphic
+ // hierarchy or if we have non-inverse containers.
+ //
+ if (!poly_derived && (poly || straight_containers))
+ {
+ // If this is a polymorphic root without containers, then we only
+ // need to do this if we are not a top-level call. If we are poly-
+ // abstract, then top will always be false.
+ //
+ if (poly && !straight_containers && !abst)
+ os << "if (!top)"
+ << "{";
+
+ os << "id_image_type& i (sts.id_image ());"
+ << "init (i, obj." << id->name () << ");"
+ << endl
+ << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;"
+ << "}";
+
+ if (poly && !straight_containers && !abst)
+ os << "}";
+ }
+
+ if (straight_containers)
+ {
+ instance<container_calls> t (container_calls::persist_call);
+ t->traverse (c);
+ }
+
+ // Call callback (post_persist).
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "callback (db," << endl
+ << (auto_id ? "static_cast<const object_type&> (obj)," : "obj,") << endl
+ << "callback_event::post_persist);";
+ }
+
+ os << "}";
+
+ // update ()
+ //
+ bool readonly (context::readonly (c));
+
+ if (id != 0 && (!readonly || poly))
+ {
+ os << "void " << traits << "::" << endl
+ << "update (database& db, const object_type& obj";
+
+ if (poly)
+ os << ", bool top, bool dyn";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (top);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (poly)
+ os << "if (dyn)" << endl
+ << "{"
+ << "const std::type_info& t (typeid (obj));"
+ << endl
+ << "if (t != info.type)"
+ << "{"
+ << "const info_type& pi (root_traits::map->find (t));"
+ << "pi.dispatch (info_type::call_update, db, &obj, 0);"
+ << "return;"
+ << "}"
+ << "}";
+
+ // If we are database-poly-abstract but not C++-abstract, then make
+ // sure we are not trying to update an instance of an abstract class.
+ //
+ if (abst && !c.abstract ())
+ os << "if (top)" << endl
+ << "throw abstract_class ();"
+ << endl;
+
+ // If we are readonly, then there is nothing else to do.
+ //
+ if (!readonly)
+ {
+ // Call callback (pre_update).
+ //
+ if (!abst) // If we are poly-abstract then top will always be false.
+ {
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "callback (db, obj, callback_event::pre_update);"
+ << endl;
+ }
+
+ if (poly_derived)
+ {
+ bool readonly_base (context::readonly (*poly_base));
+
+ if (readonly_base ||
+ cc.total != cc.id + cc.inverse + cc.readonly ||
+ straight_readwrite_containers)
+ {
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection ());"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+ }
+
+ // Unless our base is readonly, call it first.
+ //
+ if (!readonly_base)
+ {
+ os << "object_traits<base_type>::update (db, obj, false, false);"
+ << endl;
+ }
+ else
+ {
+ // Otherwise, we have to initialize the id image ourselves. If
+ // we don't have any columns or containers to update, then we
+ // only have to do it if this is not a top-level call. If we
+ // are abstract, then top is always false.
+ //
+ if (cc.total == cc.id + cc.inverse + cc.readonly &&
+ !straight_readwrite_containers &&
+ !abst)
+ os << "if (!top)";
+
+ os << "{"
+ << "id_image_type& i (sts.id_image ());"
+ << "init (i, obj." << id->name () << ");"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;"
+ << "}"
+ << "}";
+ }
+
+ if (cc.total != cc.id + cc.inverse + cc.readonly ||
+ straight_readwrite_containers)
+ {
+ os << "const binding& idb (sts.id_image_binding ());"
+ << endl;
+ }
+
+ if (cc.total != cc.id + cc.inverse + cc.readonly)
+ {
+ // Initialize the object image.
+ //
+ os << "image_type& im (sts.image ());"
+ << "if (init (im, obj, statement_update))" << endl
+ << "im.version++;"
+ << endl;
+
+ os << "binding& imb (sts.update_image_binding ());"
+ << "if (idb.version != sts.update_id_binding_version () ||" << endl
+ << "im.version != sts.update_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, idb.bind, idb.count, im, statement_update);"
+ << "sts.update_id_binding_version (idb.version);"
+ << "sts.update_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+
+ os << "if (sts.update_statement ().execute () == 0)" << endl
+ << "throw object_not_persistent ();"
+ << endl;
+ }
+ else if (readonly_base)
+ {
+ // If our base is readonly and we don't have any columns to
+ // update, then we have to make sure this object actually
+ // exists in the database. Use the discriminator_() call for
+ // that.
+ //
+ os << "root_traits::discriminator_ (sts.root_statements (), obj." <<
+ id->name () << ", 0);"
+ << endl;
+ }
+ // Otherwise, nothing else to do here if we don't have any columns
+ // to update.
+ //
+ }
+ else if (cc.total != cc.id + cc.inverse + cc.readonly)
+ {
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection ());"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+
+ // Initialize object and id images.
+ //
+ os << "id_image_type& i (sts.id_image ());";
+
+ if (optimistic == 0)
+ os << "init (i, obj." << id->name () << ");";
+ else
+ os << "init (i, obj." << id->name () << ", &obj." <<
+ optimistic->name () << ");";
+
+ os << endl
+ << "image_type& im (sts.image ());"
+ << "if (init (im, obj, statement_update))" << endl
+ << "im.version++;"
+ << endl;
+
+ // Update binding is bound to two images (object and id)
+ // so we have to track both versions.
+ //
+ os << "bool u (false);" // Avoid incrementing version twice.
+ << "binding& imb (sts.update_image_binding ());"
+ << "if (im.version != sts.update_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, statement_update);"
+ << "sts.update_image_version (im.version);"
+ << "imb.version++;"
+ << "u = true;"
+ << "}";
+
+ // To update the id part of the update binding we have to do
+ // it indirectly via the id binding, which just points to the
+ // suffix of the update bind array (see object_statements).
+ //
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.update_id_image_version () ||" << endl
+ << "idb.version == 0)"
+ << "{"
+ // If the id binding is up-to-date, then that means update
+ // binding is too and we just need to update the versions.
+ //
+ << "if (i.version != sts.id_image_version () ||" << endl
+ << "idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ // Update the id binding versions since we may use them later
+ // to update containers.
+ //
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;"
+ << "}"
+ << "sts.update_id_image_version (i.version);"
+ << endl
+ << "if (!u)" << endl
+ << "imb.version++;"
+ << "}";
+
+ os << "if (sts.update_statement ().execute () == 0)" << endl;
+
+ if (optimistic == 0)
+ os << "throw object_not_persistent ();";
+ else
+ os << "throw object_changed ();";
+
+ os << endl;
+ }
+ else
+ {
+ // We don't have any columns to update. Note that we still have
+ // to make sure this object exists in the database. For that we
+ // will run the SELECT query using the find_() function.
+ //
+ //
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection ());"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+
+ if (poly)
+ {
+ // In case of a polymorphic root, use discriminator_(), which
+ // is faster. And initialize the id image, unless this is a
+ // top-level call.
+ //
+ os << "discriminator_ (sts, obj." << id->name () << ", 0);"
+ << endl;
+
+ if (!abst)
+ os << "if (!top)";
+
+ os << "{"
+ << "id_image_type& i (sts.id_image ());"
+ << "init (i, obj." << id->name () << ");"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;"
+ << "}"
+ << "}";
+ }
+ else
+ {
+ os << "if (!find_ (sts, &obj." << id->name () << "))" << endl
+ << "throw object_not_persistent ();"
+ << endl;
+
+ if (delay_freeing_statement_result)
+ os << "sts.find_statement ().free_result ();";
+ }
+
+ if (straight_readwrite_containers)
+ os << "binding& idb (sts.id_image_binding ());"
+ << endl;
+ }
+
+ if (straight_readwrite_containers)
+ {
+ instance<container_calls> t (container_calls::update_call);
+ t->traverse (c);
+ }
+
+ if (optimistic != 0 && !poly_derived)
+ {
+ // Update version in the object member.
+ //
+ os << "const_cast<version_type&> (obj." << optimistic->name () << ")++;";
+ }
+
+ // Call callback (post_update).
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "callback (db, obj, callback_event::post_update);";
+ }
+ } // readonly
+
+ os << "}";
+ }
+
+ // erase (id)
+ //
+ if (id != 0)
+ {
+ os << "void " << traits << "::" << endl
+ << "erase (database& db, const id_type& id";
+
+ if (poly)
+ os << ", bool top, bool dyn";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (top);";
+
+ os << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection ());"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+
+ // Get the discriminator and determine the dynamic type of
+ // this object.
+ //
+ if (poly)
+ {
+ os << "if (dyn)" << endl
+ << "{"
+ << "discriminator_type d;"
+ << "root_traits::discriminator_ (sts.root_statements (), id, &d);";
+
+ if (!abst)
+ os << endl
+ << "if (d != info.discriminator)"
+ << "{";
+
+ os << "const info_type& pi (root_traits::map->find (d));"
+ << endl
+ // Check that the dynamic type is derived from the static type.
+ //
+ << "if (!pi.derived (info))" << endl
+ << "throw object_not_persistent ();"
+ << endl
+ << "pi.dispatch (info_type::call_erase, db, 0, &id);"
+ << "return;";
+
+ if (!abst)
+ os << "}";
+
+ os << "}";
+ }
+
+ // Initialize id image.
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ os << "id_image_type& i (sts.id_image ());"
+ << "init (i, id);"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;"
+ << "}";
+
+ if (poly)
+ os << "}";
+ }
+
+ // Erase containers first so that there are no reference
+ // violations (we don't want to rely on ON DELETE CASCADE
+ // here since in case of a custom schema, it might not be
+ // there).
+ //
+ if (straight_containers)
+ {
+ if (poly)
+ os << "binding& idb (sts.id_image_binding ());"
+ << endl;
+
+ instance<container_calls> t (container_calls::erase_call);
+ t->traverse (c);
+ }
+
+ os << "if (sts.erase_statement ().execute () != 1)" << endl
+ << "throw object_not_persistent ();"
+ << endl;
+
+ if (poly_derived)
+ {
+ // Call our base last (we erase polymorphic objects from base
+ // to derived in order not to trigger cascading deletes).
+ //
+ os << "object_traits<base_type>::erase (db, id, false, false);"
+ << endl;
+ }
+
+ // Remove from the object cache.
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "pointer_cache_traits::erase (db, id);";
+ }
+
+ os << "}";
+ }
+
+ // erase (object)
+ //
+ if (id != 0 && (poly || optimistic != 0))
+ {
+ os << "void " << traits << "::" << endl
+ << "erase (database& db, const object_type& obj";
+
+ if (poly)
+ os << ", bool top, bool dyn";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (top);";
+
+ os << endl;
+
+ if (poly)
+ os << "if (dyn)" << endl
+ << "{"
+ << "const std::type_info& t (typeid (obj));"
+ << endl
+ << "if (t != info.type)"
+ << "{"
+ << "const info_type& pi (root_traits::map->find (t));"
+ << "pi.dispatch (info_type::call_erase, db, &obj, 0);"
+ << "return;"
+ << "}"
+ << "}";
+
+ // If we are database-poly-abstract but not C++-abstract, then make
+ // sure we are not trying to persist an instance of an abstract class.
+ //
+ if (abst && !c.abstract ())
+ os << "if (top)" << endl
+ << "throw abstract_class ();"
+ << endl;
+
+ // Determine the dynamic type of this object.
+ //
+ if (optimistic == 0)
+ {
+ os << "callback (db, obj, callback_event::pre_erase);"
+ << "erase (db, id (obj), true, false);"
+ << "callback (db, obj, callback_event::post_erase);";
+ }
+ else
+ {
+ string rsts (poly_derived ? "rsts" : "sts");
+
+ os << "using namespace " << db << ";"
+ << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection ());"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (poly_derived)
+ os << "root_statements_type& rsts (sts.root_statements ());";
+
+ os << endl;
+
+ // Call callback (pre_erase).
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "callback (db, obj, callback_event::pre_erase);"
+ << endl;
+ }
+
+ // Initialize id + managed column image.
+ //
+ os << "binding& idb (" << rsts << ".id_image_binding ());"
+ << endl;
+
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ os << "id_image_type& i (" << rsts << ".id_image ());"
+ << "init (i, obj." << id->name () << ", &obj." <<
+ optimistic->name () << ");"
+ << endl;
+
+ // To update the id part of the optimistic id binding we have
+ // to do it indirectly via the id binding, since both id and
+ // optimistic id bindings just point to the suffix of the
+ // update bind array (see object_statements).
+ //
+ os << "binding& oidb (" << rsts << ".optimistic_id_image_binding ());"
+ << "if (i.version != " << rsts <<
+ ".optimistic_id_image_version () ||" << endl
+ << "oidb.version == 0)"
+ << "{"
+ // If the id binding is up-to-date, then that means optimistic
+ // id binding is too and we just need to update the versions.
+ //
+ << "if (i.version != " << rsts << ".id_image_version () ||" << endl
+ << "idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ // Update the id binding versions since we may use them later
+ // to delete containers.
+ //
+ << rsts << ".id_image_version (i.version);"
+ << "idb.version++;"
+ << "}"
+ << rsts << ".optimistic_id_image_version (i.version);"
+ << "oidb.version++;"
+ << "}";
+
+ if (poly)
+ os << "}"; // if (top)
+ }
+
+ // If this is a derived type in a polymorphic hierarchy, then we
+ // need to check the version (stored in root) before we go ahead
+ // and start deleting things. Also use the same code for root with
+ // containers since it is more efficient than the find_() method
+ // below.
+ //
+ if (poly_derived || (poly && straight_containers))
+ {
+ // Only do the check in the top-level call.
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ os << "if (top)"
+ << "{"
+ << "version_type v;"
+ << "root_traits::discriminator_ (" << rsts << ", obj." <<
+ id->name () << ", 0, &v);"
+ << endl
+ << "if (v != obj." << optimistic->name () << ")" << endl
+ << "throw object_changed ();"
+ << "}";
+ }
+ }
+ else if (straight_containers)
+ {
+ // Things get complicated here: we don't want to trash the
+ // containers and then find out that the versions don't match
+ // and we therefore cannot delete the object. After all, there
+ // is no guarantee that the user will abort the transaction.
+ // In fact, a perfectly reasonable scenario is to reload the
+ // object, re-check the condition, decide not to delete the
+ // object, and then commit the transaction.
+ //
+ // There doesn't seem to be anything better than first making
+ // sure we can delete the object, then deleting the container
+ // data, and then deleting the object. To check that we can
+ // delete the object we are going to use find_() and then
+ // compare the versions. A special-purpose SELECT query would
+ // have been more efficient but it would complicated and bloat
+ // things significantly.
+ //
+ os << "if (!find_ (sts, &obj." << id->name () << "))" << endl
+ << "throw object_changed ();"
+ << endl;
+
+ if (delay_freeing_statement_result)
+ os << "sts.find_statement ().free_result ();";
+
+ os << "if (version (sts.image ()) != obj." <<
+ optimistic->name () << ")" << endl
+ << "throw object_changed ();"
+ << endl;
+ }
+
+ // Erase containers first so that there are no reference
+ // violations (we don't want to rely on ON DELETE CASCADE
+ // here since in case of a custom schema, it might not be
+ // there).
+ //
+ if (straight_containers)
+ {
+ instance<container_calls> t (container_calls::erase_call);
+ t->traverse (c);
+ }
+
+ const char* st (
+ poly_derived ? "erase_statement" : "optimistic_erase_statement");
+
+ os << "if (sts." << st << " ().execute () != 1)" << endl
+ << "throw object_changed ();"
+ << endl;
+
+ if (poly_derived)
+ {
+ // Call our base last (we erase polymorphic objects from base
+ // to derived in order not to trigger cascading deletes).
+ //
+ os << "object_traits<base_type>::erase (db, obj, false, false);"
+ << endl;
+ }
+
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ // Remove from the object cache.
+ //
+ os << "pointer_cache_traits::erase (db, obj." << id->name () << ");";
+
+ // Call callback (post_erase).
+ //
+ os << "callback (db, obj, callback_event::post_erase);";
+
+ if (poly)
+ os << "}";
+ }
+ }
+
+ os << "}";
+ }
+
+ // find (id)
+ //
+ if (id != 0 && c.default_ctor ())
+ {
+ string rsts (poly_derived ? "rsts" : "sts");
+
+ os << traits << "::pointer_type" << endl
+ << traits << "::" << endl
+ << "find (database& db, const id_type& id)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ // First check the session.
+ //
+ os << "{";
+
+ if (poly_derived)
+ os << "root_traits::pointer_type rp (pointer_cache_traits::find (" <<
+ "db, id));"
+ << endl
+ << "if (!root_traits::pointer_traits::null_ptr (rp))" << endl
+ << "return" << endl
+ << " root_traits::pointer_traits::dynamic_pointer_cast<" <<
+ "object_type> (rp);";
+ else
+ os << "pointer_type p (pointer_cache_traits::find (db, id));"
+ << endl
+ << "if (!pointer_traits::null_ptr (p))" << endl
+ << "return p;";
+
+ os << "}";
+
+ // Get the connection.
+ //
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection ());"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (poly_derived)
+ os << "root_statements_type& rsts (sts.root_statements ());";
+
+ os << endl
+ << "statements_type::auto_lock l (" << rsts << ");";
+
+ if (delay_freeing_statement_result)
+ os << "auto_result ar;";
+
+ if (poly)
+ os << "root_traits::discriminator_type d;";
+
+ os << endl
+ << "if (l.locked ())"
+ << "{"
+ << "if (!find_ (sts, &id))" << endl
+ << "return pointer_type ();";
+
+ if (delay_freeing_statement_result)
+ os << endl
+ << "ar.set (sts.find_statement (" << (poly_derived ? "depth" : "") <<
+ "));";
+
+ if (poly)
+ os << "d = root_traits::discriminator (" << rsts << ".image ());";
+
+ os << "}";
+
+ if (poly)
+ {
+ // If statements are locked, then get the discriminator by
+ // executing a special SELECT statement. We need it to be
+ // able to create an object of the correct dynamic type
+ // which will be loaded later.
+ //
+ os << "else" << endl
+ << "root_traits::discriminator_ (" << rsts << ", id, &d);"
+ << endl;
+
+ if (abst)
+ os << "const info_type& pi (root_traits::map->find (d));"
+ << endl;
+ else
+ os << "const info_type& pi (" << endl
+ << "d == info.discriminator ? info : root_traits::map->find (d));"
+ << endl;
+ }
+
+ // Create the object.
+ //
+ if (poly_derived)
+ {
+ os << "root_traits::pointer_type rp (pi.create ());"
+ << "pointer_type p (" << endl
+ << "root_traits::pointer_traits::static_pointer_cast<object_type> " <<
+ "(rp));"
+ << "pointer_traits::guard pg (p);"
+ << endl;
+
+ // Insert it as a root pointer (for non-unique pointers, rp should
+ // still be valid and for unique pointers this is a no-op).
+ //
+ os << "pointer_cache_traits::insert_guard ig (" << endl
+ << "pointer_cache_traits::insert (db, id, rp));"
+ << endl;
+ }
+ else
+ {
+ if (poly)
+ os << "pointer_type p (pi.create ());";
+ else
+ os << "pointer_type p (" << endl
+ << "access::object_factory<object_type, pointer_type>::create ());";
+
+ os << "pointer_traits::guard pg (p);"
+ << endl;
+
+ os << "pointer_cache_traits::insert_guard ig (" << endl
+ << "pointer_cache_traits::insert (db, id, p));"
+ << endl;
+ }
+
+ os << "object_type& obj (pointer_traits::get_ref (p));"
+ << endl
+ << "if (l.locked ())"
+ << "{"
+ << "select_statement& st (sts.find_statement (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << "ODB_POTENTIALLY_UNUSED (st);"
+ << endl;
+
+ if (poly)
+ os << "callback_event ce (callback_event::pre_load);"
+ << "pi.dispatch (info_type::call_callback, db, &obj, &ce);";
+ else
+ os << "callback (db, obj, callback_event::pre_load);";
+
+ os << "init (obj, sts.image (), &db);";
+
+ init_value_extra ();
+
+ if (delay_freeing_statement_result)
+ os << "ar.free ();";
+
+ os << "load_ (sts, obj);";
+
+ if (poly)
+ // Load the dynamic part of the object unless static and dynamic
+ // types are the same.
+ //
+ os << endl
+ << "if (&pi != &info)"
+ << "{"
+ << "std::size_t d (depth);"
+ << "pi.dispatch (info_type::call_load, db, &obj, &d);"
+ << "}";
+
+ os << rsts << ".load_delayed ();"
+ << "l.unlock ();";
+
+ if (poly)
+ os << "ce = callback_event::post_load;"
+ << "pi.dispatch (info_type::call_callback, db, &obj, &ce);";
+ else
+ os << "callback (db, obj, callback_event::post_load);";
+
+ os << "}"
+ << "else" << endl
+ << rsts << ".delay_load (id, obj, ig.position ()" <<
+ (poly ? ", pi.delayed_loader" : "") << ");"
+ << endl;
+
+ os << "ig.release ();"
+ << "pg.release ();"
+ << "return p;"
+ << "}";
+ }
+
+ // find (id, obj)
+ //
+ if (id != 0)
+ {
+ string rsts (poly_derived ? "rsts" : "sts");
+
+ os << "bool " << traits << "::" << endl
+ << "find (database& db, const id_type& id, object_type& obj";
+
+ if (poly)
+ os << ", bool dyn";
+
+ os << ")"
+ << "{";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (dyn);"
+ << endl;
+
+ os << "using namespace " << db << ";"
+ << endl;
+
+ if (poly)
+ {
+ if (!abst)
+ os << "if (dyn)" << endl
+ << "{";
+
+ os << "const std::type_info& t (typeid (obj));";
+
+ if (!abst)
+ os << endl
+ << "if (t != info.type)"
+ << "{";
+
+ os << "const info_type& pi (root_traits::map->find (t));"
+ << "return pi.dispatch (info_type::call_find, db, &obj, &id);";
+
+ if (!abst)
+ os << "}"
+ << "}";
+ }
+
+ if (!abst)
+ {
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection ());"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (poly_derived)
+ os << "root_statements_type& rsts (sts.root_statements ());";
+
+ // This can only be top-level call so auto_lock must succeed.
+ //
+ os << endl
+ << "statements_type::auto_lock l (" << rsts << ");"
+ << endl;
+
+ os << "if (!find_ (sts, &id))" << endl
+ << "return false;"
+ << endl;
+
+ os << "select_statement& st (sts.find_statement (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << "ODB_POTENTIALLY_UNUSED (st);"
+ << endl;
+
+ if (delay_freeing_statement_result)
+ os << "auto_result ar (st);";
+
+ os << "reference_cache_traits::insert_guard ig (" << endl
+ << "reference_cache_traits::insert (db, id, obj));"
+ << endl
+ << "callback (db, obj, callback_event::pre_load);"
+ << "init (obj, sts.image (), &db);";
+
+ init_value_extra ();
+
+ if (delay_freeing_statement_result)
+ os << "ar.free ();";
+
+ os << "load_ (sts, obj);"
+ << rsts << ".load_delayed ();"
+ << "l.unlock ();"
+ << "callback (db, obj, callback_event::post_load);"
+ << "ig.release ();"
+ << "return true;";
+ }
+
+ os << "}";
+ }
+
+ // reload ()
+ //
+ if (id != 0)
+ {
+ string rsts (poly_derived ? "rsts" : "sts");
+
+ // This implementation is almost exactly the same as find(id, obj)
+ // above except that it doesn't interract with the object cache and,
+ // in case of optimistic concurrency, checks if the object actually
+ // needs to be reloaded.
+ //
+ os << "bool " << traits << "::" << endl
+ << "reload (database& db, object_type& obj";
+
+ if (poly)
+ os << ", bool dyn";
+
+ os << ")"
+ << "{";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (dyn);"
+ << endl;
+
+ os << "using namespace " << db << ";"
+ << endl;
+
+ if (poly)
+ {
+ if (!abst)
+ os << "if (dyn)" << endl
+ << "{";
+
+ os << "const std::type_info& t (typeid (obj));";
+
+ if (!abst)
+ os << endl
+ << "if (t != info.type)"
+ << "{";
+
+ os << "const info_type& pi (root_traits::map->find (t));"
+ << "return pi.dispatch (info_type::call_reload, db, &obj, 0);";
+
+ if (!abst)
+ os << "}"
+ << "}";
+ }
+
+ if (!abst)
+ {
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection ());"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (poly_derived)
+ os << "root_statements_type& rsts (sts.root_statements ());";
+
+ // This can only be top-level call so auto_lock must succeed.
+ //
+ os << endl
+ << "statements_type::auto_lock l (" << rsts << ");"
+ << endl;
+
+ os << "if (!find_ (sts, &obj." << id->name () << "))" << endl
+ << "return false;"
+ << endl;
+
+ os << "select_statement& st (sts.find_statement (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << "ODB_POTENTIALLY_UNUSED (st);"
+ << endl;
+
+ if (delay_freeing_statement_result)
+ os << "auto_result ar (st);"
+ << endl;
+
+ if (optimistic != 0)
+ {
+ os << "if (" << (poly_derived ? "root_traits::" : "") << "version (" <<
+ rsts << ".image ()) == obj." << optimistic->name () << ")" << endl
+ << "return true;"
+ << endl;
+ }
+
+ os << "callback (db, obj, callback_event::pre_load);"
+ << "init (obj, sts.image (), &db);";
+
+ init_value_extra ();
+
+ if (delay_freeing_statement_result)
+ os << "ar.free ();";
+
+ os << "load_ (sts, obj);"
+ << rsts << ".load_delayed ();"
+ << "l.unlock ();"
+ << "callback (db, obj, callback_event::post_load);"
+ << "return true;";
+ }
+
+ os << "}";
+ }
+
+ // find_ ()
+ //
+ if (id != 0)
+ {
+ os << "bool " << traits << "::" << endl
+ << "find_ (";
+
+ if (poly && !poly_derived)
+ os << "base_statements_type& sts, ";
+ else
+ os << "statements_type& sts, ";
+
+ os << "const id_type* id";
+
+ if (poly_derived && !abst)
+ os << ", std::size_t d";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ // Initialize id image.
+ //
+ if (poly_derived && !abst)
+ os << "if (d == depth)"
+ << "{";
+
+ os << "id_image_type& i (sts.id_image ());"
+ << "init (i, *id);"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;"
+ << "}";
+
+ if (poly_derived && !abst)
+ os << "}";
+
+ // Rebind data image.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.select_image_binding (" <<
+ (poly_derived ? (abst ? "depth" : "d") : "") << "));"
+ << endl;
+
+ if (poly_derived)
+ {
+ os << "if (imb.version == 0 ||" << endl
+ << "check_version (sts.select_image_versions (), im))"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select);"
+ << "update_version (sts.select_image_versions ()," << endl
+ << "im," << endl
+ << "sts.select_image_bindings ());"
+ << "}";
+ }
+ else
+ {
+ os << "if (im.version != sts.select_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, statement_select);"
+ << "sts.select_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+ }
+
+ os << "select_statement& st (sts.find_statement (" <<
+ (poly_derived ? (abst ? "depth" : "d") : "") << "));"
+ << "st.execute ();"
+ << "auto_result ar (st);"
+ << "select_statement::result r (st.fetch ());"
+ << endl;
+
+ if (grow)
+ {
+ os << "if (r == select_statement::truncated)"
+ << "{"
+ << "if (grow (im, sts.select_image_truncated ()" <<
+ (poly_derived ? (abst ? ", depth" : ", d") : "") << "))" << endl
+ << "im.version++;"
+ << endl;
+
+ if (poly_derived)
+ {
+ os << "if (check_version (sts.select_image_versions (), im))"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select);"
+ << "update_version (sts.select_image_versions ()," << endl
+ << "im," << endl
+ << "sts.select_image_bindings ());"
+ << "st.refetch ();"
+ << "}";
+ }
+ else
+ {
+ os << "if (im.version != sts.select_image_version ())"
+ << "{"
+ << "bind (imb.bind, im, statement_select);"
+ << "sts.select_image_version (im.version);"
+ << "imb.version++;"
+ << "st.refetch ();"
+ << "}";
+ }
+
+ os << "}";
+ }
+
+ // If we are delaying, only free the result if it is empty.
+ //
+ if (delay_freeing_statement_result)
+ os << "if (r != select_statement::no_data)"
+ << "{"
+ << "ar.release ();"
+ << "return true;"
+ << "}"
+ << "else" << endl
+ << "return false;";
+ else
+ os << "return r != select_statement::no_data;";
+
+ os << "}";
+ }
+
+ // load_() containers
+ //
+ if (containers || poly_derived)
+ {
+ os << "void " << traits << "::" << endl
+ << "load_ (";
+
+ if (poly && !poly_derived)
+ os << "base_statements_type& sts, ";
+ else
+ os << "statements_type& sts, ";
+
+ os << "object_type& obj";
+
+ if (poly_derived)
+ os << ", std::size_t d";
+
+ os << ")"
+ << "{";
+
+ if (poly_derived)
+ os << "if (--d != 0)" << endl
+ << "object_traits<base_type>::load_ (sts.base_statements (), obj" <<
+ (poly_base != poly_root ? ", d" : "") << ");"
+ << endl;
+
+ if (containers)
+ {
+ os << db << "::binding& idb (sts.id_image_binding ());"
+ << endl;
+ instance<container_calls> t (container_calls::load_call);
+ t->traverse (c);
+ }
+
+ os << "}";
+ }
+
+ // load_()
+ //
+ // Load the dynamic part of the object. We don't need it if we are
+ // poly-abstract.
+ //
+ if (poly_derived && !abst)
+ {
+ os << "void " << traits << "::" << endl
+ << "load_ (database& db, root_type& r, std::size_t d)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "object_type& obj (static_cast<object_type&> (r));"
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection ());"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl
+ << "d = depth - d;" // Convert to distance from derived.
+ << endl;
+
+ os << "if (!find_ (sts, 0, d))" << endl
+ << "throw object_not_persistent ();" // Database inconsistency.
+ << endl;
+
+ os << "select_statement& st (sts.find_statement (d));"
+ << "ODB_POTENTIALLY_UNUSED (st);"
+ << endl;
+
+ if (delay_freeing_statement_result)
+ os << "auto_result ar (st);";
+
+ os << "init (obj, sts.image (), &db, d);";
+
+ init_value_extra ();
+
+ if (delay_freeing_statement_result)
+ os << "ar.free ();";
+
+ os << "load_ (sts, obj, d);"
+ << "}";
+ }
+
+ // discriminator_ ()
+ //
+ if (poly && !poly_derived)
+ {
+ os << "void " << traits << "::" << endl
+ << "discriminator_ (statements_type & sts," << endl
+ << "const id_type& id," << endl
+ << "discriminator_type* pd";
+
+ if (optimistic != 0)
+ os << "," << endl
+ << "version_type* pv";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ // Initialize id image.
+ //
+ os << "id_image_type& idi (sts.discriminator_id_image ());"
+ << "init (idi, id);"
+ << endl;
+
+ os << "binding& idb (sts.discriminator_id_image_binding ());"
+ << "if (idi.version != sts.discriminator_id_image_version () ||" << endl
+ << "idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, idi" << (optimistic != 0 ? ", false" : "") << ");"
+ << "sts.discriminator_id_image_version (idi.version);"
+ << "idb.version++;"
+ << "}";
+
+ // Rebind data image.
+ //
+ os << "discriminator_image_type& i (sts.discriminator_image ());"
+ << "binding& imb (sts.discriminator_image_binding ());"
+ << endl
+ << "if (i.version != sts.discriminator_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ // Generate bind code inline. For now discriminator is simple
+ // value so we don't need statement kind (sk).
+ //
+ << bind_vector << " b (imb.bind);"
+ << "std::size_t n (0);"
+ << "{";
+ bind_discriminator_member_->traverse (*discriminator);
+ os << "}";
+
+ if (optimistic != 0)
+ {
+ os << "n++;" // For now discriminator is a simple value.
+ << "{";
+ bind_version_member_->traverse (*optimistic);
+ os << "}";
+ }
+
+ os << "sts.discriminator_image_version (i.version);"
+ << "imb.version++;"
+ << "}";
+
+ os << "{"
+ << "select_statement& st (sts.find_discriminator_statement ());"
+ << "st.execute ();"
+ << "auto_result ar (st);"
+ << "select_statement::result r (st.fetch ());"
+ << endl
+ << "if (r == select_statement::no_data)"
+ << "{";
+
+ if (optimistic != 0)
+ os << "if (pv != 0)" << endl
+ << "throw object_changed ();"
+ << "else" << endl;
+
+ os << "throw object_not_persistent ();"
+ << "}";
+
+ if (generate_grow && (
+ context::grow (*discriminator) ||
+ (optimistic != 0 && context::grow (*optimistic))))
+ {
+ os << "else if (r == select_statement::truncated)"
+ << "{";
+
+ // Generate grow code inline.
+ //
+ os << "bool grew (false);"
+ << truncated_vector << " t (sts.discriminator_image_truncated ());"
+ << endl;
+
+ index_ = 0;
+ grow_discriminator_member_->traverse (*discriminator);
+
+ if (optimistic != 0)
+ grow_version_member_->traverse (*optimistic);
+
+ os << "if (grew)" << endl
+ << "i.version++;"
+ << endl;
+
+ os << "if (i.version != sts.discriminator_image_version ())"
+ << "{"
+ // Generate bind code inline. The same code as above.
+ //
+ << bind_vector << " b (imb.bind);"
+ << "std::size_t n (0);"
+ << "{";
+ bind_discriminator_member_->traverse (*discriminator);
+ os << "}";
+
+ if (optimistic != 0)
+ {
+ os << "n++;" // For now discriminator is a simple value.
+ << "{";
+ bind_version_member_->traverse (*optimistic);
+ os << "}";
+ }
+
+ os << "sts.discriminator_image_version (i.version);"
+ << "imb.version++;"
+ << "st.refetch ();"
+ << "}"
+ << "}";
+ }
+
+ // Discriminator cannot be long data (no streaming).
+ //
+ os << "}";
+
+ // Initialize value inline instead of generating a separate
+ // init() function. For now discriminator is simple value so
+ // we don't need the database (db).
+ //
+ os << "if (pd != 0)"
+ << "{"
+ << "discriminator_type& d (*pd);";
+ init_named_discriminator_value_member_->traverse (*discriminator);
+ os << "}";
+
+ if (optimistic != 0)
+ {
+ os << "if (pv != 0)"
+ << "{"
+ << "version_type& v (*pv);";
+ init_named_version_value_member_->traverse (*optimistic);
+ os << "}";
+ }
+
+ os << "}";
+ }
+
+ if (options.generate_query ())
+ {
+ // query ()
+ //
+ os << "result< " << traits << "::object_type >" << endl
+ << traits << "::" << endl
+ << "query (database&, const query_base_type& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using odb::details::shared;"
+ << "using odb::details::shared_ptr;"
+ << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection ());"
+ << endl
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+
+ // Rebind the image if necessary.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.select_image_binding (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << endl;
+
+ if (poly_derived)
+ {
+ os << "if (imb.version == 0 ||" << endl
+ << "check_version (sts.select_image_versions (), im))"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select);"
+ << "update_version (sts.select_image_versions ()," << endl
+ << "im," << endl
+ << "sts.select_image_bindings ());"
+ << "}";
+ }
+ else
+ {
+ os << "if (im.version != sts.select_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, statement_select);"
+ << "sts.select_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+ }
+
+ os << "shared_ptr<select_statement> st (" << endl
+ << "new (shared) select_statement (" << endl;
+ object_query_statement_ctor_args (c);
+ os << "));" << endl
+ << "st->execute ();";
+
+ post_query_ (c);
+
+ char const* result_type;
+ if (poly)
+ result_type = "polymorphic_object_result_impl<object_type>";
+ else if (id != 0)
+ result_type = "object_result_impl<object_type>";
+ else
+ result_type = "no_id_object_result_impl<object_type>";
+
+ os << endl
+ << "shared_ptr< odb::" << result_type << " > r (" << endl
+ << "new (shared) " << db << "::" << result_type << " (" << endl
+ << "q, st, sts));"
+ << endl
+ << "return result<object_type> (r);"
+ << "}";
+
+ // erase_query
+ //
+ os << "unsigned long long " << traits << "::" << endl
+ << "erase_query (database&, const query_base_type& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection ());"
+ << endl
+ << "delete_statement st (" << endl;
+ object_erase_query_statement_ctor_args (c);
+ os << ");"
+ << endl
+ << "return st.execute ();"
+ << "}";
+ }
+
+ if (embedded_schema)
+ schema_->traverse (c);
+}
+
+void relational::source::class_::
+traverse_view (type& c)
+{
+ string const& type (class_fq_name (c));
+ string traits ("access::view_traits< " + type + " >");
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ view_extra (c);
+
+ //
+ // Functions.
+ //
+
+ // grow ()
+ //
+ if (generate_grow)
+ {
+ os << "bool " << traits << "::" << endl
+ << "grow (image_type& i, " << truncated_vector << " t)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (t);"
+ << endl
+ << "bool grew (false);"
+ << endl;
+
+ index_ = 0;
+ names (c, grow_member_names_);
+
+ os << "return grew;"
+ << "}";
+ }
+
+ // bind (image_type)
+ //
+ os << "void " << traits << "::" << endl
+ << "bind (" << bind_vector << " b, image_type& i)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << db << "::statement_kind sk (statement_select);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl
+ << "std::size_t n (0);"
+ << endl;
+
+ names (c, bind_member_names_);
+
+ os << "}";
+
+ // init (view, image)
+ //
+ os << "void " << traits << "::" << endl
+ << "init (view_type& o, const image_type& i, database* db)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (o);"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl;
+
+ names (c, init_value_member_names_);
+
+ os << "}";
+
+ // query_statement()
+ //
+ view_query& vq (c.get<view_query> ("query"));
+
+ if (vq.kind != view_query::runtime)
+ {
+ os << traits << "::query_base_type" << endl
+ << traits << "::" << endl
+ << "query_statement (const query_base_type& q)"
+ << "{";
+
+ if (vq.kind == view_query::complete)
+ {
+ os << "query_base_type r (" << endl;
+
+ bool ph (false);
+
+ if (!vq.literal.empty ())
+ {
+ // See if we have the '(?)' placeholder.
+ //
+ // @@ Ideally we would need to make sure we don't match
+ // this inside strings and quoted identifier. So the
+ // proper way to handle this would be to tokenize the
+ // statement using sql_lexer, once it is complete enough.
+ //
+ string::size_type p (vq.literal.find ("(?)"));
+
+ if (p != string::npos)
+ {
+ ph = true;
+ os << strlit (string (vq.literal, 0, p + 1)) << " +" << endl
+ << "(q.empty () ? query_base_type::true_expr : q) +" << endl
+ << strlit (string (vq.literal, p + 2));
+ }
+ else
+ os << strlit (vq.literal);
+ }
+ else
+ // Output the pragma location for easier error tracking.
+ //
+ os << "// From " <<
+ location_file (vq.loc).leaf () << ":" <<
+ location_line (vq.loc) << ":" <<
+ location_column (vq.loc) << endl
+ << translate_expression (
+ c, vq.expr, vq.scope, vq.loc, "query", &ph).value;
+
+ os << ");";
+
+ // If there was no placeholder, add the query condition
+ // at the end.
+ //
+ if (!ph)
+ os << "r += q.clause_prefix ();"
+ << "r += q;";
+ }
+ else // vq.kind == view_query::condition
+ {
+ statement_columns sc;
+ {
+ instance<view_columns> t (sc);
+ t->traverse (c);
+ process_statement_columns (sc, statement_select);
+ }
+
+ os << "query_base_type r (" << endl
+ << strlit ("SELECT ") << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "")) << endl;
+ }
+
+ os << ");"
+ << endl;
+
+ // Generate from-list.
+ //
+ view_objects const& objs (c.get<view_objects> ("objects"));
+
+ for (view_objects::const_iterator i (objs.begin ());
+ i != objs.end ();
+ ++i)
+ {
+ bool first (i == objs.begin ());
+ string l;
+
+ //
+ // Tables.
+ //
+
+ if (i->kind == view_object::table)
+ {
+ if (first)
+ {
+ l = "FROM ";
+ l += quote_id (i->tbl_name);
+
+ if (!i->alias.empty ())
+ l += (need_alias_as ? " AS " : " ") + quote_id (i->alias);
+
+ os << "r += " << strlit (l) << ";"
+ << endl;
+
+ continue;
+ }
+
+ l = "LEFT JOIN ";
+ l += quote_id (i->tbl_name);
+
+ if (!i->alias.empty ())
+ l += (need_alias_as ? " AS " : " ") + quote_id (i->alias);
+
+ expression e (
+ translate_expression (
+ c, i->cond, i->scope, i->loc, "table"));
+
+ if (e.kind != expression::literal)
+ {
+ error (i->loc)
+ << "invalid join condition in db pragma table" << endl;
+
+ throw operation_failed ();
+ }
+
+ l += " ON";
+
+ os << "r += " << strlit (l) << ";"
+ // Output the pragma location for easier error tracking.
+ //
+ << "// From " <<
+ location_file (i->loc).leaf () << ":" <<
+ location_line (i->loc) << ":" <<
+ location_column (i->loc) << endl
+ << "r += " << e.value << ";"
+ << endl;
+
+ continue;
+ }
+
+ //
+ // Objects.
+ //
+ semantics::class_& o (*i->obj);
+
+ bool poly (polymorphic (o));
+ size_t poly_depth (poly ? polymorphic_depth (o) : 1);
+
+ string alias (i->alias);
+
+ // For polymorphic objects, alias is just a prefix.
+ //
+ if (poly && !alias.empty ())
+ alias += "_" + table_name (o).uname ();
+
+ // First object.
+ //
+ if (first)
+ {
+ l = "FROM ";
+ l += table_qname (o);
+
+ if (!alias.empty ())
+ l += (need_alias_as ? " AS " : " ") + quote_id (alias);
+
+ os << "r += " << strlit (l) << ";";
+
+ if (poly_depth != 1)
+ {
+ instance<polymorphic_object_joins> j (
+ o, poly_depth, i->alias, "r += ", ";");
+ j->traverse (o);
+ }
+
+ os << endl;
+
+ continue;
+ }
+
+ expression e (
+ translate_expression (
+ c, i->cond, i->scope, i->loc, "object"));
+
+ // Literal expression.
+ //
+ if (e.kind == expression::literal)
+ {
+ l = "LEFT JOIN ";
+ l += table_qname (o);
+
+ if (!alias.empty ())
+ l += (need_alias_as ? " AS " : " ") + quote_id (alias);
+
+ l += " ON";
+
+ os << "r += " << strlit (l) << ";"
+ // Output the pragma location for easier error tracking.
+ //
+ << "// From " <<
+ location_file (i->loc).leaf () << ":" <<
+ location_line (i->loc) << ":" <<
+ location_column (i->loc) << endl
+ << "r += " << e.value << ";";
+
+ if (poly_depth != 1)
+ {
+ instance<polymorphic_object_joins> j (
+ o, poly_depth, i->alias, "r += ", ";");
+ j->traverse (o);
+ }
+
+ os << endl;
+
+ continue;
+ }
+
+ // We have an object relationship (pointer) for which we need
+ // to come up with the corresponding JOIN condition. If this
+ // is a to-many relationship, then we first need to JOIN the
+ // container table. This code is similar to object_joins.
+ //
+ using semantics::data_member;
+
+ data_member& m (*e.member_path.back ());
+
+ // Resolve the pointed-to object to view_object and do
+ // some sanity checks while at it.
+ //
+ semantics::class_* c (0);
+
+ if (semantics::type* cont = container (m))
+ c = object_pointer (container_vt (*cont));
+ else
+ c = object_pointer (utype (m));
+
+ view_object const* vo (0);
+
+ // Check if the pointed-to object has been previously
+ // associated with this view and is unambiguous. A
+ // pointer to ourselves is always assumed to point
+ // to this association.
+ //
+ if (&o == c)
+ vo = &*i;
+ else
+ {
+ bool ambig (false);
+
+ for (view_objects::const_iterator j (objs.begin ());
+ j != i;
+ ++j)
+ {
+ if (j->obj != c)
+ continue;
+
+ if (vo == 0)
+ {
+ vo = &*j;
+ continue;
+ }
+
+ // If it is the first ambiguous object, issue the
+ // error.
+ //
+ if (!ambig)
+ {
+ error (i->loc)
+ << "pointed-to object '" << class_name (*c) << "' is "
+ << "ambiguous" << endl;
+
+ info (i->loc)
+ << "candidates are:" << endl;
+
+ info (vo->loc)
+ << " '" << vo->name () << "'" << endl;
+
+ ambig = true;
+ }
+
+ info (j->loc)
+ << " '" << j->name () << "'" << endl;
+ }
+
+ if (ambig)
+ {
+ info (i->loc)
+ << "use the full join condition clause in db pragma "
+ << "object to resolve this ambiguity" << endl;
+
+ throw operation_failed ();
+ }
+
+ if (vo == 0)
+ {
+ error (i->loc)
+ << "pointed-to object '" << class_name (*c) << "' "
+ << "specified in the join condition has not been "
+ << "previously associated with this view" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ // Left and right-hand side table names.
+ //
+ qname lt;
+ {
+ using semantics::class_;
+
+ class_& o (*e.vo->obj);
+ string const& a (e.vo->alias);
+
+ if (class_* root = polymorphic (o))
+ {
+ // If the object is polymorphic, then figure out which of the
+ // bases this member comes from and use the corresponding
+ // table.
+ //
+ class_* c (
+ &static_cast<class_&> (
+ e.member_path.front ()->scope ()));
+
+ // If this member's class is not polymorphic (root uses reuse
+ // inheritance), then use the root table.
+ //
+ if (!polymorphic (*c))
+ c = root;
+
+ qname const& t (table_name (*c));
+
+ if (a.empty ())
+ lt = t;
+ else
+ lt = qname (a + "_" + t.uname ());
+ }
+ else
+ lt = a.empty () ? table_name (o) : qname (a);
+ }
+
+ qname rt;
+ {
+ qname t (table_name (*vo->obj));
+ string const& a (vo->alias);
+ rt = a.empty ()
+ ? t
+ : qname (polymorphic (*vo->obj) ? a + "_" + t.uname () : a);
+ }
+
+ // First join the container table if necessary.
+ //
+ data_member* im (inverse (m));
+
+ semantics::type* cont (container (im != 0 ? *im : m));
+
+ string ct; // Container table.
+ if (cont != 0)
+ {
+ if (im != 0)
+ {
+ // For now a direct member can only be directly in
+ // the object scope. If this changes, the inverse()
+ // function would have to return a member path instead
+ // of just a single member.
+ //
+ table_prefix tp (
+ context::schema (vo->obj->scope ()),
+ context::table_name_prefix (vo->obj->scope ()),
+ table_name (*vo->obj) + "_");
+ ct = table_qname (*im, tp);
+ }
+ else
+ ct = table_qname (*e.vo->obj, e.member_path);
+
+ l = "LEFT JOIN ";
+ l += ct;
+ l += " ON";
+ os << "r += " << strlit (l) << ";";
+
+ // If we are the pointed-to object, then we have to turn
+ // things around. This is necessary to have the proper
+ // JOIN order. There seems to be a pattern there but it
+ // is not yet intuitively clear what it means.
+ //
+ instance<object_columns_list> c_cols; // Container columns.
+ instance<object_columns_list> o_cols; // Object columns.
+
+ qname* ot; // Object table (either lt or rt).
+
+ if (im != 0)
+ {
+ if (&o == c)
+ {
+ // container.value = pointer.id
+ //
+ semantics::data_member& id (*id_member (*e.vo->obj));
+
+ c_cols->traverse (*im, utype (id), "value", "value");
+ o_cols->traverse (id);
+ ot = &lt;
+ }
+ else
+ {
+ // container.id = pointed-to.id
+ //
+ semantics::data_member& id (*id_member (*vo->obj));
+
+ c_cols->traverse (
+ *im, utype (id), "id", "object_id", vo->obj);
+ o_cols->traverse (id);
+ ot = &rt;
+ }
+ }
+ else
+ {
+ if (&o == c)
+ {
+ // container.id = pointer.id
+ //
+ semantics::data_member& id (*id_member (*e.vo->obj));
+
+ c_cols->traverse (
+ m, utype (id), "id", "object_id", e.vo->obj);
+ o_cols->traverse (id);
+ ot = &lt;
+ }
+ else
+ {
+ // container.value = pointed-to.id
+ //
+ semantics::data_member& id (*id_member (*vo->obj));
+
+ c_cols->traverse (m, utype (id), "value", "value");
+ o_cols->traverse (id);
+ ot = &rt;
+ }
+ }
+
+ for (object_columns_list::iterator b (c_cols->begin ()), i (b),
+ j (o_cols->begin ()); i != c_cols->end (); ++i, ++j)
+ {
+ l.clear ();
+
+ if (i != b)
+ l += "AND ";
+
+ l += ct;
+ l += '.';
+ l += quote_id (i->name);
+ l += '=';
+ l += quote_id (*ot);
+ l += '.';
+ l += quote_id (j->name);
+
+ os << "r += " << strlit (l) << ";";
+ }
+ }
+
+ l = "LEFT JOIN ";
+ l += table_qname (o);
+
+ if (!alias.empty ())
+ l += (need_alias_as ? " AS " : " ") + quote_id (alias);
+
+ l += " ON";
+ os << "r += " << strlit (l) << ";";
+
+ if (cont != 0)
+ {
+ instance<object_columns_list> c_cols; // Container columns.
+ instance<object_columns_list> o_cols; // Object columns.
+
+ qname* ot; // Object table (either lt or rt).
+
+ if (im != 0)
+ {
+ if (&o == c)
+ {
+ // container.id = pointed-to.id
+ //
+ semantics::data_member& id (*id_member (*vo->obj));
+
+ c_cols->traverse (*im, utype (id), "id", "object_id", vo->obj);
+ o_cols->traverse (id);
+ ot = &rt;
+ }
+ else
+ {
+ // container.value = pointer.id
+ //
+ semantics::data_member& id (*id_member (*e.vo->obj));
+
+ c_cols->traverse (*im, utype (id), "value", "value");
+ o_cols->traverse (id);
+ ot = &lt;
+ }
+ }
+ else
+ {
+ if (&o == c)
+ {
+ // container.value = pointed-to.id
+ //
+ semantics::data_member& id (*id_member (*vo->obj));
+
+ c_cols->traverse (m, utype (id), "value", "value");
+ o_cols->traverse (id);
+ ot = &rt;
+ }
+ else
+ {
+ // container.id = pointer.id
+ //
+ semantics::data_member& id (*id_member (*e.vo->obj));
+
+ c_cols->traverse (m, utype (id), "id", "object_id", e.vo->obj);
+ o_cols->traverse (id);
+ ot = &lt;
+ }
+ }
+
+ for (object_columns_list::iterator b (c_cols->begin ()), i (b),
+ j (o_cols->begin ()); i != c_cols->end (); ++i, ++j)
+ {
+ l.clear ();
+
+ if (i != b)
+ l += "AND ";
+
+ l += ct;
+ l += '.';
+ l += quote_id (i->name);
+ l += '=';
+ l += quote_id (*ot);
+ l += '.';
+ l += quote_id (j->name);
+
+ os << "r += " << strlit (l) << ";";
+ }
+ }
+ else
+ {
+ string col_prefix;
+
+ if (im == 0)
+ col_prefix = object_columns_base::column_prefix (e.member_path);
+
+ instance<object_columns_list> l_cols (col_prefix);
+ instance<object_columns_list> r_cols;
+
+ if (im != 0)
+ {
+ // our.id = pointed-to.pointer
+ //
+ l_cols->traverse (*id_member (*e.vo->obj));
+ r_cols->traverse (*im);
+ }
+ else
+ {
+ // our.pointer = pointed-to.id
+ //
+ l_cols->traverse (*e.member_path.back ());
+ r_cols->traverse (*id_member (*vo->obj));
+ }
+
+ for (object_columns_list::iterator b (l_cols->begin ()), i (b),
+ j (r_cols->begin ()); i != l_cols->end (); ++i, ++j)
+ {
+ l.clear ();
+
+ if (i != b)
+ l += "AND ";
+
+ l += quote_id (lt);
+ l += '.';
+ l += quote_id (i->name);
+ l += '=';
+ l += quote_id (rt);
+ l += '.';
+ l += quote_id (j->name);
+
+ os << "r += " << strlit (l) << ";";
+ }
+ }
+
+ if (poly_depth != 1)
+ {
+ instance<polymorphic_object_joins> j (
+ o, poly_depth, i->alias, "r += ", ";");
+ j->traverse (o);
+ }
+
+ os << endl;
+
+ } // End JOIN-generating for-loop.
+
+ // Generate the query condition.
+ //
+ if (!vq.literal.empty () || !vq.expr.empty ())
+ {
+ os << "query_base_type c (" << endl;
+
+ bool ph (false);
+
+ if (!vq.literal.empty ())
+ {
+ // See if we have the '(?)' placeholder.
+ //
+ // @@ Ideally we would need to make sure we don't match
+ // this inside strings and quoted identifier. So the
+ // proper way to handle this would be to tokenize the
+ // statement using sql_lexer, once it is complete enough.
+ //
+ string::size_type p (vq.literal.find ("(?)"));
+
+ if (p != string::npos)
+ {
+ ph = true;
+ os << strlit (string (vq.literal, 0, p + 1))<< " +" << endl
+ << "(q.empty () ? query_base_type::true_expr : q) +" << endl
+ << strlit (string (vq.literal, p + 2));
+ }
+ else
+ os << strlit (vq.literal);
+
+ os << ");";
+ }
+ else
+ {
+ // Output the pragma location for easier error tracking.
+ //
+ os << "// From " <<
+ location_file (vq.loc).leaf () << ":" <<
+ location_line (vq.loc) << ":" <<
+ location_column (vq.loc) << endl
+ << translate_expression (
+ c, vq.expr, vq.scope, vq.loc, "query", &ph).value;
+
+ os << ");";
+
+ // Optimize the query if it had a placeholder. This gets
+ // rid of useless clauses like WHERE TRUE.
+ //
+ if (ph)
+ os << "c.optimize ();";
+ }
+
+ if (!ph)
+ os << "c += q;";
+
+ os << "r += c.clause_prefix ();"
+ << "r += c;"
+ << endl;
+ }
+ else
+ {
+ os << "r += q.clause_prefix ();"
+ << "r += q;"
+ << endl;
+ }
+ }
+
+ os << "return r;"
+ << "}";
+ }
+
+ // query ()
+ //
+ os << "result< " << traits << "::view_type >" << endl
+ << traits << "::" << endl
+ << "query (database&, const query_base_type& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using odb::details::shared;"
+ << "using odb::details::shared_ptr;"
+ << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection ());"
+ << endl
+ << "view_statements< view_type >& sts (" << endl
+ << "conn.statement_cache ().find_view<view_type> ());"
+ << endl
+ << "image_type& im (sts.image ());"
+ << "binding& imb (sts.image_binding ());"
+ << endl
+ << "if (im.version != sts.image_version () || imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im);"
+ << "sts.image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+
+ if (vq.kind == view_query::runtime)
+ os << "const query_base_type& qs (q);";
+ else
+ os << "const query_base_type& qs (query_statement (q));";
+
+ os << "shared_ptr<select_statement> st (" << endl
+ << "new (shared) select_statement (" << endl;
+
+ view_query_statement_ctor_args (c);
+
+ os << "));" << endl
+ << "st->execute ();";
+
+ post_query_ (c);
+
+ os << endl
+ << "shared_ptr< odb::view_result_impl<view_type> > r (" << endl
+ << "new (shared) " << db << "::view_result_impl<view_type> (" << endl
+ << "qs, st, sts));"
+ << endl
+ << "return result<view_type> (r);"
+ << "}";
+}
+
namespace relational
{
namespace source
diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx
index e6ca961..42ff235 100644
--- a/odb/relational/source.hxx
+++ b/odb/relational/source.hxx
@@ -26,15 +26,17 @@ namespace relational
struct statement_column
{
statement_column (): member (0) {}
- statement_column (std::string const& c,
+ statement_column (std::string const& tbl,
+ std::string const& col,
std::string const& t,
semantics::data_member& m,
std::string const& kp = "")
- : column (c), type (t), member (&m), key_prefix (kp)
+ : table (tbl), column (col), type (t), member (&m), key_prefix (kp)
{
}
- std::string column; // Column name.
+ std::string table; // Schema-qualifed and quoted table name.
+ std::string column; // Table-qualifed and quoted column expr.
std::string type; // Column SQL type.
semantics::data_member* member;
std::string key_prefix;
@@ -73,20 +75,55 @@ namespace relational
object_columns (statement_kind sk,
statement_columns& sc,
query_parameters* param = 0)
- : sk_ (sk), sc_ (sc), param_ (param)
+ : object_columns_base (true, "", true),
+ sk_ (sk), sc_ (sc), param_ (param), depth_ (1)
{
}
object_columns (std::string const& table_qname,
statement_kind sk,
- statement_columns& sc)
- : sk_ (sk), sc_ (sc), param_ (0), table_name_ (table_qname)
+ statement_columns& sc,
+ size_t depth = 1)
+ : object_columns_base (true, "", true),
+ sk_ (sk),
+ sc_ (sc),
+ param_ (0),
+ table_name_ (table_qname),
+ depth_ (depth)
+ {
+ }
+
+ virtual void
+ traverse_object (semantics::class_& c)
{
+ // If we are generating a select statement and this is a derived
+ // type in a polymorphic hierarchy, then we need to include base
+ // columns, but do it in reverse order as well as switch the table
+ // name (base columns come from different tables).
+ //
+ semantics::class_* poly_root (polymorphic (c));
+ if (poly_root != 0 && poly_root != &c)
+ {
+ names (c);
+
+ if (sk_ == statement_select && --depth_ != 0)
+ {
+ table_name_ = table_qname (polymorphic_base (c));
+ inherits (c);
+ }
+ }
+ else
+ object_columns_base::traverse_object (c);
}
virtual void
traverse_pointer (semantics::data_member& m, semantics::class_& c)
{
+ // Ignore polymorphic id references for select statements.
+ //
+ if (sk_ == statement_select && m.count ("polymorphic-ref"))
+ return;
+
semantics::data_member* im (inverse (m, key_prefix_));
// Ignore certain columns depending on what kind statement we are
@@ -215,7 +252,9 @@ namespace relational
r += param_->next ();
}
- sc_.push_back (statement_column (r, column_type (), m, key_prefix_));
+ sc_.push_back (
+ statement_column (
+ table, r, column_type (), m, key_prefix_));
}
protected:
@@ -223,6 +262,7 @@ namespace relational
statement_columns& sc_;
query_parameters* param_;
string table_name_;
+ size_t depth_;
};
struct view_columns: object_columns_base, virtual context
@@ -306,6 +346,7 @@ namespace relational
virtual bool
traverse_column (semantics::data_member& m, string const& name, bool)
{
+ string tbl;
string col;
// If we are inside a composite value, use the standard
@@ -315,7 +356,8 @@ namespace relational
{
if (!table_prefix_.empty ())
{
- col += quote_id (table_prefix_);
+ tbl = quote_id (table_prefix_);
+ col += tbl;
col += '.';
}
@@ -331,7 +373,8 @@ namespace relational
{
if (!tc.table.empty ())
{
- col += quote_id (tc.table);
+ tbl = quote_id (tc.table);
+ col += tbl;
col += '.';
}
@@ -357,7 +400,8 @@ namespace relational
}
case column_expr_part::reference:
{
- col += quote_id (i->table);
+ tbl = quote_id (i->table);
+ col += tbl;
col += '.';
col += quote_id (column_name (i->member_path));
break;
@@ -378,7 +422,7 @@ namespace relational
throw operation_failed ();
}
- column (m, col);
+ column (m, tbl, col);
return true;
}
@@ -386,9 +430,11 @@ namespace relational
// expression.
//
virtual void
- column (semantics::data_member& m, string const& column)
+ column (semantics::data_member& m,
+ string const& table,
+ string const& column)
{
- sc_.push_back (statement_column (column, column_type (), m));
+ sc_.push_back (statement_column (table, column, column_type (), m));
}
protected:
@@ -397,50 +443,132 @@ namespace relational
qname table_prefix_; // Table corresponding to column_prefix_;
};
+ struct polymorphic_object_joins: object_columns_base, virtual context
+ {
+ typedef polymorphic_object_joins base;
+
+ polymorphic_object_joins (semantics::class_& obj,
+ size_t depth,
+ string const& alias = "",
+ string const prefix = "",
+ string const& suffix = "\n")
+ : object_columns_base (true, "", true),
+ obj_ (obj),
+ depth_ (depth),
+ alias_ (alias),
+ prefix_ (prefix),
+ suffix_ (suffix)
+ {
+ }
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ if (&c == &obj_)
+ {
+ // Get the table and id columns.
+ //
+ table_ = alias_.empty ()
+ ? table_qname (c)
+ : quote_id (alias_ + "_" + table_name (c).uname ());
+
+ cols_->traverse (*id_member (c));
+
+ if (--depth_ != 0)
+ inherits (c);
+ return;
+ }
+
+ semantics::class_* poly_root ();
+ std::ostringstream cond;
+
+ qname table (table_name (c));
+ string alias (alias_.empty ()
+ ? quote_id (table)
+ : quote_id (alias_ + "_" + table.uname ()));
+
+ for (object_columns_list::iterator b (cols_->begin ()), i (b);
+ i != cols_->end ();
+ ++i)
+ {
+ if (i != b)
+ cond << " AND ";
+
+ string qn (quote_id (i->name));
+ cond << alias << '.' << qn << '=' << table_ << '.' << qn;
+ }
+
+ string line (" LEFT JOIN " + quote_id (table));
+
+ if (!alias_.empty ())
+ line += (need_alias_as ? " AS " : " ") + alias;
+
+ line += " ON " + cond.str ();
+
+ os << prefix_ << strlit (line) << suffix_;
+
+ if (&c != polymorphic (c) && --depth_ != 0)
+ inherits (c);
+ }
+
+ private:
+ semantics::class_& obj_;
+ size_t depth_;
+ string alias_;
+ string prefix_;
+ string suffix_;
+ string table_;
+ instance<object_columns_list> cols_;
+ };
+
struct object_joins: object_columns_base, virtual context
{
typedef object_joins base;
//@@ context::{cur,top}_object; might have to be created every time.
//
- object_joins (semantics::class_& scope, bool query)
- : query_ (query),
+ object_joins (semantics::class_& scope, bool query, size_t depth = 1)
+ : object_columns_base (true, "", true),
+ query_ (query),
+ depth_ (depth),
table_ (table_qname (scope)),
id_ (*id_member (scope))
{
id_cols_->traverse (id_);
}
- size_t
- count () const
- {
- return joins_.size ();
- }
-
- void
- write ()
+ virtual void
+ traverse_object (semantics::class_& c)
{
- for (joins::iterator i (joins_.begin ()); i != joins_.end (); ++i)
+ // If this is a derived type in a polymorphic hierarchy, then we
+ // need to include base joins, but do it in reverse order as well
+ // as switch the table name (base columns come from different
+ // tables).
+ //
+ semantics::class_* poly_root (polymorphic (c));
+ if (poly_root != 0 && poly_root != &c)
{
- if (i->table.empty ())
- continue;
-
- string line (" LEFT JOIN ");
- line += i->table;
-
- if (!i->alias.empty ())
- line += (need_alias_as ? " AS " : " ") + i->alias;
+ names (c);
- line += " ON ";
- line += i->cond;
-
- os << strlit (line) << endl;
+ if (query_ || --depth_ != 0)
+ {
+ table_ = table_qname (polymorphic_base (c));
+ inherits (c);
+ }
}
+ else
+ object_columns_base::traverse_object (c);
}
virtual void
traverse_pointer (semantics::data_member& m, semantics::class_& c)
{
+ // Ignore polymorphic id references; they are joined by
+ // polymorphic_object_joins in a special way.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
string t, a, dt, da;
std::ostringstream cond, dcond; // @@ diversion?
@@ -466,8 +594,13 @@ namespace relational
alias = column_prefix_ + p;
}
else
- alias = column_prefix_ +
- column_name (m, key_prefix_, default_name_);
+ alias = column_prefix_ + column_name (m, key_prefix_, default_name_);
+
+ semantics::class_* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ bool obj_joined (false);
if (semantics::data_member* im = inverse (m, key_prefix_))
{
@@ -503,8 +636,10 @@ namespace relational
//
if (query_)
{
- dt = quote_id (ct);
- da = quote_id (alias);
+ qname const& table (table_name (c));
+
+ dt = quote_id (table);
+ da = quote_id (poly ? alias + "_" + table.uname () : alias);
semantics::data_member& id (*id_member (c));
@@ -522,12 +657,16 @@ namespace relational
dcond << da << '.' << quote_id (j->name) << '=' <<
t << '.' << quote_id (i->name);
}
+
+ obj_joined = true;
}
}
else
{
- t = table_qname (c);
- a = quote_id (alias);
+ qname const& table (table_name (c));
+
+ t = quote_id (table);
+ a = quote_id (poly ? alias + "_" + table.uname () : alias);
instance<object_columns_list> id_cols;
id_cols->traverse (*im);
@@ -542,6 +681,8 @@ namespace relational
cond << a << '.' << quote_id (i->name) << '=' <<
table_ << '.' << quote_id (j->name);
}
+
+ obj_joined = true;
}
}
else if (query_)
@@ -549,8 +690,10 @@ namespace relational
// We need the join to be able to use the referenced object
// in the WHERE clause.
//
- t = table_qname (c);
- a = quote_id (alias);
+ qname const& table (table_name (c));
+
+ t = quote_id (table);
+ a = quote_id (poly ? alias + "_" + table.uname () : alias);
instance<object_columns_list> oid_cols (column_prefix_);
oid_cols->traverse (m);
@@ -568,14 +711,22 @@ namespace relational
cond << a << '.' << quote_id (i->name) << '=' <<
table_ << '.' << quote_id (j->name);
}
+
+ obj_joined = true;
}
if (!t.empty ())
{
- joins_.push_back (join ());
- joins_.back ().table = t;
- joins_.back ().alias = a;
- joins_.back ().cond = cond.str ();
+ string line (" LEFT JOIN ");
+ line += t;
+
+ if (!a.empty ())
+ line += (need_alias_as ? " AS " : " ") + a;
+
+ line += " ON ";
+ line += cond.str ();
+
+ os << strlit (line) << endl;
}
// Add dependent join (i.e., an object table join via the
@@ -583,29 +734,35 @@ namespace relational
//
if (!dt.empty ())
{
- joins_.push_back (join ());
- joins_.back ().table = dt;
- joins_.back ().alias = da;
- joins_.back ().cond = dcond.str ();
+ string line (" LEFT JOIN ");
+ line += dt;
+
+ if (!da.empty ())
+ line += (need_alias_as ? " AS " : " ") + da;
+
+ line += " ON ";
+ line += dcond.str ();
+
+ os << strlit (line) << endl;
+ }
+
+ // If we joined the object and it is a derived type in a
+ // polymorphic hierarchy, then join its bases as well.
+ //
+ if (obj_joined && poly_derived)
+ {
+ size_t depth (polymorphic_depth (c));
+ instance<polymorphic_object_joins> t (c, depth, alias);
+ t->traverse (c);
}
}
private:
bool query_;
+ size_t depth_;
string table_;
semantics::data_member& id_;
instance<object_columns_list> id_cols_;
-
- struct join
- {
- string table;
- string alias;
- string cond;
- };
-
- typedef std::vector<join> joins;
-
- joins joins_;
};
//
@@ -637,6 +794,146 @@ namespace relational
string arg_override_;
};
+ template <typename T>
+ struct bind_member_impl: bind_member, virtual member_base_impl<T>
+ {
+ typedef bind_member_impl base_impl;
+
+ bind_member_impl (base const& x)
+ : base (x)
+ {
+ }
+
+ typedef typename member_base_impl<T>::member_info member_info;
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ if (container (mi))
+ return false;
+
+ // Ignore polymorphic id references; they are bound in a special
+ // way.
+ //
+ if (mi.ptr != 0 && mi.m.count ("polymorphic-ref"))
+ return false;
+
+ std::ostringstream ostr;
+ ostr << "b[n]";
+ b = ostr.str ();
+
+ arg = arg_override_.empty () ? string ("i") : arg_override_;
+
+ if (var_override_.empty ())
+ {
+ os << "// " << mi.m.name () << endl
+ << "//" << endl;
+
+ if (!insert_send_auto_id && id (mi.m) && auto_ (mi.m))
+ os << "if (sk != statement_insert && sk != statement_update)"
+ << "{";
+ else if (inverse (mi.m, key_prefix_) || version (mi.m))
+ os << "if (sk == statement_select)"
+ << "{";
+ // If the whole class is readonly, then we will never be
+ // called with sk == statement_update.
+ //
+ else if (!readonly (*context::top_object))
+ {
+ semantics::class_* c;
+
+ if (id (mi.m) ||
+ readonly (mi.m) ||
+ ((c = composite (mi.t)) && readonly (*c)))
+ os << "if (sk != statement_update)"
+ << "{";
+ }
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ if (var_override_.empty ())
+ {
+ semantics::class_* c;
+
+ if ((c = composite (mi.t)))
+ {
+ bool ro (readonly (*c));
+ column_count_type const& cc (column_count (*c));
+
+ os << "n += " << cc.total << "UL";
+
+ // select = total
+ // insert = total - inverse
+ // update = total - inverse - readonly
+ //
+ if (cc.inverse != 0 || (!ro && cc.readonly != 0))
+ {
+ os << " - (" << endl
+ << "sk == statement_select ? 0 : ";
+
+ if (cc.inverse != 0)
+ os << cc.inverse << "UL";
+
+ if (!ro && cc.readonly != 0)
+ {
+ if (cc.inverse != 0)
+ os << " + ";
+
+ os << "(" << endl
+ << "sk == statement_insert ? 0 : " <<
+ cc.readonly << "UL)";
+ }
+
+ os << ")";
+ }
+
+ os << ";";
+ }
+ else
+ os << "n++;";
+
+ bool block (false);
+
+ // The same logic as in pre().
+ //
+ if (!insert_send_auto_id && id (mi.m) && auto_ (mi.m))
+ block = true;
+ else if (inverse (mi.m, key_prefix_) || version (mi.m))
+ block = true;
+ else if (!readonly (*context::top_object))
+ {
+ semantics::class_* c;
+
+ if (id (mi.m) ||
+ readonly (mi.m) ||
+ ((c = composite (mi.t)) && readonly (*c)))
+ block = true;
+ }
+
+ if (block)
+ os << "}";
+ else
+ os << endl;
+ }
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ os << "composite_value_traits< " << mi.fq_type () <<
+ " >::bind (b + n, " << arg << "." << mi.var << "value, sk);";
+ }
+
+ protected:
+ string b;
+ string arg;
+ };
+
struct bind_base: traversal::class_, virtual context
{
typedef bind_base base;
@@ -721,8 +1018,8 @@ namespace relational
{
typedef grow_member base;
- grow_member (size_t& index)
- : member_base (string (), 0, string (), string ()), index_ (index)
+ grow_member (size_t& index, string const& var = string ())
+ : member_base (var, 0, string (), string ()), index_ (index)
{
}
@@ -827,6 +1124,12 @@ namespace relational
if (container (mi) || inverse (mi.m, key_prefix_))
return false;
+ // Ignore polymorphic id references; they are initialized in a
+ // special way.
+ //
+ if (mi.ptr != 0 && mi.m.count ("polymorphic-ref"))
+ return false;
+
if (!member_override_.empty ())
member = member_override_;
else
@@ -844,7 +1147,6 @@ namespace relational
return false;
string const& name (mi.m.name ());
- member = "o." + name;
os << "// " << name << endl
<< "//" << endl;
@@ -861,8 +1163,19 @@ namespace relational
((c = composite (mi.t)) && readonly (*c))) // Can't be id.
os << "if (sk == statement_insert)";
}
+
+ if (discriminator (mi.m))
+ member = "di.discriminator";
+ else
+ member = "o." + name;
}
+ os << "{";
+
+ if (discriminator (mi.m))
+ os << "const info_type& di (map->find (typeid (o)));"
+ << endl;
+
bool comp (composite (mi.t));
// If this is a wrapped composite value, then we need to "unwrap"
@@ -888,15 +1201,14 @@ namespace relational
// Handle NULL pointers and extract the id.
//
- os << "{"
- << "typedef object_traits< " << class_fq_name (*mi.ptr) <<
+ os << "typedef object_traits< " << class_fq_name (*mi.ptr) <<
" > obj_traits;";
if (weak_pointer (pt))
{
- os << "typedef pointer_traits< " << mi.ptr_fq_type () <<
+ os << "typedef odb::pointer_traits< " << mi.ptr_fq_type () <<
" > wptr_traits;"
- << "typedef pointer_traits< wptr_traits::" <<
+ << "typedef odb::pointer_traits< wptr_traits::" <<
"strong_pointer_type > ptr_traits;"
<< endl
<< "wptr_traits::strong_pointer_type sp (" <<
@@ -905,7 +1217,7 @@ namespace relational
member = "sp";
}
else
- os << "typedef pointer_traits< " << mi.ptr_fq_type () <<
+ os << "typedef odb::pointer_traits< " << mi.ptr_fq_type () <<
" > ptr_traits;"
<< endl;
@@ -926,17 +1238,11 @@ namespace relational
member = "id";
}
else if (comp)
- {
type = mi.fq_type ();
-
- os << "{";
- }
else
{
type = mi.fq_type ();
-
- os << "{"
- << "bool is_null;";
+ os << "bool is_null;";
}
if (comp)
@@ -1045,9 +1351,12 @@ namespace relational
{
typedef init_value_member base;
- init_value_member (string const& member = string ())
- : member_base (string (), 0, string (), string ()),
- member_override_ (member)
+ init_value_member (string const& member = string (),
+ string const& var = string (),
+ bool ignore_implicit_discriminator = true)
+ : member_base (var, 0, string (), string ()),
+ member_override_ (member),
+ ignore_implicit_discriminator_ (ignore_implicit_discriminator)
{
}
@@ -1057,12 +1366,14 @@ namespace relational
string const& fq_type,
string const& key_prefix)
: member_base (var, &t, fq_type, key_prefix),
- member_override_ (member)
+ member_override_ (member),
+ ignore_implicit_discriminator_ (true)
{
}
protected:
string member_override_;
+ bool ignore_implicit_discriminator_;
};
template <typename T>
@@ -1090,6 +1401,17 @@ namespace relational
if (container (mi))
return false;
+ // Ignore polymorphic id references; they are initialized in a
+ // special way.
+ //
+ if (mi.ptr != 0 && mi.m.count ("polymorphic-ref"))
+ return false;
+
+ // Ignore implicit discriminators.
+ //
+ if (ignore_implicit_discriminator_ && discriminator (mi.m))
+ return false;
+
if (!member_override_.empty ())
member = member_override_;
else
@@ -1130,7 +1452,7 @@ namespace relational
os << "{"
<< "typedef object_traits< " << class_fq_name (*mi.ptr) <<
" > obj_traits;"
- << "typedef pointer_traits< " << mi.ptr_fq_type () <<
+ << "typedef odb::pointer_traits< " << mi.ptr_fq_type () <<
" > ptr_traits;"
<< endl;
@@ -1213,8 +1535,8 @@ namespace relational
if (weak_pointer (pt))
{
os << endl
- << "if (pointer_traits< ptr_traits::strong_pointer_type >" <<
- "::null_ptr (" << endl
+ << "if (odb::pointer_traits<" <<
+ "ptr_traits::strong_pointer_type>::null_ptr (" << endl
<< "ptr_traits::lock (" << member << ")))" << endl
<< "throw session_required ();";
}
@@ -1339,20 +1661,20 @@ namespace relational
using semantics::type;
// Figure out if this member is from a base object or composite
- // value and whether it is abstract.
+ // value and if it's from an object, whether it is reuse-abstract.
//
- bool base, abst;
+ bool base, reuse_abst;
if (object (c_))
{
base = cur_object != &c_ ||
!object (dynamic_cast<type&> (m.scope ()));
- abst = abstract (c_);
+ reuse_abst = abstract (c_) && !polymorphic (c_);
}
else
{
- base = false; // We don't go into bases.
- abst = true; // Always abstract.
+ base = false; // We don't go into bases.
+ reuse_abst = true; // Always abstract.
}
container_kind_type ck (container_kind (t));
@@ -1425,7 +1747,7 @@ namespace relational
//
// Statements.
//
- if (!abst)
+ if (!reuse_abst)
{
semantics::type& idt (container_idt (m));
@@ -1434,8 +1756,8 @@ namespace relational
// select_all_statement
//
- os << "const char " << scope <<
- "::select_all_statement[] =" << endl;
+ os << "const char " << scope << "::" << endl
+ << "select_all_statement[] =" << endl;
if (inverse)
{
@@ -1472,6 +1794,7 @@ namespace relational
//
sc.push_back (
statement_column (
+ inv_table,
inv_table + "." + quote_id (i->name),
i->type,
*i->member,
@@ -1492,6 +1815,7 @@ namespace relational
{
sc.push_back (
statement_column (
+ inv_table,
inv_table + "." + quote_id (i->name),
i->type,
*i->member));
@@ -1587,8 +1911,8 @@ namespace relational
// insert_one_statement
//
- os << "const char " << scope <<
- "::insert_one_statement[] =" << endl;
+ os << "const char " << scope << "::" << endl
+ << "insert_one_statement[] =" << endl;
if (inverse)
os << strlit ("") << ";"
@@ -1652,8 +1976,8 @@ namespace relational
// delete_all_statement
//
- os << "const char " << scope <<
- "::delete_all_statement[] =" << endl;
+ os << "const char " << scope << "::" << endl
+ << "delete_all_statement[] =" << endl;
if (inverse)
os << strlit ("") << ";"
@@ -2683,12 +3007,19 @@ namespace relational
class_ ()
: grow_base_ (index_),
grow_member_ (index_),
+ grow_version_member_ (index_, "version_"),
+ grow_discriminator_member_ (index_, "discriminator_"),
bind_id_member_ ("id_"),
bind_version_member_ ("version_"),
+ bind_discriminator_member_ ("discriminator_"),
init_id_image_member_ ("id_", "id"),
init_version_image_member_ ("version_", "(*v)"),
init_id_value_member_ ("id"),
- init_version_value_member_ ("v")
+ init_version_value_member_ ("v"),
+ init_named_version_value_member_ ("v", "version_"),
+ init_discriminator_value_member_ ("d", "", false),
+ init_named_discriminator_value_member_ (
+ "d", "discriminator_", false)
{
init ();
}
@@ -2698,12 +3029,19 @@ namespace relational
context (),
grow_base_ (index_),
grow_member_ (index_),
+ grow_version_member_ (index_, "version_"),
+ grow_discriminator_member_ (index_, "discriminator_"),
bind_id_member_ ("id_"),
bind_version_member_ ("version_"),
+ bind_discriminator_member_ ("discriminator_"),
init_id_image_member_ ("id_", "id"),
init_version_image_member_ ("version_", "(*v)"),
init_id_value_member_ ("id"),
- init_version_value_member_ ("v")
+ init_version_value_member_ ("v"),
+ init_named_version_value_member_ ("v", "version_"),
+ init_discriminator_value_member_ ("d", "", false),
+ init_named_discriminator_value_member_ (
+ "d", "discriminator_", false)
{
init ();
}
@@ -2819,1203 +3157,7 @@ namespace relational
}
virtual void
- traverse_object (type& c)
- {
- bool abstract (context::abstract (c));
- string const& type (class_fq_name (c));
- string traits ("access::object_traits< " + type + " >");
-
- bool has_ptr (has_a (c, test_pointer));
-
- semantics::data_member* id (id_member (c));
- bool auto_id (id ? id->count ("auto") : false);
- bool base_id (id ? &id->scope () != &c : false); // Comes from base.
-
- semantics::data_member* optimistic (context::optimistic (c));
-
- bool grow (false);
- bool grow_id (false);
-
- if (generate_grow)
- {
- grow = context::grow (c);
- grow_id = (id ? context::grow (*id) : false) ||
- (optimistic ? context::grow (*optimistic) : false);
- }
-
- column_count_type const& cc (column_count (c));
-
- os << "// " << class_name (c) << endl
- << "//" << endl
- << endl;
-
- object_extra (c);
-
- //
- // Query.
- //
-
- if (options.generate_query ())
- {
- // query_columns_base
- //
- if (has_ptr)
- {
- instance<query_columns_base> t (c);
- t->traverse (c);
- }
- }
-
- //
- // Containers (abstract and concrete).
- //
- bool containers (has_a (c, test_container));
- bool straight_containers (false);
- bool straight_readwrite_containers (false);
-
- if (containers)
- {
- containers = true;
- size_t scn (has_a (c, test_straight_container));
-
- if (scn != 0)
- {
- straight_containers = true;
-
- // Inverse containers cannot be marked readonly.
- //
- straight_readwrite_containers =
- scn > has_a (c, test_readonly_container);
- }
- }
-
- if (containers)
- {
- instance<container_traits> t (c);
- t->traverse (c);
- }
-
- //
- // Functions (abstract and concrete).
- //
-
- // id (image_type)
- //
- if (id != 0 && options.generate_query () && !base_id)
- {
- os << traits << "::id_type" << endl
- << traits << "::" << endl
- << "id (const image_type& i)"
- << "{"
- << db << "::database* db (0);"
- << "ODB_POTENTIALLY_UNUSED (db);"
- << endl
- << "id_type id;";
- init_id_value_member_->traverse (*id);
- os << "return id;"
- << "}";
- }
-
- if (id != 0 && optimistic != 0 && !base_id)
- {
- os << traits << "::version_type" << endl
- << traits << "::" << endl
- << "version (const image_type& i)"
- << "{"
- << "version_type v;";
- init_version_value_member_->traverse (*optimistic);
- os << "return v;"
- << "}";
- }
-
- // grow ()
- //
- if (generate_grow)
- {
- os << "bool " << traits << "::" << endl
- << "grow (image_type& i, " << truncated_vector << " t)"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (i);"
- << "ODB_POTENTIALLY_UNUSED (t);"
- << endl
- << "bool grew (false);"
- << endl;
-
- index_ = 0;
- inherits (c, grow_base_inherits_);
- names (c, grow_member_names_);
-
- os << "return grew;"
- << "}";
- }
-
- // bind (image_type)
- //
- os << "void " << traits << "::" << endl
- << "bind (" << bind_vector << " b, image_type& i, " <<
- db << "::statement_kind sk)"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (sk);"
- << endl
- << "using namespace " << db << ";"
- << endl;
-
- if (readonly (c))
- os << "assert (sk != statement_update);"
- << endl;
-
- os << "std::size_t n (0);"
- << endl;
-
- inherits (c, bind_base_inherits_);
- names (c, bind_member_names_);
-
- os << "}";
-
- // bind (id_image_type)
- //
- if (id != 0 && !base_id)
- {
- os << "void " << traits << "::" << endl
- << "bind (" << bind_vector << " b, id_image_type& i)"
- << "{"
- << "std::size_t n (0);";
-
- if (composite_wrapper (utype (*id)))
- os << db << "::statement_kind sk (" << db << "::statement_select);";
-
- bind_id_member_->traverse (*id);
-
- if (optimistic != 0)
- {
- os << "n += " << column_count (c).id << ";"
- << endl;
-
- bind_version_member_->traverse (*optimistic);
- }
-
- os << "}";
- }
-
- // init (image, object)
- //
- os << "bool " << traits << "::" << endl
- << "init (image_type& i, const object_type& o, " <<
- db << "::statement_kind sk)"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (i);"
- << "ODB_POTENTIALLY_UNUSED (o);"
- << "ODB_POTENTIALLY_UNUSED (sk);"
- << endl
- << "using namespace " << db << ";"
- << endl;
-
- if (readonly (c))
- os << "assert (sk != statement_update);"
- << endl;
-
- init_image_pre (c);
-
- os << "bool grew (false);"
- << endl;
-
- inherits (c, init_image_base_inherits_);
- names (c, init_image_member_names_);
-
- os << "return grew;"
- << "}";
-
- // init (object, image)
- //
- os << "void " << traits << "::" << endl
- << "init (object_type& o, const image_type& i, database* db)"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (o);"
- << "ODB_POTENTIALLY_UNUSED (i);"
- << "ODB_POTENTIALLY_UNUSED (db);"
- << endl;
-
- inherits (c, init_value_base_inherits_);
- names (c, init_value_member_names_);
-
- os << "}";
-
- // init (id_image, id)
- //
- if (id != 0 && !base_id)
- {
- os << "void " << traits << "::" << endl
- << "init (id_image_type& i, const id_type& id" <<
- (optimistic != 0 ? ", const version_type* v" : "") << ")"
- << "{";
-
- if (grow_id)
- os << "bool grew (false);";
-
- if (composite_wrapper (utype (*id)))
- os << db << "::statement_kind sk (" << db << "::statement_select);";
-
- init_id_image_member_->traverse (*id);
-
- if (optimistic != 0)
- {
- // Here we rely on the fact that init_image_member
- // always wraps the statements in a block.
- //
- os << "if (v != 0)";
- init_version_image_member_->traverse (*optimistic);
- }
-
- if (grow_id)
- os << "if (grew)" << endl
- << "i.version++;";
-
- os << "}";
- }
-
- //
- // The rest only applies to concrete objects.
- //
- if (abstract)
- return;
-
- //
- // Containers (concrete).
- //
-
- // Statement cache (definition).
- //
- if (id != 0)
- {
- os << "struct " << traits << "::container_statement_cache_type"
- << "{";
-
- instance<container_cache_members> cm;
- cm->traverse (c);
-
- os << (containers ? "\n" : "")
- << "container_statement_cache_type (" << db << "::connection&" <<
- (containers ? " c" : "") << ")";
-
- instance<container_cache_init_members> im;
- im->traverse (c);
-
- os << "{"
- << "}"
- << "};";
- }
-
- //
- // Statements.
- //
-
- string const& table (table_qname (c));
-
- // persist_statement
- //
- {
- statement_columns sc;
- {
- statement_kind sk (statement_insert); // Imperfect forwarding.
- instance<object_columns> ct (sk, sc);
- ct->traverse (c);
- process_statement_columns (sc, statement_insert);
- }
-
- bool dv (sc.empty ()); // The DEFAULT VALUES syntax.
-
- os << "const char " << traits << "::persist_statement[] " <<
- "=" << endl
- << strlit ("INSERT INTO " + table_qname(c) +
- (dv ? "" : " (")) << endl;
-
- for (statement_columns::const_iterator i (sc.begin ()),
- e (sc.end ()); i != e;)
- {
- string const& c (i->column);
- os << strlit (c + (++i != e ? "," : ")")) << endl;
- }
-
- instance<query_parameters> qp;
-
- persist_statement_extra (c, *qp, persist_after_columns);
-
- if (!dv)
- {
- string values;
- instance<persist_statement_params> pt (values, *qp);
- pt->traverse (c);
- os << strlit (" VALUES (" + values + ")");
- }
- else
- os << strlit (" DEFAULT VALUES");
-
- persist_statement_extra (c, *qp, persist_after_values);
-
- os << ";"
- << endl;
- }
-
- if (id != 0)
- {
- instance<object_columns_list> id_cols;
- id_cols->traverse (*id);
-
- // find_statement
- //
- {
- statement_columns sc;
- {
- statement_kind sk (statement_select); // Imperfect forwarding.
- instance<object_columns> t (table, sk, sc);
- t->traverse (c);
- process_statement_columns (sc, statement_select);
- }
-
- os << "const char " << traits << "::find_statement[] =" << endl
- << strlit ("SELECT ") << endl;
-
- for (statement_columns::const_iterator i (sc.begin ()),
- e (sc.end ()); i != e;)
- {
- string const& c (i->column);
- os << strlit (c + (++i != e ? "," : "")) << endl;
- }
-
- os << strlit (" FROM " + table) << endl;
-
- bool f (false); // @@ (im)perfect forwarding
- instance<object_joins> j (c, f); // @@ (im)perfect forwarding
- j->traverse (c);
- j->write ();
-
- instance<query_parameters> qp;
- for (object_columns_list::iterator b (id_cols->begin ()), i (b);
- i != id_cols->end (); ++i)
- {
- if (i != b)
- os << endl;
-
- os << strlit ((i == b ? " WHERE " : " AND ") + table + "." +
- quote_id (i->name) + "=" + qp->next ());
- }
-
- os << ";"
- << endl;
- }
-
- // update_statement
- //
- if (cc.total != cc.id + cc.inverse + cc.readonly)
- {
- instance<query_parameters> qp;
-
- statement_columns sc;
- {
- query_parameters* p (qp.get ()); // Imperfect forwarding.
- statement_kind sk (statement_update); // Imperfect forwarding.
- instance<object_columns> t (sk, sc, p);
- t->traverse (c);
- process_statement_columns (sc, statement_update);
- }
-
- os << "const char " << traits << "::update_statement[] " <<
- "=" << endl
- << strlit ("UPDATE " + table + " SET ") << endl;
-
- for (statement_columns::const_iterator i (sc.begin ()),
- e (sc.end ()); i != e;)
- {
- string const& c (i->column);
- os << strlit (c + (++i != e ? "," : "")) << endl;
- }
-
- for (object_columns_list::iterator b (id_cols->begin ()), i (b);
- i != id_cols->end (); ++i)
- {
- if (i != b)
- os << endl;
-
- os << strlit ((i == b ? " WHERE " : " AND ") +
- quote_id (i->name) + "=" + qp->next ());
- }
-
- if (optimistic != 0)
- os << endl
- << strlit (" AND " + column_qname (*optimistic) +
- "=" + qp->next ());
-
- os << ";"
- << endl;
- }
-
- // erase_statement
- //
- {
- instance<query_parameters> qp;
- os << "const char " << traits << "::erase_statement[] =" << endl
- << strlit ("DELETE FROM " + table);
-
- for (object_columns_list::iterator b (id_cols->begin ()), i (b);
- i != id_cols->end (); ++i)
- {
-
- os << endl
- << strlit ((i == b ? " WHERE " : " AND ") +
- quote_id (i->name) + "=" + qp->next ());
- }
-
- os << ";"
- << endl;
- }
-
- if (optimistic != 0)
- {
- instance<query_parameters> qp;
-
- os << "const char " << traits <<
- "::optimistic_erase_statement[] =" << endl
- << strlit ("DELETE FROM " + table);
-
- for (object_columns_list::iterator b (id_cols->begin ()), i (b);
- i != id_cols->end (); ++i)
- {
- os << endl
- << strlit ((i == b ? " WHERE " : " AND ") +
- quote_id (i->name) + "=" + qp->next ());
- }
-
- os << endl
- << strlit (" AND " + column_qname (*optimistic) +
- "=" + qp->next ()) << ";"
- << endl;
- }
- }
-
- if (options.generate_query ())
- {
- // query_statement
- //
- statement_columns sc;
- {
- statement_kind sk (statement_select); // Imperfect forwarding.
- instance<object_columns> oc (table, sk, sc);
- oc->traverse (c);
- process_statement_columns (sc, statement_select);
- }
-
- os << "const char " << traits << "::query_statement[] =" << endl
- << strlit ("SELECT ") << endl;
-
- for (statement_columns::const_iterator i (sc.begin ()),
- e (sc.end ()); i != e;)
- {
- string const& c (i->column);
- os << strlit (c + (++i != e ? "," : "")) << endl;
- }
-
- os << strlit (" FROM " + table) << endl;
-
- if (id != 0)
- {
- bool t (true); //@@ (im)perfect forwarding
- instance<object_joins> oj (c, t); //@@ (im)perfect forwarding
- oj->traverse (c);
- oj->write ();
- }
-
- os << strlit (" ") << ";"
- << endl;
-
- // erase_query_statement
- //
- os << "const char " << traits << "::erase_query_statement[] =" << endl
- << strlit ("DELETE FROM " + table) << endl;
-
- // DELETE JOIN:
- //
- // MySQL:
- // << strlit ("DELETE FROM " + table + " USING " + table) << endl;
- // << strlit ("DELETE " + table + " FROM " + table) << endl;
- // oj->write ();
- //
-
- os << strlit (" ") << ";"
- << endl;
-
- // table_name
- //
- os << "const char " << traits << "::table_name[] =" << endl
- << strlit (table_qname (c)) << ";" // Use quoted name.
- << endl;
- }
-
- // persist ()
- //
- char const* object_statements_type (
- id != 0
- ? "object_statements< object_type >"
- : "object_statements_no_id< object_type >");
-
- os << "void " << traits << "::" << endl
- << "persist (database&, " << (auto_id ? "" : "const ") <<
- "object_type& obj)"
- << "{"
- << "using namespace " << db << ";"
- << endl
- << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << object_statements_type << "& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());"
- << "image_type& im (sts.image ());"
- << "binding& imb (sts.insert_image_binding ());"
- << endl
- << "if (init (im, obj, statement_insert))" << endl
- << "im.version++;"
- << endl;
-
- if (auto_id && insert_send_auto_id)
- {
- string const& n (id->name ());
- string var ("im." + n + (n[n.size () - 1] == '_' ? "" : "_"));
- init_auto_id (*id, var);
- os << endl;
- }
-
- os << "if (im.version != sts.insert_image_version () || " <<
- "imb.version == 0)"
- << "{"
- << "bind (imb.bind, im, statement_insert);"
- << "sts.insert_image_version (im.version);"
- << "imb.version++;"
- << "}"
- << "insert_statement& st (sts.persist_statement ());"
- << "if (!st.execute ())" << endl
- << "throw object_already_persistent ();"
- << endl;
-
- if (auto_id)
- {
- if (const_type (id->type ()))
- os << "const_cast< id_type& > (obj." << id->name () << ")";
- else
- os << "obj." << id->name ();
-
- os << " = static_cast< id_type > (st.id ());"
- << endl;
- }
-
- if (optimistic != 0)
- {
- // Set the version in the object member.
- //
- if (!auto_id || const_type (optimistic->type ()))
- os << "const_cast< version_type& > (" <<
- "obj." << optimistic->name () << ") = 1;";
- else
- os << "obj." << optimistic->name () << " = 1;";
-
- os << endl;
- }
-
- if (straight_containers)
- {
- // Initialize id_image and binding.
- //
- os << "id_image_type& i (sts.id_image ());"
- << "init (i, obj." << id->name () << ");"
- << endl
- << "binding& idb (sts.id_image_binding ());"
- << "if (i.version != sts.id_image_version () || idb.version == 0)"
- << "{"
- << "bind (idb.bind, i);"
- << "sts.id_image_version (i.version);"
- << "idb.version++;"
- << "}";
-
- instance<container_calls> t (container_calls::persist_call);
- t->traverse (c);
- }
-
- os << "}";
-
- // update ()
- //
- if (id != 0 && !readonly (c))
- {
- os << "void " << traits << "::" << endl
- << "update (database&, const object_type& obj)"
- << "{"
- << "using namespace " << db << ";"
- << endl
- << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << object_statements_type << "& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());"
- << endl;
-
- if (cc.total != cc.id + cc.inverse + cc.readonly)
- {
- // Initialize object and id images.
- //
- os << "id_image_type& i (sts.id_image ());";
-
- if (optimistic == 0)
- os << "init (i, obj." << id->name () << ");";
- else
- os << "init (i, obj." << id->name () << ", &obj." <<
- optimistic->name () << ");";
-
- os << endl
- << "image_type& im (sts.image ());"
- << "if (init (im, obj, statement_update))" << endl
- << "im.version++;"
- << endl;
-
- // Update binding is bound to two images (object and id)
- // so we have to track both versions.
- //
- os << "bool u (false);" // Avoid incrementing version twice.
- << "binding& imb (sts.update_image_binding ());"
- << "if (im.version != sts.update_image_version () || " <<
- "imb.version == 0)"
- << "{"
- << "bind (imb.bind, im, statement_update);"
- << "sts.update_image_version (im.version);"
- << "imb.version++;"
- << "u = true;"
- << "}";
-
- // To update the id part of the update binding we have to do
- // it indirectly via the id binding, which just points to the
- // suffix of the update bind array (see object_statements).
- //
- os << "binding& idb (sts.id_image_binding ());"
- << "if (i.version != sts.update_id_image_version () || " <<
- "idb.version == 0)"
- << "{"
- // If the id binding is up-to-date, then that means update
- // binding is too and we just need to update the versions.
- //
- << "if (i.version != sts.id_image_version () || " <<
- "idb.version == 0)"
- << "{"
- << "bind (idb.bind, i);"
- // Update the id binding versions since we may use them later
- // to update containers.
- //
- << "sts.id_image_version (i.version);"
- << "idb.version++;"
- << "}"
- << "sts.update_id_image_version (i.version);"
- << endl
- << "if (!u)" << endl
- << "imb.version++;"
- << "}";
-
- os << "if (sts.update_statement ().execute () == 0)" << endl;
-
- if (optimistic == 0)
- os << "throw object_not_persistent ();";
- else
- os << "throw object_changed ();";
-
- os << endl;
- }
- else
- {
- // We don't have any columns to update. Note that we still have
- // to make sure this object exists in the database. For that we
- // will run the SELECT query using the find_() function.
- //
- os << "if (!find_ (sts, obj." << id->name () << "))" << endl
- << "throw object_not_persistent ();"
- << endl;
-
- if (delay_freeing_statement_result)
- os << "sts.find_statement ().free_result ();";
-
- if (straight_readwrite_containers)
- os << "binding& idb (sts.id_image_binding ());"
- << endl;
- }
-
- if (straight_readwrite_containers)
- {
- instance<container_calls> t (container_calls::update_call);
- t->traverse (c);
- }
-
- if (optimistic != 0)
- {
- // Update version in the object member.
- //
- os << "const_cast< version_type& > (" <<
- "obj." << optimistic->name () << ")++;";
- }
-
- os << "}";
- }
-
- // erase (id_type)
- //
- if (id != 0)
- {
- os << "void " << traits << "::" << endl
- << "erase (database&, const id_type& id)"
- << "{"
- << "using namespace " << db << ";"
- << endl
- << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << object_statements_type << "& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());"
- << endl;
-
- // Initialize id image.
- //
- os << "id_image_type& i (sts.id_image ());"
- << "init (i, id);"
- << endl;
-
- os << "binding& idb (sts.id_image_binding ());"
- << "if (i.version != sts.id_image_version () || idb.version == 0)"
- << "{"
- << "bind (idb.bind, i);"
- << "sts.id_image_version (i.version);"
- << "idb.version++;"
- << "}";
-
- // Erase containers first so that there are no reference
- // violations (we don't want to reply on ON DELETE CASCADE
- // here since in case of a custom schema, it might not be
- // there).
- //
- if (straight_containers)
- {
- instance<container_calls> t (container_calls::erase_call);
- t->traverse (c);
- }
-
- os << "if (sts.erase_statement ().execute () != 1)" << endl
- << "throw object_not_persistent ();";
-
- os << "}";
- }
-
- // erase (object_type)
- //
- if (id != 0 && optimistic != 0)
- {
- os << "void " << traits << "::" << endl
- << "erase (database&, const object_type& obj)"
- << "{"
- << "using namespace " << db << ";"
- << endl
- << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << object_statements_type << "& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());"
- << endl;
-
- // Initialize id + managed column image.
- //
- os << "id_image_type& i (sts.id_image ());"
- << "init (i, obj." << id->name () << ", &obj." <<
- optimistic->name () << ");"
- << endl;
-
- // To update the id part of the optimistic id binding we have
- // to do it indirectly via the id binding, since both id and
- // optimistic id bindings just point to the suffix of the
- // update bind array (see object_statements).
- //
- os << "binding& idb (sts.id_image_binding ());"
- << "binding& oidb (sts.optimistic_id_image_binding ());"
- << "if (i.version != sts.optimistic_id_image_version () || " <<
- "oidb.version == 0)"
- << "{"
- // If the id binding is up-to-date, then that means optimistic
- // id binding is too and we just need to update the versions.
- //
- << "if (i.version != sts.id_image_version () || idb.version == 0)"
- << "{"
- << "bind (idb.bind, i);"
- // Update the id binding versions since we may use them later
- // to delete containers.
- //
- << "sts.id_image_version (i.version);"
- << "idb.version++;"
- << "}"
- << "sts.optimistic_id_image_version (i.version);"
- << "oidb.version++;"
- << "}";
-
- // Erase containers first so that there are no reference
- // violations (we don't want to rely on ON DELETE CASCADE
- // here since in case of a custom schema, it might not be
- // there).
- //
- if (straight_containers)
- {
- // Things get complicated here: we don't want to trash the
- // containers and then find out that the versions don't match
- // and we therefore cannot delete the object. After all, there
- // is no guarantee that the user will abort the transaction.
- // In fact, a perfectly reasonable scenario is to reload the
- // object, re-apply the changes, and commit the transaction.
- //
- // There doesn't seem to be anything better than first making
- // sure we can delete the object, then deleting the container
- // data, and then deleting the object. To check that we can
- // delete the object we are going to use find_() and then
- // compare the versions. A special-purpose SELECT query would
- // have been more efficient but it would complicated and bloat
- // things significantly.
- //
-
- os << "if (!find_ (sts, obj." << id->name () << "))" << endl
- << "throw object_changed ();"
- << endl;
-
- if (delay_freeing_statement_result)
- os << "sts.find_statement ().free_result ();";
-
- os << "if (version (sts.image ()) != obj." <<
- optimistic->name () << ")" << endl
- << "throw object_changed ();"
- << endl;
-
- instance<container_calls> t (container_calls::erase_call);
- t->traverse (c);
- }
-
- os << "if (sts.optimistic_erase_statement ().execute () != 1)" << endl
- << "throw object_changed ();"
- << "}";
- }
-
- // find (id)
- //
- if (id != 0 && c.default_ctor ())
- {
- os << traits << "::pointer_type" << endl
- << traits << "::" << endl
- << "find (database& db, const id_type& id)"
- << "{"
- << "using namespace " << db << ";"
- << endl
- << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << object_statements_type << "& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());"
- << object_statements_type << "::auto_lock l (sts);";
-
- if (delay_freeing_statement_result)
- os << "auto_result ar;";
-
- os << endl
- << "if (l.locked ())"
- << "{"
- << "if (!find_ (sts, id))" << endl
- << "return pointer_type ();";
-
- if (delay_freeing_statement_result)
- os << endl
- << "ar.set (sts.find_statement ());";
-
- os << "}"
- << "pointer_type p (" << endl
- << "access::object_factory< object_type, pointer_type >::create ());"
- << "pointer_traits< pointer_type >::guard pg (p);"
- << "pointer_cache_traits< pointer_type >::insert_guard ig (" << endl
- << "pointer_cache_traits< pointer_type >::insert (db, id, p));"
- << "object_type& obj (pointer_traits< pointer_type >::get_ref (p));"
- << endl
- << "if (l.locked ())"
- << "{"
- << "callback (db, obj, callback_event::pre_load);"
- << "init (obj, sts.image (), &db);";
-
- init_value_extra ();
-
- if (delay_freeing_statement_result)
- os << "ar.free ();";
-
- os << "load_ (sts, obj);"
- << "sts.load_delayed ();"
- << "l.unlock ();"
- << "callback (db, obj, callback_event::post_load);"
- << "}"
- << "else" << endl
- << "sts.delay_load (id, obj, ig.position ());"
- << endl;
-
- os << "ig.release ();"
- << "pg.release ();"
- << "return p;"
- << "}";
- }
-
- // find (id, obj)
- //
- if (id != 0)
- {
- os << "bool " << traits << "::" << endl
- << "find (database& db, const id_type& id, object_type& obj)"
- << "{"
- << "using namespace " << db << ";"
- << endl
- << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << object_statements_type << "& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());"
- << endl
- // This can only be top-level call so auto_lock must succeed.
- //
- << object_statements_type << "::auto_lock l (sts);"
- << endl;
-
- os << "if (!find_ (sts, id))" << endl
- << "return false;"
- << endl;
-
- if (delay_freeing_statement_result)
- os << "auto_result ar (sts.find_statement ());";
-
- os << "reference_cache_traits< object_type >::insert_guard ig (" << endl
- << "reference_cache_traits< object_type >::insert (db, id, obj));"
- << endl
- << "callback (db, obj, callback_event::pre_load);"
- << "init (obj, sts.image (), &db);";
-
- init_value_extra ();
-
- if (delay_freeing_statement_result)
- os << "ar.free ();";
-
- os << "load_ (sts, obj);"
- << "sts.load_delayed ();"
- << "l.unlock ();"
- << "callback (db, obj, callback_event::post_load);"
- << "ig.release ();"
- << "return true;"
- << "}";
- }
-
- // reload()
- //
- if (id != 0)
- {
- os << "bool " << traits << "::" << endl
- << "reload (database& db, object_type& obj)"
- << "{"
- << "using namespace " << db << ";"
- << endl
- << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << object_statements_type << "& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());"
- << endl
- // This can only be top-level call so auto_lock must succeed.
- //
- << object_statements_type << "::auto_lock l (sts);"
- << endl;
-
- os << "if (!find_ (sts, obj." << id->name () << "))" << endl
- << "return false;"
- << endl;
-
- if (delay_freeing_statement_result)
- os << "auto_result ar (sts.find_statement ());"
- << endl;
-
- if (optimistic != 0)
- {
- os << "if (version (sts.image ()) == obj." <<
- optimistic->name () << ")" << endl
- << "return true;";
- }
-
- os << "callback (db, obj, callback_event::pre_load);"
- << "init (obj, sts.image (), &db);";
-
- init_value_extra ();
-
- if (delay_freeing_statement_result)
- os << "ar.free ();";
-
- os << "load_ (sts, obj);"
- << "sts.load_delayed ();"
- << "l.unlock ();"
- << "callback (db, obj, callback_event::post_load);"
- << "return true;"
- << "}";
- }
-
- // find_ ()
- //
- if (id != 0)
- {
- os << "bool " << traits << "::" << endl
- << "find_ (" << db << "::" << object_statements_type << "& " <<
- "sts, const id_type& id)"
- << "{"
- << "using namespace " << db << ";"
- << endl;
-
- // Initialize id image.
- //
- os << "id_image_type& i (sts.id_image ());"
- << "init (i, id);"
- << endl;
-
- os << "binding& idb (sts.id_image_binding ());"
- << "if (i.version != sts.id_image_version () || idb.version == 0)"
- << "{"
- << "bind (idb.bind, i);"
- << "sts.id_image_version (i.version);"
- << "idb.version++;"
- << "}";
-
- // Rebind data image.
- //
- os << "image_type& im (sts.image ());"
- << "binding& imb (sts.select_image_binding ());"
- << endl
- << "if (im.version != sts.select_image_version () || " <<
- "imb.version == 0)"
- << "{"
- << "bind (imb.bind, im, statement_select);"
- << "sts.select_image_version (im.version);"
- << "imb.version++;"
- << "}"
- << "select_statement& st (sts.find_statement ());"
- << "st.execute ();"
- << "auto_result ar (st);"
- << "select_statement::result r (st.fetch ());"
- << endl;
-
- if (grow)
- os << "if (r == select_statement::truncated)"
- << "{"
- << "if (grow (im, sts.select_image_truncated ()))" << endl
- << "im.version++;"
- << endl
- << "if (im.version != sts.select_image_version ())"
- << "{"
- << "bind (imb.bind, im, statement_select);"
- << "sts.select_image_version (im.version);"
- << "imb.version++;"
- << "st.refetch ();"
- << "}"
- << "}";
-
- // If we are delaying, only free the result if it is empty.
- //
- if (delay_freeing_statement_result)
- os << "if (r != select_statement::no_data)"
- << "{"
- << "ar.release ();"
- << "return true;"
- << "}"
- << "else" << endl
- << "return false;";
- else
- os << "return r != select_statement::no_data;";
-
- os << "}";
- }
-
- // load_()
- //
- if (containers)
- {
- os << "void " << traits << "::" << endl
- << "load_ (" << db << "::" << object_statements_type << "& " <<
- "sts, object_type& obj)"
- << "{"
- << db << "::binding& idb (sts.id_image_binding ());"
- << endl;
- instance<container_calls> t (container_calls::load_call);
- t->traverse (c);
- os << "}";
- }
-
- if (options.generate_query ())
- {
- // query ()
- //
- os << "result< " << traits << "::object_type >" << endl
- << traits << "::" << endl
- << "query (database&, const query_base_type& q)"
- << "{"
- << "using namespace " << db << ";"
- << "using odb::details::shared;"
- << "using odb::details::shared_ptr;"
- << endl
- << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << endl
- << object_statements_type << "& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());"
- << endl
- << "image_type& im (sts.image ());"
- << "binding& imb (sts.select_image_binding ());"
- << endl
- << "if (im.version != sts.select_image_version () || " <<
- "imb.version == 0)"
- << "{"
- << "bind (imb.bind, im, statement_select);"
- << "sts.select_image_version (im.version);"
- << "imb.version++;"
- << "}"
- << "shared_ptr<select_statement> st (" << endl
- << "new (shared) select_statement (" << endl;
-
- object_query_statement_ctor_args (c);
-
- os << "));" << endl
- << "st->execute ();";
-
- post_query_ (c);
-
- char const* result_type (
- id != 0
- ? "object_result_impl<object_type>"
- : "object_result_impl_no_id<object_type>");
-
- os << endl
- << "shared_ptr< odb::" << result_type << " > r (" << endl
- << "new (shared) " << db << "::" << result_type << " (" << endl
- << "q, st, sts));"
- << endl
- << "return result<object_type> (r);"
- << "}";
-
- // erase_query
- //
- os << "unsigned long long " << traits << "::" << endl
- << "erase_query (database&, const query_base_type& q)"
- << "{"
- << "using namespace " << db << ";"
- << endl
- << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << endl
- << "delete_statement st (" << endl;
-
- object_erase_query_statement_ctor_args (c);
-
- os << ");"
- << endl
- << "return st.execute ();"
- << "}";
- }
-
- if (embedded_schema)
- schema_->traverse (c);
- }
+ traverse_object (type& c);
//
// view
@@ -4036,782 +3178,7 @@ namespace relational
}
virtual void
- traverse_view (type& c)
- {
- string const& type (class_fq_name (c));
- string traits ("access::view_traits< " + type + " >");
-
- os << "// " << class_name (c) << endl
- << "//" << endl
- << endl;
-
- view_extra (c);
-
- //
- // Query.
- //
-
- // query_type
- //
- size_t obj_count (c.get<size_t> ("object-count"));
-
- if (obj_count != 0)
- {
- view_objects& objs (c.get<view_objects> ("objects"));
-
- if (obj_count > 1)
- {
- for (view_objects::const_iterator i (objs.begin ());
- i < objs.end ();
- ++i)
- {
- if (i->kind != view_object::object)
- continue; // Skip tables.
-
- qname const& t (table_name (*i->obj));
-
- if (!i->alias.empty () &&
- (t.qualified () || i->alias != t.uname ()))
- os << "const char " << traits << "::query_columns::" << endl
- << i->alias << "_alias_[] = " <<
- strlit (quote_id (i->alias)) << ";"
- << endl;
- }
- }
- else
- {
- // For a single object view we generate a shortcut without
- // an intermediate typedef.
- //
- view_object const* vo (0);
- for (view_objects::const_iterator i (objs.begin ());
- vo == 0 && i < objs.end ();
- ++i)
- {
- if (i->kind == view_object::object)
- vo = &*i;
- }
-
- qname const& t (table_name (*vo->obj));
-
- if (!vo->alias.empty () &&
- (t.qualified () || vo->alias != t.uname ()))
- os << "const char " << traits << "::" << endl
- << "query_alias[] = " << strlit (quote_id (vo->alias)) << ";"
- << endl;
- }
- }
-
- //
- // Functions.
- //
-
- // grow ()
- //
- if (generate_grow)
- {
- os << "bool " << traits << "::" << endl
- << "grow (image_type& i, " << truncated_vector << " t)"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (i);"
- << "ODB_POTENTIALLY_UNUSED (t);"
- << endl
- << "bool grew (false);"
- << endl;
-
- index_ = 0;
- names (c, grow_member_names_);
-
- os << "return grew;"
- << "}";
- }
-
- // bind (image_type)
- //
- os << "void " << traits << "::" << endl
- << "bind (" << bind_vector << " b, image_type& i)"
- << "{"
- << "using namespace " << db << ";"
- << endl
- << db << "::statement_kind sk (statement_select);"
- << "ODB_POTENTIALLY_UNUSED (sk);"
- << endl
- << "std::size_t n (0);"
- << endl;
-
- names (c, bind_member_names_);
-
- os << "}";
-
- // init (view, image)
- //
- os << "void " << traits << "::" << endl
- << "init (view_type& o, const image_type& i, database* db)"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (o);"
- << "ODB_POTENTIALLY_UNUSED (i);"
- << "ODB_POTENTIALLY_UNUSED (db);"
- << endl;
-
- names (c, init_value_member_names_);
-
- os << "}";
-
- // query_statement()
- //
- view_query& vq (c.get<view_query> ("query"));
-
- if (vq.kind != view_query::runtime)
- {
- os << traits << "::query_base_type" << endl
- << traits << "::" << endl
- << "query_statement (const query_base_type& q)"
- << "{";
-
- if (vq.kind == view_query::complete)
- {
- os << "query_base_type r (" << endl;
-
- bool ph (false);
-
- if (!vq.literal.empty ())
- {
- // See if we have the '(?)' placeholder.
- //
- // @@ Ideally we would need to make sure we don't match
- // this inside strings and quoted identifier. So the
- // proper way to handle this would be to tokenize the
- // statement using sql_lexer, once it is complete enough.
- //
- string::size_type p (vq.literal.find ("(?)"));
-
- if (p != string::npos)
- {
- ph = true;
- os << strlit (string (vq.literal, 0, p + 1)) << " +" << endl
- << "(q.empty () ? query_base_type::true_expr : q) +" << endl
- << strlit (string (vq.literal, p + 2));
- }
- else
- os << strlit (vq.literal);
- }
- else
- // Output the pragma location for easier error tracking.
- //
- os << "// From " <<
- location_file (vq.loc).leaf () << ":" <<
- location_line (vq.loc) << ":" <<
- location_column (vq.loc) << endl
- << translate_expression (
- c, vq.expr, vq.scope, vq.loc, "query", &ph).value;
-
- os << ");";
-
- // If there was no placeholder, add the query condition
- // at the end.
- //
- if (!ph)
- os << "r += q.clause_prefix ();"
- << "r += q;";
- }
- else // vq.kind == view_query::condition
- {
- statement_columns sc;
- {
- instance<view_columns> t (sc);
- t->traverse (c);
- process_statement_columns (sc, statement_select);
- }
-
- os << "query_base_type r (" << endl
- << strlit ("SELECT ") << endl;
-
- for (statement_columns::const_iterator i (sc.begin ()),
- e (sc.end ()); i != e;)
- {
- string const& c (i->column);
- os << strlit (c + (++i != e ? "," : "")) << endl;
- }
-
- os << ");"
- << endl;
-
- // Generate from-list.
- //
- view_objects const& objs (c.get<view_objects> ("objects"));
-
- for (view_objects::const_iterator i (objs.begin ());
- i != objs.end ();
- ++i)
- {
- bool first (i == objs.begin ());
- string l;
-
- //
- // Tables.
- //
-
- if (i->kind == view_object::table)
- {
- if (first)
- {
- l = "FROM ";
- l += quote_id (i->tbl_name);
-
- if (!i->alias.empty ())
- l += (need_alias_as ? " AS " : " ") + quote_id (i->alias);
-
- os << "r += " << strlit (l) << ";"
- << endl;
-
- continue;
- }
-
- l = "LEFT JOIN ";
- l += quote_id (i->tbl_name);
-
- if (!i->alias.empty ())
- l += (need_alias_as ? " AS " : " ") + quote_id (i->alias);
-
- expression e (
- translate_expression (
- c, i->cond, i->scope, i->loc, "table"));
-
- if (e.kind != expression::literal)
- {
- error (i->loc)
- << "invalid join condition in db pragma table" << endl;
-
- throw operation_failed ();
- }
-
- l += " ON";
-
- os << "r += " << strlit (l) << ";"
- // Output the pragma location for easier error tracking.
- //
- << "// From " <<
- location_file (i->loc).leaf () << ":" <<
- location_line (i->loc) << ":" <<
- location_column (i->loc) << endl
- << "r += " << e.value << ";"
- << endl;
-
- continue;
- }
-
- //
- // Objects.
- //
-
- // First object.
- //
- if (first)
- {
- l = "FROM ";
- l += table_qname (*i->obj);
-
- if (!i->alias.empty ())
- l += (need_alias_as ? " AS " : " ") + quote_id (i->alias);
-
- os << "r += " << strlit (l) << ";"
- << endl;
-
- continue;
- }
-
- expression e (
- translate_expression (
- c, i->cond, i->scope, i->loc, "object"));
-
- // Literal expression.
- //
- if (e.kind == expression::literal)
- {
- l = "LEFT JOIN ";
- l += table_qname (*i->obj);
-
- if (!i->alias.empty ())
- l += (need_alias_as ? " AS " : " ") + quote_id (i->alias);
-
- l += " ON";
-
- os << "r += " << strlit (l) << ";"
- // Output the pragma location for easier error tracking.
- //
- << "// From " <<
- location_file (i->loc).leaf () << ":" <<
- location_line (i->loc) << ":" <<
- location_column (i->loc) << endl
- << "r += " << e.value << ";"
- << endl;
-
- continue;
- }
-
- // We have an object relationship (pointer) for which we need
- // to come up with the corresponding JOIN condition. If this
- // is a to-many relationship, then we first need to JOIN the
- // container table. This code is similar to object_joins.
- //
- using semantics::data_member;
-
- data_member& m (*e.member_path.back ());
-
- // Resolve the pointed-to object to view_object and do
- // some sanity checks while at it.
- //
- semantics::class_* c (0);
-
- if (semantics::type* cont = container (m))
- c = object_pointer (container_vt (*cont));
- else
- c = object_pointer (utype (m));
-
- view_object const* vo (0);
-
- // Check if the pointed-to object has been previously
- // associated with this view and is unambiguous. A
- // pointer to ourselves is always assumed to point
- // to this association.
- //
- if (i->obj == c)
- vo = &*i;
- else
- {
- bool ambig (false);
-
- for (view_objects::const_iterator j (objs.begin ());
- j != i;
- ++j)
- {
- if (j->obj != c)
- continue;
-
- if (vo == 0)
- {
- vo = &*j;
- continue;
- }
-
- // If it is the first ambiguous object, issue the
- // error.
- //
- if (!ambig)
- {
- error (i->loc)
- << "pointed-to object '" << class_name (*c) << "' is "
- << "ambiguous" << endl;
-
- info (i->loc)
- << "candidates are:" << endl;
-
- info (vo->loc)
- << " '" << vo->name () << "'" << endl;
-
- ambig = true;
- }
-
- info (j->loc)
- << " '" << j->name () << "'" << endl;
- }
-
- if (ambig)
- {
- info (i->loc)
- << "use the full join condition clause in db pragma "
- << "object to resolve this ambiguity" << endl;
-
- throw operation_failed ();
- }
-
- if (vo == 0)
- {
- error (i->loc)
- << "pointed-to object '" << class_name (*c) << "' "
- << "specified in the join condition has not been "
- << "previously associated with this view" << endl;
-
- throw operation_failed ();
- }
- }
-
- // Left and right-hand side table names.
- //
- qname lt (e.vo->alias.empty ()
- ? table_name (*e.vo->obj)
- : qname (e.vo->alias));
-
- qname rt (vo->alias.empty ()
- ? table_name (*vo->obj)
- : qname (vo->alias));
-
- // First join the container table if necessary.
- //
- data_member* im (inverse (m));
-
- semantics::type* cont (container (im != 0 ? *im : m));
-
- // Container table.
- //
- string ct;
- if (cont != 0)
- {
- if (im != 0)
- {
- // For now a direct member can only be directly in
- // the object scope. If this changes, the inverse()
- // function would have to return a member path instead
- // of just a single member.
- //
- table_prefix tp (
- context::schema (vo->obj->scope ()),
- context::table_name_prefix (vo->obj->scope ()),
- table_name (*vo->obj) + "_");
- ct = table_qname (*im, tp);
- }
- else
- ct = table_qname (*e.vo->obj, e.member_path);
- }
-
- if (cont != 0)
- {
- l = "LEFT JOIN ";
- l += ct;
- l += " ON";
- os << "r += " << strlit (l) << ";";
-
- // If we are the pointed-to object, then we have to turn
- // things around. This is necessary to have the proper
- // JOIN order. There seems to be a pattern there but it
- // is not yet intuitively clear what it means.
- //
- instance<object_columns_list> c_cols; // Container columns.
- instance<object_columns_list> o_cols; // Object columns.
-
- qname* ot; // Object table (either lt or rt).
-
- if (im != 0)
- {
- if (i->obj == c)
- {
- // container.value = pointer.id
- //
- semantics::data_member& id (*id_member (*e.vo->obj));
-
- c_cols->traverse (*im, utype (id), "value", "value");
- o_cols->traverse (id);
- ot = &lt;
- }
- else
- {
- // container.id = pointed-to.id
- //
- semantics::data_member& id (*id_member (*vo->obj));
-
- c_cols->traverse (
- *im, utype (id), "id", "object_id", vo->obj);
- o_cols->traverse (id);
- ot = &rt;
- }
- }
- else
- {
- if (i->obj == c)
- {
- // container.id = pointer.id
- //
- semantics::data_member& id (*id_member (*e.vo->obj));
-
- c_cols->traverse (
- m, utype (id), "id", "object_id", e.vo->obj);
- o_cols->traverse (id);
- ot = &lt;
- }
- else
- {
- // container.value = pointed-to.id
- //
- semantics::data_member& id (*id_member (*vo->obj));
-
- c_cols->traverse (m, utype (id), "value", "value");
- o_cols->traverse (id);
- ot = &rt;
- }
- }
-
- for (object_columns_list::iterator b (c_cols->begin ()), i (b),
- j (o_cols->begin ()); i != c_cols->end (); ++i, ++j)
- {
- l.clear ();
-
- if (i != b)
- l += "AND ";
-
- l += ct;
- l += '.';
- l += quote_id (i->name);
- l += '=';
- l += quote_id (*ot);
- l += '.';
- l += quote_id (j->name);
-
- os << "r += " << strlit (l) << ";";
- }
- }
-
- l = "LEFT JOIN ";
- l += table_qname (*i->obj);
-
- if (!i->alias.empty ())
- l += (need_alias_as ? " AS " : " ") + quote_id (i->alias);
-
- l += " ON";
- os << "r += " << strlit (l) << ";";
-
- if (cont != 0)
- {
- instance<object_columns_list> c_cols; // Container columns.
- instance<object_columns_list> o_cols; // Object columns.
-
- qname* ot; // Object table (either lt or rt).
-
- if (im != 0)
- {
- if (i->obj == c)
- {
- // container.id = pointed-to.id
- //
- semantics::data_member& id (*id_member (*vo->obj));
-
- c_cols->traverse (
- *im, utype (id), "id", "object_id", vo->obj);
- o_cols->traverse (id);
- ot = &rt;
- }
- else
- {
- // container.value = pointer.id
- //
- semantics::data_member& id (*id_member (*e.vo->obj));
-
- c_cols->traverse (*im, utype (id), "value", "value");
- o_cols->traverse (id);
- ot = &lt;
- }
- }
- else
- {
- if (i->obj == c)
- {
- // container.value = pointed-to.id
- //
- semantics::data_member& id (*id_member (*vo->obj));
-
- c_cols->traverse (m, utype (id), "value", "value");
- o_cols->traverse (id);
- ot = &rt;
- }
- else
- {
- // container.id = pointer.id
- //
- semantics::data_member& id (*id_member (*e.vo->obj));
-
- c_cols->traverse (
- m, utype (id), "id", "object_id", e.vo->obj);
- o_cols->traverse (id);
- ot = &lt;
- }
- }
-
- for (object_columns_list::iterator b (c_cols->begin ()), i (b),
- j (o_cols->begin ()); i != c_cols->end (); ++i, ++j)
- {
- l.clear ();
-
- if (i != b)
- l += "AND ";
-
- l += ct;
- l += '.';
- l += quote_id (i->name);
- l += '=';
- l += quote_id (*ot);
- l += '.';
- l += quote_id (j->name);
-
- os << "r += " << strlit (l) << ";";
- }
- }
- else
- {
- string col_prefix;
-
- if (im == 0)
- col_prefix =
- object_columns_base::column_prefix (e.member_path);
-
- instance<object_columns_list> l_cols (col_prefix);
- instance<object_columns_list> r_cols;
-
- if (im != 0)
- {
- // our.id = pointed-to.pointer
- //
- l_cols->traverse (*id_member (*e.vo->obj));
- r_cols->traverse (*im);
- }
- else
- {
- // our.pointer = pointed-to.id
- //
- l_cols->traverse (*e.member_path.back ());
- r_cols->traverse (*id_member (*vo->obj));
- }
-
- for (object_columns_list::iterator b (l_cols->begin ()), i (b),
- j (r_cols->begin ()); i != l_cols->end (); ++i, ++j)
- {
- l.clear ();
-
- if (i != b)
- l += "AND ";
-
- l += quote_id (lt);
- l += '.';
- l += quote_id (i->name);
- l += '=';
- l += quote_id (rt);
- l += '.';
- l += quote_id (j->name);
-
- os << "r += " << strlit (l) << ";";
- }
- }
-
- os << endl;
- }
-
- // Generate the query condition.
- //
- if (!vq.literal.empty () || !vq.expr.empty ())
- {
- os << "query_base_type c (" << endl;
-
- bool ph (false);
-
- if (!vq.literal.empty ())
- {
- // See if we have the '(?)' placeholder.
- //
- // @@ Ideally we would need to make sure we don't match
- // this inside strings and quoted identifier. So the
- // proper way to handle this would be to tokenize the
- // statement using sql_lexer, once it is complete enough.
- //
- string::size_type p (vq.literal.find ("(?)"));
-
- if (p != string::npos)
- {
- ph = true;
- os << strlit (string (vq.literal, 0, p + 1))<< " +" << endl
- << "(q.empty () ? query_base_type::true_expr : q) +" << endl
- << strlit (string (vq.literal, p + 2));
- }
- else
- os << strlit (vq.literal);
-
- os << ");";
- }
- else
- {
- // Output the pragma location for easier error tracking.
- //
- os << "// From " <<
- location_file (vq.loc).leaf () << ":" <<
- location_line (vq.loc) << ":" <<
- location_column (vq.loc) << endl
- << translate_expression (
- c, vq.expr, vq.scope, vq.loc, "query", &ph).value;
-
- os << ");";
-
- // Optimize the query if it had a placeholder. This gets
- // rid of useless clauses like WHERE TRUE.
- //
- if (ph)
- os << "c.optimize ();";
- }
-
- if (!ph)
- os << "c += q;";
-
- os << "r += c.clause_prefix ();"
- << "r += c;"
- << endl;
- }
- else
- {
- os << "r += q.clause_prefix ();"
- << "r += q;"
- << endl;
- }
- }
-
- os << "return r;"
- << "}";
- }
-
- // query ()
- //
- os << "result< " << traits << "::view_type >" << endl
- << traits << "::" << endl
- << "query (database&, const query_base_type& q)"
- << "{"
- << "using namespace " << db << ";"
- << "using odb::details::shared;"
- << "using odb::details::shared_ptr;"
- << endl
- << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << endl
- << "view_statements< view_type >& sts (" << endl
- << "conn.statement_cache ().find_view<view_type> ());"
- << endl
- << "image_type& im (sts.image ());"
- << "binding& imb (sts.image_binding ());"
- << endl
- << "if (im.version != sts.image_version () || imb.version == 0)"
- << "{"
- << "bind (imb.bind, im);"
- << "sts.image_version (im.version);"
- << "imb.version++;"
- << "}";
-
- if (vq.kind == view_query::runtime)
- os << "const query_base_type& qs (q);";
- else
- os << "const query_base_type& qs (query_statement (q));";
-
- os << "shared_ptr<select_statement> st (" << endl
- << "new (shared) select_statement (" << endl;
-
- view_query_statement_ctor_args (c);
-
- os << "));" << endl
- << "st->execute ();";
-
- post_query_ (c);
-
- os << endl
- << "shared_ptr< odb::view_result_impl<view_type> > r (" << endl
- << "new (shared) " << db << "::view_result_impl<view_type> (" << endl
- << "qs, st, sts));"
- << endl
- << "return result<view_type> (r);"
- << "}";
- }
+ traverse_view (type& c);
struct expression
{
@@ -4950,6 +3317,9 @@ namespace relational
traversal::inherits grow_base_inherits_;
instance<grow_member> grow_member_;
traversal::names grow_member_names_;
+ instance<grow_member> grow_version_member_;
+ instance<grow_member> grow_discriminator_member_;
+
instance<bind_base> bind_base_;
traversal::inherits bind_base_inherits_;
@@ -4957,6 +3327,7 @@ namespace relational
traversal::names bind_member_names_;
instance<bind_member> bind_id_member_;
instance<bind_member> bind_version_member_;
+ instance<bind_member> bind_discriminator_member_;
instance<init_image_base> init_image_base_;
traversal::inherits init_image_base_inherits_;
@@ -4973,6 +3344,9 @@ namespace relational
instance<init_value_member> init_id_value_member_;
instance<init_value_member> init_version_value_member_;
+ instance<init_value_member> init_named_version_value_member_;
+ instance<init_value_member> init_discriminator_value_member_;
+ instance<init_value_member> init_named_discriminator_value_member_;
instance<schema::cxx_object> schema_;
};
@@ -4987,11 +3361,18 @@ namespace relational
extra_pre ();
os << "#include <cassert>" << endl
- << "#include <cstring> // std::memcpy" << endl
- << endl;
+ << "#include <cstring> // std::memcpy" << endl;
+
+ if (features.polymorphic_object)
+ os << "#include <typeinfo>" << endl;
+
+ os << endl;
os << "#include <odb/cache-traits.hxx>" << endl;
+ if (features.polymorphic_object)
+ os << "#include <odb/polymorphic-map.hxx>" << endl;
+
if (embedded_schema)
os << "#include <odb/schema-catalog-impl.hxx>" << endl;
@@ -5000,23 +3381,42 @@ namespace relational
os << endl;
- os << "#include <odb/" << db << "/binding.hxx>" << endl
- << "#include <odb/" << db << "/traits.hxx>" << endl
+ os << "#include <odb/" << db << "/traits.hxx>" << endl
<< "#include <odb/" << db << "/database.hxx>" << endl
<< "#include <odb/" << db << "/transaction.hxx>" << endl
<< "#include <odb/" << db << "/connection.hxx>" << endl
<< "#include <odb/" << db << "/statement.hxx>" << endl
- << "#include <odb/" << db << "/statement-cache.hxx>" << endl
- << "#include <odb/" << db << "/object-statements.hxx>" << endl;
+ << "#include <odb/" << db << "/statement-cache.hxx>" << endl;
- if (options.generate_query ())
+ if (features.simple_object)
+ os << "#include <odb/" << db << "/simple-object-statements.hxx>" << endl;
+
+ if (features.polymorphic_object)
+ os << "#include <odb/" << db << "/polymorphic-object-statements.hxx>" << endl;
+
+ if (features.no_id_object)
+ os << "#include <odb/" << db << "/no-id-object-statements.hxx>" << endl;
+
+ if (features.view)
os << "#include <odb/" << db << "/view-statements.hxx>" << endl;
os << "#include <odb/" << db << "/container-statements.hxx>" << endl
<< "#include <odb/" << db << "/exceptions.hxx>" << endl;
if (options.generate_query ())
- os << "#include <odb/" << db << "/result.hxx>" << endl;
+ {
+ if (features.simple_object)
+ os << "#include <odb/" << db << "/simple-object-result.hxx>" << endl;
+
+ if (features.polymorphic_object)
+ os << "#include <odb/" << db << "/polymorphic-object-result.hxx>" << endl;
+
+ if (features.no_id_object)
+ os << "#include <odb/" << db << "/no-id-object-result.hxx>" << endl;
+
+ if (features.view)
+ os << "#include <odb/" << db << "/view-result.hxx>" << endl;
+ }
extra_post ();
diff --git a/odb/relational/sqlite/context.cxx b/odb/relational/sqlite/context.cxx
index 9f25fcc..3b8efb6 100644
--- a/odb/relational/sqlite/context.cxx
+++ b/odb/relational/sqlite/context.cxx
@@ -67,8 +67,9 @@ namespace relational
context (ostream& os,
semantics::unit& u,
options_type const& ops,
+ features_type& f,
sema_rel::model* m)
- : root_context (os, u, ops, data_ptr (new (shared) data (os))),
+ : root_context (os, u, ops, f, data_ptr (new (shared) data (os))),
base_context (static_cast<data*> (root_context::data_.get ()), m),
data_ (static_cast<data*> (base_context::data_))
{
@@ -79,6 +80,7 @@ namespace relational
need_alias_as = true;
insert_send_auto_id = true;
delay_freeing_statement_result = false;
+ need_image_clone = false;
data_->bind_vector_ = "sqlite::bind*";
data_->truncated_vector_ = "bool*";
diff --git a/odb/relational/sqlite/context.hxx b/odb/relational/sqlite/context.hxx
index 4123b0c..3c17d55 100644
--- a/odb/relational/sqlite/context.hxx
+++ b/odb/relational/sqlite/context.hxx
@@ -73,6 +73,7 @@ namespace relational
context (std::ostream&,
semantics::unit&,
options_type const&,
+ features_type& f,
sema_rel::model*);
static context&
diff --git a/odb/relational/sqlite/source.cxx b/odb/relational/sqlite/source.cxx
index c170904..1e93568 100644
--- a/odb/relational/sqlite/source.cxx
+++ b/odb/relational/sqlite/source.cxx
@@ -21,127 +21,17 @@ namespace relational
// bind
//
- struct bind_member: relational::bind_member, member_base
+ struct bind_member: relational::bind_member_impl<sql_type>,
+ member_base
{
bind_member (base const& x)
- : member_base::base (x), // virtual base
- base (x),
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
member_base (x)
{
}
- virtual bool
- pre (member_info& mi)
- {
- if (container (mi))
- return false;
-
- ostringstream ostr;
- ostr << "b[n]";
- b = ostr.str ();
-
- arg = arg_override_.empty () ? string ("i") : arg_override_;
-
- if (var_override_.empty ())
- {
- os << "// " << mi.m.name () << endl
- << "//" << endl;
-
- if (inverse (mi.m, key_prefix_) || version (mi.m))
- os << "if (sk == statement_select)"
- << "{";
- // If the whole class is readonly, then we will never be
- // called with sk == statement_update.
- //
- else if (!readonly (*context::top_object))
- {
- semantics::class_* c;
-
- if (id (mi.m) ||
- readonly (mi.m) ||
- ((c = composite (mi.t)) && readonly (*c)))
- os << "if (sk != statement_update)"
- << "{";
- }
- }
-
- return true;
- }
-
- virtual void
- post (member_info& mi)
- {
- if (var_override_.empty ())
- {
- semantics::class_* c;
-
- if ((c = composite (mi.t)))
- {
- bool ro (readonly (*c));
- column_count_type const& cc (column_count (*c));
-
- os << "n += " << cc.total << "UL";
-
- // select = total
- // insert = total - inverse
- // update = total - inverse - readonly
- //
- if (cc.inverse != 0 || (!ro && cc.readonly != 0))
- {
- os << " - (" << endl
- << "sk == statement_select ? 0 : ";
-
- if (cc.inverse != 0)
- os << cc.inverse << "UL";
-
- if (!ro && cc.readonly != 0)
- {
- if (cc.inverse != 0)
- os << " + ";
-
- os << "(" << endl
- << "sk == statement_insert ? 0 : " <<
- cc.readonly << "UL)";
- }
-
- os << ")";
- }
-
- os << ";";
- }
- else
- os << "n++;";
-
- bool block (false);
-
- // The same logic as in pre().
- //
- if (inverse (mi.m, key_prefix_) || version (mi.m))
- block = true;
- else if (!readonly (*context::top_object))
- {
- semantics::class_* c;
-
- if (id (mi.m) ||
- readonly (mi.m) ||
- ((c = composite (mi.t)) && readonly (*c)))
- block = true;
- }
-
- if (block)
- os << "}";
- else
- os << endl;
- }
- }
-
- virtual void
- traverse_composite (member_info& mi)
- {
- os << "composite_value_traits< " << mi.fq_type () <<
- " >::bind (b + n, " << arg << "." << mi.var << "value, sk);";
- }
-
virtual void
traverse_integer (member_info& mi)
{
@@ -179,10 +69,6 @@ namespace relational
"value.capacity ();"
<< b << ".is_null = &" << arg << "." << mi.var << "null;";
}
-
- private:
- string b;
- string arg;
};
entry<bind_member> bind_member_;
diff --git a/odb/semantics/class.hxx b/odb/semantics/class.hxx
index d62de53..b9f8f34 100644
--- a/odb/semantics/class.hxx
+++ b/odb/semantics/class.hxx
@@ -111,7 +111,7 @@ namespace semantics
}
using scope::add_edge_left;
- using scope::add_edge_right;
+ using type::add_edge_right;
// Resolve conflict between scope::scope and nameable::scope.
//
diff --git a/odb/semantics/elements.cxx b/odb/semantics/elements.cxx
index 85fb203..4b8fcea 100644
--- a/odb/semantics/elements.cxx
+++ b/odb/semantics/elements.cxx
@@ -322,8 +322,25 @@ namespace semantics
void scope::
add_edge_left (names& e)
{
- names_list::iterator it (names_.insert (names_.end (), &e));
- iterator_map_[&e] = it;
+ names_list::iterator i (names_.insert (names_.end (), &e));
+ iterator_map_[&e] = i;
+ names_map_[e.name ()].push_back (&e);
+ }
+
+ void scope::
+ add_edge_left (names& e, names_iterator after)
+ {
+ names_list::iterator i;
+
+ if (after.base () == names_.end ())
+ i = names_.insert (names_.begin (), &e);
+ else
+ {
+ names_list::iterator j (after.base ());
+ i = names_.insert (++j, &e);
+ }
+
+ iterator_map_[&e] = i;
names_map_[e.name ()].push_back (&e);
}
@@ -426,6 +443,14 @@ namespace semantics
insert (ti);
}
+ // virtual_data_member
+ //
+ {
+ type_info ti (typeid (virtual_data_member));
+ ti.add_base (typeid (data_member));
+ insert (ti);
+ }
+
// unsupported_type
//
{
diff --git a/odb/semantics/elements.hxx b/odb/semantics/elements.hxx
index ef28d23..59e058d 100644
--- a/odb/semantics/elements.hxx
+++ b/odb/semantics/elements.hxx
@@ -67,6 +67,40 @@ namespace semantics
class edge;
class unit;
+ // Support for inserting edges at specified positions.
+ //
+ template <typename N, typename I>
+ struct node_position
+ {
+ node_position (N& node, I pos)
+ : node_ (node), pos_ (pos)
+ {
+ }
+
+ operator N& () const
+ {
+ return node_;
+ }
+
+ template <typename E>
+ void
+ add_edge_left (E& e)
+ {
+ node_.add_edge_left (e, pos_);
+ }
+
+ template <typename E>
+ void
+ add_edge_right (E& e)
+ {
+ node_.add_edge_right (e, pos_);
+ }
+
+ private:
+ N& node_;
+ I pos_;
+ };
+
//
//
class edge: public context
@@ -529,6 +563,9 @@ namespace semantics
void
add_edge_left (names&);
+ void
+ add_edge_left (names&, names_iterator after);
+
using nameable::add_edge_right;
protected:
@@ -544,6 +581,8 @@ namespace semantics
//
//
+ class points;
+
class belongs;
class qualifies;
@@ -567,6 +606,15 @@ namespace semantics
}
public:
+ bool
+ pointed_p () const {return pointed_ != 0;}
+
+ points&
+ pointed () const {return *pointed_;}
+
+ public:
+ type (): pointed_ (0) {}
+
void
add_edge_right (belongs&)
{
@@ -578,10 +626,17 @@ namespace semantics
qualified_.push_back (&e);
}
+ void
+ add_edge_right (points& e)
+ {
+ pointed_ = &e;
+ }
+
using nameable::add_edge_right;
private:
qualified qualified_;
+ points* pointed_;
};
//
@@ -688,6 +743,22 @@ namespace semantics
: node (file, line, column, tn)
{
}
+
+ protected:
+ data_member ()
+ {
+ }
+ };
+
+ // Virtual data member (extension to the standard C++ model).
+ //
+ class virtual_data_member: public data_member
+ {
+ public:
+ virtual_data_member (path const& file, size_t line, size_t column)
+ : node (file, line, column, 0)
+ {
+ }
};
// Unsupported type.
diff --git a/odb/semantics/relational/elements.cxx b/odb/semantics/relational/elements.cxx
index 7a0ec1d..a73520c 100644
--- a/odb/semantics/relational/elements.cxx
+++ b/odb/semantics/relational/elements.cxx
@@ -15,16 +15,14 @@ namespace semantics
template <>
duplicate_name::
duplicate_name (uscope& s, unameable& o, unameable& d)
- : scope (s), orig (o), dup (d),
- orig_name (o.name ()), dup_name (d.name ())
+ : scope (s), orig (o), dup (d), name (o.name ())
{
}
template <>
duplicate_name::
duplicate_name (qscope& s, qnameable& o, qnameable& d)
- : scope (s), orig (o), dup (d),
- orig_name (o.name ().string ()), dup_name (d.name ().string ())
+ : scope (s), orig (o), dup (d), name (o.name ().string ())
{
}
diff --git a/odb/semantics/relational/elements.hxx b/odb/semantics/relational/elements.hxx
index 9d98619..6b4964e 100644
--- a/odb/semantics/relational/elements.hxx
+++ b/odb/semantics/relational/elements.hxx
@@ -212,8 +212,7 @@ namespace semantics
node& orig;
node& dup;
- string orig_name;
- string dup_name;
+ string name;
};
template <typename N>
diff --git a/odb/semantics/unit.hxx b/odb/semantics/unit.hxx
index 11c8a8a..ca001b1 100644
--- a/odb/semantics/unit.hxx
+++ b/odb/semantics/unit.hxx
@@ -37,6 +37,8 @@ namespace semantics
tree_node_map_[key] = &value;
}
+ using namespace_::find;
+
// Mapping from tree nodes to name hints.
//
public:
diff --git a/odb/traversal/elements.cxx b/odb/traversal/elements.cxx
index 34955d9..7ba207d 100644
--- a/odb/traversal/elements.cxx
+++ b/odb/traversal/elements.cxx
@@ -75,4 +75,24 @@ namespace traversal
{
d.dispatch (m.belongs ());
}
+
+ // virtual_data_member
+ //
+ void virtual_data_member::
+ traverse (type& m)
+ {
+ belongs (m);
+ }
+
+ void virtual_data_member::
+ belongs (type& m)
+ {
+ belongs (m, *this);
+ }
+
+ void virtual_data_member::
+ belongs (type& m, edge_dispatcher& d)
+ {
+ d.dispatch (m.belongs ());
+ }
}
diff --git a/odb/traversal/elements.hxx b/odb/traversal/elements.hxx
index ad9f4e9..967858f 100644
--- a/odb/traversal/elements.hxx
+++ b/odb/traversal/elements.hxx
@@ -233,6 +233,20 @@ namespace traversal
//
//
+ struct virtual_data_member: node<semantics::data_member>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ belongs (type&);
+
+ virtual void
+ belongs (type&, edge_dispatcher&);
+ };
+
+ //
+ //
struct unsupported_type: node<semantics::unsupported_type> {};
}
diff --git a/odb/validator.cxx b/odb/validator.cxx
index c051487..d346b25 100644
--- a/odb/validator.cxx
+++ b/odb/validator.cxx
@@ -73,7 +73,7 @@ namespace
// Make sure id or inverse member is not marked readonly since we
// depend on these three sets not having overlaps.
//
- if (readonly (m))
+ if (m.count ("readonly")) // context::readonly() also checks the class.
{
if (id (m))
{
@@ -172,7 +172,9 @@ namespace
{
if (id (m))
{
- if (id_ != 0)
+ if (id_ == 0)
+ id_ = &m;
+ else
{
os << m.file () << ":" << m.line () << ":" << m.column () << ":"
<< " error: multiple object id members" << endl;
@@ -182,13 +184,13 @@ namespace
valid_ = false;
}
- else
- id_ = &m;
}
if (version (m))
{
- if (optimistic_ != 0)
+ if (optimistic_ == 0)
+ optimistic_ = &m;
+ else
{
os << m.file () << ":" << m.line () << ":" << m.column () << ":"
<< " error: multiple version members" << endl;
@@ -200,8 +202,6 @@ namespace
valid_ = false;
}
- else
- optimistic_ = &m;
}
}
@@ -305,6 +305,7 @@ namespace
// Check bases.
//
bool base (false);
+ type* poly_root (0);
for (type::inherits_iterator i (c.inherits_begin ());
i != c.inherits_end ();
@@ -313,7 +314,37 @@ namespace
type& b (i->base ());
if (object (b))
+ {
base = true;
+
+ if (type* r = polymorphic (b))
+ {
+ if (poly_root == 0)
+ {
+ poly_root = r;
+ c.set ("polymorphic-base", &static_cast<semantics::class_&> (b));
+ }
+ // If poly_root and r are the same, then we have virtual
+ // inheritance. Though we don't support it at the moment.
+ //
+ else //if (poly_root != r)
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: persistent class '" << class_name (c) << "' "
+ << "derives from multiple polymorphic bases" << endl;
+
+ type& a (*poly_root);
+ os << a.file () << ":" << a.line () << ":" << a.column () << ":"
+ << " info: first polymorphic base defined here" << endl;
+
+ type& b (*r);
+ os << b.file () << ":" << b.line () << ":" << b.column () << ":"
+ << " info: second polymorphic base defined here" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
else if (view (b) || composite (b))
{
// @@ Should we use hint here?
@@ -347,8 +378,9 @@ namespace
if (id == 0)
{
- // An object without an id should either be abstract or explicitly
- // marked as such.
+ // An object without an id should either be reuse-abstract
+ // or explicitly marked as such. We check that it is not
+ // polymorphic below.
//
if (!(c.count ("id") || abstract (c)))
{
@@ -479,6 +511,36 @@ namespace
}
}
+ // Polymorphic inheritance.
+ //
+ if (c.count ("polymorphic") && poly_root == 0)
+ {
+ // Root of the hierarchy.
+ //
+
+ if (id == 0)
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: polymorphic class without an object id" << endl;
+
+ valid_ = false;
+ }
+
+ if (!TYPE_POLYMORPHIC_P (c.tree_node ()))
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: non-polymorphic class (class without virtual "
+ << "functions) cannot be declared polymorphic" << endl;
+
+ valid_ = false;
+ }
+
+ poly_root = &c;
+ }
+
+ if (poly_root != 0)
+ c.set ("polymorphic-root", poly_root);
+
// Check members.
//
member_.count_ = 0;
@@ -491,6 +553,18 @@ namespace
valid_ = false;
}
+
+ // Update features set based on this object.
+ //
+ if (class_file (c) == unit.file ())
+ {
+ if (poly_root != 0)
+ features.polymorphic_object = true;
+ else if (id == 0 && !abstract (c))
+ features.no_id_object = true;
+ else
+ features.simple_object = true;
+ }
}
virtual void
@@ -512,6 +586,34 @@ namespace
valid_ = false;
}
+ // Check that the callback function exist.
+ //
+ if (c.count ("callback"))
+ {
+ string name (c.get<string> ("callback"));
+ tree decl (
+ lookup_qualified_name (
+ c.tree_node (), get_identifier (name.c_str ()), false, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != BASELINK)
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ": "
+ << "error: unable to resolve member function '" << name << "' "
+ << "specified with '#pragma db callback' for class '"
+ << class_name (c) << "'" << endl;
+
+ valid_ = false;
+ }
+
+ // No const version for views.
+
+ //@@ Would be nice to check the signature of the function(s)
+ // instead of postponing it until the C++ compilation. Though
+ // we may still get C++ compilation errors because of const
+ // mismatch.
+ //
+ }
+
// Check bases.
//
for (type::inherits_iterator i (c.inherits_begin ());
@@ -584,6 +686,13 @@ namespace
valid_ = false;
}
+
+ // Update features set based on this view.
+ //
+ if (class_file (c) == unit.file ())
+ {
+ features.view = true;
+ }
}
virtual void
@@ -1068,11 +1177,12 @@ namespace
void validator::
validate (options const& ops,
+ features& f,
semantics::unit& u,
semantics::path const&,
unsigned short pass)
{
- auto_ptr<context> ctx (create_context (cerr, u, ops, 0));
+ auto_ptr<context> ctx (create_context (cerr, u, ops, f, 0));
bool valid (true);
diff --git a/odb/validator.hxx b/odb/validator.hxx
index ad9cb32..090cf86 100644
--- a/odb/validator.hxx
+++ b/odb/validator.hxx
@@ -6,6 +6,7 @@
#define ODB_VALIDATOR_HXX
#include <odb/options.hxx>
+#include <odb/features.hxx>
#include <odb/semantics/unit.hxx>
class validator
@@ -17,6 +18,7 @@ public:
//
void
validate (options const&,
+ features&,
semantics::unit&,
semantics::path const&,
unsigned short pass);