From 2842ac773c779b6bd8298a34320076a8c5ca77ee Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 23 Aug 2012 11:52:05 +0200 Subject: Document new accessor/modifier functionality --- NEWS | 16 + doc/manual.xhtml | 1003 ++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 697 insertions(+), 322 deletions(-) diff --git a/NEWS b/NEWS index ca6b081..217ca20 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,21 @@ Version 2.1.0 + * ODB compiler is now capable of automatically discovering accessor and + modifier functions for inaccessible data members in persistent classes, + composite value types, and views. It will then use these accessors and + modifiers in the generated code. The names of these functions are derived + from the data member names and by default the ODB compiler will look for + names in the form: get_foo/set_foo, getFoo/setFoo, getfoo/setfoo, and + just foo. You can also add custom name derivations with the + --accessor-regex and --modifier-regex ODB compiler options. For more + information, refer to Section 3.2, "Declaring Persistent Objects and + Values" in the ODB manual. + + * New pragmas, get, set, and access, allow the specification of custom + accessor and modifier expressions for data members in persistent classes, + composite value types, and views. For more information, refer to Section + 12.4.5, "get/set/access" in the ODB manual. + * Support for defining database indexes. Both simple and composite indexes can be defined with support for database-specific index types, methods, and options. For more information, refer to Section 12.6, "Index Definition diff --git a/doc/manual.xhtml b/doc/manual.xhtml index ffc485c..972b26a 100644 --- a/doc/manual.xhtml +++ b/doc/manual.xhtml @@ -320,18 +320,19 @@ for consistency. 3Working with Persistent Objects - - - - - - - - - - - - + + + + + + + + + + + + +
3.1Concepts and Terminology
3.2Object and View Pointers
3.3Database
3.4Transactions
3.5Connections
3.6Error Handling and Recovery
3.7Making Objects Persistent
3.8Loading Persistent Objects
3.9Updating Persistent Objects
3.10Deleting Persistent Objects
3.11Executing Native SQL Statements
3.12Tracing SQL Statement Execution
3.13ODB Exceptions
3.2Declaring Persistent Objects and Values
3.3Object and View Pointers
3.4Database
3.5Transactions
3.6Connections
3.7Error Handling and Recovery
3.8Making Objects Persistent
3.9Loading Persistent Objects
3.10Updating Persistent Objects
3.11Deleting Persistent Objects
3.12Executing Native SQL Statements
3.13Tracing SQL Statement Execution
3.14ODB Exceptions
@@ -507,31 +508,32 @@ for consistency. 12.4.2auto 12.4.3type 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.14index - 12.4.15unique - 12.4.16unordered - 12.4.17table - 12.4.18index_type - 12.4.19key_type - 12.4.20value_type - 12.4.21value_null/value_not_null - 12.4.22id_options - 12.4.23index_options - 12.4.24key_options - 12.4.25value_options - 12.4.26id_column - 12.4.27index_column - 12.4.28key_column - 12.4.29value_column + 12.4.5get/set/access + 12.4.6null/not_null + 12.4.7default + 12.4.8options + 12.4.9column (object, composite value) + 12.4.10column (view) + 12.4.11transient + 12.4.12readonly + 12.4.13inverse + 12.4.14version + 12.4.15index + 12.4.16unique + 12.4.17unordered + 12.4.18table + 12.4.19index_type + 12.4.20key_type + 12.4.21value_type + 12.4.22value_null/value_not_null + 12.4.23id_options + 12.4.24index_options + 12.4.25key_options + 12.4.26value_options + 12.4.27id_column + 12.4.28index_column + 12.4.29key_column + 12.4.30value_column @@ -654,6 +656,7 @@ for consistency. 16.5.5Large FLOAT and NUMBER Types 16.5.6Timezones 16.5.7LONG Types + 16.5.8LOB Types and By-Value Accessors/Modifiers @@ -678,6 +681,7 @@ for consistency. 17.5.4Multithreaded Windows Applications 17.5.5Affected Row Count and DDL Statements 17.5.6Long Data and Automatically Assigned Object Ids + 17.5.7Long Data and By-Value Accessors/Modifiers @@ -1125,17 +1129,11 @@ public: const std::string& last, unsigned short age); - const std::string& - first () const; + const std::string& first () const; + const std::string& last () const; - const std::string& - last () const; - - unsigned short - age () const; - - void - age (unsigned short); + unsigned short age () const; + void age (unsigned short); private: std::string first_; @@ -1197,14 +1195,16 @@ private: when instantiating an object from the persistent state. Just as we have done for the person class, you can make the default constructor private or protected if you don't want to make it - available to the users of your class.

+ available to the users of your class. Note also that with some + limitations it is possible to have a persistent class without + the default constructor.

With the fourth change we make the odb::access class a friend of our person class. This is necessary to make the default constructor and the data members accessible to the - ODB support code. If your class has public default constructor and - public data members, then the friend declaration is - unnecessary.

+ database support code. If your class has a public default constructor and + either public data members or public accessors and modifiers for the + data members, then the friend declaration is unnecessary.

The final change adds a data member called id_ which is preceded by another pragma. In ODB every persistent object normally @@ -1228,20 +1228,41 @@ private: in other countries), then we could use that as an id. Or, if we stored an email associated with each person, then we could have used that if each person is presumed to have a unique - email address, for example:

+ email address.

+ +

As another example, consider the following alternative version + of the person class. Here we use one of + the existing data members as id. Also the data members are kept + private and are instead accessed via public accessor and modifier + functions. Finally, the ODB pragmas are grouped together and are + placed after the class definition. They could have also been moved + into a separate header leaving the original class completely + unchanged (for more information on such a non-intrusive conversion + refer to Chapter 12, "ODB Pragma Language").

 class person
 {
-  ...
+public:
+  person ();
 
-  #pragma db id
-  std::string email_;
+  const std::string& email () const;
+  void email (const std::string&);
 
-  std::string first_;
-  std::string last_;
+  const std::string& get_name () const;
+  std::string& set_name ();
+
+  unsigned short getAge () const;
+  void setAge (unsigned short);
+
+private:
+  std::string email_;
+  std::string name_;
   unsigned short age_;
 };
+
+#pragma db object(person)
+#pragma db member(person::email_) id
   

Now that we have the header file with the persistent class, let's @@ -1386,7 +1407,7 @@ mysql --user=odb_test --database=odb_test < person.sql only way to create a database schema in ODB. We can also embed the schema directly into our application or use custom schemas that were not generated by the ODB compiler. Refer to - Section 3.3, "Database" for details.

+ Section 3.4, "Database" for details.

Once the database schema is ready, we run our application using the same login and database name:

@@ -1542,7 +1563,7 @@ main (int argc, char* argv[])

