From af15cca60b6bb967383d1b70d19ed715c62a984c Mon Sep 17 00:00:00 2001
From: Boris Kolpackov The final bit of code in our example is the Let's now compile (Section 2.3, "Compiling and
@@ -1473,7 +1473,45 @@ mysql> select * from person;
mysql> quit
- In the next section we will see how to access persistent objects
+ Another way to get more insight into what's going on under the hood,
+ is to trace the SQL statements executed by ODB as a result of
+ each database operation. Here is how we can enable tracing just for
+ the duration of our transaction: With this modification our application now produces the following
+ output: 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".
+ In the next section we will see how to access persistent objects
from our application. The second group of ODB exceptions signify soft or
@@ -3233,7 +3271,175 @@ t.commit ();
Oftentimes it is useful to understand what SQL statement are
+ executed as a result of high-level database operations. For
+ example, we can use this information to figure out why certain
+ transactions don't produce desired results or why they take
+ longer than expected. While this information can usually be obtained from the database
+ logs, ODB provides an application-side SQL statement tracing
+ support that is both more convenient and finer-grained.
+ For example, in a typical situation that calls for tracing
+ we would like to see the SQL statements executed as a result
+ of a specific transaction. While it may be difficult to
+ extract such a subset of statements from the database logs,
+ it is easy to achieve with ODB tracing support: ODB allows us to specify a tracer on the database, connection,
+ and transaction levels. If specified for the database, then
+ all the statements executed on this database will be traced.
+ On the other hand, if a tracer is specified for the
+ connection, then only the SQL statements executed on this
+ connection will be traced. Similarly, a tracer specified
+ for a transaction will only show statements that are
+ executed as part of this transaction. All three classes
+ ( The first two The The The In addition to the common As an example, consider a more elaborate, PostgreSQL-specific
+ tracer implementation. Here we rely on the fact that the PostgreSQL
+ ODB runtime uses names to identify prepared statement and this
+ information can be obtained from the Note also that you can only set a database-specific tracer object
+ using a database-specific database instance, for example: In the previous sections we have already mentioned some of the
exceptions that can be thrown by the database functions. In this
@@ -11375,7 +11581,7 @@ namespace odb
SQLite results is the unavailability of the 3.9 Updating Persistent Objects 3.10 Deleting Persistent Objects
- 3.11 Executing Native SQL Statements
+ 3.12 ODB Exceptions
@@ -1436,7 +1436,7 @@ main (int argc, char* argv[])
3.13 ODB Exceptions catch
block that handles the database exceptions. We do this by catching
- the base ODB exception (Section 3.12, "ODB
+ the base ODB exception (Section 3.13, "ODB
Exceptions") and printing the diagnostics.
+ // Create a few persistent person objects.
+ //
+ {
+ ...
+
+ transaction t (db->begin ());
+
+ t.tracer (stderr_tracer);
+
+ // Make objects persistent and save their ids for later use.
+ //
+ john_id = db->persist (john);
+ jane_id = db->persist (jane);
+ joe_id = db->persist (joe);
+
+ t.commit ();
+ }
+
+
+
+INSERT INTO `person` (`id`,`first`,`last`,`age`) VALUES (?,?,?,?)
+INSERT INTO `person` (`id`,`first`,`last`,`age`) VALUES (?,?,?,?)
+INSERT INTO `person` (`id`,`first`,`last`,`age`) VALUES (?,?,?,?)
+
+
+ 2.5 Querying the Database for Objects
@@ -2682,7 +2720,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.12, "ODB Exceptions"
+ of this chapter with Section 3.13, "ODB Exceptions"
providing a quick reference for all the ODB exceptions.connection::execute()
functions as described in
Section 3.5, "Connections".3.12 ODB Exceptions
+ 3.12 Tracing SQL Statement Execution
+
+
+transaction t (db.begin ());
+t.tracer (stderr_tracer);
+
+...
+
+t.commit ();
+
+
+ odb::database
, odb::connection
,
+ and odb::transaction
) provide the identical
+ tracing API:
+ void
+ tracer (odb::tracer&);
+
+ void
+ tracer (odb::tracer*);
+
+ odb::tracer*
+ tracer () const;
+
+
+ tracer()
functions allow us to set
+ the tracer object with the second one allowing us to clear the
+ current tracer by passing a NULL
pointer. The
+ last tracer()
function allow us to get the
+ current tracer object. It returns a NULL
pointer
+ if there is no tracer in effect. Note that the tracing API
+ does not manage the lifetime of the tracer object. The tracer
+ should be valid for as long as it is being used. Furthermore,
+ the tracing API is not thread-safe. Trying to set a tracer
+ from multiple threads simultaneously will result in
+ undefined behavior.odb::tracer
class defines a callback interface
+ that can be used to create custom tracer implementations. The
+ odb::stderr_tracer
is a built-in tracer implementation
+ provided by the ODB runtime. It prints each executed SQL statement
+ to the standard error stream.odb::tracer
class is defined in the
+ <odb/tracer.hxx>
header file which you will need to
+ include in order to make this class available in your application.
+ The odb::tracer
interface provided the following
+ callback functions:
+namespace odb
+{
+ class tracer
+ {
+ public:
+ virtual void
+ prepare (connection&, const statement&);
+
+ virtual void
+ execute (connection&, const statement&);
+
+ virtual void
+ execute (connection&, const char* statement) = 0;
+
+ virtual void
+ deallocate (connection&, const statement&);
+ };
+}
+
+
+ prepare()
and deallocate()
functions
+ are called when a prepared statement is created and destroyed,
+ respectively. The first execute()
function is called
+ when a prepared statement is executed while the second one is called
+ when a normal statement is executed. The default implementations
+ for the prepare()
and deallocate()
+ functions do nothing while the first execute()
function
+ calls the second one passing the statement text as the second
+ argument. As a result, if all you are interested in are the
+ SQL statements being executed, that you only need to override the
+ second execute()
function.odb::tracer
interface,
+ each database runtime provides a database-specific version
+ as odb::<database>::tracer
. It has exactly
+ the same interface as the common version except that the
+ connection
and statement
types
+ are database-specific, which gives us access to additional,
+ database-specific information.odb::pgsql::statement
+ object:
+#include <odb/pgsql/tracer.hxx>
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/statement.hxx>
+
+class pgsql_tracer: public odb::pgsql::tracer
+{
+ virtual void
+ prepare (odb::pgsql::connection& c, const odb::pgsql::statement& s)
+ {
+ cerr << c.database ().db () << ": PREPARE " << s.name ()
+ << " AS " << s.text () << endl;
+ }
+
+ virtual void
+ execute (odb::pgsql::connection& c, const odb::pgsql::statement& s)
+ {
+ cerr << c.database ().db () << ": EXECUTE " << s.name () << endl;
+ }
+
+ virtual void
+ execute (odb::pgsql::connection& c, const char* statement)
+ {
+ cerr << c.database ().db () << ": " << statement << endl;
+ }
+
+ virtual void
+ deallocate (odb::pgsql::connection& c, const odb::pgsql::statement& s)
+ {
+ cerr << c.database ().db () << ": DEALLOCATE " << s.name () << endl;
+ }
+};
+
+
+
+pgsql_tracer tracer;
+
+odb::database& db = ...;
+db.tracer (tracer); // Compile error.
+
+odb::pgsql::database& db = ...;
+db.tracer (tracer); // Ok.
+
+
+ 3.13 ODB Exceptions
result::size()
function. If you call this function on an SQLite query result, then
the odb::result_not_cached
exception
- (Section 3.12, "ODB Exceptions") is always
+ (Section 3.13, "ODB Exceptions") is always
thrown. Future versions of the SQLite ODB runtime library may add support
for result caching.object_not_persistent
exception (Section
- 3.12, "ODB Exceptions").
object_not_persistent
exception (Section
+ 3.13, "ODB Exceptions").
odb::boost::exception
class which in turn derives from
the root of the ODB exception hierarchy, class odb::exception
- (Section 3.12, "ODB Exceptions"). The
+ (Section 3.13, "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
@@ -12617,7 +12823,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.12, "ODB Exceptions"). The
+ (Section 3.13, "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
--
cgit v1.1