Implement Database::backup() (#211)

Easily backup/restore a live DB via the sqlite3_backup_init() API
 - https://www.sqlite.org/backup.html
 - https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupinit

Use the reference implementation from the documentation
This commit is contained in:
Oleg Smolsky 2019-07-09 00:35:31 -07:00 committed by Sébastien Rombauts
parent 3ba20a3519
commit 514d7d6846
3 changed files with 89 additions and 11 deletions

View File

@ -434,6 +434,18 @@ public:
*/
static bool isUnencrypted(const std::string& aFilename);
/**
* @brief Load or save the database content.
*
* This function is used to load the contents of a database file on disk
* into the "main" database of open database connection, or to save the current
* contents of the database into a database file on disk.
*
* @return SQLITE_OK on success or an error code from SQLite.
*/
enum class BackupType { Save, Load };
int backup(const char* zFilename, BackupType type);
private:
/// @{ Database must be non-copyable
Database(const Database&);

View File

@ -300,4 +300,48 @@ bool Database::isUnencrypted(const std::string& aFilename)
throw exception;
}
// This is a reference implementation of live backup taken from the official sit:
// https://www.sqlite.org/backup.html
int Database::backup(const char* zFilename, BackupType type) {
/* Open the database file identified by zFilename. Exit early if this fails. */
sqlite3* pFile;
int rc = sqlite3_open(zFilename, &pFile);
if (rc == SQLITE_OK)
{
/* If this is a 'load' operation (isSave==0), then data is copied
** from the database file just opened to database mpSQLite.
** Otherwise, if this is a 'save' operation (isSave==1), then data
** is copied from mpSQLite to pFile. Set the variables pFrom and
** pTo accordingly. */
sqlite3* pFrom = (type == BackupType::Save ? mpSQLite : pFile);
sqlite3* pTo = (type == BackupType::Save ? pFile : mpSQLite);
/* Set up the backup procedure to copy from the "main" database of
** connection pFile to the main database of connection mpSQLite.
** If something goes wrong, pBackup will be set to NULL and an error
** code and message left in connection pTo.
**
** If the backup object is successfully created, call backup_step()
** to copy data from pFile to mpSQLite. Then call backup_finish()
** to release resources associated with the pBackup object. If an
** error occurred, then an error code and message will be left in
** connection pTo. If no error occurred, then the error code belonging
** to pTo is set to SQLITE_OK.
*/
sqlite3_backup *pBackup = sqlite3_backup_init(pTo, "main", pFrom, "main");
if (pBackup)
{
sqlite3_backup_step(pBackup, -1);
sqlite3_backup_finish(pBackup);
}
rc = sqlite3_errcode(pTo);
}
/* Close the database connection opened on database file zFilename
** and return the result of this function. */
sqlite3_close(pFile);
return rc;
}
} // namespace SQLite

View File

@ -130,6 +130,28 @@ TEST(Database, inMemory)
} // Close an destroy DB
}
TEST(Database, import_export)
{
// Create a new in-memory database
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE);
EXPECT_FALSE(db.tableExists("test"));
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");
EXPECT_TRUE(db.tableExists("test"));
// Export the data into a file
remove("backup");
EXPECT_EQ(db.backup("backup", SQLite::Database::BackupType::Save), SQLITE_OK);
// Trash the table
db.exec("DROP TABLE test;");
EXPECT_FALSE(db.tableExists("test"));
// Import the data back from the file
EXPECT_EQ(db.backup("backup", SQLite::Database::BackupType::Load), SQLITE_OK);
EXPECT_TRUE(db.tableExists("test"));
}
#if SQLITE_VERSION_NUMBER >= 3007015 // SQLite v3.7.15 is first version with PRAGMA busy_timeout
TEST(Database, busyTimeout)
{