From 3a1eed21d4d5d0e7f6a9f400420fdc28d7be9b61 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 17 Feb 2012 10:08:18 +0200 Subject: Add support for composite object ids New pragma id_type (member). New test: common/composite-id. The composite example has also been updated. --- NEWS | 7 + doc/manual.xhtml | 330 ++++++----- odb/common.cxx | 254 +++++---- odb/common.hxx | 176 +++++- odb/context.cxx | 70 ++- odb/context.hxx | 17 + odb/gcc.hxx | 1 + odb/odb.cxx | 20 + odb/pragma.cxx | 13 +- odb/relational/common.cxx | 259 ++++++--- odb/relational/common.hxx | 189 ++++++- odb/relational/common.txx | 112 ++++ odb/relational/header.hxx | 41 +- odb/relational/model.hxx | 328 ++++++----- odb/relational/mssql/common.cxx | 103 +--- odb/relational/mssql/common.hxx | 132 +---- odb/relational/mssql/context.cxx | 17 +- odb/relational/mssql/context.hxx | 8 +- odb/relational/mssql/model.cxx | 2 +- odb/relational/mssql/source.cxx | 343 +----------- odb/relational/mysql/common.cxx | 103 +--- odb/relational/mysql/common.hxx | 118 +--- odb/relational/mysql/context.cxx | 17 +- odb/relational/mysql/context.hxx | 7 +- odb/relational/mysql/model.cxx | 2 +- odb/relational/mysql/source.cxx | 361 ++---------- odb/relational/oracle/common.cxx | 101 +--- odb/relational/oracle/common.hxx | 118 +--- odb/relational/oracle/context.cxx | 17 +- odb/relational/oracle/context.hxx | 8 +- odb/relational/oracle/model.cxx | 4 +- odb/relational/oracle/source.cxx | 356 ++---------- odb/relational/pgsql/common.cxx | 101 +--- odb/relational/pgsql/common.hxx | 118 +--- odb/relational/pgsql/context.cxx | 17 +- odb/relational/pgsql/context.hxx | 8 +- odb/relational/pgsql/model.cxx | 2 +- odb/relational/pgsql/source.cxx | 436 +++------------ odb/relational/processor.cxx | 147 +++-- odb/relational/source.hxx | 1096 ++++++++++++++++++++++++++++--------- odb/relational/sqlite/common.cxx | 103 +--- odb/relational/sqlite/common.hxx | 118 +--- odb/relational/sqlite/context.cxx | 17 +- odb/relational/sqlite/context.hxx | 8 +- odb/relational/sqlite/model.cxx | 2 +- odb/relational/sqlite/source.cxx | 340 +----------- odb/validator.cxx | 583 +++++++++++++------- 47 files changed, 3076 insertions(+), 3654 deletions(-) create mode 100644 odb/relational/common.txx diff --git a/NEWS b/NEWS index f36d7b7..b0453dd 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,10 @@ +Version 1.9.0 + + * Support for composite object ids. Now a composite value type can be used + to declare an object id member. For more information, refer to Section + 7.2.1, "Composite Object Ids" in the ODB manual as well as the 'composite' + example in the odb-examples package. + Version 1.8.0 * Support for the Microsoft SQL Server database. The provided connection diff --git a/doc/manual.xhtml b/doc/manual.xhtml index 64d96f8..30f4901 100644 --- a/doc/manual.xhtml +++ b/doc/manual.xhtml @@ -390,7 +390,8 @@ for consistency. 7.2Composite Value Types - + +
7.2.1Composite Value Column and Table Names
7.2.1Composite Object Ids
7.2.2Composite Value Column and Table Names
@@ -492,29 +493,30 @@ for consistency. 12.4.1id 12.4.2auto 12.4.3type - 12.4.4null/not_null - 12.4.5default - 12.4.6options - 12.4.7column (object, composite value) - 12.4.8column (view) - 12.4.9transient - 12.4.10readonly - 12.4.11inverse - 12.4.12version - 12.4.13unordered - 12.4.14table - 12.4.15index_type - 12.4.16key_type - 12.4.17value_type - 12.4.18value_null/value_not_null - 12.4.19id_options - 12.4.20index_options - 12.4.21key_options - 12.4.22value_options - 12.4.23id_column - 12.4.24index_column - 12.4.25key_column - 12.4.26value_column + 12.4.4id_type + 12.4.5null/not_null + 12.4.6default + 12.4.7options + 12.4.8column (object, composite value) + 12.4.9column (view) + 12.4.10transient + 12.4.11readonly + 12.4.12inverse + 12.4.13version + 12.4.14unordered + 12.4.15table + 12.4.16index_type + 12.4.17key_type + 12.4.18value_type + 12.4.19value_null/value_not_null + 12.4.20id_options + 12.4.21index_options + 12.4.22key_options + 12.4.23value_options + 12.4.24id_column + 12.4.25index_column + 12.4.26key_column + 12.4.27value_column @@ -870,7 +872,7 @@ for consistency. object state in binary format instead of text which reduces the load on the application and the database server. Extensive caching of connections, prepared statements, and buffers saves - time and resources on connection establishment, statement parsing + time and resources on connection establishment, statement parsing, and memory allocations. For each supported database system the native C API is used instead of ODBC or higher-level wrapper APIs to reduce overhead and provide the most efficient implementation @@ -2157,8 +2159,11 @@ class person without the default constructor. However, in this case, the database operations can only load the persistent state into an existing instance (Section 3.8, "Loading Persistent Objects", - Section 4.4, "Query Result").

The object id type - should be default-constructible.

+ Section 4.4, "Query Result").

+ +

The object id can be of a simple or composite (Section + 7.2.1, "Composite Object Ids") value type which should be + default-constructible.

