From 8d82c02a90cd7cc3f161828624db5b973585c34c Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 27 Oct 2011 15:16:49 +0200 Subject: Add support for persistent classes without object ids New pragma id (object). New test: common/no-id. --- NEWS | 5 + doc/manual.xhtml | 86 +++++++-- odb/pragma.cxx | 59 +++++-- odb/relational/header.hxx | 119 ++++++++----- odb/relational/inline.hxx | 34 ++-- odb/relational/oracle/source.cxx | 2 +- odb/relational/pgsql/source.cxx | 34 ++-- odb/relational/processor.cxx | 19 +- odb/relational/source.hxx | 372 +++++++++++++++++++++------------------ odb/validator.cxx | 76 +++++++- 10 files changed, 522 insertions(+), 284 deletions(-) diff --git a/NEWS b/NEWS index 8e835b3..6de41f6 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,11 @@ Version 1.7.0 Section 11.3.6 "readonly (composite value)", and Section 11.4.10 "readonly (data member)" in the ODB manual. + * Support for persistent classes without object ids. Such objects have to + be explicitly declared as not having object id and they have limited + functionality. For more information, refer to Section 11.1.5 "id", in + the ODB manual. + Version 1.6.0 * New concept, view, is a C++ class that embodies a light-weight, read- diff --git a/doc/manual.xhtml b/doc/manual.xhtml index 6c1c57c..f14febd 100644 --- a/doc/manual.xhtml +++ b/doc/manual.xhtml @@ -438,7 +438,8 @@ for consistency. 11.1.2pointer 11.1.3abstract 11.1.4readonly - 11.1.5callback + 11.1.5id + 11.1.6callback @@ -1925,10 +1926,10 @@ max age: 33

An object is an independent entity. It can be stored, updated, and deleted in the database independent of other objects. - An object has an identifier, called object id, that is - unique among all instances of an object type within a database. In - contrast, a value can only be stored in the database as part of an - object and doesn't have its own unique identifier.

+ Normally, an object has an identifier, called object id, + that is unique among all instances of an object type within a + database. In contrast, a value can only be stored in the database + as part of an object and doesn't have its own unique identifier.

An object consists of data members which are either values (Chapter 7, "Value Types"), pointers @@ -2030,9 +2031,13 @@ class person }; -

These two pragmas are the minimum required to declare a - persistent class. Other pragmas can be used to fine-tune - the database-related properties of a class and its +

While it is possible to declare a persistent class without an + object id, such a class will have limited functionality + (Section 11.1.5, "id"). + +

The above two pragmas are the minimum required to declare a + persistent class with an object id. Other pragmas can be used to + fine-tune the database-related properties of a class and its members (Chapter 11, "ODB Pragma Language").

Normally, an object class should define the default constructor. The @@ -7496,9 +7501,15 @@ class person + id + persistent class has no object id + 11.1.5 + + + callback database operations callback - 11.1.5 + 11.1.6 @@ -7631,7 +7642,46 @@ class person as well as composite value types (Section 11.3.6, "readonly") as read-only.

-

11.1.5 callback

+

11.1.5 id

+ +

The id specifier specifies that the persistent class + has no object id. It should be followed by opening and closing + parenthesis. For example:

+ +
+#pragma db object id()
+class person
+{
+  ...
+};
+  
+ +

A persistent class without an object id has limited functionality. + Such a class cannot be loaded with the database::load() + or database::find() functions (Section 3.8, + "Loading Persistent Objects"), updated with the + database::update() function (Section 3.9, + "Updating Persistent Objects"), or deleted with the + database::erase() function (Section 3.10, + "Deleting Persistent Objects"). To load and delete + objects without ids you can use the database::query() + (Chapter 4, "Querying the Database") and + database::erase_query() (Section 3.10, + "Deleting Persistent Objects") functions, respectively. + There is no way to update such objects except by using native SQL + statements (Section 3.11, "Executing Native SQL + Statements").

+ +

