Skip to content

Commit c134346

Browse files
authored
Merge pull request #83 from Mytherin/busytimeout
Add more options for opening SQLite databases - busy_timeout and journal_mode - and set busy time-out by default
2 parents 078cd16 + e299990 commit c134346

File tree

9 files changed

+93
-15
lines changed

9 files changed

+93
-15
lines changed

src/include/sqlite_db.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#pragma once
1010

1111
#include "sqlite_utils.hpp"
12+
#include "storage/sqlite_options.hpp"
1213

1314
namespace duckdb {
1415
class SQLiteStatement;
@@ -29,7 +30,7 @@ class SQLiteDB {
2930
sqlite3 *db;
3031

3132
public:
32-
static SQLiteDB Open(const string &path, bool is_read_only = true, bool is_shared = false);
33+
static SQLiteDB Open(const string &path, const SQLiteOpenOptions &options, bool is_shared = false);
3334
bool TryPrepare(const string &query, SQLiteStatement &result);
3435
SQLiteStatement Prepare(const string &query);
3536
void Execute(const string &query);

src/include/storage/sqlite_catalog.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,19 @@
99
#pragma once
1010

1111
#include "duckdb/catalog/catalog.hpp"
12-
#include "duckdb/common/enums/access_mode.hpp"
12+
#include "sqlite_options.hpp"
1313
#include "sqlite_db.hpp"
1414

1515
namespace duckdb {
1616
class SQLiteSchemaEntry;
1717

1818
class SQLiteCatalog : public Catalog {
1919
public:
20-
explicit SQLiteCatalog(AttachedDatabase &db_p, const string &path, AccessMode access_mode);
20+
explicit SQLiteCatalog(AttachedDatabase &db_p, const string &path, SQLiteOpenOptions options);
2121
~SQLiteCatalog();
2222

2323
string path;
24-
AccessMode access_mode;
24+
SQLiteOpenOptions options;
2525

2626
public:
2727
void Initialize(bool load_builtin) override;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//===----------------------------------------------------------------------===//
2+
// DuckDB
3+
//
4+
// storage/sqlite_options.hpp
5+
//
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#pragma once
10+
11+
#include "duckdb/common/common.hpp"
12+
#include "duckdb/common/enums/access_mode.hpp"
13+
14+
namespace duckdb {
15+
16+
struct SQLiteOpenOptions {
17+
// access mode
18+
AccessMode access_mode = AccessMode::READ_WRITE;
19+
// busy time-out in ms
20+
idx_t busy_timeout = 5000;
21+
// journal mode
22+
string journal_mode;
23+
};
24+
25+
26+
} // namespace duckdb

src/sqlite_db.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ SQLiteDB &SQLiteDB::operator=(SQLiteDB &&other) noexcept {
2828
return *this;
2929
}
3030

31-
SQLiteDB SQLiteDB::Open(const string &path, bool is_read_only, bool is_shared) {
31+
SQLiteDB SQLiteDB::Open(const string &path, const SQLiteOpenOptions &options, bool is_shared) {
3232
SQLiteDB result;
3333
int flags = SQLITE_OPEN_PRIVATECACHE;
34-
if (is_read_only) {
34+
if (options.access_mode == AccessMode::READ_ONLY) {
3535
flags |= SQLITE_OPEN_READONLY;
3636
} else {
3737
flags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
@@ -46,6 +46,19 @@ SQLiteDB SQLiteDB::Open(const string &path, bool is_read_only, bool is_shared) {
4646
if (rc != SQLITE_OK) {
4747
throw std::runtime_error("Unable to open database \"" + path + "\": " + string(sqlite3_errstr(rc)));
4848
}
49+
// default busy time-out of 5 seconds
50+
if (options.busy_timeout > 0) {
51+
if (options.busy_timeout > NumericLimits<int>::Maximum()) {
52+
throw std::runtime_error("busy_timeout out of range - must be within valid range for type int");
53+
}
54+
rc = sqlite3_busy_timeout(result.db, int(options.busy_timeout));
55+
if (rc != SQLITE_OK) {
56+
throw std::runtime_error("Failed to set busy timeout");
57+
}
58+
}
59+
if (!options.journal_mode.empty()) {
60+
result.Execute("PRAGMA journal_mode=" + KeywordHelper::EscapeQuotes(options.journal_mode, '\''));
61+
}
4962
return result;
5063
}
5164

src/sqlite_scanner.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ static unique_ptr<FunctionData> SqliteBind(ClientContext &context, TableFunction
5050

5151
SQLiteDB db;
5252
SQLiteStatement stmt;
53-
db = SQLiteDB::Open(result->file_name);
53+
SQLiteOpenOptions options;
54+
options.access_mode = AccessMode::READ_ONLY;
55+
db = SQLiteDB::Open(result->file_name, options);
5456

5557
ColumnList columns;
5658
vector<unique_ptr<Constraint>> constraints;
@@ -90,7 +92,9 @@ static void SqliteInitInternal(ClientContext &context, const SqliteBindData &bin
9092
// function
9193
local_state.stmt.Close();
9294
if (!local_state.db) {
93-
local_state.owned_db = SQLiteDB::Open(bind_data.file_name.c_str());
95+
SQLiteOpenOptions options;
96+
options.access_mode = AccessMode::READ_ONLY;
97+
local_state.owned_db = SQLiteDB::Open(bind_data.file_name.c_str(), options);
9498
local_state.db = &local_state.owned_db;
9599
}
96100

@@ -292,7 +296,9 @@ static void AttachFunction(ClientContext &context, TableFunctionInput &data_p, D
292296
return;
293297
}
294298

295-
SQLiteDB db = SQLiteDB::Open(data.file_name);
299+
SQLiteOpenOptions options;
300+
options.access_mode = AccessMode::READ_ONLY;
301+
SQLiteDB db = SQLiteDB::Open(data.file_name, options);
296302
auto dconn = Connection(context.db->GetDatabase(context));
297303
{
298304
auto tables = db.GetTables();

src/sqlite_storage.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,16 @@ namespace duckdb {
1515
static unique_ptr<Catalog> SQLiteAttach(StorageExtensionInfo *storage_info, ClientContext &context,
1616
AttachedDatabase &db, const string &name, AttachInfo &info,
1717
AccessMode access_mode) {
18-
return make_uniq<SQLiteCatalog>(db, info.path, access_mode);
18+
SQLiteOpenOptions options;
19+
options.access_mode = access_mode;
20+
for(auto &entry : info.options) {
21+
if (StringUtil::CIEquals(entry.first, "busy_timeout")) {
22+
options.busy_timeout = entry.second.GetValue<uint64_t>();
23+
} else if (StringUtil::CIEquals(entry.first, "journal_mode")) {
24+
options.journal_mode = entry.second.ToString();
25+
}
26+
}
27+
return make_uniq<SQLiteCatalog>(db, info.path, std::move(options));
1928
}
2029

2130
static unique_ptr<TransactionManager> SQLiteCreateTransactionManager(StorageExtensionInfo *storage_info,

src/storage/sqlite_catalog.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88

99
namespace duckdb {
1010

11-
SQLiteCatalog::SQLiteCatalog(AttachedDatabase &db_p, const string &path, AccessMode access_mode)
12-
: Catalog(db_p), path(path), access_mode(access_mode), in_memory(path == ":memory:"), active_in_memory(false) {
11+
SQLiteCatalog::SQLiteCatalog(AttachedDatabase &db_p, const string &path, SQLiteOpenOptions options_p)
12+
: Catalog(db_p), path(path), options(std::move(options_p)), in_memory(path == ":memory:"), active_in_memory(false) {
1313
if (InMemory()) {
14-
in_memory_db = SQLiteDB::Open(path, false, true);
14+
in_memory_db = SQLiteDB::Open(path, options, true);
1515
}
1616
}
1717

src/storage/sqlite_transaction.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ SQLiteTransaction::SQLiteTransaction(SQLiteCatalog &sqlite_catalog, TransactionM
1717
db = sqlite_catalog.GetInMemoryDatabase();
1818
} else {
1919
// on-disk database - open a new database connection
20-
owned_db = SQLiteDB::Open(sqlite_catalog.path,
21-
sqlite_catalog.access_mode == AccessMode::READ_ONLY ? true : false, true);
20+
owned_db = SQLiteDB::Open(sqlite_catalog.path, sqlite_catalog.options, true);
2221
db = &owned_db;
2322
}
2423
}

test/sql/storage/attach_options.test

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# name: test/sql/storage/attach_options.test
2+
# description:
3+
# group: [sqlite_storage]
4+
5+
require sqlite_scanner
6+
7+
statement error
8+
ATTACH ':memory:' AS mem (TYPE SQLITE, BUSY_TIMEOUT 'hello')
9+
----
10+
Could not convert string
11+
12+
statement error
13+
ATTACH ':memory:' AS mem (TYPE SQLITE, BUSY_TIMEOUT 99999999999)
14+
----
15+
busy_timeout out of range
16+
17+
statement ok
18+
ATTACH ':memory:' AS mem (TYPE SQLITE, BUSY_TIMEOUT 0)
19+
20+
statement ok
21+
DETACH mem
22+
23+
statement ok
24+
ATTACH ':memory:' AS mem (TYPE SQLITE, JOURNAL_MODE 'WAL')

0 commit comments

Comments
 (0)