summaryrefslogtreecommitdiff
path: root/readme.md
diff options
context:
space:
mode:
authorJohn Turner <jturner.usa@gmail.com>2025-09-14 00:16:10 -0400
committerJohn Turner <jturner.usa@gmail.com>2025-09-14 00:16:10 -0400
commitefcea3a80da7c4479d5fe168435ecc9fd06bdc72 (patch)
tree5cb0177e17b1b00a177f2e830e809f606334571b /readme.md
downloadsqlite-kv-bench-efcea3a80da7c4479d5fe168435ecc9fd06bdc72.tar.gz
Squashed 'subprojects/boost-sqlite/' content from commit 3378e35
git-subtree-dir: subprojects/boost-sqlite git-subtree-split: 3378e353705271e569cf4ba15c467b840a39798c
Diffstat (limited to 'readme.md')
-rw-r--r--readme.md277
1 files changed, 277 insertions, 0 deletions
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..e1a62e1
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,277 @@
+# boost_sqlite
+
+This library provides a simple C++ sqlite wrapper.
+
+It includes:
+
+ - typed queries
+ - prepared statements
+ - json support
+ - custom functions (scalar, aggregrate, windows)
+ - event hooks
+ - virtual tables
+
+sqlite provides an excellent C-API, so this library does not attempt to hide, but to augment it.
+
+## Building the library
+
+You can either build the library and link against `boost_sqlite` for embedding it,
+or `boost_sqlite_ext` for extensions.
+
+If you want to use it for extensions you'll need to
+define `BOOST_SQLITE_COMPILE_EXTENSION` or include `boost/sqlite/extensions.hpp` first.
+
+## Quickstart
+
+First we open a database. Note that this can be `":memory:"` for an in-memory database.
+
+```cpp
+boost::sqlite::connection conn{"./my-database.db"};
+```
+
+Next we're creating tables using boost::sqlite::connection::execute,
+because it can execute multiple statements in one command:
+
+```cpp
+ conn.execute(R"(
+create table if not exists author (
+ id integer primary key autoincrement,
+ first_name text,
+ last_name text
+);
+
+create table if not exists library(
+ id integer primary key autoincrement,
+ name text unique,
+ author integer references author(id)
+);
+)"
+);
+```
+
+Next, we'll use a prepared statement to insert multiple values by index:
+
+```cpp
+conn.prepare("insert into author (first_name, last_name) values (?1, ?2), (?3, ?4), (?5, ?6), (?7, ?8)")
+ .execute({"vinnie", "falco", "richard", "hodges", "ruben", "perez", "peter", "dimov"});
+```
+
+Prepared statements can also be used multiple time and used with named parameters instead of indexed.
+
+```cpp
+{
+ conn.query("begin transaction;");
+
+ auto st = conn.prepare("insert into library (\"name\", author) values ($library, "
+ " (select id from author where first_name = $fname and last_name = $lname))");
+
+ st.execute({{"library", "beast"}, {"fname", "vinnie"}, {"lname", "falco"}});
+ st.execute({{"library", "mysql"}, {"fname", "ruben"}, {"lname", "perez"}});
+ st.execute({{"library", "mp11"}, {"fname", "peter"}, {"lname", "dimov"}});
+ st.execute({{"library", "variant2"}, {"fname", "peter"}, {"lname", "dimov"}});
+
+ conn.query("commit;");
+}
+```
+
+Now that we have the values in the table, let's add a custom aggregate function to create a comma separated list:
+
+```cpp
+struct collect_libs
+{
+ void step(std::string & name, span<sqlite::value, 1> args)
+ {
+ if (name.empty())
+ name = args[0].get_text();
+ else
+ (name += ", ") += args[0].get_text();
+ }
+ std::string final(std::string & name) { return name; }
+};
+sqlite::create_aggregate_function(conn, "collect_libs", collect_libs{});
+```
+
+Print out the query with aggregates libraries:
+
+```cpp
+for (boost::sqlite::row r : conn.query(
+ "select first_name, collect_libs(name) "
+ " from author inner join library l on author.id = l.author group by last_name"))
+ std::cout << r.at(0u).get_text() << " authored " << r.at(1u).get_text() << std::endl;
+```
+
+Alternatively a query result can also be read manually instead of using a loop:
+
+```cpp
+boost::sqlite::row r;
+boost::sqlite::query q = conn.query(
+ "select first_name, collect_libs(name) "
+ " from author inner join library l on author.id = l.author group by last_name")
+do
+{
+ auto r = q.current();''
+ std::cout << r.at(0u).get_text() << " authored " << r.at(1u).get_text() << std::endl;
+}
+while (q.read_next());
+
+```
+
+## Fields, values & parameters
+
+sqlite3 has a weak typesystem, where everything is one of
+following [value_types](@ref boost::sqlite::value_type):
+
+ - `integer`
+ - `floating`
+ - `text`
+ - `blob`
+ - `null`
+
+The result of a query is a [field](@ref boost::sqlite::field) type,
+while a [value](@ref boost::sqlite::value) is used in functions.
+
+Fields & values can have [subtypes](https://www.sqlite.org/c3ref/value_subtype.html),
+while parameter to prepared statements do not have thos associated.
+
+Because of this the values that can be bound to an [execute](@ref boost::sqlite::statement::execute)
+need to be convertible to a fixed set of types (see [param_ref](@ref boost::sqlite::param_ref) for details).
+
+When a [value](@ref boost::sqlite::value) is returned from a custom function,
+such as done through [create_scalar_function](@ref boost::sqlite::create_scalar_function), additional types
+can be added with the following tag_invoke function:
+
+```cpp
+void tag_invoke(const struct set_result_tag &, sqlite3_context * ctx, const my_type & value);
+```
+
+An implementation can look like this:
+
+```cpp
+void tag_invoke(const struct set_result_tag &, sqlite3_context * ctx, const my_type & value)
+{
+ auto data = value.to_string();
+ sqlite3_result_text(ctx, data.c_str(), data.size(), sqlite3_free);
+ sqlite3_result_subtype(ctx, my_subtype);
+}
+
+```
+
+## Typed queries
+
+Queries can be typed through tuples, describe or, if you're on C++20, by plain structs.
+The type to hold them is `static_resultset<T>` which will check if the columns match the result types before usage.
+Tuples are matched by position, structs by name.
+
+```cpp
+for (auto q : conn.query<std::tuple<std::string, std::string>>(
+ "select first_name, collect_libs(name) "
+ " from author inner join library l on author.id = l.author group by last_name"))
+ std::cout << std::get<0>(q) << " authored " << std::get<0>(q) << std::endl;
+```
+
+
+```cpp
+struct query_result { std::string first_name, lib_name;};
+BOOST_DESCRIBE_STRUCT(query_result, (), (first_name, lib_name)); // this can be omitted with C++20.
+
+for (auto q : conn.query<query_result>(
+ "select first_name, collect_libs(name) as lib_name"
+ " from author inner join library l on author.id = l.author group by last_name"))
+ std::cout << q.first_name << " authored " << q.lib_name << std::endl;
+```
+
+The following types are allowed in a static query result:
+
+ - `sqlite::value`
+ - `int`
+ - `sqlite_int64`
+ - `double`
+ - `std::string`
+ - `sqlite::string_view`
+ - `sqlite::blob`
+ - `sqlite::blob_view`
+
+
+You'll need to include `boost/sqlite/static_resultset.hpp` for this to work.
+
+## Custom functions
+
+Since sqlite is running in the same process you can add custom functions that can be used from within sqlite.
+
+ - [collation](@ref boost::sqlite::create_collation)
+ - [scalar function](@ref boost::sqlite::create_scalar_function)
+ - [aggregate function](@ref boost::sqlite::create_aggregate_function)
+ - [window function](@ref boost::sqlite::create_window_function)
+
+## Vtables
+
+This library also simplifies adding virtual tables significantly;
+virtual tables are table that are backed by code instead of data.
+
+See [create_module](@ref boost::sqlite::create_module) and [prototype](@ref boost::sqlite::vtab_module_prototype) for more details.
+
+## Modules
+
+This library can also be used to build a sql plugin:
+
+```cpp
+BOOST_SQLITE_EXTENSION(testlibrary, conn)
+{
+ // create a function that can be used in the plugin
+ create_scalar_function(
+ conn, "assert",
+ [](boost::sqlite::context<>, boost::span<boost::sqlite::value, 1u> sp)
+ {
+ if (sp.front().get_int() == 0)
+ throw std::logic_error("assertion failed");
+ });
+}
+```
+
+The plugin can then be loaded & used like this:
+
+```sql
+SELECT load_extension('./test_library');
+
+select assert((3 * 4) = 12);
+```
+
+To build a plugin you need to define `BOOST_SQLITE_COMPILE_EXTENSION`
+(e.g. by including `boost/sqlite/extension.hpp` or linking against `boost_sqlite_ext`).
+
+This will include the matching sqlite header (`sqlite3ext.h`) and
+will move all the symbols into an inline namespace `ext` inside `boost::sqlite`.
+
+<a name="api-reference"></a>
+## Reference
+
+* [Reference](#reference): Covers the topics discussed in this document.
+
+## Library Comparisons
+
+While there are many sqlite wrappers out there, most haven't been updated in the last five years - while sqlite has.
+
+Here are some actively maintained ones:
+
+ - [SQLiteCpp](https://github.com/SRombauts/SQLiteCpp)
+
+SQLiteCpp is the closest to this library, a C++11 wrapper only depending on sqlite & the STL.
+It's great and served as an inspiration for this library.
+boost.sqlite does provide more functionality when it comes to hooks, custom functions & virtual tables.
+Furthermore, boost.sqlite has a non-throwing interface and supports variants & json, as those are available through boost.
+
+ - [sqlite_modern_cpp](https://github.com/SqliteModernCpp/sqlite_modern_cpp)
+
+This library takes a different approach, by making everything an `iostream` interface.
+`iostream` interfaces have somewhat fallen out of favor.
+
+ - [sqlite_orm](https://github.com/fnc12/sqlite_orm)
+
+As the name says, it's an ORM. While there is nothing wrong with ORMs, they are one layer of abstraction
+above a client library like this.
+
+ - [SOCI](https://github.com/SOCI/soci)
+
+SOCI is an abstraction layer for multiple databases in C++, including sqlite.
+It's interfaces encourages dynamic building of query string, which should not be considered safe.
+