The final bit of code in our example is the catch block that handles the database exceptions. We do this by catching - the base ODB exception (Section 3.13, "ODB + the base ODB exception (Section 3.14, "ODB Exceptions") and printing the diagnostics.

Let's now compile (Section 2.3, "Compiling and @@ -1616,7 +1637,7 @@ INSERT INTO `person` (`id`,`first`,`last`,`age`) VALUES (?,?,?,?)

Note that we see question marks instead of the actual values because ODB uses prepared statements and sends the data to the database in binary form. For more information on tracing, refer - to Section 3.12, "Tracing SQL Statement Execution". + to Section 3.13, "Tracing SQL Statement Execution". In the next section we will see how to access persistent objects from our application.

@@ -1820,7 +1841,7 @@ main (int argc, char* argv[]) std::unique_ptr from C++11 or shared_ptr from TR1, C++11, or Boost. For more information on the object lifetime management and the smart pointers that we - can use for that, see Section 3.2, "Object + can use for that, see Section 3.3, "Object and View Pointers".

With the instantiated object in hand we increment the age @@ -2034,11 +2055,11 @@ max age: 33 chapter we will examine the ODB object persistence model as well as the core database APIs in greater detail. We will start with basic concepts and terminology in Section - 3.1 and Section 3.2 and continue with the + 3.1 and Section 3.3 and continue with the discussion of the odb::database class in - Section 3.3, transactions in - Section 3.4, and connections in - Section 3.5. The remainder of this chapter + Section 3.4, transactions in + Section 3.5, and connections in + Section 3.6. The remainder of this chapter deals with the core database operations and concludes with the discussion of ODB exceptions.

@@ -2157,6 +2178,51 @@ max age: 33 we invariably refer to an object class rather than a value class.

+

Normally, you would use object types to model real-world entities, + things that have their own identity. For example, in the + previous chapter we created a person class to model + a person, which is a real-world entity. Name and age, which we + used as data members in our person class are clearly + values. It is hard to think of age 31 or name "Joe" as having their + own identities.

+ +

A good test to determine whether something is an object or + a value, is to consider if other objects might reference + it. A person is clearly an object because it can be referred + to by other objects such as a spouse, an employer, or a + bank. On the other hand, a person's age or name is not + something that other objects would normally refer to.

+ +

Also, when an object represents a real entity, it is easy to + choose a suitable object id. For example, for a + person there is an established notion of an identifier + (SSN, student id, passport number, etc). Another alternative + is to use a person's email address as an identifier.

+ +

Note, however, that these are only guidelines. There could + be good reasons to make something that would normally be + a value an object. Consider, for example, a database that + stores a vast number of people. Many of the person + objects in this database have the same names and surnames and + the overhead of storing them in every object may negatively + affect the performance. In this case, we could make the first name + and last name each an object and only store pointers to + these objects in the person class.

+ +

An instance of a persistent class can be in one of two states: + transient and persistent. A transient + instance only has a representation in the application's + memory and will cease to exist when the application terminates, + unless it is explicitly made persistent. In other words, a + transient instance of a persistent class behaves just like an + instance of any ordinary C++ class. A persistent instance + has a representation in both the application's memory and the + database. A persistent instance will remain even after the + application terminates unless and until it is explicitly + deleted from the database.

+ +

3.2 Declaring Persistent Objects and Values

+

To make a C++ class a persistent object class we declare it as such using the db object pragma, for example:

@@ -2184,34 +2250,52 @@ class person }; -

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

+

The object id can be of a simple or composite (Section + 7.2.1, "Composite Object Ids") value type. This type should be + default-constructible. It is also possible to declare a persistent + class without an object id, however, such a class will have limited + functionality (Section 12.1.6, + "id").

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

-

Normally, an object class should define the default constructor. The +

Normally, a persistent class should define the default constructor. The generated database support code uses this constructor when instantiating an object from the persistent state. If we add the default constructor only for the database support code, then we - can make it private. It is also possible to have an object type - 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").

+ can make it private provided we also make the odb::access + class, defined in the <odb/core.hxx> header, a + friend of this object class. For example:

-

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

+
+#include <odb/core.hxx>
 
-  

If an object class has private or protected non-transient data - members or if its default constructor is not public, then the - odb::access class, defined in the - <odb/core.hxx> header, should be declared a - friend of this object type. For example:

+#pragma db object +class person +{ + ... + +private: + friend class odb::access; + person () {} +}; +
+ +

It is also possible to have an object class without the default + constructor. However, in this case, the database operations will + only be able to load the persistent state into an existing instance + (Section 3.9, "Loading Persistent Objects", + Section 4.4, "Query Result").

+ +

The ODB compiler also needs access to the non-transient + (Section 12.4.11, "transient") + data members of a persistent class. The ODB compiler can access + such data members directly if they are public. It can also do + so if they are private or protected and the odb::access + class is declared a friend of the object type. For example:

 #include <odb/core.hxx>
@@ -2223,26 +2307,80 @@ class person
 
 private:
   friend class odb::access;
-
   person () {}
 
   #pragma db id
   unsigned long id_;
+
+  std::string name_;
+};
+  
+ +

If data members are not accessible directly, then the ODB + compiler will try to automatically find suitable accessor and + modifier functions. To accomplish this, the ODB compiler will + try to lookup common accessor and modifier names derived from + the data member name. Specifically, for the name_ + data member in the above example, the ODB compiler will look + for accessor functions with names: get_name(), + getName(), getname(), and just + name() as well as for modifier functions with + names: set_name(), setName(), + setname(), and just name(). You can + also add support for custom name derivations with the + --accessor-regex and --modifier-regex + ODB compiler options. Refer to the + ODB + Compiler Command Line Manual for details on these options. + The following example illustrates automatic accessor and modifier + discovery:

+ +
+#pragma db object
+class person
+{
+public:
+  person () {}
+
+  ...
+
+  unsigned long id () const;
+  void id (unsigned long);
+
+  const std::string& get_name () const;
+  std::string& set_name ();
+
+private:
+  #pragma db id
+  unsigned long id_; // Uses id() for access.
+
+  std::string name_; // Uses get_name()/set_name() for access.
 };
   
+

Finally, if a data member is not directly accessible and the + ODB compiler was unable to discover suitable accessor and + modifier functions, then we can provide custom accessor + and modifier expressions using the db get + and db set pragmas. For more information + on custom accessor and modifier expressions refer to + Section 12.4.5, + "get/set/access".

+

You may be wondering whether we also have to declare value types as persistent. We don't need to do anything special for simple value types such as int or std::string since the - ODB compiler knows how to map them to suitable database system types and + ODB compiler knows how to map them to suitable database types and how to convert between the two. On the other hand, if a simple value is unknown to the ODB compiler then we will need to provide the - mapping to the database system type and, possibly, the code to + mapping to the database type and, possibly, the code to convert between the two. For more information on how to achieve this refer to the db type pragma description - in Section 12.3.1, "type". Similar - to object types, composite value types have to be explicitly declared - as persistent using the db value pragma, for example:

+ in Section 12.3.1, "type".

