diff options
-rw-r--r-- | odb/common.cxx | 77 | ||||
-rw-r--r-- | odb/common.hxx | 6 | ||||
-rw-r--r-- | odb/context.cxx | 139 | ||||
-rw-r--r-- | odb/context.hxx | 41 | ||||
-rw-r--r-- | odb/pragma.cxx | 32 | ||||
-rw-r--r-- | odb/relational/header.hxx | 17 | ||||
-rw-r--r-- | odb/relational/mysql/source.cxx | 74 | ||||
-rw-r--r-- | odb/relational/oracle/source.cxx | 65 | ||||
-rw-r--r-- | odb/relational/pgsql/source.cxx | 77 | ||||
-rw-r--r-- | odb/relational/processor.cxx | 2 | ||||
-rw-r--r-- | odb/relational/source.hxx | 251 | ||||
-rw-r--r-- | odb/relational/sqlite/source.cxx | 74 | ||||
-rw-r--r-- | odb/validator.cxx | 26 |
13 files changed, 663 insertions, 218 deletions
diff --git a/odb/common.cxx b/odb/common.cxx index 98f92c7..bc9e214 100644 --- a/odb/common.cxx +++ b/odb/common.cxx @@ -54,7 +54,21 @@ traverse_view (semantics::class_& c) void object_members_base:: traverse (semantics::data_member& m, semantics::class_& c) { + // We are starting from the member. Add an empty chain which + // corresponds to the scope that contains this member. + // + //member_scope_.push_back (class_inheritance_chain ()); + //member_path_.push_back (&m); + + member_scope_.push_back (class_inheritance_chain ()); + member_scope_.back ().push_back (&c); + traverse_composite_wrapper (&m, c, 0); + + member_scope_.pop_back (); + + //member_path_.pop_back (); + //member_scope_.pop_back (); } void object_members_base:: @@ -71,7 +85,18 @@ traverse (semantics::class_& c) } else if (k == class_composite) { + if (member_scope_.empty ()) + member_scope_.push_back (class_inheritance_chain ()); + + member_scope_.back ().push_back (&c); + traverse_composite_wrapper (0, c, 0); + + member_scope_.back ().pop_back (); + + if (member_scope_.back ().empty ()) + member_scope_.pop_back (); + return; } @@ -81,6 +106,11 @@ traverse (semantics::class_& c) if (context::top_object == 0) context::top_object = &c; + if (member_scope_.empty ()) + member_scope_.push_back (class_inheritance_chain ()); + + member_scope_.back ().push_back (&c); + if (build_table_prefix_) { // Don't reset the table prefix if we are traversing a base. @@ -114,6 +144,11 @@ traverse (semantics::class_& c) traverse_view (c); } + member_scope_.back ().pop_back (); + + if (member_scope_.back ().empty ()) + member_scope_.pop_back (); + if (prev == 0) context::top_object = 0; @@ -126,10 +161,15 @@ traverse (semantics::data_member& m) if (transient (m)) return; + om_.member_path_.push_back (&m); + semantics::type& t (m.type ()); if (semantics::class_* comp = context::composite_wrapper (t)) { + om_.member_scope_.push_back (class_inheritance_chain ()); + om_.member_scope_.back ().push_back (comp); + string old_flat_prefix, old_table_prefix, old_member_prefix; if (om_.build_flat_prefix_) @@ -165,6 +205,8 @@ traverse (semantics::data_member& m) if (om_.build_member_prefix_) om_.member_prefix_ = old_member_prefix; + + om_.member_scope_.pop_back (); } else if (semantics::type* c = context::container_wrapper (t)) { @@ -174,6 +216,8 @@ traverse (semantics::data_member& m) { om_.traverse_simple (m); } + + om_.member_path_.pop_back (); } void object_members_base:: @@ -248,12 +292,26 @@ traverse (semantics::data_member& m, string const& key_prefix, string const& default_name) { + // We are starting from the member. Add an empty chain which + // corresponds to the scope that contains this member. + // + //member_scope_.push_back (class_inheritance_chain ()); + //member_path_.push_back (&m); + + member_scope_.push_back (class_inheritance_chain ()); + member_scope_.back ().push_back (&c); + column_prefix_ = column_prefix (m, key_prefix, default_name); traverse_composite (&m, c); if (!member_.first_) flush (); + + member_scope_.pop_back (); + + //member_path_.pop_back (); + //member_scope_.pop_back (); } void object_columns_base:: @@ -284,6 +342,11 @@ traverse (semantics::class_& c) context::top_object = &c; } + if (member_scope_.empty ()) + member_scope_.push_back (class_inheritance_chain ()); + + member_scope_.back ().push_back (&c); + if (k == class_object) traverse_object (c); else if (k == class_view) @@ -291,6 +354,11 @@ traverse (semantics::class_& c) else if (k == class_composite) traverse_composite (0, c); + member_scope_.back ().pop_back (); + + if (member_scope_.back ().empty ()) + member_scope_.pop_back (); + if (k == class_object || k == class_view) { if (prev == 0) @@ -340,16 +408,23 @@ traverse (semantics::data_member& m) if (transient (m)) return; + oc_.member_path_.push_back (&m); + semantics::type& t (m.type ()); if (semantics::class_* comp = composite_wrapper (t)) { + oc_.member_scope_.push_back (class_inheritance_chain ()); + oc_.member_scope_.back ().push_back (comp); + string old_prefix (oc_.column_prefix_); oc_.column_prefix_ += column_prefix (m); oc_.traverse_composite (&m, *comp); oc_.column_prefix_ = old_prefix; + + oc_.member_scope_.pop_back (); } else if (container_wrapper (t)) { @@ -364,4 +439,6 @@ traverse (semantics::data_member& m) first_ = false; } } + + oc_.member_path_.pop_back (); } diff --git a/odb/common.hxx b/odb/common.hxx index 96b0935..679e25c 100644 --- a/odb/common.hxx +++ b/odb/common.hxx @@ -98,6 +98,9 @@ protected: table_prefix table_prefix_; std::string member_prefix_; + data_member_path member_path_; + data_member_scope member_scope_; + private: void init (bool build_flat_prefix, @@ -213,6 +216,9 @@ public: protected: string column_prefix_; + data_member_path member_path_; + data_member_scope member_scope_; + private: void init () diff --git a/odb/context.cxx b/odb/context.cxx index 45dd55b..00d894f 100644 --- a/odb/context.cxx +++ b/odb/context.cxx @@ -188,6 +188,58 @@ context () context* context::current_; bool context:: +readonly (data_member_path const& mp, data_member_scope const& ms) +{ + assert (mp.size () == ms.size ()); + + data_member_scope::const_reverse_iterator si (ms.rbegin ()); + + for (data_member_path::const_reverse_iterator pi (mp.rbegin ()); + pi != mp.rend (); + ++pi, ++si) + { + semantics::data_member& m (**pi); + + if (m.count ("readonly")) + return true; + + // Check if any of the classes in the inheritance chain for the + // class containing this member are readonly. + // + class_inheritance_chain const& ic (*si); + + assert (ic.back () == &m.scope ()); + + for (class_inheritance_chain::const_reverse_iterator ci (ic.rbegin ()); + ci != ic.rend (); + ++ci) + { + semantics::class_& c (**ci); + + if (c.count ("readonly")) + return true; + } + } + + return false; +} + +bool context:: +readonly (semantics::data_member& m) +{ + if (m.count ("readonly")) + return true; + + // Check if the whole class (object or composite value) is marked + // as readonly. + // + if (m.scope ().count ("readonly")) + return true; + + return false; +} + +bool context:: null (semantics::data_member& m) { semantics::type& t (m.type ()); @@ -388,8 +440,11 @@ composite_ (semantics::class_& c) { bool r (true); - //@@ This is bad. Did we add new value pragmas and forgot to - // account for them here? + // List of pragmas that disqualify a value type from being treated as + // composite. + // + //@@ Did we add new simple value pragmas and forgot to account for + // them here? // r = r && c.count ("value"); r = r && !c.count ("table"); @@ -968,34 +1023,6 @@ namespace { struct column_count_impl: object_members_base { - typedef context::column_count_type count_type; - - virtual void - traverse (semantics::class_& c) - { - if (c.count ("column-count")) - { - count_type const& bc (c.get<count_type> ("column-count")); - - c_.total += bc.total; - c_.id += bc.id; - c_.inverse += bc.inverse; - c_.readonly += bc.readonly; - } - else - { - count_type t (c_); - object_members_base::traverse (c); - - t.total = c_.total - t.total; - t.id = c_.id - t.id; - t.inverse = c_.inverse - t.inverse; - t.readonly = c_.readonly - t.readonly; - - c.set ("column-count", t); - } - } - virtual void traverse_simple (semantics::data_member& m) { @@ -1003,13 +1030,13 @@ namespace if (m.count ("id")) c_.id++; - - if (context::inverse (m)) + else if (context::inverse (m)) c_.inverse++; + else if (context::readonly (member_path_, member_scope_)) + c_.readonly++; } - private: - count_type c_; + context::column_count_type c_; }; } @@ -1020,6 +1047,7 @@ column_count (semantics::class_& c) { column_count_impl t; t.traverse (c); + c.set ("column-count", t.c_); } return c.get<column_count_type> ("column-count"); @@ -1030,34 +1058,39 @@ namespace struct has_a_impl: object_members_base { has_a_impl (unsigned short flags) - : r_ (false), flags_ (flags) + : r_ (0), flags_ (flags) { } - bool + size_t result () const { return r_; } virtual void - traverse_simple (semantics::data_member& m) + traverse_simple (semantics::data_member&) { - r_ = r_ || context::is_a (m, flags_); + if (context::is_a (member_path_, member_scope_, flags_)) + r_++; } virtual void - traverse_container (semantics::data_member& m, semantics::type& c) + traverse_container (semantics::data_member&, semantics::type& c) { // We don't cross the container boundaries (separate table). // - r_ = r_ || context::is_a ( - m, - flags_ & (context::test_container | - context::test_straight_container | - context::test_inverse_container), - context::container_vt (c), - "value"); + unsigned short f (flags_ & (context::test_container | + context::test_straight_container | + context::test_inverse_container | + context::test_readonly_container)); + + if (context::is_a (member_path_, + member_scope_, + f, + context::container_vt (c), + "value")) + r_++; } virtual void @@ -1070,19 +1103,22 @@ namespace } private: - bool r_; + size_t r_; unsigned short flags_; }; } bool context:: -is_a (semantics::data_member& m, +is_a (data_member_path const& mp, + data_member_scope const& ms, unsigned short f, semantics::type& t, string const& kp) { bool r (false); + semantics::data_member& m (*mp.back ()); + if (f & test_pointer) { r = r || object_pointer (t); @@ -1113,10 +1149,15 @@ is_a (semantics::data_member& m, r = r || (container_wrapper (m.type ()) && inverse (m, kp)); } + if (f & test_readonly_container) + { + r = r || (container_wrapper (m.type ()) && readonly (mp, ms)); + } + return r; } -bool context:: +size_t context:: has_a (semantics::class_& c, unsigned short flags) { has_a_impl impl (flags); diff --git a/odb/context.hxx b/odb/context.hxx index ffafb43..16be673 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -81,6 +81,17 @@ enum class_kind // typedef std::vector<semantics::data_member*> data_member_path; +// Class inheritance chain, from the most derived to base. +// +typedef std::vector<semantics::class_*> class_inheritance_chain; + +// A list of inheritance chains for a data member in an object. +// The first entry in this list would correspond to the object. +// All subsequent entries, if any, correspond to composite +// values. +// +typedef std::vector<class_inheritance_chain> data_member_scope; + // // Semantic graph context types. // @@ -328,10 +339,19 @@ public: return m.count ("id"); } - bool - readonly (semantics::data_member& m) + // The member scope is used to override readonly status when a readonly + // class (object or composite value) inherits from a readwrite base. + // + static bool + readonly (data_member_path const&, data_member_scope const&); + + static bool + readonly (semantics::data_member&); + + static bool + readonly (semantics::class_& c) { - return m.count ("readonly"); + return c.count ("readonly"); } bool @@ -544,22 +564,29 @@ public: static unsigned short const test_container = 0x08; static unsigned short const test_straight_container = 0x10; static unsigned short const test_inverse_container = 0x20; + static unsigned short const test_readonly_container = 0x40; static unsigned short const exclude_base = 0x8000; bool - is_a (semantics::data_member& m, unsigned short flags) + is_a (data_member_path const& mp, + data_member_scope const& ms, + unsigned short flags) { - return is_a (m, flags, m.type (), ""); + return is_a (mp, ms, flags, mp.back ()->type (), ""); } bool - is_a (semantics::data_member&, + is_a (data_member_path const&, + data_member_scope const&, unsigned short flags, semantics::type&, string const& key_prefix); - bool + // Return the number of matching entities. Can be uses as a just + // a bool value (0 means no match). + // + size_t has_a (semantics::class_&, unsigned short flags); public: diff --git a/odb/pragma.cxx b/odb/pragma.cxx index 56cf7c9..407f8f5 100644 --- a/odb/pragma.cxx +++ b/odb/pragma.cxx @@ -315,6 +315,18 @@ check_spec_decl_type (tree d, return false; } } + else if (p == "readonly") + { + // Readonly can be used for both data members and classes (object or + // composite value). + // + if (tc != FIELD_DECL && !CLASS_TYPE_P (d)) + { + error (l) << "name '" << name << "' in db pragma " << p << " does " + << "not refer to a data member or class" << endl; + return false; + } + } else if (p == "pointer" || p == "abstract" || p == "callback" || @@ -1407,6 +1419,18 @@ handle_pragma (cpp_reader* reader, tt = pragma_lex (&t); } + else if (p == "readonly") + { + // transient + // + + // Make sure we've got the correct declaration type. + // + if (decl != 0 && !check_spec_decl_type (decl, decl_name, p, loc)) + return; + + tt = pragma_lex (&t); + } else if (p == "transient") { // transient @@ -1565,6 +1589,7 @@ handle_pragma_qualifier (cpp_reader* reader, string const& p) p == "default" || p == "inverse" || p == "unordered" || + p == "readonly" || p == "transient") { handle_pragma (reader, p, 0, ""); @@ -1777,6 +1802,12 @@ handle_pragma_db_unordered (cpp_reader* r) } extern "C" void +handle_pragma_db_readonly (cpp_reader* r) +{ + handle_pragma_qualifier (r, "readonly"); +} + +extern "C" void handle_pragma_db_transient (cpp_reader* r) { handle_pragma_qualifier (r, "transient"); @@ -1843,6 +1874,7 @@ register_odb_pragmas (void*, void*) c_register_pragma_with_expansion ("db", "default", handle_pragma_db_default); c_register_pragma_with_expansion ("db", "inverse", handle_pragma_db_inverse); c_register_pragma_with_expansion ("db", "unordered", handle_pragma_db_unordered); + c_register_pragma_with_expansion ("db", "readonly", handle_pragma_db_readonly); c_register_pragma_with_expansion ("db", "transient", handle_pragma_db_transient); */ } diff --git a/odb/relational/header.hxx b/odb/relational/header.hxx index 4351db9..7190e49 100644 --- a/odb/relational/header.hxx +++ b/odb/relational/header.hxx @@ -538,19 +538,19 @@ namespace relational os << " container_type;"; os << "typedef odb::access::container_traits< container_type > " << - "container_traits;"; + "container_traits_type;"; switch (ck) { case ck_ordered: { - os << "typedef container_traits::index_type index_type;"; + os << "typedef container_traits_type::index_type index_type;"; break; } case ck_map: case ck_multimap: { - os << "typedef container_traits::key_type key_type;"; + os << "typedef container_traits_type::key_type key_type;"; } case ck_set: case ck_multiset: @@ -559,7 +559,7 @@ namespace relational } } - os << "typedef container_traits::value_type value_type;" + os << "typedef container_traits_type::value_type value_type;" << endl; // functions_type @@ -844,7 +844,7 @@ namespace relational // update // - if (!inverse) + if (!(inverse || readonly (member_path_, member_scope_))) os << "static void" << endl << "update (const container_type&," << endl << "const " << db << "::binding& id," << endl @@ -1169,9 +1169,10 @@ namespace relational // update () // - os << "static void" << endl - << "update (database&, const object_type&);" - << endl; + if (!readonly (c)) + os << "static void" << endl + << "update (database&, const object_type&);" + << endl; // erase () // diff --git a/odb/relational/mysql/source.cxx b/odb/relational/mysql/source.cxx index 103c301..25fac41 100644 --- a/odb/relational/mysql/source.cxx +++ b/odb/relational/mysql/source.cxx @@ -200,9 +200,19 @@ namespace relational if (inverse (mi.m, key_prefix_)) os << "if (sk == statement_select)" << "{"; - else if (id (mi.m) || readonly (mi.m)) - os << "if (sk != statement_update)" - << "{"; + // 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; @@ -213,8 +223,11 @@ namespace relational { if (var_override_.empty ()) { - if (semantics::class_* c = composite (mi.t)) + 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"; @@ -223,7 +236,7 @@ namespace relational // insert = total - inverse // update = total - inverse - readonly // - if (cc.inverse != 0 || cc.readonly != 0) + if (cc.inverse != 0 || (!ro && cc.readonly != 0)) { os << " - (" << endl << "sk == statement_select ? 0 : "; @@ -231,7 +244,7 @@ namespace relational if (cc.inverse != 0) os << cc.inverse << "UL" << endl; - if (cc.readonly != 0) + if (!ro && cc.readonly != 0) { if (cc.inverse != 0) os << " + "; @@ -249,7 +262,23 @@ namespace relational else os << "n++;"; - if (inverse (mi.m, key_prefix_) || id (mi.m) || readonly (mi.m)) + bool block (false); + + // The same logic as in pre(). + // + if (inverse (mi.m, key_prefix_)) + 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; @@ -564,10 +593,18 @@ namespace relational os << "// " << name << endl << "//" << endl; - if (id (mi.m) || readonly (mi.m)) - // The block scope is added later, if necessary. - // - os << "if (sk == statement_insert)"; + // If the whole class is readonly, then we will never be + // called with sk == statement_update. + // + 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_insert)"; + } } // If this is a wrapped composite value, then we need to @@ -583,7 +620,10 @@ namespace relational } if (composite (mi.t)) + { + os << "{"; traits = "composite_value_traits< " + mi.fq_type () + " >"; + } else { // When handling a pointer, mi.t is the id type of the referenced @@ -655,7 +695,9 @@ namespace relational virtual void post (member_info& mi) { - if (!composite (mi.t)) + if (composite (mi.t)) + os << "}"; + else { // When handling a pointer, mi.t is the id type of the referenced // object. @@ -677,15 +719,11 @@ namespace relational virtual void traverse_composite (member_info& mi) { - // Should be a single statement or a block. - // os << "if (" << traits << "::init (" << endl << "i." << mi.var << "value," << endl << member << "," << endl - << "sk))" - << "{" - << "grew = true;" - << "}"; + << "sk))" << endl + << "grew = true;"; } virtual void diff --git a/odb/relational/oracle/source.cxx b/odb/relational/oracle/source.cxx index dd3fe8b..ad58bb0 100644 --- a/odb/relational/oracle/source.cxx +++ b/odb/relational/oracle/source.cxx @@ -93,9 +93,19 @@ namespace relational if (inverse (mi.m, key_prefix_)) os << "if (sk == statement_select)" << "{"; - else if (id (mi.m) || readonly (mi.m)) - os << "if (sk != statement_update)" - << "{"; + // 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; @@ -106,9 +116,11 @@ namespace relational { if (var_override_.empty ()) { - if (semantics::class_* c = composite (mi.t)) + 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"; @@ -117,7 +129,7 @@ namespace relational // insert = total - inverse // update = total - inverse - readonly // - if (cc.inverse != 0 || cc.readonly != 0) + if (cc.inverse != 0 || (!ro && cc.readonly != 0)) { os << " - (" << endl << "sk == statement_select ? 0 : "; @@ -125,7 +137,7 @@ namespace relational if (cc.inverse != 0) os << cc.inverse << "UL" << endl; - if (cc.readonly != 0) + if (!ro && cc.readonly != 0) { if (cc.inverse != 0) os << " + "; @@ -143,7 +155,23 @@ namespace relational else os << "n++;"; - if (inverse (mi.m, key_prefix_) || id (mi.m) || readonly (mi.m)) + bool block (false); + + // The same logic as in pre(). + // + if (inverse (mi.m, key_prefix_)) + 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; @@ -313,10 +341,18 @@ namespace relational os << "// " << name << endl << "//" << endl; - if (id (mi.m) || readonly (mi.m)) - // The block scope is added later, if necessary. - // - os << "if (sk == statement_insert)"; + // If the whole class is readonly, then we will never be + // called with sk == statement_update. + // + 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_insert)"; + } } // If this is a wrapped composite value, then we need to @@ -332,7 +368,10 @@ namespace relational } if (composite (mi.t)) + { + os << "{"; traits = "composite_value_traits< " + mi.fq_type () + " >"; + } else { // When handling a pointer, mi.t is the id type of the referenced @@ -404,7 +443,9 @@ namespace relational virtual void post (member_info& mi) { - if (!composite (mi.t)) + if (composite (mi.t)) + os << "}"; + else { // When handling a pointer, mi.t is the id type of the referenced // object. diff --git a/odb/relational/pgsql/source.cxx b/odb/relational/pgsql/source.cxx index c5df9bd..8547df3 100644 --- a/odb/relational/pgsql/source.cxx +++ b/odb/relational/pgsql/source.cxx @@ -118,7 +118,8 @@ namespace relational if (inverse (m) && sk_ != statement_select) return false; - if ((id (m) || readonly (m)) && sk_ == statement_update) + if ((id (m) || readonly (member_path_, member_scope_)) && + sk_ == statement_update) return false; if (!first) @@ -166,9 +167,19 @@ namespace relational if (inverse (mi.m, key_prefix_)) os << "if (sk == statement_select)" << "{"; - else if (id (mi.m) || readonly (mi.m)) - os << "if (sk != statement_update)" - << "{"; + // 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; @@ -179,8 +190,11 @@ namespace relational { if (var_override_.empty ()) { - if (semantics::class_* c = composite (mi.t)) + 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"; @@ -189,7 +203,7 @@ namespace relational // insert = total - inverse // update = total - inverse - readonly // - if (cc.inverse != 0 || cc.readonly != 0) + if (cc.inverse != 0 || (!ro && cc.readonly != 0)) { os << " - (" << endl << "sk == statement_select ? 0 : "; @@ -197,7 +211,7 @@ namespace relational if (cc.inverse != 0) os << cc.inverse << "UL" << endl; - if (cc.readonly != 0) + if (!ro && cc.readonly != 0) { if (cc.inverse != 0) os << " + "; @@ -215,7 +229,23 @@ namespace relational else os << "n++;"; - if (inverse (mi.m, key_prefix_) || id (mi.m) || readonly (mi.m)) + bool block (false); + + // The same logic as in pre(). + // + if (inverse (mi.m, key_prefix_)) + 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; @@ -469,10 +499,18 @@ namespace relational os << "// " << name << endl << "//" << endl; - if (id (mi.m) || readonly (mi.m)) - // The block scope is added later, if necessary. - // - os << "if (sk == statement_insert)"; + // If the whole class is readonly, then we will never be + // called with sk == statement_update. + // + 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_insert)"; + } } // If this is a wrapped composite value, then we need to @@ -488,7 +526,10 @@ namespace relational } if (composite (mi.t)) + { + os << "{"; traits = "composite_value_traits< " + mi.fq_type () + " >"; + } else { // When handling a pointer, mi.t is the id type of the referenced @@ -560,7 +601,9 @@ namespace relational virtual void post (member_info& mi) { - if (!composite (mi.t)) + if (composite (mi.t)) + os << "}"; + else { // When handling a pointer, mi.t is the id type of the referenced // object. @@ -582,15 +625,11 @@ namespace relational virtual void traverse_composite (member_info& mi) { - // Should be a single statement or a block. - // os << "if (" << traits << "::init (" << endl << "i." << mi.var << "value," << endl << member << "," << endl - << "sk))" - << "{" - << "grew = true;" - << "}"; + << "sk))" << endl + << "grew = true;"; } virtual void diff --git a/odb/relational/processor.cxx b/odb/relational/processor.cxx index 6571d22..d1c3cd8 100644 --- a/odb/relational/processor.cxx +++ b/odb/relational/processor.cxx @@ -122,6 +122,8 @@ namespace relational semantics::type& t (m.type ()); + // Handle wrappers. + // semantics::type* wt (0); semantics::names* wh (0); if (process_wrapper (t)) diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx index 55f94f5..3ce3676 100644 --- a/odb/relational/source.hxx +++ b/odb/relational/source.hxx @@ -78,7 +78,8 @@ namespace relational if (im != 0 && sk_ != statement_select) return false; - if ((id (m) || readonly (m)) && sk_ == statement_update) + if ((id (m) || readonly (member_path_, member_scope_)) && + sk_ == statement_update) return false; if (!first) @@ -546,6 +547,16 @@ namespace relational os << "// " << c.name () << " base" << endl << "//" << endl; + // If the derived class is readonly, then we will never be + // called with sk == statement_update. + // + bool ro (readonly (c)); + bool check (ro && !readonly (*context::top_object)); + + if (check) + os << "if (sk != statement_update)" + << "{"; + os << (obj ? "object" : "composite_value") << "_traits< " << c.fq_name () << " >::bind (b + n, i, sk);"; @@ -557,7 +568,7 @@ namespace relational // insert = total - inverse // update = total - inverse - id - readonly // - if (cc.inverse != 0 || cc.id != 0 || cc.readonly != 0) + if (cc.inverse != 0 || (!ro && (cc.id != 0 || cc.readonly != 0))) { os << " - (" << endl << "sk == statement_select ? 0 : "; @@ -565,7 +576,7 @@ namespace relational if (cc.inverse != 0) os << cc.inverse << "UL" << endl; - if (cc.id != 0 || cc.readonly != 0) + if (!ro && (cc.id != 0 || cc.readonly != 0)) { if (cc.inverse != 0) os << " + "; @@ -578,8 +589,12 @@ namespace relational os << ")"; } - os << ";" - << endl; + os << ";"; + + if (check) + os << "}"; + else + os << endl; } }; @@ -684,11 +699,25 @@ namespace relational return; os << "// " << c.name () << " base" << endl - << "//" << endl - << "if (" << (obj ? "object" : "composite_value") << "_traits< " << + << "//" << endl; + + // If the derived class is readonly, then we will never be + // called with sk == statement_update. + // + bool check (readonly (c) && !readonly (*context::top_object)); + + if (check) + os << "if (sk != statement_update)" + << "{"; + + os << "if (" << (obj ? "object" : "composite_value") << "_traits< " << c.fq_name () << " >::init (i, o, sk))" << endl - << "grew = true;" - << endl; + << "grew = true;"; + + if (check) + os << "}"; + else + os << endl; } }; @@ -855,8 +884,11 @@ namespace relational if (generate_grow) grow = grow || context::grow (m, vt, "value"); - bool eager_ptr (is_a (m, test_eager_pointer, vt, "value")); - + bool eager_ptr (is_a (member_path_, + member_scope_, + test_eager_pointer, + vt, + "value")); if (!eager_ptr) { if (semantics::class_* cvt = composite_wrapper (vt)) @@ -1684,7 +1716,7 @@ namespace relational if (ck == ck_ordered) os << "fs.ordered (" << (ordered ? "true" : "false") << ");"; - os << "container_traits::persist (c, fs);" + os << "container_traits_type::persist (c, fs);" << "}"; } @@ -1751,12 +1783,12 @@ namespace relational if (ck == ck_ordered) os << "fs.ordered (" << (ordered ? "true" : "false") << ");"; - os << "container_traits::load (c, more, fs);" + os << "container_traits_type::load (c, more, fs);" << "}"; // update // - if (!inverse) + if (!(inverse || readonly (member_path_, member_scope_))) { os << "void " << scope << "::" << endl << "update (const container_type& c," << endl @@ -1790,7 +1822,7 @@ namespace relational if (ck == ck_ordered) os << "fs.ordered (" << (ordered ? "true" : "false") << ");"; - os << "container_traits::update (c, fs);" + os << "container_traits_type::update (c, fs);" << "}"; } @@ -1817,7 +1849,7 @@ namespace relational if (ck == ck_ordered) os << "fs.ordered (" << (ordered ? "true" : "false") << ");"; - os << "container_traits::erase (fs);" + os << "container_traits_type::erase (fs);" << "}"; } } @@ -1982,7 +2014,7 @@ namespace relational } case update_call: { - if (!inverse) + if (!(inverse || readonly (member_path_, member_scope_))) os << traits << "::update (" << endl << obj_name << "," << endl << "idb," << endl @@ -2106,12 +2138,16 @@ namespace relational if (c.file () != unit.file ()) return; + context::top_object = context::cur_object = &c; + if (object (c)) traverse_object (c); else if (view (c)) traverse_view (c); else if (composite (c)) traverse_composite (c); + + context::top_object = context::cur_object = 0; } // @@ -2206,8 +2242,25 @@ namespace relational // // Containers (abstract and concrete). // - bool straight_containers (has_a (c, test_straight_container)); - bool containers (straight_containers || has_a (c, test_container)); + 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) { @@ -2263,8 +2316,13 @@ namespace relational << "ODB_POTENTIALLY_UNUSED (sk);" << endl << "using namespace " << db << ";" - << endl - << "std::size_t n (0);" + << endl; + + if (readonly (c)) + os << "assert (sk != statement_update);" + << endl; + + os << "std::size_t n (0);" << endl; inherits (c, bind_base_inherits_); @@ -2295,8 +2353,13 @@ namespace relational << "ODB_POTENTIALLY_UNUSED (sk);" << endl << "using namespace " << db << ";" - << endl - << "bool grew (false);" + << endl; + + if (readonly (c)) + os << "assert (sk != statement_update);" + << endl; + + os << "bool grew (false);" << endl; inherits (c, init_image_base_inherits_); @@ -2560,72 +2623,75 @@ namespace relational // update () // - os << "void " << traits << "::" << endl - << "update (database&, const object_type& obj)" - << "{" - << "using namespace " << db << ";" - << endl - << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" - << "object_statements< object_type >& sts (" << endl - << "conn.statement_cache ().find_object<object_type> ());" - << endl; - - if (cc.total != cc.id + cc.inverse + cc.readonly) + if (!readonly (c)) { - // Initialize id image. - // - os << "id_image_type& i (sts.id_image ());" - << "init (i, obj." << id->name () << ");" + os << "void " << traits << "::" << endl + << "update (database&, const object_type& obj)" + << "{" + << "using namespace " << db << ";" + << endl + << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << "object_statements< object_type >& sts (" << endl + << "conn.statement_cache ().find_object<object_type> ());" << 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) + { + // Initialize id image. + // + os << "id_image_type& i (sts.id_image ());" + << "init (i, obj." << id->name () << ");" + << endl; - // Initialize data image. - // - os << "image_type& im (sts.image ());" - << "binding& imb (sts.update_image_binding ());" - << endl - << "if (init (im, obj, statement_update))" << endl - << "im.version++;" - << endl - << "if (im.version != sts.update_image_version () || " << - "imb.version == 0)" - << "{" - << "bind (imb.bind, im, statement_update);" - << "sts.update_image_version (im.version);" - << "imb.version++;" - << "}" - << "sts.update_statement ().execute ();"; - } - 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 ();"; + 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 (straight_containers) - os << endl - << "binding& idb (sts.id_image_binding ());"; - } + // Initialize data image. + // + os << "image_type& im (sts.image ());" + << "binding& imb (sts.update_image_binding ());" + << endl + << "if (init (im, obj, statement_update))" << endl + << "im.version++;" + << endl + << "if (im.version != sts.update_image_version () || " << + "imb.version == 0)" + << "{" + << "bind (imb.bind, im, statement_update);" + << "sts.update_image_version (im.version);" + << "imb.version++;" + << "}" + << "sts.update_statement ().execute ();"; + } + 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 ();"; - if (straight_containers) - { - os << endl; - instance<container_calls> t (container_calls::update_call); - t->traverse (c); - } + if (straight_readwrite_containers) + os << endl + << "binding& idb (sts.id_image_binding ());"; + } - os << "}"; + if (straight_readwrite_containers) + { + os << endl; + instance<container_calls> t (container_calls::update_call); + t->traverse (c); + } + + os << "}"; + } // erase () // @@ -3861,8 +3927,13 @@ namespace relational << "ODB_POTENTIALLY_UNUSED (sk);" << endl << "using namespace " << db << ";" - << endl - << "std::size_t n (0);" + << endl; + + if (readonly (c)) + os << "assert (sk != statement_update);" + << endl; + + os << "std::size_t n (0);" << "ODB_POTENTIALLY_UNUSED (n);" << endl; @@ -3882,8 +3953,13 @@ namespace relational << "ODB_POTENTIALLY_UNUSED (sk);" << endl << "using namespace " << db << ";" - << endl - << "bool grew (false);" + << endl; + + if (readonly (c)) + os << "assert (sk != statement_update);" + << endl; + + os << "bool grew (false);" << endl; inherits (c, init_image_base_inherits_); @@ -3950,7 +4026,8 @@ namespace relational { extra_pre (); - os << "#include <cstring> // std::memcpy" << endl + os << "#include <cassert>" << endl + << "#include <cstring> // std::memcpy" << endl << endl; os << "#include <odb/cache-traits.hxx>" << endl; diff --git a/odb/relational/sqlite/source.cxx b/odb/relational/sqlite/source.cxx index 89173dd..92c0941 100644 --- a/odb/relational/sqlite/source.cxx +++ b/odb/relational/sqlite/source.cxx @@ -51,9 +51,19 @@ namespace relational if (inverse (mi.m, key_prefix_)) os << "if (sk == statement_select)" << "{"; - else if (id (mi.m) || readonly (mi.m)) - os << "if (sk != statement_update)" - << "{"; + // 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; @@ -64,8 +74,11 @@ namespace relational { if (var_override_.empty ()) { - if (semantics::class_* c = composite (mi.t)) + 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"; @@ -74,7 +87,7 @@ namespace relational // insert = total - inverse // update = total - inverse - readonly // - if (cc.inverse != 0 || cc.readonly != 0) + if (cc.inverse != 0 || (!ro && cc.readonly != 0)) { os << " - (" << endl << "sk == statement_select ? 0 : "; @@ -82,7 +95,7 @@ namespace relational if (cc.inverse != 0) os << cc.inverse << "UL" << endl; - if (cc.readonly != 0) + if (!ro && cc.readonly != 0) { if (cc.inverse != 0) os << " + "; @@ -100,7 +113,23 @@ namespace relational else os << "n++;"; - if (inverse (mi.m, key_prefix_) || id (mi.m) || readonly (mi.m)) + bool block (false); + + // The same logic as in pre(). + // + if (inverse (mi.m, key_prefix_)) + 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; @@ -272,10 +301,18 @@ namespace relational os << "// " << name << endl << "//" << endl; - if (id (mi.m) || readonly (mi.m)) - // The block scope is added later, if necessary. - // - os << "if (sk == statement_insert)"; + // If the whole class is readonly, then we will never be + // called with sk == statement_update. + // + 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_insert)"; + } } // If this is a wrapped composite value, then we need to @@ -291,7 +328,10 @@ namespace relational } if (composite (mi.t)) + { + os << "{"; traits = "composite_value_traits< " + mi.fq_type () + " >"; + } else { // When handling a pointer, mi.t is the id type of the referenced @@ -363,7 +403,9 @@ namespace relational virtual void post (member_info& mi) { - if (!composite (mi.t)) + if (composite (mi.t)) + os << "}"; + else { // When handling a pointer, mi.t is the id type of the referenced // object. @@ -384,15 +426,11 @@ namespace relational virtual void traverse_composite (member_info& mi) { - // Should be a single statement or a block. - // os << "if (" << traits << "::init (" << endl << "i." << mi.var << "value," << endl << member << "," << endl - << "sk))" - << "{" - << "grew = true;" - << "}"; + << "sk))" << endl + << "grew = true;"; } virtual void diff --git a/odb/validator.cxx b/odb/validator.cxx index c12bff0..ffa8aec 100644 --- a/odb/validator.cxx +++ b/odb/validator.cxx @@ -71,6 +71,32 @@ namespace valid_ = false; } + // Make sure id or inverse member is not marked readonly since we + // depend on these three sets not having overlaps. Once we support + // composite ids, we will also need to make sure there are no + // nested readonly members (probably move it to pass 2 and use + // column_count()). + // + if (m.count ("readonly")) + { + if (m.count ("id")) + { + cerr << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: object id should not be declared readonly" << endl; + + valid_ = false; + } + + if (m.count ("inverse")) + { + cerr << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: inverse object pointer should not be declared " + << "readonly" << endl; + + valid_ = false; + } + } + // Resolve null overrides. // override_null (m); |