From 8e69f40ab32dc8604b68f360ae30fa961ba036ee Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 4 Feb 2015 17:23:54 +0200 Subject: Implement object loading views See section 10.2 in the manual for details. --- NEWS | 5 + doc/manual.xhtml | 432 ++++++++++++- odb/common.cxx | 6 +- odb/context.cxx | 62 +- odb/context.hxx | 46 +- odb/header.cxx | 6 +- odb/instance.hxx | 17 + odb/pragma.cxx | 23 +- odb/processor.cxx | 129 +++- odb/relational/common.cxx | 9 + odb/relational/common.hxx | 42 +- odb/relational/common.txx | 26 +- odb/relational/header.hxx | 75 +++ odb/relational/mssql/common.cxx | 9 + odb/relational/mssql/common.hxx | 6 +- odb/relational/mssql/header.cxx | 41 +- odb/relational/mssql/source.cxx | 4 +- odb/relational/mysql/common.cxx | 9 + odb/relational/mysql/common.hxx | 6 +- odb/relational/mysql/header.cxx | 42 +- odb/relational/mysql/source.cxx | 147 +---- odb/relational/oracle/common.cxx | 9 + odb/relational/oracle/common.hxx | 6 +- odb/relational/oracle/header.cxx | 42 +- odb/relational/oracle/source.cxx | 4 +- odb/relational/pgsql/common.cxx | 9 + odb/relational/pgsql/common.hxx | 6 +- odb/relational/pgsql/header.cxx | 42 +- odb/relational/pgsql/source.cxx | 158 +---- odb/relational/processor.cxx | 97 +-- odb/relational/source.cxx | 1105 ++++++++++++++++++---------------- odb/relational/source.hxx | 1231 ++++++++++++++++++++++++++++++++++++-- odb/relational/sqlite/common.cxx | 9 + odb/relational/sqlite/common.hxx | 7 +- odb/relational/sqlite/header.cxx | 41 +- odb/relational/sqlite/source.cxx | 147 +---- odb/relational/validator.cxx | 16 +- 37 files changed, 2788 insertions(+), 1283 deletions(-) diff --git a/NEWS b/NEWS index 51fb133..d59dd96 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,10 @@ Version 2.4.0 + * Support for object loading views. Object loading views allow loading of + one or more complete objects instead of, or in addition to, a subset of + data member and with a single SELECT statement execution. For details, + refer to Section 10.2, "Object Loading Views" in the ODB manual. + * Support for bulk operations in Oracle and SQL Server. Bulk operations persist, update, or erase a range of objects using a single database statement execution which often translates to significantly better diff --git a/doc/manual.xhtml b/doc/manual.xhtml index 6183d2b..f91a4af 100644 --- a/doc/manual.xhtml +++ b/doc/manual.xhtml @@ -469,11 +469,12 @@ for consistency. 10Views - - - - - + + + + + +
10.1Object Views
10.2Table Views
10.3Mixed Views
10.4View Query Conditions
10.5Native Views
10.6Other View Features and Limitations
10.2Object Loading Views
10.3Table Views
10.4Mixed Views
10.5View Query Conditions
10.6Native Views
10.7Other View Features and Limitations
@@ -9962,7 +9963,7 @@ struct employee_employer below. The optional join-condition part provides the criteria which should be used to associate this object with any of the previously associated objects or, as we will see in - Section 10.3, "Mixed Views", tables. Note that + Section 10.4, "Mixed Views", tables. Note that while the first associated object can have an alias, it cannot have a join condition.

@@ -10311,7 +10312,398 @@ t.commit (); query expression, and you need to qualify the column with the table, then you will need to use the table alias instead.

-

10.2 Table Views

+

10.2 Object Loading Views

+ +

A special variant of object views is object loading views. Object + loading views allow us to load one or more complete objects + instead of, or in addition to, a subset of data member. While we + can often achieve the same end result by calling + database::load(), using a view has several advantages.

+ +

If we need to load multiple objects, then using a view allows us + to do this with a single SELECT statement execution + instead of one for each object that would be necessary in case of + load(). A view can also be useful for loading only + a single object if the query criterion that we would like to use + involves other, potentially unrelated, objects. We will examine + concrete examples of these and other scenarios in the rest of this + section.

+ +

To load a complete object as part of a view we use a data member of + the pointer to object type, just like for object relationships + (Chapter 6, "Relationships"). As an example, here + is how we can load both the employee and + employer objects from the previous section with a single + statement:

+ +
+#pragma db view object(employee) object(employer)
+struct employee_employer
+{
+  shared_ptr<employee> ee;
+  shared_ptr<employer> er;
+};
+  
+ +

We use an object loading view just like any other view. In the + result of a query, as we would expect, the pointer data members + point to the loaded objects. For example:

+ +
+typedef odb::query<employee_employer> query;
+
+transaction t (db.begin ());
+
+for (const employee_employer& r:
+       db.query<employee_employer> (query::employee::age < 31))
+{
+  cout << r.ee->age () << " " << r.er->name () << endl;
+}
+
+t.commit ();
+  
+ +

As another example, consider a query that loads the employer + objects using some condition based on its employees. For instance, we + want to find all the employers that employ people over 65 years old. + We can use this object loading view to implement such a query:

+ +
+#pragma db view object(employer) object(employee)
+struct employer_view
+{
+  shared_ptr<employer> er;
+};
+  
+ +

And this is how we can use this view to find all the employers that + employ seniors:

+ +
+typedef odb::query<employee_employer> query;
+
+db.query<employer_view> ((query::employee::age > 65) + distinct))
+  
+ +

We can even use object loading views to load completely unrelated + (from the ODB object relationships point of view) objects. For example, + the following view will load all the employers that are named the + same as a country (notice the inner join type):

+ +
+#pragma db view object(employer) \
+  object(country inner: employer::name == country::name)
+struct employer_named_country
+{
+  shared_ptr<employer> e;
+  shared_ptr<country> c;
+};
+  
+ +

An object loading view can contain ordinary data members + in addition to object pointers. For example, if we are only + interested in the country code in the above view, then we + can reimplement it like this:

+ +
+#pragma db view object(employer) \
+  object(country inner: employer::name == country::name)
+struct employer_named_country
+{
+  shared_ptr<employer> e;
+  std::string code;
+};
+  
+ +

Object loading views also have a few rules and restrictions. + Firstly, the pointed-to object in the data member must be associated + with the view. Furthermore, if the associated object has an alias, + then the data member name must be the same as the alias (more + precisely, the public name derived from the data member must + match the alias; which means we can use normal data member + decorations such as trailing underscores, etc., see the previous + section for more information on public names). The following view + illustrates the use of aliases as data member names:

+ +
+#pragma db view object(employee)               \
+  object(country = res: employee::residence_)  \
+  object(country = nat: employee::nationality_)
+struct employee_country
+{
+  shared_ptr<country> res;
+  shared_ptr<country> nat_;
+};
+  
+ +

Finally, the object pointers must be direct data members of + the view. Using, for example, a composite value that contains + pointers as a view data member is not supported. Note also + that depending on the join type you are using, some of the + resulting pointers might be NULL.

+ +

Up until now we have consistently used shared_ptr + as an object pointer in our views. Can we use other pointers, + such as unique_ptr or raw pointers? To answer + this question we first need to discuss what happens with + object pointers that may be inside objects that a view + loads. As a concrete example, let us revisit the + employee_employer view from the beginning of + this section:

+ +
+#pragma db view object(employee) object(employer)
+struct employee_employer
+{
+  shared_ptr<employee> ee;
+  shared_ptr<employer> er;
+};
+  
+ +

This view loads two objects: employee and + employer. The employee object, + however, also contains a pointer to employer + (see the employed_by_ data member). In fact, + this is the same object that the view loads since employer + is associated with the view using this same relationship (ODB + automatically uses it since it is the only one). The correct + result of loading such a view is then clear: both er and + er->employed_by_ must point to (or share) the + same instance.

+ +

Just like object loading via the database class + functions, views achieve this correct behavior of only loading + a single instance of the same object with the help of session's + object cache (Chapter 11, "Session"). In fact, + object loading views enforce this by throwing the + session_required exception if there is no current + session and the view loads an object that is also indirectly + loaded by one of the other objects. The ODB compiler will also + issue diagnostics if such an object has session support + disabled (Section 14.1.10, + "session").

+ +

With this understanding we can now provide the correct implementation + of our transaction that uses the employee_employer view:

+ +
+typedef odb::query<employee_employer> query;
+
+transaction t (db.begin ());
+odb::session s;
+
+for (const employee_employer& r:
+       db.query<employee_employer> (query::employee::age < 31))
+{
+  assert (r.ee->employed_by_ == r.er);
+  cout << r.ee->age () << " " << r.er->name () << endl;
+}
+
+t.commit ();
+  
+ +

It might seem logical, then, to always load all the objects from + all the eager relationships with the view. After all, this will + lead to them all being loaded with a single statement. While + this is theoretically true, the reality is slightly more nuanced. + If there is a high probability of the object already have been + loaded and sitting in the cache, then not loading the object + as part of the view (and therefore not fetching all its data + from the database) might result in better performance.

+ +

Now we can also answer the question about which pointers we can + use in object loading views. From the above discussion it should + be clear that if an object that we are loading is also part of a + relationship inside another object that we are loading, then we + should use some form of a shared ownership pointer. If, however, + there are no relationships involved, as is the case, for example, + in our employer_named_country and + employee_country views above, then we can use a + unique ownership pointer such as unique_ptr.

+ +

Note also that your choice of a pointer type can be limited by the + "official" object pointer type assigned to the object + (Section 3.3, "Object and View Pointers"). + For example, if the object pointer type is shared_ptr, + you will not be able to use unique_ptr to load + such an object into a view since initializing unique_ptr + from shared_ptr would be a mistake.

+ +

Unless you want to perform your own object cleanup, raw object + pointers in views are not particularly useful. They do have one + special semantics, however: If a raw pointer is used as a view + member, then, before creating a new instance, the implementation + will check if the member is NULL. If it is not, then + it is assumed to point to an existing instance and the implementation + will load the data into it instead of creating a new one. The + primary use of this special functionality is to implement by-value + loading with the ability to detect NULL values.

+ +

To illustrate this functionality, consider the following view that + load the employee's residence country by value:

+ +
+#pragma db view object(employee) \
+  object(country = res: employee::residence_) transient
+struct employee_res_country
+{
+  typedef country* country_ptr;
+
+  #pragma db member(res_) virtual(country_ptr) get(&this.res) \
+    set(this.res_null = ((?) == nullptr))
+
+  country res;
+  bool res_null;
+};
+  
+ +

Here we are using a virtual data member + (Section 14.4.13, "virtual") to + add an object pointer member to the view. Its accessor expression + returns the pointer to the res member so that + the implementation can load the data into it. The modifier + expression checks the passed pointer to initialize the + NULL value indicator. Here, the two possible + values that can be passed to the modifier expression are + the address of the res member that we returned + earlier from the accessor and NULL (strictly + speaking, there is a third possibility: the address of an + object that was found in the session cache).

+ +

If we are not interested in the NULL indicator, + then the above view can simplified to this:

+ +
+#pragma db view object(employee) \
+  object(country = res: employee::residence_) transient
+struct employee_res_country
+{
+  typedef country* country_ptr;
+
+  #pragma db member(res_) virtual(country_ptr) get(&this.res) set()
+
+  country res;
+};
+  
+ +

That is, we specify an empty modifier expression which leads to + the value being ignored.

+ +

As another example of by-value loading, consider a view that allows + us to load objects into existing instances that have been allocated + outside the view:

+ +
+#pragma db view object(employee)               \
+  object(country = res: employee::residence_)  \
+  object(country = nat: employee::nationality_)
+struct employee_country
+{
+  employee_country (country& r, country& n): res (&r), nat (&n) {}
+
+  country* res;
+  country* nat;
+};
+  
+ +

And here is how we can use this view:

+ +
+typedef odb::result<employee_country> result;
+
+transaction t (db.begin ());
+
+result r (db.query<employee_country> (...);
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+{
+  country res, nat;
+  employee_country v (res, nat);
+  i.load (v);
+
+  if (v.res != nullptr)
+    ... // Result is in res.
+
+  if (v.nat != nullptr)
+    ... // Result is in nat.
+}
+
+t.commit ();
+  
+ +

As a final example of the by-value loading, consider the following + view which implements a slightly more advanced logic: if the object + is already in the session cache, then it sets the pointer data member + in the view (er_p) to that. Otherwise, it loads the data + into the by-value instance (er). We can also check + whether the pointer data member points to the instance to distinguish + between the two outcomes. And we can check it for nullptr + to detect NULL values.

+ +
+#pragma db view object(employer)
+struct employer_view
+{
+  // Since we may be getting the pointer as both smart and raw, we
+  // need to create a bit of support code to use in the modifier
+  // expression.
+  //
+  void set_er (employer* p) {er_p = p;}                   // &er or NULL.
+  void set_er (shared_ptr<employer> p) {er_p = p.get ();} // From cache.
+
+  #pragma db get(&this.er) set(set_er(?))
+  employer* er_p;
+
+  #pragma db transient
+  employer er;
+
+  // Return-by-value support (e.g., query_value()).
+  //
+  employer_view (): er_p (0) {}
+  employer_view (const employer_view& x)
+    : er_p (x.er_p == &x.er ? &er : x.er_p), er (x.er) {}
+};
+  
+ +

We can use object loading views with polymorphic objects + (Section 8.2, "Polymorphism Inheritance"). Note, + however, that when loading a derived object via the base pointer + in a view, a separate statement will be executed to load the + dynamic part of the object. There is no support for by-value + loading for polymorphic objects.

+ +

We can also use object loading views with objects without id + (Section 14.1.6, "no_id"). + Note, however, that for such objects, NULL values + are not automatically detected (since there is no primary key, + which is otherwise guaranteed to be not NULL, there + might not be a column on which to base this detection). The + workaround for this limitation is to load an otherwise not + NULL column next to the object which will serve + as an indicator. For example:

+ +
+#pragma db object no_id
+class object
+{
+  ...
+
+  int n; // NOT NULL
+  std::string s;
+};
+
+#include <odb/nullable.hxx>
+
+#pragma db view object(object)
+struct view
+{
+
+  odb::nullable<int> n; // If 'n' is NULL, then, logically, so is 'o'.
+  unique_ptr<object> o;
+};
+  
+ +

10.3 Table Views

A table view is similar to an object view except that it is based on one or more database tables instead of persistent @@ -10400,7 +10792,7 @@ struct employee_max_vacation column names, and query expressions. The optional join-condition part provides the criteria which should be used to associate this table with any of the previously associated tables or, as we will see in - Section 10.3, "Mixed Views", objects. Note that + Section 10.4, "Mixed Views", objects. Note that while the first associated table can have an alias, it cannot have a join condition.

@@ -10468,7 +10860,7 @@ t.commit (); -

10.3 Mixed Views

+

10.4 Mixed Views

A mixed view has both associated objects and tables. As a first example of a mixed view, let us improve employee_vacation @@ -10531,7 +10923,7 @@ struct employee_prev_employer }; -

10.4 View Query Conditions

+

10.5 View Query Conditions

Object, table, and mixed views can also specify an optional query condition that should be used whenever the database is queried for @@ -10642,7 +11034,7 @@ struct employer_age }; -

10.5 Native Views

+

10.6 Native Views

The last kind of view supported by ODB is a native view. Native views are a low-level mechanism for capturing results of native @@ -10651,7 +11043,7 @@ struct employer_age db query pragma to specify the native SQL query, which should normally include the select-list and, if applicable, the from-list. For example, here is how we can re-implement the - employee_vacation table view from Section 10.2 above + employee_vacation table view from Section 10.3 above as a native view:

@@ -10777,7 +11169,7 @@ result n (db.query<sequence_value> (
       for each database system in Part II, "Database
       Systems".

-

10.6 Other View Features and Limitations

+

10.7 Other View Features and Limitations

Views cannot be derived from other views. However, you can derive a view from a transient C++ class. View data members cannot be @@ -14608,7 +15000,7 @@ class employer

The table specifier specifies a database table that should be associated with the view. For more information - on table associations refer to Section 10.2, "Table + on table associations refer to Section 10.3, "Table Views".

14.2.3 query

@@ -14617,9 +15009,9 @@ class employer for an object or table view or a native SQL query for a native view. An empty query specifier indicates that a native SQL query is provided at runtime. For more information - on query conditions refer to Section 10.4, "View + on query conditions refer to Section 10.5, "View Query Conditions". For more information on native SQL queries, - refer to Section 10.5, "Native Views".

+ refer to Section 10.6, "Native Views".

14.2.4 pointer

@@ -16183,7 +16575,7 @@ class person object data member, the potentially qualified column name, or the column expression for the data member of a view class. For more information, refer to Section 10.1, "Object Views" and - Section 10.2, "Table Views".

+ Section 10.3, "Table Views".

14.4.11 transient

@@ -20134,7 +20526,7 @@ class object

17.7 MySQL Stored Procedures

-

ODB native views (Section 10.5, "Native Views") +

ODB native views (Section 10.6, "Native Views") can be used to call MySQL stored procedures. For example, assuming we are using the person class from Chapter 2, "Hello World Example" (and the corresponding person @@ -23399,7 +23791,7 @@ for (result::iterator i (r.begin ()); i != r.end (); ++i) t.commit ();

-

Finally, if a native view (Section 10.5, "Native +

Finally, if a native view (Section 10.6, "Native Views") contains one or more long data members, then such members should come last both in the select-list of the native SQL query and the list of data members in the C++ class.

@@ -24147,7 +24539,7 @@ class object

21.7 SQL Server Stored Procedures

-

ODB native views (Section 10.5, "Native Views") +

ODB native views (Section 10.6, "Native Views") can be used to call SQL Server stored procedures. For example, assuming we are using the person class from Chapter 2, "Hello World Example" (and the corresponding person diff --git a/odb/common.cxx b/odb/common.cxx index b75d323..ed586c9 100644 --- a/odb/common.cxx +++ b/odb/common.cxx @@ -18,7 +18,8 @@ traverse_simple (semantics::data_member&) void object_members_base:: traverse_pointer (semantics::data_member& m, semantics::class_& c) { - traverse_member (m, utype (*id_member (c))); + if (!view_member (m)) // Not really "as if" pointed-to id member. + traverse_member (m, utype (*id_member (c))); } void object_members_base:: @@ -244,7 +245,8 @@ traverse_column (semantics::data_member&, string const&, bool) void object_columns_base:: traverse_pointer (semantics::data_member& m, semantics::class_& c) { - traverse_member (m, utype (*id_member (c))); + if (!view_member (m)) // Not really "as if" pointed-to id column. + traverse_member (m, utype (*id_member (c))); } void object_columns_base:: diff --git a/odb/context.cxx b/odb/context.cxx index e56b412..44d7ef9 100644 --- a/odb/context.cxx +++ b/odb/context.cxx @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -69,6 +70,12 @@ add_space (string& s) string member_access:: translate (string const& obj, string const& val) const { + if (empty ()) + { + error (loc) << "non-empty " << kind << " expression required" << endl; + throw operation_failed (); + } + // This code is similar to translate_expression() from relations/source.cxx. // string r; @@ -2400,18 +2407,59 @@ namespace virtual void traverse_pointer (semantics::data_member& m, semantics::class_& c) { - size_t t (c_.total); + // Object pointers in views require special treatment. + // + if (view_member (m)) + { + using semantics::class_; + + column_count_type cc; + + if (class_* root = polymorphic (c)) + { + // For a polymorphic class we are going to load all the members + // from all the bases (i.e., equivalent to the first statement + // in the list of SELECT statements generated for the object). + // So our count should be the same as the first value in the + // generated column_counts array. + // + for (class_* b (&c);; b = &polymorphic_base (*b)) + { + column_count_type const& ccb (column_count (*b, section_)); + + cc.total += ccb.total - (b != root ? ccb.id : 0); + cc.separate_load += ccb.separate_load; + cc.soft += ccb.soft; - object_members_base::traverse_pointer (m, c); + if (b == root) + break; + } + } + else + cc = column_count (c, section_); - if (context::inverse (m)) + c_.total += cc.total - cc.separate_load; + + if (added (member_path_) != 0 || deleted (member_path_) != 0) + c_.soft += cc.total; + else + c_.soft += cc.soft; + } + else { - size_t n (c_.total - t); + size_t t (c_.total); + + object_members_base::traverse_pointer (m, c); - c_.inverse += n; + if (context::inverse (m)) + { + size_t n (c_.total - t); - if (separate_update (member_path_)) - c_.separate_update -= n; + c_.inverse += n; + + if (separate_update (member_path_)) + c_.separate_update -= n; + } } } diff --git a/odb/context.hxx b/odb/context.hxx index 9538389..34f9692 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -84,7 +84,13 @@ enum class_kind // that lead all the way from the object member to the innermost // composite value member. // -typedef std::vector data_member_path; +struct data_member_path: std::vector +{ + data_member_path () {} + + explicit + data_member_path (semantics::data_member& m) {push_back (&m);} +}; // Class inheritance chain, from the most derived to base. // @@ -162,6 +168,7 @@ struct view_object tree scope; location_t loc; semantics::class_* obj; + semantics::data_member* ptr; // Corresponding object pointer, if any. cxx_tokens cond; // Join condition tokens. }; @@ -173,6 +180,21 @@ typedef std::vector view_objects; typedef std::map view_alias_map; typedef std::map view_object_map; +// Collection of relationships via which the objects are joined. +// We need this information to figure out which alias/table +// names to use for columns corresponding to inverse object +// pointers inside objects that this view may be loading. +// +// The first object is the pointer (i.e., the one containing +// this data member) while the other is the pointee. In other +// words, first -> second. We always store the direct (i.e., +// non-inverse) side of the relationship. Note also that there +// could be multiple objects joined using the same relationship. +// +typedef +std::multimap > +view_relationship_map; + // // struct view_query @@ -232,8 +254,8 @@ struct column_expr: std::vector // struct member_access { - member_access (const location& l, bool s) - : loc (l), synthesized (s), by_value (false) {} + member_access (const location& l, const char* k, bool s) + : loc (l), kind (k), synthesized (s), by_value (false) {} // Return true of we have the (?) placeholder. // @@ -249,11 +271,21 @@ struct member_access return synthesized && expr.size () == 3; // this.member } + bool + empty () const + { + return expr.empty (); + } + + // Issues diagnostics and throws operation_failed if expression is + // empty. + // std::string translate (std::string const& obj, std::string const& val = std::string ()) const; location loc; + const char* kind; // accessor/modifier; used for diagnostics. bool synthesized; // If true, then this is a synthesized expression. cxx_tokens expr; bool by_value; // True if accessor returns by value. False doesn't @@ -623,6 +655,14 @@ public: return t.count ("view"); } + // Direct member of a view. + // + static bool + view_member (semantics::data_member& m) + { + return view (dynamic_cast (m.scope ())); + } + // Check whether the type is a wrapper. Return the wrapped type if // it is a wrapper and NULL otherwise. Note that the returned type // may be cvr-qualified. diff --git a/odb/header.cxx b/odb/header.cxx index fad1668..989adc3 100644 --- a/odb/header.cxx +++ b/odb/header.cxx @@ -758,6 +758,9 @@ namespace header if (ctx.features.polymorphic_object) os << "#include " << endl; // For discriminator. + if (ctx.options.std () >= cxx_version::cxx11) + os << "#include " << endl; // move() + os << endl; os << "#include " << endl @@ -819,7 +822,8 @@ namespace header os << "#include " << endl; if (ctx.features.view) - os << "#include " << endl; + os << "#include " << endl + << "#include " << endl; } os << endl diff --git a/odb/instance.hxx b/odb/instance.hxx index 461efd1..7047c08 100644 --- a/odb/instance.hxx +++ b/odb/instance.hxx @@ -212,6 +212,23 @@ struct instance x_ = factory_type::create (prototype); } + template + instance (A1& a1, A2& a2, A3& a3, A4& a4, A5& a5, A6 a6) + { + base_type prototype (a1, a2, a3, a4, a5, a6); + x_ = factory_type::create (prototype); + } + + template + instance (A1 const& a1, A2 const& a2, A3 const& a3, A4 const& a4, + A5 const& a5, A6 const& a6) + { + base_type prototype (a1, a2, a3, a4, a5, a6); + x_ = factory_type::create (prototype); + } + instance (instance const& i) { // This is tricky: use the other instance as a prototype. diff --git a/odb/pragma.cxx b/odb/pragma.cxx index 4d0acae..7a3d6ec 100644 --- a/odb/pragma.cxx +++ b/odb/pragma.cxx @@ -1089,18 +1089,23 @@ handle_pragma (cxx_lexer& l, return; } - tt = l.next (tl, &tn); - - val = member_access (loc, false); - if (!parse_expression (l, tt, tl, tn, val.value ().expr, p)) - return; // Diagnostics has already been issued. + member_access ma (loc, p == "set" ? "modifier" : "accessor", false); - if (tt != CPP_CLOSE_PAREN) + tt = l.next (tl, &tn); + if (tt != CPP_CLOSE_PAREN) // Empty expression are ok. { - error (l) << "')' expected at the end of db pragma " << p << endl; - return; + if (!parse_expression (l, tt, tl, tn, ma.expr, p)) + return; // Diagnostics has already been issued. + + if (tt != CPP_CLOSE_PAREN) + { + error (l) << "')' expected at the end of db pragma " << p << endl; + return; + } } + val = ma; + // Convert access to the get/set pair. // if (p == "access") @@ -1109,6 +1114,8 @@ handle_pragma (cxx_lexer& l, add_pragma ( pragma (p, "get", val, loc, &check_spec_decl_type, 0), decl, ns); + ma.kind = "modifier"; + val = ma; name = "set"; } diff --git a/odb/processor.cxx b/odb/processor.cxx index a6f18e0..7a0efa3 100644 --- a/odb/processor.cxx +++ b/odb/processor.cxx @@ -350,7 +350,9 @@ namespace { found_type found (found_none); semantics::access const& a (m.named ().access ()); - member_access& ma (m.set (k, member_access (m.location (), true))); + member_access& ma ( + m.set ( + k, member_access (m.location (), kind, true))); // If this member is not virtual and is either public or if we // are a friend of this class, then go for the member directly. @@ -490,6 +492,10 @@ namespace } member_access& ma (m.get (k)); + + if (ma.empty ()) + return; + cxx_tokens& e (ma.expr); // If it is just a name, resolve it and convert to an appropriate @@ -1350,9 +1356,10 @@ namespace throw operation_failed (); } - // Make sure the pointed-to class has object id. + // Make sure the pointed-to class has object id unless it is in a + // view where we can load no-id objects. // - if (id_member (*c) == 0) + if (id_member (*c) == 0 && !view_member (m)) { os << m.file () << ":" << m.line () << ":" << m.column () << ": " << "error: pointed-to class '" << class_fq_name (*c) << "' " @@ -2014,6 +2021,96 @@ namespace tree container_traits_; }; + struct view_data_member: traversal::data_member, context + { + view_data_member (semantics::class_& c) + : view_ (c), + amap_ (c.get ("alias-map")), + omap_ (c.get ("object-map")) {} + + virtual void + traverse (semantics::data_member& m) + { + using semantics::data_member; + + if (transient (m)) + return; + + semantics::type& t (utype (m)); + + if (semantics::class_* c = object_pointer (t)) + { + location const& l (m.location ()); + + if (lazy_pointer (t)) + { + error (l) << "lazy object pointer in view" << endl; + throw operation_failed (); + } + + // Find the corresponding associated object. First see if this + // data member name matches any aliases. + // + view_alias_map::iterator i (amap_.find (m.name ())); + + if (i == amap_.end ()) + i = amap_.find (public_name (m, false)); + + view_object* vo (0); + + if (i != amap_.end ()) + { + vo = i->second; + + if (vo->obj != c) // @@ Poly base/derived difference? + { + error (l) << "different pointed-to and associated objects" << endl; + info (vo->loc) << "associated object is defined here" << endl; + throw operation_failed (); + } + } + else + { + // If there is no alias match, try the object type. + // + view_object_map::iterator i (omap_.find (c)); + + if (i == omap_.end ()) + { + error (l) << "unable to find associated object for object " + << "pointer" << endl; + info (l) << "use associated object alias as this data member " + << "name" << endl; + throw operation_failed (); + } + + vo = i->second; + } + + if (vo->ptr != 0) + { + location const& l2 (vo->ptr->location ()); + + error (l) << "associated object is already loaded via another " + << "object pointer" << endl; + info (l2) << "the other data member is defined here" << endl; + info (l2) << "use associated object alias as this data member " + << "name to load a different object" << endl; + + throw operation_failed (); + } + + vo->ptr = &m; + m.set ("view-object", vo); + } + } + + private: + semantics::class_& view_; + view_alias_map& amap_; + view_object_map& omap_; + }; + // Figure out the "summary" added/deleted version for a composite // value type. // @@ -2485,6 +2582,7 @@ namespace } i->obj = &o; + i->ptr = 0; // Nothing yet. if (i->alias.empty ()) { @@ -2546,6 +2644,14 @@ namespace virtual void traverse_view_post (type& c) { + // Handle data members. + // + { + view_data_member t (c); + traversal::names n (t); + names (c, n); + } + // Figure out if we are versioned. Forced versioning is handled // in relational/processing. // @@ -2594,6 +2700,7 @@ namespace try { + bool raw; string ptr; string const& type (class_fq_name (c)); @@ -2623,15 +2730,20 @@ namespace if (p == "*") { + raw = true; ptr = type + "*"; cp_template = true; } else if (p[p.size () - 1] == '*') + { + raw = true; ptr = p; + } else if (p.find ('<') != string::npos) { // Template-id. // + raw = false; // Fair to assume not raw, though technically can be. ptr = p; decl_name.assign (p, 0, p.find ('<')); } @@ -2645,6 +2757,7 @@ namespace if (tc == TYPE_DECL) { + raw = (TREE_CODE (TREE_TYPE (decl)) == POINTER_TYPE); ptr = p; // This can be a typedef'ed alias for a TR1 template-id. @@ -2663,6 +2776,7 @@ namespace } else if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl)) { + raw = false; ptr = p + "< " + type + " >"; decl_name = p; cp_template = true; @@ -2713,7 +2827,10 @@ namespace // Namespace-specified pointer can only be '*' or are template. // if (p == "*") + { + raw = true; ptr = type + "*"; + } else if (p[p.size () - 1] == '*') { error (cp->loc) @@ -2735,6 +2852,7 @@ namespace if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl)) { + raw = false; ptr = p + "< " + type + " >"; decl_name = p; } @@ -2765,9 +2883,13 @@ namespace string const& p (options.default_pointer ()); if (p == "*") + { + raw = true; ptr = type + "*"; + } else { + raw = false; ptr = p + "< " + type + " >"; decl_name = p; } @@ -2917,6 +3039,7 @@ namespace } c.set ("object-pointer", ptr); + c.set ("object-pointer-raw", raw); } catch (invalid_name const& ex) { diff --git a/odb/relational/common.cxx b/odb/relational/common.cxx index 5e20f5f..22dfe48 100644 --- a/odb/relational/common.cxx +++ b/odb/relational/common.cxx @@ -8,6 +8,15 @@ using namespace std; namespace relational { + // member_image_type + // + string member_image_type:: + image_type (semantics::data_member&) + { + assert (false); + return string (); + } + // member_database_type_id // string member_database_type_id:: diff --git a/odb/relational/common.hxx b/odb/relational/common.hxx index abe5e31..a70bef0 100644 --- a/odb/relational/common.hxx +++ b/odb/relational/common.hxx @@ -174,34 +174,44 @@ namespace relational // should be called for this member. // virtual bool - pre (member_info&) - { - return true; - } + pre (member_info&) {return true;} virtual void - post (member_info&) - { - } + post (member_info&) {} virtual void - traverse_composite (member_info&) - { - } + traverse_composite (member_info&) {} virtual void - traverse_container (member_info&) - { - } + traverse_container (member_info&) {} - // Note that by default traverse_object_pointer() will traverse the + // Note that by default traverse_pointer() will traverse the // pointed-to object id type. // virtual void - traverse_object_pointer (member_info&); + traverse_pointer (member_info&); virtual void - traverse_simple (member_info&) = 0; + traverse_simple (member_info&) {} + }; + + // + // + struct member_image_type: virtual member_base + { + typedef member_image_type base; + + member_image_type (semantics::type* type = 0, + string const& fq_type = string (), + string const& key_prefix = string ()) + : member_base (type, fq_type, key_prefix) + { + } + + // Has to be overriden. + // + virtual string + image_type (semantics::data_member&); }; // diff --git a/odb/relational/common.txx b/odb/relational/common.txx index 9f22b88..4b873dd 100644 --- a/odb/relational/common.txx +++ b/odb/relational/common.txx @@ -31,8 +31,11 @@ namespace relational semantics::type* cont; if (semantics::class_* c = object_pointer (t)) { - semantics::type& t (utype (*id_member (*c))); - semantics::class_* comp (composite_wrapper (t)); + // A pointer in view might point to an object without id. + // + semantics::data_member* idm (id_member (*c)); + semantics::type& t (utype (idm != 0 ? *idm : m)); + semantics::class_* comp (idm != 0 ? composite_wrapper (t) : 0); member_info mi (m, (comp != 0 ? *comp : t), @@ -43,12 +46,14 @@ namespace relational mi.ptr = c; - if (comp == 0) + // Pointer in views aren't really a "column". + // + if (!view_member (m) && comp == 0) mi.st = &member_sql_type (m); if (pre (mi)) { - traverse_object_pointer (mi); + traverse_pointer (mi); post (mi); } } @@ -102,11 +107,14 @@ namespace relational template void member_base_impl:: - traverse_object_pointer (member_info& mi) + traverse_pointer (member_info& mi) { - if (composite (mi.t)) // Already unwrapped. - traverse_composite (mi); - else - traverse_simple (mi); + if (!view_member (mi.m)) // Not really "as if" pointed-to id member. + { + if (composite (mi.t)) // Already unwrapped. + traverse_composite (mi); + else + traverse_simple (mi); + } } } diff --git a/odb/relational/header.hxx b/odb/relational/header.hxx index 7a36d0e..dc20854 100644 --- a/odb/relational/header.hxx +++ b/odb/relational/header.hxx @@ -34,6 +34,81 @@ namespace relational } }; + template + struct image_member_impl: image_member, virtual member_base_impl + { + typedef image_member_impl base_impl; + + image_member_impl (base const& x) + : member_base::base (x), // virtual base + base (x), + member_image_type_ (base::type_override_, + base::fq_type_override_, + base::key_prefix_) + { + } + + typedef typename member_base_impl::member_info member_info; + + virtual bool + pre (member_info& mi) + { + if (container (mi)) + return false; + + image_type = member_image_type_->image_type (mi.m); + + if (var_override_.empty ()) + os << "// " << mi.m.name () << endl + << "//" << endl; + + return true; + } + + virtual void + traverse_pointer (member_info& mi) + { + // Object pointers in views require special treatment. + // + if (view_member (mi.m)) + { + using semantics::class_; + + class_& c (*mi.ptr); + class_* poly_root (polymorphic (c)); + bool poly_derived (poly_root != 0 && poly_root != &c); + + if (poly_derived) + // Use a helper to create a complete chain of images all + // the way to the root (see libodb/odb/view-image.hxx). + // + os << "view_object_image<" << endl + << " " << class_fq_name (c) << "," << endl + << " " << class_fq_name (*poly_root) << "," << endl + << " id_" << db << " >"; + else + os << "object_traits_impl< " << class_fq_name (c) << ", " << + "id_" << db << " >::image_type"; + + os << " " << mi.var << "value;" + << endl; + } + else + member_base_impl::traverse_pointer (mi); + } + + virtual void + traverse_composite (member_info& mi) + { + os << image_type << " " << mi.var << "value;" + << endl; + } + + protected: + string image_type; + instance member_image_type_; + }; + struct image_base: traversal::class_, virtual context { typedef image_base base; diff --git a/odb/relational/mssql/common.cxx b/odb/relational/mssql/common.cxx index a97b78d..be35ca9 100644 --- a/odb/relational/mssql/common.cxx +++ b/odb/relational/mssql/common.cxx @@ -184,6 +184,13 @@ namespace relational }; member_image_type:: + member_image_type (base const& x) + : member_base::base (x), // virtual base + base (x) + { + } + + member_image_type:: member_image_type (semantics::type* type, string const& fq_type, string const& key_prefix) @@ -314,6 +321,8 @@ namespace relational type_ = "unsigned char*"; } + entry member_image_type_; + // // member_database_type // diff --git a/odb/relational/mssql/common.hxx b/odb/relational/mssql/common.hxx index e597d4d..ec99776 100644 --- a/odb/relational/mssql/common.hxx +++ b/odb/relational/mssql/common.hxx @@ -119,12 +119,14 @@ namespace relational } }; - struct member_image_type: member_base + struct member_image_type: relational::member_image_type, + member_base { + member_image_type (base const&); member_image_type (semantics::type* type = 0, string const& fq_type = string (), string const& key_prefix = string ()); - string + virtual string image_type (semantics::data_member&); virtual void diff --git a/odb/relational/mssql/header.cxx b/odb/relational/mssql/header.cxx index ff16950..aa0e751 100644 --- a/odb/relational/mssql/header.cxx +++ b/odb/relational/mssql/header.cxx @@ -149,39 +149,14 @@ namespace relational }; entry image_type_; - struct image_member: relational::image_member, member_base + struct image_member: relational::image_member_impl, + member_base { image_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_image_type_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - image_type = member_image_type_.image_type (mi.m); - - if (var_override_.empty ()) - os << "// " << mi.m.name () << endl - << "//" << endl; - - return true; - } - - virtual void - traverse_composite (member_info& mi) - { - os << image_type << " " << mi.var << "value;" - << endl; - } + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) {} virtual void traverse_integer (member_info& mi) @@ -331,10 +306,6 @@ namespace relational << "SQLLEN " << mi.var << "size_ind;" << endl; } - - private: - string image_type; - member_image_type member_image_type_; }; entry image_member_; } diff --git a/odb/relational/mssql/source.cxx b/odb/relational/mssql/source.cxx index 2ce8810..ca36dfb 100644 --- a/odb/relational/mssql/source.cxx +++ b/odb/relational/mssql/source.cxx @@ -607,9 +607,9 @@ namespace relational } virtual void - get_null (member_info& mi) + get_null (string const& var) const { - os << "i." << mi.var << "size_ind == SQL_NULL_DATA"; + os << "i." << var << "size_ind == SQL_NULL_DATA"; } virtual void diff --git a/odb/relational/mysql/common.cxx b/odb/relational/mysql/common.cxx index 011bad7..6833ace 100644 --- a/odb/relational/mysql/common.cxx +++ b/odb/relational/mysql/common.cxx @@ -150,6 +150,13 @@ namespace relational }; member_image_type:: + member_image_type (base const& x) + : member_base::base (x), // virtual base + base (x) + { + } + + member_image_type:: member_image_type (semantics::type* type, string const& fq_type, string const& key_prefix) @@ -233,6 +240,8 @@ namespace relational type_ = "details::buffer"; } + entry member_image_type_; + // // member_database_type // diff --git a/odb/relational/mysql/common.hxx b/odb/relational/mysql/common.hxx index f6913ad..571ed51 100644 --- a/odb/relational/mysql/common.hxx +++ b/odb/relational/mysql/common.hxx @@ -81,12 +81,14 @@ namespace relational } }; - struct member_image_type: member_base + struct member_image_type: relational::member_image_type, + member_base { + member_image_type (base const&); member_image_type (semantics::type* type = 0, string const& fq_type = string (), string const& key_prefix = string ()); - string + virtual string image_type (semantics::data_member&); virtual void diff --git a/odb/relational/mysql/header.cxx b/odb/relational/mysql/header.cxx index bffb46d..189f005 100644 --- a/odb/relational/mysql/header.cxx +++ b/odb/relational/mysql/header.cxx @@ -15,39 +15,14 @@ namespace relational { namespace relational = relational::header; - struct image_member: relational::image_member, member_base + struct image_member: relational::image_member_impl, + member_base { image_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_image_type_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - image_type = member_image_type_.image_type (mi.m); - - if (var_override_.empty ()) - os << "// " << mi.m.name () << endl - << "//" << endl; - - return true; - } - - virtual void - traverse_composite (member_info& mi) - { - os << image_type << " " << mi.var << "value;" - << endl; - } + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) {} virtual void traverse_integer (member_info& mi) @@ -155,11 +130,6 @@ namespace relational << "my_bool " << mi.var << "null;" << endl; } - - private: - string image_type; - - member_image_type member_image_type_; }; entry image_member_; } diff --git a/odb/relational/mysql/source.cxx b/odb/relational/mysql/source.cxx index 0df66db..bcc7f23 100644 --- a/odb/relational/mysql/source.cxx +++ b/odb/relational/mysql/source.cxx @@ -301,142 +301,14 @@ namespace relational // grow // - struct grow_member: relational::grow_member, member_base + struct grow_member: relational::grow_member_impl, + member_base { grow_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - // Ignore polymorphic id references; they are not returned by - // the select statement. - // - if (mi.ptr != 0 && mi.m.count ("polymorphic-ref")) - return false; - - ostringstream ostr; - ostr << "t[" << index_ << "UL]"; - e = ostr.str (); - - if (var_override_.empty ()) - { - os << "// " << mi.m.name () << endl - << "//" << endl; - - semantics::class_* comp (composite (mi.t)); - - // If the member is soft- added or deleted, check the version. - // - unsigned long long av (added (mi.m)); - unsigned long long dv (deleted (mi.m)); - - // If this is a composite member, see if it is summarily - // added/deleted. - // - if (comp != 0) - { - unsigned long long cav (added (*comp)); - unsigned long long cdv (deleted (*comp)); - - if (cav != 0 && (av == 0 || av < cav)) - av = cav; - - if (cdv != 0 && (dv == 0 || dv > cdv)) - dv = cdv; - } - - // If the addition/deletion version is the same as the section's, - // then we don't need the test. - // - if (user_section* s = dynamic_cast (section_)) - { - if (av == added (*s->member)) - av = 0; - - if (dv == deleted (*s->member)) - dv = 0; - } - - if (av != 0 || dv != 0) - { - os << "if ("; - - if (av != 0) - os << "svm >= schema_version_migration (" << av << "ULL, true)"; - - if (av != 0 && dv != 0) - os << " &&" << endl; - - if (dv != 0) - os << "svm <= schema_version_migration (" << dv << "ULL, true)"; - - os << ")" - << "{"; - } - } - - return true; - } - - virtual void - post (member_info& mi) - { - semantics::class_* comp (composite (mi.t)); - - if (var_override_.empty ()) - { - unsigned long long av (added (mi.m)); - unsigned long long dv (deleted (mi.m)); - - if (comp != 0) - { - unsigned long long cav (added (*comp)); - unsigned long long cdv (deleted (*comp)); - - if (cav != 0 && (av == 0 || av < cav)) - av = cav; - - if (cdv != 0 && (dv == 0 || dv > cdv)) - dv = cdv; - } - - if (user_section* s = dynamic_cast (section_)) - { - if (av == added (*s->member)) - av = 0; - - if (dv == deleted (*s->member)) - dv = 0; - } - - if (av != 0 || dv != 0) - os << "}"; - } - - if (comp != 0) - index_ += column_count (*comp).total; - else - index_++; - } - - virtual void - traverse_composite (member_info& mi) - { - os << "if (composite_value_traits< " << mi.fq_type () << - ", id_mysql >::grow (" << endl - << "i." << mi.var << "value, t + " << index_ << "UL" << - (versioned (*composite (mi.t)) ? ", svm" : "") << "))" << endl - << "grew = true;" - << endl; - } + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) {} virtual void traverse_integer (member_info&) @@ -528,9 +400,6 @@ namespace relational << "grew = true;" << "}"; } - - private: - string e; }; entry grow_member_; @@ -694,9 +563,9 @@ namespace relational } virtual void - get_null (member_info& mi) + get_null (string const& var) const { - os << "i." << mi.var << "null"; + os << "i." << var << "null"; } virtual void diff --git a/odb/relational/oracle/common.cxx b/odb/relational/oracle/common.cxx index 9bfafde..a4f89a4 100644 --- a/odb/relational/oracle/common.cxx +++ b/odb/relational/oracle/common.cxx @@ -170,6 +170,13 @@ namespace relational // member_image_type:: + member_image_type (base const& x) + : member_base::base (x), // virtual base + base (x) + { + } + + member_image_type:: member_image_type (semantics::type* type, string const& fq_type, string const& key_prefix) @@ -270,6 +277,8 @@ namespace relational type_ = "oracle::lob_callback"; } + entry member_image_type_; + // // member_database_type // diff --git a/odb/relational/oracle/common.hxx b/odb/relational/oracle/common.hxx index 7872e5a..fc21468 100644 --- a/odb/relational/oracle/common.hxx +++ b/odb/relational/oracle/common.hxx @@ -89,12 +89,14 @@ namespace relational } }; - struct member_image_type: member_base + struct member_image_type: relational::member_image_type, + member_base { + member_image_type (base const&); member_image_type (semantics::type* type = 0, string const& fq_type = string (), string const& key_prefix = string ()); - string + virtual string image_type (semantics::data_member&); virtual void diff --git a/odb/relational/oracle/header.cxx b/odb/relational/oracle/header.cxx index 055ef7b..8ce4a20 100644 --- a/odb/relational/oracle/header.cxx +++ b/odb/relational/oracle/header.cxx @@ -52,39 +52,14 @@ namespace relational }; entry image_type_; - struct image_member: relational::image_member, member_base + struct image_member: relational::image_member_impl, + member_base { image_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_image_type_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - image_type = member_image_type_.image_type (mi.m); - - if (var_override_.empty ()) - os << "// " << mi.m.name () << endl - << "//" << endl; - - return true; - } - - virtual void - traverse_composite (member_info& mi) - { - os << image_type << " " << mi.var << "value;" - << endl; - } + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) {} virtual void traverse_int32 (member_info& mi) @@ -219,11 +194,6 @@ namespace relational << "oracle::lob " << mi.var << "lob;" << endl; } - - private: - string image_type; - - member_image_type member_image_type_; }; entry image_member_; diff --git a/odb/relational/oracle/source.cxx b/odb/relational/oracle/source.cxx index 2a795e2..ea4a14b 100644 --- a/odb/relational/oracle/source.cxx +++ b/odb/relational/oracle/source.cxx @@ -375,9 +375,9 @@ namespace relational } virtual void - get_null (member_info& mi) + get_null (string const& var) const { - os << "i." << mi.var << "indicator == -1"; + os << "i." << var << "indicator == -1"; } virtual void diff --git a/odb/relational/pgsql/common.cxx b/odb/relational/pgsql/common.cxx index 41cfc6b..6f04824 100644 --- a/odb/relational/pgsql/common.cxx +++ b/odb/relational/pgsql/common.cxx @@ -126,6 +126,13 @@ namespace relational } member_image_type:: + member_image_type (base const& x) + : member_base::base (x), // virtual base + base (x) + { + } + + member_image_type:: member_image_type (semantics::type* type, string const& fq_type, string const& key_prefix) @@ -196,6 +203,8 @@ namespace relational type_ = "unsigned char*"; } + entry member_image_type_; + // // member_database_type // diff --git a/odb/relational/pgsql/common.hxx b/odb/relational/pgsql/common.hxx index bb9d961..7fa8b60 100644 --- a/odb/relational/pgsql/common.hxx +++ b/odb/relational/pgsql/common.hxx @@ -69,12 +69,14 @@ namespace relational } }; - struct member_image_type: member_base + struct member_image_type: relational::member_image_type, + member_base { + member_image_type (base const&); member_image_type (semantics::type* type = 0, string const& fq_type = string (), string const& key_prefix = string ()); - string + virtual string image_type (semantics::data_member&); virtual void diff --git a/odb/relational/pgsql/header.cxx b/odb/relational/pgsql/header.cxx index a23ec9e..fd9093b 100644 --- a/odb/relational/pgsql/header.cxx +++ b/odb/relational/pgsql/header.cxx @@ -177,39 +177,14 @@ namespace relational }; entry section_traits_; - struct image_member: relational::image_member, member_base + struct image_member: relational::image_member_impl, + member_base { image_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_image_type_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - image_type = member_image_type_.image_type (mi.m); - - if (var_override_.empty ()) - os << "// " << mi.m.name () << endl - << "//" << endl; - - return true; - } - - virtual void - traverse_composite (member_info& mi) - { - os << image_type << " " << mi.var << "value;" - << endl; - } + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) {} virtual void traverse_integer (member_info& mi) @@ -290,11 +265,6 @@ namespace relational << "bool " << mi.var << "null;" << endl; } - - private: - string image_type; - - member_image_type member_image_type_; }; entry image_member_; } diff --git a/odb/relational/pgsql/source.cxx b/odb/relational/pgsql/source.cxx index b3d934d..9606e71 100644 --- a/odb/relational/pgsql/source.cxx +++ b/odb/relational/pgsql/source.cxx @@ -267,153 +267,14 @@ namespace relational // grow // - struct grow_member: relational::grow_member, member_base + struct grow_member: relational::grow_member_impl, + member_base { grow_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - if (section_ != 0 && *section_ != section (mi.m)) - return false; - - if (var_override_.empty ()) - { - // Ignore separately loaded members. - // - if (section_ == 0 && separate_load (mi.m)) - return false; - } - - // Ignore polymorphic id references; they are not returned by - // the select statement. - // - if (mi.ptr != 0 && mi.m.count ("polymorphic-ref")) - return false; - - ostringstream ostr; - ostr << "t[" << index_ << "UL]"; - e = ostr.str (); - - if (var_override_.empty ()) - { - os << "// " << mi.m.name () << endl - << "//" << endl; - - semantics::class_* comp (composite (mi.t)); - - // If the member is soft- added or deleted, check the version. - // - unsigned long long av (added (mi.m)); - unsigned long long dv (deleted (mi.m)); - - // If this is a composite member, see if it is summarily - // added/deleted. - // - if (comp != 0) - { - unsigned long long cav (added (*comp)); - unsigned long long cdv (deleted (*comp)); - - if (cav != 0 && (av == 0 || av < cav)) - av = cav; - - if (cdv != 0 && (dv == 0 || dv > cdv)) - dv = cdv; - } - - // If the addition/deletion version is the same as the section's, - // then we don't need the test. - // - if (user_section* s = dynamic_cast (section_)) - { - if (av == added (*s->member)) - av = 0; - - if (dv == deleted (*s->member)) - dv = 0; - } - - if (av != 0 || dv != 0) - { - os << "if ("; - - if (av != 0) - os << "svm >= schema_version_migration (" << av << "ULL, true)"; - - if (av != 0 && dv != 0) - os << " &&" << endl; - - if (dv != 0) - os << "svm <= schema_version_migration (" << dv << "ULL, true)"; - - os << ")" - << "{"; - } - } - - return true; - } - - virtual void - post (member_info& mi) - { - semantics::class_* comp (composite (mi.t)); - - if (var_override_.empty ()) - { - unsigned long long av (added (mi.m)); - unsigned long long dv (deleted (mi.m)); - - if (comp != 0) - { - unsigned long long cav (added (*comp)); - unsigned long long cdv (deleted (*comp)); - - if (cav != 0 && (av == 0 || av < cav)) - av = cav; - - if (cdv != 0 && (dv == 0 || dv > cdv)) - dv = cdv; - } - - if (user_section* s = dynamic_cast (section_)) - { - if (av == added (*s->member)) - av = 0; - - if (dv == deleted (*s->member)) - dv = 0; - } - - if (av != 0 || dv != 0) - os << "}"; - } - - if (comp != 0) - index_ += column_count (*comp).total; - else - index_++; - } - - virtual void - traverse_composite (member_info& mi) - { - os << "if (composite_value_traits< " << mi.fq_type () << - ", id_pgsql >::grow (" << endl - << "i." << mi.var << "value, t + " << index_ << "UL" << - (versioned (*composite (mi.t)) ? ", svm" : "") << "))" << endl - << "grew = true;" - << endl; - } + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) {} virtual void traverse_integer (member_info&) @@ -479,9 +340,6 @@ namespace relational os << e << " = 0;" << endl; } - - private: - string e; }; entry grow_member_; @@ -617,9 +475,9 @@ namespace relational } virtual void - get_null (member_info& mi) + get_null (string const& var) const { - os << "i." << mi.var << "null"; + os << "i." << var << "null"; } virtual void diff --git a/odb/relational/processor.cxx b/odb/relational/processor.cxx index b70451a..7658725 100644 --- a/odb/relational/processor.cxx +++ b/odb/relational/processor.cxx @@ -75,53 +75,61 @@ namespace relational if (semantics::class_* c = object_pointer (t)) { - // This is an object pointer. The column type is the pointed-to - // object id type. + // An object pointer in view doesn't really have a "column" + // so pretend that it has already been handled. // - semantics::data_member& id (*id_member (*c)); - - semantics::names* idhint; - semantics::type& idt (utype (id, idhint)); - - // The id type can be a composite value type. - // - if (composite_wrapper (idt)) - kind = composite; + if (view_member (m)) + kind = simple; else { - semantics::type* wt; - semantics::names* whint (0); - if ((wt = wrapper (idt, whint))) - wt = &utype (*wt, whint); - - if (type.empty () && id.count ("id-type")) - type = id.get ("id-type"); + // This is an object pointer. The column type is the pointed-to + // object id type. + // + semantics::data_member& id (*id_member (*c)); - if (type.empty () && id.count ("type")) - type = id.get ("type"); + semantics::names* idhint; + semantics::type& idt (utype (id, idhint)); - // The rest should be identical to the code for the id_type in - // the else block. + // The id type can be a composite value type. // - if (type.empty () && idt.count ("id-type")) - type = idt.get ("id-type"); + if (composite_wrapper (idt)) + kind = composite; + else + { + semantics::type* wt; + semantics::names* whint (0); + if ((wt = wrapper (idt, whint))) + wt = &utype (*wt, whint); - if (type.empty () && wt != 0 && wt->count ("id-type")) - type = wt->get ("id-type"); + if (type.empty () && id.count ("id-type")) + type = id.get ("id-type"); - if (type.empty () && idt.count ("type")) - type = idt.get ("type"); + if (type.empty () && id.count ("type")) + type = id.get ("type"); - if (type.empty () && wt != 0 && wt->count ("type")) - type = wt->get ("type"); + // The rest should be identical to the code for the id_type in + // the else block. + // + if (type.empty () && idt.count ("id-type")) + type = idt.get ("id-type"); - if (type.empty ()) - type = database_type (idt, idhint, true); + if (type.empty () && wt != 0 && wt->count ("id-type")) + type = wt->get ("id-type"); - if (type.empty () && wt != 0) - type = database_type (*wt, whint, true); + if (type.empty () && idt.count ("type")) + type = idt.get ("type"); - id_type = type; + if (type.empty () && wt != 0 && wt->count ("type")) + type = wt->get ("type"); + + if (type.empty ()) + type = database_type (idt, idhint, true); + + if (type.empty () && wt != 0) + type = database_type (*wt, whint, true); + + id_type = type; + } } } else @@ -454,6 +462,13 @@ namespace relational if (transient (m)) return; + semantics::type& t (utype (m)); + + // Object pointers are associated with objects. + // + if (object_pointer (t)) + return; + data_member* src_m (0); // Source member. // Resolve member references in column expressions. @@ -1493,10 +1508,16 @@ namespace relational // Ignore inverse sides of the same relationship to avoid // phony conflicts caused by the direct side that will end - // up in the relationship list as well. + // up in the relationship list as well. Unless the inverse + // member is in the polymorphic base in which case we will + // miss it since we don't examine inside poly bases on the + // backwards scan (see above). // - if (inverse (m)) - return; + if (semantics::data_member* im = inverse (m)) + { + if (&im->scope () == &c) // Direct member. + return; + } // Ignore self-pointers if requested. // diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx index fad4ac5..39a210e 100644 --- a/odb/relational/source.cxx +++ b/odb/relational/source.cxx @@ -4741,6 +4741,594 @@ traverse_view (type& c) if (!schema_name.empty ()) schema_name = strlit (schema_name); + // Generate the from-list. Also collect relationships via which + // the objects are joined. + // + strings from; + view_relationship_map rel_map; + + if (vq.kind == view_query::condition) + { + view_objects& objs (c.get ("objects")); + + for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i) + { + bool first (i == objs.begin ()); + string l; + + // + // Tables. + // + + if (i->kind == view_object::table) + { + if (first) + { + l = "FROM "; + l += quote_id (i->tbl_name); + + if (!i->alias.empty ()) + l += (need_alias_as ? " AS " : " ") + quote_id (i->alias); + + from.push_back (l); + continue; + } + + l = "LEFT JOIN "; + l += quote_id (i->tbl_name); + + if (!i->alias.empty ()) + l += (need_alias_as ? " AS " : " ") + quote_id (i->alias); + + semantics::scope& scope ( + dynamic_cast (*unit.find (i->scope))); + + expression e ( + translate_expression ( + c, i->cond, scope, i->loc, "table")); + + if (e.kind != expression::literal) + { + error (i->loc) << "invalid join condition in db pragma " << + "table" << endl; + throw operation_failed (); + } + + l += " ON"; + + // Add the pragma location for easier error tracking. + // + from.push_back (l); + from.push_back ("// From " + location_string (i->loc, true)); + from.push_back (e.value); + continue; + } + + // + // Objects. + // + semantics::class_& o (*i->obj); + + bool poly (polymorphic (o)); + size_t poly_depth (poly ? polymorphic_depth (o) : 1); + + string alias (i->alias); + + // For polymorphic objects, alias is just a prefix. + // + if (poly && !alias.empty ()) + alias += "_" + table_name (o).uname (); + + // First object. + // + if (first) + { + l = "FROM "; + l += table_qname (o); + + if (!alias.empty ()) + l += (need_alias_as ? " AS " : " ") + quote_id (alias); + + from.push_back (l); + + if (poly_depth != 1) + { + bool t (true); //@@ (im)perfect forwarding + size_t d (poly_depth - 1); //@@ (im)perfect forward. + instance j (o, t, d, i->alias); + j->traverse (polymorphic_base (o)); + + from.insert (from.end (), j->begin (), j->end ()); + query_optimize = query_optimize || !j->joins.empty (); + } + continue; + } + + semantics::scope& scope ( + dynamic_cast (*unit.find (i->scope))); + + expression e ( + translate_expression ( + c, i->cond, scope, i->loc, "object")); + + // Literal expression. + // + if (e.kind == expression::literal) + { + l = "LEFT JOIN "; + l += table_qname (o); + + if (!alias.empty ()) + l += (need_alias_as ? " AS " : " ") + quote_id (alias); + + l += " ON"; + + // Add the pragma location for easier error tracking. + // + from.push_back (l); + from.push_back ("// From " + location_string (i->loc, true)); + from.push_back (e.value); + + if (poly_depth != 1) + { + bool t (true); //@@ (im)perfect forwarding + size_t d (poly_depth - 1); //@@ (im)perfect forward. + instance j (o, t, d, i->alias); + j->traverse (polymorphic_base (o)); + + from.insert (from.end (), j->begin (), j->end ()); + query_optimize = query_optimize || !j->joins.empty (); + } + continue; + } + + // We have an object relationship (pointer) for which we need + // to come up with the corresponding JOIN condition. If this + // is a to-many relationship, then we first need to JOIN the + // container table. This code is similar to object_joins. + // + using semantics::data_member; + + data_member& m (*e.member_path.back ()); + data_member* im (inverse (m)); + + // Resolve the pointed-to object to view_object and do + // some sanity checks while at it. + // + semantics::class_* c (0); + + if (semantics::type* cont = container (m)) + c = object_pointer (container_vt (*cont)); + else + c = object_pointer (utype (m)); + + view_object* vo (0); + + // Check if the pointed-to object has been previously associated + // with this view and is unambiguous. A pointer to ourselves is + // always assumed to point to this association. + // + if (&o == c) + vo = &*i; + else + { + bool ambig (false); + + for (view_objects::iterator j (objs.begin ()); j != i; ++j) + { + if (j->obj != c) + continue; + + if (vo == 0) + { + vo = &*j; + continue; + } + + // If it is the first ambiguous object, issue the + // error. + // + if (!ambig) + { + error (i->loc) << "pointed-to object '" << class_name (*c) << + "' is ambiguous" << endl; + info (i->loc) << "candidates are:" << endl; + info (vo->loc) << " '" << vo->name () << "'" << endl; + ambig = true; + } + + info (j->loc) << " '" << j->name () << "'" << endl; + } + + if (ambig) + { + info (i->loc) << "use the other side of the relationship " << + "or full join condition clause in db pragma object to " << + "resolve this ambiguity" << endl; + throw operation_failed (); + } + + if (vo == 0) + { + error (i->loc) << "pointed-to object '" << class_name (*c) << + "' specified in the join condition has not been " << + "previously associated with this view" << endl; + throw operation_failed (); + } + } + + // JOIN relationship points to us: + // vo - us + // e.vo - other side + // e.member_path - in other side + // + // JOIN relationship points to other side: + // vo - other side + // e.vo - us + // e.member_path - in us + // + if (im == 0) + rel_map.insert (make_pair (e.member_path, make_pair (e.vo, vo))); + else + rel_map.insert ( + make_pair (data_member_path (*im), make_pair (vo, e.vo))); + + // Left and right-hand side table names. + // + qname lt; + { + using semantics::class_; + + class_& o (*e.vo->obj); + string const& a (e.vo->alias); + + if (class_* root = polymorphic (o)) + { + // If the object is polymorphic, then figure out which of the + // bases this member comes from and use the corresponding + // table. + // + class_* c ( + &static_cast ( + e.member_path.front ()->scope ())); + + // If this member's class is not polymorphic (root uses reuse + // inheritance), then use the root table. + // + if (!polymorphic (*c)) + c = root; + + qname const& t (table_name (*c)); + + if (a.empty ()) + lt = t; + else + lt = qname (a + "_" + t.uname ()); + } + else + lt = a.empty () ? table_name (o) : qname (a); + } + + qname rt; + { + qname t (table_name (*vo->obj)); + string const& a (vo->alias); + rt = a.empty () + ? t + : qname (polymorphic (*vo->obj) ? a + "_" + t.uname () : a); + } + + // First join the container table if necessary. + // + semantics::type* cont (container (im != 0 ? *im : m)); + + string ct; // Container table. + if (cont != 0) + { + l = "LEFT JOIN "; + + // The same relationship can be used by multiple associated + // objects. So if the object that contains this container has + // an alias, then also construct one for the table that we + // are joining. + // + { + using semantics::class_; + + qname t; + + // In a polymorphic hierarchy the member can be in a base (see + // above). + // + if (class_* root = polymorphic (im != 0 ? *vo->obj : *e.vo->obj)) + { + class_* c; + if (im == 0) + { + c = &static_cast (e.member_path.front ()->scope ()); + + if (!polymorphic (*c)) + c = root; + + t = table_name (*c, e.member_path); + } + else + { + c = &static_cast (im->scope ()); + + if (!polymorphic (*c)) + c = root; + + t = table_name (*im, table_prefix (*c)); + } + } + else + t = im != 0 + ? table_name (*im, table_prefix (*vo->obj)) + : table_name (*e.vo->obj, e.member_path); + + // The tricky part is to figure out which view_object, vo + // or e.vo we should use. We want to use the alias from the + // object that actually contains this container. The following + // might not make intuitive sense, but it has been verified + // with the truth table. + // + string const& a (im != 0 ? vo->alias : e.vo->alias); + + if (a.empty ()) + { + ct = quote_id (t); + l += ct; + } + else + { + ct = quote_id (a + '_' + t.uname ()); + l += quote_id (t); + l += (need_alias_as ? " AS " : " ") + ct; + } + } + + l += " ON"; + from.push_back (l); + + // If we are the pointed-to object, then we have to turn + // things around. This is necessary to have the proper + // JOIN order. There seems to be a pattern there but it + // is not yet intuitively clear what it means. + // + instance c_cols; // Container columns. + instance o_cols; // Object columns. + + qname* ot; // Object table (either lt or rt). + + if (im != 0) + { + if (&o == c) + { + // container.value = pointer.id + // + semantics::data_member& id (*id_member (*e.vo->obj)); + + c_cols->traverse (*im, utype (id), "value", "value"); + o_cols->traverse (id); + ot = < + } + else + { + // container.id = pointed-to.id + // + semantics::data_member& id (*id_member (*vo->obj)); + + c_cols->traverse ( + *im, utype (id), "id", "object_id", vo->obj); + o_cols->traverse (id); + ot = &rt; + } + } + else + { + if (&o == c) + { + // container.id = pointer.id + // + semantics::data_member& id (*id_member (*e.vo->obj)); + + c_cols->traverse ( + m, utype (id), "id", "object_id", e.vo->obj); + o_cols->traverse (id); + ot = < + } + else + { + // container.value = pointed-to.id + // + semantics::data_member& id (*id_member (*vo->obj)); + + c_cols->traverse (m, utype (id), "value", "value"); + o_cols->traverse (id); + ot = &rt; + } + } + + for (object_columns_list::iterator b (c_cols->begin ()), i (b), + j (o_cols->begin ()); i != c_cols->end (); ++i, ++j) + { + l.clear (); + + if (i != b) + l += "AND "; + + l += ct; + l += '.'; + l += quote_id (i->name); + l += '='; + l += quote_id (*ot); + l += '.'; + l += quote_id (j->name); + + from.push_back (strlit (l)); + } + } + + l = "LEFT JOIN "; + l += table_qname (o); + + if (!alias.empty ()) + l += (need_alias_as ? " AS " : " ") + quote_id (alias); + + l += " ON"; + from.push_back (l); + + if (cont != 0) + { + instance c_cols; // Container columns. + instance o_cols; // Object columns. + + qname* ot; // Object table (either lt or rt). + + if (im != 0) + { + if (&o == c) + { + // container.id = pointed-to.id + // + semantics::data_member& id (*id_member (*vo->obj)); + + c_cols->traverse (*im, utype (id), "id", "object_id", vo->obj); + o_cols->traverse (id); + ot = &rt; + } + else + { + // container.value = pointer.id + // + semantics::data_member& id (*id_member (*e.vo->obj)); + + c_cols->traverse (*im, utype (id), "value", "value"); + o_cols->traverse (id); + ot = < + } + } + else + { + if (&o == c) + { + // container.value = pointed-to.id + // + semantics::data_member& id (*id_member (*vo->obj)); + + c_cols->traverse (m, utype (id), "value", "value"); + o_cols->traverse (id); + ot = &rt; + } + else + { + // container.id = pointer.id + // + semantics::data_member& id (*id_member (*e.vo->obj)); + + c_cols->traverse (m, utype (id), "id", "object_id", e.vo->obj); + o_cols->traverse (id); + ot = < + } + } + + for (object_columns_list::iterator b (c_cols->begin ()), i (b), + j (o_cols->begin ()); i != c_cols->end (); ++i, ++j) + { + l.clear (); + + if (i != b) + l += "AND "; + + l += ct; + l += '.'; + l += quote_id (i->name); + l += '='; + l += quote_id (*ot); + l += '.'; + l += quote_id (j->name); + + from.push_back (strlit (l)); + } + } + else + { + column_prefix col_prefix; + + if (im == 0) + col_prefix = column_prefix (e.member_path); + + instance l_cols (col_prefix); + instance r_cols; + + if (im != 0) + { + // our.id = pointed-to.pointer + // + l_cols->traverse (*id_member (*e.vo->obj)); + r_cols->traverse (*im); + } + else + { + // our.pointer = pointed-to.id + // + l_cols->traverse (*e.member_path.back ()); + r_cols->traverse (*id_member (*vo->obj)); + } + + for (object_columns_list::iterator b (l_cols->begin ()), i (b), + j (r_cols->begin ()); i != l_cols->end (); ++i, ++j) + { + l.clear (); + + if (i != b) + l += "AND "; + + l += quote_id (lt); + l += '.'; + l += quote_id (i->name); + l += '='; + l += quote_id (rt); + l += '.'; + l += quote_id (j->name); + + from.push_back (strlit (l)); + } + } + + if (poly_depth != 1) + { + bool t (true); //@@ (im)perfect forwarding + size_t d (poly_depth - 1); //@@ (im)perfect forward. + instance j (o, t, d, i->alias); + j->traverse (polymorphic_base (o)); + + from.insert (from.end (), j->begin (), j->end ()); + query_optimize = query_optimize || !j->joins.empty (); + } + } // End JOIN-generating for-loop. + } + + // Check that pointed-to objects inside objects that we are loading + // have session support enabled (required to have a shared copy). + // Also see if we need to throw if there is no session. + // + bool need_session (false); + if (vq.kind == view_query::condition) + { + view_objects& objs (c.get ("objects")); + for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i) + { + if (i->kind != view_object::object || i->ptr == 0) + continue; // Not an object or not loaded. + + instance t (*i, rel_map); + t->traverse (*i->obj); + need_session = need_session || t->session_; + } + } + os << "// " << class_name (c) << endl << "//" << endl << endl; @@ -4843,7 +5431,19 @@ traverse_view (type& c) os << endl; + if (need_session) + os << "if (!session::has_current ())" << endl + << "throw session_required ();" + << endl; + + if (has_a (c, test_pointer)) + os << db << "::connection& conn (" << endl + << db << "::transaction::current ().connection ());" + << endl; + + names (c, init_view_pointer_member_pre_names_); names (c, init_value_member_names_); + names (c, init_view_pointer_member_post_names_); os << "}"; } @@ -4926,512 +5526,11 @@ traverse_view (type& c) } else // vq.kind == view_query::condition { - // Generate the from-list. + // Use the from-list generated above. // - strings from; - view_objects const& objs (c.get ("objects")); - - for (view_objects::const_iterator i (objs.begin ()); - i != objs.end (); - ++i) - { - bool first (i == objs.begin ()); - string l; - - // - // Tables. - // - - if (i->kind == view_object::table) - { - if (first) - { - l = "FROM "; - l += quote_id (i->tbl_name); - - if (!i->alias.empty ()) - l += (need_alias_as ? " AS " : " ") + quote_id (i->alias); - - from.push_back (l); - continue; - } - - l = "LEFT JOIN "; - l += quote_id (i->tbl_name); - - if (!i->alias.empty ()) - l += (need_alias_as ? " AS " : " ") + quote_id (i->alias); - - semantics::scope& scope ( - dynamic_cast (*unit.find (i->scope))); - - expression e ( - translate_expression ( - c, i->cond, scope, i->loc, "table")); - - if (e.kind != expression::literal) - { - error (i->loc) << "invalid join condition in db pragma " << - "table" << endl; - throw operation_failed (); - } - - l += " ON"; - - // Add the pragma location for easier error tracking. - // - from.push_back (l); - from.push_back ("// From " + location_string (i->loc, true)); - from.push_back (e.value); - continue; - } - - // - // Objects. - // - semantics::class_& o (*i->obj); - - bool poly (polymorphic (o)); - size_t poly_depth (poly ? polymorphic_depth (o) : 1); - - string alias (i->alias); - - // For polymorphic objects, alias is just a prefix. - // - if (poly && !alias.empty ()) - alias += "_" + table_name (o).uname (); - - // First object. - // - if (first) - { - l = "FROM "; - l += table_qname (o); - - if (!alias.empty ()) - l += (need_alias_as ? " AS " : " ") + quote_id (alias); - - from.push_back (l); - - if (poly_depth != 1) - { - bool t (true); //@@ (im)perfect forwarding - size_t d (poly_depth - 1); //@@ (im)perfect forward. - instance j (o, t, d, i->alias); - j->traverse (polymorphic_base (o)); - - from.insert (from.end (), j->begin (), j->end ()); - query_optimize = query_optimize || !j->joins.empty (); - } - continue; - } - - semantics::scope& scope ( - dynamic_cast (*unit.find (i->scope))); - - expression e ( - translate_expression ( - c, i->cond, scope, i->loc, "object")); - - // Literal expression. - // - if (e.kind == expression::literal) - { - l = "LEFT JOIN "; - l += table_qname (o); - - if (!alias.empty ()) - l += (need_alias_as ? " AS " : " ") + quote_id (alias); - - l += " ON"; - - // Add the pragma location for easier error tracking. - // - from.push_back (l); - from.push_back ("// From " + location_string (i->loc, true)); - from.push_back (e.value); - - if (poly_depth != 1) - { - bool t (true); //@@ (im)perfect forwarding - size_t d (poly_depth - 1); //@@ (im)perfect forward. - instance j (o, t, d, i->alias); - j->traverse (polymorphic_base (o)); - - from.insert (from.end (), j->begin (), j->end ()); - query_optimize = query_optimize || !j->joins.empty (); - } - continue; - } - - // We have an object relationship (pointer) for which we need - // to come up with the corresponding JOIN condition. If this - // is a to-many relationship, then we first need to JOIN the - // container table. This code is similar to object_joins. - // - using semantics::data_member; - - data_member& m (*e.member_path.back ()); - - // Resolve the pointed-to object to view_object and do - // some sanity checks while at it. - // - semantics::class_* c (0); - - if (semantics::type* cont = container (m)) - c = object_pointer (container_vt (*cont)); - else - c = object_pointer (utype (m)); - - view_object const* vo (0); - - // Check if the pointed-to object has been previously - // associated with this view and is unambiguous. A - // pointer to ourselves is always assumed to point - // to this association. - // - if (&o == c) - vo = &*i; - else - { - bool ambig (false); - - for (view_objects::const_iterator j (objs.begin ()); - j != i; - ++j) - { - if (j->obj != c) - continue; - - if (vo == 0) - { - vo = &*j; - continue; - } - - // If it is the first ambiguous object, issue the - // error. - // - if (!ambig) - { - error (i->loc) << "pointed-to object '" << class_name (*c) << - "' is ambiguous" << endl; - info (i->loc) << "candidates are:" << endl; - info (vo->loc) << " '" << vo->name () << "'" << endl; - ambig = true; - } - - info (j->loc) << " '" << j->name () << "'" << endl; - } - - if (ambig) - { - info (i->loc) << "use the full join condition clause in db " << - "pragma object to resolve this ambiguity" << endl; - throw operation_failed (); - } - - if (vo == 0) - { - error (i->loc) << "pointed-to object '" << class_name (*c) << - "' specified in the join condition has not been " << - "previously associated with this view" << endl; - throw operation_failed (); - } - } - - // Left and right-hand side table names. - // - qname lt; - { - using semantics::class_; - - class_& o (*e.vo->obj); - string const& a (e.vo->alias); - - if (class_* root = polymorphic (o)) - { - // If the object is polymorphic, then figure out which of the - // bases this member comes from and use the corresponding - // table. - // - class_* c ( - &static_cast ( - e.member_path.front ()->scope ())); - - // If this member's class is not polymorphic (root uses reuse - // inheritance), then use the root table. - // - if (!polymorphic (*c)) - c = root; - - qname const& t (table_name (*c)); - - if (a.empty ()) - lt = t; - else - lt = qname (a + "_" + t.uname ()); - } - else - lt = a.empty () ? table_name (o) : qname (a); - } - - qname rt; - { - qname t (table_name (*vo->obj)); - string const& a (vo->alias); - rt = a.empty () - ? t - : qname (polymorphic (*vo->obj) ? a + "_" + t.uname () : a); - } - - // First join the container table if necessary. - // - data_member* im (inverse (m)); - - semantics::type* cont (container (im != 0 ? *im : m)); - - string ct; // Container table. - if (cont != 0) - { - if (im != 0) - { - // For now a direct member can only be directly in - // the object scope. If this changes, the inverse() - // function would have to return a member path instead - // of just a single member. - // - ct = table_qname (*im, table_prefix (*vo->obj)); - } - else - ct = table_qname (*e.vo->obj, e.member_path); - - l = "LEFT JOIN "; - l += ct; - l += " ON"; - from.push_back (l); - - // If we are the pointed-to object, then we have to turn - // things around. This is necessary to have the proper - // JOIN order. There seems to be a pattern there but it - // is not yet intuitively clear what it means. - // - instance c_cols; // Container columns. - instance o_cols; // Object columns. - - qname* ot; // Object table (either lt or rt). - - if (im != 0) - { - if (&o == c) - { - // container.value = pointer.id - // - semantics::data_member& id (*id_member (*e.vo->obj)); - - c_cols->traverse (*im, utype (id), "value", "value"); - o_cols->traverse (id); - ot = < - } - else - { - // container.id = pointed-to.id - // - semantics::data_member& id (*id_member (*vo->obj)); - - c_cols->traverse ( - *im, utype (id), "id", "object_id", vo->obj); - o_cols->traverse (id); - ot = &rt; - } - } - else - { - if (&o == c) - { - // container.id = pointer.id - // - semantics::data_member& id (*id_member (*e.vo->obj)); - - c_cols->traverse ( - m, utype (id), "id", "object_id", e.vo->obj); - o_cols->traverse (id); - ot = < - } - else - { - // container.value = pointed-to.id - // - semantics::data_member& id (*id_member (*vo->obj)); - - c_cols->traverse (m, utype (id), "value", "value"); - o_cols->traverse (id); - ot = &rt; - } - } - - for (object_columns_list::iterator b (c_cols->begin ()), i (b), - j (o_cols->begin ()); i != c_cols->end (); ++i, ++j) - { - l.clear (); - - if (i != b) - l += "AND "; - - l += ct; - l += '.'; - l += quote_id (i->name); - l += '='; - l += quote_id (*ot); - l += '.'; - l += quote_id (j->name); - - from.push_back (strlit (l)); - } - } - - l = "LEFT JOIN "; - l += table_qname (o); - - if (!alias.empty ()) - l += (need_alias_as ? " AS " : " ") + quote_id (alias); - - l += " ON"; - from.push_back (l); - - if (cont != 0) - { - instance c_cols; // Container columns. - instance o_cols; // Object columns. - - qname* ot; // Object table (either lt or rt). - - if (im != 0) - { - if (&o == c) - { - // container.id = pointed-to.id - // - semantics::data_member& id (*id_member (*vo->obj)); - - c_cols->traverse (*im, utype (id), "id", "object_id", vo->obj); - o_cols->traverse (id); - ot = &rt; - } - else - { - // container.value = pointer.id - // - semantics::data_member& id (*id_member (*e.vo->obj)); - - c_cols->traverse (*im, utype (id), "value", "value"); - o_cols->traverse (id); - ot = < - } - } - else - { - if (&o == c) - { - // container.value = pointed-to.id - // - semantics::data_member& id (*id_member (*vo->obj)); - - c_cols->traverse (m, utype (id), "value", "value"); - o_cols->traverse (id); - ot = &rt; - } - else - { - // container.id = pointer.id - // - semantics::data_member& id (*id_member (*e.vo->obj)); - - c_cols->traverse (m, utype (id), "id", "object_id", e.vo->obj); - o_cols->traverse (id); - ot = < - } - } - - for (object_columns_list::iterator b (c_cols->begin ()), i (b), - j (o_cols->begin ()); i != c_cols->end (); ++i, ++j) - { - l.clear (); - - if (i != b) - l += "AND "; - - l += ct; - l += '.'; - l += quote_id (i->name); - l += '='; - l += quote_id (*ot); - l += '.'; - l += quote_id (j->name); - - from.push_back (strlit (l)); - } - } - else - { - column_prefix col_prefix; - - if (im == 0) - col_prefix = column_prefix (e.member_path); - - instance l_cols (col_prefix); - instance r_cols; - - if (im != 0) - { - // our.id = pointed-to.pointer - // - l_cols->traverse (*id_member (*e.vo->obj)); - r_cols->traverse (*im); - } - else - { - // our.pointer = pointed-to.id - // - l_cols->traverse (*e.member_path.back ()); - r_cols->traverse (*id_member (*vo->obj)); - } - - for (object_columns_list::iterator b (l_cols->begin ()), i (b), - j (r_cols->begin ()); i != l_cols->end (); ++i, ++j) - { - l.clear (); - - if (i != b) - l += "AND "; - - l += quote_id (lt); - l += '.'; - l += quote_id (i->name); - l += '='; - l += quote_id (rt); - l += '.'; - l += quote_id (j->name); - - from.push_back (strlit (l)); - } - } - - if (poly_depth != 1) - { - bool t (true); //@@ (im)perfect forwarding - size_t d (poly_depth - 1); //@@ (im)perfect forward. - instance j (o, t, d, i->alias); - j->traverse (polymorphic_base (o)); - - from.insert (from.end (), j->begin (), j->end ()); - query_optimize = query_optimize || !j->joins.empty (); - } - } // End JOIN-generating for-loop. - statement_columns sc; { - instance t (sc); + instance t (sc, from, rel_map); t->traverse (c); process_statement_columns ( sc, statement_select, versioned || query_optimize); diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx index cbdec73..a725b6a 100644 --- a/odb/relational/source.hxx +++ b/odb/relational/source.hxx @@ -77,12 +77,29 @@ namespace relational { typedef object_columns base; + // If provided, used to resolve table/alias names for inverse + // pointed-to and base objects. Returns qualified name. + // + struct table_name_resolver + { + virtual string + resolve_pointer (semantics::data_member&) const = 0; + + virtual string + resolve_base (semantics::class_&) const = 0; + }; + object_columns (statement_kind sk, statement_columns& sc, query_parameters* param = 0, object_section* section = 0) : object_columns_base (true, true, section), - sk_ (sk), ro_ (true), sc_ (sc), param_ (param), depth_ (1) + sk_ (sk), + ro_ (true), + sc_ (sc), + param_ (param), + table_name_resolver_ (0), + depth_ (1) { } @@ -91,7 +108,12 @@ namespace relational statement_columns& sc, query_parameters* param) : object_columns_base (true, true, 0), - sk_ (sk), ro_ (ignore_ro), sc_ (sc), param_ (param), depth_ (1) + sk_ (sk), + ro_ (ignore_ro), + sc_ (sc), + param_ (param), + table_name_resolver_ (0), + depth_ (1) { } @@ -99,13 +121,15 @@ namespace relational statement_kind sk, statement_columns& sc, size_t depth = 1, - object_section* section = 0) + object_section* section = 0, + table_name_resolver* tnr = 0) : object_columns_base (true, true, section), sk_ (sk), ro_ (true), sc_ (sc), param_ (0), table_name_ (table_qname), + table_name_resolver_ (tnr), depth_ (depth) { } @@ -143,7 +167,12 @@ namespace relational if (sk_ == statement_select && --depth_ != 0) { - table_name_ = table_qname (polymorphic_base (c)); + semantics::class_& b (polymorphic_base (c)); + + table_name_ = table_name_resolver_ != 0 + ? table_name_resolver_->resolve_base (b) + : table_qname (b); + inherits (c); } } @@ -196,7 +225,12 @@ namespace relational string table; if (!table_name_.empty ()) - table = table_qname (*im, table_prefix (imc)); + { + if (table_name_resolver_ != 0) + table = table_name_resolver_->resolve_pointer (m); + else + table = table_qname (*im, table_prefix (imc)); + } instance oc (table, sk_, sc_); oc->traverse (*im, idt, "id", "object_id", &imc); @@ -219,32 +253,37 @@ namespace relational if (!table_name_.empty ()) { - string n; - - if (composite_wrapper (idt)) - { - n = column_prefix (m, key_prefix_, default_name_).prefix; - - if (n.empty ()) - n = public_name_db (m); - else if (n[n.size () - 1] == '_') - n.resize (n.size () - 1); // Remove trailing underscore. - } + if (table_name_resolver_ != 0) + alias = table_name_resolver_->resolve_pointer (m); else { - bool dummy; - n = column_name (m, key_prefix_, default_name_, dummy); - } + string n; - alias = compose_name (column_prefix_.prefix, n); + if (composite_wrapper (idt)) + { + n = column_prefix (m, key_prefix_, default_name_).prefix; - if (poly) - { - qname const& table (table_name (imc)); - alias = quote_id (alias + "_" + table.uname ()); + if (n.empty ()) + n = public_name_db (m); + else if (n[n.size () - 1] == '_') + n.resize (n.size () - 1); // Remove trailing underscore. + } + else + { + bool dummy; + n = column_name (m, key_prefix_, default_name_, dummy); + } + + alias = compose_name (column_prefix_.prefix, n); + + if (poly) + { + qname const& table (table_name (imc)); + alias = quote_id (alias + "_" + table.uname ()); + } + else + alias = quote_id (alias); } - else - alias = quote_id (alias); } instance oc (alias, sk_, sc_); @@ -312,14 +351,233 @@ namespace relational statement_columns& sc_; query_parameters* param_; string table_name_; + table_name_resolver* table_name_resolver_; size_t depth_; }; - struct view_columns: object_columns_base, virtual context + struct view_columns: object_columns_base, + object_columns::table_name_resolver, + virtual context { typedef view_columns base; - view_columns (statement_columns& sc): sc_ (sc), in_composite_ (false) {} + view_columns (statement_columns& sc, + strings& from, + const view_relationship_map& rm) + : sc_ (sc), from_ (from), rel_map_ (rm), in_composite_ (false) {} + + // Implementation of table_name_resolver for object_columns. + // + virtual string + resolve_pointer (semantics::data_member& m) const + { + view_object& us (*ptr_->get ("view-object")); + + semantics::data_member& im (*inverse (m)); + + typedef view_relationship_map::const_iterator iterator; + + std::pair r ( + rel_map_.equal_range (data_member_path (im))); + + using semantics::class_; + + for (; r.first != r.second; ++r.first) + { + if (r.first->second.second != &us) // Not our associated. + continue; + + view_object& vo (*r.first->second.first); // First because inverse. + + // Derive the table name the same way as the JOIN code. + // + class_* c (vo.obj); + if (class_* root = polymorphic (*c)) + { + // Can be in base. + // + c = &static_cast (im.scope ()); + + if (!polymorphic (*c)) + c = root; + } + + string const& a (vo.alias); + + if (container (im)) + { + // If this is a container, then object_columns will use the + // column from the container table, not from the object table + // (which, strictly speaking, might not have been JOIN'ed). + // + qname t (table_name (im, table_prefix (*c))); + return a.empty () + ? quote_id (t) + : quote_id (a + '_' + t.uname ()); + } + else + { + qname t; + if (a.empty ()) + t = table_name (*c); + else + { + if (polymorphic (*c)) + t = qname (a + "_" + table_name (*c).uname ()); + else + t = qname (a); + } + return quote_id (t); + } + } + + // So there is no associated object for this column. The initial + // plan was to complain and ask the user to explicitly associate + // the object. This is not a bad plan except for one thing: if + // the direct side of the relationship is a container, then + // associating that object explicitly will result in both the + // container table and the object table being JOIN'ed. But we + // only need the container table (for the object id) So we will + // be joining a table for nothing, which is not very clean. So + // the alternative, and more difficult, plan is to go ahead and + // add the necessary JOIN's automatically. + // + // This code follows the normal JOIN generation code. + // + class_* o (object_pointer (utype (m))); + if (class_* root = polymorphic (*o)) + { + o = &static_cast (im.scope ()); + + if (!polymorphic (*o)) + o = root; + } + + string const& a (us.alias); + string lt (a.empty () ? table_qname (*us.obj) : quote_id (a)); + string rt; + + qname ct ( + container (im) + ? table_name (im, table_prefix (*o)) + : table_name (*o)); + + string l ("LEFT JOIN "); + + if (a.empty ()) + { + rt = quote_id (ct); + l += rt; + } + else + { + // The same relationship can be used by multiple associated + // objects. So if we have an alias, then also construct one + // for the table that we are joining. + // + rt = quote_id (a + '_' + ct.uname ()); + l += quote_id (ct); + l += (need_alias_as ? " AS " : " ") + rt; + } + + l += " ON"; + from_.push_back (l); + + instance l_cols; // Our id columns. + instance r_cols; // Other side id columns. + + semantics::data_member& id (*id_member (*us.obj)); + + l_cols->traverse (id); + + if (container (im)) + r_cols->traverse (im, utype (id), "value", "value"); + else + r_cols->traverse (im); + + for (object_columns_list::iterator b (l_cols->begin ()), i (b), + j (r_cols->begin ()); i != l_cols->end (); ++i, ++j) + { + l.clear (); + + if (i != b) + l += "AND "; + + l += lt; + l += '.'; + l += quote_id (i->name); + l += '='; + l += rt; + l += '.'; + l += quote_id (j->name); + + from_.push_back (strlit (l)); + } + + return rt; + + /* + // The alternative implementation: + // + location const& l1 (m.location ()); + location const& l2 (ptr_->location ()); + + string n1 (class_name (*object_pointer (utype (m)))); + string n2 (class_name (*object_pointer (utype (*ptr_)))); + + error (l1) << "object '" << n1 << "' pointed-to by the inverse " + << "data member in object '" << n2 << "' must be " + << "explicitly associated with the view" << endl; + + info (l2) << "view data member that loads '" << n2 << "' is " + << "defined here" << endl; + + throw operation_failed (); + */ + } + + virtual string + resolve_base (semantics::class_& b) const + { + view_object& vo (*ptr_->get ("view-object")); + + qname t (vo.alias.empty () + ? table_name (b) + : qname (vo.alias + "_" + table_name (b).uname ())); + + return quote_id (t); + } + + virtual void + traverse_pointer (semantics::data_member& m, semantics::class_& c) + { + type* poly_root (polymorphic (c)); + bool poly (poly_root != 0); + bool poly_derived (poly && poly_root != &c); + size_t poly_depth (poly_derived ? polymorphic_depth (c) : 1); + + view_object& vo (*m.get ("view-object")); + string const& a (vo.alias); + + qname t; + if (a.empty ()) + t = table_name (c); + else + { + if (poly) + t = qname (a + "_" + table_name (c).uname ()); + else + t = qname (a); + } + string qt (quote_id (t)); + + ptr_ = &m; + + statement_kind sk (statement_select); // Imperfect forwarding. + object_section* s (&main_section); // Imperfect forwarding. + instance oc (qt, sk, sc_, poly_depth, s, this); + oc->traverse (c); + } virtual void traverse_composite (semantics::data_member* pm, semantics::class_& c) @@ -492,8 +750,15 @@ namespace relational protected: statement_columns& sc_; + strings& from_; + const view_relationship_map& rel_map_; + bool in_composite_; qname table_prefix_; // Table corresponding to column_prefix_; + + // Set to the current pointer data member that we are traversing. + // + semantics::data_member* ptr_; }; struct polymorphic_object_joins: object_columns_base, virtual context @@ -909,6 +1174,148 @@ namespace relational instance id_cols_; }; + // Check that eager object pointers in the objects that a view loads + // can be loaded from the cache (i.e., they have session support + // enabled). + // + struct view_object_check: object_members_base + { + typedef view_object_check base; + + view_object_check (view_object& vo, view_relationship_map& rm) + : object_members_base (false, &main_section), + session_ (false), vo_ (vo), rel_map_ (rm) {} + + virtual bool + section_test (data_member_path const& mp) + { + // Include eager loaded members into the main section. + // + object_section& s (section (mp)); + return *section_ == s || !s.separate_load (); + } + + virtual void + traverse_pointer (semantics::data_member& m, semantics::class_& c) + { + // Ignore polymorphic id references. + // + if (m.count ("polymorphic-ref")) + return; + + check (m, inverse (m), utype (m), c); + } + + virtual void + traverse_container (semantics::data_member& m, semantics::type& t) + { + semantics::type& vt (container_vt (t)); + semantics::data_member* im (inverse (m, "value")); + + if (semantics::class_* cvt = composite_wrapper (vt)) + { + // Check this composite value for any pointers. + // + instance t (vo_, rel_map_); + t->traverse (*cvt); + + session_ = session_ || t->session_; + } + else if (semantics::class_* c = object_pointer (vt)) + check (m, im, vt, *c); + } + + void + check (semantics::data_member& m, + semantics::data_member* im, + semantics::type& pt, // Pointer type. + semantics::class_& c) + { + // We don't care about lazy pointers. + // + if (lazy_pointer (pt)) + return; + + // First check the pointed-to object recursively. + // + if (!c.count ("view-object-check-seen")) + { + c.set ("view-object-check-seen", true); + instance t (vo_, rel_map_); + t->traverse (c); + + // We may come again for another view. + // + c.remove ("view-object-check-seen"); + + session_ = session_ || t->session_; + } + + // See if the pointed-to object in this relationship is loaded + // by this view. + // + typedef view_relationship_map::const_iterator iterator; + + std::pair r ( + rel_map_.equal_range ( + im != 0 ? data_member_path (*im) : member_path_)); + + if (r.first == r.second) + return; // This relationship does not figure in the view. + + view_object& vo (*(im != 0 + ? r.first->second.first + : r.first->second.second)); + + assert (vo.obj == &c); // Got the above right? + + if (vo.ptr == 0) + return; // This object is not loaded by the view. + + // The pointed-to object in this relationship is loaded + // by the view. The hard question, of course, is whether + // it has anything to do with us. We assume it does. + // + if (!session (c)) + { + // Always direct data member. + // + semantics::class_& v ( + dynamic_cast (vo.ptr->scope ())); + + location const& l1 (c.location ()); + location const& l2 (m.location ()); + location const& l3 (vo_.ptr->location ()); + location const& l4 (vo.ptr->location ()); + + string on (class_name (c)); + string vn (class_name (v)); + + error (l1) << "object '" << on << "' has session support disabled " + << "but may be loaded by view '" << vn << "' via " + << "several data members" << endl; + + info (l2) << "indirectly via this data member..." << endl; + info (l3) << "...as a result of this object load" << endl; + info (l4) << "and directly as a result of this load" << endl; + info (l1) << "session support is required to only load one copy " + << "of the object" << endl; + info (l1) << "and don't forget to create a session instance when " + << "using this view" << endl; + + throw operation_failed (); + } + + session_ = true; + } + + bool session_; + + private: + view_object& vo_; + view_relationship_map& rel_map_; + }; + // // bind // @@ -1103,7 +1510,30 @@ namespace relational if (av != 0 || dv != 0) os << "}"; - if (comp != 0) + if (mi.ptr != 0 && view_member (mi.m)) + { + // See column_count_impl for details on what's going on here. + // + column_count_type cc; + if (semantics::class_* root = polymorphic (*mi.ptr)) + { + for (semantics::class_* b (mi.ptr);; b = &polymorphic_base (*b)) + { + column_count_type const& ccb (column_count (*b)); + + cc.total += ccb.total - (b != root ? ccb.id : 0); + cc.separate_load += ccb.separate_load; + + if (b == root) + break; + } + } + else + cc = column_count (*mi.ptr); + + os << "n += " << cc.total - cc.separate_load << "UL;"; + } + else if (comp != 0) { bool ro (readonly (*comp)); column_count_type const& cc (column_count (*comp)); @@ -1169,6 +1599,26 @@ namespace relational } virtual void + traverse_pointer (member_info& mi) + { + // Object pointers in views require special treatment. + // + if (view_member (mi.m)) + { + semantics::class_& c (*mi.ptr); + semantics::class_* poly_root (polymorphic (c)); + bool poly_derived (poly_root != 0 && poly_root != &c); + + os << "object_traits_impl< " << class_fq_name (c) << ", id_" << + db << " >::bind (" << endl + << "b + n, " << (poly_derived ? "0, 0, " : "") << arg << "." << + mi.var << "value, sk" << (versioned (c) ? ", svm" : "") << ");"; + } + else + member_base_impl::traverse_pointer (mi); + } + + virtual void traverse_composite (member_info& mi) { os << "composite_value_traits< " << mi.fq_type () << ", id_" << @@ -1262,25 +1712,214 @@ namespace relational { typedef grow_member base; - grow_member (size_t& index, - string const& var = string (), - user_section* section = 0) - : member_base (var, 0, string (), string (), section), - index_ (index) + grow_member (size_t& index, + string const& var = string (), + user_section* section = 0) + : member_base (var, 0, string (), string (), section), + index_ (index) + { + } + + grow_member (size_t& index, + string const& var, + semantics::type& t, + string const& fq_type, + string const& key_prefix) + : member_base (var, &t, fq_type, key_prefix), index_ (index) + { + } + + protected: + size_t& index_; + }; + + template + struct grow_member_impl: grow_member, virtual member_base_impl + { + typedef grow_member_impl base_impl; + + grow_member_impl (base const& x) + : member_base::base (x), // virtual base + base (x) {} + + typedef typename member_base_impl::member_info member_info; + + virtual bool + pre (member_info& mi) + { + if (container (mi)) + return false; + + // Ignore polymorphic id references; they are not returned by + // the select statement. + // + if (mi.ptr != 0 && mi.m.count ("polymorphic-ref")) + return false; + + std::ostringstream ostr; + ostr << "t[" << index_ << "UL]"; + e = ostr.str (); + + if (var_override_.empty ()) + { + os << "// " << mi.m.name () << endl + << "//" << endl; + + semantics::class_* comp (composite (mi.t)); + + // If the member is soft- added or deleted, check the version. + // + unsigned long long av (added (mi.m)); + unsigned long long dv (deleted (mi.m)); + + // If this is a composite member, see if it is summarily + // added/deleted. + // + if (comp != 0) + { + unsigned long long cav (added (*comp)); + unsigned long long cdv (deleted (*comp)); + + if (cav != 0 && (av == 0 || av < cav)) + av = cav; + + if (cdv != 0 && (dv == 0 || dv > cdv)) + dv = cdv; + } + + // If the addition/deletion version is the same as the section's, + // then we don't need the test. + // + if (user_section* s = dynamic_cast (section_)) + { + if (av == added (*s->member)) + av = 0; + + if (dv == deleted (*s->member)) + dv = 0; + } + + if (av != 0 || dv != 0) + { + os << "if ("; + + if (av != 0) + os << "svm >= schema_version_migration (" << av << "ULL, true)"; + + if (av != 0 && dv != 0) + os << " &&" << endl; + + if (dv != 0) + os << "svm <= schema_version_migration (" << dv << "ULL, true)"; + + os << ")" + << "{"; + } + } + + return true; + } + + virtual void + post (member_info& mi) + { + semantics::class_* comp (composite (mi.t)); + + if (var_override_.empty ()) + { + unsigned long long av (added (mi.m)); + unsigned long long dv (deleted (mi.m)); + + if (comp != 0) + { + unsigned long long cav (added (*comp)); + unsigned long long cdv (deleted (*comp)); + + if (cav != 0 && (av == 0 || av < cav)) + av = cav; + + if (cdv != 0 && (dv == 0 || dv > cdv)) + dv = cdv; + } + + if (user_section* s = dynamic_cast (section_)) + { + if (av == added (*s->member)) + av = 0; + + if (dv == deleted (*s->member)) + dv = 0; + } + + if (av != 0 || dv != 0) + os << "}"; + } + + if (mi.ptr != 0 && view_member (mi.m)) + { + // See column_count_impl for details on what's going on here. + // + column_count_type cc; + if (semantics::class_* root = polymorphic (*mi.ptr)) + { + for (semantics::class_* b (mi.ptr);; b = &polymorphic_base (*b)) + { + column_count_type const& ccb (column_count (*b)); + + cc.total += ccb.total - (b != root ? ccb.id : 0); + cc.separate_load += ccb.separate_load; + + if (b == root) + break; + } + } + else + cc = column_count (*mi.ptr); + + index_ += cc.total - cc.separate_load; + } + else if (comp != 0) + index_ += column_count (*comp).total; + else + index_++; + } + + virtual void + traverse_pointer (member_info& mi) { + // Object pointers in views require special treatment. They + // can only be immediate members of the view class. + // + if (view_member (mi.m)) + { + semantics::class_& c (*mi.ptr); + + os << "if (object_traits_impl< " << class_fq_name (c) << + ", id_" << db << " >::grow (" << endl + << "i." << mi.var << "value, t + " << index_ << "UL" << + (versioned (c) ? ", svm" : "") << "))" << endl + << "grew = true;" + << endl; + } + else + member_base_impl::traverse_pointer (mi); } - grow_member (size_t& index, - string const& var, - semantics::type& t, - string const& fq_type, - string const& key_prefix) - : member_base (var, &t, fq_type, key_prefix), index_ (index) + virtual void + traverse_composite (member_info& mi) { + semantics::class_& c (*composite (mi.t)); + + os << "if (composite_value_traits< " << mi.fq_type () << + ", id_" << db << " >::grow (" << endl + << "i." << mi.var << "value, t + " << index_ << "UL" << + (versioned (c) ? ", svm" : "") << "))" << endl + << "grew = true;" + << endl; } protected: - size_t& index_; + string e; }; struct grow_base: traversal::class_, virtual context @@ -1795,6 +2434,9 @@ namespace relational { } + virtual void + get_null (string const& /*var*/) const {}; + protected: string member_override_; bool ignore_implicit_discriminator_; @@ -1817,7 +2459,7 @@ namespace relational typedef typename member_base_impl::member_info member_info; virtual void - get_null (member_info&) = 0; + get_null (string const& var) const = 0; virtual void check_modifier (member_info&, member_access&) {} @@ -1909,6 +2551,9 @@ namespace relational os << "{"; + if (mi.ptr != 0 && view_member (mi.m)) + return true; // That's enough for the object pointer in view. + // Get the member using the accessor expression. // member_access& ma (mi.m.template get ("set")); @@ -2007,7 +2652,7 @@ namespace relational << "i." << mi.var << "value" << (versioned (*comp) ? ", svm" : "") << ")"; else - get_null (mi); + get_null (mi.var); os << ")" << endl; @@ -2046,6 +2691,13 @@ namespace relational { if (mi.ptr != 0) { + if (view_member (mi.m)) + { + // The object pointer in view doesn't need any of this. + os << "}"; + return; + } + // Restore the member variable name. // member = member_override_.empty () ? "v" : member_override_; @@ -2110,6 +2762,116 @@ namespace relational } virtual void + traverse_pointer (member_info& mi) + { + // Object pointers in views require special treatment. + // + if (view_member (mi.m)) + { + // This is the middle part. The pre and post parts are generated + // by init_view_pointer_member below. + // + using semantics::class_; + + class_& c (*mi.ptr); + class_* poly_root (polymorphic (c)); + bool poly (poly_root != 0); + bool poly_derived (poly && poly_root != &c); + + string o_tp (mi.var + "object_type"); + string o_tr (mi.var + "object_traits"); + string r_tr (poly_derived ? mi.var + "root_traits" : o_tr); + string i_tp (mi.var + "info_type"); + + string id (mi.var + "id"); + string o (mi.var + "o"); + string pi (mi.var + "pi"); // Polymorphic type info. + + // If load_() will be loading containers or the rest of the + // polymorphic object, then we need to perform several extra + // things. We need to initialize the id image in the object + // statements. We also have to lock the statements so that + // nobody messes up this id image. + // + bool init_id ( + poly || + has_a (c, test_container | include_eager_load, &main_section)); + + bool versioned (context::versioned (c)); + + os << "if (" << o << " != 0)" + << "{"; + + if (poly) + os << "callback_event ce (callback_event::pre_load);" + << pi << "->dispatch (" << i_tp << "::call_callback, " << + "*db, " << o << ", &ce);"; + else + os << o_tr << "::callback (*db, *" << o << + ", callback_event::pre_load);"; + + os << o_tr << "::init (*" << o << ", i." << mi.var << "value, db" << + (versioned ? ", svm" : "") << ");"; + + // Call load_() to load the rest of the object (containers, etc). + // + if (id_member (poly ? *poly_root : c) != 0) + { + const char* s (poly_derived ? "osts" : "sts"); + + os << o_tr << "::statements_type& " << s << " (" << endl + << "conn.statement_cache ().find_object<" << o_tp << "> ());"; + + if (poly_derived) + os << r_tr << "::statements_type& sts (osts.root_statements ());"; + + if (init_id) + { + // This can only be top-level call so lock must succeed. + // + os << r_tr << "::statements_type::auto_lock l (sts);" + << endl + << r_tr << "::id_image_type& i (sts.id_image ());" + << r_tr << "::init (i, " << id << ");" + << db << "::binding& idb (sts.id_image_binding ());" + << "if (i.version != sts.id_image_version () || " << + "idb.version == 0)" + << "{" + << r_tr << "::bind (idb.bind, i);" + << "sts.id_image_version (i.version);" + << "idb.version++;"; + if (optimistic (poly ? *poly_root : c) != 0) + os << "sts.optimistic_id_image_binding ().version++;"; + os << "}"; + } + + os << o_tr << "::load_ (" << s << ", *" << o << ", false" << + (versioned ? ", svm" : "") << ");"; + + // Load the dynamic part of the object unless static and dynamic + // types are the same. + // + if (poly) + os << endl + << "if (" << pi << " != &" << o_tr << "::info)" + << "{" + << "std::size_t d (" << o_tr << "::depth);" + << pi << "->dispatch (" << i_tp << "::call_load, *db, " << + o << ", &d);" + << "}"; + + if (init_id) + os << "sts.load_delayed (" << (versioned ? "&svm" : "0") << ");" + << "l.unlock ();"; + } + + os << "}"; + } + else + member_base_impl::traverse_pointer (mi); + } + + virtual void traverse_composite (member_info& mi) { os << traits << "::init (" << endl @@ -2134,6 +2896,377 @@ namespace relational instance member_database_type_id_; }; + // This class generates the pre and post parts. The middle part is + // generated by init_value_member above. + // + struct init_view_pointer_member: virtual member_base, + member_base_impl // Dummy SQL type. + { + typedef init_view_pointer_member base; + + init_view_pointer_member (bool pre, init_value_member const& ivm) + : member_base ("", 0, "", "", 0), + pre_ (pre), init_value_member_ (ivm) {} + + virtual bool + pre (member_info& mi) + { + // Only interested in object pointers inside views. + // + return mi.ptr != 0 && view_member (mi.m); + } + + virtual void + traverse_pointer (member_info& mi) + { + using semantics::class_; + + class_& c (*mi.ptr); + bool abst (abstract (c)); + class_* poly_root (polymorphic (c)); + bool poly (poly_root != 0); + bool poly_derived (poly && poly_root != &c); + size_t poly_depth (poly_derived ? polymorphic_depth (c) : 1); + + semantics::data_member* idm (id_member (poly ? *poly_root : c)); + + os << "// " << mi.m.name () << (pre_ ? " pre" : " post") << endl + << "//" << endl; + + string o_tp (mi.var + "object_type"); + string o_tr (mi.var + "object_traits"); + string r_tr (poly_derived ? mi.var + "root_traits" : o_tr); + string i_tp (mi.var + "info_type"); + string p_tp (mi.var + "pointer_type"); + string p_tr (mi.var + "pointer_traits"); + string c_tr (mi.var + "cache_traits"); + + string id (mi.var + "id"); // Object id. + string p (mi.var + "p"); // Object pointer. + string pg (mi.var + "pg"); // Pointer guard. + string ig (mi.var + "ig"); // Cache insert guard. + string o (mi.var + "o"); // Object. + string pi (mi.var + "pi"); // Polymorphic type info. + + bool op_raw (c.get ("object-pointer-raw")); + bool mp_raw (utype (mi.m).is_a ()); + + // Output aliases and variables before any schema version if- + // blocks since we need to be able to access them across all + // three parts. + // + if (pre_) + { + os << "typedef " << class_fq_name (c) << " " << o_tp << ";" + << "typedef object_traits_impl<" << o_tp << ", id_" << db << + "> " << o_tr << ";"; + + if (poly_derived) + os << "typedef " << o_tr << "::root_traits " << r_tr << ";"; + + if (poly) + os << "typedef " << r_tr << "::info_type " << i_tp << ";"; + + os << "typedef " << r_tr << "::pointer_type " << p_tp << ";" + << "typedef " << r_tr << "::pointer_traits " << p_tr << ";"; + if (idm != 0) + os << "typedef " << r_tr << "::pointer_cache_traits " << + c_tr << ";"; + os << endl; + + if (idm != 0) + os << r_tr << "::id_type " << id << ";"; + os << p_tp << " " << p << (op_raw ? " = 0" : "") << ";" // VC++ + << p_tr << "::guard " << pg << ";"; + if (idm != 0) + os << c_tr << "::insert_guard " << ig << ";"; + os << o_tp << "* " << o << " (0);"; + + if (poly) + os << "const " << i_tp << "* " << pi << " = 0;"; // VC++ + + os << endl; + } + + // If the member is soft- added or deleted, check the version. + // + unsigned long long av (added (mi.m)); + unsigned long long dv (deleted (mi.m)); + + if (av != 0 || dv != 0) + { + os << "if ("; + + if (av != 0) + os << "svm >= schema_version_migration (" << av << "ULL, true)"; + + if (av != 0 && dv != 0) + os << " &&" << endl; + + if (dv != 0) + os << "svm <= schema_version_migration (" << dv << "ULL, true)"; + + os << ")"; + } + + os << "{"; + + if (pre_) + { + string id_im; + if (idm != 0) + { + // Check for NULL. + // + string id_var; + { + id_im = mi.var + "value"; + + // In a polymorphic class, the id is in the root image. + // + for (size_t i (0); i < poly_depth - 1; ++i) + id_im += (i == 0 ? ".base" : "->base"); + + string const& n (idm->name ()); + id_var = id_im + (poly_derived ? "->" : ".") + n + + (n[n.size () - 1] == '_' ? "" : "_"); + + id_im = (poly_derived ? "*i." : "i.") + id_im; + } + + os << "if ("; + + if (semantics::class_* comp = composite (mi.t)) + os << "!composite_value_traits< " << o_tr << "::id_type, id_" << + db << " >::get_null (" << endl + << "i." << id_var << "value" << + (versioned (*comp) ? ", svm" : "") << ")"; + else + { + os << "!("; + init_value_member_.get_null (id_var); + os << ")"; + } + + os << ")" + << "{"; + + // Check cache. + // + os << id << " = " << r_tr << "::id (" << id_im << ");" + << p << " = " << c_tr << "::find (*db, " << id << ");" + << endl; + + os << "if (" << p_tr << "::null_ptr (" << p << "))" + << "{"; + } + + // To support by-value object loading, we are going to load + // into an existing instance if the pointer is already not + // NULL. To limit the potential misuse (especially when it + // comes to sessions), we are going to limit this support + // only to raw pointers. Furthermore, we will only insert + // such an object into the cache if its object pointer is + // also raw. + // + if (mp_raw && !poly) + { + // Get the member using the accessor expression. + // + member_access& ma (mi.m.get ("get")); + + // If this is not a synthesized expression, then output + // its location for easier error tracking. + // + if (!ma.synthesized) + os << "// From " << location_string (ma.loc, true) << endl; + + // Use the original type to form the const reference. VC++ + // cannot grok the constructor syntax. + // + os << member_ref_type (mi.m, true, "m") << " =" << endl + << " " << ma.translate ("o") << ";" + << endl; + + os << "if (m != 0)" + << "{"; + + if (op_raw) + os << ig << ".reset (" << c_tr << "::insert (*db, " << id << + ", m));"; + + os << o << " = m;" + << "}" + << "else" + << "{"; + } + + if (poly) + { + os << r_tr << "::discriminator_type d (" << endl + << r_tr << "::discriminator (" << id_im << "));"; + + if (abst) + os << pi << " = &" << r_tr << "::map->find (d);"; + else + os << pi << " = (d == " << o_tr << "::info.discriminator" << endl + << "? &" << o_tr << "::info" << endl + << ": &" << r_tr << "::map->find (d));"; + + os << p << " = " << pi << "->create ();"; + } + else + os << p << " = object_factory<" << o_tp << ", " << p_tp << + ">::create ();"; + + os << pg << ".reset (" << p << ");"; + if (idm != 0) + os << ig << ".reset (" << c_tr << "::insert (*db, " << id << + ", " << p << "));"; + + if (poly_derived) + os << o << " = static_cast<" << o_tp << "*> (" << p_tr << + "::get_ptr (" << p << "));"; + else + os << o << " = " << p_tr << "::get_ptr (" << p << ");"; + + if (mp_raw && !poly) + os << "}"; + + if (idm != 0) + os << "}" // Cache. + << "}"; // NULL. + } + else + { + os << "if (" << o << " != 0)" + << "{"; + + if (poly) + os << "callback_event ce (callback_event::post_load);" + << pi << "->dispatch (" << i_tp << "::call_callback, " << + "*db, " << o << ", &ce);"; + else + os << o_tr << "::callback (*db, *" << o << + ", callback_event::post_load);"; + + if (idm != 0) + { + if (mp_raw && !op_raw && !poly) + os << "if (!" << p_tr << "::null_ptr (" << p << "))" + << "{"; + + os << c_tr << "::load (" << ig << ".position ());" + << ig << ".release ();"; + + if (mp_raw && !op_raw && !poly) + os << "}"; + } + + os << pg << ".release ();"; + + os << "}"; + + // If member pointer is not raw, then result is in p. + // If both member and object are raw, then result is in o. + // If member is raw but object is not, then result is in + // p if it is not NULL, and in o (either NULL or the same + // as the member value) otherwise. + // + member_access& ma (mi.m.get ("set")); + + if (ma.empty () && !poly) + { + // It is ok to have empty modifier expression as long as + // the member pointer is raw. This is the by-value load + // and the user is not interested in learning whether the + // object is NULL. + // + if (!mp_raw) + { + error (ma.loc) << "non-empty modifier expression required " << + "for loading an object via a smart pointer" << endl; + throw operation_failed (); + } + + os << "// Empty modifier expression was specified for this\n" + << "// object so make sure we have actually loaded the\n" + << "// data into the existing instance rather than, say,\n" + << "// finding the object in the cache or creating a new one.\n" + << "//\n" + << "assert (" << p_tr << "::null_ptr (" << p << "));"; + } + else + { + if (!(mp_raw && op_raw) || poly) + { + string r (options.std () >= cxx_version::cxx11 + ? "std::move (" + p + ")" + : p); + + if (poly_derived) + // This pointer could have from from cache, so use dynamic + // cast. + // + r = p_tr + "::dynamic_pointer_cast<" + o_tp + "> (\n" + + r + ")"; + + // Unless the pointer is raw, explicitly construct the + // smart pointer from the object pointer so that we get + // the behavior similar to calling database::load() (in + // both cases we are the "ownership end-points"; unless + // the object was already in the session before loading + // this view (in which case using raw pointers as object + // pointers is a really stupid idea), this logic will do + // the right thing and what the user most likely expects. + // + if (!mp_raw) + r = member_val_type (mi.m, false) + " (\n" + r + ")"; + + if (mp_raw && !op_raw) + os << "if (!" << p_tr << "::null_ptr (" << p << "))" << endl; + + os << "// If a compiler error points to the line below, then\n" + << "// it most likely means that a pointer used in view\n" + << "// member cannot be initialized from an object pointer.\n" + << "//" << endl; + + if (!ma.synthesized) + os << "// From " << location_string (ma.loc, true) << endl; + + if (ma.placeholder ()) + os << ma.translate ("o", r) << ";"; + else + os << ma.translate ("o") << " = " << r << ";"; + } + + if (mp_raw && !poly) + { + if (!op_raw) + os << "else" << endl; // NULL p + + if (!ma.synthesized) + os << "// From " << location_string (ma.loc, true) << endl; + + if (ma.placeholder ()) + os << ma.translate ("o", o) << ";"; + else + os << ma.translate ("o") << " = " << o << ";"; + } + } + } + + os << "}"; + } + + virtual bool const& + member_sql_type (semantics::data_member&) {return pre_;}; + + protected: + bool pre_; + init_value_member const& init_value_member_; + }; + struct init_value_base: traversal::class_, virtual context { typedef init_value_base base; @@ -5318,6 +6451,8 @@ namespace relational bind_discriminator_member_ ("discriminator_"), init_id_image_member_ ("id_", "id"), init_version_image_member_ ("version_", "(*v)"), + init_view_pointer_member_pre_ (true, *init_value_member_), + init_view_pointer_member_post_ (false, *init_value_member_), init_id_value_member_ ("id"), init_id_value_member_id_image_ ("id", "id_"), init_version_value_member_ ("v"), @@ -5344,6 +6479,8 @@ namespace relational bind_discriminator_member_ ("discriminator_"), init_id_image_member_ ("id_", "id"), init_version_image_member_ ("version_", "(*v)"), + init_view_pointer_member_pre_ (true, *init_value_member_), + init_view_pointer_member_post_ (false, *init_value_member_), init_id_value_member_ ("id"), init_id_value_member_id_image_ ("id", "id_"), init_version_value_member_ ("v"), @@ -5375,6 +6512,9 @@ namespace relational init_value_base_inherits_ >> init_value_base_; init_value_member_names_ >> init_value_member_; + + init_view_pointer_member_pre_names_ >> init_view_pointer_member_pre_; + init_view_pointer_member_post_names_ >> init_view_pointer_member_post_; } virtual void @@ -5760,6 +6900,11 @@ namespace relational instance init_value_member_; traversal::names init_value_member_names_; + instance init_view_pointer_member_pre_; + instance init_view_pointer_member_post_; + traversal::names init_view_pointer_member_pre_names_; + traversal::names init_view_pointer_member_post_names_; + instance init_id_value_member_; instance init_id_value_member_id_image_; instance init_version_value_member_; diff --git a/odb/relational/sqlite/common.cxx b/odb/relational/sqlite/common.cxx index 2a932a0..09c5a1f 100644 --- a/odb/relational/sqlite/common.cxx +++ b/odb/relational/sqlite/common.cxx @@ -60,6 +60,13 @@ namespace relational // member_image_type:: + member_image_type (base const& x) + : member_base::base (x), // virtual base + base (x) + { + } + + member_image_type:: member_image_type (semantics::type* type, string const& fq_type, string const& key_prefix) @@ -100,6 +107,8 @@ namespace relational type_ = "details::buffer"; } + entry member_image_type_; + // // member_database_type // diff --git a/odb/relational/sqlite/common.hxx b/odb/relational/sqlite/common.hxx index c97a665..742284e 100644 --- a/odb/relational/sqlite/common.hxx +++ b/odb/relational/sqlite/common.hxx @@ -58,12 +58,14 @@ namespace relational } }; - struct member_image_type: member_base + struct member_image_type: relational::member_image_type, + member_base { + member_image_type (base const&); member_image_type (semantics::type* type = 0, string const& fq_type = string (), string const& key_prefix = string ()); - string + virtual string image_type (semantics::data_member&); virtual void @@ -86,7 +88,6 @@ namespace relational member_base { member_database_type_id (base const&); - member_database_type_id (semantics::type* type = 0, string const& fq_type = string (), string const& key_prefix = string ()); diff --git a/odb/relational/sqlite/header.cxx b/odb/relational/sqlite/header.cxx index 883bfbf..31073a7 100644 --- a/odb/relational/sqlite/header.cxx +++ b/odb/relational/sqlite/header.cxx @@ -15,39 +15,14 @@ namespace relational { namespace relational = relational::header; - struct image_member: relational::image_member, member_base + struct image_member: relational::image_member_impl, + member_base { image_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_image_type_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - image_type = member_image_type_.image_type (mi.m); - - if (var_override_.empty ()) - os << "// " << mi.m.name () << endl - << "//" << endl; - - return true; - } - - virtual void - traverse_composite (member_info& mi) - { - os << image_type << " " << mi.var << "value;" - << endl; - } + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) {} virtual void traverse_integer (member_info& mi) @@ -73,10 +48,6 @@ namespace relational << "bool " << mi.var << "null;" << endl; } - - private: - string image_type; - member_image_type member_image_type_; }; entry image_member_; } diff --git a/odb/relational/sqlite/source.cxx b/odb/relational/sqlite/source.cxx index 78bee1d..3dfaae2 100644 --- a/odb/relational/sqlite/source.cxx +++ b/odb/relational/sqlite/source.cxx @@ -78,142 +78,14 @@ namespace relational // grow // - struct grow_member: relational::grow_member, member_base + struct grow_member: relational::grow_member_impl, + member_base { grow_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - // Ignore polymorphic id references; they are not returned by - // the select statement. - // - if (mi.ptr != 0 && mi.m.count ("polymorphic-ref")) - return false; - - ostringstream ostr; - ostr << "t[" << index_ << "UL]"; - e = ostr.str (); - - if (var_override_.empty ()) - { - os << "// " << mi.m.name () << endl - << "//" << endl; - - semantics::class_* comp (composite (mi.t)); - - // If the member is soft- added or deleted, check the version. - // - unsigned long long av (added (mi.m)); - unsigned long long dv (deleted (mi.m)); - - // If this is a composite member, see if it is summarily - // added/deleted. - // - if (comp != 0) - { - unsigned long long cav (added (*comp)); - unsigned long long cdv (deleted (*comp)); - - if (cav != 0 && (av == 0 || av < cav)) - av = cav; - - if (cdv != 0 && (dv == 0 || dv > cdv)) - dv = cdv; - } - - // If the addition/deletion version is the same as the section's, - // then we don't need the test. - // - if (user_section* s = dynamic_cast (section_)) - { - if (av == added (*s->member)) - av = 0; - - if (dv == deleted (*s->member)) - dv = 0; - } - - if (av != 0 || dv != 0) - { - os << "if ("; - - if (av != 0) - os << "svm >= schema_version_migration (" << av << "ULL, true)"; - - if (av != 0 && dv != 0) - os << " &&" << endl; - - if (dv != 0) - os << "svm <= schema_version_migration (" << dv << "ULL, true)"; - - os << ")" - << "{"; - } - } - - return true; - } - - virtual void - post (member_info& mi) - { - semantics::class_* comp (composite (mi.t)); - - if (var_override_.empty ()) - { - unsigned long long av (added (mi.m)); - unsigned long long dv (deleted (mi.m)); - - if (comp != 0) - { - unsigned long long cav (added (*comp)); - unsigned long long cdv (deleted (*comp)); - - if (cav != 0 && (av == 0 || av < cav)) - av = cav; - - if (cdv != 0 && (dv == 0 || dv > cdv)) - dv = cdv; - } - - if (user_section* s = dynamic_cast (section_)) - { - if (av == added (*s->member)) - av = 0; - - if (dv == deleted (*s->member)) - dv = 0; - } - - if (av != 0 || dv != 0) - os << "}"; - } - - if (comp != 0) - index_ += column_count (*comp).total; - else - index_++; - } - - virtual void - traverse_composite (member_info& mi) - { - os << "if (composite_value_traits< " << mi.fq_type () << - ", id_sqlite >::grow (" << endl - << "i." << mi.var << "value, t + " << index_ << "UL" << - (versioned (*composite (mi.t)) ? ", svm" : "") << "))" << endl - << "grew = true;" - << endl; - } + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) {} virtual void traverse_integer (member_info&) @@ -238,9 +110,6 @@ namespace relational << "grew = true;" << "}"; } - - private: - string e; }; entry grow_member_; @@ -316,9 +185,9 @@ namespace relational } virtual void - get_null (member_info& mi) + get_null (string const& var) const { - os << "i." << mi.var << "null"; + os << "i." << var << "null"; } virtual void diff --git a/odb/relational/validator.cxx b/odb/relational/validator.cxx index 33d796d..8629588 100644 --- a/odb/relational/validator.cxx +++ b/odb/relational/validator.cxx @@ -221,18 +221,16 @@ namespace relational } virtual void - traverse_simple (semantics::data_member& m) + traverse_pointer (semantics::data_member& m, semantics::class_&) { - if (object_pointer (utype (m))) + if (dm_ != 0 && object_pointer (utype (m))) { - semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + location const& l (dm_->location ()); - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: view data member '" << member_prefix_ << m.name () - << "' is an object pointer" << endl; - - os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << ": info: views cannot contain object pointers" << endl; + error (l) << "nested view data member '" << member_prefix_ + << m.name () << "' is an object pointer" << endl; + info (l) << "views can only contain direct object pointer members" + << endl; valid_ = false; } -- cgit v1.1