From 8d89bf37dd4ef3cb7373e1841ff57a53916fff0d Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 21 Mar 2012 08:36:21 +0200 Subject: Polymorphic inheritance support --- odb/relational/source.hxx | 2634 +++++++++------------------------------------ 1 file changed, 517 insertions(+), 2117 deletions(-) (limited to 'odb/relational/source.hxx') 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 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; + names (c); - if (!i->alias.empty ()) - line += (need_alias_as ? " AS " : " ") + i->alias; - - 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 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 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 t (c, depth, alias); + t->traverse (c); } } private: bool query_; + size_t depth_; string table_; semantics::data_member& id_; instance id_cols_; - - struct join - { - string table; - string alias; - string cond; - }; - - typedef std::vector joins; - - joins joins_; }; // @@ -637,6 +794,146 @@ namespace relational string arg_override_; }; + template + struct bind_member_impl: bind_member, virtual member_base_impl + { + typedef bind_member_impl base_impl; + + bind_member_impl (base const& x) + : base (x) + { + } + + typedef typename member_base_impl::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 @@ -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 (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,2024 +3157,53 @@ 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 + " >"); + traverse_object (type& c); - bool has_ptr (has_a (c, test_pointer)); + // + // view + // - 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. + virtual void + view_extra (type&) + { + } - semantics::data_member* optimistic (context::optimistic (c)); + virtual void + view_query_statement_ctor_args (type&) + { + os << "sts.connection ()," << endl + << "qs.clause ()," << endl + << "qs.parameters_binding ()," << endl + << "imb"; + } - bool grow (false); - bool grow_id (false); + virtual void + traverse_view (type& c); - if (generate_grow) - { - grow = context::grow (c); - grow_id = (id ? context::grow (*id) : false) || - (optimistic ? context::grow (*optimistic) : false); - } + struct expression + { + explicit + expression (std::string const& v): kind (literal), value (v) {} + expression (view_object* vo): kind (pointer), vo (vo) {} - column_count_type const& cc (column_count (c)); + enum kind_type {literal, pointer}; - os << "// " << class_name (c) << endl - << "//" << endl - << endl; + kind_type kind; + std::string value; + data_member_path member_path; + view_object* vo; + }; - object_extra (c); - - // - // Query. - // - - if (options.generate_query ()) - { - // query_columns_base - // - if (has_ptr) - { - instance 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 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 cm; - cm->traverse (c); - - os << (containers ? "\n" : "") - << "container_statement_cache_type (" << db << "::connection&" << - (containers ? " c" : "") << ")"; - - instance 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 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 qp; - - persist_statement_extra (c, *qp, persist_after_columns); - - if (!dv) - { - string values; - instance 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 id_cols; - id_cols->traverse (*id); - - // find_statement - // - { - statement_columns sc; - { - statement_kind sk (statement_select); // Imperfect forwarding. - instance 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 j (c, f); // @@ (im)perfect forwarding - j->traverse (c); - j->write (); - - instance 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 qp; - - statement_columns sc; - { - query_parameters* p (qp.get ()); // Imperfect forwarding. - statement_kind sk (statement_update); // Imperfect forwarding. - instance 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 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 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 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 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 ());" - << "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 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 ());" - << 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 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 ());" - << 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 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 ());" - << 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 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_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 ());" - << 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 ());" - << 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 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 ());" - << 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 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_result_impl_no_id"); - - os << endl - << "shared_ptr< odb::" << result_type << " > r (" << endl - << "new (shared) " << db << "::" << result_type << " (" << endl - << "q, st, sts));" - << endl - << "return result (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); - } - - // - // view - // - - virtual void - view_extra (type&) - { - } - - virtual void - view_query_statement_ctor_args (type&) - { - os << "sts.connection ()," << endl - << "qs.clause ()," << endl - << "qs.parameters_binding ()," << endl - << "imb"; - } - - 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 ("object-count")); - - if (obj_count != 0) - { - view_objects& objs (c.get ("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 ("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 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 ("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 c_cols; // Container columns. - instance 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 = < - } - 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 = < - } - 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 c_cols; // Container columns. - instance 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 = < - } - } - 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 = < - } - } - - 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 l_cols (col_prefix); - instance 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 ());" - << 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 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 > r (" << endl - << "new (shared) " << db << "::view_result_impl (" << endl - << "qs, st, sts));" - << endl - << "return result (r);" - << "}"; - } - - struct expression - { - explicit - expression (std::string const& v): kind (literal), value (v) {} - expression (view_object* vo): kind (pointer), vo (vo) {} - - enum kind_type {literal, pointer}; - - kind_type kind; - std::string value; - data_member_path member_path; - view_object* vo; - }; - - expression - translate_expression (type& c, - cxx_tokens const&, - tree scope, - location_t loc, - string const& prag, - bool* placeholder = 0); - // - // composite - // + expression + translate_expression (type& c, + cxx_tokens const&, + tree scope, + location_t loc, + string const& prag, + bool* placeholder = 0); + // + // composite + // virtual void traverse_composite (type& c) @@ -4950,6 +3317,9 @@ namespace relational traversal::inherits grow_base_inherits_; instance grow_member_; traversal::names grow_member_names_; + instance grow_version_member_; + instance grow_discriminator_member_; + instance bind_base_; traversal::inherits bind_base_inherits_; @@ -4957,6 +3327,7 @@ namespace relational traversal::names bind_member_names_; instance bind_id_member_; instance bind_version_member_; + instance bind_discriminator_member_; instance init_image_base_; traversal::inherits init_image_base_inherits_; @@ -4973,6 +3344,9 @@ namespace relational instance init_id_value_member_; instance init_version_value_member_; + instance init_named_version_value_member_; + instance init_discriminator_value_member_; + instance init_named_discriminator_value_member_; instance schema_; }; @@ -4987,11 +3361,18 @@ namespace relational extra_pre (); os << "#include " << endl - << "#include // std::memcpy" << endl - << endl; + << "#include // std::memcpy" << endl; + + if (features.polymorphic_object) + os << "#include " << endl; + + os << endl; os << "#include " << endl; + if (features.polymorphic_object) + os << "#include " << endl; + if (embedded_schema) os << "#include " << endl; @@ -5000,23 +3381,42 @@ namespace relational os << endl; - os << "#include " << endl - << "#include " << endl + os << "#include " << endl << "#include " << endl << "#include " << endl << "#include " << endl << "#include " << endl - << "#include " << endl - << "#include " << endl; + << "#include " << endl; - if (options.generate_query ()) + if (features.simple_object) + os << "#include " << endl; + + if (features.polymorphic_object) + os << "#include " << endl; + + if (features.no_id_object) + os << "#include " << endl; + + if (features.view) os << "#include " << endl; os << "#include " << endl << "#include " << endl; if (options.generate_query ()) - os << "#include " << endl; + { + if (features.simple_object) + os << "#include " << endl; + + if (features.polymorphic_object) + os << "#include " << endl; + + if (features.no_id_object) + os << "#include " << endl; + + if (features.view) + os << "#include " << endl; + } extra_post (); -- cgit v1.1