aboutsummaryrefslogtreecommitdiff
path: root/odb/relational/source.cxx
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 /odb/relational/source.cxx
parent68ff21c56fb287790440208907ad2a851a108f89 (diff)
Polymorphic inheritance support
Diffstat (limited to 'odb/relational/source.cxx')
-rw-r--r--odb/relational/source.cxx3245
1 files changed, 3245 insertions, 0 deletions
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