Skip to content

Commit 3a898c1

Browse files
committed
Implement SQLite extension loading
1 parent 19c41df commit 3a898c1

File tree

4 files changed

+56
-0
lines changed

4 files changed

+56
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Features
3535
* Support for creating SQL functions (scalar, aggregate, and window)
3636
* Support for creating SQL collations
3737
* Support for database operation interrupts
38+
* Support for loading SQLite extension libraries
3839
* Optional "strict typing" on a per-query basis, allowing for the prevention of SQLite automatic type conversions across the SQLite fundamental types when retrieving values
3940
* Convenience functions for attaching and detaching databases
4041

include/sqlitemm.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,14 @@ namespace sqlitemm
244244
*/
245245
long long last_insert_rowid() const noexcept;
246246

247+
/**
248+
* Loads an SQLite extension library from the named file.
249+
*
250+
* If the entry point name is an empty string, SQLite will try to come
251+
* up with an entry point name on its own.
252+
*/
253+
void load_extension(const std::string& filename, const std::string entry_point_name = std::string{});
254+
247255
/**
248256
* Connects to the database given by filename.
249257
*/

src/sqlitemm.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,24 @@ namespace sqlitemm
307307
return static_cast<long long>(sqlite3_last_insert_rowid(db));
308308
}
309309

310+
void Connection::load_extension(const std::string& filename, const std::string entry_point_name)
311+
{
312+
char* error_message = nullptr;
313+
int result_code = sqlite3_load_extension(
314+
db, filename.c_str(), entry_point_name.empty() ? nullptr : entry_point_name.c_str(), &error_message
315+
);
316+
if (result_code != SQLITE_OK)
317+
{
318+
std::string message{"failed to load SQLite extension"};
319+
if (error_message)
320+
{
321+
message = error_message;
322+
sqlite3_free(error_message);
323+
}
324+
throw Error(message, result_code);
325+
}
326+
}
327+
310328
void Connection::open(const std::string& filename)
311329
{
312330
assert(!db);

tests/connection.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,3 +389,32 @@ SCENARIO("interrupts can be started and checked")
389389
// so we shall just run it to make sure it does not throw:
390390
REQUIRE_NOTHROW(conn.interrupt());
391391
}
392+
393+
SCENARIO("SQLite extensions can be loaded")
394+
{
395+
sqlitemm::Connection conn(":memory:");
396+
int load_extension_enabled = 0;
397+
conn.set_config(SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, &load_extension_enabled);
398+
399+
// It is difficult to simulate loading an extension,
400+
// so we will test failure:
401+
WHEN("an invalid extension is attempted to be loaded")
402+
{
403+
try
404+
{
405+
conn.load_extension("test_for_failure");
406+
}
407+
catch(const sqlitemm::Error& e)
408+
{
409+
std::string error_message = e.what();
410+
if (load_extension_enabled)
411+
{
412+
REQUIRE(error_message.find("(no such file)") != std::string::npos);
413+
}
414+
else
415+
{
416+
REQUIRE(error_message == "not authorized");
417+
}
418+
}
419+
}
420+
}

0 commit comments

Comments
 (0)