summaryrefslogtreecommitdiff
path: root/odb
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2011-10-15 11:33:40 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2011-10-21 19:25:06 +0200
commit6508eb18a20a2f0bc48374f3f0a352f1195cc95b (patch)
tree0aaf5a65d82b7d58721b0020c17885d16f178b3e /odb
parentb2f0cd834b8f5651985357f8acbe82edd7d11c63 (diff)
Add support for readonly members
New pragma: readonly. New test: readonly.
Diffstat (limited to 'odb')
-rw-r--r--odb/common.cxx77
-rw-r--r--odb/common.hxx6
-rw-r--r--odb/context.cxx139
-rw-r--r--odb/context.hxx41
-rw-r--r--odb/pragma.cxx32
-rw-r--r--odb/relational/header.hxx17
-rw-r--r--odb/relational/mysql/source.cxx74
-rw-r--r--odb/relational/oracle/source.cxx65
-rw-r--r--odb/relational/pgsql/source.cxx77
-rw-r--r--odb/relational/processor.cxx2
-rw-r--r--odb/relational/source.hxx251
-rw-r--r--odb/relational/sqlite/source.cxx74
-rw-r--r--odb/validator.cxx26
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);