Furthermore, persistent classes without object ids cannot have container + data members nor can they be used in object relationships. Such objects + are not entered into the session object cache + (Section 10.1, "Object Cache") either.

+ +

To declare a persistent class with an object id, use the data member + id specifier (Section 11.4.1, + "id").

+ +

11.1.6 callback

The callback specifier specifies the persist class member function that should be called before and after a @@ -7840,7 +7890,7 @@ private: result iteration. The semantics of the callback specifier for a view are similar to those of the callback specifier for an object - (Section 11.1.5, "callback") + (Section 11.1.6, "callback") except that the only events that can trigger a callback call in the case of a view are pre_load and post_load.

@@ -8616,8 +8666,8 @@ typedef std::map<unsigned short, float> age_weight_map;

11.4.1 id

The id specifier specifies that a data member contains - the object id. Every persistent class must have a member designated - as an object's identifier. For example:

+ the object id. In a relational database, an identifier member is + mapped to a primary key. For example:

 #pragma db object
@@ -8630,11 +8680,13 @@ class person
 };
   
-

In a relational database, an identifier member is mapped to a - primary key.

+

Normally, every persistent class has a data member designated as an + object's identifier. However, it is possible to declare a + persistent class without an id using the object id + specifier (Section 11.1.5, "id").

-

Note also that the id specifier cannot be specified - for data members of composite value types or views.

+

Note also that the id specifier cannot be used for data + members of composite value types or views.

11.4.2 auto

