Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Database::backup() #211

Merged
merged 2 commits into from
Jul 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 21 additions & 9 deletions include/SQLiteCpp/Database.h
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ class Database
}

/**
* @brief Create or redefine a SQL function or aggregate in the sqlite database.
* @brief Create or redefine a SQL function or aggregate in the sqlite database.
*
* This is the equivalent of the sqlite3_create_function_v2 command.
* @see http://www.sqlite.org/c3ref/create_function.html
Expand Down Expand Up @@ -341,7 +341,7 @@ class Database
void (*apDestroy)(void *));

/**
* @brief Create or redefine a SQL function or aggregate in the sqlite database.
* @brief Create or redefine a SQL function or aggregate in the sqlite database.
*
* This is the equivalent of the sqlite3_create_function_v2 command.
* @see http://www.sqlite.org/c3ref/create_function.html
Expand Down Expand Up @@ -373,7 +373,7 @@ class Database
}

/**
* @brief Load a module into the current sqlite database instance.
* @brief Load a module into the current sqlite database instance.
*
* This is the equivalent of the sqlite3_load_extension call, but additionally enables
* module loading support prior to loading the requested module.
Expand All @@ -392,8 +392,8 @@ class Database
/**
* @brief Set the key for the current sqlite database instance.
*
* This is the equivalent of the sqlite3_key call and should thus be called
* directly after opening the database.
* This is the equivalent of the sqlite3_key call and should thus be called
* directly after opening the database.
* Open encrypted database -> call db.key("secret") -> database ready
*
* @param[in] aKey Key to decode/encode the database
Expand Down Expand Up @@ -421,10 +421,10 @@ class Database
/**
* @brief Test if a file contains an unencrypted database.
*
* This is a simple test that reads the first bytes of a database file and
* compares them to the standard header for unencrypted databases. If the
* header does not match the standard string, we assume that we have an
* encrypted file.
* This is a simple test that reads the first bytes of a database file and
* compares them to the standard header for unencrypted databases. If the
* header does not match the standard string, we assume that we have an
* encrypted file.
*
* @param[in] aFilename path/uri to a file
*
Expand All @@ -434,6 +434,18 @@ class Database
*/
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&);
Expand Down
44 changes: 44 additions & 0 deletions src/Database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
26 changes: 24 additions & 2 deletions tests/Database_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ TEST(Database, ctorExecCreateDropExist)
EXPECT_TRUE(db.tableExists("test"));
EXPECT_TRUE(db.tableExists(std::string("test")));
EXPECT_EQ(0, db.getLastInsertRowid());

EXPECT_EQ(0, db.exec("DROP TABLE IF EXISTS test"));
EXPECT_FALSE(db.tableExists("test"));
EXPECT_FALSE(db.tableExists(std::string("test")));
Expand Down Expand Up @@ -88,7 +88,7 @@ TEST(Database, moveConstructor)
remove("test.db3");
}

#endif
#endif

TEST(Database, createCloseReopen)
{
Expand Down Expand Up @@ -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)
{
Expand Down