aboutsummaryrefslogtreecommitdiff
path: root/odb
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2021-04-29 09:05:42 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2021-04-30 12:25:45 +0200
commit64f60cfa94d730ea5d6a9e7bc22a8d706f73d53c (patch)
tree1de8d43fa0da0ca9005c459461a8c303e74322ba /odb
parentfa3f14db14671b4c37c400880d097b17aa12b824 (diff)
Add support for SQLite ATTACH DATABASE functionality
Diffstat (limited to 'odb')
-rw-r--r--odb/sqlite/connection-factory.cxx118
-rw-r--r--odb/sqlite/connection-factory.hxx32
-rw-r--r--odb/sqlite/connection.cxx45
-rw-r--r--odb/sqlite/connection.hxx109
-rw-r--r--odb/sqlite/connection.ixx30
-rw-r--r--odb/sqlite/database.cxx31
-rw-r--r--odb/sqlite/database.hxx79
-rw-r--r--odb/sqlite/database.ixx16
-rw-r--r--odb/sqlite/makefile1
-rw-r--r--odb/sqlite/prepared-query.cxx12
-rw-r--r--odb/sqlite/prepared-query.hxx3
-rw-r--r--odb/sqlite/statement.cxx38
-rw-r--r--odb/sqlite/transaction-impl.cxx58
-rw-r--r--odb/sqlite/transaction-impl.hxx9
14 files changed, 547 insertions, 34 deletions
diff --git a/odb/sqlite/connection-factory.cxx b/odb/sqlite/connection-factory.cxx
index 1a1f85a..0bbf617 100644
--- a/odb/sqlite/connection-factory.cxx
+++ b/odb/sqlite/connection-factory.cxx
@@ -1,6 +1,8 @@
// file : odb/sqlite/connection-factory.cxx
// license : GNU GPL v2; see accompanying LICENSE file
+#include <cassert>
+
#include <odb/details/lock.hxx>
#include <odb/sqlite/database.hxx>
@@ -295,5 +297,121 @@ namespace odb
pooled_connection* c (static_cast<pooled_connection*> (arg));
return static_cast<connection_pool_factory&> (c->factory_).release (c);
}
+
+ //
+ // default_attached_connection_factory
+ //
+
+ void default_attached_connection_factory::
+ detach ()
+ {
+ // Note that this function may be called several times, for example, in
+ // case of detach_database() failure.
+ //
+ if (attached_connection_ != 0)
+ {
+ // We should hold the last reference to the attached connection.
+ //
+ assert (attached_connection_.count () == 1);
+
+ // While it may seem like a good idea to also invalidate query results
+ // and reset active statements, if any such result/statement is still
+ // alive, then there would be bigger problems since it would have a
+ // dangling reference to the connection. In a way, this's the same
+ // reason we don't do it in the connection destructor.
+
+ // Remove ourselves from the active object list of the main
+ // connection.
+ //
+ if (next_ != this) // Might have already been done.
+ list_remove ();
+
+ const string& s (database ().schema ());
+
+ if (s != "main" && s != "temp")
+ main_factory ().detach_database (main_connection_, s);
+
+ // Explicitly free the attached connection so that we don't try to
+ // redo this.
+ //
+ attached_connection_.reset ();
+ }
+ }
+
+ default_attached_connection_factory::
+ ~default_attached_connection_factory ()
+ {
+ if (attached_connection_ != 0)
+ {
+ // This can throw. Ignoring the failure to detach seems like the most
+ // sensible thing to do here.
+ //
+ try{ detach (); } catch (...) {}
+ }
+ }
+
+ connection_ptr default_attached_connection_factory::
+ connect ()
+ {
+ return attached_connection_;
+ }
+
+ void default_attached_connection_factory::
+ database (database_type& db)
+ {
+ attached_connection_factory::database (db);
+
+ if (!attached_connection_)
+ {
+ const string& s (db.schema ());
+
+ if (s != "main" && s != "temp")
+ main_factory ().attach_database (main_connection_, db.name (), s);
+
+ attached_connection_.reset (
+ new (shared) connection (*this,
+ s != "main" ? &translate_statement : 0));
+
+ // Add ourselves to the active object list of the main connection.
+ //
+ list_add ();
+ }
+ }
+
+ void default_attached_connection_factory::
+ clear ()
+ {
+ attached_connection_->clear ();
+ }
+
+ void default_attached_connection_factory::
+ translate_statement (string& r,
+ const char* text,
+ size_t text_size,
+ connection& conn)
+ {
+ r.assign (text, text_size);
+
+ // Things will fall apart if any of the statements we translate use
+ // "main" as a table alias. So we have this crude check even though it
+ // means we cannot use "main" for other aliases (e.g., column).
+ //
+ assert (r.find ("AS \"main\"") == string::npos);
+
+ const string& s (conn.database ().schema ());
+ for (size_t p (0); (p = r.find ("\"main\".", p, 7)) != string::npos; )
+ {
+ // Verify the preceding character.
+ //
+ if (p != 0 && r[p - 1] == '.')
+ {
+ p += 7;
+ continue;
+ }
+
+ r.replace (p + 1, 4, s);
+ p += s.size () + 3;
+ }
+ }
}
}
diff --git a/odb/sqlite/connection-factory.hxx b/odb/sqlite/connection-factory.hxx
index 141fff6..0d01b85 100644
--- a/odb/sqlite/connection-factory.hxx
+++ b/odb/sqlite/connection-factory.hxx
@@ -233,6 +233,38 @@ namespace odb
details::mutex mutex_;
details::condition cond_;
};
+
+ class LIBODB_SQLITE_EXPORT default_attached_connection_factory:
+ public attached_connection_factory
+ {
+ public:
+ explicit
+ default_attached_connection_factory (const connection_ptr& main)
+ : attached_connection_factory (main) {}
+
+ using attached_connection_factory::database; // Accessor.
+
+ virtual void
+ database (database_type&);
+
+ virtual connection_ptr
+ connect ();
+
+ // Active object interface.
+ //
+ virtual void
+ clear ();
+
+ virtual void
+ detach ();
+
+ virtual
+ ~default_attached_connection_factory ();
+
+ protected:
+ static void
+ translate_statement (std::string&, const char*, std::size_t, connection&);
+ };
}
}
diff --git a/odb/sqlite/connection.cxx b/odb/sqlite/connection.cxx
index 57ce860..bb71274 100644
--- a/odb/sqlite/connection.cxx
+++ b/odb/sqlite/connection.cxx
@@ -30,8 +30,11 @@ namespace odb
namespace sqlite
{
connection::
- connection (connection_factory& cf, int extra_flags)
+ connection (connection_factory& cf,
+ int extra_flags,
+ statement_translator* st)
: odb::connection (cf),
+ statement_translator_ (st),
unlock_cond_ (unlock_mutex_),
active_objects_ (0)
{
@@ -83,9 +86,12 @@ namespace odb
}
connection::
- connection (connection_factory& cf, sqlite3* handle)
+ connection (connection_factory& cf,
+ sqlite3* handle,
+ statement_translator* st)
: odb::connection (cf),
handle_ (handle),
+ statement_translator_ (st),
unlock_cond_ (unlock_mutex_),
active_objects_ (0)
{
@@ -119,6 +125,25 @@ namespace odb
}
connection::
+ connection (attached_connection_factory& cf, statement_translator* st)
+ : odb::connection (cf),
+ handle_ (0),
+ statement_translator_ (st),
+ unlock_cond_ (unlock_mutex_),
+ active_objects_ (0)
+ {
+ // Copy some things over from the main connection.
+ //
+ connection& main (*cf.main_connection_);
+
+ tracer_ = main.tracer_;
+
+ // Create statement cache.
+ //
+ statement_cache_.reset (new statement_cache_type (*this));
+ }
+
+ connection::
~connection ()
{
// Destroy prepared query statements before freeing the connections.
@@ -213,7 +238,7 @@ namespace odb
// unlock_notify() returns SQLITE_OK or SQLITE_LOCKED (deadlock).
//
- int e (sqlite3_unlock_notify (handle_,
+ int e (sqlite3_unlock_notify (handle (),
&odb_sqlite_connection_unlock_callback,
this));
if (e == SQLITE_LOCKED)
@@ -261,6 +286,20 @@ namespace odb
odb::connection_factory::db_ = &db;
db_ = &db;
}
+
+ void connection_factory::
+ attach_database (const connection_ptr& conn,
+ const std::string& name,
+ const std::string& schema)
+ {
+ conn->execute ("ATTACH DATABASE '" + name + "' AS \"" + schema + '"');
+ }
+
+ void connection_factory::
+ detach_database (const connection_ptr& conn, const std::string& schema)
+ {
+ conn->execute ("DETACH DATABASE \"" + schema + '"');
+ }
}
}
diff --git a/odb/sqlite/connection.hxx b/odb/sqlite/connection.hxx
index 30bb467..f8df9d0 100644
--- a/odb/sqlite/connection.hxx
+++ b/odb/sqlite/connection.hxx
@@ -32,13 +32,14 @@ namespace odb
class statement_cache;
class generic_statement;
class connection_factory;
+ class attached_connection_factory;
class connection;
typedef details::shared_ptr<connection> connection_ptr;
// SQLite "active object", i.e., an object that needs to be
// "cleared" before the transaction can be committed and the
- // connection release. These form a doubly-linked list.
+ // connection released. These form a doubly-linked list.
//
class LIBODB_SQLITE_EXPORT active_object
{
@@ -77,15 +78,42 @@ namespace odb
typedef sqlite::statement_cache statement_cache_type;
typedef sqlite::database database_type;
+ // Translate the database schema in statement text (used to implement
+ // attached databases). If the result is empty, then no translation is
+ // required and the original text should be used as is.
+ //
+ typedef void (statement_translator) (std::string& result,
+ const char* text,
+ std::size_t text_size,
+ connection&);
virtual
~connection ();
- connection (connection_factory&, int extra_flags = 0);
- connection (connection_factory&, sqlite3* handle);
+ connection (connection_factory&,
+ int extra_flags = 0,
+ statement_translator* = 0);
+
+ connection (connection_factory&,
+ sqlite3* handle,
+ statement_translator* = 0);
+
+ // Create an attached connection (see the attached database constructor
+ // for details).
+ //
+ connection (attached_connection_factory&, statement_translator*);
database_type&
database ();
+ // Return the main connection of an attached connection. If this
+ // connection is main, return itself.
+ //
+ connection&
+ main_connection ();
+
+ static connection_ptr
+ main_connection (const connection_ptr&);
+
public:
virtual transaction_impl*
begin ();
@@ -142,10 +170,7 @@ namespace odb
public:
sqlite3*
- handle ()
- {
- return handle_;
- }
+ handle ();
statement_cache_type&
statement_cache ()
@@ -167,6 +192,8 @@ namespace odb
clear ();
public:
+ // Note: only available on main connection.
+ //
generic_statement&
begin_statement ();
@@ -182,6 +209,12 @@ namespace odb
generic_statement&
rollback_statement ();
+ protected:
+ friend class attached_connection_factory;
+
+ connection_factory&
+ factory ();
+
private:
connection (const connection&);
connection& operator= (const connection&);
@@ -191,8 +224,13 @@ namespace odb
init ();
private:
+ // Note that we use NULL handle as an indication of an attached
+ // connection.
+ //
auto_handle<sqlite3> handle_;
+ statement_translator* statement_translator_;
+
// Keep statement_cache_ after handle_ so that it is destroyed before
// the connection is closed.
//
@@ -218,6 +256,7 @@ namespace odb
connection_unlock_callback (void**, int);
private:
+ friend class statement; // statement_translator_
friend class transaction_impl; // invalidate_results()
// Linked list of active objects currently associated
@@ -248,12 +287,68 @@ namespace odb
connection_factory (): db_ (0) {}
+ // Attach/detach additional databases. Connection is one of the main
+ // connections created by this factory. Note: not called for "main" and
+ // "temp" schemas.
+ //
+ // The default implementations simply execute the ATTACH DATABASE and
+ // DETACH DATABASE SQLite statements.
+ //
+ virtual void
+ attach_database (const connection_ptr&,
+ const std::string& name,
+ const std::string& schema);
+
+ virtual void
+ detach_database (const connection_ptr&, const std::string& schema);
+
// Needed to break the circular connection_factory-database dependency
// (odb::connection_factory has the odb::database member).
//
protected:
database_type* db_;
};
+
+ // The call to database() should cause ATTACH DATABASE (or otherwise make
+ // sure the database is attached). Destruction of the factory should cause
+ // DETACH DATABASE (or otherwise notice that this factory no longer needs
+ // the database attached).
+ //
+ // Note that attached_connection_factory is an active object that
+ // registers itself with the main connection in order to get notified on
+ // transaction finalization.
+ //
+ class LIBODB_SQLITE_EXPORT attached_connection_factory:
+ public connection_factory,
+ public active_object
+ {
+ public:
+ explicit
+ attached_connection_factory (const connection_ptr& main)
+ : active_object (*main), main_connection_ (main) {}
+
+ virtual void
+ detach () = 0;
+
+ protected:
+ friend class database;
+ friend class connection;
+ friend class transaction_impl;
+
+ connection_factory&
+ main_factory ()
+ {
+ return main_connection_->factory ();
+ }
+
+ // Note that this essentially establishes a "protocol" for all the
+ // attached connection factory implementations: they hold a counted
+ // reference to the main connection and they maintain a single shared
+ // attached connection.
+ //
+ connection_ptr main_connection_;
+ connection_ptr attached_connection_;
+ };
}
}
diff --git a/odb/sqlite/connection.ixx b/odb/sqlite/connection.ixx
index 69739b7..c0e49b9 100644
--- a/odb/sqlite/connection.ixx
+++ b/odb/sqlite/connection.ixx
@@ -37,6 +37,36 @@ namespace odb
return static_cast<connection_factory&> (factory_).database ();
}
+ inline connection& connection::
+ main_connection ()
+ {
+ return handle_ != 0
+ ? *this
+ : *static_cast<attached_connection_factory&> (factory_).main_connection_;
+ }
+
+ inline connection_ptr connection::
+ main_connection (const connection_ptr& c)
+ {
+ return c->handle_ != 0
+ ? c
+ : static_cast<attached_connection_factory&> (c->factory_).main_connection_;
+ }
+
+ inline sqlite3* connection::
+ handle ()
+ {
+ return handle_ != 0
+ ? handle_
+ : static_cast<attached_connection_factory&> (factory_).main_connection_->handle_;
+ }
+
+ inline connection_factory& connection::
+ factory ()
+ {
+ return static_cast<connection_factory&> (factory_);
+ }
+
template <typename T>
inline prepared_query<T> connection::
prepare_query (const char* n, const char* q)
diff --git a/odb/sqlite/database.cxx b/odb/sqlite/database.cxx
index c15c633..a7cf098 100644
--- a/odb/sqlite/database.cxx
+++ b/odb/sqlite/database.cxx
@@ -151,8 +151,35 @@ namespace odb
factory_->database (*this);
}
+ database::
+ database (const connection_ptr& conn,
+ const string& name,
+ const string& schema,
+ transfer_ptr<attached_connection_factory> factory)
+ : odb::database (id_sqlite),
+ name_ (name),
+ schema_ (schema),
+ flags_ (0),
+ factory_ (factory.transfer ())
+ {
+ assert (!schema_.empty ());
+
+ // Copy some things over from the connection's database.
+ //
+ database& db (conn->database ());
+
+ tracer_ = db.tracer_;
+ foreign_keys_ = db.foreign_keys_;
+
+ if (!factory_)
+ factory_.reset (new default_attached_connection_factory (
+ connection::main_connection (conn)));
+
+ factory_->database (*this);
+ }
+
void database::
- print_usage (std::ostream& os)
+ print_usage (ostream& os)
{
details::options::print_usage (os);
}
@@ -196,7 +223,7 @@ namespace odb
else if (!schema_version_table_.empty ())
text += schema_version_table_; // Already quoted.
else
- text += "\"schema_version\"";
+ text += "\"main\".\"schema_version\"";
text += " WHERE \"name\" = ?";
diff --git a/odb/sqlite/database.hxx b/odb/sqlite/database.hxx
index e4c9e78..478fbdb 100644
--- a/odb/sqlite/database.hxx
+++ b/odb/sqlite/database.hxx
@@ -83,6 +83,69 @@ namespace odb
details::transfer_ptr<connection_factory> =
details::transfer_ptr<connection_factory> ());
+ // Attach to the specified connection a database with the specified name
+ // as the specified schema. Good understanding of SQLite ATTACH/DETACH
+ // DATABASE semantics and ODB connection management is recommended when
+ // using this mechanism.
+ //
+ // The resulting database instance is referred to as an "attached
+ // database" and the connection it returns as an "attached connection"
+ // (which is just a proxy for the main connection). Database operations
+ // executed on the attached database or attached connection are
+ // automatically translated to refer to the specified schema rather than
+ // "main". For uniformity attached databases can also be created for the
+ // pre-attached "main" and "temp" schemas (in this case name can be
+ // anything).
+ //
+ // The main connection and attached to it databases and connections are
+ // all meant to be used withing the same thread. In particular, the
+ // attached database holds a counted reference to the main connection
+ // which means the connection will not be released until all the
+ // attached to this connection databases are destroyed.
+ //
+ // Note that in this model the attached databases are attached to the
+ // main connection, not to the (main) database, which mimics the
+ // underlying semantics of SQLite. An alternative model would have been
+ // to notionally attach the databases to the main database and under the
+ // hood automatically attach them to each returned connecton. While this
+ // may seem like a more convenient model in some cases, it is also less
+ // flexible: the current model allows attaching a different set of
+ // databases to different connections, attaching them on demand as the
+ // transaction progresses, etc. Also, the more convenient model can be
+ // implemented on top this model by deriving an aplication-specific
+ // database class and/or providing custom connection factories.
+ //
+ // Note also that unless the name is a URI with appropriate mode, it is
+ // opened with the SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE flags. So if
+ // you want just SQLITE_OPEN_READWRITE, then you will need to verify its
+ // existence manually prior to calling this constructor.
+ //
+ // The automatic translation of the statements relies on all the
+ // statements text having references to top-level database entities
+ // (tables, indexes, etc) qualified with the "main" schema. To achieve
+ // this, compile your headers with `--schema main` and, if using schema
+ // migration, with `--schema-version-table main.schema_version`. You
+ // must also not use "main" as a table alias.
+ //
+ database (const connection_ptr&,
+ const std::string& name,
+ const std::string& schema,
+ details::transfer_ptr<attached_connection_factory> =
+ details::transfer_ptr<attached_connection_factory> ());
+
+ // The database is automatically detached on destruction but a failure
+ // to detach is ignored. To detect such a failure perform explicit
+ // detach. For uniformity detaching a main database is a no-op.
+ //
+ void
+ detach ();
+
+ // Return the main database of an attached database. If this database
+ // is main, return itself.
+ //
+ database&
+ main_database ();
+
// Move-constructible but not move-assignable.
//
#ifdef ODB_CXX11
@@ -99,6 +162,15 @@ namespace odb
return name_;
}
+ // Schema name under which this database was attached or empty for the
+ // main database.
+ //
+ const std::string&
+ schema () const
+ {
+ return schema_;
+ }
+
int
flags () const
{
@@ -463,12 +535,19 @@ namespace odb
connection_ ();
private:
+ friend class transaction_impl; // factory_
+
// Note: remember to update move ctor if adding any new members.
//
std::string name_;
+ std::string schema_;
int flags_;
bool foreign_keys_;
std::string vfs_;
+
+ // Note: keep last so that all other database members are still valid
+ // during factory's destruction.
+ //
details::unique_ptr<connection_factory> factory_;
};
}
diff --git a/odb/sqlite/database.ixx b/odb/sqlite/database.ixx
index d5302a1..e906a39 100644
--- a/odb/sqlite/database.ixx
+++ b/odb/sqlite/database.ixx
@@ -14,6 +14,7 @@ namespace odb
database (database&& db) // Has to be inline.
: odb::database (std::move (db)),
name_ (std::move (db.name_)),
+ schema_ (std::move (db.schema_)),
flags_ (db.flags_),
foreign_keys_ (db.foreign_keys_),
vfs_ (std::move (db.vfs_)),
@@ -23,6 +24,21 @@ namespace odb
}
#endif
+ inline void database::
+ detach ()
+ {
+ if (!schema_.empty ())
+ static_cast<attached_connection_factory&> (*factory_).detach ();
+ }
+
+ inline database& database::
+ main_database ()
+ {
+ return schema_.empty ()
+ ? *this
+ : static_cast<attached_connection_factory&> (*factory_).main_connection_->database ();
+ }
+
inline connection_ptr database::
connection ()
{
diff --git a/odb/sqlite/makefile b/odb/sqlite/makefile
index 13a3c40..cead138 100644
--- a/odb/sqlite/makefile
+++ b/odb/sqlite/makefile
@@ -16,7 +16,6 @@ query-const-expr.cxx \
simple-object-statements.cxx \
statement.cxx \
statements-base.cxx \
-statement-cache.cxx \
stream.cxx \
tracer.cxx \
traits.cxx \
diff --git a/odb/sqlite/prepared-query.cxx b/odb/sqlite/prepared-query.cxx
index ed63191..79df0f2 100644
--- a/odb/sqlite/prepared-query.cxx
+++ b/odb/sqlite/prepared-query.cxx
@@ -3,6 +3,8 @@
#include <odb/sqlite/prepared-query.hxx>
+#include <odb/sqlite/connection.hxx>
+
namespace odb
{
namespace sqlite
@@ -11,5 +13,15 @@ namespace odb
~prepared_query_impl ()
{
}
+
+ bool prepared_query_impl::
+ verify_connection (odb::transaction& t)
+ {
+ // The transaction can be started using the main database of any of the
+ // attached databases. So we verify the main connections match.
+ //
+ return &static_cast<connection&> (t.connection ()).main_connection () ==
+ &static_cast<connection&> (stmt->connection ()).main_connection ();
+ }
}
}
diff --git a/odb/sqlite/prepared-query.hxx b/odb/sqlite/prepared-query.hxx
index 4938ff3..a8873a5 100644
--- a/odb/sqlite/prepared-query.hxx
+++ b/odb/sqlite/prepared-query.hxx
@@ -24,6 +24,9 @@ namespace odb
prepared_query_impl (odb::connection& c): odb::prepared_query_impl (c) {}
+ virtual bool
+ verify_connection (odb::transaction&);
+
sqlite::query_base query;
};
}
diff --git a/odb/sqlite/statement.cxx b/odb/sqlite/statement.cxx
index 8f93a9f..b1b0f58 100644
--- a/odb/sqlite/statement.cxx
+++ b/odb/sqlite/statement.cxx
@@ -27,7 +27,7 @@ namespace odb
{
{
odb::tracer* t;
- if ((t = conn_.transaction_tracer ()) ||
+ if ((t = conn_.main_connection ().transaction_tracer ()) ||
(t = conn_.tracer ()) ||
(t = conn_.database ().tracer ()))
t->deallocate (conn_, *this);
@@ -55,27 +55,27 @@ namespace odb
{
active_ = false;
- string tmp;
+ string tmp1;
if (proc != 0)
{
switch (sk)
{
case statement_select:
- process_select (tmp,
+ process_select (tmp1,
text,
&proc->bind->buffer, proc->count, sizeof (bind),
'"', '"',
optimize);
break;
case statement_insert:
- process_insert (tmp,
+ process_insert (tmp1,
text,
&proc->bind->buffer, proc->count, sizeof (bind),
'?',
'$');
break;
case statement_update:
- process_update (tmp,
+ process_update (tmp1,
text,
&proc->bind->buffer, proc->count, sizeof (bind),
'?',
@@ -86,8 +86,20 @@ namespace odb
assert (false);
}
- text = tmp.c_str ();
- text_size = tmp.size ();
+ text = tmp1.c_str ();
+ text_size = tmp1.size ();
+ }
+
+ string tmp2;
+ if (conn_.statement_translator_ != 0)
+ {
+ conn_.statement_translator_ (tmp2, text, text_size, conn_);
+
+ if (!tmp2.empty ())
+ {
+ text = tmp2.c_str ();
+ text_size = tmp2.size ();
+ }
}
#if SQLITE_VERSION_NUMBER < 3005003
@@ -101,7 +113,7 @@ namespace odb
{
odb::tracer* t;
- if ((t = conn_.transaction_tracer ()) ||
+ if ((t = conn_.main_connection ().transaction_tracer ()) ||
(t = conn_.tracer ()) ||
(t = conn_.database ().tracer ()))
{
@@ -477,7 +489,7 @@ namespace odb
{
odb::tracer* t;
- if ((t = conn_.transaction_tracer ()) ||
+ if ((t = conn_.main_connection ().transaction_tracer ()) ||
(t = conn_.tracer ()) ||
(t = conn_.database ().tracer ()))
t->execute (conn_, *this);
@@ -618,7 +630,7 @@ namespace odb
{
odb::tracer* t;
- if ((t = conn_.transaction_tracer ()) ||
+ if ((t = conn_.main_connection ().transaction_tracer ()) ||
(t = conn_.tracer ()) ||
(t = conn_.database ().tracer ()))
t->execute (conn_, *this);
@@ -738,7 +750,7 @@ namespace odb
{
{
odb::tracer* t;
- if ((t = conn_.transaction_tracer ()) ||
+ if ((t = conn_.main_connection ().transaction_tracer ()) ||
(t = conn_.tracer ()) ||
(t = conn_.database ().tracer ()))
t->execute (conn_, *this);
@@ -845,7 +857,7 @@ namespace odb
{
{
odb::tracer* t;
- if ((t = conn_.transaction_tracer ()) ||
+ if ((t = conn_.main_connection ().transaction_tracer ()) ||
(t = conn_.tracer ()) ||
(t = conn_.database ().tracer ()))
t->execute (conn_, *this);
@@ -931,7 +943,7 @@ namespace odb
{
{
odb::tracer* t;
- if ((t = conn_.transaction_tracer ()) ||
+ if ((t = conn_.main_connection ().transaction_tracer ()) ||
(t = conn_.tracer ()) ||
(t = conn_.database ().tracer ()))
t->execute (conn_, *this);
diff --git a/odb/sqlite/transaction-impl.cxx b/odb/sqlite/transaction-impl.cxx
index 721b3fe..106270d 100644
--- a/odb/sqlite/transaction-impl.cxx
+++ b/odb/sqlite/transaction-impl.cxx
@@ -42,21 +42,23 @@ namespace odb
odb::transaction_impl::connection_ = connection_.get ();
}
+ connection_type& mc (connection_->main_connection ());
+
switch (lock_)
{
case deferred:
{
- connection_->begin_statement ().execute ();
+ mc.begin_statement ().execute ();
break;
}
case immediate:
{
- connection_->begin_immediate_statement ().execute ();
+ mc.begin_immediate_statement ().execute ();
break;
}
case exclusive:
{
- connection_->begin_exclusive_statement ().execute ();
+ mc.begin_exclusive_statement ().execute ();
break;
}
}
@@ -92,6 +94,8 @@ namespace odb
void transaction_impl::
commit ()
{
+ connection_type& mc (connection_->main_connection ());
+
// Invalidate query results and reset active statements.
//
// Active statements will prevent COMMIT from completing (write
@@ -99,11 +103,13 @@ namespace odb
// statement is automatically reset on completion, however, if an
// exception is thrown, that may not happen.
//
- connection_->clear ();
+ // Note: must be done via the main connection.
+ //
+ mc.clear ();
{
- commit_guard cg (*connection_);
- connection_->commit_statement ().execute ();
+ commit_guard cg (mc);
+ mc.commit_statement ().execute ();
cg.release ();
}
@@ -115,16 +121,52 @@ namespace odb
void transaction_impl::
rollback ()
{
+ connection_type& mc (connection_->main_connection ());
+
// Invalidate query results and reset active statements (the same
// reasoning as in commit()).
//
- connection_->clear ();
+ // Note: must be done via the main connection.
+ //
+ mc.clear ();
- connection_->rollback_statement ().execute ();
+ mc.rollback_statement ().execute ();
// Release the connection.
//
connection_.reset ();
}
+
+ odb::connection& transaction_impl::
+ connection (odb::database* pdb)
+ {
+ if (pdb == 0)
+ return *connection_;
+
+ // Pick the corresponding connection for main/attached database.
+ //
+ database_type& db (static_cast<database_type&> (*pdb));
+
+ assert (&db.main_database () ==
+ &static_cast<database_type&> (database_).main_database ());
+
+ return db.schema ().empty ()
+ ? connection_->main_connection ()
+ : *static_cast<attached_connection_factory&> (*db.factory_).attached_connection_;
+ }
+
+ // Store transaction tracer in the main database.
+ //
+ void transaction_impl::
+ tracer (odb::tracer* t)
+ {
+ connection_->main_connection ().transaction_tracer_ = t;
+ }
+
+ odb::tracer* transaction_impl::
+ tracer () const
+ {
+ return connection_->main_connection ().transaction_tracer_;
+ }
}
}
diff --git a/odb/sqlite/transaction-impl.hxx b/odb/sqlite/transaction-impl.hxx
index 040bcd7..d1a310b 100644
--- a/odb/sqlite/transaction-impl.hxx
+++ b/odb/sqlite/transaction-impl.hxx
@@ -45,6 +45,15 @@ namespace odb
virtual void
rollback ();
+ virtual odb::connection&
+ connection (odb::database*);
+
+ virtual void
+ tracer (odb::tracer*);
+
+ virtual odb::tracer*
+ tracer () const;
+
private:
connection_ptr connection_;
lock lock_;