+ +

Similar to object classes, composite value types have to be + explicitly declared as persistent using the db value + pragma, for example:

 #pragma db value
@@ -2255,53 +2393,16 @@ class name
 };
   
-

Composite value types are discussed in more detail in +

Note that a composite value cannot have a data member designated + as an object id since, as we have discussed earlier, values do + not have a notion of identity. A composite value type also doesn't + have to define the default constructor, unless it is used as an + element of a container. The ODB compiler uses the same mechanisms + to access data members in composite value types as in object types. + Composite value types are discussed in more detail in Section 7.2, "Composite Value Types".

-

Normally, you would use object types to model real-world entities, - things that have their own identity. For example, in the - previous chapter we created a person class to model - a person, which is a real-world entity. Name and age, which we - used as data members in our person class are clearly - values. It is hard to think of age 31 or name "Joe" as having their - own identities.

- -

A good test to determine whether something is an object or - a value, is to consider if other objects might reference - it. A person is clearly an object because it can be referred - to by other objects such as a spouse, an employer, or a - bank. On the other hand, a person's age or name is not - something that other objects would normally refer to.

- -

Also, when an object represents a real entity, it is easy to - choose a suitable object id. For example, for a - person there is an established notion of an identifier - (SSN, student id, passport number, etc). Another alternative - is to use a person's email address as an identifier.

- -

Note, however, that these are only guidelines. There could - be good reasons to make something that would normally be - a value an object. Consider, for example, a database that - stores a vast number of people. Many of the person - objects in this database have the same names and surnames and - the overhead of storing them in every object may negatively - affect the performance. In this case, we could make the first name - and last name each an object and only store pointers to - these objects in the person class.

- -

An instance of a persistent class can be in one of two states: - transient and persistent. A transient - instance only has a representation in the application's - memory and will cease to exist when the application terminates, - unless it is explicitly made persistent. In other words, a - transient instance of a persistent class behaves just like an - instance of any ordinary C++ class. A persistent instance - has a representation in both the application's memory and the - database. A persistent instance will remain even after the - application terminates unless and until it is explicitly - deleted from the database.

- -

3.2 Object and View Pointers

+

3.3 Object and View Pointers

As we have seen in the previous chapter, some database operations create dynamically allocated instances of persistent classes and @@ -2422,7 +2523,7 @@ namespace accounting easy to add support for our own smart pointers, as described in Section 6.4, "Using Custom Smart Pointers".

-

3.3 Database

+

3.4 Database

Before an application can make use of persistence services offered by ODB, it has to create a database class instance. A @@ -2539,7 +2640,7 @@ namespace odb for various C++ constructs, refer to the schema/custom example in the odb-examples package.

-

3.4 Transactions

+

3.5 Transactions

A transaction is an atomic, consistent, isolated and durable (ACID) unit of work. Database operations can only be @@ -2652,7 +2753,7 @@ namespace odb

The database() accessor returns the database this transaction is working on. Similarly, the connection() accessor returns the database connection this transaction is on - (Section 3.5, "Connections").

+ (Section 3.6, "Connections").

The static current() accessor returns the currently active transaction for this thread. If there is no active @@ -2802,7 +2903,7 @@ update_age (database& db, person& p) } -

3.5 Connections

+

3.6 Connections

The odb::connection class represents a connection to the database. Normally, you wouldn't work with connections @@ -2856,7 +2957,7 @@ namespace odb on the connection. The execute() functions allow us to execute native database statements on the connection. Their semantics are equivalent to the database::execute() - functions (Section 3.11, "Executing Native SQL + functions (Section 3.12, "Executing Native SQL Statements") except that they can be legally called outside a transaction. Finally, the database() accessor returns a reference to the odb::database instance @@ -2902,7 +3003,7 @@ c->execute ("SET FOREIGN_KEY_CHECKS = 1"); sure that a set of transactions is executed on the same connection.

-

3.6 Error Handling and Recovery

+

3.7 Error Handling and Recovery

ODB uses C++ exceptions to report database operation errors. Most ODB exceptions signify hard errors or errors that cannot @@ -2913,7 +3014,7 @@ c->execute ("SET FOREIGN_KEY_CHECKS = 1"); instance, by obtaining a valid object id and trying again. The hard errors and corresponding ODB exceptions that can be thrown by each database function are described in the remainder - of this chapter with Section 3.13, "ODB Exceptions" + of this chapter with Section 3.14, "ODB Exceptions" providing a quick reference for all the ODB exceptions.

The second group of ODB exceptions signify soft or @@ -2980,7 +3081,7 @@ for (unsigned short retry_count (0); ; retry_count++) } -

3.7 Making Objects Persistent

+

3.8 Making Objects Persistent

A newly created instance of a persistent class is transient. We use the database::persist() function template @@ -3008,8 +3109,8 @@ for (unsigned short retry_count (0); ; retry_count++)

Here and in the rest of the manual, object_traits<T>::pointer_type and object_traits<T>::const_pointer_type denote the - unrestricted and constant object pointer types (Section - 3.2, "Object and View Pointers"), respectively. + unrestricted and constant object pointer types (Section + 3.3, "Object and View Pointers"), respectively. Similarly, object_traits<T>::id_type denotes the object id type. The odb::object_traits template is part of the database support code generated by the ODB compiler.

@@ -3066,7 +3167,7 @@ cerr << "Jane's id: " << jane_id << endl; threads in your application and to other applications as soon as possible.

-

3.8 Loading Persistent Objects

+

3.9 Loading Persistent Objects

Once an object is made persistent, and you know its object id, it can be loaded by the application using the database::load() @@ -3172,7 +3273,7 @@ t.commit (); identifier can be significantly faster than executing a query.

-

3.9 Updating Persistent Objects

+

3.10 Updating Persistent Objects

If a persistent object has been modified, we can store the updated state in the database using the database::update() @@ -3270,7 +3371,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.11, "readonly + Section 12.4.12, "readonly (data member)").

If an individual data member is declared read-only, then @@ -3285,7 +3386,7 @@ t.commit (); update() functions for such an object will result in a compile-time error.

-

3.10 Deleting Persistent Objects

+

3.11 Deleting Persistent Objects

To delete a persistent object's state from the database we use the database::erase() or database::erase_query() @@ -3420,7 +3521,7 @@ t.commit (); -

3.11 Executing Native SQL Statements

+

3.12 Executing Native SQL Statements

In some situations we may need to execute native SQL statements instead of using the object-oriented database API described above. @@ -3462,9 +3563,9 @@ t.commit (); it may be necessary to execute a native statement outside a transaction. This can be done using the connection::execute() functions as described in - Section 3.5, "Connections".

+ Section 3.6, "Connections".

-

3.12 Tracing SQL Statement Execution

+

3.13 Tracing SQL Statement Execution

Oftentimes it is useful to understand what SQL statements are executed as a result of high-level database operations. For @@ -3632,7 +3733,7 @@ odb::pgsql::database& db = ...; db.tracer (tracer); // Ok. -

