mirror of
https://github.com/cuberite/SQLiteCpp.git
synced 2025-08-04 17:56:13 -04:00
setBusyTimeout() now uses check() and throw in case of error
- add unit test for setBusyTimeout() - add unit test for in memory databases
This commit is contained in:
parent
18620457b1
commit
acaed41465
@ -89,6 +89,21 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual ~Database() noexcept; // nothrow
|
virtual ~Database() noexcept; // nothrow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set a busy handler that sleeps for a specified amount of time when a table is locked.
|
||||||
|
*
|
||||||
|
* This is usefull in multithreaded program to handle case where a table is locked for writting by a thread.
|
||||||
|
* Any other thread cannot access the table and will receive a SQLITE_BUSY error:
|
||||||
|
* setting a timeout will wait and retry up to the time specified before returning this SQLITE_BUSY error.
|
||||||
|
* Reading the value of timeout for current connection can be done with SQL query "PRAGMA busy_timeout;".
|
||||||
|
* Default busy timeout is 0ms.
|
||||||
|
*
|
||||||
|
* @param[in] aTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
void setBusyTimeout(int aTimeoutMs) noexcept; // nothrow
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Shortcut to execute one or multiple statements without results.
|
* @brief Shortcut to execute one or multiple statements without results.
|
||||||
*
|
*
|
||||||
@ -206,16 +221,6 @@ public:
|
|||||||
return tableExists(aTableName.c_str());
|
return tableExists(aTableName.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set a busy handler that sleeps for a specified amount of time when a table is locked.
|
|
||||||
*
|
|
||||||
* @param[in] aTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY
|
|
||||||
*/
|
|
||||||
inline int setBusyTimeout(int aTimeoutMs) noexcept // nothrow
|
|
||||||
{
|
|
||||||
return sqlite3_busy_timeout(mpSQLite, aTimeoutMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the rowid of the most recent successful INSERT into the database from the current connection.
|
* @brief Get the rowid of the most recent successful INSERT into the database from the current connection.
|
||||||
*
|
*
|
||||||
|
@ -30,7 +30,7 @@ Database::Database(const char* apFilename, const int aFlags /*= SQLITE_OPEN_READ
|
|||||||
mpSQLite(NULL),
|
mpSQLite(NULL),
|
||||||
mFilename(apFilename)
|
mFilename(apFilename)
|
||||||
{
|
{
|
||||||
int ret = sqlite3_open_v2(apFilename, &mpSQLite, aFlags, apVfs);
|
const int ret = sqlite3_open_v2(apFilename, &mpSQLite, aFlags, apVfs);
|
||||||
if (SQLITE_OK != ret)
|
if (SQLITE_OK != ret)
|
||||||
{
|
{
|
||||||
std::string strerr = sqlite3_errmsg(mpSQLite);
|
std::string strerr = sqlite3_errmsg(mpSQLite);
|
||||||
@ -44,7 +44,7 @@ Database::Database(const std::string& aFilename, const int aFlags /*= SQLITE_OPE
|
|||||||
mpSQLite(NULL),
|
mpSQLite(NULL),
|
||||||
mFilename(aFilename)
|
mFilename(aFilename)
|
||||||
{
|
{
|
||||||
int ret = sqlite3_open_v2(aFilename.c_str(), &mpSQLite, aFlags, aVfs.empty() ? NULL : aVfs.c_str());
|
const int ret = sqlite3_open_v2(aFilename.c_str(), &mpSQLite, aFlags, aVfs.empty() ? NULL : aVfs.c_str());
|
||||||
if (SQLITE_OK != ret)
|
if (SQLITE_OK != ret)
|
||||||
{
|
{
|
||||||
std::string strerr = sqlite3_errmsg(mpSQLite);
|
std::string strerr = sqlite3_errmsg(mpSQLite);
|
||||||
@ -56,15 +56,34 @@ Database::Database(const std::string& aFilename, const int aFlags /*= SQLITE_OPE
|
|||||||
// Close the SQLite database connection.
|
// Close the SQLite database connection.
|
||||||
Database::~Database() noexcept // nothrow
|
Database::~Database() noexcept // nothrow
|
||||||
{
|
{
|
||||||
int ret = sqlite3_close(mpSQLite);
|
const int ret = sqlite3_close(mpSQLite);
|
||||||
// Never throw an exception in a destructor
|
// Never throw an exception in a destructor
|
||||||
SQLITECPP_ASSERT(SQLITE_OK == ret, sqlite3_errmsg(mpSQLite)); // See SQLITECPP_ENABLE_ASSERT_HANDLER
|
SQLITECPP_ASSERT(SQLITE_OK == ret, sqlite3_errmsg(mpSQLite)); // See SQLITECPP_ENABLE_ASSERT_HANDLER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set a busy handler that sleeps for a specified amount of time when a table is locked.
|
||||||
|
*
|
||||||
|
* This is usefull in multithreaded program to handle case where a table is locked for writting by a thread.
|
||||||
|
* Any other thread cannot access the table and will receive a SQLITE_BUSY error:
|
||||||
|
* setting a timeout will wait and retry up to the time specified before returning this SQLITE_BUSY error.
|
||||||
|
* Reading the value of timeout for current connection can be done with SQL query "PRAGMA busy_timeout;".
|
||||||
|
* Default busy timeout is 0ms.
|
||||||
|
*
|
||||||
|
* @param[in] aTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY
|
||||||
|
*
|
||||||
|
* @throw SQLite::Exception in case of error
|
||||||
|
*/
|
||||||
|
void Database::setBusyTimeout(int aTimeoutMs) noexcept // nothrow
|
||||||
|
{
|
||||||
|
const int ret = sqlite3_busy_timeout(mpSQLite, aTimeoutMs);
|
||||||
|
check(ret);
|
||||||
|
}
|
||||||
|
|
||||||
// Shortcut to execute one or multiple SQL statements without results (UPDATE, INSERT, ALTER, COMMIT, CREATE...).
|
// Shortcut to execute one or multiple SQL statements without results (UPDATE, INSERT, ALTER, COMMIT, CREATE...).
|
||||||
int Database::exec(const char* apQueries)
|
int Database::exec(const char* apQueries)
|
||||||
{
|
{
|
||||||
int ret = sqlite3_exec(mpSQLite, apQueries, NULL, NULL, NULL);
|
const int ret = sqlite3_exec(mpSQLite, apQueries, NULL, NULL, NULL);
|
||||||
check(ret);
|
check(ret);
|
||||||
|
|
||||||
// Return the number of rows modified by those SQL statements (INSERT, UPDATE or DELETE only)
|
// Return the number of rows modified by those SQL statements (INSERT, UPDATE or DELETE only)
|
||||||
@ -90,7 +109,7 @@ bool Database::tableExists(const char* apTableName)
|
|||||||
Statement query(*this, "SELECT count(*) FROM sqlite_master WHERE type='table' AND name=?");
|
Statement query(*this, "SELECT count(*) FROM sqlite_master WHERE type='table' AND name=?");
|
||||||
query.bind(1, apTableName);
|
query.bind(1, apTableName);
|
||||||
(void)query.executeStep(); // Cannot return false, as the above query always return a result
|
(void)query.executeStep(); // Cannot return false, as the above query always return a result
|
||||||
int Nb = query.getColumn(0);
|
const int Nb = query.getColumn(0);
|
||||||
return (1 == Nb);
|
return (1 == Nb);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +138,7 @@ void Database::createFunction(const char* apFuncName,
|
|||||||
if (abDeterministic) {
|
if (abDeterministic) {
|
||||||
TextRep = TextRep|SQLITE_DETERMINISTIC;
|
TextRep = TextRep|SQLITE_DETERMINISTIC;
|
||||||
}
|
}
|
||||||
int ret = sqlite3_create_function_v2(mpSQLite, apFuncName, aNbArg, TextRep,
|
const int ret = sqlite3_create_function_v2(mpSQLite, apFuncName, aNbArg, TextRep,
|
||||||
apApp, apFunc, apStep, apFinal, apDestroy);
|
apApp, apFunc, apStep, apFinal, apDestroy);
|
||||||
|
|
||||||
check(ret);
|
check(ret);
|
||||||
|
@ -54,11 +54,42 @@ TEST(Database, ctorExecCreateDropExist) {
|
|||||||
remove("test.db3");
|
remove("test.db3");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Database, exec) {
|
TEST(Database, inMemory) {
|
||||||
remove("test.db3");
|
|
||||||
{
|
{
|
||||||
// Create a new database
|
// Create a new database
|
||||||
SQLite::Database db("test.db3", SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
|
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"));
|
||||||
|
// Create a new database: not shared with the above db
|
||||||
|
SQLite::Database db2(":memory:");
|
||||||
|
EXPECT_FALSE(db2.tableExists("test"));
|
||||||
|
} // Close an destroy DBs
|
||||||
|
{
|
||||||
|
// Create a new database: no more "test" table
|
||||||
|
SQLite::Database db(":memory:");
|
||||||
|
EXPECT_FALSE(db.tableExists("test"));
|
||||||
|
} // Close an destroy DB
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Database, busyTimeout) {
|
||||||
|
// Create a new database
|
||||||
|
SQLite::Database db(":memory:");
|
||||||
|
// Busy timeout default to 0ms: any contention between threads or process leads to SQLITE_BUSY error
|
||||||
|
EXPECT_EQ(0, db.execAndGet("PRAGMA busy_timeout").getInt());
|
||||||
|
|
||||||
|
// Set a non null busy timeout: any contention between threads will leads to as much retry as possible during the time
|
||||||
|
db.setBusyTimeout(5000);
|
||||||
|
EXPECT_EQ(5000, db.execAndGet("PRAGMA busy_timeout").getInt());
|
||||||
|
|
||||||
|
// Reset timeout to null
|
||||||
|
db.setBusyTimeout(0);
|
||||||
|
EXPECT_EQ(0, db.execAndGet("PRAGMA busy_timeout").getInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Database, exec) {
|
||||||
|
// Create a new database
|
||||||
|
SQLite::Database db(":memory:", SQLITE_OPEN_READWRITE);
|
||||||
|
|
||||||
// Create a new table with an explicit "id" column aliasing the underlying rowid
|
// Create a new table with an explicit "id" column aliasing the underlying rowid
|
||||||
// NOTE: here exec() returns 0 only because it is the first statements since database connexion,
|
// NOTE: here exec() returns 0 only because it is the first statements since database connexion,
|
||||||
@ -117,16 +148,11 @@ TEST(Database, exec) {
|
|||||||
|
|
||||||
// Add a row with too many values (more than rows in the table)
|
// Add a row with too many values (more than rows in the table)
|
||||||
EXPECT_THROW(db.exec("INSERT INTO test VALUES (NULL, \"first\", 123, 0.123)"), SQLite::Exception);
|
EXPECT_THROW(db.exec("INSERT INTO test VALUES (NULL, \"first\", 123, 0.123)"), SQLite::Exception);
|
||||||
|
|
||||||
} // Close DB test.db3
|
|
||||||
remove("test.db3");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Database, execAndGet) {
|
TEST(Database, execAndGet) {
|
||||||
remove("test.db3");
|
|
||||||
{
|
|
||||||
// Create a new database
|
// Create a new database
|
||||||
SQLite::Database db("test.db3", SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
|
SQLite::Database db(":memory:", SQLITE_OPEN_READWRITE);
|
||||||
|
|
||||||
// Create a new table with an explicit "id" column aliasing the underlying rowid
|
// Create a new table with an explicit "id" column aliasing the underlying rowid
|
||||||
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT, weight INTEGER)");
|
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT, weight INTEGER)");
|
||||||
@ -139,16 +165,12 @@ TEST(Database, execAndGet) {
|
|||||||
// Get a single value result with an easy to use shortcut
|
// Get a single value result with an easy to use shortcut
|
||||||
EXPECT_STREQ("second", db.execAndGet("SELECT value FROM test WHERE id=2"));
|
EXPECT_STREQ("second", db.execAndGet("SELECT value FROM test WHERE id=2"));
|
||||||
EXPECT_STREQ("third", db.execAndGet("SELECT value FROM test WHERE weight=7"));
|
EXPECT_STREQ("third", db.execAndGet("SELECT value FROM test WHERE weight=7"));
|
||||||
EXPECT_EQ(3, (int)db.execAndGet("SELECT weight FROM test WHERE value=\"first\""));
|
EXPECT_EQ(3, db.execAndGet("SELECT weight FROM test WHERE value=\"first\"").getInt());
|
||||||
} // Close DB test.db3
|
|
||||||
remove("test.db3");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Database, execException) {
|
TEST(Database, execException) {
|
||||||
remove("test.db3");
|
|
||||||
{
|
|
||||||
// Create a new database
|
// Create a new database
|
||||||
SQLite::Database db("test.db3", SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
|
SQLite::Database db(":memory:", SQLITE_OPEN_READWRITE);
|
||||||
EXPECT_EQ(SQLITE_OK, db.getErrorCode());
|
EXPECT_EQ(SQLITE_OK, db.getErrorCode());
|
||||||
EXPECT_EQ(SQLITE_OK, db.getExtendedErrorCode());
|
EXPECT_EQ(SQLITE_OK, db.getExtendedErrorCode());
|
||||||
|
|
||||||
@ -173,6 +195,4 @@ TEST(Database, execException) {
|
|||||||
EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"first\", 3)"));
|
EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"first\", 3)"));
|
||||||
// exception with SQL error: "No row to get a column from"
|
// exception with SQL error: "No row to get a column from"
|
||||||
EXPECT_THROW(db.execAndGet("SELECT weight FROM test WHERE value=\"second\""), SQLite::Exception);
|
EXPECT_THROW(db.execAndGet("SELECT weight FROM test WHERE value=\"second\""), SQLite::Exception);
|
||||||
} // Close DB test.db3
|
|
||||||
remove("test.db3");
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user