If an object class has private or protected non-transient data members or if its default constructor is not public, then the @@ -3140,7 +3145,7 @@ t.commit (); data members can be declared read-only (see Section 12.1.4, "readonly (object)", Section 12.3.6, "readonly (composite value)", and - Section 12.4.10, "readonly + Section 12.4.11, "readonly (data member)").

If an individual data member is declared read-only, then @@ -4446,7 +4451,7 @@ private: of person's nicknames is probably not important. To instruct the ODB compiler to ignore the order in ordered containers we can use the db unordered pragma (Section 12.3.7, - "unordered", Section 12.4.13, + "unordered", Section 12.4.14, "unordered"). For example:

@@ -4696,11 +4701,11 @@ class employee
 
   

By default, an object pointer can be NULL. To specify that a pointer always points to a valid object we can - use the not_null pragma (Section - 12.4.4, "null/not_null") for + use the not_null pragma (Section + 12.4.5, "null/not_null") for single object pointers and the value_not_null pragma - (Section - 12.4.18, "value_null/value_not_null") + (Section + 12.4.19, "value_null/value_not_null") for containers of object pointers. For example:

@@ -5069,7 +5074,7 @@ CREATE TABLE employee (
      of these references.

To eliminate redundant database schema references we can use the - inverse pragma (Section 12.4.11, + inverse pragma (Section 12.4.12, "inverse") which tells the ODB compiler that a pointer is the inverse side of a bidirectional relationship. Either side of a relationship can be made inverse. For example:

@@ -5113,7 +5118,7 @@ CREATE TABLE employee ( pointer. Also note that an ordered container (Section 5.1, "Ordered Containers") of pointers that is an inverse side of a bidirectional relationship is always treated as unordered - (Section 12.4.13, "unordered") + (Section 12.4.14, "unordered") because the contents of such a container are implicitly built from the direct side of the relationship which does not contain the element order (index).

@@ -5859,12 +5864,42 @@ result r (db.query<person> ( t.commit ();
-

7.2.1 Composite Value Column and Table Names

+

7.2.1 Composite Object Ids

+ +

An object id can be of a composite value type, for example:

+ +
+#pragma db value
+class name
+{
+  ...
+
+  std::string first_;
+  std::string last_;
+};
+
+#pragma db object
+class person
+{
+  ...
+
+  #pragma db id
+  name name_;
+};
+  
+ +

However, a value type that can be used as an object id has a number + of restrictions. Such a value type cannot have container, object + pointer, or read-only data members. It also must be + default-constructible and implement the less-than comparison operator + (operator<).

+ +

7.2.2 Composite Value Column and Table Names

Customizing a column name for a data member of a simple value type is straightforward: we simply specify the desired name with - the db column pragma (Section - 12.4.7, "column"). For composite value + the db column pragma (Section + 12.4.8, "column"). For composite value types things are slightly more complex since they are mapped to multiple columns. Consider the following example:

@@ -5965,9 +6000,9 @@ CREATE TABLE person (

The same principle applies when a composite value type is used as an element of a container, except that instead of db column, either the db value_column - (Section 12.4.26, "value_column") or + (Section 12.4.27, "value_column") or db key_column - (Section 12.4.25, "key_column") + (Section 12.4.26, "key_column") pragmas are used to specify the column prefix.

When a composite value type contains a container, an extra table @@ -6011,8 +6046,8 @@ CREATE TABLE person (

To customize the container table name we can use the - db table pragma (Section - 12.4.14, "table"), for example:

+ db table pragma (Section + 12.4.15, "table"), for example:

 #pragma db value
@@ -6053,7 +6088,7 @@ CREATE TABLE person_nickname (
      of a valid value in a column. While by default ODB maps
      values to columns that do not allow NULL values,
      it is possible to change that with the db null
-     pragma (Section 12.4.4,
+     pragma (Section 12.4.5,
      "null/not_null").

To properly support the NULL semantics, the @@ -6804,7 +6839,7 @@ struct employee_birth_code or the match is ambiguous, the ODB compiler will issue an error. To associate two differently-named members or to resolve an ambiguity, we can explicitly specify the member association using the - db column pragma (Section 12.4.7, + db column pragma (Section 12.4.8, "column"). For example:

@@ -7822,7 +7857,7 @@ p.age (age);
   

To declare a persistent class with the optimistic concurrency model we use the optimistic pragma (Section 12.1.5, "optimistic"). We also use the version - pragma (Section 12.4.12, "version") + pragma (Section 12.4.13, "version") to specify which data member will store the object version. For example:

@@ -8402,7 +8437,7 @@ class person read-only while the rest is treated as read-write.

Note that it is also possible to declare individual data members - (Section 12.4.10, "readonly") + (Section 12.4.11, "readonly") as well as composite value types (Section 12.3.6, "readonly") as read-only.

@@ -8412,7 +8447,7 @@ class person has the optimistic concurrency model. A class with the optimistic concurrency model must also specify the data member that is used to store the object version using the version pragma - (Section 12.4.12, "version"). + (Section 12.4.13, "version"). For example:

@@ -8794,8 +8829,8 @@ class employee
 
   

The standard syntax for qualified names used in the schema and table specifiers as well - as the view column specifier (Section - 12.4.8, "column (view)") has the + as the view column specifier (Section + 12.4.9, "column (view)") has the "name.name..." form where, as discussed above, the leading name component can be empty to denote a fully qualified name. This form, however, @@ -9181,7 +9216,7 @@ typedef shared_ptr<person> person_ptr;

The NULL semantics can also be specified on the - per-member basis (Section 12.4.4, + per-member basis (Section 12.4.5, "null/not_null"). If both a type and a member have null/not_null specifiers, then the member specifier takes precedence. If a member specifier @@ -9234,7 +9269,7 @@ class person

The semantics of the default specifier for a value type are similar to those of the default specifier for a - data member (Section 12.4.5, + data member (Section 12.4.6, "default").

12.3.5 options

@@ -9257,7 +9292,7 @@ class person

The semantics of the options specifier for a value type are similar to those of the options specifier for a - data member (Section 12.4.6, + data member (Section 12.4.7, "options").

12.3.6 readonly

@@ -9286,7 +9321,7 @@ class person_name read-only while the rest is treated as read-write.

Note that it is also possible to declare individual data members - (Section 12.4.10, "readonly") + (Section 12.4.11, "readonly") as well as whole objects (Section 12.1.4, "readonly") as read-only.

@@ -9395,7 +9430,7 @@ typedef std::vector<std::string> nicknames;

The semantics of the id_options specifier for a container type are similar to those of the id_options specifier for - a container data member (Section 12.4.19, + a container data member (Section 12.4.20, "id_options").

@@ -9412,7 +9447,7 @@ typedef std::vector<std::string> nicknames;

The semantics of the index_options specifier for a container type are similar to those of the index_options specifier for - a container data member (Section 12.4.20, + a container data member (Section 12.4.21, "index_options").

@@ -9429,7 +9464,7 @@ typedef std::map<std::string, std::string> properties;

The semantics of the key_options specifier for a container type are similar to those of the key_options specifier for - a container data member (Section 12.4.21, + a container data member (Section 12.4.22, "key_options").

@@ -9446,7 +9481,7 @@ typedef std::set<std::string> nicknames;

The semantics of the value_options specifier for a container type are similar to those of the value_options specifier for - a container data member (Section 12.4.22, + a container data member (Section 12.4.23, "value_options").

@@ -9543,141 +9578,147 @@ typedef std::map<unsigned short, float> age_weight_map; + id_type + database type for a member when used as an object id + 12.4.4 + + + null/not_null member can/cannot be NULL - 12.4.4 + 12.4.5 default default value for a member - 12.4.5 + 12.4.6 options database options for a member - 12.4.6 + 12.4.7 column column name for a member of an object or composite value - 12.4.7 + 12.4.8 column column name for a member of a view - 12.4.8 + 12.4.9 transient member is not stored in the database - 12.4.9 + 12.4.10 readonly member is read-only - 12.4.10 + 12.4.11 inverse member is an inverse side of a bidirectional relationship - 12.4.11 + 12.4.12 version member stores object version - 12.4.12 + 12.4.13 unordered ordered container should be stored unordered - 12.4.13 + 12.4.14 table table name for a container - 12.4.14 + 12.4.15 index_type database type for a container's index type - 12.4.15 + 12.4.16 key_type database type for a container's key type - 12.4.16 + 12.4.17 value_type database type for a container's value type - 12.4.17 + 12.4.18 value_null/value_not_null container's value can/cannot be NULL - 12.4.18 + 12.4.19 id_options database options for a container's id column - 12.4.19 + 12.4.20 index_options database options for a container's index column - 12.4.20 + 12.4.21 key_options database options for a container's key column - 12.4.21 + 12.4.22 value_options database options for a container's value column - 12.4.22 + 12.4.23 id_column column name for a container's object id - 12.4.23 + 12.4.24 index_column column name for a container's index - 12.4.24 + 12.4.25 key_column column name for a container's key - 12.4.25 + 12.4.26 value_column column name for a container's value - 12.4.26 + 12.4.27 @@ -9764,11 +9805,44 @@ class person };
-

The null and not_null (Section - 12.4.4, "null/not_null") specifiers +

The null and not_null (Section + 12.4.5, "null/not_null") specifiers can be used to control the NULL semantics of a data member.

-

12.4.4 null/not_null

+

12.4.4 id_type

+ +

The type specifier specifies the native database type + that should be used for a data member when it is part of an + object identifier. This specifier only makes sense when applied to + a member of a composite value type that is used for both id and + non-id members. For example:

+ +
+#pragma db value
+class name
+{
+  ...
+
+  #pragma db type("VARCHAR(256)") id_type("VARCHAR(64)")
+  std::string first_;
+
+  #pragma db type("VARCHAR(256)") id_type("VARCHAR(64)")
+  std::string last_;
+};
+
+#pragma db object
+class person
+{
+  ...
+
+  #pragma db id
+  name name_;  // name_.first_, name_.last_ mapped to VARCHAR(64)
+
+  name alias_; // alias_.first_, alias_.last_ mapped to VARCHAR(256)
+};
+  
+ +

12.4.5 null/not_null

The null and not_null specifiers specify that a data member can or cannot be NULL, respectively. @@ -9823,7 +9897,7 @@ class account discussion of the NULL semantics for object pointers, refer to Chapter 6, "Relationships".

-

12.4.5 default

+

12.4.6 default

The default specifier specifies the database default value that should be used for a data member. For example:

@@ -9844,8 +9918,8 @@ class person an integer literal, a floating point literal, a string literal, or an enumerator name. If you need to specify a default value that is an expression, for example an SQL function call, then you can use - the options specifier (Section - 12.4.6, "options") instead. For example:

+ the options specifier (Section + 12.4.7, "options") instead. For example:

 enum gender {male, female, undisclosed};
@@ -9931,7 +10005,7 @@ class person
   

Additionally, the default specifier cannot be specified for view data members.

-

12.4.6 options

+

12.4.7 options

The options specifier specifies additional column definition options that should be used for a data member. For @@ -9979,9 +10053,9 @@ class person

ODB provides dedicated specifiers for specifying column types (Section 12.4.3, "type"), - NULL constraints (Section 12.4.4, + NULL constraints (Section 12.4.5, "null/not_null"), and default - values (Section 12.4.5, "default"). + values (Section 12.4.6, "default"). For ODB to function correctly these specifiers should always be used instead of the opaque options specifier for these components of a column definition.

@@ -9989,7 +10063,7 @@ class person

Note also that the options specifier cannot be specified for view data members.

-

12.4.7 column (object, composite value)

+

12.4.8 column (object, composite value)

The column specifier specifies the column name that should be used to store a data member of a persistent class @@ -10007,7 +10081,7 @@ class person

For a member of a composite value type, the column specifier - specifies the column name prefix. Refer to Section 7.2.1, + specifies the column name prefix. Refer to Section 7.2.2, "Composite Value Column and Table Names" for details.

If the column name is not specified, it is derived from the member's @@ -10015,7 +10089,7 @@ class person the common data member name decorations, such as leading and trailing underscores, the m_ prefix, etc.

-

12.4.8 column (view)

+

12.4.9 column (view)

The column specifier can be used to specify the associated object data member, the potentially qualified column name, or the column @@ -10023,7 +10097,7 @@ class person refer to Section 9.1, "Object Views" and Section 9.2, "Table Views".

-

12.4.9 transient

+

12.4.10 transient

The transient specifier instructs the ODB compiler not to store a data member in the database. For example:

@@ -10045,7 +10119,7 @@ class person references that are only meaningful in the application's memory, as well as utility members such as mutexes, etc.

-

12.4.10 readonly

+

12.4.11 readonly

The readonly specifier specifies that a data member of an object or composite value type is read-only. Changes to a read-only @@ -10054,7 +10128,7 @@ class person containing such a member. Since views are read-only, it is not necessary to use this specifier for view data members. Object id (Section 12.4.1, "id") - and inverse (Section 12.4.11, + and inverse (Section 12.4.12, "inverse") data members are automatically treated as read-only and must not be explicitly declared as such. For example:

@@ -10138,7 +10212,7 @@ class person as well as whole objects (Section 12.1.4, "readonly") as read-only.

-

12.4.11 inverse

+

12.4.12 inverse

The inverse specifier specifies that a data member of an object pointer or a container of object pointers type is an @@ -10176,12 +10250,12 @@ class person relationship information. Only ordered and set containers can be used for inverse members. If an inverse member is of an ordered container type, it is automatically marked as unordered - (Section 12.4.13, "unordered").

+ (Section 12.4.14, "unordered").

For a more detailed discussion of inverse members, refer to Section 6.2, "Bidirectional Relationships".

-

12.4.12 version

+

12.4.13 version

The version specifier specifies that the data member stores the object version used to support optimistic concurrency. If a class @@ -10211,7 +10285,7 @@ class person

For a more detailed discussion of optimistic concurrency, refer to Chapter 11, "Optimistic Concurrency".

-

12.4.13 unordered

+

12.4.14 unordered

The unordered specifier specifies that a member of an ordered container type should be stored unordered in the database. @@ -10234,7 +10308,7 @@ class person storage in the database, refer to Section 5.1, "Ordered Containers".

-

12.4.14 table

+

12.4.15 table

The table specifier specifies the table name that should be used to store the contents of a container member. For example:

@@ -10261,7 +10335,7 @@ class person

The table specifier can also be used for members of composite value types. In this case it specifies the table name prefix for container members inside the composite value type. Refer - to Section 7.2.1, "Composite Value Column and Table + to Section 7.2.2, "Composite Value Column and Table Names" for details.

The container table name can be qualified with a database @@ -10282,7 +10356,7 @@ class person qualified names, refer to Section 12.1.8, "schema".

-

12.4.15 index_type

+

12.4.16 index_type

The index_type specifier specifies the native database type that should be used for an ordered container's @@ -10302,7 +10376,7 @@ class person };

-

12.4.16 key_type

+

12.4.17 key_type

The key_type specifier specifies the native database type that should be used for a map container's @@ -10322,7 +10396,7 @@ class person }; -

12.4.17 value_type

+

12.4.18 value_type

The value_type specifier specifies the native database type that should be used for a container's @@ -10343,18 +10417,18 @@ class person

The value_null and value_not_null - (Section 12.4.18, + (Section 12.4.19, "value_null/value_not_null") specifiers can be used to control the NULL semantics of a value column.

-

12.4.18 value_null/value_not_null

+

12.4.19 value_null/value_not_null

The value_null and value_not_null specifiers specify that a container's element value for a data member can or cannot be NULL, respectively. The semantics of value_null and value_not_null are similar to those of the null and not_null specifiers - (Section 12.4.4, "null/not_null"). + (Section 12.4.5, "null/not_null"). For example:

@@ -10380,7 +10454,7 @@ class account
      Multiset Containers") the element value is automatically treated
      as not allowing a NULL value.

-

12.4.19 id_options

+

12.4.20 id_options

The id_options specifier specifies additional column definition options that should be used for a container's @@ -10401,10 +10475,10 @@ class person

The semantics of id_options are similar to those - of the options specifier (Section - 12.4.6, "options").

+ of the options specifier (Section + 12.4.7, "options").

-

12.4.20 index_options

+

12.4.21 index_options

The index_options specifier specifies additional column definition options that should be used for a container's @@ -10422,10 +10496,10 @@ class person

The semantics of index_options are similar to those - of the options specifier (Section - 12.4.6, "options").

+ of the options specifier (Section + 12.4.7, "options").

-

12.4.21 key_options

+

12.4.22 key_options

The key_options specifier specifies additional column definition options that should be used for a container's @@ -10443,10 +10517,10 @@ class person

The semantics of key_options are similar to those - of the options specifier (Section - 12.4.6, "options").

+ of the options specifier (Section + 12.4.7, "options").

-

12.4.22 value_options

+

12.4.23 value_options

The value_options specifier specifies additional column definition options that should be used for a container's @@ -10464,17 +10538,17 @@ class person

The semantics of value_options are similar to those - of the options specifier (Section - 12.4.6, "options").

+ of the options specifier (Section + 12.4.7, "options").

-

12.4.23 id_column

+

12.4.24 id_column

The id_column specifier specifies the column name that should be used to store the object id in a container's table for a data member. The semantics of id_column are similar to those of the column specifier - (Section 12.4.7, "column"). + (Section 12.4.8, "column"). For example:

@@ -10491,14 +10565,14 @@ class person
   

If the column name is not specified, then object_id is used by default.

-

12.4.24 index_column

+

12.4.25 index_column

The index_column specifier specifies the column name that should be used to store the element index in an ordered container's table for a data member. The semantics of index_column are similar to those of the column specifier - (Section 12.4.7, "column"). + (Section 12.4.8, "column"). For example:

@@ -10515,14 +10589,14 @@ class person
   

If the column name is not specified, then index is used by default.

-

12.4.25 key_column

+

12.4.26 key_column

The key_column specifier specifies the column name that should be used to store the key in a map container's table for a data member. The semantics of key_column are similar to those of the column specifier - (Section 12.4.7, "column"). + (Section 12.4.8, "column"). For example:

@@ -10539,14 +10613,14 @@ class person
   

If the column name is not specified, then key is used by default.

-

12.4.26 value_column

+

12.4.27 value_column

The value_column specifier specifies the column name that should be used to store the element value in a container's table for a data member. The semantics of value_column are similar to those of the column specifier - (Section 12.4.7, "column"). + (Section 12.4.8, "column"). For example:

@@ -12885,7 +12959,7 @@ SHOW integer_datetimes
      in the generated schema, columns of these types are always
      declared as NULL, even if explicitly declared as
      NOT NULL with the db not_null pragma
-     (Section 12.4.4, "null/not_null").

+ (Section 12.4.5, "null/not_null").

The Oracle ODB runtime library also provides support for mapping the std::string type to the Oracle CHAR, diff --git a/odb/common.cxx b/odb/common.cxx index 230e674..6261b85 100644 --- a/odb/common.cxx +++ b/odb/common.cxx @@ -16,6 +16,12 @@ 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))); +} + +void object_members_base:: traverse_composite (semantics::data_member*, semantics::class_& c) { inherits (c); @@ -51,26 +57,6 @@ traverse_view (semantics::class_& c) } void object_members_base:: -traverse (semantics::data_member& m, semantics::class_& c) -{ - // We are starting from the member. Add an empty chain which - // corresponds to the scope that contains this member. - // - //member_scope_.push_back (class_inheritance_chain ()); - //member_path_.push_back (&m); - - member_scope_.push_back (class_inheritance_chain ()); - member_scope_.back ().push_back (&c); - - traverse_composite_wrapper (&m, c, 0); - - member_scope_.pop_back (); - - //member_path_.pop_back (); - //member_scope_.pop_back (); -} - -void object_members_base:: traverse (semantics::class_& c) { class_kind_type k (class_kind (c)); @@ -156,68 +142,73 @@ traverse (semantics::class_& c) context::cur_object = prev; } -void object_members_base::member:: -traverse (semantics::data_member& m) +void object_members_base:: +traverse_member (semantics::data_member& m, semantics::type& t) { - if (transient (m)) - return; - - om_.member_path_.push_back (&m); - - semantics::type& t (utype (m)); - if (semantics::class_* comp = context::composite_wrapper (t)) { - om_.member_scope_.push_back (class_inheritance_chain ()); - om_.member_scope_.back ().push_back (comp); + member_scope_.push_back (class_inheritance_chain ()); + member_scope_.back ().push_back (comp); qname old_table_prefix; string old_flat_prefix, old_member_prefix; - if (om_.build_flat_prefix_) + if (build_flat_prefix_) { - old_flat_prefix = om_.flat_prefix_; - om_.flat_prefix_ += om_.public_name (m); - om_.flat_prefix_ += '_'; + old_flat_prefix = flat_prefix_; + flat_prefix_ += public_name (m); + flat_prefix_ += '_'; } - if (om_.build_member_prefix_) + if (build_member_prefix_) { - old_member_prefix = om_.member_prefix_; - om_.member_prefix_ += m.name (); - om_.member_prefix_ += '.'; + old_member_prefix = member_prefix_; + member_prefix_ += m.name (); + member_prefix_ += '.'; } - if (om_.build_table_prefix_) + if (build_table_prefix_) { - old_table_prefix = om_.table_prefix_.prefix; - append (m, om_.table_prefix_); + old_table_prefix = table_prefix_.prefix; + append (m, table_prefix_); } - om_.traverse_composite_wrapper (&m, *comp, (wrapper (t) ? &t : 0)); + traverse_composite_wrapper (&m, *comp, (wrapper (t) ? &t : 0)); - if (om_.build_table_prefix_) + if (build_table_prefix_) { - om_.table_prefix_.level--; - om_.table_prefix_.prefix = old_table_prefix; + table_prefix_.level--; + table_prefix_.prefix = old_table_prefix; } - if (om_.build_flat_prefix_) - om_.flat_prefix_ = old_flat_prefix; + if (build_flat_prefix_) + flat_prefix_ = old_flat_prefix; - if (om_.build_member_prefix_) - om_.member_prefix_ = old_member_prefix; + if (build_member_prefix_) + member_prefix_ = old_member_prefix; - om_.member_scope_.pop_back (); + member_scope_.pop_back (); } - else if (semantics::type* c = context::container (m)) - { + else + traverse_simple (m); +} + +void object_members_base::member:: +traverse (semantics::data_member& m) +{ + if (transient (m)) + return; + + om_.member_path_.push_back (&m); + + semantics::type& t (utype (m)); + + if (semantics::type* c = context::container (m)) om_.traverse_container (m, *c); - } + else if (semantics::class_* c = object_pointer (t)) + om_.traverse_pointer (m, *c); else - { - om_.traverse_simple (m); - } + om_.traverse_member (m, t); om_.member_path_.pop_back (); } @@ -283,6 +274,18 @@ flush () { } +bool object_columns_base:: +traverse_column (semantics::data_member&, string const&, bool) +{ + return false; +} + +void object_columns_base:: +traverse_pointer (semantics::data_member& m, semantics::class_& c) +{ + traverse_member (m, utype (*id_member (c))); +} + void object_columns_base:: traverse_composite (semantics::data_member*, semantics::class_& c) { @@ -307,30 +310,39 @@ traverse_view (semantics::class_& c) void object_columns_base:: traverse (semantics::data_member& m, - semantics::class_& c, - string const& key_prefix, - string const& default_name) + semantics::type& t, + std::string const& kp, + std::string const& dn, + semantics::class_* to) { - // We are starting from the member. Add an empty chain which - // corresponds to the scope that contains this member. - // - //member_scope_.push_back (class_inheritance_chain ()); - //member_path_.push_back (&m); + semantics::class_* oto (context::top_object); - member_scope_.push_back (class_inheritance_chain ()); - member_scope_.back ().push_back (&c); + if (to != 0) + context::top_object = to; - column_prefix_ = column_prefix (m, key_prefix, default_name); + semantics::class_* c (object_pointer (t)); + semantics::type* rt (c == 0 ? &t : &utype (*id_member (*c))); - traverse_composite (&m, c); + root_ = &m; + root_id_ = (kp.empty () ? context::id (m) : kp == "id"); + root_op_ = (c != 0); - if (!member_.first_) - flush (); + key_prefix_ = kp; + default_name_ = dn; - member_scope_.pop_back (); + if (root_op_) + traverse_pointer (m, *c); + else + traverse_member (m, *rt); + + key_prefix_.clear (); + default_name_.clear (); + + if (!first_ && composite_wrapper (*rt)) + flush (); - //member_path_.pop_back (); - //member_scope_.pop_back (); + root_ = 0; + context::top_object = oto; } void object_columns_base:: @@ -386,7 +398,7 @@ traverse (semantics::class_& c) context::cur_object = prev; } - if (f && !member_.first_) + if (f && !first_) flush (); } @@ -421,48 +433,102 @@ column_prefix (semantics::data_member& m, string const& kp, string const& dn) return r; } -void object_columns_base::member:: -traverse (semantics::data_member& m) +string object_columns_base:: +column_prefix (data_member_path const& mp) { - if (transient (m)) - return; + if (mp.size () < 2) + return ""; - oc_.member_path_.push_back (&m); + string r; - semantics::type& t (utype (m)); + for (data_member_path::const_iterator i (mp.begin ()), e (mp.end () - 1); + i != e; ++i) + r += column_prefix (**i); + + return r; +} +void object_columns_base:: +traverse_member (semantics::data_member& m, semantics::type& t) +{ if (semantics::class_* comp = composite_wrapper (t)) { - oc_.member_scope_.push_back (class_inheritance_chain ()); - oc_.member_scope_.back ().push_back (comp); + member_scope_.push_back (class_inheritance_chain ()); + member_scope_.back ().push_back (comp); - string old_prefix (oc_.column_prefix_); - oc_.column_prefix_ += column_prefix (m); + string old_prefix (column_prefix_); + column_prefix_ += column_prefix (m, key_prefix_, default_name_); - oc_.traverse_composite (&m, *comp); + // Save and clear the key prefix and default name. + // + string old_kp, old_dn; + old_kp.swap (key_prefix_); + old_dn.swap (default_name_); - oc_.column_prefix_ = old_prefix; + traverse_composite (&m, *comp); - oc_.member_scope_.pop_back (); - } - else if (container (m)) - { - // Container gets its own table, so nothing to do here. - // + old_kp.swap (key_prefix_); + old_dn.swap (default_name_); + + column_prefix_ = old_prefix; + member_scope_.pop_back (); } else { - if (oc_.traverse_column (m, oc_.column_prefix_ + column_name (m), first_)) + string name (column_prefix_ + column_name (m, key_prefix_, default_name_)); + + if (traverse_column (m, name, first_)) { if (first_) first_ = false; } } +} + +void object_columns_base::member:: +traverse (semantics::data_member& m) +{ + if (transient (m)) + return; + + // Container gets its own table, so nothing to do here. + // + if (container (m)) + return; + + oc_.member_path_.push_back (&m); + + semantics::type& t (utype (m)); + + if (semantics::class_* c = object_pointer (t)) + oc_.traverse_pointer (m, *c); + else + oc_.traverse_member (m, t); oc_.member_path_.pop_back (); } // +// object_columns_list +// + +void object_columns_list:: +traverse_pointer (semantics::data_member& m, semantics::class_& c) +{ + // Ignore inverse object pointers. + // + if (!ignore_inverse_ || !inverse (m, key_prefix_)) + object_columns_base::traverse_pointer (m, c); +} + +bool object_columns_list:: +traverse_column (semantics::data_member& m, std::string const& name, bool) +{ + columns_.push_back (column (name, column_type (), m)); + return true; +} + +// // typedefs // diff --git a/odb/common.hxx b/odb/common.hxx index 28ed2c1..ffbd2d6 100644 --- a/odb/common.hxx +++ b/odb/common.hxx @@ -6,7 +6,9 @@ #define ODB_COMMON_HXX #include +#include #include // std::size_t +#include #include @@ -18,6 +20,14 @@ struct object_members_base: traversal::class_, virtual context virtual void traverse_simple (semantics::data_member&); + // Traverse object pointer. The second argument is the pointed-to + // class. When overriding this function, you will most likely want + // to call the base in order to traverse the pointer as a simple + // member (simple object id) or as composite (composite object id). + // + virtual void + traverse_pointer (semantics::data_member&, semantics::class_&); + // If you override this function, you can call the base to traverse // bases and members. The first argument is the data member and can // be NULL if we are traversing the root type or a base. The second @@ -81,11 +91,6 @@ public: virtual void traverse (semantics::class_&); - // Composite value with data member. - // - virtual void - traverse (semantics::data_member&, semantics::class_& comp); - public: // Append composite member prefix. // @@ -93,13 +98,21 @@ public: append (semantics::data_member&, table_prefix&); protected: - std::string flat_prefix_; + string flat_prefix_; table_prefix table_prefix_; - std::string member_prefix_; + string member_prefix_; data_member_path member_path_; data_member_scope member_scope_; +protected: + semantics::data_member* + id () const + { + assert (!member_path_.empty ()); + return context::id (member_path_); + } + private: void init (bool build_flat_prefix, @@ -115,6 +128,9 @@ private: } private: + virtual void + traverse_member (semantics::data_member&, semantics::type&); + struct member: traversal::data_member { member (object_members_base& om) @@ -148,8 +164,16 @@ struct object_columns_base: traversal::class_, virtual context // virtual bool traverse_column (semantics::data_member&, - std::string const& name, - bool first) = 0; + string const& name, + bool first); + + // Traverse object pointer. The second argument is the pointed-to + // class. When overriding this function, you will most likely want + // to call the base in order to traverse the member as a column + // (simple object id) or columns (composite object id). + // + virtual void + traverse_pointer (semantics::data_member&, semantics::class_&); // If you override this function, you can call the base to traverse // bases and members. The first argument is the data member and can @@ -179,14 +203,22 @@ struct object_columns_base: traversal::class_, virtual context flush (); public: - object_columns_base () - : top_level_ (true), member_ (*this) + object_columns_base (bool first = true, + string const& column_prefix = string ()) + : column_prefix_ (column_prefix), + root_ (0), + first_ (first), + top_level_ (true), + member_ (*this) { init (); } - object_columns_base (object_columns_base const&) + object_columns_base (object_columns_base const& x) : context (), //@@ -Wextra + column_prefix_ (x.column_prefix_), + root_ (0), + first_ (x.first_), top_level_ (true), member_ (*this) { @@ -196,28 +228,76 @@ public: virtual void traverse (semantics::class_&); - // Composite value with data member. + // Traverse a data member with type, which can be a simple or composite + // value type, or an object pointer (with a simple or composite id). // virtual void + traverse (semantics::data_member& m) + { + traverse (m, utype (m), string (), string ()); + } + + virtual void traverse (semantics::data_member&, - semantics::class_& comp, - std::string const& key_prefix, - std::string const& default_name); + semantics::type&, + string const& key_prefix, + string const& default_name, + semantics::class_* top_object = 0); // If not 0, switch top object. public: // Return column prefix for composite data member. // static string column_prefix (semantics::data_member&, - std::string const& key_prefix = std::string (), - std::string const& default_name = std::string ()); + string const& key_prefix = string (), + string const& default_name = string ()); + + // Return column prefix up to (but not including) the last member + // in the path. + // + static string + column_prefix (data_member_path const&); protected: + string key_prefix_; + string default_name_; + string column_prefix_; data_member_path member_path_; data_member_scope member_scope_; +protected: + semantics::data_member* + id () const + { + if (root_ != 0) + return root_id_ ? root_ : 0; // Cannot have ids below root. + else + { + assert (!member_path_.empty ()); + return context::id (member_path_); + } + } + + string + column_type () + { + if (member_path_.empty ()) + { + assert (root_ != 0); + return context::column_type (*root_, key_prefix_); + } + else + return context::column_type ( + member_path_, key_prefix_, (root_ != 0 && (root_id_ || root_op_))); + } + +private: + semantics::data_member* root_; // Root member if traversing from a member. + bool root_id_; // True if traversing root as object id. + bool root_op_; // True if traversing root as object pointer. + private: void init () @@ -227,21 +307,21 @@ private: } private: + virtual void + traverse_member (semantics::data_member&, semantics::type&); + struct member: traversal::data_member, context { - member (object_columns_base& oc) - : oc_ (oc), first_ (true) - { - } + member (object_columns_base& oc): oc_ (oc) {} virtual void traverse (semantics::data_member&); public: object_columns_base& oc_; - bool first_; }; + bool first_; bool top_level_; member member_; @@ -249,6 +329,56 @@ private: traversal::inherits inherits_; }; +struct object_columns_list: object_columns_base +{ + object_columns_list (bool ignore_inverse = true) + : ignore_inverse_ (ignore_inverse) + { + } + + object_columns_list (string const& column_prefix, bool ignore_inverse = true) + : object_columns_base (true, column_prefix), + ignore_inverse_ (ignore_inverse) + { + } + + struct column + { + column (std::string const& n, + std::string const& t, + semantics::data_member& m) + : name (n), type (t), member (&m) + { + } + + std::string name; + std::string type; + semantics::data_member* member; + }; + + typedef std::vector columns; + typedef columns::const_iterator iterator; + + iterator + begin () const {return columns_.begin ();} + + iterator + end () const {return columns_.end ();} + + columns::size_type + size () const {return columns_.size ();} + + virtual void + traverse_pointer (semantics::data_member&, semantics::class_&); + + virtual bool + traverse_column (semantics::data_member&, string const&, bool); + +private: + bool ignore_inverse_; + columns columns_; +}; + // Traverse composite values that are class template instantiations. // struct typedefs: traversal::typedefs, context diff --git a/odb/context.cxx b/odb/context.cxx index f4fa651..15579f3 100644 --- a/odb/context.cxx +++ b/odb/context.cxx @@ -210,6 +210,32 @@ context () context* context::current_; +semantics::data_member* context:: +id (data_member_path const& mp) +{ + for (data_member_path::const_reverse_iterator i (mp.rbegin ()); + i != mp.rend (); ++i) + { + if (id (**i)) + return *i; + } + + return 0; +} + +semantics::data_member* context:: +object_pointer (data_member_path const& mp) +{ + for (data_member_path::const_reverse_iterator i (mp.rbegin ()); + i != mp.rend (); ++i) + { + if (object_pointer (utype (**i))) + return *i; + } + + return 0; +} + bool context:: readonly (data_member_path const& mp, data_member_scope const& ms) { @@ -767,6 +793,9 @@ column_name (data_member_path const& mp) const string context:: column_name (semantics::data_member& m, string const& p, string const& d) const { + if (p.empty () && d.empty ()) + return column_name (m); + // A container column name can be specified for the member or for the // container type. // @@ -786,6 +815,23 @@ column_name (semantics::data_member& m, string const& p, string const& d) const } string context:: +column_type (const data_member_path& mp, string const& kp, bool id) +{ + if (kp.empty ()) + { + // Return the id type if this member is or is a part of an object id + // or pointer to object. + // + return mp.back ()->get ( + id || context::id (mp) || object_pointer (mp) + ? "column-id-type" + : "column-type"); + } + else + return indirect_value (*mp.back (), kp + "-column-type"); +} + +string context:: column_type (semantics::data_member& m, string const& kp) { return kp.empty () @@ -1221,14 +1267,23 @@ namespace struct column_count_impl: object_members_base { virtual void + traverse_pointer (semantics::data_member& m, semantics::class_& c) + { + size_t t (c_.total); + + object_members_base::traverse_pointer (m, c); + + if (context::inverse (m)) + c_.inverse += (c_.total - t); + } + + virtual void traverse_simple (semantics::data_member& m) { c_.total++; - if (m.count ("id")) + if (id ()) c_.id++; - else if (context::inverse (m)) - c_.inverse++; else if (context::readonly (member_path_, member_scope_)) c_.readonly++; else if (context::version (m)) @@ -1268,6 +1323,15 @@ namespace } virtual void + traverse_pointer (semantics::data_member&, semantics::class_&) + { + if (context::is_a (member_path_, member_scope_, flags_)) + r_++; + + // No need to go inside. + } + + virtual void traverse_simple (semantics::data_member&) { if (context::is_a (member_path_, member_scope_, flags_)) diff --git a/odb/context.hxx b/odb/context.hxx index e67af48..7e62161 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -364,6 +364,12 @@ public: return t.get ("element-type", 0); } + // If this data member is or is part of an object pointer, then + // return the member that is the pointer. Otherwise, return 0. + // + static semantics::data_member* + object_pointer (data_member_path const&); + static bool abstract (semantics::class_& c) { @@ -385,6 +391,12 @@ public: return m.count ("id"); } + // If this data member is or is part of an id member, then return + // the member that is marked as the id. Otherwise, return 0. + // + static semantics::data_member* + id (data_member_path const&); + static bool auto_ (semantics::data_member& m) { @@ -500,6 +512,11 @@ public: string const& default_name) const; string + column_type (const data_member_path&, + string const& key_prefix = string (), + bool id = false); // Pass true if this type is object id other + // than because of the members in the path. + string column_type (semantics::data_member&, string const& key_prefix = string ()); string diff --git a/odb/gcc.hxx b/odb/gcc.hxx index a9da1bd..da77027 100644 --- a/odb/gcc.hxx +++ b/odb/gcc.hxx @@ -42,6 +42,7 @@ extern "C" #endif #include +#include } #ifndef LOCATION_COLUMN diff --git a/odb/odb.cxx b/odb/odb.cxx index dd9b180..b17369a 100644 --- a/odb/odb.cxx +++ b/odb/odb.cxx @@ -692,6 +692,26 @@ main (int argc, char* argv[]) << "#endif" << endl << endl; + // Add ODB compiler metaprogramming tests. + // + os << "namespace odb" << endl + << "{" << endl + << "namespace compiler" << endl + << "{" << endl; + + // operator< test, used in validator. + // + os << "template " << endl + << "bool" << endl + << "has_lt_operator (T const& x, T const& y)" << endl + << "{" << endl + << "bool r (x < y);" << endl + << "return r;" << endl + << "}" << endl; + + os << "}" << endl + << "}" << endl; + // Add custom prologue if any. // // NOTE: if you change the format, you also need to update code diff --git a/odb/pragma.cxx b/odb/pragma.cxx index 5149bee..6c92f0f 100644 --- a/odb/pragma.cxx +++ b/odb/pragma.cxx @@ -493,18 +493,8 @@ check_spec_decl_type (tree d, return false; } } - else if (p == "id_type") - { - // Id type can only be used for types. - // - if (!TYPE_P (d)) - { - error (l) << "name '" << name << "' in db pragma " << p << " does " - << "not refer to a type" << endl; - return false; - } - } else if (p == "type" || + p == "id_type" || p == "value_type" || p == "index_type" || p == "key_type") @@ -1841,6 +1831,7 @@ handle_pragma_qualifier (cpp_reader* reader, string const& p) p == "key_options" || p == "id_options" || p == "type" || + p == "id_type" || p == "value_type" || p == "index_type" || p == "key_type" || diff --git a/odb/relational/common.cxx b/odb/relational/common.cxx index bf325f7..381d77f 100644 --- a/odb/relational/common.cxx +++ b/odb/relational/common.cxx @@ -78,16 +78,11 @@ namespace relational } } - bool query_columns_base:: - traverse_column (semantics::data_member& m, string const& column, bool) + void query_columns_base:: + traverse_pointer (semantics::data_member& m, semantics::class_& c) { - semantics::class_* ptr (object_pointer (utype (m))); - - if (ptr == 0) - return false; - string name (public_name (m)); - bool inv (inverse (m)); + bool inv (inverse (m, key_prefix_)); if (decl_) { @@ -101,7 +96,7 @@ namespace relational os << "typedef" << endl << "odb::query_pointer<" << endl << " odb::pointer_query_columns<" << endl - << " " << class_fq_name (*ptr) << "," << endl + << " " << class_fq_name (c) << "," << endl << " " << name << "_alias_ > >" << endl << name << "_type_ ;" << endl @@ -111,11 +106,32 @@ namespace relational } else { - // For now use column name as table alias. - // @@ This will become problematic when we add support for composite ids. + // Come up with a table alias. Generally, we want it to be based + // on the column name. This is straightforward for single-column + // references. In case of a composite id, we will need to use the + // column prefix which is based on the data member name, unless + // overridden by the user. In the latter case the prefix can be + // empty, in which case we will just fall back on the member's + // public name. // + string alias; + + if (composite_wrapper (utype ((*id_member (c))))) + { + string p (column_prefix (m, key_prefix_, default_name_)); + + if (p.empty ()) + p = public_name_db (m); + else + p.resize (p.size () - 1); // Remove trailing underscore. + + alias = column_prefix_ + p; + } + else + alias = column_prefix_ + column_name (m, key_prefix_, default_name_); + os << "const char " << scope_ << "::" << name << "_alias_[] = " << - strlit (quote_id (column)) << ";" + strlit (quote_id (alias)) << ";" << endl; if (inv) @@ -123,8 +139,6 @@ namespace relational << scope_ << "::" << name << ";" << endl; } - - return true; } // query_columns @@ -132,13 +146,13 @@ namespace relational query_columns:: query_columns (bool ptr) - : ptr_ (ptr), decl_ (true) + : ptr_ (ptr), decl_ (true), in_ptr_ (false) { } query_columns:: query_columns (bool ptr, semantics::class_& c) //@@ context::{cur,top}_object - : ptr_ (ptr), decl_ (false) + : ptr_ (ptr), decl_ (false), in_ptr_ (false) { scope_ = ptr ? "pointer_query_columns" : "query_columns"; scope_ += "< " + class_fq_name (c) + ", table >"; @@ -164,12 +178,13 @@ namespace relational } string name (public_name (*m)); + string suffix (in_ptr_ ? "_column_type_" : "_type_"); if (decl_) { os << "// " << name << endl << "//" << endl - << "struct " << name << "_type_"; + << "struct " << name << suffix; // Derive from the base in query_columns_base. It contains columns // data for the pointer members. @@ -178,27 +193,29 @@ namespace relational os << ": " << name << "_base_"; os << "{" - << name << "_type_ (){}"; // For some reason GCC needs this c-tor - // if we make the static member const. + << name << suffix << " (){}"; // For some reason GCC needs this c-tor + // if we make the static member const. object_columns_base::traverse_composite (m, c); - os << "};" - << "static const " << name << "_type_ " << name << ";" - << endl; + os << "};"; + + if (!in_ptr_) + os << "static const " << name << "_type_ " << name << ";" + << endl; } else { // Handle nested members first. // string old_scope (scope_); - scope_ += "::" + name + "_type_"; + scope_ += "::" + name + suffix; object_columns_base::traverse_composite (m, c); scope_ = old_scope; - // Composite member. + // Composite member. Note that here we don't use suffix. // os << "template " << endl << "const typename " << scope_ << "::" << name << "_type_" << endl @@ -216,39 +233,16 @@ namespace relational << "}"; } - bool query_columns:: - traverse_column (semantics::data_member& m, string const& column, bool) + void query_columns:: + column_common (semantics::data_member& m, + string const& type, + string const& column, + string const& suffix) { - semantics::names* hint; - semantics::type& t (utype (m, hint)); - semantics::class_* ptr (object_pointer (t)); - - if (ptr != 0) - { - // If this is for the pointer_query_columns and the member is not - // inverse, then create the normal member corresponding to the id - // column. This will allow the user to check it for NULL or to - // compare ids. In case this is for query_columns, then for the - // inverse member everything has been generated in query_columns_base. - // - if (inverse (m)) - return false; - } - string name (public_name (m)); if (decl_) { - string type; - if (ptr != 0) - { - semantics::data_member& id (*id_member (*ptr)); - semantics::type& t (utype (id, hint)); - type = t.fq_name (hint); - } - else - type = t.fq_name (hint); - string type_id (database_type_id (m)); os << "// " << name << endl @@ -259,43 +253,14 @@ namespace relational << " " << db << "::value_traits<" << endl << " " << type << "," << endl << " " << type_id << " >::query_type," << endl - << " " << type_id << " >" << endl; - - if (ptr == 0 || ptr_) - os << name << "_type_;" - << endl; - else - { - os << name << "_column_type_;" - << endl - << "typedef" << endl - << "odb::query_pointer<" << endl - << " odb::pointer_query_columns<" << endl - << " " << class_fq_name (*ptr) << "," << endl - << " " << name << "_alias_ > >" << endl - << name << "_pointer_type_;" - << endl; - - // If this is a non-inverse relationship, then make the column have - // a dual interface: that of an object pointer and of an id column. - // The latter allows the user to, for example, use the is_null() - // test in a natural way. For inverse relationships there is no - // column and so the column interface is not available. - // - os << "struct " << name << "_type_: " << - name << "_pointer_type_, " << name << "_column_type_" - << "{"; - - column_ctor (name + "_type_", name + "_column_type_"); - - os << "};"; - } - - os << "static const " << name << "_type_ " << name << ";" + << " " << type_id << " >" << endl + << name << suffix << ";" << endl; } else { + // Note that here we don't use suffix. + // os << "template " << endl << "const typename " << scope_ << "::" << name << "_type_" << endl << scope_ << "::" << name << " (" << "table, " << @@ -306,10 +271,134 @@ namespace relational os << ");" << endl; } + } + + bool query_columns:: + traverse_column (semantics::data_member& m, string const& column, bool) + { + semantics::names* hint; + semantics::type& t (utype (m, hint)); + + column_common (m, t.fq_name (hint), column); + + if (decl_) + { + string name (public_name (m)); + + os << "static const " << name << "_type_ " << name << ";" + << endl; + } return true; } + void query_columns:: + traverse_pointer (semantics::data_member& m, semantics::class_& c) + { + // If this is for the pointer_query_columns and the member is not + // inverse, then create the normal member corresponding to the id + // column. This will allow the user to check it for NULL or to + // compare ids. In case this is for query_columns, then for the + // inverse member everything has been generated in query_columns_base. + // + if (inverse (m, key_prefix_)) + return; + + string name (public_name (m)); + + semantics::data_member& id (*id_member (c)); + semantics::names* hint; + semantics::type& t (utype (id, hint)); + + if (composite_wrapper (t)) + { + // Composite id. + // + + // For pointer_query_columns generate normal composite mapping. + // + if (ptr_) + object_columns_base::traverse_pointer (m, c); + else + { + // If this is a non-inverse relationship, then make the column have + // a dual interface: that of an object pointer and of an id column. + // The latter allows the user to, for example, use the is_null() + // test in a natural way. For inverse relationships there is no + // column and so the column interface is not available. + // + in_ptr_ = true; + object_columns_base::traverse_pointer (m, c); + in_ptr_ = false; + + if (decl_) + { + os << "typedef" << endl + << "odb::query_pointer<" << endl + << " odb::pointer_query_columns<" << endl + << " " << class_fq_name (c) << "," << endl + << " " << name << "_alias_ > >" << endl + << name << "_pointer_type_;" + << endl; + + os << "struct " << name << "_type_: " << + name << "_pointer_type_, " << name << "_column_type_" + << "{" + << "};"; + + os << "static const " << name << "_type_ " << name << ";" + << endl; + } + } + } + else + { + // Simple id. + // + string type (t.fq_name (hint)); + string column (column_prefix_ + + column_name (m, key_prefix_, default_name_)); + + // For pointer_query_columns generate normal column mapping. + // + if (ptr_) + column_common (m, type, column); + else + { + // If this is a non-inverse relationship, then make the column have + // a dual interface: that of an object pointer and of an id column. + // The latter allows the user to, for example, use the is_null() + // test in a natural way. For inverse relationships there is no + // column and so the column interface is not available. + // + column_common (m, type, column, "_column_type_"); + + if (decl_) + { + os << "typedef" << endl + << "odb::query_pointer<" << endl + << " odb::pointer_query_columns<" << endl + << " " << class_fq_name (c) << "," << endl + << " " << name << "_alias_ > >" << endl + << name << "_pointer_type_;" + << endl; + + os << "struct " << name << "_type_: " << + name << "_pointer_type_, " << name << "_column_type_" + << "{"; + + column_ctor (name + "_type_", name + "_column_type_"); + + os << "};"; + } + } + + if (decl_) + os << "static const " << name << "_type_ " << name << ";" + << endl; + } + } + // // Dynamic traversal support. // diff --git a/odb/relational/common.hxx b/odb/relational/common.hxx index d552a72..276868c 100644 --- a/odb/relational/common.hxx +++ b/odb/relational/common.hxx @@ -42,7 +42,7 @@ namespace relational protected: // For virtual inheritance only. Should not be actually called. // - member_base (); // {assert (false);} + member_base (); protected: string var_override_; @@ -51,6 +51,176 @@ namespace relational string key_prefix_; }; + // Template argument is the database SQL type (sql_type). + // + template + struct member_base_impl: virtual member_base + { + typedef member_base_impl base_impl; + + member_base_impl (base const& x): base (x) {} + + protected: + member_base_impl () {} + + public: + virtual T const& + member_sql_type (semantics::data_member&) = 0; + + virtual void + traverse (semantics::data_member&); + + struct member_info + { + semantics::data_member& m; // Member. + semantics::type& t; // Cvr-unqualified member C++ type, note + // that m.type () may not be the same as t. + semantics::class_* ptr; // Pointed-to object if m is an object + // pointer. In this case t is the id type + // while fq_type_ is the pointer fq-type. + semantics::type* wrapper; // Wrapper type if member is a composite or + // container wrapper, also cvr-unqualified. + // In this case t is the wrapped type. + bool cq; // True if the original (wrapper) type + // is const-qualified. + T const* st; // Member SQL type (only simple values). + string& var; // Member variable name with trailing '_'. + + // C++ type fq-name. + // + string + fq_type (bool unwrap = true) const + { + semantics::names* hint; + + if (wrapper != 0 && unwrap) + { + // Use the hint from the wrapper unless the wrapped type + // is qualified. + // + hint = wrapper->get ("wrapper-hint"); + utype (*context::wrapper (*wrapper), hint); + return t.fq_name (hint); + } + + // Use the original type from 'm' instead of 't' since the hint may + // be invalid for a different type. Plus, if a type is overriden, + // then the fq_type must be as well. + // + if (ptr != 0) + { + semantics::type& t (utype (*id_member (*ptr), hint)); + return t.fq_name (hint); + } + else if (fq_type_.empty ()) + { + semantics::type& t (utype (m, hint)); + return t.fq_name (hint); + } + else + return fq_type_; + } + + string + ptr_fq_type () const + { + assert (ptr != 0); + + if (fq_type_.empty ()) + { + // If type is overridden so should fq_type so it is safe to + // get the type from the member. + // + semantics::names* hint; + semantics::type& t (utype (m, hint)); + return t.fq_name (hint); + } + else + return fq_type_; + } + + string const& fq_type_; + + member_info (semantics::data_member& m_, + semantics::type& t_, + semantics::type* wrapper_, + bool cq_, + string& var_, + string const& fq_type) + : m (m_), + t (t_), + ptr (0), + wrapper (wrapper_), + cq (cq_), + st (0), + var (var_), + fq_type_ (fq_type) + { + } + }; + + bool + container (member_info& mi) + { + // This cannot be a container if we have a type override. + // + return type_override_ == 0 && context::container (mi.m); + } + + // The false return value indicates that no further callbacks + // should be called for this member. + // + virtual bool + pre (member_info&) + { + return true; + } + + virtual void + post (member_info&) + { + } + + virtual void + traverse_composite (member_info&) + { + } + + virtual void + traverse_container (member_info&) + { + } + + // Note that by default traverse_object_pointer() will traverse the + // pointed-to object id type. + // + virtual void + traverse_object_pointer (member_info&); + + virtual void + traverse_simple (member_info&) = 0; + }; + + // + // + struct member_database_type_id: virtual member_base + { + typedef member_database_type_id base; + + member_database_type_id (semantics::type* type = 0, + string const& fq_type = string (), + string const& key_prefix = string ()) + : member_base (type, fq_type, key_prefix) + { + } + + virtual string + database_type_id (semantics::data_member&) + { + assert (false); + } + }; + // // struct query_columns_base: object_columns_base, virtual context @@ -66,8 +236,8 @@ namespace relational virtual void traverse_composite (semantics::data_member*, semantics::class_&); - virtual bool - traverse_column (semantics::data_member&, string const&, bool); + virtual void + traverse_pointer (semantics::data_member&, semantics::class_&); protected: bool decl_; @@ -103,13 +273,24 @@ namespace relational virtual void traverse_composite (semantics::data_member*, semantics::class_&); + virtual void + column_common (semantics::data_member&, + string const& type, + string const& column, + string const& suffix = "_type_"); + virtual bool traverse_column (semantics::data_member&, string const&, bool); + virtual void + traverse_pointer (semantics::data_member&, semantics::class_&); + protected: bool ptr_; bool decl_; + bool in_ptr_; // True if we are "inside" an object pointer. + string scope_; string table_; string default_table_; @@ -349,4 +530,6 @@ namespace relational } } +#include + #endif // ODB_RELATIONAL_COMMON_HXX diff --git a/odb/relational/common.txx b/odb/relational/common.txx new file mode 100644 index 0000000..1d3ecbf --- /dev/null +++ b/odb/relational/common.txx @@ -0,0 +1,112 @@ +// file : odb/relational/common.txx +// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +namespace relational +{ + // + // member_base_impl + // + + template + void member_base_impl:: + traverse (semantics::data_member& m) + { + if (transient (m)) + return; + + string var; + + if (!var_override_.empty ()) + var = var_override_; + else + { + string const& name (m.name ()); + var = name + (name[name.size () - 1] == '_' ? "" : "_"); + } + + bool cq (type_override_ != 0 ? false : const_type (m.type ())); + semantics::type& t (type_override_ != 0 ? *type_override_ : utype (m)); + + semantics::type* cont; + if (semantics::class_* c = object_pointer (t)) + { + semantics::type& t (utype (*id_member (*c))); + semantics::class_* comp (composite_wrapper (t)); + + member_info mi (m, + (comp != 0 ? *comp : t), + (comp != 0 && wrapper (t) ? &t : 0), + cq, + var, + fq_type_override_); // Pointer type. + + mi.ptr = c; + + if (comp == 0) + mi.st = &member_sql_type (m); + + if (pre (mi)) + { + traverse_object_pointer (mi); + post (mi); + } + } + else if (semantics::class_* c = composite_wrapper (t)) + { + // If t is a wrapper, pass the wrapped type. Also pass the + // original, wrapper type. + // + member_info mi (m, + *c, + (wrapper (t) ? &t : 0), + cq, + var, + fq_type_override_); + if (pre (mi)) + { + traverse_composite (mi); + post (mi); + } + } + // This cannot be a container if we have a type override. + // + else if (type_override_ == 0 && (cont = context::container (m))) + { + // The same unwrapping logic as for composite values. + // + member_info mi (m, + *cont, + (wrapper (t) ? &t : 0), + cq, + var, + fq_type_override_); + if (pre (mi)) + { + traverse_container (mi); + post (mi); + } + } + else + { + member_info mi (m, t, 0, cq, var, fq_type_override_); + mi.st = &member_sql_type (m); + + if (pre (mi)) + { + traverse_simple (mi); + post (mi); + } + } + } + + template + void member_base_impl:: + traverse_object_pointer (member_info& mi) + { + 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 cf103b5..dda9e2a 100644 --- a/odb/relational/header.hxx +++ b/odb/relational/header.hxx @@ -441,8 +441,11 @@ namespace relational // size_t n; - if (class_* kc = composite_wrapper (*kt)) - n = column_count (*kc).total; + class_* ptr (object_pointer (*kt)); + semantics::type& t (ptr == 0 ? *kt : utype (*id_member (*ptr))); + + if (class_* comp = composite_wrapper (t)) + n = column_count (*comp).total; else n = 1; @@ -461,19 +464,27 @@ namespace relational // // Value is also a key. // - //if (class_* vc = composite_wrapper (vt)) - // cond_columns += column_count (*vc).total; - //else - // cond_columns++; - + // class_* ptr (object_pointer (vt)); + // semantics::type& t (ptr == 0 ? vt : utype (*id_member (*ptr))); + // + // if (class_* comp = composite_wrapper (t)) + // cond_columns += column_count (*comp).total; + // else + // cond_columns++; + // break; } } - if (class_* vc = composite_wrapper (vt)) - data_columns += column_count (*vc).total; - else - data_columns++; + { + class_* ptr (object_pointer (vt)); + semantics::type& t (ptr == 0 ? vt : utype (*id_member (*ptr))); + + if (class_* comp = composite_wrapper (t)) + data_columns += column_count (*comp).total; + else + data_columns++; + } // Store column counts for the source generator. // @@ -786,7 +797,7 @@ namespace relational } } - os << "const data_image_type&, database&);" + os << "const data_image_type&, database*);" << endl; // insert_one @@ -1138,7 +1149,7 @@ namespace relational // init (object, image) // os << "static void" << endl - << "init (object_type&, const image_type&, database&);" + << "init (object_type&, const image_type&, database*);" << endl; // init (id_image, id) @@ -1503,7 +1514,7 @@ namespace relational // init (view, image) // os << "static void" << endl - << "init (view_type&, const image_type&, database&);" + << "init (view_type&, const image_type&, database*);" << endl; // column_count @@ -1605,7 +1616,7 @@ namespace relational // init (value, image) // os << "static void" << endl - << "init (value_type&, const image_type&, database&);" + << "init (value_type&, const image_type&, database*);" << endl; os << "};"; diff --git a/odb/relational/model.hxx b/odb/relational/model.hxx index 5f6470d..66db3d6 100644 --- a/odb/relational/model.hxx +++ b/odb/relational/model.hxx @@ -26,12 +26,10 @@ namespace relational { typedef object_columns base; - object_columns (sema_rel::model& model, - sema_rel::table& table, - string const& prefix = string ()) + object_columns (sema_rel::model& model, sema_rel::table& table) : model_ (model), table_ (table), - prefix_ (prefix), + pkey_ (0), id_override_ (false) { } @@ -77,20 +75,25 @@ namespace relational virtual void traverse (semantics::data_member& m, - semantics::class_& c, - std::string const& kp, - std::string const& dn) + semantics::type& t, + string const& kp, + string const& dn, + semantics::class_* to = 0) { // This overrides the member name for a composite container value // or key. // if (!kp.empty ()) { - id_prefix_ = kp + "."; - id_override_ = true; + semantics::class_* c (object_pointer (t)); + if (composite_wrapper (c == 0 ? t : utype (*id_member (*c)))) + { + id_prefix_ = kp + "."; + id_override_ = true; + } } - object_columns_base::traverse (m, c, kp, dn); + object_columns_base::traverse (m, t, kp, dn, to); } using object_columns_base::traverse; @@ -98,23 +101,22 @@ namespace relational virtual bool traverse_column (semantics::data_member& m, string const& name, bool) { - // Ignore inverse object pointers. - // - if (inverse (m)) - return false; + bool id (object_columns_base::id ()); // Id or part of. + bool null (!id && context::null (m, key_prefix_)); - string id (id_prefix_ + (prefix_.empty () ? m.name () : prefix_)); + string col_id (id_prefix_ + + (key_prefix_.empty () ? m.name () : key_prefix_)); sema_rel::column& c ( - model_.new_node ( - id, column_type (m, prefix_), context::null (m, prefix_))); + model_.new_node (col_id, column_type (), null)); + c.set ("cxx-node", static_cast (&m)); model_.new_edge (table_, c, name); // An id member cannot have a default value. // - if (!context::id (m)) + if (!id) { string const& d (default_ (m)); @@ -124,14 +126,12 @@ namespace relational // If we have options, add them. // - string const& o (column_options (m, prefix_)); + string const& o (column_options (m, key_prefix_)); if (!o.empty ()) c.options (o); - constraints (m, name, id, c); - reference (m, name, id, c); - + constraints (m, name, col_id, c); return true; } @@ -188,58 +188,129 @@ namespace relational string const& /* id */, sema_rel::column& c) { - if (!id (m)) - return; - - sema_rel::primary_key& pk ( - model_.new_node (m.count ("auto"))); - pk.set ("cxx-node", static_cast (&m)); - - model_.new_edge (pk, c); - - // In most databases the primary key constraint can be manipulated - // without an explicit name. So we use the special empty name for - // primary keys in order not to clash with columns and other - // constraints. If the target database does not support unnamed - // primary key manipulation, then the database-specific code will - // have to come up with a suitable name. - // - model_.new_edge (table_, pk, ""); + if (table_.is_a ()) + { + if (semantics::data_member* idm = id ()) + { + if (pkey_ == 0) + { + pkey_ = &model_.new_node ( + m.count ("auto")); + pkey_->set ("cxx-node", static_cast (idm)); + + // In most databases the primary key constraint can be + // manipulated without an explicit name. So we use the special + // empty name for primary keys in order not to clash with + // columns and other constraints. If the target database does + // not support unnamed primary key manipulation, then the + // database-specific code will have to come up with a suitable + // name. + // + model_.new_edge (table_, *pkey_, ""); + } + + model_.new_edge (*pkey_, c); + } + } } virtual void - reference (semantics::data_member& m, - string const& name, - string const& id, - sema_rel::column& c) + traverse_pointer (semantics::data_member& m, semantics::class_& c) { - semantics::class_* p (object_pointer (member_utype (m, prefix_))); + using sema_rel::column; - if (p == 0) + // Ignore inverse object pointers. + // + if (inverse (m, key_prefix_)) return; + string id (id_prefix_ + + (key_prefix_.empty () ? m.name () : key_prefix_)); + sema_rel::foreign_key& fk ( model_.new_node ( id, - table_name (*p), + table_name (c), true)); // deferred fk.set ("cxx-node", static_cast (&m)); - fk.referenced_columns ().push_back (column_name (*id_member (*p))); + bool simple; + + // Get referenced columns. + // + { + semantics::data_member& idm (*id_member (c)); + + instance ocl; + ocl->traverse (idm); + + for (object_columns_list::iterator i (ocl->begin ()); + i != ocl->end (); ++i) + fk.referenced_columns ().push_back (i->name); + + simple = (fk.referenced_columns ().size () == 1); + } + + // Get the position of the last column. + // + sema_rel::table::names_iterator i (table_.names_end ()); + + while (i != table_.names_begin ()) + { + --i; + + if (i->nameable ().is_a ()) + break; + } + + // Traverse the object pointer as columns. + // + object_columns_base::traverse_pointer (m, c); - model_.new_edge (fk, c); + // Add the newly added columns to the foreign key. + // + if (i != table_.names_end ()) + ++i; + else + i = table_.names_begin (); + + for (; i != table_.names_end (); ++i) + { + if (column* c = dynamic_cast (&i->nameable ())) + model_.new_edge (fk, *c); + else + break; + } // Derive the constraint name. Generally, we want it to be based - // on the column name. This is straightforward for single column - // references. In case of the composite ids, we will need to use - // the column prefix which is based on the data member name, - // unless overridden (see how the column pragma works for members - // of composite value types). @@ This is a TODO. Perhaps use the - // up-to-and-including composite member prefix? Though it can be - // empty. + // on the column name. This is straightforward for single-column + // references. In case of a composite id, we will need to use the + // column prefix which is based on the data member name, unless + // overridden by the user. In the latter case the prefix can be + // empty, in which case we will just fall back on the member's + // public name. // - model_.new_edge (table_, fk, name + "_fk"); + string name; + + if (simple) + name = fk.contains_begin ()->column ().name (); + else + { + string p (column_prefix (m, key_prefix_, default_name_)); + + if (p.empty ()) + p = public_name_db (m); + + name = column_prefix_ + p; + } + + // Add an underscore unless we already have one. + // + if (name[name.size () - 1] != '_') + name += '_'; + + model_.new_edge (table_, fk, name + "fk"); } protected: @@ -249,7 +320,7 @@ namespace relational protected: sema_rel::model& model_; sema_rel::table& table_; - string prefix_; + sema_rel::primary_key* pkey_; string id_prefix_; bool id_override_; }; @@ -310,13 +381,15 @@ namespace relational using semantics::type; using semantics::data_member; + using sema_rel::index; + using sema_rel::column; + // Ignore inverse containers of object pointers. // if (inverse (m, "value")) return; container_kind_type ck (container_kind (ct)); - type& vt (container_vt (ct)); qname const& name (table_name (m, table_prefix_)); @@ -332,15 +405,14 @@ namespace relational model_.new_edge (model_, t, name); - // object_id (simple value, for now) + // object_id // - string id_name (column_name (m, "id", "object_id")); { - instance oc (model_, t, "id"); - oc->traverse_column (m, id_name, true); + instance oc (model_, t); + oc->traverse (m, container_idt (m), "id", "object_id"); } - // Foreign key for the object id. + // Foreign key and index for the object id. // { sema_rel::foreign_key& fk ( @@ -349,105 +421,91 @@ namespace relational table_name (*context::top_object), false, // immediate sema_rel::foreign_key::cascade)); - fk.set ("cxx-node", static_cast (&m)); - fk.referenced_columns ().push_back ( - column_name ( - *id_member (*context::top_object))); + index& in (model_.new_node (id + ".id")); + in.set ("cxx-node", static_cast (&m)); + + // Get referenced columns. + // + { + data_member& idm (*id_member (*context::top_object)); + + instance ocl; + ocl->traverse (idm); + + for (object_columns_list::iterator i (ocl->begin ()); + i != ocl->end (); ++i) + fk.referenced_columns ().push_back (i->name); + } // All the columns we have in this table so far are for the - // object id. + // object id. Add them to the foreign key and the index. // for (sema_rel::table::names_iterator i (t.names_begin ()); i != t.names_end (); ++i) - model_.new_edge ( - fk, dynamic_cast (i->nameable ())); + { + column& c (dynamic_cast (i->nameable ())); - // Derive the constraint name. See the comment for the other - // foreign key code above. + model_.new_edge (fk, c); + model_.new_edge (in, c); + } + + // Derive the names. See the comment for the other foreign key + // code above. // - model_.new_edge (t, fk, id_name + "_fk"); - } + // Note also that id_name can be a column prefix (if id is + // composite), in which case it can be empty and if not, then + // it will most likely already contain a trailing underscore. + // + string id_name (column_name (m, "id", "object_id")); - // index (simple value) - // - string index_name; - bool ordered (ck == ck_ordered && !unordered (m)); - if (ordered) - { - instance oc (model_, t, "index"); - index_name = column_name (m, "index", "index"); - oc->traverse_column (m, index_name, true); - } + if (id_name.empty ()) + id_name = "object_id"; - // key (simple or composite value) - // - if (ck == ck_map || ck == ck_multimap) - { - type& kt (container_kt (ct)); + if (id_name[id_name.size () - 1] != '_') + id_name += '_'; - if (semantics::class_* ckt = composite_wrapper (kt)) - { - instance oc (model_, t); - oc->traverse (m, *ckt, "key", "key"); - } - else - { - instance oc (model_, t, "key"); - string const& name (column_name (m, "key", "key")); - oc->traverse_column (m, name, true); - } + model_.new_edge (t, fk, id_name + "fk"); + + model_.new_edge ( + model_, in, name + "_" + id_name + "i"); } - // value (simple or composite value) + // index (simple value) // + bool ordered (ck == ck_ordered && !unordered (m)); + if (ordered) { - if (semantics::class_* cvt = composite_wrapper (vt)) - { - instance oc (model_, t); - oc->traverse (m, *cvt, "value", "value"); - } - else - { - instance oc (model_, t, "value"); - string const& name (column_name (m, "value", "value")); - oc->traverse_column (m, name, true); - } - } + instance oc (model_, t); + oc->traverse (m, container_it (ct), "index", "index"); - // Create indexes. - // - using sema_rel::index; - using sema_rel::column; + string col_name (column_name (m, "index", "index")); - { - index& i (model_.new_node (id + ".id")); - i.set ("cxx-node", static_cast (&m)); + index& in (model_.new_node (id + ".index")); + in.set ("cxx-node", static_cast (&m)); model_.new_edge ( - i, dynamic_cast (t.find (id_name)->nameable ())); + in, dynamic_cast (t.find (col_name)->nameable ())); - //@@ Once id can be composite, we need to revise this (see - // a comment for the foreign key generation above). - // model_.new_edge ( - model_, i, name + "_" + id_name + "_i"); + model_, in, name + "_" + col_name + "_i"); } - if (ordered) + // key + // + if (ck == ck_map || ck == ck_multimap) { - index& i (model_.new_node (id + ".index")); - i.set ("cxx-node", static_cast (&m)); - - model_.new_edge ( - i, dynamic_cast (t.find (index_name)->nameable ())); + instance oc (model_, t); + oc->traverse (m, container_kt (ct), "key", "key"); + } - // This is always a single column (simple value). - // - model_.new_edge ( - model_, i, name + "_" + index_name + "_i"); + // value + // + { + instance oc (model_, t); + oc->traverse (m, container_vt (ct), "value", "value"); } } diff --git a/odb/relational/mssql/common.cxx b/odb/relational/mssql/common.cxx index f4ce42e..20be733 100644 --- a/odb/relational/mssql/common.cxx +++ b/odb/relational/mssql/common.cxx @@ -16,91 +16,10 @@ namespace relational // member_base // - void member_base:: - traverse (semantics::data_member& m) + sql_type const& member_base:: + member_sql_type (semantics::data_member& m) { - if (transient (m)) - return; - - string var; - - if (!var_override_.empty ()) - var = var_override_; - else - { - string const& name (m.name ()); - var = name + (name[name.size () - 1] == '_' ? "" : "_"); - } - - bool cq (type_override_ != 0 ? false : const_type (m.type ())); - semantics::type& t (type_override_ != 0 ? *type_override_ : utype (m)); - - semantics::type* cont; - if (semantics::class_* c = composite_wrapper (t)) - { - // If t is a wrapper, pass the wrapped type. Also pass the - // original, wrapper type. - // - member_info mi (m, - *c, - (wrapper (t) ? &t : 0), - cq, - var, - fq_type_override_); - if (pre (mi)) - { - traverse_composite (mi); - post (mi); - } - } - // This cannot be a container if we have a type override. - // - else if (type_override_ == 0 && (cont = context::container (m))) - { - // The same unwrapping logic as for composite values. - // - member_info mi (m, - *cont, - (wrapper (t) ? &t : 0), - cq, - var, - fq_type_override_); - if (pre (mi)) - { - traverse_container (mi); - post (mi); - } - } - else - { - sql_type const& st (column_sql_type (m, key_prefix_)); - - if (semantics::class_* c = object_pointer (t)) - { - member_info mi (m, - utype (*id_member (*c)), - 0, - cq, - var, - fq_type_override_); - mi.st = &st; - if (pre (mi)) - { - traverse_object_pointer (mi); - post (mi); - } - } - else - { - member_info mi (m, t, 0, cq, var, fq_type_override_); - mi.st = &st; - if (pre (mi)) - { - traverse_simple (mi); - post (mi); - } - } - } + return parse_sql_type (column_type (m, key_prefix_), m); } void member_base:: @@ -406,15 +325,23 @@ namespace relational }; member_database_type_id:: + member_database_type_id (base const& x) + : member_base::base (x), // virtual base + base (x) + { + } + + member_database_type_id:: member_database_type_id (semantics::type* type, string const& fq_type, string const& key_prefix) - : relational::member_base (type, fq_type, key_prefix) + : member_base::base (type, fq_type, key_prefix), // virtual base + base (type, fq_type, key_prefix) { } string member_database_type_id:: - database_type_id (type& m) + database_type_id (semantics::data_member& m) { type_id_.clear (); member_base::traverse (m); @@ -535,6 +462,8 @@ namespace relational type_id_ = "mssql::id_rowversion"; } + entry member_database_type_id_; + // // query_columns // @@ -566,7 +495,7 @@ namespace relational { // For some types we need to pass precision and scale. // - sql_type const& st (column_sql_type (m)); + sql_type const& st (parse_sql_type (column_type (), m)); switch (st.type) { diff --git a/odb/relational/mssql/common.hxx b/odb/relational/mssql/common.hxx index 7f2009a..a04c9a3 100644 --- a/odb/relational/mssql/common.hxx +++ b/odb/relational/mssql/common.hxx @@ -12,118 +12,18 @@ namespace relational { namespace mssql { - struct member_base: virtual relational::member_base, context + struct member_base: virtual relational::member_base_impl, context { - member_base (base const& x): base (x) {} + member_base (base const& x): base (x), base_impl (x) {} // This c-tor is for the direct use inside the mssql namespace. // If you do use this c-tor, you should also explicitly call - // relational::member_base. + // relational::member_base (aka base). // member_base () {} - virtual void - traverse (semantics::data_member& m); - - struct member_info - { - semantics::data_member& m; // Member. - semantics::type& t; // Cvr-unqualified member C++ type, note - // that m.type () may not be the same as t. - semantics::type* wrapper; // Wrapper type if member is a composite or - // container wrapper, also cvr-unqualified. - // In this case t is the wrapped type. - bool cq; // True if the original (wrapper) type - // is const-qualified. - sql_type const* st; // Member SQL type (only simple values). - string& var; // Member variable name with trailing '_'. - - // C++ type fq-name. - // - string - fq_type (bool unwrap = true) const - { - semantics::names* hint; - - if (wrapper != 0 && unwrap) - { - // Use the hint from the wrapper unless the wrapped type - // is qualified. - // - hint = wrapper->get ("wrapper-hint"); - utype (*context::wrapper (*wrapper), hint); - return t.fq_name (hint); - } - - // Use the original type from 'm' instead of 't' since the hint - // may be invalid for a different type. Plus, if a type is - // overriden, then the fq_type must be as well. - // - if (fq_type_.empty ()) - { - semantics::type& t (utype (m, hint)); - return t.fq_name (hint); - } - else - return fq_type_; - } - - string const& fq_type_; - - member_info (semantics::data_member& m_, - semantics::type& t_, - semantics::type* wrapper_, - bool cq_, - string& var_, - string const& fq_type) - : m (m_), - t (t_), - wrapper (wrapper_), - cq (cq_), - st (0), - var (var_), - fq_type_ (fq_type) - { - } - }; - - bool - container (member_info& mi) - { - // This cannot be a container if we have a type override. - // - return type_override_ == 0 && context::container (mi.m); - } - - // The false return value indicates that no further callbacks - // should be called for this member. - // - virtual bool - pre (member_info&) - { - return true; - } - - virtual void - post (member_info&) - { - } - - virtual void - traverse_composite (member_info&) - { - } - - virtual void - traverse_container (member_info&) - { - } - - virtual void - traverse_object_pointer (member_info& mi) - { - traverse_simple (mi); - } + virtual sql_type const& + member_sql_type (semantics::data_member&); virtual void traverse_simple (member_info&); @@ -288,13 +188,17 @@ namespace relational string type_; }; - struct member_database_type_id: member_base + struct member_database_type_id: relational::member_database_type_id, + 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 ()); - string - database_type_id (type&); + + virtual string + database_type_id (semantics::data_member&); virtual void traverse_composite (member_info&); @@ -361,13 +265,17 @@ namespace relational { has_long_data (bool& r): r_ (r) {} + virtual void + traverse_pointer (semantics::data_member& m, semantics::class_& c) + { + if (!inverse (m, key_prefix_)) + object_columns_base::traverse_pointer (m, c); + } + virtual bool traverse_column (semantics::data_member& m, string const&, bool) { - if (inverse (m)) - return false; - - sql_type const& st (column_sql_type (m)); + sql_type const& st (parse_sql_type (column_type (), m)); switch (st.type) { diff --git a/odb/relational/mssql/context.cxx b/odb/relational/mssql/context.cxx index e882ba3..312cc03 100644 --- a/odb/relational/mssql/context.cxx +++ b/odb/relational/mssql/context.cxx @@ -557,17 +557,20 @@ namespace relational } sql_type const& context:: - column_sql_type (semantics::data_member& m, string const& kp) + parse_sql_type (string const& t, semantics::data_member& m) { - string key (kp.empty () - ? string ("mssql-column-sql-type") - : "mssql-" + kp + "-column-sql-type"); + // If this proves to be too expensive, we can maintain a + // cache of parsed types. + // + data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t)); - if (!m.count (key)) + if (i != data_->sql_type_cache_.end ()) + return i->second; + else { try { - m.set (key, parse_sql_type (column_type (m, kp))); + return (data_->sql_type_cache_[t] = parse_sql_type (t)); } catch (invalid_sql_type const& e) { @@ -577,8 +580,6 @@ namespace relational throw operation_failed (); } } - - return m.get (key); } sql_type context:: diff --git a/odb/relational/mssql/context.hxx b/odb/relational/mssql/context.hxx index 7530a9f..fe30746 100644 --- a/odb/relational/mssql/context.hxx +++ b/odb/relational/mssql/context.hxx @@ -5,6 +5,8 @@ #ifndef ODB_RELATIONAL_MSSQL_CONTEXT_HXX #define ODB_RELATIONAL_MSSQL_CONTEXT_HXX +#include + #include namespace relational @@ -83,8 +85,7 @@ namespace relational { public: sql_type const& - column_sql_type (semantics::data_member&, - string const& key_prefix = string ()); + parse_sql_type (string const&, semantics::data_member&); public: struct invalid_sql_type @@ -132,6 +133,9 @@ namespace relational struct data: base_context::data { data (std::ostream& os): base_context::data (os) {} + + typedef std::map sql_type_cache; + sql_type_cache sql_type_cache_; }; data* data_; }; diff --git a/odb/relational/mssql/model.cxx b/odb/relational/mssql/model.cxx index aec027c..c3f4948 100644 --- a/odb/relational/mssql/model.cxx +++ b/odb/relational/mssql/model.cxx @@ -28,7 +28,7 @@ namespace relational { // Make sure the column is mapped to an integer or DECIMAL type. // - switch (column_sql_type (m).type) + switch (parse_sql_type (column_type (), m).type) { case sql_type::BIT: case sql_type::TINYINT: diff --git a/odb/relational/mssql/source.cxx b/odb/relational/mssql/source.cxx index 1c11984..1b800aa 100644 --- a/odb/relational/mssql/source.cxx +++ b/odb/relational/mssql/source.cxx @@ -41,17 +41,16 @@ namespace relational virtual void column (semantics::data_member& m, - string const& key_prefix, string const& table, string const& column) { // Don't add a column for auto id in the INSERT statement. // if (!(sk_ == statement_insert && - key_prefix.empty () && - id (m) && auto_(m))) + key_prefix_.empty () && + context::id (m) && auto_(m))) // Only simple id can be auto. { - base::column (m, key_prefix, table, column); + base::column (m, table, column); } } }; @@ -413,181 +412,21 @@ namespace relational // init image // - struct init_image_member: relational::init_image_member, member_base + struct init_image_member: relational::init_image_member_impl, + member_base { init_image_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_database_type_id_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) { - // Ignore containers (they get their own table) and inverse - // object pointers (they are not present in this binding). - // - if (container (mi) || inverse (mi.m, key_prefix_)) - return false; - - if (!member_override_.empty ()) - member = member_override_; - else - { - // If we are generating standard init() and this member - // contains version, ignore it. - // - if (version (mi.m)) - return false; - - // For SQL Server we don't send auto id in INSERT statement - // (nor in UPDATE, as for other databases). So ignore it - // altogether. - // - if (id (mi.m) && auto_ (mi.m)) - return false; - - string const& name (mi.m.name ()); - member = "o." + name; - - os << "// " << name << endl - << "//" << endl; - - // If the whole class is readonly, then we will never be - // called with sk == statement_update. - // - if (!readonly (*context::top_object)) - { - semantics::class_* c; - - if (id (mi.m) || - readonly (mi.m) || - ((c = composite (mi.t)) && readonly (*c))) - os << "if (sk == statement_insert)"; - } - } - - // If this is a wrapped composite value, then we need to - // "unwrap" it. For simple values this is taken care of - // by the value_traits specializations. - // - if (mi.wrapper != 0 && composite (mi.t)) - { - // Here we need the wrapper type, not the wrapped type. - // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "get_ref (" + member + ")"; - } - - if (composite (mi.t)) - { - os << "{"; - traits = "composite_value_traits< " + mi.fq_type () + " >"; - } - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (semantics::class_* c = object_pointer (mt)) - { - type = "obj_traits::id_type"; - db_type_id = member_database_type_id_.database_type_id (mi.m); - - // Handle NULL pointers and extract the id. - // - os << "{" - << "typedef object_traits< " << class_fq_name (*c) << - " > obj_traits;"; - - if (weak_pointer (mt)) - { - os << "typedef pointer_traits< " << mi.fq_type () << - " > wptr_traits;" - << "typedef pointer_traits< wptr_traits::" << - "strong_pointer_type > ptr_traits;" - << endl - << "wptr_traits::strong_pointer_type sp (" << - "wptr_traits::lock (" << member << "));"; - - member = "sp"; - } - else - os << "typedef pointer_traits< " << mi.fq_type () << - " > ptr_traits;" - << endl; - - os << "bool is_null (ptr_traits::null_ptr (" << member << "));" - << "if (!is_null)" - << "{" - << "const " << type << "& id (" << endl; - - if (lazy_pointer (mt)) - os << "ptr_traits::object_id< ptr_traits::element_type > (" << - member << ")"; - else - os << "obj_traits::id (ptr_traits::get_ref (" << member << "))"; - - os << ");" - << endl; - - member = "id"; - } - else - { - type = mi.fq_type (); - db_type_id = member_database_type_id_.database_type_id (mi.m); - - os << "{" - << "bool is_null;"; - } - - traits = "mssql::value_traits<\n " - + type + ",\n " - + db_type_id + " >"; - } - - return true; } virtual void - post (member_info& mi) + set_null (member_info& mi) { - if (composite (mi.t)) - os << "}"; - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - if (object_pointer (member_utype (mi.m, key_prefix_))) - { - os << "}" - << "else" << endl; - - if (!null (mi.m, key_prefix_)) - os << "throw null_pointer ();"; - else - os << "i." << mi.var << "size_ind = SQL_NULL_DATA;"; - } - - os << "}"; - } - } - - virtual void - traverse_composite (member_info& mi) - { - os << traits << "::init (" << endl - << "i." << mi.var << "value," << endl - << member << "," << endl - << "sk);"; + os << "i." << mi.var << "size_ind = SQL_NULL_DATA;"; } virtual void @@ -800,14 +639,6 @@ namespace relational << "i." << mi.var << "value, is_null, " << member << ");" << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 8;"; } - - private: - string type; - string db_type_id; - string member; - string traits; - - member_database_type_id member_database_type_id_; }; entry init_image_member_; @@ -815,147 +646,21 @@ namespace relational // init value // - struct init_value_member: relational::init_value_member, member_base + struct init_value_member: relational::init_value_member_impl, + member_base { init_value_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_database_type_id_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - if (!member_override_.empty ()) - member = member_override_; - else - { - string const& name (mi.m.name ()); - member = "o." + name; - - if (mi.cq) - member = "const_cast< " + mi.fq_type (false) + "& > (" + - member + ")"; - - os << "// " << name << endl - << "//" << endl; - } - - // If this is a wrapped composite value, then we need to - // "unwrap" it. For simple values this is taken care of - // by the value_traits specializations. - // - if (mi.wrapper != 0 && composite (mi.t)) - { - // Here we need the wrapper type, not the wrapped type. - // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "set_ref (\n" + member + ")"; - } - - if (composite (mi.t)) - traits = "composite_value_traits< " + mi.fq_type () + " >"; - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (semantics::class_* c = object_pointer (mt)) - { - type = "obj_traits::id_type"; - db_type_id = member_database_type_id_.database_type_id (mi.m); - - // Handle NULL pointers and extract the id. - // - os << "{" - << "typedef object_traits< " << class_fq_name (*c) << - " > obj_traits;" - << "typedef pointer_traits< " << mi.fq_type () << - " > ptr_traits;" - << endl - << "if (i." << mi.var << "size_ind == SQL_NULL_DATA)" << endl; - - if (null (mi.m, key_prefix_)) - os << member << " = ptr_traits::pointer_type ();"; - else - os << "throw null_pointer ();"; - - os << "else" - << "{" - << type << " id;"; - - member = "id"; - } - else - { - type = mi.fq_type (); - db_type_id = member_database_type_id_.database_type_id (mi.m); - } - - traits = "mssql::value_traits<\n " - + type + ",\n " - + db_type_id + " >"; - } - - return true; - } - - virtual void - post (member_info& mi) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) { - if (composite (mi.t)) - return; - - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (object_pointer (mt)) - { - if (!member_override_.empty ()) - member = member_override_; - else - { - member = "o." + mi.m.name (); - - if (mi.cq) - member = "const_cast< " + mi.fq_type (false) + "& > (" + - member + ")"; - } - - if (lazy_pointer (mt)) - os << member << " = ptr_traits::pointer_type (db, id);"; - else - os << "// If a compiler error points to the line below, then" << endl - << "// it most likely means that a pointer used in a member" << endl - << "// cannot be initialized from an object pointer." << endl - << "//" << endl - << member << " = ptr_traits::pointer_type (" << endl - << "db.load< obj_traits::object_type > (id));"; - - os << "}" - << "}"; - } } virtual void - traverse_composite (member_info& mi) + get_null (member_info& mi) { - os << traits << "::init (" << endl - << member << "," << endl - << "i." << mi.var << "value," << endl - << "db);" - << endl; + os << "i." << mi.var << "size_ind == SQL_NULL_DATA"; } virtual void @@ -1141,14 +846,6 @@ namespace relational << "i." << mi.var << "size_ind == SQL_NULL_DATA);" << endl; } - - private: - string type; - string db_type_id; - string traits; - string member; - - member_database_type_id member_database_type_id_; }; entry init_value_member_; @@ -1172,7 +869,7 @@ namespace relational size_t n (cols.size ()); for (statement_columns::iterator i (cols.begin ()); n != 0; --n) { - sql_type const& st (column_sql_type (*i->member, i->key_prefix)); + sql_type const& st (parse_sql_type (i->type, *i->member)); bool l (false); diff --git a/odb/relational/mysql/common.cxx b/odb/relational/mysql/common.cxx index c8bad12..b8fa4c3 100644 --- a/odb/relational/mysql/common.cxx +++ b/odb/relational/mysql/common.cxx @@ -16,91 +16,10 @@ namespace relational // member_base // - void member_base:: - traverse (semantics::data_member& m) + sql_type const& member_base:: + member_sql_type (semantics::data_member& m) { - if (transient (m)) - return; - - string var; - - if (!var_override_.empty ()) - var = var_override_; - else - { - string const& name (m.name ()); - var = name + (name[name.size () - 1] == '_' ? "" : "_"); - } - - bool cq (type_override_ != 0 ? false : const_type (m.type ())); - semantics::type& t (type_override_ != 0 ? *type_override_ : utype (m)); - - semantics::type* cont; - if (semantics::class_* c = composite_wrapper (t)) - { - // If t is a wrapper, pass the wrapped type. Also pass the - // original, wrapper type. - // - member_info mi (m, - *c, - (wrapper (t) ? &t : 0), - cq, - var, - fq_type_override_); - if (pre (mi)) - { - traverse_composite (mi); - post (mi); - } - } - // This cannot be a container if we have a type override. - // - else if (type_override_ == 0 && (cont = context::container (m))) - { - // The same unwrapping logic as for composite values. - // - member_info mi (m, - *cont, - (wrapper (t) ? &t : 0), - cq, - var, - fq_type_override_); - if (pre (mi)) - { - traverse_container (mi); - post (mi); - } - } - else - { - sql_type const& st (column_sql_type (m, key_prefix_)); - - if (semantics::class_* c = object_pointer (t)) - { - member_info mi (m, - utype (*id_member (*c)), - 0, - cq, - var, - fq_type_override_); - mi.st = &st; - if (pre (mi)) - { - traverse_object_pointer (mi); - post (mi); - } - } - else - { - member_info mi (m, t, 0, cq, var, fq_type_override_); - mi.st = &st; - if (pre (mi)) - { - traverse_simple (mi); - post (mi); - } - } - } + return parse_sql_type (column_type (m, key_prefix_), m); } void member_base:: @@ -363,10 +282,18 @@ namespace relational }; member_database_type_id:: + member_database_type_id (base const& x) + : member_base::base (x), // virtual base + base (x) + { + } + + member_database_type_id:: member_database_type_id (semantics::type* type, - string const& fq_type, - string const& key_prefix) - : relational::member_base (type, fq_type, key_prefix) + string const& fq_type, + string const& key_prefix) + : member_base::base (type, fq_type, key_prefix), // virtual base + base (type, fq_type, key_prefix) { } @@ -437,6 +364,8 @@ namespace relational type_id_ = "mysql::id_set"; } + entry member_database_type_id_; + // // query_columns // diff --git a/odb/relational/mysql/common.hxx b/odb/relational/mysql/common.hxx index c49bd09..93f5fbd 100644 --- a/odb/relational/mysql/common.hxx +++ b/odb/relational/mysql/common.hxx @@ -12,118 +12,18 @@ namespace relational { namespace mysql { - struct member_base: virtual relational::member_base, context + struct member_base: virtual relational::member_base_impl, context { - member_base (base const& x): base (x) {} + member_base (base const& x): base (x), base_impl (x) {} // This c-tor is for the direct use inside the mysql namespace. // If you do use this c-tor, you should also explicitly call - // relational::member_base. + // relational::member_base (aka base). // member_base () {} - virtual void - traverse (semantics::data_member& m); - - struct member_info - { - semantics::data_member& m; // Member. - semantics::type& t; // Cvr-unqualified member C++ type, note - // that m.type () may not be the same as t. - semantics::type* wrapper; // Wrapper type if member is a composite or - // container wrapper, also cvr-unqualified. - // In this case t is the wrapped type. - bool cq; // True if the original (wrapper) type - // is const-qualified. - sql_type const* st; // Member SQL type (only simple values). - string& var; // Member variable name with trailing '_'. - - // C++ type fq-name. - // - string - fq_type (bool unwrap = true) const - { - semantics::names* hint; - - if (wrapper != 0 && unwrap) - { - // Use the hint from the wrapper unless the wrapped type - // is qualified. - // - hint = wrapper->get ("wrapper-hint"); - utype (*context::wrapper (*wrapper), hint); - return t.fq_name (hint); - } - - // Use the original type from 'm' instead of 't' since the hint - // may be invalid for a different type. Plus, if a type is - // overriden, then the fq_type must be as well. - // - if (fq_type_.empty ()) - { - semantics::type& t (utype (m, hint)); - return t.fq_name (hint); - } - else - return fq_type_; - } - - string const& fq_type_; - - member_info (semantics::data_member& m_, - semantics::type& t_, - semantics::type* wrapper_, - bool cq_, - string& var_, - string const& fq_type) - : m (m_), - t (t_), - wrapper (wrapper_), - cq (cq_), - st (0), - var (var_), - fq_type_ (fq_type) - { - } - }; - - bool - container (member_info& mi) - { - // This cannot be a container if we have a type override. - // - return type_override_ == 0 && context::container (mi.m); - } - - // The false return value indicates that no further callbacks - // should be called for this member. - // - virtual bool - pre (member_info&) - { - return true; - } - - virtual void - post (member_info&) - { - } - - virtual void - traverse_composite (member_info&) - { - } - - virtual void - traverse_container (member_info&) - { - } - - virtual void - traverse_object_pointer (member_info& mi) - { - traverse_simple (mi); - } + virtual sql_type const& + member_sql_type (semantics::data_member&); virtual void traverse_simple (member_info&); @@ -220,12 +120,16 @@ namespace relational string type_; }; - struct member_database_type_id: member_base + struct member_database_type_id: relational::member_database_type_id, + 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 ()); - string + + virtual string database_type_id (type&); virtual void diff --git a/odb/relational/mysql/context.cxx b/odb/relational/mysql/context.cxx index b8327eb..ad7236c 100644 --- a/odb/relational/mysql/context.cxx +++ b/odb/relational/mysql/context.cxx @@ -316,17 +316,20 @@ namespace relational // sql_type const& context:: - column_sql_type (semantics::data_member& m, string const& kp) + parse_sql_type (string const& t, semantics::data_member& m) { - string key (kp.empty () - ? string ("mysql-column-sql-type") - : "mysql-" + kp + "-column-sql-type"); + // If this proves to be too expensive, we can maintain a + // cache of parsed types. + // + data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t)); - if (!m.count (key)) + if (i != data_->sql_type_cache_.end ()) + return i->second; + else { try { - m.set (key, parse_sql_type (column_type (m, kp))); + return (data_->sql_type_cache_[t] = parse_sql_type (t)); } catch (invalid_sql_type const& e) { @@ -336,8 +339,6 @@ namespace relational throw operation_failed (); } } - - return m.get (key); } sql_type context:: diff --git a/odb/relational/mysql/context.hxx b/odb/relational/mysql/context.hxx index 49b825e..294b5d1 100644 --- a/odb/relational/mysql/context.hxx +++ b/odb/relational/mysql/context.hxx @@ -5,6 +5,7 @@ #ifndef ODB_RELATIONAL_MYSQL_CONTEXT_HXX #define ODB_RELATIONAL_MYSQL_CONTEXT_HXX +#include #include #include @@ -80,8 +81,7 @@ namespace relational { public: sql_type const& - column_sql_type (semantics::data_member&, - string const& key_prefix = string ()); + parse_sql_type (string const&, semantics::data_member&); public: struct invalid_sql_type @@ -138,6 +138,9 @@ namespace relational struct data: base_context::data { data (std::ostream& os): base_context::data (os) {} + + typedef std::map sql_type_cache; + sql_type_cache sql_type_cache_; }; data* data_; }; diff --git a/odb/relational/mysql/model.cxx b/odb/relational/mysql/model.cxx index 8e07f38..11ca157 100644 --- a/odb/relational/mysql/model.cxx +++ b/odb/relational/mysql/model.cxx @@ -35,7 +35,7 @@ namespace relational { // Make sure the column is mapped to an ENUM or integer type. // - sql_type const& t (column_sql_type (m)); + sql_type const& t (parse_sql_type (column_type (), m)); switch (t.type) { diff --git a/odb/relational/mysql/source.cxx b/odb/relational/mysql/source.cxx index 91b3061..3a784af 100644 --- a/odb/relational/mysql/source.cxx +++ b/odb/relational/mysql/source.cxx @@ -68,7 +68,6 @@ namespace relational virtual void column (semantics::data_member& m, - string const& key_prefix, string const& table, string const& column) { @@ -110,10 +109,12 @@ namespace relational // to value_traits. // + string type (column_type ()); + if (sk_ != statement_select || - column_sql_type (m, key_prefix).type != sql_type::ENUM) + parse_sql_type (type, m).type != sql_type::ENUM) { - base::column (m, key_prefix, table, column); + base::column (m, table, column); return; } @@ -140,7 +141,8 @@ namespace relational r += ")"; - sc_.push_back (relational::statement_column (r, m, key_prefix)); + sc_.push_back ( + relational::statement_column (r, type, m, key_prefix_)); } }; entry object_columns_; @@ -154,7 +156,9 @@ namespace relational { // The same idea as in object_columns. // - if (column_sql_type (m).type != sql_type::ENUM) + string type (column_type ()); + + if (parse_sql_type (type, m).type != sql_type::ENUM) { base::column (m, column); return; @@ -168,7 +172,7 @@ namespace relational r += column; r += ")"; - sc_.push_back (relational::statement_column (r, m)); + sc_.push_back (relational::statement_column (r, type, m)); } }; entry view_columns_; @@ -568,188 +572,37 @@ namespace relational // init image // - struct init_image_member: relational::init_image_member, member_base + struct init_image_member: relational::init_image_member_impl, + member_base { init_image_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_database_type_id_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) { } - virtual bool - pre (member_info& mi) - { - // Ignore containers (they get their own table) and inverse - // object pointers (they are not present in this binding). - // - if (container (mi) || inverse (mi.m, key_prefix_)) - return false; - - if (!member_override_.empty ()) - member = member_override_; - else - { - // If we are generating standard init() and this member - // contains version, ignore it. - // - if (version (mi.m)) - return false; - - string const& name (mi.m.name ()); - member = "o." + name; - - os << "// " << name << endl - << "//" << endl; - - // If the whole class is readonly, then we will never be - // called with sk == statement_update. - // - if (!readonly (*context::top_object)) - { - semantics::class_* c; - - if (id (mi.m) || - readonly (mi.m) || - ((c = composite (mi.t)) && readonly (*c))) - os << "if (sk == statement_insert)"; - } - } - - // If this is a wrapped composite value, then we need to - // "unwrap" it. For simple values this is taken care of - // by the value_traits specializations. - // - if (mi.wrapper != 0 && composite (mi.t)) - { - // Here we need the wrapper type, not the wrapped type. - // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "get_ref (" + member + ")"; - } - - if (composite (mi.t)) - { - os << "{"; - traits = "composite_value_traits< " + mi.fq_type () + " >"; - } - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (semantics::class_* c = object_pointer (mt)) - { - type = "obj_traits::id_type"; - db_type_id = member_database_type_id_.database_type_id (mi.m); - - // Handle NULL pointers and extract the id. - // - os << "{" - << "typedef object_traits< " << class_fq_name (*c) << - " > obj_traits;"; - - if (weak_pointer (mt)) - { - os << "typedef pointer_traits< " << mi.fq_type () << - " > wptr_traits;" - << "typedef pointer_traits< wptr_traits::" << - "strong_pointer_type > ptr_traits;" - << endl - << "wptr_traits::strong_pointer_type sp (" << - "wptr_traits::lock (" << member << "));"; - - member = "sp"; - } - else - os << "typedef pointer_traits< " << mi.fq_type () << - " > ptr_traits;" - << endl; - - os << "bool is_null (ptr_traits::null_ptr (" << member << "));" - << "if (!is_null)" - << "{" - << "const " << type << "& id (" << endl; - - if (lazy_pointer (mt)) - os << "ptr_traits::object_id< ptr_traits::element_type > (" << - member << ")"; - else - os << "obj_traits::id (ptr_traits::get_ref (" << member << "))"; - - os << ");" - << endl; - - member = "id"; - } - else - { - type = mi.fq_type (); - db_type_id = member_database_type_id_.database_type_id (mi.m); - - os << "{" - << "bool is_null;"; - } - - traits = "mysql::value_traits<\n " - + type + ",\n " - + db_type_id + " >"; - } - - return true; - } - virtual void - post (member_info& mi) + set_null (member_info& mi) { - if (composite (mi.t)) - os << "}"; - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - if (object_pointer (member_utype (mi.m, key_prefix_))) - { - os << "}"; - - if (!null (mi.m, key_prefix_)) - os << "else" << endl - << "throw null_pointer ();"; - } - - os << "i." << mi.var << "null = is_null;" - << "}"; - } - } - - virtual void - traverse_composite (member_info& mi) - { - os << "if (" << traits << "::init (" << endl - << "i." << mi.var << "value," << endl - << member << "," << endl - << "sk))" << endl - << "grew = true;"; + os << "i." << mi.var << "null = 1;"; } virtual void traverse_integer (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "null = is_null;"; } virtual void traverse_float (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "null = is_null;"; } virtual void @@ -764,6 +617,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "null = is_null;" << "i." << mi.var << "size = static_cast (size);" << "grew = grew || (cap != i." << mi.var << "value.capacity ());"; } @@ -772,7 +626,8 @@ namespace relational traverse_date_time (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "null = is_null;"; } virtual void @@ -787,6 +642,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "null = is_null;" << "i." << mi.var << "size = static_cast (size);" << "grew = grew || (cap != i." << mi.var << "value.capacity ());"; } @@ -801,6 +657,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "null = is_null;" << "i." << mi.var << "size = static_cast (size);" << "grew = grew || (cap != i." << mi.var << "value.capacity ());"; } @@ -817,6 +674,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "null = is_null;" << "i." << mi.var << "size = static_cast (size);"; } @@ -830,7 +688,9 @@ namespace relational << "i." << mi.var << "size," << endl << "is_null," << endl << member << "))" << endl - << "grew = true;"; + << "grew = true;" + << endl + << "i." << mi.var << "null = is_null;"; } virtual void @@ -845,17 +705,10 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "null = is_null;" << "i." << mi.var << "size = static_cast (size);" << "grew = grew || (cap != i." << mi.var << "value.capacity ());"; } - - private: - string type; - string db_type_id; - string member; - string traits; - - member_database_type_id member_database_type_id_; }; entry init_image_member_; @@ -863,147 +716,21 @@ namespace relational // init value // - struct init_value_member: relational::init_value_member, member_base + struct init_value_member: relational::init_value_member_impl, + member_base { init_value_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_database_type_id_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - if (!member_override_.empty ()) - member = member_override_; - else - { - string const& name (mi.m.name ()); - member = "o." + name; - - if (mi.cq) - member = "const_cast< " + mi.fq_type (false) + "& > (" + - member + ")"; - - os << "// " << name << endl - << "//" << endl; - } - - // If this is a wrapped composite value, then we need to - // "unwrap" it. For simple values this is taken care of - // by the value_traits specializations. - // - if (mi.wrapper != 0 && composite (mi.t)) - { - // Here we need the wrapper type, not the wrapped type. - // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "set_ref (\n" + member + ")"; - } - - if (composite (mi.t)) - traits = "composite_value_traits< " + mi.fq_type () + " >"; - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (semantics::class_* c = object_pointer (mt)) - { - type = "obj_traits::id_type"; - db_type_id = member_database_type_id_.database_type_id (mi.m); - - // Handle NULL pointers and extract the id. - // - os << "{" - << "typedef object_traits< " << class_fq_name (*c) << - " > obj_traits;" - << "typedef pointer_traits< " << mi.fq_type () << - " > ptr_traits;" - << endl - << "if (i." << mi.var << "null)" << endl; - - if (null (mi.m, key_prefix_)) - os << member << " = ptr_traits::pointer_type ();"; - else - os << "throw null_pointer ();"; - - os << "else" - << "{" - << type << " id;"; - - member = "id"; - } - else - { - type = mi.fq_type (); - db_type_id = member_database_type_id_.database_type_id (mi.m); - } - - traits = "mysql::value_traits<\n " - + type + ",\n " - + db_type_id + " >"; - } - - return true; - } - - virtual void - post (member_info& mi) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) { - if (composite (mi.t)) - return; - - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (object_pointer (mt)) - { - if (!member_override_.empty ()) - member = member_override_; - else - { - member = "o." + mi.m.name (); - - if (mi.cq) - member = "const_cast< " + mi.fq_type (false) + "& > (" + - member + ")"; - } - - if (lazy_pointer (mt)) - os << member << " = ptr_traits::pointer_type (db, id);"; - else - os << "// If a compiler error points to the line below, then" << endl - << "// it most likely means that a pointer used in a member" << endl - << "// cannot be initialized from an object pointer." << endl - << "//" << endl - << member << " = ptr_traits::pointer_type (" << endl - << "db.load< obj_traits::object_type > (id));"; - - os << "}" - << "}"; - } } virtual void - traverse_composite (member_info& mi) + get_null (member_info& mi) { - os << traits << "::init (" << endl - << member << "," << endl - << "i." << mi.var << "value," << endl - << "db);" - << endl; + os << "i." << mi.var << "null"; } virtual void @@ -1107,14 +834,6 @@ namespace relational << "i." << mi.var << "null);" << endl; } - - private: - string type; - string db_type_id; - string traits; - string member; - - member_database_type_id member_database_type_id_; }; entry init_value_member_; diff --git a/odb/relational/oracle/common.cxx b/odb/relational/oracle/common.cxx index 88183cf..661bb4e 100644 --- a/odb/relational/oracle/common.cxx +++ b/odb/relational/oracle/common.cxx @@ -16,91 +16,10 @@ namespace relational // member_base // - void member_base:: - traverse (semantics::data_member& m) + sql_type const& member_base:: + member_sql_type (semantics::data_member& m) { - if (transient (m)) - return; - - string var; - - if (!var_override_.empty ()) - var = var_override_; - else - { - string const& name (m.name ()); - var = name + (name[name.size () - 1] == '_' ? "" : "_"); - } - - bool cq (type_override_ != 0 ? false : const_type (m.type ())); - semantics::type& t (type_override_ != 0 ? *type_override_ : utype (m)); - - semantics::type* cont; - if (semantics::class_* c = composite_wrapper (t)) - { - // If t is a wrapper, pass the wrapped type. Also pass the - // original, wrapper type. - // - member_info mi (m, - *c, - (wrapper (t) ? &t : 0), - cq, - var, - fq_type_override_); - if (pre (mi)) - { - traverse_composite (mi); - post (mi); - } - } - // This cannot be a container if we have a type override. - // - else if (type_override_ == 0 && (cont = context::container (m))) - { - // The same unwrapping logic as for composite values. - // - member_info mi (m, - *cont, - (wrapper (t) ? &t : 0), - cq, - var, - fq_type_override_); - if (pre (mi)) - { - traverse_container (mi); - post (mi); - } - } - else - { - sql_type const& st (column_sql_type (m, key_prefix_)); - - if (semantics::class_* c = object_pointer (t)) - { - member_info mi (m, - utype (*id_member (*c)), - 0, - cq, - var, - fq_type_override_); - mi.st = &st; - if (pre (mi)) - { - traverse_object_pointer (mi); - post (mi); - } - } - else - { - member_info mi (m, t, 0, cq, var, fq_type_override_); - mi.st = &st; - if (pre (mi)) - { - traverse_simple (mi); - post (mi); - } - } - } + return parse_sql_type (column_type (m, key_prefix_), m); } void member_base:: @@ -374,10 +293,18 @@ namespace relational } member_database_type_id:: + member_database_type_id (base const& x) + : member_base::base (x), // virtual base + base (x) + { + } + + member_database_type_id:: member_database_type_id (semantics::type* type, string const& fq_type, string const& key_prefix) - : relational::member_base (type, fq_type, key_prefix) + : member_base::base (type, fq_type, key_prefix), // virtual base + base (type, fq_type, key_prefix) { } @@ -469,6 +396,8 @@ namespace relational lob_database_id[mi.st->type - sql_type::BLOB]; } + entry member_database_type_id_; + // // query_columns // @@ -494,7 +423,7 @@ namespace relational { // For some types we need to pass precision and scale. // - sql_type const& st (column_sql_type (m)); + sql_type const& st (parse_sql_type (column_type (), m)); switch (st.type) { diff --git a/odb/relational/oracle/common.hxx b/odb/relational/oracle/common.hxx index 0f4517d..15120fd 100644 --- a/odb/relational/oracle/common.hxx +++ b/odb/relational/oracle/common.hxx @@ -12,118 +12,18 @@ namespace relational { namespace oracle { - struct member_base: virtual relational::member_base, context + struct member_base: virtual relational::member_base_impl, context { - member_base (base const& x): base (x) {} + member_base (base const& x): base (x), base_impl (x) {} // This c-tor is for the direct use inside the oracle namespace. // If you do use this c-tor, you should also explicitly call - // relational::member_base. + // relational::member_base (aka base). // member_base () {} - virtual void - traverse (semantics::data_member& m); - - struct member_info - { - semantics::data_member& m; // Member. - semantics::type& t; // Cvr-unqualified member C++ type, note - // that m.type () may not be the same as t. - semantics::type* wrapper; // Wrapper type if member is a composite or - // container wrapper, also cvr-unqualified. - // In this case t is the wrapped type. - bool cq; // True if the original (wrapper) type - // is const-qualified. - sql_type const* st; // Member SQL type (only simple values). - string& var; // Member variable name with trailing '_'. - - // C++ type fq-name. - // - string - fq_type (bool unwrap = true) const - { - semantics::names* hint; - - if (wrapper != 0 && unwrap) - { - // Use the hint from the wrapper unless the wrapped type - // is qualified. - // - hint = wrapper->get ("wrapper-hint"); - utype (*context::wrapper (*wrapper), hint); - return t.fq_name (hint); - } - - // Use the original type from 'm' instead of 't' since the hint - // may be invalid for a different type. Plus, if a type is - // overriden, then the fq_type must be as well. - // - if (fq_type_.empty ()) - { - semantics::type& t (utype (m, hint)); - return t.fq_name (hint); - } - else - return fq_type_; - } - - string const& fq_type_; - - member_info (semantics::data_member& m_, - semantics::type& t_, - semantics::type* wrapper_, - bool cq_, - string& var_, - string const& fq_type) - : m (m_), - t (t_), - wrapper (wrapper_), - cq (cq_), - st (0), - var (var_), - fq_type_ (fq_type) - { - } - }; - - bool - container (member_info& mi) - { - // This cannot be a container if we have a type override. - // - return type_override_ == 0 && context::container (mi.m); - } - - // The false return value indicates that no further callbacks - // should be called for this member. - // - virtual bool - pre (member_info&) - { - return true; - } - - virtual void - post (member_info&) - { - } - - virtual void - traverse_composite (member_info&) - { - } - - virtual void - traverse_container (member_info&) - { - } - - virtual void - traverse_object_pointer (member_info& mi) - { - traverse_simple (mi); - } + virtual sql_type const& + member_sql_type (semantics::data_member&); virtual void traverse_simple (member_info&); @@ -240,12 +140,16 @@ namespace relational string type_; }; - struct member_database_type_id: member_base + struct member_database_type_id: relational::member_database_type_id, + 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 ()); - string + + virtual string database_type_id (type&); virtual void diff --git a/odb/relational/oracle/context.cxx b/odb/relational/oracle/context.cxx index 3d87729..dbe61af 100644 --- a/odb/relational/oracle/context.cxx +++ b/odb/relational/oracle/context.cxx @@ -159,17 +159,20 @@ namespace relational // sql_type const& context:: - column_sql_type (semantics::data_member& m, string const& kp) + parse_sql_type (string const& t, semantics::data_member& m) { - string key (kp.empty () - ? string ("oracle-column-sql-type") - : "oracle-" + kp + "-column-sql-type"); + // If this proves to be too expensive, we can maintain a + // cache of parsed types. + // + data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t)); - if (!m.count (key)) + if (i != data_->sql_type_cache_.end ()) + return i->second; + else { try { - m.set (key, parse_sql_type (column_type (m, kp))); + return (data_->sql_type_cache_[t] = parse_sql_type (t)); } catch (invalid_sql_type const& e) { @@ -179,8 +182,6 @@ namespace relational throw operation_failed (); } } - - return m.get (key); } sql_type context:: diff --git a/odb/relational/oracle/context.hxx b/odb/relational/oracle/context.hxx index 273f0e5..37716f0 100644 --- a/odb/relational/oracle/context.hxx +++ b/odb/relational/oracle/context.hxx @@ -5,6 +5,8 @@ #ifndef ODB_RELATIONAL_ORACLE_CONTEXT_HXX #define ODB_RELATIONAL_ORACLE_CONTEXT_HXX +#include + #include namespace relational @@ -73,8 +75,7 @@ namespace relational { public: sql_type const& - column_sql_type (semantics::data_member&, - string const& key_prefix = string ()); + parse_sql_type (string const&, semantics::data_member&); public: struct invalid_sql_type @@ -126,6 +127,9 @@ namespace relational struct data: base_context::data { data (std::ostream& os): base_context::data (os) {} + + typedef std::map sql_type_cache; + sql_type_cache sql_type_cache_; }; data* data_; }; diff --git a/odb/relational/oracle/model.cxx b/odb/relational/oracle/model.cxx index 347ea37..4b874c8 100644 --- a/odb/relational/oracle/model.cxx +++ b/odb/relational/oracle/model.cxx @@ -28,9 +28,7 @@ namespace relational { // Make sure the column is mapped to Oracle NUMBER. // - sql_type t (column_sql_type (m)); - - if (t.type != sql_type::NUMBER) + if (parse_sql_type (column_type (), m).type != sql_type::NUMBER) { cerr << m.file () << ":" << m.line () << ":" << m.column () << ": error: column with default value specified as C++ " diff --git a/odb/relational/oracle/source.cxx b/odb/relational/oracle/source.cxx index fb0984a..1d7d81f 100644 --- a/odb/relational/oracle/source.cxx +++ b/odb/relational/oracle/source.cxx @@ -316,187 +316,37 @@ namespace relational // init image // - struct init_image_member: relational::init_image_member, member_base + struct init_image_member: relational::init_image_member_impl, + member_base { init_image_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_database_type_id_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - // Ignore containers (they get their own table) and inverse - // object pointers (they are not present in this binding). - // - if (container (mi) || inverse (mi.m, key_prefix_)) - return false; - - if (!member_override_.empty ()) - member = member_override_; - else - { - // If we are generating standard init() and this member - // contains version, ignore it. - // - if (version (mi.m)) - return false; - - string const& name (mi.m.name ()); - member = "o." + name; - - os << "// " << name << endl - << "//" << endl; - - // If the whole class is readonly, then we will never be - // called with sk == statement_update. - // - if (!readonly (*context::top_object)) - { - semantics::class_* c; - - if (id (mi.m) || - readonly (mi.m) || - ((c = composite (mi.t)) && readonly (*c))) - os << "if (sk == statement_insert)"; - } - } - - // If this is a wrapped composite value, then we need to - // "unwrap" it. For simple values this is taken care of - // by the value_traits specializations. - // - if (mi.wrapper != 0 && composite (mi.t)) - { - // Here we need the wrapper type, not the wrapped type. - // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "get_ref (" + member + ")"; - } - - if (composite (mi.t)) - { - os << "{"; - traits = "composite_value_traits< " + mi.fq_type () + " >"; - } - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (semantics::class_* c = object_pointer (mt)) - { - type = "obj_traits::id_type"; - db_type_id = member_database_type_id_.database_type_id (mi.m); - - // Handle NULL pointers and extract the id. - // - os << "{" - << "typedef object_traits< " << class_fq_name (*c) << - " > obj_traits;"; - - if (weak_pointer (mt)) - { - os << "typedef pointer_traits< " << mi.fq_type () << - " > wptr_traits;" - << "typedef pointer_traits< wptr_traits::" << - "strong_pointer_type > ptr_traits;" - << endl - << "wptr_traits::strong_pointer_type sp (" << - "wptr_traits::lock (" << member << "));"; - - member = "sp"; - } - else - os << "typedef pointer_traits< " << mi.fq_type () << - " > ptr_traits;" - << endl; - - os << "bool is_null (ptr_traits::null_ptr (" << member << "));" - << "if (!is_null)" - << "{" - << "const " << type << "& id (" << endl; - - if (lazy_pointer (mt)) - os << "ptr_traits::object_id< ptr_traits::element_type > (" << - member << ")"; - else - os << "obj_traits::id (ptr_traits::get_ref (" << member << "))"; - - os << ");" - << endl; - - member = "id"; - } - else - { - type = mi.fq_type (); - db_type_id = member_database_type_id_.database_type_id (mi.m); - - os << "{" - << "bool is_null;"; - } - - traits = "oracle::value_traits<\n " - + type + ",\n " - + db_type_id + " >"; - } - - return true; - } - - virtual void - post (member_info& mi) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) { - if (composite (mi.t)) - os << "}"; - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - if (object_pointer (member_utype (mi.m, key_prefix_))) - { - os << "}"; - - if (!null (mi.m, key_prefix_)) - os << "else" << endl - << "throw null_pointer ();"; - } - - os << "i." << mi.var << "indicator = is_null ? -1 : 0;" - << "}"; - } } virtual void - traverse_composite (member_info& mi) + set_null (member_info& mi) { - os << traits << "::init (" << endl - << "i." << mi.var << "value," << endl - << member << "," << endl - << "sk);"; + os << "i." << mi.var << "indicator = -1;"; } virtual void traverse_int32 (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; } virtual void traverse_int64 (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; } virtual void @@ -508,6 +358,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;" << "i." << mi.var << "size = static_cast (size);"; } @@ -515,14 +366,16 @@ namespace relational traverse_float (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; } virtual void traverse_double (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; } virtual void @@ -535,6 +388,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;" << "i." << mi.var << "size = static_cast (size);"; } @@ -542,28 +396,32 @@ namespace relational traverse_date (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; } virtual void traverse_timestamp (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; } virtual void traverse_interval_ym (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; } virtual void traverse_interval_ds (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; } virtual void @@ -576,6 +434,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;" << "i." << mi.var << "size = static_cast (size);"; } @@ -587,16 +446,9 @@ namespace relational << "i." << mi.var << "callback.callback.param," << endl << "i." << mi.var << "callback.context.param," << endl << "is_null," << endl - << member << ");"; + << member << ");" + << "i." << mi.var << "indicator = is_null ? -1 : 0;"; } - - private: - string type; - string db_type_id; - string member; - string traits; - - member_database_type_id member_database_type_id_; }; entry init_image_member_; @@ -604,147 +456,21 @@ namespace relational // init value // - struct init_value_member: relational::init_value_member, member_base + struct init_value_member: relational::init_value_member_impl, + member_base { init_value_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_database_type_id_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - if (!member_override_.empty ()) - member = member_override_; - else - { - string const& name (mi.m.name ()); - member = "o." + name; - - if (mi.cq) - member = "const_cast< " + mi.fq_type (false) + "& > (" + - member + ")"; - - os << "// " << name << endl - << "//" << endl; - } - - // If this is a wrapped composite value, then we need to - // "unwrap" it. For simple values this is taken care of - // by the value_traits specializations. - // - if (mi.wrapper != 0 && composite (mi.t)) - { - // Here we need the wrapper type, not the wrapped type. - // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "set_ref (\n" + member + ")"; - } - - if (composite (mi.t)) - traits = "composite_value_traits< " + mi.fq_type () + " >"; - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (semantics::class_* c = object_pointer (mt)) - { - type = "obj_traits::id_type"; - db_type_id = member_database_type_id_.database_type_id (mi.m); - - // Handle NULL pointers and extract the id. - // - os << "{" - << "typedef object_traits< " << class_fq_name (*c) << - " > obj_traits;" - << "typedef pointer_traits< " << mi.fq_type () << - " > ptr_traits;" - << endl - << "if (i." << mi.var << "indicator == -1)" << endl; - - if (null (mi.m, key_prefix_)) - os << member << " = ptr_traits::pointer_type ();"; - else - os << "throw null_pointer ();"; - - os << "else" - << "{" - << type << " id;"; - - member = "id"; - } - else - { - type = mi.fq_type (); - db_type_id = member_database_type_id_.database_type_id (mi.m); - } - - traits = "oracle::value_traits<\n " - + type + ",\n " - + db_type_id + " >"; - } - - return true; - } - - virtual void - post (member_info& mi) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) { - if (composite (mi.t)) - return; - - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (object_pointer (mt)) - { - if (!member_override_.empty ()) - member = member_override_; - else - { - member = "o." + mi.m.name (); - - if (mi.cq) - member = "const_cast< " + mi.fq_type (false) + "& > (" + - member + ")"; - } - - if (lazy_pointer (mt)) - os << member << " = ptr_traits::pointer_type (db, id);"; - else - os << "// If a compiler error points to the line below, then" << endl - << "// it most likely means that a pointer used in a member" << endl - << "// cannot be initialized from an object pointer." << endl - << "//" << endl - << member << " = ptr_traits::pointer_type (" << endl - << "db.load< obj_traits::object_type > (id));"; - - os << "}" - << "}"; - } } virtual void - traverse_composite (member_info& mi) + get_null (member_info& mi) { - os << traits << "::init (" << endl - << member << "," << endl - << "i." << mi.var << "value," << endl - << "db);" - << endl; + os << "i." << mi.var << "indicator == -1"; } virtual void @@ -870,14 +596,6 @@ namespace relational << "i." << mi.var << "indicator == -1);" << endl; } - - private: - string type; - string db_type_id; - string traits; - string member; - - member_database_type_id member_database_type_id_; }; entry init_value_member_; diff --git a/odb/relational/pgsql/common.cxx b/odb/relational/pgsql/common.cxx index 0ec9f7c..3d7e01e 100644 --- a/odb/relational/pgsql/common.cxx +++ b/odb/relational/pgsql/common.cxx @@ -16,91 +16,10 @@ namespace relational // member_base // - void member_base:: - traverse (semantics::data_member& m) + sql_type const& member_base:: + member_sql_type (semantics::data_member& m) { - if (transient (m)) - return; - - string var; - - if (!var_override_.empty ()) - var = var_override_; - else - { - string const& name (m.name ()); - var = name + (name[name.size () - 1] == '_' ? "" : "_"); - } - - bool cq (type_override_ != 0 ? false : const_type (m.type ())); - semantics::type& t (type_override_ != 0 ? *type_override_ : utype (m)); - - semantics::type* cont; - if (semantics::class_* c = composite_wrapper (t)) - { - // If t is a wrapper, pass the wrapped type. Also pass the - // original, wrapper type. - // - member_info mi (m, - *c, - (wrapper (t) ? &t : 0), - cq, - var, - fq_type_override_); - if (pre (mi)) - { - traverse_composite (mi); - post (mi); - } - } - // This cannot be a container if we have a type override. - // - else if (type_override_ == 0 && (cont = context::container (m))) - { - // The same unwrapping logic as for composite values. - // - member_info mi (m, - *cont, - (wrapper (t) ? &t : 0), - cq, - var, - fq_type_override_); - if (pre (mi)) - { - traverse_container (mi); - post (mi); - } - } - else - { - sql_type const& st (column_sql_type (m, key_prefix_)); - - if (semantics::class_* c = object_pointer (t)) - { - member_info mi (m, - utype (*id_member (*c)), - 0, - cq, - var, - fq_type_override_); - mi.st = &st; - if (pre (mi)) - { - traverse_object_pointer (mi); - post (mi); - } - } - else - { - member_info mi (m, t, 0, cq, var, fq_type_override_); - mi.st = &st; - if (pre (mi)) - { - traverse_simple (mi); - post (mi); - } - } - } + return parse_sql_type (column_type (m, key_prefix_), m); } void member_base:: @@ -310,13 +229,21 @@ namespace relational "id_string", // TEXT, "id_bytea" // BYTEA }; - } + } + + member_database_type_id:: + member_database_type_id (base const& x) + : member_base::base (x), // virtual base + base (x) + { + } member_database_type_id:: member_database_type_id (semantics::type* type, string const& fq_type, string const& key_prefix) - : relational::member_base (type, fq_type, key_prefix) + : member_base::base (type, fq_type, key_prefix), // virtual base + base (type, fq_type, key_prefix) { } @@ -386,6 +313,8 @@ namespace relational type_id_ = "pgsql::id_uuid"; } + entry member_database_type_id_; + // // query_columns // diff --git a/odb/relational/pgsql/common.hxx b/odb/relational/pgsql/common.hxx index 4f3ee4d..4b9bbd5 100644 --- a/odb/relational/pgsql/common.hxx +++ b/odb/relational/pgsql/common.hxx @@ -12,118 +12,18 @@ namespace relational { namespace pgsql { - struct member_base: virtual relational::member_base, context + struct member_base: virtual relational::member_base_impl, context { - member_base (base const& x): base (x) {} + member_base (base const& x): base (x), base_impl (x) {} // This c-tor is for the direct use inside the pgsql namespace. // If you do use this c-tor, you should also explicitly call - // relational::member_base. + // relational::member_base (aka base). // member_base () {} - virtual void - traverse (semantics::data_member& m); - - struct member_info - { - semantics::data_member& m; // Member. - semantics::type& t; // Cvr-unqualified member C++ type, note - // that m.type () may not be the same as t. - semantics::type* wrapper; // Wrapper type if member is a composite or - // container wrapper, also cvr-unqualified. - // In this case t is the wrapped type. - bool cq; // True if the original (wrapper) type - // is const-qualified. - sql_type const* st; // Member SQL type (only simple values). - string& var; // Member variable name with trailing '_'. - - // C++ type fq-name. - // - string - fq_type (bool unwrap = true) const - { - semantics::names* hint; - - if (wrapper != 0 && unwrap) - { - // Use the hint from the wrapper unless the wrapped type - // is qualified. - // - hint = wrapper->get ("wrapper-hint"); - utype (*context::wrapper (*wrapper), hint); - return t.fq_name (hint); - } - - // Use the original type from 'm' instead of 't' since the hint - // may be invalid for a different type. Plus, if a type is - // overriden, then the fq_type must be as well. - // - if (fq_type_.empty ()) - { - semantics::type& t (utype (m, hint)); - return t.fq_name (hint); - } - else - return fq_type_; - } - - string const& fq_type_; - - member_info (semantics::data_member& m_, - semantics::type& t_, - semantics::type* wrapper_, - bool cq_, - string& var_, - string const& fq_type) - : m (m_), - t (t_), - wrapper (wrapper_), - cq (cq_), - st (0), - var (var_), - fq_type_ (fq_type) - { - } - }; - - bool - container (member_info& mi) - { - // This cannot be a container if we have a type override. - // - return type_override_ == 0 && context::container (mi.m); - } - - // The false return value indicates that no further callbacks - // should be called for this member. - // - virtual bool - pre (member_info&) - { - return true; - } - - virtual void - post (member_info&) - { - } - - virtual void - traverse_composite (member_info&) - { - } - - virtual void - traverse_container (member_info&) - { - } - - virtual void - traverse_object_pointer (member_info& mi) - { - traverse_simple (mi); - } + virtual sql_type const& + member_sql_type (semantics::data_member&); virtual void traverse_simple (member_info&); @@ -208,12 +108,16 @@ namespace relational string type_; }; - struct member_database_type_id: member_base + struct member_database_type_id: relational::member_database_type_id, + 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 ()); - string + + virtual string database_type_id (type&); virtual void diff --git a/odb/relational/pgsql/context.cxx b/odb/relational/pgsql/context.cxx index 8cc4eca..17ceaaa 100644 --- a/odb/relational/pgsql/context.cxx +++ b/odb/relational/pgsql/context.cxx @@ -236,17 +236,20 @@ namespace relational // sql_type const& context:: - column_sql_type (semantics::data_member& m, string const& kp) + parse_sql_type (string const& t, semantics::data_member& m) { - string key (kp.empty () - ? string ("pgsql-column-sql-type") - : "pgsql-" + kp + "-column-sql-type"); + // If this proves to be too expensive, we can maintain a + // cache of parsed types. + // + data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t)); - if (!m.count (key)) + if (i != data_->sql_type_cache_.end ()) + return i->second; + else { try { - m.set (key, parse_sql_type (column_type (m, kp))); + return (data_->sql_type_cache_[t] = parse_sql_type (t)); } catch (invalid_sql_type const& e) { @@ -256,8 +259,6 @@ namespace relational throw operation_failed (); } } - - return m.get (key); } sql_type context:: diff --git a/odb/relational/pgsql/context.hxx b/odb/relational/pgsql/context.hxx index a79cceb..1da353a 100644 --- a/odb/relational/pgsql/context.hxx +++ b/odb/relational/pgsql/context.hxx @@ -5,6 +5,8 @@ #ifndef ODB_RELATIONAL_PGSQL_CONTEXT_HXX #define ODB_RELATIONAL_PGSQL_CONTEXT_HXX +#include + #include namespace relational @@ -69,8 +71,7 @@ namespace relational { public: sql_type const& - column_sql_type (semantics::data_member&, - string const& key_prefix = string ()); + parse_sql_type (string const&, semantics::data_member&); public: struct invalid_sql_type @@ -124,6 +125,9 @@ namespace relational struct data: base_context::data { data (std::ostream& os): base_context::data (os) {} + + typedef std::map sql_type_cache; + sql_type_cache sql_type_cache_; }; data* data_; }; diff --git a/odb/relational/pgsql/model.cxx b/odb/relational/pgsql/model.cxx index ebd04af..06a7dde 100644 --- a/odb/relational/pgsql/model.cxx +++ b/odb/relational/pgsql/model.cxx @@ -34,7 +34,7 @@ namespace relational { // Make sure the column is mapped to an integer type. // - switch (column_sql_type (m).type) + switch (parse_sql_type (column_type (), m).type) { case sql_type::SMALLINT: case sql_type::INTEGER: diff --git a/odb/relational/pgsql/source.cxx b/odb/relational/pgsql/source.cxx index ceb7dbd..ae19e05 100644 --- a/odb/relational/pgsql/source.cxx +++ b/odb/relational/pgsql/source.cxx @@ -103,7 +103,21 @@ namespace relational struct statement_oids: object_columns_base, context { - statement_oids (statement_kind sk): sk_ (sk) {} + statement_oids (statement_kind sk, bool first = true) + : object_columns_base (first), sk_ (sk) + { + } + + virtual void + traverse_pointer (semantics::data_member& m, semantics::class_& c) + { + // Ignore certain columns depending on what kind statement we are + // generating. See object_columns in common source generator for + // details. + // + if (!(inverse (m, key_prefix_) && sk_ != statement_select)) + object_columns_base::traverse_pointer (m, c); + } virtual bool traverse_column (semantics::data_member& m, @@ -114,10 +128,7 @@ namespace relational // generating. See object_columns in common source generator for // details. // - if (inverse (m) && sk_ != statement_select) - return false; - - if ((id (m) || readonly (member_path_, member_scope_)) && + if ((id () || readonly (member_path_, member_scope_)) && sk_ == statement_update) return false; @@ -128,7 +139,7 @@ namespace relational if (!first) os << ',' << endl; - os << oids[column_sql_type (m).type]; + os << oids[parse_sql_type (column_type (), m).type]; return true; } @@ -471,188 +482,37 @@ namespace relational // init image // - struct init_image_member: relational::init_image_member, member_base + struct init_image_member: relational::init_image_member_impl, + member_base { init_image_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_database_type_id_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - // Ignore containers (they get their own table) and inverse - // object pointers (they are not present in this binding). - // - if (container (mi) || inverse (mi.m, key_prefix_)) - return false; - - if (!member_override_.empty ()) - member = member_override_; - else - { - // If we are generating standard init() and this member - // contains version, ignore it. - // - if (version (mi.m)) - return false; - - string const& name (mi.m.name ()); - member = "o." + name; - - os << "// " << name << endl - << "//" << endl; - - // If the whole class is readonly, then we will never be - // called with sk == statement_update. - // - if (!readonly (*context::top_object)) - { - semantics::class_* c; - - if (id (mi.m) || - readonly (mi.m) || - ((c = composite (mi.t)) && readonly (*c))) - os << "if (sk == statement_insert)"; - } - } - - // If this is a wrapped composite value, then we need to - // "unwrap" it. For simple values this is taken care of - // by the value_traits specializations. - // - if (mi.wrapper != 0 && composite (mi.t)) - { - // Here we need the wrapper type, not the wrapped type. - // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "get_ref (" + member + ")"; - } - - if (composite (mi.t)) - { - os << "{"; - traits = "composite_value_traits< " + mi.fq_type () + " >"; - } - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (semantics::class_* c = object_pointer (mt)) - { - type = "obj_traits::id_type"; - db_type_id = member_database_type_id_.database_type_id (mi.m); - - // Handle NULL pointers and extract the id. - // - os << "{" - << "typedef object_traits< " << class_fq_name (*c) << - " > obj_traits;"; - - if (weak_pointer (mt)) - { - os << "typedef pointer_traits< " << mi.fq_type () << - " > wptr_traits;" - << "typedef pointer_traits< wptr_traits::" << - "strong_pointer_type > ptr_traits;" - << endl - << "wptr_traits::strong_pointer_type sp (" << - "wptr_traits::lock (" << member << "));"; - - member = "sp"; - } - else - os << "typedef pointer_traits< " << mi.fq_type () << - " > ptr_traits;" - << endl; - - os << "bool is_null (ptr_traits::null_ptr (" << member << "));" - << "if (!is_null)" - << "{" - << "const " << type << "& id (" << endl; - - if (lazy_pointer (mt)) - os << "ptr_traits::object_id< ptr_traits::element_type > (" << - member << ")"; - else - os << "obj_traits::id (ptr_traits::get_ref (" << member << "))"; - - os << ");" - << endl; - - member = "id"; - } - else - { - type = mi.fq_type (); - db_type_id = member_database_type_id_.database_type_id (mi.m); - - os << "{" - << "bool is_null;"; - } - - traits = "pgsql::value_traits<\n " - + type + ",\n " - + db_type_id + " >"; - } - - return true; - } - - virtual void - post (member_info& mi) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) { - if (composite (mi.t)) - os << "}"; - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - if (object_pointer (member_utype (mi.m, key_prefix_))) - { - os << "}"; - - if (!null (mi.m, key_prefix_)) - os << "else" << endl - << "throw null_pointer ();"; - } - - os << "i." << mi.var << "null = is_null;" - << "}"; - } } virtual void - traverse_composite (member_info& mi) + set_null (member_info& mi) { - os << "if (" << traits << "::init (" << endl - << "i." << mi.var << "value," << endl - << member << "," << endl - << "sk))" << endl - << "grew = true;"; + os << "i." << mi.var << "null = true;"; } virtual void traverse_integer (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "null = is_null;"; } virtual void traverse_float (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "null = is_null;"; } virtual void @@ -667,6 +527,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "null = is_null;" << "i." << mi.var << "size = size;" << "grew = grew || (cap != i." << mi.var << "value.capacity ());"; } @@ -675,7 +536,8 @@ namespace relational traverse_date_time (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "null = is_null;"; } virtual void @@ -688,6 +550,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "null = is_null;" << "i." << mi.var << "size = size;" << "grew = grew || (cap != i." << mi.var << "value.capacity ());"; } @@ -702,6 +565,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "null = is_null;" << "i." << mi.var << "size = size;"; } @@ -715,6 +579,7 @@ namespace relational << "size," << endl << "is_null," << endl << member << ");" + << "i." << mi.var << "null = is_null;" << "i." << mi.var << "size = size;" << "grew = grew || (cap != i." << mi.var << "value.capacity ());"; } @@ -723,16 +588,9 @@ namespace relational traverse_uuid (member_info& mi) { os << traits << "::set_image (" << endl - << "i." << mi.var << "value, is_null, " << member << ");"; + << "i." << mi.var << "value, is_null, " << member << ");" + << "i." << mi.var << "null = is_null;"; } - - private: - string type; - string db_type_id; - string member; - string traits; - - member_database_type_id member_database_type_id_; }; entry init_image_member_; @@ -740,147 +598,21 @@ namespace relational // init value // - struct init_value_member: relational::init_value_member, member_base + struct init_value_member: relational::init_value_member_impl, + member_base { init_value_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_database_type_id_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - if (!member_override_.empty ()) - member = member_override_; - else - { - string const& name (mi.m.name ()); - member = "o." + name; - - if (mi.cq) - member = "const_cast< " + mi.fq_type (false) + "& > (" + - member + ")"; - - os << "// " << name << endl - << "//" << endl; - } - - // If this is a wrapped composite value, then we need to - // "unwrap" it. For simple values this is taken care of - // by the value_traits specializations. - // - if (mi.wrapper != 0 && composite (mi.t)) - { - // Here we need the wrapper type, not the wrapped type. - // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "set_ref (\n" + member + ")"; - } - - if (composite (mi.t)) - traits = "composite_value_traits< " + mi.fq_type () + " >"; - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (semantics::class_* c = object_pointer (mt)) - { - type = "obj_traits::id_type"; - db_type_id = member_database_type_id_.database_type_id (mi.m); - - // Handle NULL pointers and extract the id. - // - os << "{" - << "typedef object_traits< " << class_fq_name (*c) << - " > obj_traits;" - << "typedef pointer_traits< " << mi.fq_type () << - " > ptr_traits;" - << endl - << "if (i." << mi.var << "null)" << endl; - - if (null (mi.m, key_prefix_)) - os << member << " = ptr_traits::pointer_type ();"; - else - os << "throw null_pointer ();"; - - os << "else" - << "{" - << type << " id;"; - - member = "id"; - } - else - { - type = mi.fq_type (); - db_type_id = member_database_type_id_.database_type_id (mi.m); - } - - traits = "pgsql::value_traits<\n " - + type + ",\n " - + db_type_id + " >"; - } - - return true; - } - - virtual void - post (member_info& mi) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) { - if (composite (mi.t)) - return; - - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (object_pointer (mt)) - { - if (!member_override_.empty ()) - member = member_override_; - else - { - member = "o." + mi.m.name (); - - if (mi.cq) - member = "const_cast< " + mi.fq_type (false) + "& > (" + - member + ")"; - } - - if (lazy_pointer (mt)) - os << member << " = ptr_traits::pointer_type (db, id);"; - else - os << "// If a compiler error points to the line below, then" << endl - << "// it most likely means that a pointer used in a member" << endl - << "// cannot be initialized from an object pointer." << endl - << "//" << endl - << member << " = ptr_traits::pointer_type (" << endl - << "db.load< obj_traits::object_type > (id));"; - - os << "}" - << "}"; - } } virtual void - traverse_composite (member_info& mi) + get_null (member_info& mi) { - os << traits << "::init (" << endl - << member << "," << endl - << "i." << mi.var << "value," << endl - << "db);" - << endl; + os << "i." << mi.var << "null"; } virtual void @@ -970,14 +702,6 @@ namespace relational << "i." << mi.var << "null);" << endl; } - - private: - string type; - string db_type_id; - string traits; - string member; - - member_database_type_id member_database_type_id_; }; entry init_value_member_; @@ -1090,7 +814,7 @@ namespace relational << "{"; instance st (statement_select); - st->traverse_column (*id, "", true); + st->traverse (*id); os << "};"; } @@ -1111,16 +835,11 @@ namespace relational bool first (cc.total == cc.id + cc.inverse + cc.readonly + cc.optimistic_managed); - { - instance st (statement_where); - st->traverse_column (*id, "", first); - } + instance st (statement_where, first); + st->traverse (*id); if (optimistic != 0) - { - instance st (statement_where); - st->traverse_column (*optimistic, "", false); - } + st->traverse (*optimistic); os << "};"; } @@ -1134,7 +853,7 @@ namespace relational << "{"; instance st (statement_where); - st->traverse_column (*id, "", true); + st->traverse (*id); os << "};"; } @@ -1145,15 +864,9 @@ namespace relational << "optimistic_erase_statement_types[] =" << "{"; - { - instance st (statement_where); - st->traverse_column (*id, "", true); - } - - { - instance st (statement_where); - st->traverse_column (*optimistic, "", false); - } + instance st (statement_where); + st->traverse (*id); + st->traverse (*optimistic); os << "};"; } @@ -1258,8 +971,7 @@ namespace relational bool inv (inv_m != 0); semantics::type& vt (container_vt (t)); - - string id_oid (oids[column_sql_type (m, "id").type]); + semantics::type& idt (container_idt (m)); // select_all statement types. // @@ -1268,20 +980,22 @@ namespace relational << "select_all_types[] =" << "{"; + instance so (statement_where); + if (inv) { // many(i)-to-many // if (container (*inv_m)) - os << oids[column_sql_type (*inv_m, "value").type]; + so->traverse (*inv_m, idt, "value", "value"); // many(i)-to-one // else - os << oids[column_sql_type (*inv_m).type]; + so->traverse (*inv_m); } else - os << id_oid; + so->traverse (m, idt, "id", "object_id"); os << "};"; } @@ -1295,30 +1009,22 @@ namespace relational if (!inv) { - os << id_oid << ","; + instance so (statement_insert); + + so->traverse (m, idt, "id", "object_id"); switch (container_kind (t)) { case ck_ordered: { if (!unordered (m)) - os << oids[column_sql_type (m, "index").type] << ","; - + so->traverse (m, container_it (t), "index", "index"); break; } case ck_map: case ck_multimap: { - if (semantics::class_* ktc = - composite_wrapper (container_kt (t))) - { - instance st (statement_insert); - st->traverse (m, *ktc, "key", "key"); - os << ","; - } - else - os << oids[column_sql_type (m, "key").type] << ","; - + so->traverse (m, container_kt (t), "key", "key"); break; } case ck_set: @@ -1328,14 +1034,7 @@ namespace relational } } - if (semantics::class_* vtc = composite_wrapper (vt)) - { - instance st (statement_insert); - st->traverse (m, *vtc, "value", "value"); - } - else - os << oids[column_sql_type (m, "value").type]; - + so->traverse (m, vt, "value", "value"); } else // MSVC does not allow zero length arrays or uninitialized @@ -1354,7 +1053,10 @@ namespace relational << "{"; if (!inv) - os << id_oid; + { + instance so (statement_where); + so->traverse (m, idt, "id", "object_id"); + } else os << "0"; diff --git a/odb/relational/processor.cxx b/odb/relational/processor.cxx index 8c18f02..0bede14 100644 --- a/odb/relational/processor.cxx +++ b/odb/relational/processor.cxx @@ -142,7 +142,7 @@ namespace relational // const auto_ptr - can modify by changing the pointed-to value // if (const_type (m.type ()) && - !(m.count ("id") || m.count ("version") || m.count ("inverse"))) + !(id (m) || version (m) || m.count ("inverse"))) { if (qwt == 0 || const_type (*qwt)) m.set ("readonly", true); @@ -153,11 +153,19 @@ namespace relational if (composite_wrapper (t)) return; - string type, ref_type; + string type, id_type; + + if (m.count ("id-type")) + id_type = m.get ("id-type"); if (m.count ("type")) + { type = m.get ("type"); + if (id_type.empty ()) + id_type = type; + } + if (semantics::class_* c = process_object_pointer (m, t)) { // This is an object pointer. The column type is the pointed-to @@ -168,26 +176,55 @@ namespace relational semantics::names* idhint; semantics::type& idt (utype (id, idhint)); + semantics::type* wt (0); + semantics::names* whint (0); + if (process_wrapper (idt)) + { + whint = idt.get ("wrapper-hint"); + wt = &utype (*idt.get ("wrapper-type"), whint); + } + + // Nothing to do if this is a composite value type. + // + if (composite_wrapper (idt)) + return; + + if (type.empty () && id.count ("id-type")) + type = id.get ("id-type"); + if (type.empty () && id.count ("type")) type = id.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 () && wt != 0 && wt->count ("id-type")) + type = wt->get ("id-type"); + if (type.empty () && idt.count ("type")) type = idt.get ("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 { - if (type.empty () && m.count ("id") && t.count ("id-type")) - type = t.get ("id-type"); + if (id_type.empty () && t.count ("id-type")) + id_type = t.get ("id-type"); - if (type.empty () && wt != 0 && m.count ("id") && - wt->count ("id-type")) - type = wt->get ("id-type"); + if (id_type.empty () && wt != 0 && wt->count ("id-type")) + id_type = wt->get ("id-type"); if (type.empty () && t.count ("type")) type = t.get ("type"); @@ -195,16 +232,29 @@ namespace relational if (type.empty () && wt != 0 && wt->count ("type")) type = wt->get ("type"); + if (id_type.empty ()) + id_type = type; + + if (id_type.empty ()) + id_type = database_type (t, hint, true); + if (type.empty ()) - type = database_type (t, hint, m.count ("id")); + type = database_type (t, hint, false); + + if (id_type.empty () && wt != 0) + id_type = database_type (*wt, whint, true); if (type.empty () && wt != 0) - type = database_type (*wt, whint, m.count ("id")); + type = database_type (*wt, whint, false); + + if (id (m)) + type = id_type; } if (!type.empty ()) { m.set ("column-type", type); + m.set ("column-id-type", id_type); // Issue a warning if we are relaxing null-ness. // @@ -274,24 +324,53 @@ namespace relational if (obj_ptr && (c = process_object_pointer (m, t, prefix))) { // This is an object pointer. The column type is the pointed-to - // object id type. Except by default it can be NULL. + // object id type. // semantics::data_member& id (*id_member (*c)); - semantics::names* hint; - semantics::type& idt (utype (id, hint)); + semantics::names* idhint; + semantics::type& idt (utype (id, idhint)); + + semantics::type* wt (0); + semantics::names* whint (0); + if (process_wrapper (idt)) + { + whint = idt.get ("wrapper-hint"); + wt = &utype (*idt.get ("wrapper-type"), whint); + } + + // Nothing to do if this is a composite value type. + // + if (composite_wrapper (idt)) + return; + + if (type.empty () && id.count ("id-type")) + type = id.get ("id-type"); if (type.empty () && id.count ("type")) type = id.get ("type"); + // The rest of the code is identical to the else block except here + // we have to check for "id-type" before checking for "type". + // + if (type.empty () && idt.count ("id-type")) type = idt.get ("id-type"); + if (type.empty () && wt != 0 && wt->count ("id-type")) + type = wt->get ("id-type"); + if (type.empty () && idt.count ("type")) type = idt.get ("type"); + if (type.empty () && wt != 0 && wt->count ("type")) + type = wt->get ("type"); + if (type.empty ()) - type = database_type (idt, hint, true); + type = database_type (idt, idhint, true); + + if (type.empty () && wt != 0) + type = database_type (*wt, whint, true); } else { @@ -311,6 +390,7 @@ namespace relational if (!type.empty ()) { m.set (prefix + "-column-type", type); + m.set (prefix + "-column-id-type", type); return; } @@ -1094,7 +1174,7 @@ namespace relational TREE_VEC_ELT (args, 0) = arg; // This step should succeed regardles of whether there is a - // container traits specialization for this type. + // specialization for this type. // tree inst ( lookup_template_class (t, args, 0, 0, 0, tf_warning_or_error)); @@ -1939,30 +2019,27 @@ namespace relational } virtual void - traverse_simple (semantics::data_member& m) + traverse_pointer (semantics::data_member& m, semantics::class_& c) { - if (semantics::class_* c = object_pointer (utype (m))) - { - // 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. - // - if (inverse (m)) - return; + // 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. + // + if (inverse (m)) + return; - // Ignore self-pointers if requested. - // - if (!self_pointer_ && pointer_->obj == c) - return; + // Ignore self-pointers if requested. + // + if (!self_pointer_ && pointer_->obj == &c) + return; - if (pointee_.obj == c) - { - relationships_.push_back (relationship ()); - relationships_.back ().member = &m; - relationships_.back ().name = member_prefix_ + m.name (); - relationships_.back ().pointer = pointer_; - relationships_.back ().pointee = &pointee_; - } + if (pointee_.obj == &c) + { + relationships_.push_back (relationship ()); + relationships_.back ().member = &m; + relationships_.back ().name = member_prefix_ + m.name (); + relationships_.back ().pointer = pointer_; + relationships_.back ().pointee = &pointee_; } } diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx index 52ff093..7f94410 100644 --- a/odb/relational/source.hxx +++ b/odb/relational/source.hxx @@ -28,13 +28,15 @@ namespace relational { statement_column (): member (0) {} statement_column (std::string const& c, + std::string const& t, semantics::data_member& m, std::string const& kp = "") - : column (c), member (&m), key_prefix (kp) + : column (c), type (t), member (&m), key_prefix (kp) { } - std::string column; + std::string column; // Column name. + std::string type; // Column SQL type. semantics::data_member* member; std::string key_prefix; }; @@ -83,73 +85,108 @@ namespace relational { } - virtual bool - traverse_column (semantics::data_member& m, string const& name, bool) + virtual void + traverse_pointer (semantics::data_member& m, semantics::class_& c) { - semantics::data_member* im (inverse (m)); + semantics::data_member* im (inverse (m, key_prefix_)); // Ignore certain columns depending on what kind statement we are // generating. Columns corresponding to the inverse members are - // only present in the select statements while the id and readonly - // columns are not present in the update statements. + // only present in the select statements. // if (im != 0 && sk_ != statement_select) - return false; - - if ((id (m) || readonly (member_path_, member_scope_)) && - sk_ == statement_update) - return false; + return; // Inverse object pointers come from a joined table. // if (im != 0) { - semantics::class_* c (object_pointer (utype (m))); + semantics::data_member& id (*id_member (c)); + semantics::type& idt (utype (id)); if (container (*im)) { // This container is a direct member of the class so the table // prefix is just the class table name. We don't assign join // aliases for container tables so use the actual table name. - // Note that the (table_name_.empty () ? :) test may look wrong - // at first but it is no. + // Note that the if(!table_name_.empty ()) test may look wrong + // at first but it is not; if table_name_ is empty then we are + // generating a container table where we don't qualify columns + // with tables. // - column ( - *im, - "id", - table_name_.empty () - ? table_name_ - : table_qname (*im, - table_prefix (schema (c->scope ()), - table_name (*c) + "_", - 1)), - column_qname (*im, "id", "object_id")); + string table; + + if (!table_name_.empty ()) + { + table_prefix tp (schema (c.scope ()), table_name (c) + "_", 1); + table = table_qname (*im, tp); + } + + instance oc (table, sk_, sc_); + oc->traverse (*im, idt, "id", "object_id", &c); } else { - semantics::data_member& id (*id_member (*c)); - - // Use the join alias (column name) instead of the actual - // table name unless we are handling a container. Note that - // the (table_name_.empty () ? :) test may look wrong at - // first but it is no. + // Use the join alias instead of the actual table name unless we + // are handling a container. Generally, we want the join alias + // to be based on the column name. This is straightforward for + // single-column references. In case of a composite id, we will + // need to use the column prefix which is based on the data + // member name, unless overridden by the user. In the latter + // case the prefix can be empty, in which case we will just + // fall back on the member's public name. Note that the + // if(!table_name_.empty ()) test may look wrong at first but + // it is not; if table_name_ is empty then we are generating a + // container table where we don't qualify columns with tables. // - column ( - id, - "", - table_name_.empty () ? table_name_ : quote_id (name), - column_qname (id)); + string table; + + if (!table_name_.empty ()) + { + if (composite_wrapper (idt)) + { + string p (column_prefix (m, key_prefix_, default_name_)); + + if (p.empty ()) + p = public_name_db (m); + else + p.resize (p.size () - 1); // Remove trailing underscore. + + table = column_prefix_ + p; + } + else + table = column_prefix_ + + column_name (m, key_prefix_, default_name_); + + table = quote_id (table); + } + + instance oc (table, sk_, sc_); + oc->traverse (id); } } else - column (m, "", table_name_, quote_id (name)); + object_columns_base::traverse_pointer (m, c); + } + + virtual bool + traverse_column (semantics::data_member& m, string const& name, bool) + { + // Ignore certain columns depending on what kind statement we are + // generating. Id and readonly columns are not present in the update + // statements. + // + if ((id () || readonly (member_path_, member_scope_)) && + sk_ == statement_update) + return false; + + column (m, table_name_, quote_id (name)); return true; } virtual void column (semantics::data_member& m, - string const& key_prefix, string const& table, string const& column) { @@ -177,7 +214,7 @@ namespace relational r += param_->next (); } - sc_.push_back (statement_column (r, m, key_prefix)); + sc_.push_back (statement_column (r, column_type (), m, key_prefix_)); } protected: @@ -350,7 +387,7 @@ namespace relational virtual void column (semantics::data_member& m, string const& column) { - sc_.push_back (statement_column (column, m)); + sc_.push_back (statement_column (column, column_type (), m)); } protected: @@ -370,6 +407,7 @@ namespace relational table_ (table_qname (scope)), id_ (*id_member (scope)) { + id_cols_->traverse (id_); } size_t @@ -399,31 +437,63 @@ namespace relational } } - virtual bool - traverse_column (semantics::data_member& m, string const& column, bool) + virtual void + traverse_pointer (semantics::data_member& m, semantics::class_& c) { - semantics::class_* c (object_pointer (utype (m))); - - if (c == 0) - return false; - string t, a, dt, da; std::ostringstream cond, dcond; // @@ diversion? - if (semantics::data_member* im = inverse (m)) + // Derive table alias for this member. Generally, we want the + // alias to be based on the column name. This is straightforward + // for single-column references. In case of a composite id, we + // will need to use the column prefix which is based on the data + // member name, unless overridden by the user. In the latter + // case the prefix can be empty, in which case we will just + // fall back on the member's public name. + // + string alias; + + if (composite_wrapper (utype (*id_member (c)))) + { + string p (column_prefix (m, key_prefix_, default_name_)); + + if (p.empty ()) + p = public_name_db (m); + else + p.resize (p.size () - 1); // Remove trailing underscore. + + alias = column_prefix_ + p; + } + else + alias = column_prefix_ + + column_name (m, key_prefix_, default_name_); + + if (semantics::data_member* im = inverse (m, key_prefix_)) { if (container (*im)) { // This container is a direct member of the class so the table // prefix is just the class table name. // - qname const& ct (table_name (*c)); - table_prefix tp (schema (c->scope ()), ct + "_", 1); + qname const& ct (table_name (c)); + table_prefix tp (schema (c.scope ()), ct + "_", 1); t = table_qname (*im, tp); - string const& val (column_qname (*im, "value", "value")); - cond << t << '.' << val << " = " << - table_ << "." << column_qname (id_); + // Container's value is our id. + // + instance id_cols; + id_cols->traverse (*im, utype (id_), "value", "value"); + + for (object_columns_list::iterator b (id_cols->begin ()), i (b), + j (id_cols_->begin ()); i != id_cols->end (); ++i, ++j) + { + + if (i != b) + cond << " AND "; + + cond << t << '.' << quote_id (i->name) << '=' << + table_ << '.' << quote_id (j->name); + } // Add the join for the object itself so that we are able to // use it in the WHERE clause. @@ -431,21 +501,44 @@ namespace relational if (query_) { dt = quote_id (ct); - da = quote_id (column); + da = quote_id (alias); + + semantics::data_member& id (*id_member (c)); + + instance oid_cols, cid_cols; + oid_cols->traverse (id); + cid_cols->traverse (*im, utype (id), "id", "object_id", &c); + + for (object_columns_list::iterator b (cid_cols->begin ()), i (b), + j (oid_cols->begin ()); i != cid_cols->end (); ++i, ++j) + { - string const& id (column_qname (*im, "id", "object_id")); + if (i != b) + dcond << " AND "; - dcond << da << '.' << column_qname (*id_member (*c)) << " = " << - t << '.' << id; + dcond << da << '.' << quote_id (j->name) << '=' << + t << '.' << quote_id (i->name); + } } } else { - t = table_qname (*c); - a = quote_id (column); + t = table_qname (c); + a = quote_id (alias); + + instance id_cols; + id_cols->traverse (*im); + + for (object_columns_list::iterator b (id_cols->begin ()), i (b), + j (id_cols_->begin ()); i != id_cols->end (); ++i, ++j) + { + + if (i != b) + cond << " AND "; - cond << a << '.' << column_qname (*im) << " = " << - table_ << "." << column_qname (id_); + cond << a << '.' << quote_id (i->name) << '=' << + table_ << '.' << quote_id (j->name); + } } } else if (query_) @@ -453,11 +546,25 @@ namespace relational // We need the join to be able to use the referenced object // in the WHERE clause. // - t = table_qname (*c); - a = quote_id (column); + t = table_qname (c); + a = quote_id (alias); - cond << a << '.' << column_qname (*id_member (*c)) << " = " << - table_ << "." << quote_id (column); + instance oid_cols (column_prefix_); + oid_cols->traverse (m); + + instance pid_cols; + pid_cols->traverse (*id_member (c)); + + for (object_columns_list::iterator b (pid_cols->begin ()), i (b), + j (oid_cols->begin ()); i != pid_cols->end (); ++i, ++j) + { + + if (i != b) + cond << " AND "; + + cond << a << '.' << quote_id (i->name) << '=' << + table_ << '.' << quote_id (j->name); + } } if (!t.empty ()) @@ -478,14 +585,13 @@ namespace relational joins_.back ().alias = da; joins_.back ().cond = dcond.str (); } - - return true; } private: bool query_; string table_; semantics::data_member& id_; + instance id_cols_; struct join { @@ -690,6 +796,207 @@ namespace relational string member_override_; }; + template + struct init_image_member_impl: init_image_member, + virtual member_base_impl + { + typedef init_image_member_impl base_impl; + + init_image_member_impl (base const& x) + : base (x), + member_database_type_id_ (base::type_override_, + base::fq_type_override_, + base::key_prefix_) + { + } + + typedef typename member_base_impl::member_info member_info; + + virtual void + set_null (member_info&) = 0; + + virtual bool + pre (member_info& mi) + { + // Ignore containers (they get their own table) and inverse + // object pointers (they are not present in this binding). + // + if (container (mi) || inverse (mi.m, key_prefix_)) + return false; + + if (!member_override_.empty ()) + member = member_override_; + else + { + // If we are generating standard init() and this member + // contains version, ignore it. + // + if (version (mi.m)) + return false; + + // If we don't send auto id in INSERT statement, ignore this + // member altogether (we never send auto id in UPDATE). + // + if (!insert_send_auto_id && id (mi.m) && auto_ (mi.m)) + return false; + + string const& name (mi.m.name ()); + member = "o." + name; + + os << "// " << name << endl + << "//" << endl; + + // If the whole class is readonly, then we will never be + // called with sk == statement_update. + // + if (!readonly (*context::top_object)) + { + semantics::class_* c; + + if (id (mi.m) || + readonly (mi.m) || + ((c = composite (mi.t)) && readonly (*c))) // Can't be id. + os << "if (sk == statement_insert)"; + } + } + + bool comp (composite (mi.t)); + + // If this is a wrapped composite value, then we need to "unwrap" + // it. For simple values this is taken care of by the value_traits + // specializations. + // + if (mi.wrapper != 0 && comp) + { + // Here we need the wrapper type, not the wrapped type. + // + member = "wrapper_traits< " + mi.fq_type (false) + " >::" + + "get_ref (" + member + ")"; + } + + if (mi.ptr != 0) + { + // When handling a pointer, mi.t is the id type of the referenced + // object. + // + semantics::type& pt (member_utype (mi.m, key_prefix_)); + + type = "obj_traits::id_type"; + + // Handle NULL pointers and extract the id. + // + os << "{" + << "typedef object_traits< " << class_fq_name (*mi.ptr) << + " > obj_traits;"; + + if (weak_pointer (pt)) + { + os << "typedef pointer_traits< " << mi.ptr_fq_type () << + " > wptr_traits;" + << "typedef pointer_traits< wptr_traits::" << + "strong_pointer_type > ptr_traits;" + << endl + << "wptr_traits::strong_pointer_type sp (" << + "wptr_traits::lock (" << member << "));"; + + member = "sp"; + } + else + os << "typedef pointer_traits< " << mi.ptr_fq_type () << + " > ptr_traits;" + << endl; + + os << "bool is_null (ptr_traits::null_ptr (" << member << "));" + << "if (!is_null)" + << "{" + << "const " << type << "& id (" << endl; + + if (lazy_pointer (pt)) + os << "ptr_traits::object_id< ptr_traits::element_type > (" << + member << ")"; + else + os << "obj_traits::id (ptr_traits::get_ref (" << member << "))"; + + os << ");" + << endl; + + member = "id"; + } + else if (comp) + { + type = mi.fq_type (); + + os << "{"; + } + else + { + type = mi.fq_type (); + + os << "{" + << "bool is_null;"; + } + + if (comp) + traits = "composite_value_traits< " + type + " >"; + else + { + db_type_id = member_database_type_id_->database_type_id (mi.m); + traits = string (db.string ()) + "::value_traits<\n " + + type + ",\n " + + db_type_id + " >"; + } + + return true; + } + + virtual void + post (member_info& mi) + { + if (mi.ptr != 0) + { + os << "}" + << "else" << endl; + + // @@ Composite value currently cannot be NULL. + // + if (!null (mi.m, key_prefix_) || composite (mi.t)) + os << "throw null_pointer ();"; + else + set_null (mi); + } + + os << "}"; + } + + virtual void + traverse_composite (member_info& mi) + { + bool grow (generate_grow && context::grow (mi.m, mi.t, key_prefix_)); + + if (grow) + os << "if ("; + + os << traits << "::init (" << endl + << "i." << mi.var << "value," << endl + << member << "," << endl + << "sk)"; + + if (grow) + os << ")" << endl + << "grew = true"; + + os << ";"; + } + + protected: + string type; + string db_type_id; + string member; + string traits; + + instance member_database_type_id_; + }; + struct init_image_base: traversal::class_, virtual context { typedef init_image_base base; @@ -755,6 +1062,171 @@ namespace relational string member_override_; }; + template + struct init_value_member_impl: init_value_member, + virtual member_base_impl + { + typedef init_value_member_impl base_impl; + + init_value_member_impl (base const& x) + : base (x), + member_database_type_id_ (base::type_override_, + base::fq_type_override_, + base::key_prefix_) + { + } + + typedef typename member_base_impl::member_info member_info; + + virtual void + get_null (member_info&) = 0; + + virtual bool + pre (member_info& mi) + { + if (container (mi)) + return false; + + if (!member_override_.empty ()) + member = member_override_; + else + { + string const& name (mi.m.name ()); + member = "o." + name; + + if (mi.cq) + { + string t (mi.ptr == 0 ? mi.fq_type (false) : mi.ptr_fq_type ()); + member = "const_cast< " + t + "& > (" + member + ")"; + } + + os << "// " << name << endl + << "//" << endl; + } + + bool comp (composite (mi.t)); + + // If this is a wrapped composite value, then we need to + // "unwrap" it. For simple values this is taken care of + // by the value_traits specializations. + // + if (mi.wrapper != 0 && comp) + { + // Here we need the wrapper type, not the wrapped type. + // + member = "wrapper_traits< " + mi.fq_type (false) + " >::" + + "set_ref (\n" + member + ")"; + } + + if (mi.ptr != 0) + { + type = "obj_traits::id_type"; + + // Handle NULL pointers and extract the id. + // + os << "{" + << "typedef object_traits< " << class_fq_name (*mi.ptr) << + " > obj_traits;" + << "typedef pointer_traits< " << mi.ptr_fq_type () << + " > ptr_traits;" + << endl; + + // @@ Composite value currently cannot be NULL. + // + if (!comp) + { + os << "if ("; + get_null (mi); + os << ")" << endl; + + if (!null (mi.m, key_prefix_) ) + os << "throw null_pointer ();"; + else + os << member << " = ptr_traits::pointer_type ();"; + + os << "else" + << "{"; + } + + os << type << " id;"; + + member = "id"; + } + else + type = mi.fq_type (); + + if (comp) + traits = "composite_value_traits< " + type + " >"; + else + { + db_type_id = member_database_type_id_->database_type_id (mi.m); + traits = string (db.string ()) + "::value_traits<\n " + + type + ",\n " + + db_type_id + " >"; + } + + return true; + } + + virtual void + post (member_info& mi) + { + if (mi.ptr != 0) + { + if (!member_override_.empty ()) + member = member_override_; + else + { + member = "o." + mi.m.name (); + + if (mi.cq) + member = "const_cast< " + mi.ptr_fq_type () + + "& > (" + member + ")"; + } + + // When handling a pointer, mi.t is the id type of the referenced + // object. + // + semantics::type& pt (member_utype (mi.m, key_prefix_)); + + if (lazy_pointer (pt)) + os << member << " = ptr_traits::pointer_type (*db, id);"; + else + os << "// If a compiler error points to the line below, then" << endl + << "// it most likely means that a pointer used in a member" << endl + << "// cannot be initialized from an object pointer." << endl + << "//" << endl + << member << " = ptr_traits::pointer_type (" << endl + << "db->load< obj_traits::object_type > (id));"; + + // @@ Composite value currently cannot be NULL. + // + if (!composite (mi.t)) + os << "}"; + + os << "}"; + } + } + + virtual void + traverse_composite (member_info& mi) + { + os << traits << "::init (" << endl + << member << "," << endl + << "i." << mi.var << "value," << endl + << "db);" + << endl; + } + + protected: + string type; + string db_type_id; + string traits; + string member; + + instance member_database_type_id_; + }; + struct init_value_base: traversal::class_, virtual context { typedef init_value_base base; @@ -930,7 +1402,10 @@ namespace relational // if (!abst) { + semantics::type& idt (container_idt (m)); + string table (table_qname (m, table_prefix_)); + instance id_cols; // select_all_statement // @@ -940,10 +1415,12 @@ namespace relational if (inverse) { semantics::class_* c (object_pointer (vt)); + semantics::data_member& inv_id (*id_member (*c)); - string inv_table; // Other table name. - string inv_id; // Other id column. - string inv_fid; // Other foreign id column (ref to us). + string inv_table; // Other table name. + instance inv_id_cols; // Other id column. + instance inv_fid_cols; // Other foreign id + // column (ref to us). statement_columns sc; if (container (*im)) @@ -956,23 +1433,42 @@ namespace relational // table_prefix tp (schema (c->scope ()), table_name (*c) + "_", 1); inv_table = table_qname (*im, tp); - inv_id = column_qname (*im, "id", "object_id"); - inv_fid = column_qname (*im, "value", "value"); - sc.push_back (statement_column ( - inv_table + "." + inv_id, *im, "id")); + inv_id_cols->traverse (*im, utype (inv_id), "id", "object_id", c); + inv_fid_cols->traverse (*im, idt, "value", "value"); + + for (object_columns_list::iterator i (inv_id_cols->begin ()); + i != inv_id_cols->end (); ++i) + { + // If this is a simple id, then pass the "id" key prefix. If + // it is a composite id, then the members have no prefix. + // + sc.push_back ( + statement_column ( + inv_table + "." + quote_id (i->name), + i->type, + *i->member, + inv_id_cols->size () == 1 ? "id" : "")); + } } else { // many(i)-to-one // - semantics::data_member& id (*id_member (*c)); - inv_table = table_qname (*c); - inv_id = column_qname (id); - inv_fid = column_qname (*im); - sc.push_back (statement_column (inv_table + "." + inv_id, id)); + inv_id_cols->traverse (inv_id); + inv_fid_cols->traverse (*im); + + for (object_columns_list::iterator i (inv_id_cols->begin ()); + i != inv_id_cols->end (); ++i) + { + sc.push_back ( + statement_column ( + inv_table + "." + quote_id (i->name), + i->type, + *i->member)); + } } process_statement_columns (sc, statement_select); @@ -987,12 +1483,20 @@ namespace relational } instance qp; - os << strlit (" FROM " + inv_table + - " WHERE " + inv_table + "." + inv_fid + "=" + - qp->next ()); + os << strlit (" FROM " + inv_table); + + for (object_columns_list::iterator b (inv_fid_cols->begin ()), + i (b); i != inv_fid_cols->end (); ++i) + { + os << endl + << strlit ((i == b ? " WHERE " : " AND ") + inv_table + "." + + quote_id (i->name) + "=" + qp->next ()); + } } else { + id_cols->traverse (m, idt, "id", "object_id"); + statement_columns sc; statement_kind sk (statement_select); // Imperfect forwarding. instance t (table, sk, sc); @@ -1002,22 +1506,13 @@ namespace relational case ck_ordered: { if (ordered) - { - string const& col (column_qname (m, "index", "index")); - t->column (m, "index", table, col); - } + t->traverse (m, *it, "index", "index"); break; } case ck_map: case ck_multimap: { - if (semantics::class_* ckt = composite_wrapper (*kt)) - t->traverse (m, *ckt, "key", "key"); - else - { - string const& col (column_qname (m, "key", "key")); - t->column (m, "key", table, col); - } + t->traverse (m, *kt, "key", "key"); break; } case ck_set: @@ -1027,13 +1522,7 @@ namespace relational } } - if (semantics::class_* cvt = composite_wrapper (vt)) - t->traverse (m, *cvt, "value", "value"); - else - { - string const& col (column_qname (m, "value", "value")); - t->column (m, "value", table, col); - } + t->traverse (m, vt, "value", "value"); process_statement_columns (sc, statement_select); @@ -1047,18 +1536,22 @@ namespace relational } instance qp; - string const& id_col (column_qname (m, "id", "object_id")); + os << strlit (" FROM " + table); - os << strlit (" FROM " + table + - " WHERE " + table + "." + id_col + "=" + - qp->next ()); + for (object_columns_list::iterator b (id_cols->begin ()), i (b); + i != id_cols->end (); ++i) + { + os << endl + << strlit ((i == b ? " WHERE " : " AND ") + table + "." + + quote_id (i->name) + "=" + qp->next ()); + } if (ordered) { string const& col (column_qname (m, "index", "index")); os << endl - << strlit (" ORDER BY " + table + "." + col) << endl; + << strlit (" ORDER BY " + table + "." + col); } } @@ -1076,29 +1569,23 @@ namespace relational else { statement_columns sc; - sc.push_back ( - statement_column ( - column_qname (m, "id", "object_id"), m, "id")); - statement_kind sk (statement_insert); // Imperfect forwarding. instance t (sk, sc); + t->traverse (m, idt, "id", "object_id"); + switch (ck) { case ck_ordered: { if (ordered) - t->column ( - m, "index", "", column_qname (m, "index", "index")); + t->traverse (m, *it, "index", "index"); break; } case ck_map: case ck_multimap: { - if (semantics::class_* ckt = composite_wrapper (*kt)) - t->traverse (m, *ckt, "key", "key"); - else - t->column (m, "key", "", column_qname (m, "key", "key")); + t->traverse (m, *kt, "key", "key"); break; } case ck_set: @@ -1108,10 +1595,7 @@ namespace relational } } - if (semantics::class_* cvt = composite_wrapper (vt)) - t->traverse (m, *cvt, "value", "value"); - else - t->column (m, "value", "", column_qname (m, "value", "value")); + t->traverse (m, vt, "value", "value"); process_statement_columns (sc, statement_insert); @@ -1151,9 +1635,17 @@ namespace relational { instance qp; - os << strlit ("DELETE FROM " + table) << endl - << strlit (" WHERE " + column_qname (m, "id", "object_id") + - "=" + qp->next ()) << ";" + os << strlit ("DELETE FROM " + table); + + for (object_columns_list::iterator b (id_cols->begin ()), i (b); + i != id_cols->end (); ++i) + { + os << endl + << strlit ((i == b ? " WHERE " : " AND ") + + quote_id (i->name) + "=" + qp->next ()); + } + + os << ";" << endl; } } @@ -1462,10 +1954,10 @@ namespace relational { if (ordered) os << "init (index_type& j, value_type& v, " << - "const data_image_type& i, database& db)"; + "const data_image_type& i, database* db)"; else os << "init (value_type& v, const data_image_type& i, " << - "database& db)"; + "database* db)"; os << "{" << "ODB_POTENTIALLY_UNUSED (db);" @@ -1487,7 +1979,7 @@ namespace relational case ck_multimap: { os << "init (key_type& k, value_type& v, " << - "const data_image_type& i, database& db)" + "const data_image_type& i, database* db)" << "{" << "ODB_POTENTIALLY_UNUSED (db);" << endl @@ -1504,7 +1996,7 @@ namespace relational case ck_multiset: { os << "init (value_type& v, const data_image_type& i, " << - "database& db)" + "database* db)" << "{" << "ODB_POTENTIALLY_UNUSED (db);" << endl; @@ -1652,21 +2144,21 @@ namespace relational case ck_ordered: { os << "init (" << (ordered ? "i, " : "") << - "v, di, sts.connection ().database ());" + "v, di, &sts.connection ().database ());" << endl; break; } case ck_map: case ck_multimap: { - os << "init (k, v, di, sts.connection ().database ());" + os << "init (k, v, di, &sts.connection ().database ());" << endl; break; } case ck_set: case ck_multiset: { - os << "init (v, di, sts.connection ().database ());" + os << "init (v, di, &sts.connection ().database ());" << endl; break; } @@ -2130,26 +2622,30 @@ namespace relational } virtual void - traverse_simple (semantics::data_member& m) + traverse_pointer (semantics::data_member& m, semantics::class_& c) { if (!inverse (m)) - { - string p; + object_members_base::traverse_pointer (m, c); + } - if (version (m)) - p = "1"; - else if (id (m) && auto_ (m)) - p = qp_.auto_id (); - else - p = qp_.next (); + virtual void + traverse_simple (semantics::data_member& m) + { + string p; - if (!p.empty ()) - { - if (count_++ != 0) - params_ += ','; + if (version (m)) + p = "1"; + else if (context::id (m) && auto_ (m)) // Only simple id can be auto. + p = qp_.auto_id (); + else + p = qp_.next (); - params_ += p; - } + if (!p.empty ()) + { + if (count_++ != 0) + params_ += ','; + + params_ += p; } } @@ -2434,6 +2930,9 @@ namespace relational << traits << "::" << endl << "id (const image_type& i)" << "{" + << db << "::database* db (0);" + << "ODB_POTENTIALLY_UNUSED (db);" + << endl << "id_type id;"; init_id_value_member_->traverse (*id); os << "return id;" @@ -2505,11 +3004,14 @@ namespace relational << "{" << "std::size_t n (0);"; + if (composite_wrapper (utype (*id))) + os << db << "::statement_kind sk (" << db << "::statement_select);"; + bind_id_member_->traverse (*id); if (optimistic != 0) { - os << "n++;" //@@ composite id + os << "n += " << column_count (c).id << ";" << endl; bind_version_member_->traverse (*optimistic); @@ -2549,7 +3051,7 @@ namespace relational // init (object, image) // os << "void " << traits << "::" << endl - << "init (object_type& o, const image_type& i, database& db)" + << "init (object_type& o, const image_type& i, database* db)" << "{" << "ODB_POTENTIALLY_UNUSED (o);" << "ODB_POTENTIALLY_UNUSED (i);" @@ -2571,8 +3073,10 @@ namespace relational << "{"; if (grow_id) - os << "bool grew (false);" - << endl; + os << "bool grew (false);"; + + if (composite_wrapper (utype (*id))) + os << db << "::statement_kind sk (" << db << "::statement_select);"; init_id_image_member_->traverse (*id); @@ -2677,7 +3181,8 @@ namespace relational if (id != 0) { - string const& id_col (column_qname (*id)); + instance id_cols; + id_cols->traverse (*id); // find_statement // @@ -2702,14 +3207,23 @@ namespace relational os << strlit (" FROM " + table) << endl; - bool f (false); + bool f (false); // @@ (im)perfect forwarding instance j (c, f); // @@ (im)perfect forwarding j->traverse (c); j->write (); instance qp; - os << strlit (" WHERE " + table + "." + id_col + "=" + - qp->next ()) << ";" + for (object_columns_list::iterator b (id_cols->begin ()), i (b); + i != id_cols->end (); ++i) + { + if (i != b) + os << endl; + + os << strlit ((i == b ? " WHERE " : " AND ") + table + "." + + quote_id (i->name) + "=" + qp->next ()); + } + + os << ";" << endl; } @@ -2739,13 +3253,22 @@ namespace relational os << strlit (c + (++i != e ? "," : "")) << endl; } - string where (" WHERE " + id_col + "=" + qp->next ()); + for (object_columns_list::iterator b (id_cols->begin ()), i (b); + i != id_cols->end (); ++i) + { + if (i != b) + os << endl; + + os << strlit ((i == b ? " WHERE " : " AND ") + + quote_id (i->name) + "=" + qp->next ()); + } if (optimistic != 0) - where += " AND " + column_qname (*optimistic) + "=" + - qp->next (); + os << endl + << strlit (" AND " + column_qname (*optimistic) + + "=" + qp->next ()); - os << strlit (where) << ";" + os << ";" << endl; } @@ -2754,8 +3277,18 @@ namespace relational { instance qp; os << "const char " << traits << "::erase_statement[] =" << endl - << strlit ("DELETE FROM " + table) << endl - << strlit (" WHERE " + id_col + "=" + qp->next ()) << ";" + << strlit ("DELETE FROM " + table); + + for (object_columns_list::iterator b (id_cols->begin ()), i (b); + i != id_cols->end (); ++i) + { + + os << endl + << strlit ((i == b ? " WHERE " : " AND ") + + quote_id (i->name) + "=" + qp->next ()); + } + + os << ";" << endl; } @@ -2763,13 +3296,21 @@ namespace relational { instance qp; - string where (" WHERE " + id_col + "=" + qp->next ()); - where += " AND " + column_qname (*optimistic) + "=" + qp->next (); - os << "const char " << traits << "::optimistic_erase_statement[] =" << endl - << strlit ("DELETE FROM " + table) << endl - << strlit (where) << ";" + << strlit ("DELETE FROM " + table); + + for (object_columns_list::iterator b (id_cols->begin ()), i (b); + i != id_cols->end (); ++i) + { + os << endl + << strlit ((i == b ? " WHERE " : " AND ") + + quote_id (i->name) + "=" + qp->next ()); + } + + os << endl + << strlit (" AND " + column_qname (*optimistic) + + "=" + qp->next ()) << ";" << endl; } } @@ -2786,10 +3327,6 @@ namespace relational process_statement_columns (sc, statement_select); } - bool t (true); - instance oj (c, t); //@@ (im)perfect forwarding - oj->traverse (c); - os << "const char " << traits << "::query_statement[] =" << endl << strlit ("SELECT ") << endl; @@ -2801,7 +3338,15 @@ namespace relational } os << strlit (" FROM " + table) << endl; - oj->write (); + + if (id != 0) + { + bool t (true); //@@ (im)perfect forwarding + instance oj (c, t); //@@ (im)perfect forwarding + oj->traverse (c); + oj->write (); + } + os << strlit (" ") << ";" << endl; @@ -3202,7 +3747,7 @@ namespace relational << "if (l.locked ())" << "{" << "callback (db, obj, callback_event::pre_load);" - << "init (obj, sts.image (), db);"; + << "init (obj, sts.image (), &db);"; init_value_extra (); free_statement_result_delayed (); @@ -3248,7 +3793,7 @@ namespace relational << "reference_cache_traits< object_type >::insert (db, id, obj));" << endl << "callback (db, obj, callback_event::pre_load);" - << "init (obj, sts.image (), db);"; + << "init (obj, sts.image (), &db);"; init_value_extra (); free_statement_result_delayed (); @@ -3298,7 +3843,7 @@ namespace relational } os << "callback (db, obj, callback_event::pre_load);" - << "init (obj, sts.image (), db);"; + << "init (obj, sts.image (), &db);"; init_value_extra (); free_statement_result_delayed (); @@ -3766,7 +4311,7 @@ namespace relational // init (view, image) // os << "void " << traits << "::" << endl - << "init (view_type& o, const image_type& i, database& db)" + << "init (view_type& o, const image_type& i, database* db)" << "{" << "ODB_POTENTIALLY_UNUSED (o);" << "ODB_POTENTIALLY_UNUSED (i);" @@ -4080,7 +4625,7 @@ namespace relational if (im != 0) { // For now a direct member can only be directly in - // the object scope. When this changes, the inverse() + // the object scope. If this changes, the inverse() // function would have to return a member path instead // of just a single member. // @@ -4102,34 +4647,36 @@ namespace relational // 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. + // JOIN order. There seems to be a pattern there but it + // is not yet intuitively clear what it means. // + instance c_cols; // Container columns. + instance o_cols; // Object columns. + + qname* ot; // Object table (either lt or rt). + if (im != 0) { if (i->obj == c) { // container.value = pointer.id // - l = ct; - l += '.'; - l += column_qname (*im, "value", "value"); - l += "="; - l += quote_id (lt); - l += '.'; - l += column_qname (*id_member (*e.vo->obj)); + 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 // - l = ct; - l += '.'; - l += column_qname (*im, "id", "object_id"); - l += "="; - l += quote_id (rt); - l += '.'; - l += column_qname (*id_member (*vo->obj)); + 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 @@ -4138,29 +4685,43 @@ namespace relational { // container.id = pointer.id // - l = ct; - l += '.'; - l += column_qname (m, "id", "object_id"); - l += "="; - l += quote_id (lt); - l += '.'; - l += column_qname (*id_member (*e.vo->obj)); + 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 // - l = ct; - l += '.'; - l += column_qname (m, "value", "value"); - l += "="; - l += quote_id (rt); - l += '.'; - l += column_qname (*id_member (*vo->obj)); + semantics::data_member& id (*id_member (*vo->obj)); + + c_cols->traverse (m, utype (id), "value", "value"); + o_cols->traverse (id); + ot = &rt; } } - os << "r += " << strlit (l) << ";"; + for (object_columns_list::iterator b (c_cols->begin ()), i (b), + j (o_cols->begin ()); i != c_cols->end (); ++i, ++j) + { + l.clear (); + + if (i != b) + l += "AND "; + + l += ct; + l += '.'; + l += quote_id (i->name); + l += '='; + l += quote_id (*ot); + l += '.'; + l += quote_id (j->name); + + os << "r += " << strlit (l) << ";"; + } } l = "LEFT JOIN "; @@ -4174,31 +4735,33 @@ namespace relational if (cont != 0) { + instance c_cols; // Container columns. + instance o_cols; // Object columns. + + qname* ot; // Object table (either lt or rt). + if (im != 0) { if (i->obj == c) { // container.id = pointed-to.id // - l = ct; - l += '.'; - l += column_qname (*im, "id", "object_id"); - l += "="; - l += quote_id (rt); - l += '.'; - l += column_qname (*id_member (*vo->obj)); + 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 // - l = ct; - l += '.'; - l += column_qname (*im, "value", "value"); - l += "="; - l += quote_id (lt); - l += '.'; - l += column_qname (*id_member (*e.vo->obj)); + semantics::data_member& id (*id_member (*e.vo->obj)); + + c_cols->traverse (*im, utype (id), "value", "value"); + o_cols->traverse (id); + ot = < } } else @@ -4207,58 +4770,91 @@ namespace relational { // container.value = pointed-to.id // - l = ct; - l += '.'; - l += column_qname (m, "value", "value"); - l += "="; - l += quote_id (rt); - l += '.'; - l += column_qname (*id_member (*vo->obj)); + 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 // - l = ct; - l += '.'; - l += column_qname (m, "id", "object_id"); - l += "="; - l += quote_id (lt); - l += '.'; - l += column_qname (*id_member (*e.vo->obj)); + semantics::data_member& id (*id_member (*e.vo->obj)); + + c_cols->traverse ( + m, utype (id), "id", "object_id", e.vo->obj); + o_cols->traverse (id); + ot = < } } + + for (object_columns_list::iterator b (c_cols->begin ()), i (b), + j (o_cols->begin ()); i != c_cols->end (); ++i, ++j) + { + l.clear (); + + if (i != b) + l += "AND "; + + l += ct; + l += '.'; + l += quote_id (i->name); + l += '='; + l += quote_id (*ot); + l += '.'; + l += quote_id (j->name); + + os << "r += " << strlit (l) << ";"; + } } else { + string col_prefix; + + if (im == 0) + col_prefix = + object_columns_base::column_prefix (e.member_path); + + instance l_cols (col_prefix); + instance r_cols; + if (im != 0) { // our.id = pointed-to.pointer // - l = quote_id (lt); - l += '.'; - l += column_qname (*id_member (*e.vo->obj)); - l += " = "; - l += quote_id (rt); - l += '.'; - l += column_qname (*im); + l_cols->traverse (*id_member (*e.vo->obj)); + r_cols->traverse (*im); } else { // our.pointer = pointed-to.id // - l = quote_id (lt); + 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 += column_qname (e.member_path); - l += " = "; + l += quote_id (i->name); + l += '='; l += quote_id (rt); l += '.'; - l += column_qname (*id_member (*vo->obj)); + l += quote_id (j->name); + + os << "r += " << strlit (l) << ";"; } } - os << "r += " << strlit (l) << ";" - << endl; + os << endl; } // Generate the query condition. @@ -4499,7 +5095,7 @@ namespace relational // init (value, image) // os << "void " << traits << "::" << endl - << "init (value_type& o, const image_type& i, database& db)" + << "init (value_type& o, const image_type& i, database* db)" << "{" << "ODB_POTENTIALLY_UNUSED (o);" << "ODB_POTENTIALLY_UNUSED (i);" @@ -4588,8 +5184,12 @@ namespace relational << "#include " << endl << "#include " << endl << "#include " << endl - << "#include " << endl - << "#include " << endl + << "#include " << endl; + + if (options.generate_query ()) + os << "#include " << endl; + + os << "#include " << endl << "#include " << endl; if (options.generate_query ()) diff --git a/odb/relational/sqlite/common.cxx b/odb/relational/sqlite/common.cxx index 480b69a..7164ef3 100644 --- a/odb/relational/sqlite/common.cxx +++ b/odb/relational/sqlite/common.cxx @@ -16,91 +16,10 @@ namespace relational // member_base // - void member_base:: - traverse (semantics::data_member& m) + sql_type const& member_base:: + member_sql_type (semantics::data_member& m) { - if (transient (m)) - return; - - string var; - - if (!var_override_.empty ()) - var = var_override_; - else - { - string const& name (m.name ()); - var = name + (name[name.size () - 1] == '_' ? "" : "_"); - } - - bool cq (type_override_ != 0 ? false : const_type (m.type ())); - semantics::type& t (type_override_ != 0 ? *type_override_ : utype (m)); - - semantics::type* cont; - if (semantics::class_* c = composite_wrapper (t)) - { - // If t is a wrapper, pass the wrapped type. Also pass the - // original, wrapper type. - // - member_info mi (m, - *c, - (wrapper (t) ? &t : 0), - cq, - var, - fq_type_override_); - if (pre (mi)) - { - traverse_composite (mi); - post (mi); - } - } - // This cannot be a container if we have a type override. - // - else if (type_override_ == 0 && (cont = context::container (m))) - { - // The same unwrapping logic as for composite values. - // - member_info mi (m, - *cont, - (wrapper (t) ? &t : 0), - cq, - var, - fq_type_override_); - if (pre (mi)) - { - traverse_container (mi); - post (mi); - } - } - else - { - sql_type const& st (column_sql_type (m, key_prefix_)); - - if (semantics::class_* c = object_pointer (t)) - { - member_info mi (m, - utype (*id_member (*c)), - 0, - cq, - var, - fq_type_override_); - mi.st = &st; - if (pre (mi)) - { - traverse_object_pointer (mi); - post (mi); - } - } - else - { - member_info mi (m, t, 0, cq, var, fq_type_override_); - mi.st = &st; - if (pre (mi)) - { - traverse_simple (mi); - post (mi); - } - } - } + return parse_sql_type (column_type (m, key_prefix_), m); } void member_base:: @@ -185,10 +104,18 @@ namespace relational // member_database_type_id:: + member_database_type_id (base const& x) + : member_base::base (x), // virtual base + base (x) + { + } + + member_database_type_id:: member_database_type_id (semantics::type* type, - string const& fq_type, - string const& key_prefix) - : relational::member_base (type, fq_type, key_prefix) + string const& fq_type, + string const& key_prefix) + : member_base::base (type, fq_type, key_prefix), // virtual base + base (type, fq_type, key_prefix) { } @@ -230,6 +157,8 @@ namespace relational type_id_ = "sqlite::id_blob"; } + entry member_database_type_id_; + // // query_columns // diff --git a/odb/relational/sqlite/common.hxx b/odb/relational/sqlite/common.hxx index 7885a04..941e200 100644 --- a/odb/relational/sqlite/common.hxx +++ b/odb/relational/sqlite/common.hxx @@ -12,118 +12,18 @@ namespace relational { namespace sqlite { - struct member_base: virtual relational::member_base, context + struct member_base: virtual relational::member_base_impl, context { - member_base (base const& x): base (x) {} + member_base (base const& x): base (x), base_impl (x) {} // This c-tor is for the direct use inside the sqlite namespace. // If you do use this c-tor, you should also explicitly call - // relational::member_base. + // relational::member_base (aka base). // member_base () {} - virtual void - traverse (semantics::data_member& m); - - struct member_info - { - semantics::data_member& m; // Member. - semantics::type& t; // Cvr-unqualified member C++ type, note - // that m.type () may not be the same as t. - semantics::type* wrapper; // Wrapper type if member is a composite or - // container wrapper, also cvr-unqualified. - // In this case t is the wrapped type. - bool cq; // True if the original (wrapper) type - // is const-qualified. - sql_type const* st; // Member SQL type (only simple values). - string& var; // Member variable name with trailing '_'. - - // C++ type fq-name. - // - string - fq_type (bool unwrap = true) const - { - semantics::names* hint; - - if (wrapper != 0 && unwrap) - { - // Use the hint from the wrapper unless the wrapped type - // is qualified. - // - hint = wrapper->get ("wrapper-hint"); - utype (*context::wrapper (*wrapper), hint); - return t.fq_name (hint); - } - - // Use the original type from 'm' instead of 't' since the hint - // may be invalid for a different type. Plus, if a type is - // overriden, then the fq_type must be as well. - // - if (fq_type_.empty ()) - { - semantics::type& t (utype (m, hint)); - return t.fq_name (hint); - } - else - return fq_type_; - } - - string const& fq_type_; - - member_info (semantics::data_member& m_, - semantics::type& t_, - semantics::type* wrapper_, - bool cq_, - string& var_, - string const& fq_type) - : m (m_), - t (t_), - wrapper (wrapper_), - cq (cq_), - st (0), - var (var_), - fq_type_ (fq_type) - { - } - }; - - bool - container (member_info& mi) - { - // This cannot be a container if we have a type override. - // - return type_override_ == 0 && context::container (mi.m); - } - - // The false return value indicates that no further callbacks - // should be called for this member. - // - virtual bool - pre (member_info&) - { - return true; - } - - virtual void - post (member_info&) - { - } - - virtual void - traverse_composite (member_info&) - { - } - - virtual void - traverse_container (member_info&) - { - } - - virtual void - traverse_object_pointer (member_info& mi) - { - traverse_simple (mi); - } + virtual sql_type const& + member_sql_type (semantics::data_member&); virtual void traverse_simple (member_info&); @@ -182,12 +82,16 @@ namespace relational string type_; }; - struct member_database_type_id: member_base + struct member_database_type_id: relational::member_database_type_id, + 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 ()); - string + + virtual string database_type_id (type&); virtual void diff --git a/odb/relational/sqlite/context.cxx b/odb/relational/sqlite/context.cxx index b04dcb9..46689bd 100644 --- a/odb/relational/sqlite/context.cxx +++ b/odb/relational/sqlite/context.cxx @@ -365,17 +365,20 @@ namespace relational } sql_type const& context:: - column_sql_type (semantics::data_member& m, string const& kp) + parse_sql_type (string const& t, semantics::data_member& m) { - string key (kp.empty () - ? string ("sqlite-column-sql-type") - : "sqlite-" + kp + "-column-sql-type"); + // If this proves to be too expensive, we can maintain a + // cache of parsed types. + // + data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t)); - if (!m.count (key)) + if (i != data_->sql_type_cache_.end ()) + return i->second; + else { try { - m.set (key, parse_sql_type (column_type (m, kp))); + return (data_->sql_type_cache_[t] = parse_sql_type (t)); } catch (invalid_sql_type const& e) { @@ -385,8 +388,6 @@ namespace relational throw operation_failed (); } } - - return m.get (key); } sql_type context:: diff --git a/odb/relational/sqlite/context.hxx b/odb/relational/sqlite/context.hxx index e9f755c..4123b0c 100644 --- a/odb/relational/sqlite/context.hxx +++ b/odb/relational/sqlite/context.hxx @@ -5,6 +5,8 @@ #ifndef ODB_RELATIONAL_SQLITE_CONTEXT_HXX #define ODB_RELATIONAL_SQLITE_CONTEXT_HXX +#include + #include namespace relational @@ -33,8 +35,7 @@ namespace relational { public: sql_type const& - column_sql_type (semantics::data_member&, - string const& key_prefix = string ()); + parse_sql_type (string const&, semantics::data_member&); public: struct invalid_sql_type @@ -87,6 +88,9 @@ namespace relational struct data: base_context::data { data (std::ostream& os): base_context::data (os) {} + + typedef std::map sql_type_cache; + sql_type_cache sql_type_cache_; }; data* data_; diff --git a/odb/relational/sqlite/model.cxx b/odb/relational/sqlite/model.cxx index 60522a3..41d5d6c 100644 --- a/odb/relational/sqlite/model.cxx +++ b/odb/relational/sqlite/model.cxx @@ -28,7 +28,7 @@ namespace relational { // Make sure the column is mapped to INTEGER. // - if (column_sql_type (m).type != sql_type::INTEGER) + if (parse_sql_type (column_type (), m).type != sql_type::INTEGER) { cerr << m.file () << ":" << m.line () << ":" << m.column () << ": error: column with default value specified as C++ " diff --git a/odb/relational/sqlite/source.cxx b/odb/relational/sqlite/source.cxx index 024694f..c170904 100644 --- a/odb/relational/sqlite/source.cxx +++ b/odb/relational/sqlite/source.cxx @@ -269,173 +269,21 @@ namespace relational // init image // - struct init_image_member: relational::init_image_member, member_base + struct init_image_member: relational::init_image_member_impl, + member_base { init_image_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_database_type_id_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - // Ignore containers (they get their own table) and inverse - // object pointers (they are not present in this binding). - // - if (container (mi) || inverse (mi.m, key_prefix_)) - return false; - - if (!member_override_.empty ()) - member = member_override_; - else - { - // If we are generating standard init() and this member - // contains version, ignore it. - // - if (version (mi.m)) - return false; - - string const& name (mi.m.name ()); - member = "o." + name; - - os << "// " << name << endl - << "//" << endl; - - // If the whole class is readonly, then we will never be - // called with sk == statement_update. - // - if (!readonly (*context::top_object)) - { - semantics::class_* c; - - if (id (mi.m) || - readonly (mi.m) || - ((c = composite (mi.t)) && readonly (*c))) - os << "if (sk == statement_insert)"; - } - } - - // If this is a wrapped composite value, then we need to - // "unwrap" it. For simple values this is taken care of - // by the value_traits specializations. - // - if (mi.wrapper != 0 && composite (mi.t)) - { - // Here we need the wrapper type, not the wrapped type. - // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "get_ref (" + member + ")"; - } - - if (composite (mi.t)) - { - os << "{"; - traits = "composite_value_traits< " + mi.fq_type () + " >"; - } - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (semantics::class_* c = object_pointer (mt)) - { - type = "obj_traits::id_type"; - db_type_id = member_database_type_id_.database_type_id (mi.m); - - // Handle NULL pointers and extract the id. - // - os << "{" - << "typedef object_traits< " << class_fq_name (*c) << - " > obj_traits;"; - - if (weak_pointer (mt)) - { - os << "typedef pointer_traits< " << mi.fq_type () << - " > wptr_traits;" - << "typedef pointer_traits< wptr_traits::" << - "strong_pointer_type > ptr_traits;" - << endl - << "wptr_traits::strong_pointer_type sp (" << - "wptr_traits::lock (" << member << "));"; - - member = "sp"; - } - else - os << "typedef pointer_traits< " << mi.fq_type () << - " > ptr_traits;" - << endl; - - os << "i." << mi.var << "null = ptr_traits::null_ptr (" << - member << ");" - << "if (!i." << mi.var << "null)" - << "{" - << "const " << type << "& id (" << endl; - - if (lazy_pointer (mt)) - os << "ptr_traits::object_id< ptr_traits::element_type > (" << - member << ")"; - else - os << "obj_traits::id (ptr_traits::get_ref (" << member << "))"; - - os << ");" - << endl; - - member = "id"; - } - else - { - type = mi.fq_type (); - db_type_id = member_database_type_id_.database_type_id (mi.m); - - os << "{"; - } - - traits = "sqlite::value_traits<\n " - + type + ",\n " - + db_type_id + " >"; - } - - return true; - } - - virtual void - post (member_info& mi) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) { - if (composite (mi.t)) - os << "}"; - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - if (object_pointer (member_utype (mi.m, key_prefix_))) - { - os << "}"; - - if (!null (mi.m, key_prefix_)) - os << "else" << endl - << "throw null_pointer ();"; - } - - os << "}"; - } } virtual void - traverse_composite (member_info& mi) + set_null (member_info& mi) { - os << "if (" << traits << "::init (" << endl - << "i." << mi.var << "value," << endl - << member << "," << endl - << "sk))" << endl - << "grew = true;"; + os << "i." << mi.var << "null = true;"; } virtual void @@ -443,8 +291,9 @@ namespace relational { os << traits << "::set_image (" << endl << "i." << mi.var << "value," << endl - << "i." << mi.var << "null," << endl - << member << ");"; + << "is_null," << endl + << member << ");" + << "i." << mi.var << "null = is_null;"; } virtual void @@ -452,8 +301,9 @@ namespace relational { os << traits << "::set_image (" << endl << "i." << mi.var << "value," << endl - << "i." << mi.var << "null," << endl - << member << ");"; + << "is_null," << endl + << member << ");" + << "i." << mi.var << "null = is_null;"; } virtual void @@ -463,18 +313,11 @@ namespace relational << traits << "::set_image (" << endl << "i." << mi.var << "value," << endl << "i." << mi.var << "size," << endl - << "i." << mi.var << "null," << endl + << "is_null," << endl << member << ");" + << "i." << mi.var << "null = is_null;" << "grew = grew || (cap != i." << mi.var << "value.capacity ());"; } - - private: - string type; - string db_type_id; - string member; - string traits; - - member_database_type_id member_database_type_id_; }; entry init_image_member_; @@ -482,147 +325,21 @@ namespace relational // init value // - struct init_value_member: relational::init_value_member, member_base + struct init_value_member: relational::init_value_member_impl, + member_base { init_value_member (base const& x) - : member_base::base (x), // virtual base - base (x), - member_base (x), - member_database_type_id_ (base::type_override_, - base::fq_type_override_, - base::key_prefix_) - { - } - - virtual bool - pre (member_info& mi) - { - if (container (mi)) - return false; - - if (!member_override_.empty ()) - member = member_override_; - else - { - string const& name (mi.m.name ()); - member = "o." + name; - - if (mi.cq) - member = "const_cast< " + mi.fq_type (false) + "& > (" + - member + ")"; - - os << "// " << name << endl - << "//" << endl; - } - - // If this is a wrapped composite value, then we need to - // "unwrap" it. For simple values this is taken care of - // by the value_traits specializations. - // - if (mi.wrapper != 0 && composite (mi.t)) - { - // Here we need the wrapper type, not the wrapped type. - // - member = "wrapper_traits< " + mi.fq_type (false) + " >::" + - "set_ref (\n" + member + ")"; - } - - if (composite (mi.t)) - traits = "composite_value_traits< " + mi.fq_type () + " >"; - else - { - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (semantics::class_* c = object_pointer (mt)) - { - type = "obj_traits::id_type"; - db_type_id = member_database_type_id_.database_type_id (mi.m); - - // Handle NULL pointers and extract the id. - // - os << "{" - << "typedef object_traits< " << class_fq_name (*c) << - " > obj_traits;" - << "typedef pointer_traits< " << mi.fq_type () << - " > ptr_traits;" - << endl - << "if (i." << mi.var << "null)" << endl; - - if (null (mi.m, key_prefix_)) - os << member << " = ptr_traits::pointer_type ();"; - else - os << "throw null_pointer ();"; - - os << "else" - << "{" - << type << " id;"; - - member = "id"; - } - else - { - type = mi.fq_type (); - db_type_id = member_database_type_id_.database_type_id (mi.m); - } - - traits = "sqlite::value_traits<\n " - + type + ",\n " - + db_type_id + " >"; - } - - return true; - } - - virtual void - post (member_info& mi) + : member_base::base (x), // virtual base + member_base::base_impl (x), // virtual base + base_impl (x), + member_base (x) { - if (composite (mi.t)) - return; - - // When handling a pointer, mi.t is the id type of the referenced - // object. - // - semantics::type& mt (member_utype (mi.m, key_prefix_)); - - if (object_pointer (mt)) - { - if (!member_override_.empty ()) - member = member_override_; - else - { - member = "o." + mi.m.name (); - - if (mi.cq) - member = "const_cast< " + mi.fq_type (false) + "& > (" + - member + ")"; - } - - if (lazy_pointer (mt)) - os << member << " = ptr_traits::pointer_type (db, id);"; - else - os << "// If a compiler error points to the line below, then" << endl - << "// it most likely means that a pointer used in a member" << endl - << "// cannot be initialized from an object pointer." << endl - << "//" << endl - << member << " = ptr_traits::pointer_type (" << endl - << "db.load< obj_traits::object_type > (id));"; - - os << "}" - << "}"; - } } virtual void - traverse_composite (member_info& mi) + get_null (member_info& mi) { - os << traits << "::init (" << endl - << member << "," << endl - << "i." << mi.var << "value," << endl - << "db);" - << endl; + os << "i." << mi.var << "null"; } virtual void @@ -655,14 +372,6 @@ namespace relational << "i." << mi.var << "null);" << endl; } - - private: - string type; - string db_type_id; - string traits; - string member; - - member_database_type_id member_database_type_id_; }; entry init_value_member_; @@ -680,7 +389,6 @@ namespace relational }; entry container_traits_; - struct class_: relational::class_, context { class_ (base const& x): base (x) {} diff --git a/odb/validator.cxx b/odb/validator.cxx index b8f7c91..c051487 100644 --- a/odb/validator.cxx +++ b/odb/validator.cxx @@ -42,7 +42,7 @@ namespace // Pass 1. // - struct data_member: traversal::data_member + struct data_member: traversal::data_member, context { data_member (bool& valid) : valid_ (valid) @@ -52,45 +52,42 @@ namespace virtual void traverse (type& m) { - if (context::transient (m)) + if (transient (m)) return; count_++; semantics::names* hint; - semantics::type& t (context::utype (m, hint)); + semantics::type& t (utype (m, hint)); if (t.fq_anonymous (hint)) { - cerr << m.file () << ":" << m.line () << ":" << m.column () << ":" - << " error: unnamed type in data member declaration" << endl; + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: unnamed type in data member declaration" << endl; - cerr << m.file () << ":" << m.line () << ":" << m.column () << ":" - << " info: use 'typedef' to name this type" << endl; + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " info: use 'typedef' to name this type" << endl; valid_ = false; } // Make sure id or inverse member is not marked readonly since we - // depend on these three sets not having overlaps. Once we support - // composite ids, we will also need to make sure there are no - // nested readonly members (probably move it to pass 2 and use - // column_count()). + // depend on these three sets not having overlaps. // - if (m.count ("readonly")) + if (readonly (m)) { - if (m.count ("id")) + if (id (m)) { - cerr << m.file () << ":" << m.line () << ":" << m.column () << ":" - << " error: object id should not be declared readonly" << endl; + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: object id should not be declared readonly" << endl; valid_ = false; } - if (m.count ("inverse")) + if (inverse (m)) { - cerr << m.file () << ":" << m.line () << ":" << m.column () << ":" - << " error: inverse object pointer should not be declared " - << "readonly" << endl; + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: inverse object pointer should not be declared " + << "readonly" << endl; valid_ = false; } @@ -108,9 +105,9 @@ namespace // Find special members (id, version). // - struct special_members: traversal::class_ + struct special_members: traversal::class_, context { - special_members (class_kind kind, + special_members (class_kind_type kind, bool& valid, semantics::data_member*& id, semantics::data_member*& optimistic) @@ -131,7 +128,7 @@ namespace { case class_object: { - if (!context::object (c)) + if (!object (c)) return; break; } @@ -141,7 +138,7 @@ namespace } case class_composite: { - if (!context::composite (c)) + if (!composite (c)) return; break; } @@ -161,7 +158,7 @@ namespace } private: - struct member: traversal::data_member + struct member: traversal::data_member, context { member (bool& valid, semantics::data_member*& id, @@ -173,17 +170,15 @@ namespace virtual void traverse (semantics::data_member& m) { - if (m.count ("id")) + if (id (m)) { if (id_ != 0) { - cerr << m.file () << ":" << m.line () << ":" << m.column () << ":" - << " error: multiple object id members" << endl; + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: multiple object id members" << endl; - semantics::data_member& i (*id_); - - cerr << i.file () << ":" << i.line () << ":" << i.column () - << ": info: previous id member is declared here" << endl; + os << id_->file () << ":" << id_->line () << ":" << id_->column () + << ": info: previous id member is declared here" << endl; valid_ = false; } @@ -191,17 +186,17 @@ namespace id_ = &m; } - if (m.count ("version")) + if (version (m)) { if (optimistic_ != 0) { - cerr << m.file () << ":" << m.line () << ":" << m.column () << ":" - << " error: multiple version members" << endl; + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: multiple version members" << endl; semantics::data_member& o (*optimistic_); - cerr << o.file () << ":" << o.line () << ":" << o.column () - << ": info: previous version member is declared here" << endl; + os << o.file () << ":" << o.line () << ":" << o.column () + << ": info: previous version member is declared here" << endl; valid_ = false; } @@ -215,7 +210,7 @@ namespace semantics::data_member*& optimistic_; }; - class_kind kind_; + class_kind_type kind_; member member_; traversal::names names_; traversal::inherits inherits_; @@ -223,7 +218,7 @@ namespace // // - struct value_type: traversal::type + struct value_type: traversal::type, context { value_type (bool& valid): valid_ (valid) {} @@ -241,17 +236,10 @@ namespace // // - struct class1: traversal::class_ + struct class1: traversal::class_, context { - class1 (bool& valid, - options const& ops, - semantics::unit& unit, - value_type& vt) - : valid_ (valid), - options_ (ops), - unit_ (unit), - vt_ (vt), - member_ (valid) + class1 (bool& valid, value_type& vt) + : valid_ (valid), vt_ (vt), member_ (valid) { *this >> names_ >> member_; } @@ -259,13 +247,13 @@ namespace virtual void traverse (type& c) { - if (context::object (c)) + if (object (c)) traverse_object (c); - else if (context::view (c)) + else if (view (c)) traverse_view (c); else { - if (context::composite (c)) + if (composite (c)) traverse_composite (c); vt_.dispatch (c); @@ -286,10 +274,10 @@ namespace if (decl == error_mark_node || TREE_CODE (decl) != BASELINK) { - cerr << c.file () << ":" << c.line () << ":" << c.column () << ": " - << "error: unable to resolve member function '" << name << "' " - << "specified with '#pragma db callback' for class '" - << context::class_name (c) << "'" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ": " + << "error: unable to resolve member function '" << name << "' " + << "specified with '#pragma db callback' for class '" + << class_name (c) << "'" << endl; valid_ = false; } @@ -324,25 +312,25 @@ namespace { type& b (i->base ()); - if (context::object (b)) + if (object (b)) base = true; - else if (context::view (b) || context::composite (b)) + else if (view (b) || composite (b)) { // @@ Should we use hint here? // - string name (context::class_fq_name (b)); + string name (class_fq_name (b)); - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: base class '" << name << "' is a view or value type" - << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: base class '" << name << "' is a view or value type" + << endl; - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " info: object types cannot derive from view or value " - << "types" - << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " info: object types cannot derive from view or value " + << "types" + << endl; - cerr << b.file () << ":" << b.line () << ":" << b.column () << ":" - << " info: class '" << name << "' is defined here" << endl; + os << b.file () << ":" << b.line () << ":" << b.column () << ":" + << " info: class '" << name << "' is defined here" << endl; valid_ = false; } @@ -359,21 +347,21 @@ namespace if (id == 0) { - // An object without an id should either be abstract or - // explicitly marked as such. + // An object without an id should either be abstract or explicitly + // marked as such. // - if (!(c.count ("id") || context::abstract (c))) + if (!(c.count ("id") || abstract (c))) { - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: no data member designated as an object id" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: no data member designated as an object id" << endl; - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " info: use '#pragma db id' to specify an object id member" - << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " info: use '#pragma db id' to specify an object id member" + << endl; - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " info: or explicitly declare that this persistent class " - << "has no object id" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " info: or explicitly declare that this persistent class " + << "has no object id" << endl; valid_ = false; } @@ -387,9 +375,9 @@ namespace // if (id->count ("default")) { - cerr << id->file () << ":" << id->line () << ":" << id->column () - << ": error: object id member cannot have default value" - << endl; + os << id->file () << ":" << id->line () << ":" << id->column () + << ": error: object id member cannot have default value" + << endl; valid_ = false; } @@ -399,8 +387,8 @@ namespace // if (id->count ("null")) { - cerr << id->file () << ":" << id->line () << ":" << id->column () - << ": error: object id member cannot be null" << endl; + os << id->file () << ":" << id->line () << ":" << id->column () + << ": error: object id member cannot be null" << endl; valid_ = false; } @@ -416,13 +404,13 @@ namespace // if (&optimistic->scope () == &c && !c.count ("optimistic")) { - cerr << m.file () << ":" << m.line () << ":" << m.column () << ":" - << " error: version data member in a class not declared " - << "optimistic" << endl; + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: version data member in a class not declared " + << "optimistic" << endl; - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " info: use '#pragma db optimistic' to declare this " - << "class optimistic" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " info: use '#pragma db optimistic' to declare this " + << "class optimistic" << endl; valid_ = false; } @@ -431,8 +419,8 @@ namespace // if (id == 0) { - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: optimistic class without an object id" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: optimistic class without an object id" << endl; valid_ = false; } @@ -442,34 +430,33 @@ namespace // if (id != 0 && &id->scope () != &optimistic->scope ()) { - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: object id and version members are in different " - << "classes" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: object id and version members are in different " + << "classes" << endl; - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " info: object id and version members must be in the same " - << "class" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " info: object id and version members must be in the same " + << "class" << endl; - cerr << id->file () << ":" << id->line () << ":" << id->column () - << ": info: object id member is declared here" << endl; + os << id->file () << ":" << id->line () << ":" << id->column () + << ": info: object id member is declared here" << endl; - cerr << m.file () << ":" << m.line () << ":" << m.column () << ":" - << " error: version member is declared here" << endl; + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: version member is declared here" << endl; valid_ = false; } // Make sure this class is not readonly. // - if (c.count ("readonly")) + if (readonly (c)) { - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: optimistic class cannot be readonly" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: optimistic class cannot be readonly" << endl; valid_ = false; } - // This takes care of also marking derived classes as optimistic. // c.set ("optimistic-member", optimistic); @@ -481,12 +468,12 @@ namespace // if (c.count ("optimistic")) { - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: optimistic class without a version member" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: optimistic class without a version member" << endl; - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " info: use '#pragma db version' to declare on of the " - << "data members as a version" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " info: use '#pragma db version' to declare on of the " + << "data members as a version" << endl; valid_ = false; } @@ -499,8 +486,8 @@ namespace if (member_.count_ == 0 && !base) { - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: no persistent data members in the class" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: no persistent data members in the class" << endl; valid_ = false; } @@ -511,16 +498,16 @@ namespace { // Views require query support. // - if (!options_.generate_query ()) + if (!options.generate_query ()) { - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: query support is required when using views" - << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: query support is required when using views" + << endl; - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " info: use the --generate-query option to enable query " - << "support" - << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " info: use the --generate-query option to enable query " + << "support" + << endl; valid_ = false; } @@ -533,26 +520,24 @@ namespace { type& b (i->base ()); - if (context::object (b) || - context::view (b) || - context::composite (b)) + if (object (b) || view (b) || composite (b)) { // @@ Should we use hint here? // - string name (context::class_fq_name (b)); + string name (class_fq_name (b)); - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: base class '" << name << "' is an object, " - << "view, or value type" - << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: base class '" << name << "' is an object, " + << "view, or value type" + << endl; - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " info: view types cannot derive from view, object or " - << "value types" - << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " info: view types cannot derive from view, object or " + << "value types" + << endl; - cerr << b.file () << ":" << b.line () << ":" << b.column () << ":" - << " info: class '" << name << "' is defined here" << endl; + os << b.file () << ":" << b.line () << ":" << b.column () << ":" + << " info: class '" << name << "' is defined here" << endl; valid_ = false; } @@ -569,9 +554,9 @@ namespace if (id != 0) { - cerr << id->file () << ":" << id->line () << ":" << id->column () - << ": error: view type data member cannot be designated as an " - << "object id" << endl; + os << id->file () << ":" << id->line () << ":" << id->column () + << ": error: view type data member cannot be designated as an " + << "object id" << endl; valid_ = false; } @@ -580,9 +565,9 @@ namespace { semantics::data_member& o (*optimistic); - cerr << o.file () << ":" << o.line () << ":" << o.column () - << ": error: view type data member cannot be designated as a " - << "version" << endl; + os << o.file () << ":" << o.line () << ":" << o.column () + << ": error: view type data member cannot be designated as a " + << "version" << endl; valid_ = false; } @@ -594,8 +579,8 @@ namespace if (member_.count_ == 0) { - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: no persistent data members in the class" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: no persistent data members in the class" << endl; valid_ = false; } @@ -612,25 +597,25 @@ namespace { type& b (i->base ()); - if (context::composite (b)) + if (composite (b)) base = true; - else if (context::object (b) || context::view (b)) + else if (object (b) || view (b)) { // @@ Should we use hint here? // - string name (context::class_fq_name (b)); + string name (class_fq_name (b)); - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: base class '" << name << "' is a view or object " - << "type" - << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: base class '" << name << "' is a view or object " + << "type" + << endl; - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " info: composite value types cannot derive from object " - << "or view types" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " info: composite value types cannot derive from object " + << "or view types" << endl; - cerr << b.file () << ":" << b.line () << ":" << b.column () << ":" - << " info: class '" << name << "' is defined here" << endl; + os << b.file () << ":" << b.line () << ":" << b.column () << ":" + << " info: class '" << name << "' is defined here" << endl; valid_ = false; } @@ -647,9 +632,9 @@ namespace if (id != 0) { - cerr << id->file () << ":" << id->line () << ":" << id->column () - << ": error: value type data member cannot be designated as an " - << "object id" << endl; + os << id->file () << ":" << id->line () << ":" << id->column () + << ": error: value type data member cannot be designated as an " + << "object id" << endl; valid_ = false; } @@ -658,9 +643,9 @@ namespace { semantics::data_member& o (*optimistic); - cerr << o.file () << ":" << o.line () << ":" << o.column () - << ": error: value type data member cannot be designated as a " - << "version" << endl; + os << o.file () << ":" << o.line () << ":" << o.column () + << ": error: value type data member cannot be designated as a " + << "version" << endl; valid_ = false; } @@ -672,16 +657,14 @@ namespace if (member_.count_ == 0 && !base) { - cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: no persistent data members in the class" << endl; + os << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: no persistent data members in the class" << endl; valid_ = false; } } bool& valid_; - options const& options_; - semantics::unit& unit_; value_type& vt_; data_member member_; @@ -720,15 +703,80 @@ namespace } virtual void + traverse_pointer (semantics::data_member& m, semantics::class_&) + { + if (inverse (m)) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: inverse object pointer member '" << member_prefix_ + << m.name () << "' in an object without an object id" << endl; + + valid_ = false; + } + } + + virtual void + traverse_container (semantics::data_member& m, semantics::type&) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: container member '" << member_prefix_ << m.name () + << "' in an object without an object id" << endl; + + valid_ = false; + } + + virtual void + traverse_composite (semantics::data_member* m, semantics::class_& c) + { + semantics::data_member* old_dm (dm_); + + if (dm_ == 0) + dm_ = m; + + object_members_base::traverse_composite (m, c); + + dm_ = old_dm; + } + + private: + bool& valid_; + semantics::data_member* dm_; // Direct object data member. + }; + + struct composite_id_members: object_members_base + { + composite_id_members (bool& valid) + : object_members_base (false, false, true), valid_ (valid), dm_ (0) + { + } + + virtual void + traverse_pointer (semantics::data_member& m, semantics::class_&) + { + semantics::data_member& dm (dm_ != 0 ? *dm_ : m); + + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: object pointer member '" << member_prefix_ << m.name () + << "' in a composite value type that is used as an object id" << endl; + + valid_ = false; + } + + virtual void traverse_simple (semantics::data_member& m) { - if (m.count ("inverse")) + if (readonly (member_path_, member_scope_)) { semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - cerr << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: inverse object pointer member '" << member_prefix_ - << m.name () << "' in an object without an object id" << endl; + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: readonly member '" << member_prefix_ << m.name () + << "' in a composite value type that is used as an object id" + << endl; valid_ = false; } @@ -739,9 +787,9 @@ namespace { semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - cerr << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: container member '" << member_prefix_ << m.name () - << "' in an object without an object id" << endl; + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: container member '" << member_prefix_ << m.name () + << "' in a composite value type that is used as an object id" << endl; valid_ = false; } @@ -761,7 +809,7 @@ namespace private: bool& valid_; - semantics::data_member* dm_; // Direct view data member. + semantics::data_member* dm_; // Direct composite member. }; struct view_members: object_members_base @@ -774,16 +822,16 @@ namespace virtual void traverse_simple (semantics::data_member& m) { - if (context::object_pointer (utype (m))) + if (object_pointer (utype (m))) { semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - cerr << 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 () << ":" + << " error: view data member '" << member_prefix_ << m.name () + << "' is an object pointer" << endl; - cerr << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << ": info: views cannot contain object pointers" << endl; + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << ": info: views cannot contain object pointers" << endl; valid_ = false; } @@ -794,12 +842,12 @@ namespace { semantics::data_member& dm (dm_ != 0 ? *dm_ : m); - cerr << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << " error: view data member '" << member_prefix_ << m.name () - << "' is a container" << endl; + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << " error: view data member '" << member_prefix_ << m.name () + << "' is a container" << endl; - cerr << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" - << ": info: views cannot contain containers" << endl; + os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" + << ": info: views cannot contain containers" << endl; valid_ = false; } @@ -824,36 +872,175 @@ namespace // // - struct class2: traversal::class_ + struct class2: traversal::class_, context { - class2 (bool& valid, options const& ops, semantics::unit& unit) + class2 (bool& valid) : valid_ (valid), - options_ (ops), - unit_ (unit), object_no_id_members_ (valid), + composite_id_members_ (valid), view_members_ (valid) { + // Find the has_lt_operator function template.. + // + has_lt_operator_ = 0; + + tree odb ( + lookup_qualified_name ( + global_namespace, get_identifier ("odb"), false, false)); + + if (odb != error_mark_node) + { + tree compiler ( + lookup_qualified_name ( + odb, get_identifier ("compiler"), false, false)); + + if (compiler != error_mark_node) + { + has_lt_operator_ = lookup_qualified_name ( + compiler, get_identifier ("has_lt_operator"), false, false); + + if (has_lt_operator_ != error_mark_node) + has_lt_operator_ = OVL_CURRENT (has_lt_operator_); + else + { + os << unit.file () << ": error: unable to resolve has_lt_operator " + << "function template inside odb::compiler" << endl; + has_lt_operator_ = 0; + } + } + else + os << unit.file () << ": error: unable to resolve compiler " + << "namespace inside odb" << endl; + } + else + os << unit.file () << ": error: unable to resolve odb namespace" + << endl; + + if (has_lt_operator_ == 0) + valid_ = false; } virtual void traverse (type& c) { - if (context::object (c)) + if (object (c)) traverse_object (c); - else if (context::view (c)) + else if (view (c)) traverse_view (c); - else if (context::composite (c)) + else if (composite (c)) traverse_composite (c); } virtual void traverse_object (type& c) { - if (context::id_member (c) == 0 && !context::abstract (c)) + semantics::data_member* id (id_member (c)); + + if (id != 0) { - // Make sure we don't have any containers or inverse pointers. - // - object_no_id_members_.traverse (c); + if (semantics::class_* c = composite_wrapper (utype (*id))) + { + // Composite id cannot be auto. + // + if (auto_ (*id)) + { + os << id->file () << ":" << id->line () << ":" << id->column () + << ": error: composite id cannot be automatically assigned" + << endl; + + valid_ = false; + } + + // Make sure we don't have any containers or pointers in this + // composite value type. + // + if (valid_) + { + composite_id_members_.traverse (*c); + + if (!valid_) + os << id->file () << ":" << id->line () << ":" << id->column () + << ": info: composite id is defined here" << endl; + } + + // Check that the composite value type is default-constructible. + // + if (!c->default_ctor ()) + { + os << c->file () << ":" << c->line () << ":" << c->column () + << ": error: composite value type that is used as object id " + << "is not default-constructible" << endl; + + os << c->file () << ":" << c->line () << ":" << c->column () + << ": info: provide default constructor for this value type" + << endl; + + os << id->file () << ":" << id->line () << ":" << id->column () + << ": info: composite id is defined here" << endl; + + valid_ = false; + } + + // Check that composite values can be compared (used in session). + // + if (has_lt_operator_ != 0) + { + tree args (make_tree_vec (1)); + TREE_VEC_ELT (args, 0) = c->tree_node (); + + tree inst ( + instantiate_template ( + has_lt_operator_, args, tf_none)); + + bool v (inst != error_mark_node); + + if (v && + DECL_TEMPLATE_INSTANTIATION (inst) && + !DECL_TEMPLATE_INSTANTIATED (inst)) + { + // Instantiate this function template to see if the value type + // provides operator<. Unfortunately, GCC instantiate_decl() + // does not provide any control over the diagnostics it issues + // in case of an error. To work around this, we are going to + // temporarily redirect diagnostics to /dev/null, which is + // where asm_out_file points to (see plugin.cxx). + // + int ec (errorcount); + FILE* s (global_dc->printer->buffer->stream); + global_dc->printer->buffer->stream = asm_out_file; + + instantiate_decl (inst, false, false); + + global_dc->printer->buffer->stream = s; + v = (ec == errorcount); + } + + if (!v) + { + os << c->file () << ":" << c->line () << ":" << c->column () + << ": error: composite value type that is used as object id " + << "does not support the less than (<) comparison" + << endl; + + os << c->file () << ":" << c->line () << ":" << c->column () + << ": info: provide operator< for this value type" << endl; + + os << id->file () << ":" << id->line () << ":" << id->column () + << ": info: composite id is defined here" << endl; + + valid_ = false; + } + } + } + } + else + { + if (!abstract (c)) + { + // Make sure we don't have any containers or inverse pointers. + // + object_no_id_members_.traverse (c); + } } } @@ -871,10 +1058,10 @@ namespace } bool& valid_; - options const& options_; - semantics::unit& unit_; + tree has_lt_operator_; object_no_id_members object_no_id_members_; + composite_id_members composite_id_members_; view_members view_members_; }; } @@ -897,7 +1084,7 @@ validate (options const& ops, typedefs1 unit_typedefs (unit_declares); traversal::namespace_ ns; value_type vt (valid); - class1 c (valid, ops, u, vt); + class1 c (valid, vt); unit >> unit_defines >> ns; unit_defines >> c; @@ -921,7 +1108,7 @@ validate (options const& ops, traversal::defines unit_defines; typedefs unit_typedefs (true); traversal::namespace_ ns; - class2 c (valid, ops, u); + class2 c (valid); unit >> unit_defines >> ns; unit_defines >> c; -- cgit v1.1