3.13 ODB Exceptions

+

3.14 ODB Exceptions

In the previous sections we have already mentioned some of the exceptions that can be thrown by the database functions. In this @@ -3802,7 +3903,7 @@ namespace odb not_in_transaction, transaction_already_finalized) are thrown by the odb::transaction class and are discussed - in Section 3.4, "Transactions".

+ in Section 3.5, "Transactions".

The next two exceptions (already_in_session, and not_in_session) are thrown by the odb::session @@ -3823,20 +3924,20 @@ namespace odb has timed out. The deadlock exception is thrown when a transaction deadlock is detected by the database system. These exceptions can be thrown by any database function. See - Section 3.6, "Error Handling and Recovery" + Section 3.7, "Error Handling and Recovery" for details.

The object_already_persistent exception is thrown by the persist() database function. See - Section 3.7, "Making Objects Persistent" + Section 3.8, "Making Objects Persistent" for details.

The object_not_persistent exception is thrown by the load(), update(), and erase() database functions. Refer to - Section 3.8, "Loading Persistent Objects", - Section 3.9, "Updating Persistent Objects", and - Section 3.10, "Deleting Persistent Objects" for + Section 3.9, "Loading Persistent Objects", + Section 3.10, "Updating Persistent Objects", and + Section 3.11, "Deleting Persistent Objects" for more information.

The object_changed exception is thrown @@ -3872,7 +3973,7 @@ namespace odb

The unknown_schema exception is thrown by the odb::schema_catalog class if a schema with the specified - name is not found. Refer to Section 3.3, "Database" + name is not found. Refer to Section 3.4, "Database" for details.

The odb::exception class is defined in the @@ -4648,7 +4749,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.8, - "unordered", Section 12.4.16, + "unordered", Section 12.4.17, "unordered"). For example:

@@ -4862,7 +4963,7 @@ private:
      in  Section 6.4, "Using Custom Smart Pointers". Any
      supported smart pointer can be used in a data member as long as it can be
      explicitly constructed from the canonical object pointer
-     (Section 3.2, "Object and View Pointers").  For
+     (Section 3.3, "Object and View Pointers").  For
      example, we can use weak_ptr if the object pointer
      is shared_ptr.

@@ -4911,11 +5012,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.5, "null/not_null") for + use the not_null pragma (Section + 12.4.6, "null/not_null") for single object pointers and the value_not_null pragma - (Section - 12.4.21, "value_null/value_not_null") + (Section + 12.4.22, "value_null/value_not_null") for containers of object pointers. For example:

@@ -5328,7 +5429,7 @@ CREATE TABLE employee (
      of these references.

To eliminate redundant database schema references we can use the - inverse pragma (Section 12.4.12, + inverse pragma (Section 12.4.13, "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:

@@ -5372,7 +5473,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.16, "unordered") + (Section 12.4.17, "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).

@@ -5970,14 +6071,15 @@ class basic_name example in the odb-examples package.

A composite value type does not have to define a default constructor, - unless it is used as an element of a container, in which case the - default constructor can be made private. If a composite value type - has private or protected non-transient data members or if its - default constructor is not public and the value type is used as - an element of a container, then the odb::access class - should be declared a friend of this value type. For example:

+ unless it is used as an element of a container. In this case the + default constructor can be made private provided we also make the + odb::access class, defined in the + <odb/core.hxx> header, a friend of this value type. + For example:

+#include <odb/core.hxx>
+
 #pragma db value
 class basic_name
 {
@@ -5991,11 +6093,17 @@ private:
 
   basic_name () {} // Needed for storing basic_name in containers.
 
-  std::string first_;
-  std::string last_;
+  ...
 };
   
+

The ODB compiler also needs access to the non-transient + (Section 12.4.11, "transient") + data members of a composite value type. It uses the same mechanisms + as for persistent classes which are discussed in + Section 3.2, "Declaring Persistent Objects and + Values".

+

The members of a composite value can be other value types (either simple or composite), containers (Chapter 5, "Containers"), and pointers to objects (Chapter 6, @@ -6163,8 +6271,8 @@ class person

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.8, "column"). For composite value + the db column pragma (Section + 12.4.9, "column"). For composite value types things are slightly more complex since they are mapped to multiple columns. Consider the following example:

@@ -6265,9 +6373,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.29, "value_column") or + (Section 12.4.30, "value_column") or db key_column - (Section 12.4.28, "key_column") + (Section 12.4.29, "key_column") pragmas are used to specify the column prefix.

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

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

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

 #pragma db value
@@ -6353,7 +6461,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.5,
+     pragma (Section 12.4.6,
      "null/not_null").

To properly support the NULL semantics, the @@ -6566,11 +6674,8 @@ class person class person { public: - const std::string& - first () const; - - const std::string& - last () const; + const std::string& first () const; + const std::string& last () const; private: std::string first_; @@ -6901,7 +7006,7 @@ class contractor: public person a table in the database, just like non-abstract classes.

Persistent classes in the same polymorphic hierarchy must use the - same kind of object pointer (Section 3.2, + same kind of object pointer (Section 3.3, "Object and View Pointers"). If the object pointer for the root class is specified as a template or using the special raw pointer syntax (*), then the ODB @@ -7234,8 +7339,8 @@ struct contractor_manager };

-

The erase_query() database function (Section - 3.10, "Deleting Persistent Objects") also has limited functionality +

The erase_query() database function (Section + 3.11, "Deleting Persistent Objects") also has limited functionality when used on polymorphic objects. Because many database implementations do not support JOIN clauses in the SQL DELETE statement, only data members from the derived class being erased can @@ -7611,7 +7716,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.8, + db column pragma (Section 12.4.9, "column"). For example:

@@ -8506,17 +8611,17 @@ namespace odb
 
   

A session is an object cache. Every time a session-enabled object is made persistent by calling the database::persist() function - (Section 3.7, "Making Objects Persistent"), loaded + (Section 3.8, "Making Objects Persistent"), loaded by calling the database::load() or database::find() - function (Section 3.8, "Loading Persistent Objects"), + function (Section 3.9, "Loading Persistent Objects"), or loaded by iterating over a query result (Section 4.4, "Query Result"), the pointer to the persistent object, in the form - of the canonical object pointer (Section 3.2, "Object + of the canonical object pointer (Section 3.3, "Object and View Pointers"), is stored in the session. For as long as the session is in effect, any subsequent calls to load the same object will return the cached instance. When an object's state is deleted from the database with the database::erase() function - (Section 3.10, "Deleting Persistent Objects"), the + (Section 3.11, "Deleting Persistent Objects"), the cached object pointer is removed from the session. For example:

@@ -8590,7 +8695,7 @@ unsigned long id2 (save (db, p2)); // p2 is cached in s as non-const.
   

11 Optimistic Concurrency

-

The ODB transaction model (Section 3.4, +

The ODB transaction model (Section 3.5, "Transactions") guarantees consistency as long as we perform all the database operations corresponding to a specific application transaction in a single database transaction. That is, if we load an object within a @@ -8674,7 +8779,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.13, "version") + pragma (Section 12.4.14, "version") to specify which data member will store the object version. For example:

@@ -8713,7 +8818,7 @@ class person

When we call the database::update() function - (Section 3.9, "Updating Persistent Objects") and pass + (Section 3.10, "Updating Persistent Objects") and pass an object that has an outdated state, the odb::object_changed exception is thrown. At this point the application has two recovery options: it can abort and potentially restart the @@ -8778,7 +8883,7 @@ p.age (age);

In addition to updates, ODB also performs state mismatch detection when we are deleting an object from the database - (Section 3.10, "Deleting Persistent Objects"). + (Section 3.11, "Deleting Persistent Objects"). To understand why this can be important, consider the following application transaction:

@@ -9216,7 +9321,7 @@ class person used to specify the pointer, then the raw pointer is used by default.

For a more detailed discussion of object pointers, refer to - Section 3.2, "Object and View Pointers".

+ Section 3.3, "Object and View Pointers".

12.1.3 abstract

@@ -9255,7 +9360,7 @@ class contractor: public person

The readonly specifier specifies that the persistent class is read-only. The database state of read-only objects cannot be updated. In particular, this means that you cannot call the - database::update() function (Section 3.9, + database::update() function (Section 3.10, "Updating Persistent Objects") for such objects. For example:

@@ -9275,7 +9380,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.11, "readonly") + (Section 12.4.12, "readonly") as well as composite value types (Section 12.3.6, "readonly") as read-only.

@@ -9285,7 +9390,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.13, "version"). + (Section 12.4.14, "version"). For example:

@@ -9324,18 +9429,18 @@ class person
 
   

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

Furthermore, persistent classes without object ids cannot have container @@ -9660,8 +9765,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.9, "column (view)") has the + as the view column specifier (Section + 12.4.10, "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, @@ -10128,7 +10233,7 @@ typedef shared_ptr<person> person_ptr;

The NULL semantics can also be specified on the - per-member basis (Section 12.4.5, + per-member basis (Section 12.4.6, "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 @@ -10181,7 +10286,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.6, + data member (Section 12.4.7, "default").

12.3.5 options

@@ -10204,7 +10309,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.7, + data member (Section 12.4.8, "options").

12.3.6 readonly

@@ -10212,7 +10317,7 @@ class person

The readonly specifier specifies that the composite value type is read-only. Changes to data members of a read-only composite value type are ignored when updating the database - state of an object (Section 3.9, "Updating Persistent + state of an object (Section 3.10, "Updating Persistent Objects") containing such a value type. Note that this specifier is only valid for composite value types. For example:

@@ -10233,7 +10338,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.11, "readonly") + (Section 12.4.12, "readonly") as well as whole objects (Section 12.1.4, "readonly") as read-only.

@@ -10407,7 +10512,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.22, + a container data member (Section 12.4.23, "id_options").

@@ -10424,7 +10529,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.23, + a container data member (Section 12.4.24, "index_options").

@@ -10441,7 +10546,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.24, + a container data member (Section 12.4.25, "key_options").

@@ -10458,7 +10563,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.25, + a container data member (Section 12.4.26, "value_options").

@@ -10561,153 +10666,159 @@ typedef std::map<unsigned short, float> age_weight_map; + get/set/access + member accessor/modifier expressions + 12.4.5 + + + null/not_null member can/cannot be NULL - 12.4.5 + 12.4.6 default default value for a member - 12.4.6 + 12.4.7 options database options for a member - 12.4.7 + 12.4.8 column column name for a member of an object or composite value - 12.4.8 + 12.4.9 column column name for a member of a view - 12.4.9 + 12.4.10 transient member is not stored in the database - 12.4.10 + 12.4.11 readonly member is read-only - 12.4.11 + 12.4.12 inverse member is an inverse side of a bidirectional relationship - 12.4.12 + 12.4.13 version member stores object version - 12.4.13 + 12.4.14 index define database index for a member - 12.4.14 + 12.4.15 unique define unique database index for a member - 12.4.15 + 12.4.16 unordered ordered container should be stored unordered - 12.4.16 + 12.4.17 table table name for a container - 12.4.17 + 12.4.18 index_type database type for a container's index type - 12.4.18 + 12.4.19 key_type database type for a container's key type - 12.4.19 + 12.4.20 value_type database type for a container's value type - 12.4.20 + 12.4.21 value_null/value_not_null container's value can/cannot be NULL - 12.4.21 + 12.4.22 id_options database options for a container's id column - 12.4.22 + 12.4.23 index_options database options for a container's index column - 12.4.23 + 12.4.24 key_options database options for a container's key column - 12.4.24 + 12.4.25 value_options database options for a container's value column - 12.4.25 + 12.4.26 id_column column name for a container's object id - 12.4.26 + 12.4.27 index_column column name for a container's index - 12.4.27 + 12.4.28 key_column column name for a container's key - 12.4.28 + 12.4.29 value_column column name for a container's value - 12.4.29 + 12.4.30 @@ -10773,7 +10884,7 @@ class person is a safe choice.

For additional information on the automatic identifier assignment, - refer to Section 3.7, "Making Objects Persistent".

+ refer to Section 3.8, "Making Objects Persistent".

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

@@ -10794,8 +10905,8 @@ class person };
-

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

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

12.4.4 id_type

@@ -10831,7 +10942,234 @@ class person };
-

12.4.5 null/not_null

+

12.4.5 get/set/access

+ +

The get and set specifiers specify the + data member accessor and modifier expressions, respectively. If + provided, the generated database support code will use these + expressions to access and modify the data member when performing + database operations. The access specifier can be used + as a shortcut to specify both the accessor and modifier if they + happen to be the same.

+ +

In its simplest form the accessor or modifier expression can be + just a name. Such a name should resolve either to another data + member of the same type or to a suitable accessor or modifier + member function. For example:

+ +
+#pragma db object
+class person
+{
+  ...
+
+public:
+  const std::string& name () const;
+  void name (const std::string&);
+private:
+  #pragma db access(name)
+  std::string name_;
+};
+  
+ +

A suitable accessor function is a const member function + that takes no arguments and whose return value can be implicitly + converted to the const reference to the member type + (const std::string& in the example above). + An accessor function that returns a const reference + to the data member is called by-reference accessor. + Otherwise, it is called by-value accessor.

+ +

A suitable modifier function can be of two forms. It can be the + so called by-reference modifier which is a member function + that takes no arguments and returns a non-const reference + to the data member (std::string& in the example above). + Alternatively, it can be the so called by-value modifier which + is a member function taking a single argument — the new value + — that can be implicitly initialized from a variable of the member + type (std::string in the example above). The return value + of a by-value modifier, if any, is ignored. If both by-reference and + by-value modifiers are available, then ODB prefers the by-reference + version since it is more efficient. For example:

+ +
+#pragma db object
+class person
+{
+  ...
+
+public:
+  std::string get_name () const;      // By-value accessor.
+  std::string& set_name ();           // By-reference modifier.
+  void set_name (std::string const&); // By-value modifier.
+private:
+  #pragma db get(get_name) \ // Uses by-value accessor.
+             set(set_name)   // Uses by-reference modifier.
+  std::string name_;
+};
+  
+ +

Note that in many cases it is not necessary to specify accessor and + modifier functions explicitly since the ODB compiler will try to + discover them automatically in case the data member will be inaccessible + to the generated code. In particular, in both of the above examples + the ODB compiler would have successfully discovered the necessary + functions. For more information on this functionality, refer to + Section 3.2, "Declaring Persistent Objects and + Values".

+ +

Note also that by-value accessors and by-value modifiers cannot be + used for certain data members in certain situations. These limitations + are discussed in more detail later in this section.

+ +

Accessor and modifier expressions can be more elaborate than simple + names. An accessor expression is any C++ expression that can be + used to initialize a const reference to the member + type. Similar to accessor functions, which are just a special case + of accessor expressions, an accessor expression that evaluates to a + const reference to the data member is called + by-reference accessor expression. Otherwise, it is + called by-value accessor expression.

+ +

Modifier expressions can also be of two forms: by-reference + modifier expression and by-value modifier expression + (again, modifier functions are just a special case of modifier + expressions). A by-reference modifier expression is any C++ + expression that evaluates to the non-const reference + to the member type. A by-value modifier expression can be a + single or multiple (separated by semicolon) C++ statements + with the effect of setting the new member value.

+ +

There are two special placeholders that are recognized by the + ODB compiler in accessor and modifier expressions. The first + is the this keyword which denotes a reference + (note: not a pointer) to the persistent object. In accessor + expressions this reference is const while in + modifier expressions it is non-const. If an + expression does not contain the this placeholder, + then the ODB compiler automatically prefixes it with this. + sequence.

+ +

The second placeholder, the (?) sequence, is used + to denote the new value in by-value modifier expressions. The + ODB compiler replaces the question mark with the variable name, + keeping the surrounding parenthesis. The following example shows + a few more interesting accessor and modifier expressions:

+ +
+#pragma db value
+struct point
+{
+  point (int, int);
+
+  int x;
+  int y;
+};
+
+#pragma db object
+class person
+{
+  ...
+
+  public:
+    const char* name () const;
+    void name (const char*);
+  private:
+    #pragma db get(std::string (this.name ())) \
+               set(name ((?).c_str ())) // The same as this.name (...).
+    std::string name_;
+
+  public:
+    const std::unique_ptr<account>& acc () const;
+    void acc (std::unique_ptr<account>);
+  private:
+    #pragma db set(acc (std::move (?)))
+    std::unique_ptr<account> acc_;
+
+  public:
+    int loc_x () const
+    int loc_y () const
+    void loc_x (int);
+    void loc_y (int);
+  private:
+    #pragma db get(point (this.loc_x (), this.loc_y ()))    \
+               set(this.loc_x ((?).x); this.loc_y ((?).y))
+    point loc_;
+};
+  
+ +

When the data member is of an array type, then the terms "reference" + and "member type" in the above discussion should be replaced with + "pointer" and "array element type", respectively. That is, the accessor + expression for an array member is any C++ expression that can be + used to initialize a const pointer to the array + element type, and so on. The following example shows common + accessor and modifier signatures for array members:

+ +
+#pragma db object
+class person
+{
+  ...
+
+  public:
+    const char* id () const; // By-reference accessor.
+    void id (const char*);   // By-value modifier.
+  private:
+    char id_[16];
+
+  public:
+    const char* pub_key () const; // By-reference accessor.
+    char* pub_key ();             // By-reference modifier.
+  private:
+    char pub_key_[2048];
+};
+  
+ +

Accessor and modifier expressions can be used with data members + of simple value, composite value, container, and object pointer + types. They can also be used for data members in persistent classes, + composite value types, and views. There are, however, certain + limitations when it comes to using by-value accessor and modifier + expressions. First of all, if a by-value modifier is used, then + the data member type should be default-constructible. Furthermore, + a composite value type that has a container member cannot + be modified with a by-value modifier. Only a by-reference modifier + expression can be used. The ODB compiler will detect such cases + and issue diagnostics. For example:

+ +
+#pragma db value
+struct name
+{
+  std::string first_;
+  std::string last_;
+  std::vector<std::string> aliases_;
+};
+
+#pragma db object
+class person
+{
+  ...
+
+public:
+  const name& name () const;
+  void name (const name&);
+private:
+  #pragma db access(name) // Error: by-value modifier.
+  name name_;
+};
+  
+ +

In certain database systems it is also not possible to use by-value + accessor and modifier expression with certain database types. + The ODB compiler is only able to detect such cases and issue diagnostics + if you specified accessor/modifier function names as opposed to custom + expressions. For more information on these database and type-specific + limitations, refer to the "Limitations" sections in Part + II, "Database Systems".

+ +

12.4.6 null/not_null

The null and not_null specifiers specify that the data member can or cannot be NULL, respectively. @@ -10844,7 +11182,7 @@ class person of each value type. Consult the relevant documentation to find out more about the NULL semantics for such value types. A data member containing the object id (Section - 12.4.1, "id" ) is automatically treated as not + 12.4.1, "id") is automatically treated as not allowing a NULL value. Data members that allow NULL values are mapped in a relational database to columns that allow NULL values. For example:

@@ -10886,7 +11224,7 @@ class account discussion of the NULL semantics for object pointers, refer to Chapter 6, "Relationships".

-

12.4.6 default

+

12.4.7 default

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

@@ -10907,8 +11245,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.7, "options") instead. For example:

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

 enum gender {male, female, undisclosed};
@@ -10994,7 +11332,7 @@ class person
   

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

-

12.4.7 options

+

12.4.8 options

The options specifier specifies additional column definition options that should be used for the data member. For @@ -11042,9 +11380,9 @@ class person

ODB provides dedicated specifiers for specifying column types (Section 12.4.3, "type"), - NULL constraints (Section 12.4.5, + NULL constraints (Section 12.4.6, "null/not_null"), and default - values (Section 12.4.6, "default"). + values (Section 12.4.7, "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.

@@ -11052,7 +11390,7 @@ class person

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

-

12.4.8 column (object, composite value)

+

12.4.9 column (object, composite value)

The column specifier specifies the column name that should be used to store the data member of a persistent class @@ -11078,7 +11416,7 @@ class person the common data member name decorations, such as leading and trailing underscores, the m_ prefix, etc.

-

12.4.9 column (view)

+

12.4.10 column (view)

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

-

12.4.10 transient

+

12.4.11 transient

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

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

-

12.4.11 readonly

+

12.4.12 readonly

The readonly specifier specifies that the data member of an object or composite value type is read-only. Changes to a read-only data member are ignored when updating the database state of an object - (Section 3.9, "Updating Persistent Objects") + (Section 3.10, "Updating Persistent Objects") 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.12, + and inverse (Section 12.4.13, "inverse") data members are automatically treated as read-only and must not be explicitly declared as such. For example:

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

-

12.4.12 inverse

+

12.4.13 inverse

The inverse specifier specifies that the data member of an object pointer or a container of object pointers type is an @@ -11239,12 +11577,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.16, "unordered").

+ (Section 12.4.17, "unordered").

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

-

12.4.13 version

+

12.4.14 version

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

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

-

12.4.14 index

+

12.4.15 index

The index specifier instructs the ODB compiler to define a database index for the data member. For example:

@@ -11293,7 +11631,7 @@ class person

For more information on defining database indexes, refer to Section 12.6, "Index Definition Pragmas".

-

12.4.15 unique

+

12.4.16 unique

The index specifier instructs the ODB compiler to define a unique database index for the data member. For example:

@@ -11312,7 +11650,7 @@ class person

For more information on defining database indexes, refer to Section 12.6, "Index Definition Pragmas".

-

12.4.16 unordered

+

12.4.17 unordered

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

-

12.4.17 table

+

12.4.18 table

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

@@ -11383,7 +11721,7 @@ class person qualified names, refer to Section 12.1.8, "schema".

-

12.4.18 index_type

+

12.4.19 index_type

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

-

12.4.19 key_type

+

12.4.20 key_type

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

-

12.4.20 value_type

+

12.4.21 value_type

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

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

-

12.4.21 value_null/value_not_null

+

12.4.22 value_null/value_not_null

The value_null and value_not_null specifiers specify that a container's element value for the 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.5, "null/not_null"). + (Section 12.4.6, "null/not_null"). For example:

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

-

12.4.22 id_options

+

12.4.23 id_options

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

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

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

-

12.4.23 index_options

+

12.4.24 index_options

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

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

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

-

12.4.24 key_options

+

12.4.25 key_options

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

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

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

-

12.4.25 value_options

+

12.4.26 value_options

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

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

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

-

12.4.26 id_column

+

12.4.27 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 the data member. The semantics of id_column are similar to those of the column specifier - (Section 12.4.8, "column"). + (Section 12.4.9, "column"). For example:

@@ -11592,14 +11930,14 @@ class person
   

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

-

12.4.27 index_column

+

12.4.28 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 the data member. The semantics of index_column are similar to those of the column specifier - (Section 12.4.8, "column"). + (Section 12.4.9, "column"). For example:

@@ -11616,14 +11954,14 @@ class person
   

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

-

12.4.28 key_column

+

12.4.29 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 the data member. The semantics of key_column are similar to those of the column specifier - (Section 12.4.8, "column"). + (Section 12.4.9, "column"). For example:

@@ -11640,14 +11978,14 @@ class person
   

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

-

12.4.29 value_column

+

12.4.30 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 the data member. The semantics of value_column are similar to those of the column specifier - (Section 12.4.8, "column"). + (Section 12.4.9, "column"). For example:

@@ -11778,7 +12116,7 @@ namespace accounting
   

For a more detailed discussion of object and view pointers, refer - to Section 3.2, "Object and View Pointers".

+ to Section 3.3, "Object and View Pointers".

12.5.2 table

@@ -11959,9 +12297,9 @@ class object

ODB also offers a shortcut for defining an index with the default method and options for a single data member. Such an index can - be defined using the index (Section - 12.4.14, "index") or unique - (Section 12.4.15, "unique") + be defined using the index (Section + 12.4.15, "index") or unique + (Section 12.4.16, "unique") member specifier. For example:

@@ -12121,7 +12459,7 @@ class object
      The conversion expressions are regular expression substitutions.
      They must contain the special (?) placeholder which will
      be replaced with the actual value to be converted. Turning on SQL
-     statement tracing (Section 3.12, "Tracing SQL
+     statement tracing (Section 3.13, "Tracing SQL
      Statement Execution") can be useful for debugging conversion
      expressions. This allows you to see the substituted expressions
      as used in the actual statements.

@@ -12882,7 +13220,7 @@ namespace odb

For more information on the odb::connection interface, - refer to Section 3.5, "Connections". The first + refer to Section 3.6, "Connections". The first overloaded mysql::connection constructor establishes a new MySQL connection. The second constructor allows us to create a connection instance by providing an already connected @@ -13447,7 +13785,7 @@ auto_ptr<odb::database> db (

The begin_immediate() and begin_exclusive() functions are the SQLite-specific extensions to the standard odb::database::begin() function (see - Section 3.4, "Transactions"). They allow us + Section 3.5, "Transactions"). They allow us to start an immediate (BEGIN IMMEDIATE) and an exclusive (BEGIN EXCLUSIVE) SQLite transaction, respectively. For more information on the semantics of the immediate and exclusive @@ -13491,7 +13829,7 @@ namespace odb

For more information on the odb::connection interface, - refer to Section 3.5, "Connections". The first + refer to Section 3.6, "Connections". The first overloaded sqlite::connection constructor opens a new SQLite connection. The extra_flags argument can be used to specify extra sqlite3_open_v2() flags @@ -13774,7 +14112,7 @@ namespace odb SQLite results is the unavailability of the result::size() function. If you call this function on an SQLite query result, then the odb::result_not_cached exception - (Section 3.13, "ODB Exceptions") is always + (Section 3.14, "ODB Exceptions") is always thrown. Future versions of the SQLite ODB runtime library may add support for result caching.

@@ -13819,7 +14157,7 @@ class person

If foreign key constraints checking is disabled or not available, then inconsistencies in object relationships will not be detected. Furthermore, using the erase_query() function - (Section 3.10, "Deleting Persistent Objects") + (Section 3.11, "Deleting Persistent Objects") to delete persistent objects that contain containers will not work correctly. Container data for such objects will not be deleted.

@@ -13874,8 +14212,8 @@ CREATE TABLE Employee ( to distinguish between the duplicate primary key and other constraint violations. As a result, when making an object persistent, the SQLite ODB runtime will translate all constraint violation errors to the - object_already_persistent exception (Section - 3.13, "ODB Exceptions").

+ object_already_persistent exception (Section + 3.14, "ODB Exceptions").

14.5.5 Sharing of Queries

@@ -14276,7 +14614,7 @@ namespace odb

For more information on the odb::connection interface, - refer to Section 3.5, "Connections". The first + refer to Section 3.6, "Connections". The first overloaded pgsql::connection constructor establishes a new PostgreSQL connection. The second constructor allows us to create a connection instance by providing an already connected @@ -14519,7 +14857,7 @@ CREATE TABLE Employee ( constraint violations. As a result, when making an object persistent, the PostgreSQL ODB runtime will translate all unique constraint violation errors to the object_already_persistent exception - (Section 3.13, "ODB Exceptions").

+ (Section 3.14, "ODB Exceptions").

15.5.4 Date-Time Format

@@ -14715,7 +15053,7 @@ class object in the generated schema, columns of these types are declared as NULL even if explicitly declared as NOT NULL with the db not_null pragma - (Section 12.4.5, "null/not_null"), + (Section 12.4.6, "null/not_null"), except for primary key columns. This also means that for object ids that are mapped to these Oracle types, an empty string is an invalid value.

@@ -14986,7 +15324,7 @@ namespace odb

For more information on the odb::connection interface, refer - to Section 3.5, "Connections". The first overloaded + to Section 3.6, "Connections". The first overloaded oracle::connection constructor creates a new OCI service context. The OCI statement caching is enabled for the underlying session while the OCI connection pooling and session pooling are not used. The @@ -15299,7 +15637,7 @@ class long_class_name the uncached Oracle results is the unavailability of the result::size() function. If you call this function on an Oracle query result, then the odb::result_not_cached - exception (Section 3.13, "ODB Exceptions") is + exception (Section 3.14, "ODB Exceptions") is always thrown. Future versions of the Oracle ODB runtime library may add support for result caching.

@@ -15329,7 +15667,7 @@ CREATE TABLE Employee ( constraint violations. As a result, when making an object persistent, the Oracle ODB runtime will translate all unique constraint violation errors to the object_already_persistent exception - (Section 3.13, "ODB Exceptions").

+ (Section 3.14, "ODB Exceptions").

16.5.5 Large FLOAT and NUMBER Types

@@ -15371,6 +15709,17 @@ CREATE TABLE Employee ( by mapping them to one of the natively supported types, as discussed in Section 12.7, "Database Type Mapping Pragmas".

+

16.5.8 LOB Types and By-Value Accessors/Modifiers

+ +

As discussed in Section 12.4.5, + "get/set/access", by-value + accessor and modifier expressions cannot be used with data members + of Oracle large object (LOB) data types: BLOB, + CLOB, and NCLOB. The Oracle ODB runtime + uses streaming for reading/writing LOB data directly from/to + data members. As a result, by-reference accessors and modifiers + should be used for these data types.

+

16.6 Oracle Index Definitions

When the index pragma (Section 12.6, @@ -16011,7 +16360,7 @@ namespace odb

For more information on the odb::connection interface, refer - to Section 3.5, "Connections". The first overloaded + to Section 3.6, "Connections". The first overloaded mssql::connection constructor creates a new ODBC connection. The created connection is configured to use the manual commit mode with multiple active result sets (MARS) enabled. The second constructor allows @@ -16277,8 +16626,8 @@ namespace odb them. The only limitation of the uncached SQL Server results is the unavailability of the result::size() function. If you call this function on an SQL Server query result, then the - odb::result_not_cached exception (Section - 3.13, "ODB Exceptions") is always thrown. Future versions of the + odb::result_not_cached exception (Section + 3.14, "ODB Exceptions") is always thrown. Future versions of the SQL Server ODB runtime library may add support for result caching.

17.5.2 Foreign Key Constraints

@@ -16297,7 +16646,7 @@ namespace odb constraint violations. As a result, when making an object persistent, the SQL Server ODB runtime will translate all unique constraint violation errors to the object_already_persistent exception - (Section 3.13, "ODB Exceptions").

+ (Section 3.14, "ODB Exceptions").

17.5.4 Multithreaded Windows Applications

@@ -16312,7 +16661,7 @@ namespace odb

SQL Server always returns zero as the number of affected rows for DDL statements. In particular, this means that the - database::execute() (Section 3.11, + database::execute() (Section 3.12, "Executing Native SQL Statements") function will always return zero for such statements.

@@ -16334,6 +16683,16 @@ namespace odb by passing the --mssql-server-version 9.0 ODB compiler option.

+

17.5.7 Long Data and By-Value Accessors/Modifiers

+ +

As discussed in Section 12.4.5, + "get/set/access", by-value + accessor and modifier expressions cannot be used with data members + of long data types. The SQL Server ODB runtime uses streaming for + reading/writing long data directly from/to data members. As a result, + by-reference accessors and modifiers should be used for these data + types.

+

17.6 SQL Server Index Definitions

When the index pragma (Section 12.6, @@ -16453,7 +16812,7 @@ odb --profile boost/date-time ... system. All such exceptions derive from the odb::boost::exception class which in turn derives from the root of the ODB exception hierarchy, class odb::exception - (Section 3.13, "ODB Exceptions"). The + (Section 3.14, "ODB Exceptions"). The odb::boost::exception class is defined in the <odb/boost/exception.hxx> header file and has the same interface as odb::exception. The concrete exceptions @@ -16471,7 +16830,7 @@ odb --profile boost/date-time ...

The currently supported smart pointers are boost::shared_ptr and boost::weak_ptr. For more information on using smart pointers as pointers to objects and - views, refer to Section 3.2, "Object and View Pointers" + views, refer to Section 3.3, "Object and View Pointers" and Chapter 6, "Relationships". For more information on using smart pointers as pointers to values, refer to Section 7.3, "Pointers and NULL Value @@ -16523,7 +16882,7 @@ class employee

Besides providing persistence support for the above smart pointers, the smart-ptr sub-profile also changes the default - pointer (Section 3.2, "Object and View Pointers") + pointer (Section 3.3, "Object and View Pointers") to boost::shared_ptr. In particular, this means that database functions that return dynamically allocated objects and views will return them as boost::shared_ptr pointers. To override @@ -17008,7 +17367,7 @@ class person system. All such exceptions derive from the odb::qt::exception class which in turn derives from the root of the ODB exception hierarchy, class odb::exception - (Section 3.13, "ODB Exceptions"). The + (Section 3.14, "ODB Exceptions"). The odb::qt::exception class is defined in the <odb/qt/exception.hxx> header file and has the same interface as odb::exception. The concrete exceptions @@ -17297,7 +17656,7 @@ class Person

The currently supported smart pointers are QSharedPointer and QWeakPointer. For more information on using smart pointers as pointers to objects - and views, refer to Section 3.2, "Object and View + and views, refer to Section 3.3, "Object and View Pointers" and Chapter 6, "Relationships". For more information on using smart pointers as pointers to values, refer to Section 7.3, "Pointers and NULL Value @@ -17349,7 +17708,7 @@ class Employee

Besides providing persistence support for the above smart pointers, the smart-ptr sub-profile also changes the default - pointer (Section 3.2, "Object and View Pointers") + pointer (Section 3.3, "Object and View Pointers") to QSharedPointer. In particular, this means that database functions that return dynamically allocated objects and views will return them as QSharedPointer pointers. To override -- cgit v1.1