From 8f42f71ebade292b600bb883bc179c8a1b30ca1c Mon Sep 17 00:00:00 2001 From: Mike Lin Date: Fri, 22 Jan 2021 21:54:46 -1000 Subject: [PATCH] add example --- examples/example3_ext/.gitignore | 1 + examples/example3_ext/CMakeLists.txt | 24 ++++++++++++++ examples/example3_ext/README.md | 16 ++++++++++ .../example3_ext/src/example_extension.cpp | 31 +++++++++++++++++++ examples/example3_ext/src/main.cpp | 26 ++++++++++++++++ 5 files changed, 98 insertions(+) create mode 100644 examples/example3_ext/.gitignore create mode 100644 examples/example3_ext/CMakeLists.txt create mode 100644 examples/example3_ext/README.md create mode 100644 examples/example3_ext/src/example_extension.cpp create mode 100644 examples/example3_ext/src/main.cpp diff --git a/examples/example3_ext/.gitignore b/examples/example3_ext/.gitignore new file mode 100644 index 00000000..567609b1 --- /dev/null +++ b/examples/example3_ext/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/examples/example3_ext/CMakeLists.txt b/examples/example3_ext/CMakeLists.txt new file mode 100644 index 00000000..20d3c3dd --- /dev/null +++ b/examples/example3_ext/CMakeLists.txt @@ -0,0 +1,24 @@ +# Example building a SQLite3 loadable extension that uses SQLiteCpp internally +cmake_minimum_required(VERSION 3.1) # for "CMAKE_CXX_STANDARD" version +project(SQLiteCpp_ExampleExtension VERSION 1.0) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# KEY OPTION HERE: builds SQLiteCpp for use within the implementation of a loadable extension +set(SQLITECPP_IN_EXTENSION ON CACHE BOOL "" FORCE) + +set(SQLITECPP_RUN_CPPCHECK OFF CACHE BOOL "" FORCE) +set(SQLITECPP_RUN_CPPLINT OFF CACHE BOOL "" FORCE) +set(SQLITECPP_USE_STATIC_RUNTIME OFF CACHE BOOL "" FORCE) +set(SQLITECPP_USE_STATIC_RUNTIME OFF CACHE BOOL "" FORCE) +add_subdirectory(../.. SQLiteCpp) # out-of-source build requires explicit subdir name for compilation artifacts + +add_library(example SHARED src/example_extension.cpp) +target_link_libraries(example SQLiteCpp) + +# Compile driver program that'll load the extension. It links sqlite3 statically, so our extension library +# mustn't itself link sqlite3, either statically or dynamically (that's one thing accomplished by +# SQLITECPP_IN_EXTENSION) +add_executable(example_driver src/main.cpp) +target_link_libraries(example_driver -static sqlite3 dl pthread) diff --git a/examples/example3_ext/README.md b/examples/example3_ext/README.md new file mode 100644 index 00000000..f9a7a4ac --- /dev/null +++ b/examples/example3_ext/README.md @@ -0,0 +1,16 @@ +This example demonstrates how to use SQLiteCpp *within the implementation* of a +[SQLite3 loadable extension](https://sqlite.org/loadext.html). Change into this directory and + +``` +cmake -B build . +cmake --build build +build/example_driver $(pwd)/build/libexample.so +``` + +*(replace .so with .dylib or .dll if appropriate)* + +This should print `it works 42`. Here the `example_driver` program links SQLite3 *statically*, so +it's important to ensure that SQLiteCpp inside the extension will use that "copy" of SQLite3 rather +than trying to dynamically link another one. See [CMakeLists.txt](CMakeLists.txt) for the key CMake +option that ensures this, and [src/example_extension.cpp](src/example_extension.cpp) for some +necessary boilerplate. diff --git a/examples/example3_ext/src/example_extension.cpp b/examples/example3_ext/src/example_extension.cpp new file mode 100644 index 00000000..5040ed3e --- /dev/null +++ b/examples/example3_ext/src/example_extension.cpp @@ -0,0 +1,31 @@ +// Example using SQLiteCpp within the implementation of a SQLite3 run-time loadable extension +// SEE: https://sqlite.org/loadext.html +#include +// When SQLiteCpp is built with option SQLITECPP_IN_EXTENSION=ON, its compiled objects will expect +// to find an extern "C" symbol declared by the following macro in the extension implementation. +extern "C" { +SQLITE_EXTENSION_INIT1 +} +#include +#include + +extern "C" int sqlite3_example_init(sqlite3 *rawDb, char **pzErrMsg, + const sqlite3_api_routines *pApi) { + SQLITE_EXTENSION_INIT2(pApi); + + try { + // temporarily wrap rawDb as a SQLite::Database so we can use SQLiteCpp's conveniences + SQLite::Database db(rawDb); + SQLite::Statement stmt(db, "SELECT 'it works ' || ?"); + stmt.bind(1, 42); + if (stmt.executeStep()) { + std::cout << stmt.getColumn(0).getString() << std::endl; + } + // In a real extension we'd now register custom functions, virtual tables, or VFS objects, + // any of which might also want to use SQLiteCpp wrappers for the raw connection. + return SQLITE_OK; + } catch (SQLite::Exception& exn) { + std::cerr << exn.getErrorStr() << std::endl; + return exn.getErrorCode(); + } +} diff --git a/examples/example3_ext/src/main.cpp b/examples/example3_ext/src/main.cpp new file mode 100644 index 00000000..da742f0e --- /dev/null +++ b/examples/example3_ext/src/main.cpp @@ -0,0 +1,26 @@ +// driver program to load a SQLite3 extension + +#include +#include + +int main(int argc, char **argv) { + if (argc < 2) { + std::cerr << "Usage: example_driver EXTENSION_ABSOLUTE_PATH" << std::endl; + return -1; + } + sqlite3 *db; + if (sqlite3_open_v2(":memory:", &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nullptr) != SQLITE_OK) { + std::cerr << "sqlite3_open_v2() failed" << std::endl; + return -1; + } + char *zErrMsg = nullptr; + if (sqlite3_load_extension(db, argv[1], nullptr, &zErrMsg) != SQLITE_OK) { + std::cerr << "sqlite3_load_extension() failed"; + if (zErrMsg) { + std::cerr << ": " << zErrMsg << std::endl; + } + std::cerr << std::endl; + return -1; + } + return 0; +}