diff --git a/odb/pragma.cxx b/odb/pragma.cxx index 407f8f5..eee2677 100644 --- a/odb/pragma.cxx +++ b/odb/pragma.cxx @@ -302,11 +302,21 @@ check_spec_decl_type (tree d, { int tc (TREE_CODE (d)); - if (p == "id" || - p == "auto" || - p == "column" || - p == "inverse" || - p == "transient") + if (p == "id") + { + // Id can be used for both data members and objects. + // + 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 == "auto" || + p == "column" || + p == "inverse" || + p == "transient") { if (tc != FIELD_DECL) { @@ -473,6 +483,7 @@ add_pragma (pragma const& prag, tree decl) static void handle_pragma (cpp_reader* reader, string const& p, + string const& qualifier, tree decl, string const& decl_name) { @@ -860,8 +871,8 @@ handle_pragma (cpp_reader* reader, } else if (p == "id") { - // id - // + // id (member) + // id() (object) // Make sure we've got the correct declaration type. // @@ -869,6 +880,34 @@ handle_pragma (cpp_reader* reader, return; tt = pragma_lex (&t); + + if (tt == CPP_OPEN_PAREN) + { + if (qualifier == "member") + { + error () << "unexpected '(' after db pragma " << p << endl; + return; + } + + if (pragma_lex (&t) != CPP_CLOSE_PAREN) + { + error () << "')' expected at the end of db pragma " << p << endl; + return; + } + + val = false; // Object without id. + tt = pragma_lex (&t); + } + else + { + if (qualifier == "object") + { + error () << "expected '(' after db pragma " << p << endl; + return; + } + + val = true; // Member is object id. + } } else if (p == "auto") { @@ -1463,7 +1502,7 @@ handle_pragma (cpp_reader* reader, // if (tt == CPP_NAME) { - handle_pragma (reader, IDENTIFIER_POINTER (t), decl, decl_name); + handle_pragma (reader, IDENTIFIER_POINTER (t), qualifier, decl, decl_name); } else if (tt != CPP_EOF) error () << "unexpected text after " << p << " in db pragma" << endl; @@ -1592,7 +1631,7 @@ handle_pragma_qualifier (cpp_reader* reader, string const& p) p == "readonly" || p == "transient") { - handle_pragma (reader, p, 0, ""); + handle_pragma (reader, p, "member", 0, ""); return; } else @@ -1621,7 +1660,7 @@ handle_pragma_qualifier (cpp_reader* reader, string const& p) // if (tt == CPP_NAME) { - handle_pragma (reader, IDENTIFIER_POINTER (t), decl, decl_name); + handle_pragma (reader, IDENTIFIER_POINTER (t), p, decl, decl_name); } else if (tt != CPP_EOF) error () << "unexpected text after " << p << " in db pragma" << endl; diff --git a/odb/relational/header.hxx b/odb/relational/header.hxx index 7c74d09..a331b3f 100644 --- a/odb/relational/header.hxx +++ b/odb/relational/header.hxx @@ -925,6 +925,7 @@ namespace relational virtual void traverse_object (type& c) { + bool abstract (context::abstract (c)); string const& type (c.fq_name ()); semantics::data_member* id (id_member (c)); @@ -1013,6 +1014,15 @@ namespace relational << "};"; } } + else if (!abstract) + { + // Object without id. + // + os << "typedef void id_type;" + << endl + << "static const bool auto_id = false;" + << endl; + } // image_type // @@ -1033,17 +1043,19 @@ namespace relational // id () // - if (id != 0) - { + if (id != 0 || !abstract) + // We want to generate a dummy void id() accessor even if this + // object has no id to help us in the runtime. This way we can + // generic code that will both for both void and non-void ids. + // os << "static id_type" << endl << "id (const object_type&);" << endl; - if (options.generate_query ()) - os << "static id_type" << endl - << "id (const image_type&);" - << endl; - } + if (id != 0 && options.generate_query ()) + os << "static id_type" << endl + << "id (const image_type&);" + << endl; // grow () // @@ -1095,7 +1107,7 @@ namespace relational // // The rest only applies to concrete objects. // - if (abstract (c)) + if (abstract) { object_public_extra_post (c); os << "};"; @@ -1124,8 +1136,9 @@ namespace relational // Statement cache (forward declaration). // - os << "struct container_statement_cache_type;" - << endl; + if (id != 0) + os << "struct container_statement_cache_type;" + << endl; // column_count // @@ -1139,13 +1152,17 @@ namespace relational // Statements. // - os << "static const char persist_statement[];" - << "static const char find_statement[];"; + os << "static const char persist_statement[];"; - if (cc.total != cc.id + cc.inverse + cc.readonly) - os << "static const char update_statement[];"; + if (id != 0) + { + os << "static const char find_statement[];"; - os << "static const char erase_statement[];"; + if (cc.total != cc.id + cc.inverse + cc.readonly) + os << "static const char update_statement[];"; + + os << "static const char erase_statement[];"; + } if (options.generate_query ()) { @@ -1178,29 +1195,32 @@ namespace relational "object_type&);" << endl; - // update () - // - if (!readonly (c)) - os << "static void" << endl - << "update (database&, const object_type&);" + if (id != 0) + { + // find () + // + if (c.default_ctor ()) + os << "static pointer_type" << endl + << "find (database&, const id_type&);" + << endl; + + os << "static bool" << endl + << "find (database&, const id_type&, object_type&);" << endl; - // erase () - // - os << "static void" << endl - << "erase (database&, const id_type&);" - << endl; + // update () + // + if (!readonly (c)) + os << "static void" << endl + << "update (database&, const object_type&);" + << endl; - // find () - // - if (c.default_ctor ()) - os << "static pointer_type" << endl - << "find (database&, const id_type&);" + // erase () + // + os << "static void" << endl + << "erase (database&, const id_type&);" << endl; - - os << "static bool" << endl - << "find (database&, const id_type&, object_type&);" - << endl; + } // query () // @@ -1230,21 +1250,24 @@ namespace relational // os << "public:" << endl; - // Load the object image. - // - os << "static bool" << endl - << "find_ (" << db << "::object_statements< object_type >&, " << - "const id_type&);" - << endl; + if (id != 0) + { + // Load the object image. + // + os << "static bool" << endl + << "find_ (" << db << "::object_statements< object_type >&, " << + "const id_type&);" + << endl; - // Load the rest of the object (containers, etc). Expects the id - // image in the object statements to be initialized to the object - // id. - // - os << "static void" << endl - << "load_ (" << db << "::object_statements< object_type >&, " << - "object_type&);" - << endl; + // Load the rest of the object (containers, etc). Expects the id + // image in the object statements to be initialized to the object + // id. + // + os << "static void" << endl + << "load_ (" << db << "::object_statements< object_type >&, " << + "object_type&);" + << endl; + } os << "};"; } diff --git a/odb/relational/inline.hxx b/odb/relational/inline.hxx index 78d81bf..3968dce 100644 --- a/odb/relational/inline.hxx +++ b/odb/relational/inline.hxx @@ -102,7 +102,7 @@ namespace relational virtual void traverse_object (type& c) { - bool abst (abstract (c)); + bool abstract (context::abstract (c)); string const& type (c.fq_name ()); string traits ("access::object_traits< " + type + " >"); @@ -115,28 +115,34 @@ namespace relational object_extra (c); - if (id != 0) + // id (object_type) + // + if (id != 0 || !abstract) { - // id (object_type) - // os << "inline" << endl << traits << "::id_type" << endl << traits << "::" << endl - << "id (const object_type& obj)" + << "id (const object_type&" << (id != 0 ? " obj" : "") << ")" << "{"; - if (base_id) - os << "return object_traits< " << id->scope ().fq_name () << - " >::id (obj);"; - else - os << "return obj." << id->name () << ";"; + if (id != 0) + { + if (base_id) + os << "return object_traits< " << id->scope ().fq_name () << + " >::id (obj);"; + else + os << "return obj." << id->name () << ";"; + } os << "}"; + } - // id (image_type) - // + if (id != 0) + { if (options.generate_query () && base_id) { + // id (image_type) + // os << "inline" << endl << traits << "::id_type" << endl << traits << "::" << endl @@ -175,7 +181,7 @@ namespace relational // // The rest only applies to concrete objects. // - if (abst) + if (abstract) return; // callback () @@ -244,7 +250,7 @@ namespace relational // load_() // - if (!has_a (c, test_container)) + if (id != 0 && !has_a (c, test_container)) { os << "inline" << endl << "void " << traits << "::" << endl diff --git a/odb/relational/oracle/source.cxx b/odb/relational/oracle/source.cxx index 880b496..a22c235 100644 --- a/odb/relational/oracle/source.cxx +++ b/odb/relational/oracle/source.cxx @@ -876,7 +876,7 @@ namespace relational { semantics::data_member* id (id_member (c)); - if (id->count ("auto")) + if (id != 0 && id->count ("auto")) { os << endl << strlit (" RETURNING " + diff --git a/odb/relational/pgsql/source.cxx b/odb/relational/pgsql/source.cxx index c45b40e..c17edc6 100644 --- a/odb/relational/pgsql/source.cxx +++ b/odb/relational/pgsql/source.cxx @@ -988,6 +988,7 @@ namespace relational if (abstract (c)) return; + semantics::data_member* id (id_member (c)); column_count_type const& cc (column_count (c)); string const& n (c.fq_name ()); @@ -998,20 +999,24 @@ namespace relational os << name_decl << endl << "persist_statement_name[] = " << strlit (fn + "_persist") << ";" - << endl - << name_decl << endl - << "find_statement_name[] = " << strlit (fn + "_find") << ";" << endl; - if (cc.total != cc.id + cc.inverse + cc.readonly) + if (id != 0) + { os << name_decl << endl - << "update_statement_name[] = " << strlit (fn + "_update") << - ";" + << "find_statement_name[] = " << strlit (fn + "_find") << ";" << endl; - os << name_decl << endl - << "erase_statement_name[] = " << strlit (fn + "_erase") << ";" - << endl; + if (cc.total != cc.id + cc.inverse + cc.readonly) + os << name_decl << endl + << "update_statement_name[] = " << strlit (fn + "_update") << + ";" + << endl; + + os << name_decl << endl + << "erase_statement_name[] = " << strlit (fn + "_erase") << ";" + << endl; + } // Query statement name. // @@ -1029,7 +1034,6 @@ namespace relational // Statement types. // string oid_decl ("const unsigned int " + traits); - semantics::data_member* id_m (id_member (c)); // persist_statement_types. // @@ -1046,20 +1050,21 @@ namespace relational // find_statement_types. // + if (id != 0) { os << oid_decl << endl << "find_statement_types[] =" << "{"; instance st (statement_select); - st->traverse_column (*id_m, "", true); + st->traverse_column (*id, "", true); os << "};"; } // update_statement_types. // - if (cc.total != cc.id + cc.inverse + cc.readonly) + if (id != 0 && cc.total != cc.id + cc.inverse + cc.readonly) { os << oid_decl << endl << "update_statement_types[] =" @@ -1072,7 +1077,7 @@ namespace relational { instance st (statement_where); - st->traverse_column (*id_m, "", false); + st->traverse_column (*id, "", false); } os << "};"; @@ -1080,13 +1085,14 @@ namespace relational // erase_statement_types. // + if (id != 0) { os << oid_decl << endl << "erase_statement_types[] =" << "{"; instance st (statement_where); - st->traverse_column (*id_m, "", true); + st->traverse_column (*id, "", true); os << "};"; } diff --git a/odb/relational/processor.cxx b/odb/relational/processor.cxx index fdddf9a..d0587ca 100644 --- a/odb/relational/processor.cxx +++ b/odb/relational/processor.cxx @@ -851,6 +851,20 @@ namespace relational throw operation_failed (); } + // Make sure the pointed-to class has object id. + // + if (context::id_member (*c) == 0) + { + os << m.file () << ":" << m.line () << ":" << m.column () << ": " + << "error: pointed-to class '" << c->fq_name () << "' " + << "has no object id" << endl; + + os << c->file () << ":" << c->line () << ":" << c->column () << ": " + << "info: class '" << c->name () << "' is defined here" << endl; + + throw operation_failed (); + } + // See if this is the inverse side of a bidirectional relationship. // If so, then resolve the member and cache it in the context. // @@ -882,8 +896,9 @@ namespace relational } // @@ Would be good to check that the other end is actually - // an object pointer and is not marked as inverse. But the - // other class may not have been processed yet. + // an object pointer, is not marked as inverse, and points + // to the correct object. But the other class may not have + // been processed yet. // m.remove ("inverse"); m.set (kp + (kp.empty () ? "": "-") + "inverse", im); diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx index 9b04ae4..a82fe6a 100644 --- a/odb/relational/source.hxx +++ b/odb/relational/source.hxx @@ -2251,7 +2251,7 @@ namespace relational virtual void traverse_object (type& c) { - bool abst (abstract (c)); + bool abstract (context::abstract (c)); string const& type (c.fq_name ()); string traits ("access::object_traits< " + type + " >"); @@ -2461,7 +2461,7 @@ namespace relational // // The rest only applies to concrete objects. // - if (abst) + if (abstract) return; // @@ -2470,6 +2470,7 @@ namespace relational // Statement cache (definition). // + if (id != 0) { os << "struct " << traits << "::container_statement_cache_type" << "{"; @@ -2494,7 +2495,6 @@ namespace relational // string const& table (table_qname (c)); - string const& id_col (column_qname (*id)); // persist_statement // @@ -2519,52 +2519,57 @@ namespace relational << endl; } - // find_statement - // + if (id != 0) { - os << "const char " << traits << "::find_statement[] =" << endl - << strlit ("SELECT ") << endl; + string const& id_col (column_qname (*id)); - instance t (table, statement_select); - t->traverse (c); + // find_statement + // + { + os << "const char " << traits << "::find_statement[] =" << endl + << strlit ("SELECT ") << endl; - os << strlit (" FROM " + table) << endl; + instance t (table, statement_select); + t->traverse (c); - bool f (false); - instance j (c, f); // @@ (im)perfect forwarding - j->traverse (c); - j->write (); + os << strlit (" FROM " + table) << endl; - instance qp; - os << strlit (" WHERE " + table + "." + id_col + "=" + - qp->next ()) << ";" - << endl; - } + bool f (false); + instance j (c, f); // @@ (im)perfect forwarding + j->traverse (c); + j->write (); - // update_statement - // - if (cc.total != cc.id + cc.inverse + cc.readonly) - { - os << "const char " << traits << "::update_statement[] " << - "=" << endl - << strlit ("UPDATE " + table + " SET ") << endl; + instance qp; + os << strlit (" WHERE " + table + "." + id_col + "=" + + qp->next ()) << ";" + << endl; + } - instance qp; - instance t (statement_update, true, qp.get ()); - t->traverse (c); + // update_statement + // + if (cc.total != cc.id + cc.inverse + cc.readonly) + { + os << "const char " << traits << "::update_statement[] " << + "=" << endl + << strlit ("UPDATE " + table + " SET ") << endl; - os << strlit (" WHERE " + id_col + "=" + qp->next ()) << ";" - << endl; - } + instance qp; + instance t (statement_update, true, qp.get ()); + t->traverse (c); - // erase_statement - // - { - instance qp; - os << "const char " << traits << "::erase_statement[] =" << endl - << strlit ("DELETE FROM " + table) << endl - << strlit (" WHERE " + id_col + "=" + qp->next ()) << ";" - << endl; + os << strlit (" WHERE " + id_col + "=" + qp->next ()) << ";" + << endl; + } + + // erase_statement + // + { + instance qp; + os << "const char " << traits << "::erase_statement[] =" << endl + << strlit ("DELETE FROM " + table) << endl + << strlit (" WHERE " + id_col + "=" + qp->next ()) << ";" + << endl; + } } if (options.generate_query ()) @@ -2613,6 +2618,11 @@ namespace relational // 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)" @@ -2621,7 +2631,7 @@ namespace relational << endl << db << "::connection& conn (" << endl << db << "::transaction::current ().connection ());" - << "object_statements< object_type >& sts (" << endl + << object_statements_type << "& sts (" << endl << "conn.statement_cache ().find_object ());" << "image_type& im (sts.image ());" << "binding& imb (sts.insert_image_binding ());" @@ -2684,7 +2694,7 @@ namespace relational // update () // - if (!readonly (c)) + if (id != 0 && !readonly (c)) { os << "void " << traits << "::" << endl << "update (database&, const object_type& obj)" @@ -2693,7 +2703,7 @@ namespace relational << endl << db << "::connection& conn (" << endl << db << "::transaction::current ().connection ());" - << "object_statements< object_type >& sts (" << endl + << object_statements_type << "& sts (" << endl << "conn.statement_cache ().find_object ());" << endl; @@ -2756,51 +2766,54 @@ namespace relational // erase () // - os << "void " << traits << "::" << endl - << "erase (database&, const id_type& id)" - << "{" - << "using namespace " << db << ";" - << endl - << db << "::connection& conn (" << endl - << db << "::transaction::current ().connection ());" - << "object_statements< object_type >& sts (" << endl - << "conn.statement_cache ().find_object ());" - << endl; + 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; + // 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++;" - << "}"; + 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 << endl; - } + // 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 << endl; + } - os << "if (sts.erase_statement ().execute () != 1)" << endl - << "throw object_not_persistent ();"; + os << "if (sts.erase_statement ().execute () != 1)" << endl + << "throw object_not_persistent ();"; - os << "}"; + os << "}"; + } - // find () + // find (id) // - if (c.default_ctor ()) + if (id != 0 && c.default_ctor ()) { os << traits << "::pointer_type" << endl << traits << "::" << endl @@ -2810,9 +2823,9 @@ namespace relational << endl << db << "::connection& conn (" << endl << db << "::transaction::current ().connection ());" - << "object_statements< object_type >& sts (" << endl + << object_statements_type << "& sts (" << endl << "conn.statement_cache ().find_object ());" - << "object_statements< object_type >::auto_lock l (sts);" + << object_statements_type << "::auto_lock l (sts);" << endl << "if (l.locked ())" << "{" @@ -2848,110 +2861,118 @@ namespace relational << "}"; } - 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< object_type >& sts (" << endl - << "conn.statement_cache ().find_object ());" - << "object_statements< object_type >::auto_lock l (sts);" - << endl - << "if (l.locked ())" - << "{" - << "if (!find_ (sts, id))" << endl - << "return false;" - << "}" - << "reference_cache_traits< object_type >::insert_guard ig (" << endl - << "reference_cache_traits< object_type >::insert (db, id, obj));" - << endl - << "if (l.locked ())" - << "{" - << "callback (db, obj, callback_event::pre_load);" - << "init (obj, sts.image (), db);"; - - init_value_extra (); + // find (id, obj) + // + if (id != 0) + { + os << "bool " << traits << "::" << endl + << "find (database& db, const id_type& id, object_type& obj)" + << "{" + << "using namespace " << db << ";" + << endl + << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << object_statements_type << "& sts (" << endl + << "conn.statement_cache ().find_object ());" + << object_statements_type << "::auto_lock l (sts);" + << endl + << "if (l.locked ())" + << "{" + << "if (!find_ (sts, id))" << endl + << "return false;" + << "}" + << "reference_cache_traits< object_type >::insert_guard ig (" << endl + << "reference_cache_traits< object_type >::insert (db, id, obj));" + << endl + << "if (l.locked ())" + << "{" + << "callback (db, obj, callback_event::pre_load);" + << "init (obj, sts.image (), db);"; - 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; + init_value_extra (); - os << "ig.release ();" - << "return true;" - << "}"; + 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 << "bool " << traits << "::" << endl - << "find_ (" << db << "::object_statements< object_type >& sts, " << - "const id_type& id)" - << "{" - << "using namespace " << db << ";" - << endl; + os << "ig.release ();" + << "return true;" + << "}"; + } - // Initialize id image. + // find_ () // - os << "id_image_type& i (sts.id_image ());" - << "init (i, id);" - << endl; - - os << "binding& idb (sts.id_image_binding ());" - << "if (i.version != sts.id_image_version () || idb.version == 0)" - << "{" - << "bind (idb.bind, i);" - << "sts.id_image_version (i.version);" - << "idb.version++;" - << "}"; + if (id != 0) + { + os << "bool " << traits << "::" << endl + << "find_ (" << db << "::" << object_statements_type << "& " << + "sts, const id_type& id)" + << "{" + << "using namespace " << db << ";" + << endl; - // 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 ();" - << "select_statement::result r (st.fetch ());"; + // Initialize id image. + // + os << "id_image_type& i (sts.id_image ());" + << "init (i, id);" + << endl; - if (grow) - os << endl - << "if (r == select_statement::truncated)" + os << "binding& idb (sts.id_image_binding ());" + << "if (i.version != sts.id_image_version () || idb.version == 0)" << "{" - << "if (grow (im, sts.select_image_truncated ()))" << endl - << "im.version++;" + << "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 ())" + << "if (im.version != sts.select_image_version () || " << + "imb.version == 0)" << "{" << "bind (imb.bind, im, statement_select);" << "sts.select_image_version (im.version);" << "imb.version++;" - << "st.refetch ();" << "}" - << "}"; + << "select_statement& st (sts.find_statement ());" + << "st.execute ();" + << "select_statement::result r (st.fetch ());"; - os << "st.free_result ();" - << "return r != select_statement::no_data;" - << "}"; + if (grow) + os << endl + << "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 ();" + << "}" + << "}"; + + os << "st.free_result ();" + << "return r != select_statement::no_data;" + << "}"; + } // load_() // if (containers) { os << "void " << traits << "::" << endl - << "load_ (" << db << "::object_statements< object_type >& " << + << "load_ (" << db << "::" << object_statements_type << "& " << "sts, object_type& obj)" << "{" << db << "::binding& idb (sts.id_image_binding ());" @@ -2976,7 +2997,7 @@ namespace relational << db << "::connection& conn (" << endl << db << "::transaction::current ().connection ());" << endl - << "object_statements< object_type >& sts (" << endl + << object_statements_type << "& sts (" << endl << "conn.statement_cache ().find_object ());" << endl << "image_type& im (sts.image ());" @@ -2999,11 +3020,14 @@ namespace relational post_query_ (c); + char const* result_type ( + id != 0 + ? "object_result_impl" + : "object_result_impl_no_id"); + os << endl - << "shared_ptr > r (" << endl - << "new (shared) " << db << - "::result_impl (" << endl + << "shared_ptr< odb::" << result_type << " > r (" << endl + << "new (shared) " << db << "::" << result_type << " (" << endl << "q, st, sts));" << endl << "return result (r);" @@ -3928,10 +3952,8 @@ namespace relational post_query_ (c); os << endl - << "shared_ptr > r (" << endl - << "new (shared) " << db << - "::result_impl (" << endl + << "shared_ptr< odb::view_result_impl > r (" << endl + << "new (shared) " << db << "::view_result_impl (" << endl << "qs, st, sts));" << endl << "return result (r);" diff --git a/odb/validator.cxx b/odb/validator.cxx index 34bb69e..7dc7ea1 100644 --- a/odb/validator.cxx +++ b/odb/validator.cxx @@ -333,7 +333,10 @@ namespace if (id == 0) { - if (!context::abstract (c)) + // An object without an id should either be abstract or + // explicitly marked as such. + // + if (!(c.count ("id") || context::abstract (c))) { cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" << " error: no data member designated as object id" << endl; @@ -342,6 +345,10 @@ namespace << " info: use '#pragma db id' to specify object id member" << endl; + cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " info: or explicitly declare that this persistent class " + << "has no object id" << endl; + valid_ = false; } } @@ -551,6 +558,58 @@ namespace // Pass 2. // + struct object_no_id_members: object_members_base + { + object_no_id_members (bool& valid) + : object_members_base (false, false, true), valid_ (valid), dm_ (0) + { + } + + virtual void + traverse_simple (semantics::data_member& m) + { + if (m.count ("inverse")) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + cerr << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: inverse object pointer member '" << member_prefix_ + << m.name () << "' in an object without id" << endl; + + valid_ = false; + } + } + + virtual void + traverse_container (semantics::data_member& m, semantics::type&) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + cerr << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: container member '" << member_prefix_ << m.name () + << "' in an object without id" << endl; + + valid_ = false; + } + + virtual void + traverse_composite (semantics::data_member* m, semantics::class_& c) + { + semantics::data_member* old_dm (dm_); + + if (dm_ == 0) + dm_ = m; + + object_members_base::traverse_composite (m, c); + + dm_ = old_dm; + } + + private: + bool& valid_; + semantics::data_member* dm_; // Direct view data member. + }; + struct view_members: object_members_base { view_members (bool& valid) @@ -614,7 +673,11 @@ namespace struct class2: traversal::class_ { class2 (bool& valid, options const& ops, semantics::unit& unit) - : valid_ (valid), options_ (ops), unit_ (unit), view_members_ (valid) + : valid_ (valid), + options_ (ops), + unit_ (unit), + object_no_id_members_ (valid), + view_members_ (valid) { } @@ -630,8 +693,14 @@ namespace } virtual void - traverse_object (type&) + traverse_object (type& c) { + if (context::id_member (c) == 0 && !context::abstract (c)) + { + // Make sure we don't have any containers or inverse pointers. + // + object_no_id_members_.traverse (c); + } } virtual void @@ -651,6 +720,7 @@ namespace options const& options_; semantics::unit& unit_; + object_no_id_members object_no_id_members_; view_members view_members_; }; } -- cgit v1.1