diff --git a/include/SQLiteCpp/Database.h b/include/SQLiteCpp/Database.h index 1490fd83..00bb3a4a 100644 --- a/include/SQLiteCpp/Database.h +++ b/include/SQLiteCpp/Database.h @@ -201,6 +201,21 @@ class Database #endif // c++17 + /** + * @brief Wrap an existing sqlite3* connection opened by other means. + * + * When the Database object is constructed as a wrapper, its destruction does NOT automatically + * sqlite3_close() the connection. In this case (only), Statement objects may outlive the Database object with + * which they were constructed, so long as the underlying connection remains open. + * + * @param[in] apSQLite Existing sqlite3* connection to be wrapped + * @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY (see setBusyTimeout()) + * + * @throw SQLite::Exception in case of error + */ + Database(sqlite3* apSQLite, + const int aBusyTimeoutMs = 0); + // Database is non-copyable Database(const Database&) = delete; Database& operator=(const Database&) = delete; @@ -217,7 +232,12 @@ class Database * * @warning assert in case of error */ - ~Database() = default; + ~Database() + { + if (!mCloseOnDestruct) { + mSQLitePtr.release(); // prevent Deleter + } + } // Deleter functor to use with smart pointers to close the SQLite database connection in an RAII fashion. struct Deleter @@ -414,7 +434,7 @@ class Database /// Return UTF-8 encoded English language explanation of the most recent failed API call (if any). const char* getErrorMsg() const noexcept; - /// Return the filename used to open the database. + /// Return the filename used to open the database; empty if the Database wrapped existing sqlite3* const std::string& getFilename() const noexcept { return mFilename; @@ -536,10 +556,7 @@ class Database static Header getHeaderInfo(const std::string& aFilename); // Parse SQLite header data from a database file. - Header getHeaderInfo() - { - return getHeaderInfo(mFilename); - } + Header getHeaderInfo(); /** * @brief BackupType for the backup() method @@ -571,6 +588,7 @@ class Database private: // TODO: perhaps switch to having Statement sharing a pointer to the Connexion std::unique_ptr mSQLitePtr; ///< Pointer to SQLite Database Connection Handle + bool mCloseOnDestruct; ///< true iff ~Database() is to use sqlite3_close() on mSQLitePtr std::string mFilename; ///< UTF-8 filename used to open the database }; diff --git a/src/Database.cpp b/src/Database.cpp index ffa6f0f7..c5b6583e 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -71,6 +71,7 @@ Database::Database(const char* apFilename, const int aFlags /* = SQLite::OPEN_READONLY*/, const int aBusyTimeoutMs /* = 0 */, const char* apVfs /* = nullptr*/) : + mCloseOnDestruct(true), mFilename(apFilename) { sqlite3* handle; @@ -86,6 +87,19 @@ Database::Database(const char* apFilename, } } +// Wrap an existing sqlite3* connection opened by other means. +Database::Database(sqlite3* apSQLite, + const int aBusyTimeoutMs /* = 0 */) : + mCloseOnDestruct(false) +{ + SQLITECPP_ASSERT(apSQLite != nullptr, "Database(nullptr)"); + mSQLitePtr.reset(apSQLite); + if (aBusyTimeoutMs > 0) + { + setBusyTimeout(aBusyTimeoutMs); + } +} + // Deleter functor to use with smart pointers to close the SQLite database connection in an RAII fashion. void Database::Deleter::operator()(sqlite3* apSQLite) { @@ -432,6 +446,18 @@ Header Database::getHeaderInfo(const std::string& aFilename) return h; } +Header Database::getHeaderInfo() +{ + if (!mFilename.empty()) + { + return getHeaderInfo(mFilename); + } + const char *zFilename = sqlite3_db_filename(mSQLitePtr.get(), nullptr); + return getHeaderInfo(std::string(zFilename ? zFilename : "")); +} + + + void Database::backup(const char* apFilename, BackupType aType) { // Open the database file identified by apFilename