summaryrefslogtreecommitdiff
path: root/subprojects/boost-sqlite/include/boost
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/boost-sqlite/include/boost')
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite.hpp38
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/allocator.hpp48
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/backup.hpp74
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/blob.hpp156
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/collation.hpp140
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/connection.hpp210
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/cstring_ref.hpp223
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/detail/aggregate_function.hpp166
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/detail/catch.hpp169
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/detail/config.hpp83
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/detail/exception.hpp48
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/detail/scalar_function.hpp172
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/detail/vtable.hpp611
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/detail/window_function.hpp238
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/error.hpp178
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/extension.hpp67
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/field.hpp85
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/function.hpp452
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/hooks.hpp370
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/json.hpp140
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/memory.hpp105
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/meta_data.hpp58
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/mutex.hpp55
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/result.hpp147
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/resultset.hpp150
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/row.hpp167
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/statement.hpp651
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/static_resultset.hpp497
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/string.hpp49
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/transaction.hpp198
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/value.hpp130
-rw-r--r--subprojects/boost-sqlite/include/boost/sqlite/vtable.hpp615
32 files changed, 6490 insertions, 0 deletions
diff --git a/subprojects/boost-sqlite/include/boost/sqlite.hpp b/subprojects/boost-sqlite/include/boost/sqlite.hpp
new file mode 100644
index 0000000..474102a
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite.hpp
@@ -0,0 +1,38 @@
+// Copyright (c) 2022 Klemens D. Morgenstern
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#ifndef BOOST_SQLITE_HPP
+#define BOOST_SQLITE_HPP
+
+/** @defgroup reference Reference
+ *
+ * This page contains the documentation of the sqlite high-level API.
+ */
+
+#include <boost/sqlite/backup.hpp>
+#include <boost/sqlite/blob.hpp>
+#include <boost/sqlite/collation.hpp>
+#include <boost/sqlite/connection.hpp>
+#include <boost/sqlite/cstring_ref.hpp>
+#include <boost/sqlite/error.hpp>
+#include <boost/sqlite/field.hpp>
+#include <boost/sqlite/function.hpp>
+#include <boost/sqlite/meta_data.hpp>
+#include <boost/sqlite/memory.hpp>
+#include <boost/sqlite/hooks.hpp>
+#include <boost/sqlite/json.hpp>
+#include <boost/sqlite/resultset.hpp>
+#include <boost/sqlite/row.hpp>
+#include <boost/sqlite/statement.hpp>
+#include <boost/sqlite/static_resultset.hpp>
+#include <boost/sqlite/string.hpp>
+#include <boost/sqlite/transaction.hpp>
+#include <boost/sqlite/value.hpp>
+#include <boost/sqlite/vtable.hpp>
+
+#if defined(BOOST_SQLITE_COMPILE_EXTENSION)
+#include <boost/sqlite/extension.hpp>
+#endif
+
+#endif //BOOST_SQLITE_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/allocator.hpp b/subprojects/boost-sqlite/include/boost/sqlite/allocator.hpp
new file mode 100644
index 0000000..f449475
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/allocator.hpp
@@ -0,0 +1,48 @@
+//
+// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef BOOST_SQLITE_ALLOCATOR_HPP
+#define BOOST_SQLITE_ALLOCATOR_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+
+#include <cstddef>
+#include <cstdint>
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+template<typename T>
+struct allocator
+{
+ constexpr allocator() noexcept {}
+ constexpr allocator( const allocator& other ) noexcept {}
+ template< class U >
+ constexpr allocator( const allocator<U>& other ) noexcept {}
+
+#if defined(SQLITE_4_BYTE_ALIGNED_MALLOC)
+ constexpr static std::size_t alignment = 4u;
+#else
+ constexpr static std::size_t alignment = 8u;
+#endif
+
+ static_assert(alignof(T) <= alignment, "T alignment can't be fulfilled by sqlite");
+ [[nodiscard]] T* allocate( std::size_t n )
+ {
+ auto p = static_cast<T*>(sqlite3_malloc64(n * sizeof(T)));
+ if (p == nullptr)
+ boost::throw_exception(std::bad_alloc());
+ return p;
+ }
+ void deallocate( T* p, std::size_t)
+ {
+ return sqlite3_free(p);
+ }
+};
+
+BOOST_SQLITE_END_NAMESPACE
+
+#endif //BOOST_SQLITE_ALLOCATOR_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/backup.hpp b/subprojects/boost-sqlite/include/boost/sqlite/backup.hpp
new file mode 100644
index 0000000..737a28e
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/backup.hpp
@@ -0,0 +1,74 @@
+//
+// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef BOOST_SQLITE_BACKUP_HPP
+#define BOOST_SQLITE_BACKUP_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/cstring_ref.hpp>
+#include <boost/sqlite/error.hpp>
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+struct connection ;
+
+///@{
+/**
+ @brief Backup a database
+ @ingroup reference
+
+ This function will create a backup of an existing database.
+ This can be useful to write an in memory database to disk et vice versa.
+
+ @param source The source database to backup
+ @param target The target of the backup
+ @param source_name The source database to read the backup from. Default is 'main'.
+ @param target_name The target database to write the backup to. Default is 'main'.
+
+ @par Error Handling
+
+ @throws system_error from overload without `ec` & `ei`
+
+ or you need to pass
+
+ @param ec The system::error_code to capture any possibly errors
+ @param ei Additional error_info when error occurs.
+
+ @par Example
+
+ @code{.cpp}
+
+ sqlite::connection conn{":memory:"};
+ {
+ sqlite::connection read{"./read_only_db.db", SQLITE_READONLY};
+ backup(read, target);
+ }
+
+ @endcode
+ */
+BOOST_SQLITE_DECL
+void
+backup(connection & source,
+ connection & target,
+ cstring_ref source_name,
+ cstring_ref target_name,
+ system::error_code & ec,
+ error_info & ei);
+
+BOOST_SQLITE_DECL
+void
+backup(connection & source,
+ connection & target,
+ cstring_ref source_name = "main",
+ cstring_ref target_name = "main");
+
+///@}
+
+BOOST_SQLITE_END_NAMESPACE
+
+
+#endif //BOOST_SQLITE_BACKUP_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/blob.hpp b/subprojects/boost-sqlite/include/boost/sqlite/blob.hpp
new file mode 100644
index 0000000..cd82363
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/blob.hpp
@@ -0,0 +1,156 @@
+// Copyright (c) 2022 Klemens D. Morgenstern
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#ifndef BOOST_SQLITE_BLOB_HPP
+#define BOOST_SQLITE_BLOB_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/memory.hpp>
+#include <boost/sqlite/error.hpp>
+#include <type_traits>
+#include <memory>
+#include <cstring>
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+struct connection ;
+
+/// @brief a view to a binary large object @ingroup reference
+struct blob_view
+{
+ /// The data in the blob
+ const void * data() const {return data_;}
+ /// The size of the data int he blob, in bytes
+ std::size_t size() const {return size_;}
+ /// Construct a blob
+ blob_view(const void * data, std::size_t size) : data_(data), size_(size) {}
+
+ /// Construct an empty blob
+ blob_view() = default;
+ /// Construct a blob from some other blob-like structure.
+ template<typename T>
+ explicit blob_view(const T & value,
+ typename std::enable_if<
+ std::is_pointer<decltype(std::declval<T>().data())>::value
+ && std::is_convertible<decltype(std::declval<T>().size()), std::size_t>::value
+ >::type * = nullptr) : data_(value.data()), size_(value.size()) {}
+
+ inline blob_view(const struct blob & b);
+ private:
+ const void * data_{nullptr};
+ std::size_t size_{0u};
+};
+
+/// @brief Helper type to pass a blob full of zeroes without allocating extra memory. @ingroup reference
+enum class zero_blob : sqlite3_uint64 {};
+
+/// @brief An object that owns a binary large object. @ingroup reference
+struct blob
+{
+ /// The data in the blob
+ void * data() const {return impl_.get();}
+ /// The size of the data int he blob, in bytes
+ std::size_t size() const {return size_;}
+
+ /// Create a blob from a blob_view
+ explicit blob(blob_view bv)
+ {
+ impl_.reset(sqlite3_malloc(static_cast<int>(bv.size())));
+ size_ = bv.size();
+ std::memcpy(impl_.get(), bv.data(), size_);
+ }
+ /// Create an empty blob with size `n`.
+ explicit blob(std::size_t n) : impl_(sqlite3_malloc(static_cast<int>(n))), size_(n) {}
+
+ /// Construct an empty blob
+ constexpr blob() = default;
+ /// Release & take ownership of the blob.
+ void * release() && {return std::move(impl_).release(); }
+ private:
+ unique_ptr<void> impl_;
+ std::size_t size_{0u};
+};
+
+blob_view::blob_view(const blob & b) : data_(b.data()), size_(b.size()) {}
+
+/// @brief an object that holds a binary large object. Can be obtained by using @ref blob_handle. @ingroup reference
+struct blob_handle
+{
+ /// Default constructor
+ blob_handle() = default;
+
+ /// Construct from a handle. Takes owner ship
+ explicit blob_handle(sqlite3_blob * blob) : blob_(blob) {}
+
+ ///@{
+ /// @brief Reopen on another row
+ BOOST_SQLITE_DECL
+ void reopen(sqlite3_int64 row_id, system::error_code & ec);
+ BOOST_SQLITE_DECL
+ void reopen(sqlite3_int64 row_id);
+ ///@}
+
+ ///@{
+ /// @brief Read data from the blob
+ BOOST_SQLITE_DECL
+ void read_at(void *data, int len, int offset, system::error_code &ec);
+ BOOST_SQLITE_DECL
+ void read_at(void *data, int len, int offset);
+ ///@}
+
+ ///@{
+ /// @brief Write data to the blob
+ BOOST_SQLITE_DECL
+ void write_at(const void *data, int len, int offset, system::error_code &ec);
+ BOOST_SQLITE_DECL
+ void write_at(const void *data, int len, int offset);
+ ///@}
+
+ /// The size of the blob
+ std::size_t size() const {return static_cast<std::size_t>(sqlite3_blob_bytes(blob_.get()));}
+
+ /// The handle of the blob
+ using handle_type = sqlite3_blob*;
+ /// Returns the handle of the blob
+ handle_type handle() { return blob_.get(); }
+ /// Release the owned handle.
+ handle_type release() && { return blob_.release(); }
+ private:
+ struct deleter_
+ {
+ void operator()(sqlite3_blob *impl)
+ {
+ sqlite3_blob_close(impl);
+ }
+ };
+ std::unique_ptr<sqlite3_blob, deleter_> blob_;
+};
+
+///@{
+/// Open a blob for incremental access
+BOOST_SQLITE_DECL
+blob_handle open_blob(connection & conn,
+ cstring_ref db,
+ cstring_ref table,
+ cstring_ref column,
+ sqlite3_int64 row,
+ bool read_only,
+ system::error_code &ec,
+ error_info &ei);
+
+BOOST_SQLITE_DECL
+blob_handle open_blob(connection & conn,
+ cstring_ref db,
+ cstring_ref table,
+ cstring_ref column,
+ sqlite3_int64 row,
+ bool read_only = false);
+///}@
+
+
+
+BOOST_SQLITE_END_NAMESPACE
+
+
+#endif //BOOST_SQLITE_BLOB_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/collation.hpp b/subprojects/boost-sqlite/include/boost/sqlite/collation.hpp
new file mode 100644
index 0000000..a4624c4
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/collation.hpp
@@ -0,0 +1,140 @@
+//
+// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef BOOST_SQLITE_COLLATION_HPP
+#define BOOST_SQLITE_COLLATION_HPP
+
+#include <boost/sqlite/connection.hpp>
+#include <boost/sqlite/detail/exception.hpp>
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+///@{
+/** Define a custom collation function
+ @ingroup reference
+
+ @param conn A connection to the database in which to install the collation.
+ @param name The name of the collation.
+ @param func The function
+
+ The function must be callable with two `string_view` and return an int, indicating the comparison results.
+
+ @par Example
+
+ @code{.cpp}
+
+ // a case insensitive string omparison, e.g. from boost.urls
+ int ci_compare(string_view s0, string_view s1) noexcept;
+
+ extern sqlite::connection conn;
+
+ // Register the collation
+ sqlite::create_collation(conn, "iequal", &ci_compare);
+
+ // use the collation to get by name, case insensitively
+ conn.query("select first_name, last_name from people where first_name = 'Klemens' collate iequal;");
+
+ // order by names case insensitively
+ conn.query("select * from people order by last_name collate iequal asc;");
+
+ @endcode
+
+ */
+
+template<typename Func>
+void create_collation(
+ connection & conn,
+ cstring_ref name,
+ Func && func,
+ typename std::enable_if<
+ std::is_convertible<
+ decltype(func(string_view(), string_view())),
+ int>::value,
+ system::error_code>::type & ec)
+{
+ using func_type = typename std::decay<Func>::type;
+ unique_ptr<func_type> f{new (memory_tag{}) func_type(std::forward<Func>(func))};
+ if (f == nullptr)
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_NOMEM);
+ return;
+ }
+ auto res = sqlite3_create_collation_v2(
+ conn.handle(),
+ name.c_str(),
+ SQLITE_UTF8,
+ f.get(),
+ +[](void * data, int len_l, const void * str_l, int len_r, const void * str_r) -> int
+ {
+ string_view l(static_cast<const char*>(str_l), len_l);
+ string_view r(static_cast<const char*>(str_r), len_r);
+ auto & impl = (*static_cast<func_type*>(data));
+ static_assert(noexcept(impl(l, r)),
+ "Collation function must be noexcept");
+ return impl(l, r);
+ },
+ +[](void * p) { delete_(static_cast<func_type*>(p)); }
+ );
+
+ if (res != SQLITE_OK)
+ BOOST_SQLITE_ASSIGN_EC(ec, res);
+ else
+ f.release();
+}
+
+
+template<typename Func>
+auto create_collation(
+ connection & conn,
+ cstring_ref name,
+ Func && func)
+#if !defined(BOOST_SQLITE_GENERATING_DOCS)
+ -> typename std::enable_if<
+ std::is_convertible<
+ decltype(func(string_view(), string_view())),
+ int>::value>::type
+#endif
+{
+ system::error_code ec;
+ create_collation(conn, name, std::forward<Func>(func), ec);
+ if (ec)
+ detail::throw_error_code(ec, BOOST_CURRENT_LOCATION);
+}
+
+
+inline void delete_collation(
+ connection & conn,
+ cstring_ref name,
+ system::error_code & ec)
+{
+ auto res = sqlite3_create_collation_v2(
+ conn.handle(),
+ name.c_str(),
+ SQLITE_UTF8,
+ nullptr, nullptr, nullptr);
+ if (res != SQLITE_OK)
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, res);
+ }
+}
+
+
+inline auto delete_collation(
+ connection & conn,
+ cstring_ref name)
+{
+ system::error_code ec;
+ delete_collation(conn, name, ec);
+ if (ec)
+ detail::throw_error_code(ec, BOOST_CURRENT_LOCATION);
+}
+
+/// @}
+
+BOOST_SQLITE_END_NAMESPACE
+
+#endif //BOOST_SQLITE_COLLATION_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/connection.hpp b/subprojects/boost-sqlite/include/boost/sqlite/connection.hpp
new file mode 100644
index 0000000..0f6ccf8
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/connection.hpp
@@ -0,0 +1,210 @@
+// Copyright (c) 2022 Klemens D. Morgenstern
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#ifndef BOOST_SQLITE_CONNECTION_HPP
+#define BOOST_SQLITE_CONNECTION_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/error.hpp>
+#include <boost/sqlite/resultset.hpp>
+#include <boost/sqlite/statement.hpp>
+#include <memory>
+#include <boost/system/system_error.hpp>
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+template<typename T, bool Strict>
+struct static_resultset;
+
+constexpr static cstring_ref in_memory = ":memory:";
+
+/** @brief main object for a connection to a database.
+ @ingroup reference
+
+ @par Example
+ @code{.cpp}
+ sqlite::connection conn;
+ conn.connect("./my-database.db");
+ conn.prepare("insert into log (text) values ($1)").execute(std::make_tuple("booting up"));
+ @endcode
+
+ */
+struct connection
+{
+ /// The handle of the connection
+ using handle_type = sqlite3*;
+ /// Returns the handle
+ handle_type handle() const { return impl_.get(); }
+ /// Release the owned handle.
+ handle_type release() && { return impl_.release(); }
+
+ ///Default constructor
+ connection() = default;
+ /// Construct the connection from a handle.
+ explicit connection(handle_type handle, bool take_ownership = true) : impl_(handle, take_ownership) {}
+ /// Move constructor.
+ connection(connection && ) = default;
+ /// Move assign operator.
+ connection& operator=(connection && ) = default;
+
+ /// Construct a connection and connect it to `filename`. `flags` is set by `SQLITE_OPEN_*` flags. @see https://www.sqlite.org/c3ref/c_open_autoproxy.html
+ connection(cstring_ref filename,
+ int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE) { connect(filename, flags); }
+
+#if defined(BOOST_WINDOWS_API)
+ template<typename Path,
+ typename = std::enable_if_t<
+ std::is_same<typename Path::string_type, std::wstring>::value &&
+ std::is_constructible<cstring_ref, decltype(std::declval<Path>().string())>::value
+ >>
+ explicit connection(const Path & pth) : connection(pth.string()) {}
+#endif
+ ///@{
+ /// Connect the database to `filename`. `flags` is set by `SQLITE_OPEN_*` flags.
+ BOOST_SQLITE_DECL void connect(cstring_ref filename, int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
+ BOOST_SQLITE_DECL void connect(cstring_ref filename, int flags, system::error_code & ec);
+ ///@}
+
+#if defined(BOOST_WINDOWS_API)
+ template<typename Path,
+ typename = std::enable_if_t<
+ std::is_same<typename Path::string_type, std::wstring>::value &&
+ std::is_constructible<cstring_ref, decltype(std::declval<Path>().string())>::value
+ >>
+ void connect(const Path & pth, int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)
+ {
+ connect(pth.string(), flags);
+ }
+
+
+ template<typename Path,
+ typename = std::enable_if_t<
+ std::is_same<typename Path::string_type, std::wstring>::value &&
+ std::is_constructible<cstring_ref, decltype(std::declval<Path>().string())>::value
+ >>
+ void connect(const Path & pth, int flags, system::error_code & ec)
+ {
+ connect(pth.string(), flags, ec);
+ }
+#endif
+
+ ///@{
+ /// Close the database connection.
+ BOOST_SQLITE_DECL void close();
+ BOOST_SQLITE_DECL void close(system::error_code & ec, error_info & ei);
+ ///@}
+
+ /// Check if the database holds a valid handle.
+ bool valid() const {return impl_ != nullptr;}
+
+
+ ///@{
+ /// Perform a query without parameters. Can only execute a single statement.
+ BOOST_SQLITE_DECL resultset query(
+ core::string_view q,
+ system::error_code & ec,
+ error_info & ei);
+
+ BOOST_SQLITE_DECL resultset query(core::string_view q);
+
+ template<typename T, bool Strict = false>
+ static_resultset<T, Strict> query(
+ core::string_view q,
+ system::error_code & ec,
+ error_info & ei)
+ {
+ static_resultset<T, Strict> tmp = query(q, ec, ei);
+ if (ec)
+ return {};
+ tmp.check_columns_(ec, ei);
+ if (ec)
+ return {};
+
+ return tmp;
+ }
+
+ template<typename T, bool Strict = false>
+ static_resultset<T, Strict> query(core::string_view q)
+ {
+ system::error_code ec;
+ error_info ei;
+ auto tmp = query<T, Strict>(q, ec, ei);
+ if (ec)
+ throw_exception(system::system_error(ec, ei.message()));
+ return tmp;
+ }
+ ///@}
+
+ ///@{
+ /// Perform a query without parametert, It execute a multiple statement.
+ BOOST_SQLITE_DECL void execute(
+ cstring_ref q,
+ system::error_code & ec,
+ error_info & ei);
+
+ BOOST_SQLITE_DECL void execute(cstring_ref q);
+
+ template<typename StringLike,
+ typename = decltype(std::declval<const StringLike&>().c_str())>
+ void execute(
+ const StringLike& q,
+ system::error_code & ec,
+ error_info & ei)
+ {
+ execute(q.c_str(), ec, ei);
+ }
+ template<typename StringLike,
+ typename = decltype(std::declval<const StringLike&>().c_str())>
+ void execute(const StringLike & q) { execute(q.c_str());}
+ ///@}
+
+ ///@{
+ /// Perform a query with parameters. Can only execute a single statement.
+ BOOST_SQLITE_DECL statement prepare(
+ core::string_view q,
+ system::error_code & ec,
+ error_info & ei);
+
+ BOOST_SQLITE_DECL statement prepare(core::string_view q);
+ ///@}
+
+ /// Check if the database has the table
+ bool has_table(
+ cstring_ref table,
+ cstring_ref db_name = "main") const
+ {
+ return sqlite3_table_column_metadata(impl_.get(), db_name.c_str(), table.c_str(),
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)
+ == SQLITE_OK;
+ }
+
+ /// Check if the database has the table
+ bool has_column(
+ cstring_ref table,
+ cstring_ref column,
+ cstring_ref db_name = "main") const
+ {
+ return sqlite3_table_column_metadata(impl_.get(), db_name.c_str(), table.c_str(), column.c_str(),
+ nullptr, nullptr, nullptr, nullptr, nullptr)
+ == SQLITE_OK;
+ }
+ private:
+ struct deleter_
+ {
+ deleter_(bool owned = true) : owned_(owned) {}
+ bool owned_ = true;
+ void operator()(sqlite3 *impl)
+ {
+ if (owned_)
+ sqlite3_close_v2(impl);
+ }
+ };
+
+ std::unique_ptr<sqlite3, deleter_> impl_{nullptr, deleter_{}};
+};
+
+BOOST_SQLITE_END_NAMESPACE
+
+
+#endif //BOOST_SQLITE_CONNECTION_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/cstring_ref.hpp b/subprojects/boost-sqlite/include/boost/sqlite/cstring_ref.hpp
new file mode 100644
index 0000000..6747f45
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/cstring_ref.hpp
@@ -0,0 +1,223 @@
+// Copyright (c) 2022 Klemens D. Morgenstern
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+// based on boost.process
+#ifndef BOOST_SQLITE_CSTRING_REF_HPP
+#define BOOST_SQLITE_CSTRING_REF_HPP
+
+#include <boost/config.hpp>
+#include <boost/core/detail/string_view.hpp>
+
+#include <cstddef>
+#include <type_traits>
+#include <string>
+
+
+#if __cplusplus >= 201702L
+#include <string_view>
+#endif
+
+namespace boost
+{
+namespace sqlite
+{
+
+/// Small wrapper for a null-terminated string that can be directly passed to C APIS
+/** This ref can only be modified by moving the front pointer. It does not store the
+ * size, but can detect values that can directly be passed to system APIs.
+ *
+ * It can be constructed from a `char*` pointer or any class that has a `c_str()`
+ * member function, e.g. std::string or boost::static_string.
+ *
+ */
+struct cstring_ref
+{
+ using value_type = char;
+ using traits_type = std::char_traits<char>;
+ BOOST_CONSTEXPR cstring_ref() noexcept : view_("") {}
+ BOOST_CONSTEXPR cstring_ref(std::nullptr_t) = delete;
+
+ BOOST_CONSTEXPR cstring_ref( const value_type* s ) : view_(s) {}
+
+ template<typename Source,
+ typename =
+ typename std::enable_if<
+ std::is_same<const value_type,
+ typename std::remove_pointer<decltype(std::declval<Source>().c_str())>::type
+ >::value>::type>
+ BOOST_CONSTEXPR cstring_ref(Source && src) : view_(src.c_str()) {}
+
+ BOOST_CONSTEXPR const char * c_str() const BOOST_NOEXCEPT
+ {
+ return this->data();
+ }
+
+ using string_view_type = core::string_view;
+ constexpr operator string_view_type() const {return view_;}
+
+#if __cplusplus >= 201702L
+ constexpr operator std::string_view() const {return view_;}
+#endif
+
+ using pointer = char *;
+ using const_pointer = const char *;
+ using reference = char &;
+ using const_reference = const char &;
+ using const_iterator = const_pointer;
+ using iterator = const_iterator;
+ using const_reverse_iterator = typename std::reverse_iterator<const_iterator>;
+ using reverse_iterator = typename std::reverse_iterator<iterator>;
+ using size_type = std::size_t;
+ using difference_type = std::ptrdiff_t;
+
+ static BOOST_CONSTEXPR size_type npos = -1;
+
+ BOOST_CONSTEXPR const_iterator begin() const BOOST_NOEXCEPT {return view_;};
+ BOOST_CONSTEXPR const_iterator end() const BOOST_NOEXCEPT {return view_ + length();};
+ BOOST_CONSTEXPR const_iterator cbegin() const BOOST_NOEXCEPT {return view_;};
+ BOOST_CONSTEXPR const_iterator cend() const BOOST_NOEXCEPT {return view_ + length();};
+
+#if defined(BOOST_NO_CXX17)
+ const_reverse_iterator rbegin() const BOOST_NOEXCEPT {return reverse_iterator(end());};
+ const_reverse_iterator rend() const BOOST_NOEXCEPT {return reverse_iterator(begin());};
+ const_reverse_iterator crbegin() const BOOST_NOEXCEPT {return reverse_iterator(end());};
+ const_reverse_iterator crend() const BOOST_NOEXCEPT {return reverse_iterator(begin());};
+#else
+ BOOST_CONSTEXPR const_reverse_iterator rbegin() const BOOST_NOEXCEPT {return reverse_iterator(end());};
+ BOOST_CONSTEXPR const_reverse_iterator rend() const BOOST_NOEXCEPT {return reverse_iterator(begin());};
+ BOOST_CONSTEXPR const_reverse_iterator crbegin() const BOOST_NOEXCEPT {return reverse_iterator(end());};
+ BOOST_CONSTEXPR const_reverse_iterator crend() const BOOST_NOEXCEPT {return reverse_iterator(begin());};
+#endif
+
+ BOOST_CONSTEXPR size_type size() const BOOST_NOEXCEPT {return length(); }
+ BOOST_CONSTEXPR size_type length() const BOOST_NOEXCEPT {return length_impl_(); }
+ BOOST_CONSTEXPR size_type max_size() const BOOST_NOEXCEPT {return static_cast<std::size_t>(-1); }
+ BOOST_ATTRIBUTE_NODISCARD BOOST_CONSTEXPR bool empty() const BOOST_NOEXCEPT {return *view_ == '\0'; }
+
+ BOOST_CONSTEXPR const_reference operator[](size_type pos) const {return view_[pos] ;}
+ BOOST_CXX14_CONSTEXPR const_reference at(size_type pos) const
+ {
+ if (pos >= size())
+ throw_exception(std::out_of_range("cstring-view out of range"));
+ return view_[pos];
+ }
+ BOOST_CONSTEXPR const_reference front() const {return *view_;}
+ BOOST_CONSTEXPR const_reference back() const {return view_[length() - 1];}
+ BOOST_CONSTEXPR const_pointer data() const BOOST_NOEXCEPT {return view_;}
+ BOOST_CXX14_CONSTEXPR void remove_prefix(size_type n) {view_ = view_ + n;}
+ void swap(cstring_ref& s) BOOST_NOEXCEPT {std::swap(view_, s.view_);}
+
+ size_type copy(value_type* s, size_type n, size_type pos = 0) const
+ {
+ return traits_type::copy(s, view_ + pos, n) - view_;
+ }
+ BOOST_CONSTEXPR cstring_ref substr(size_type pos = 0) const
+ {
+ return cstring_ref(view_ + pos);
+ }
+
+ BOOST_CXX14_CONSTEXPR string_view_type substr(size_type pos, size_type length) const
+ {
+ return string_view_type(view_).substr(pos, length);
+ }
+
+ BOOST_CXX14_CONSTEXPR int compare(cstring_ref x) const BOOST_NOEXCEPT
+ {
+ auto idx = 0u;
+ for (; view_[idx] != null_char_()[0] && x[idx] != null_char_()[0]; idx++)
+ if (!traits_type::eq(view_[idx], x[idx]))
+ return traits_type::lt(view_[idx], x[idx]) ? -1 : 1;
+
+ return traits_type::to_int_type(view_[idx]) -
+ traits_type::to_int_type(x[idx]); // will compare to null char of either.
+ }
+
+ BOOST_CXX14_CONSTEXPR bool starts_with(string_view_type x) const BOOST_NOEXCEPT
+ {
+ if (x.empty())
+ return true;
+
+ auto idx = 0u;
+ for (; view_[idx] != null_char_()[0] && idx < x.size(); idx++)
+ if (!traits_type::eq(view_[idx], x[idx]))
+ return false;
+
+ return idx == x.size() || view_[idx] != null_char_()[0];
+ }
+ BOOST_CONSTEXPR bool starts_with(value_type x) const BOOST_NOEXCEPT
+ {
+ return traits_type::eq(view_[0], x);
+ }
+
+ BOOST_CXX14_CONSTEXPR size_type find( char ch, size_type pos = 0 ) const BOOST_NOEXCEPT
+ {
+ for (auto p = view_ + pos; *p != *null_char_(); p++)
+ if (traits_type::eq(*p, ch))
+ return p - view_;
+ return npos;
+ }
+
+
+ friend BOOST_CXX14_CONSTEXPR bool operator==(cstring_ref x, cstring_ref y) BOOST_NOEXCEPT
+ {
+ std::size_t idx = 0u;
+ for (idx = 0u; x[idx] != null_char_()[0] && y[idx] != null_char_()[0]; idx++)
+ if (!traits_type::eq(x[idx], y[idx]))
+ return false;
+ return x[idx] == y[idx];
+ }
+ friend BOOST_CXX14_CONSTEXPR bool operator!=(cstring_ref x, cstring_ref y) BOOST_NOEXCEPT
+ {
+ std::size_t idx = 0u;
+ for (idx = 0u; x[idx] != null_char_()[0] &&
+ y[idx] != null_char_()[0]; idx++)
+ if (!traits_type::eq(x[idx], y[idx]))
+ return true;
+ return x[idx] != y[idx];
+ }
+ friend BOOST_CXX14_CONSTEXPR bool operator< (cstring_ref x, cstring_ref y) BOOST_NOEXCEPT {return x.compare(y) < 0;}
+ friend BOOST_CXX14_CONSTEXPR bool operator> (cstring_ref x, cstring_ref y) BOOST_NOEXCEPT {return x.compare(y) > 0;}
+ friend BOOST_CXX14_CONSTEXPR bool operator<=(cstring_ref x, cstring_ref y) BOOST_NOEXCEPT {return x.compare(y) <= 0;}
+ friend BOOST_CXX14_CONSTEXPR bool operator>=(cstring_ref x, cstring_ref y) BOOST_NOEXCEPT {return x.compare(y) >= 0;}
+
+ // modifiers
+ void clear() BOOST_NOEXCEPT { view_ = null_char_(); } // Boost extension
+
+ std::basic_string<value_type, traits_type> to_string() const
+ {
+ return std::basic_string<char, traits_type>(begin(), end());
+ }
+
+ template<typename Allocator>
+ std::basic_string<value_type, traits_type, Allocator> to_string(const Allocator& a) const
+ {
+ return std::basic_string<value_type, traits_type, Allocator>(begin(), end(), a);
+ }
+
+ template<class A> operator std::basic_string<char, std::char_traits<char>, A>() const
+ {
+ return std::basic_string<char, std::char_traits<char>, A>( view_ );
+ }
+
+ private:
+ BOOST_CONSTEXPR static const_pointer null_char_() {return "\0";}
+ constexpr std::size_t length_impl_(std::size_t n = 0) const BOOST_NOEXCEPT
+ {
+ return view_[n] == null_char_()[0] ? n : length_impl_(n+1);
+ }
+ const_pointer view_;
+};
+
+template<typename Char>
+inline std::basic_ostream<Char>& operator<<( std::basic_ostream<Char> & os, cstring_ref str )
+{
+ return os << core::string_view(str);
+}
+
+
+}
+}
+
+
+#endif //BOOST_SQLITE_CSTRING_REF_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/detail/aggregate_function.hpp b/subprojects/boost-sqlite/include/boost/sqlite/detail/aggregate_function.hpp
new file mode 100644
index 0000000..de0c447
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/detail/aggregate_function.hpp
@@ -0,0 +1,166 @@
+// Copyright (c) 2023 Klemens D. Morgenstern
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#ifndef BOOST_SQLITE_DETAIL_AGGREGATE_FUNCTION_HPP
+#define BOOST_SQLITE_DETAIL_AGGREGATE_FUNCTION_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/detail/catch.hpp>
+#include <boost/sqlite/error.hpp>
+#include <boost/sqlite/cstring_ref.hpp>
+#include <boost/sqlite/memory.hpp>
+#include <boost/sqlite/result.hpp>
+#include <boost/sqlite/value.hpp>
+
+#include <boost/callable_traits/args.hpp>
+#include <boost/callable_traits/return_type.hpp>
+#include <boost/callable_traits/has_void_return.hpp>
+#include <boost/core/span.hpp>
+
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+namespace detail
+{
+
+template<typename Func>
+struct aggregate_function_maker
+{
+ void * mem;
+
+ template<typename ... Args>
+ Func* operator()(Args && ... args)
+ {
+ return new (mem) Func(std::forward<Args>(args)...);
+ }
+};
+
+template<typename Func, typename Args>
+int create_aggregate_function(sqlite3 * db, cstring_ref name, Args && args, int flags,
+ std::true_type /* void return */)
+{
+ using args_type = callable_traits::args_t<decltype(&Func::step)>;
+ using span_type = typename std::tuple_element<1U, args_type>::type;
+ using func_type = typename std::decay<Func>::type;
+ using func_args_type = typename std::decay<Args>::type;
+
+ return sqlite3_create_function_v2(
+ db, name.c_str(),
+ span_type::extent == boost::dynamic_extent ? -1 : static_cast<int>(span_type::extent),
+ SQLITE_UTF8 | flags,
+ new (memory_tag{}) func_args_type(std::forward<Args>(args)),
+ nullptr,
+ +[](sqlite3_context* ctx, int len, sqlite3_value** args)
+ {
+ auto aa = reinterpret_cast<value*>(args);
+ auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx));
+ auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0));
+
+ execute_context_function(
+ ctx,
+ [&]() -> result<void>
+ {
+ if (c == nullptr)
+ {
+ auto p = sqlite3_aggregate_context(ctx, sizeof(func_type));
+ if (!p)
+ return error(SQLITE_NOMEM);
+ c = mp11::tuple_apply(aggregate_function_maker<func_type>{p}, *fa);
+ }
+ c->step(span_type{aa, static_cast<std::size_t>(len)});
+ return {};
+ });
+ },
+ [](sqlite3_context* ctx)
+ {
+ auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx));
+ auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0));
+
+ execute_context_function(
+ ctx,
+ [&]() -> result<decltype(c->final())>
+ {
+ if (c == nullptr)
+ {
+ auto p = sqlite3_aggregate_context(ctx, sizeof(func_type));
+ if (!p)
+ return error(SQLITE_NOMEM);
+ c = mp11::tuple_apply(aggregate_function_maker<func_type>{p}, *fa);
+ }
+ struct reaper {void operator()(func_type * c) { c->~func_type();}};
+ std::unique_ptr<func_type, reaper> cl{c};
+ return c->final();
+ });
+ },
+ [](void * ptr) noexcept { delete_(static_cast<func_type*>(ptr));}
+ );
+}
+
+
+template<typename Func, typename Args>
+int create_aggregate_function(sqlite3 * db, cstring_ref name, Args && args, int flags,
+ std::false_type /* void return */)
+{
+ using args_type = callable_traits::args_t<decltype(&Func::step)>;
+ using span_type = typename std::tuple_element<1U, args_type>::type;
+ using func_type = typename std::decay<Func>::type;
+ using func_args_type = typename std::decay<Args>::type;
+
+ return sqlite3_create_function_v2(
+ db, name.c_str(),
+ span_type::extent == boost::dynamic_extent ? -1 : static_cast<int>(span_type::extent),
+ SQLITE_UTF8 | flags,
+ new (memory_tag{}) func_args_type(std::forward<Args>(args)),
+ nullptr,
+ +[](sqlite3_context* ctx, int len, sqlite3_value** args)
+ {
+ auto aa = reinterpret_cast<value*>(args);
+ auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx));
+ auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0));
+
+ execute_context_function(
+ ctx,
+ [&]() -> result<void>
+ {
+ if (c == nullptr)
+ {
+ auto p = sqlite3_aggregate_context(ctx, sizeof(func_type));
+ if (!p)
+ return {system::in_place_error, error(SQLITE_NOMEM)};
+ c = mp11::tuple_apply(aggregate_function_maker<func_type>{p}, *fa);
+ }
+ return c->step(span_type{aa, static_cast<std::size_t>(len)});
+ });
+ },
+ [](sqlite3_context* ctx)
+ {
+ auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx));
+ auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0));
+
+ execute_context_function(
+ ctx,
+ [&]() -> result<decltype(c->final())>
+ {
+ if (c == nullptr)
+ {
+ auto p = sqlite3_aggregate_context(ctx, sizeof(func_type));
+ if (!p)
+ return {system::in_place_error, error(SQLITE_NOMEM)};
+ c = mp11::tuple_apply(aggregate_function_maker<func_type>{p}, *fa);
+ }
+ struct reaper {void operator()(func_type * c) { c->~func_type();}};
+ std::unique_ptr<func_type, reaper> cl{c};
+ return c->final();
+ });
+ },
+ [](void * ptr) noexcept { delete_(static_cast<func_type*>(ptr));}
+ );
+}
+
+}
+
+BOOST_SQLITE_END_NAMESPACE
+
+
+#endif //BOOST_SQLITE_DETAIL_AGGREGATE_FUNCTION_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/detail/catch.hpp b/subprojects/boost-sqlite/include/boost/sqlite/detail/catch.hpp
new file mode 100644
index 0000000..e5b053f
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/detail/catch.hpp
@@ -0,0 +1,169 @@
+//
+// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef BOOST_SQLITE_DETAIL_CATCH_HPP
+#define BOOST_SQLITE_DETAIL_CATCH_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/detail/exception.hpp>
+#include <boost/sqlite/error.hpp>
+#include <boost/sqlite/result.hpp>
+
+#include <boost/system/system_error.hpp>
+
+#include <stdexcept>
+
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+namespace detail
+{
+
+template<typename Func, typename ... Args>
+void execute_context_function_impl(std::false_type /* is_void */,
+ sqlite3_context * ctx,
+ Func && func, Args && ... args)
+ noexcept(noexcept(std::forward<Func>(func)(std::forward<Args>(args)...)))
+{
+ set_result(ctx, std::forward<Func>(func)(std::forward<Args>(args)...));
+}
+
+template<typename Func, typename ... Args>
+void execute_context_function_impl(std::true_type /* is_void */,
+ sqlite3_context * ctx,
+ Func && func, Args && ... args)
+ noexcept(noexcept(std::forward<Func>(func)(std::forward<Args>(args)...)))
+{
+ std::forward<Func>(func)(std::forward<Args>(args)...);
+}
+
+template<typename T>
+int extract_error(char * &errMsg, result<T> & res)
+{
+ error err = std::move(res).error();
+ if (err.info)
+ errMsg = err.info.release();
+ return err.code;
+}
+
+
+template<typename Func, typename ... Args>
+void execute_context_function(sqlite3_context * ctx,
+ Func && func, Args && ... args) noexcept
+{
+ using return_type = decltype(func(std::forward<Args>(args)...));
+#if !defined(BOOST_NO_EXCEPTIONS)
+ try
+ {
+#endif
+ execute_context_function_impl(std::is_void<return_type>{}, ctx,
+ std::forward<Func>(func),
+ std::forward<Args>(args)...);
+#if !defined(BOOST_NO_EXCEPTIONS)
+ }
+ catch(boost::system::system_error & se)
+ {
+ const auto msg = boost::sqlite::detail::get_message(se);
+ if (!msg.empty())
+ sqlite3_result_error(ctx, msg.data(), msg.size());
+ if (se.code().category() == boost::sqlite::sqlite_category())
+ sqlite3_result_error_code(ctx, se.code().value());
+ }
+ catch(std::bad_alloc &) { sqlite3_result_error_nomem(ctx); }
+ catch(std::length_error &) { sqlite3_result_error_toobig(ctx); }
+ catch(std::out_of_range &) { sqlite3_result_error_code(ctx, SQLITE_RANGE);}
+ catch(std::logic_error &le)
+ {
+ sqlite3_result_error(ctx, le.what(), std::strlen(le.what()));
+ sqlite3_result_error_code(ctx, SQLITE_MISUSE);
+ }
+ catch(std::exception & ex)
+ {
+ sqlite3_result_error(ctx, ex.what(), std::strlen(ex.what()));
+ }
+ catch(...) {sqlite3_result_error_code(ctx, SQLITE_ERROR); }
+#endif
+}
+
+}
+BOOST_SQLITE_END_NAMESPACE
+
+#if defined(BOOST_NO_EXCEPTIONS)
+#define BOOST_SQLITE_TRY
+#define BOOST_SQLITE_CATCH_RESULT(ctx)
+#define BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(msg)
+#define BOOST_SQLITE_CATCH_AND_RETURN()
+
+#else
+
+#define BOOST_SQLITE_TRY try
+#define BOOST_SQLITE_CATCH_RESULT(ctx) \
+catch(boost::system::system_error & se) \
+{ \
+ if (se.code().category() == boost::sqlite::sqlite_category()) \
+ sqlite3_result_error_code(ctx, se.code().value()); \
+ const auto msg = boost::sqlite::detail::get_message(se); \
+ if (!msg.empty()) \
+ sqlite3_result_error(ctx, msg.data(), msg.size()); \
+} \
+catch(std::bad_alloc &) { sqlite3_result_error_nomem(ctx); } \
+catch(std::length_error &) { sqlite3_result_error_toobig(ctx); } \
+catch(std::out_of_range &) { sqlite3_result_error_code(ctx, SQLITE_RANGE);} \
+catch(std::logic_error &le) \
+{ \
+ sqlite3_result_error(ctx, le.what(), std::strlen(le.what())); \
+ sqlite3_result_error_code(ctx, SQLITE_MISUSE); \
+} \
+catch(std::exception & ex) \
+{ \
+ sqlite3_result_error(ctx, ex.what(), std::strlen(ex.what())); \
+} \
+catch(...) {sqlite3_result_error_code(ctx, SQLITE_ERROR); }
+
+
+#define BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(msg) \
+catch (boost::system::system_error & se) \
+{ \
+ auto code = SQLITE_ERROR; \
+ if (se.code().category() == boost::sqlite::sqlite_category()) \
+ code = se.code().value(); \
+ const auto pre = boost::sqlite::detail::get_message(se); \
+ msg = boost::sqlite::error_info(pre).release(); \
+ return code; \
+} \
+catch(std::bad_alloc &) { return SQLITE_NOMEM; } \
+catch(std::length_error &) { return SQLITE_TOOBIG; } \
+catch(std::out_of_range &) { return SQLITE_RANGE;} \
+catch(std::logic_error &le) \
+{ \
+ msg = boost::sqlite::error_info(le.what()).release(); \
+ return SQLITE_MISUSE; \
+} \
+catch(std::exception & ex) \
+{ \
+ msg = boost::sqlite::error_info(ex.what()).release(); \
+ return SQLITE_ERROR; \
+} \
+catch(...) {return SQLITE_ERROR; } \
+
+
+#define BOOST_SQLITE_CATCH_AND_RETURN() \
+catch (boost::system::system_error & se) \
+{ \
+ auto code = SQLITE_ERROR; \
+ if (se.code().category() == boost::sqlite::sqlite_category()) \
+ code = se.code().value(); \
+ return code; \
+} \
+catch(std::bad_alloc &) { return SQLITE_NOMEM; } \
+catch(std::length_error &) { return SQLITE_TOOBIG; } \
+catch(std::out_of_range &) { return SQLITE_RANGE;} \
+catch(std::logic_error &) { return SQLITE_MISUSE;} \
+catch(...) { return SQLITE_ERROR; } \
+
+#endif
+
+#endif //BOOST_SQLITE_DETAIL_CATCH_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/detail/config.hpp b/subprojects/boost-sqlite/include/boost/sqlite/detail/config.hpp
new file mode 100644
index 0000000..2126f60
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/detail/config.hpp
@@ -0,0 +1,83 @@
+//
+// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef BOOST_SQLITE_DETAIL_CONFIG_HPP
+#define BOOST_SQLITE_DETAIL_CONFIG_HPP
+
+#include <boost/config.hpp>
+#include <boost/core/detail/string_view.hpp>
+
+#if defined(BOOST_SQLITE_COMPILE_EXTENSION)
+#include <sqlite3ext.h>
+#define BOOST_SQLITE_COMPILING_EXTENSION 1
+#define BOOST_SQLITE_BEGIN_NAMESPACE namespace boost { namespace sqlite { inline namespace ext {
+#define BOOST_SQLITE_END_NAMESPACE } } }
+#else
+#include <sqlite3.h>
+#define BOOST_SQLITE_BEGIN_NAMESPACE namespace boost { namespace sqlite {
+#define BOOST_SQLITE_END_NAMESPACE } }
+#endif
+
+// copied from boost.json
+#if defined(BOOST_SQLITE_DOCS)
+# define BOOST_SQLITE_DECL
+#else
+# if (defined(BOOST_SQLITE_DYN_LINK) || defined(BOOST_ALL_DYN_LINK)) && !defined(BOOST_SQLITE_STATIC_LINK)
+# if defined(BOOST_SQLITE_SOURCE)
+# define BOOST_SQLITE_DECL BOOST_SYMBOL_EXPORT
+# else
+# define BOOST_SQLITE_DECL BOOST_SYMBOL_IMPORT
+# endif
+# endif // static lib
+# ifndef BOOST_SQLITE_DECL
+# define BOOST_SQLITE_DECL
+# endif
+# if !defined(BOOST_SQLITE_SOURCE) && !defined(BOOST_ALL_NO_LIB) && !defined(BOOST_SQLITE_NO_LIB)
+# define BOOST_LIB_NAME boost_sqlite
+# if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_SQLITE_DYN_LINK)
+# define BOOST_DYN_LINK
+# endif
+# include <boost/config/auto_link.hpp>
+# endif
+#endif
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+
+#if defined(BOOST_SQLITE_COMPILE_EXTENSION)
+extern const sqlite3_api_routines *sqlite3_api;
+#endif
+
+using string_view = boost::core::string_view;
+
+BOOST_SQLITE_END_NAMESPACE
+
+#define BOOST_SQLITE_RETURN_EC(ev) \
+do \
+{ \
+ static constexpr auto loc##__LINE__((BOOST_CURRENT_LOCATION)); \
+ return ::boost::system::error_code(ev, boost::sqlite::sqlite_category(), &loc##__LINE__); \
+} \
+while (false)
+
+#define BOOST_SQLITE_ASSIGN_EC(ec, ev) \
+do \
+{ \
+ static constexpr auto loc##__LINE__((BOOST_CURRENT_LOCATION)); \
+ ec.assign(ev, boost::sqlite::sqlite_category(), &loc##__LINE__); \
+} \
+while (false)
+
+#if defined(BOOST_SQLITE_NO_VIRTUAL)
+#define BOOST_SQLITE_VIRTUAL
+#define BOOST_SQLITE_PURE
+#else
+#define BOOST_SQLITE_VIRTUAL virtual
+#define BOOST_SQLITE_PURE = 0
+#endif
+
+#endif // BOOST_SQLITE_DETAIL_HPP \ No newline at end of file
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/detail/exception.hpp b/subprojects/boost-sqlite/include/boost/sqlite/detail/exception.hpp
new file mode 100644
index 0000000..9f29cbe
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/detail/exception.hpp
@@ -0,0 +1,48 @@
+// Copyright (c) 2023 Klemens D. Morgenstern
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#ifndef BOOST_SQLITE_DETAIL_EXCEPTION_HPP
+#define BOOST_SQLITE_DETAIL_EXCEPTION_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/error.hpp>
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+namespace detail
+{
+
+BOOST_SQLITE_DECL
+BOOST_NORETURN void throw_error_code(const boost::system::error_code & ec,
+ const boost::source_location & loc = BOOST_CURRENT_LOCATION);
+
+BOOST_SQLITE_DECL
+BOOST_NORETURN void throw_error_code(const boost::system::error_code & ec,
+ const error_info & ei,
+ const boost::source_location & loc = BOOST_CURRENT_LOCATION);
+
+BOOST_SQLITE_DECL
+BOOST_NORETURN void throw_out_of_range(const char * msg,
+ const boost::source_location & loc);
+
+BOOST_SQLITE_DECL
+BOOST_NORETURN void throw_invalid_argument(const char * msg,
+ const boost::source_location & loc);
+
+inline core::string_view get_message(const system::system_error & se)
+{
+ auto ec_len = se.code().what().size();
+ auto se_len = std::strlen(se.what());
+
+ if (ec_len == se_len)
+ return core::string_view();
+ else
+ return core::string_view(se.what(), se_len - (ec_len + 2));
+}
+
+
+}
+BOOST_SQLITE_END_NAMESPACE
+
+
+#endif //BOOST_SQLITE_DETAIL_EXCEPTION_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/detail/scalar_function.hpp b/subprojects/boost-sqlite/include/boost/sqlite/detail/scalar_function.hpp
new file mode 100644
index 0000000..ee16a23
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/detail/scalar_function.hpp
@@ -0,0 +1,172 @@
+// Copyright (c) 2023 Klemens D. Morgenstern
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#ifndef BOOST_SQLITE_DETAIL_SCALAR_FUNCTION_HPP
+#define BOOST_SQLITE_DETAIL_SCALAR_FUNCTION_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/cstring_ref.hpp>
+#include <boost/sqlite/memory.hpp>
+#include <boost/sqlite/result.hpp>
+#include <boost/sqlite/value.hpp>
+
+#include <boost/callable_traits/args.hpp>
+#include <boost/callable_traits/return_type.hpp>
+#include <boost/callable_traits/has_void_return.hpp>
+#include <boost/core/span.hpp>
+
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+template<typename ... Args>
+struct context;
+
+namespace detail
+{
+
+template<typename Func, typename ... Args, std::size_t Extent>
+auto create_scalar_function_impl(sqlite3 * db,
+ cstring_ref name,
+ Func && func,
+ int flags,
+ std::tuple<context<Args...>, boost::span<value, Extent>> * ,
+ std::false_type /* void return */,
+ std::false_type /* is pointer */) -> int
+{
+ using func_type = typename std::decay<Func>::type;
+ return sqlite3_create_function_v2(
+ db, name.c_str(),
+ Extent == boost::dynamic_extent ? -1 : static_cast<int>(Extent),
+ SQLITE_UTF8 | flags,
+ new (memory_tag{}) func_type(std::forward<Func>(func)),
+ +[](sqlite3_context* ctx, int len, sqlite3_value** args)
+ {
+ auto cc = context<Args...>(ctx);
+ auto aa = reinterpret_cast<value*>(args);
+ auto &f = *reinterpret_cast<func_type*>(sqlite3_user_data(ctx));
+ boost::span<value, Extent> vals{aa, static_cast<std::size_t>(len)};
+
+ execute_context_function(ctx, f, cc, vals);
+ }, nullptr, nullptr,
+ +[](void * ptr) noexcept {delete_(static_cast<func_type*>(ptr));}
+ );
+}
+
+template<typename Func, typename ... Args, std::size_t Extent>
+auto create_scalar_function_impl(sqlite3 * db,
+ cstring_ref name,
+ Func && func, int flags,
+ std::tuple<context<Args...>, boost::span<value, Extent>> * ,
+ std::true_type /* void return */,
+ std::false_type /* is pointer */) -> int
+{
+ using func_type = typename std::decay<Func>::type;
+ return sqlite3_create_function_v2(
+ db,
+ name.c_str(),
+ (Extent == boost::dynamic_extent) ? -1 : static_cast<int>(Extent),
+ SQLITE_UTF8 | flags,
+ new (memory_tag{}) func_type(std::forward<Func>(func)),
+ +[](sqlite3_context* ctx, int len, sqlite3_value** args)
+ {
+ auto cc = context<Args...>(ctx);
+ auto aa = reinterpret_cast<value*>(args);
+ auto &f = *reinterpret_cast<func_type*>(sqlite3_user_data(ctx));
+ boost::span<value, Extent> vals{aa, static_cast<std::size_t>(len)};
+
+ execute_context_function(
+ ctx,
+ [&]()
+ {
+ f(cc, vals);
+ return result<void>();
+ });
+
+ }, nullptr, nullptr,
+ +[](void * ptr){delete_(static_cast<func_type*>(ptr));}
+ );
+}
+
+
+template<typename Func, typename ... Args, std::size_t Extent>
+auto create_scalar_function_impl(sqlite3 * db,
+ cstring_ref name,
+ Func * func, int flags,
+ std::tuple<context<Args...>, boost::span<value, Extent>> * ,
+ std::false_type /* void return */,
+ std::true_type /* is pointer */) -> int
+{
+ return sqlite3_create_function_v2(
+ db, name.c_str(),
+ Extent == boost::dynamic_extent ? -1 : static_cast<int>(Extent),
+ SQLITE_UTF8 | flags,
+ reinterpret_cast<void*>(func),
+ +[](sqlite3_context* ctx, int len, sqlite3_value** args)
+ {
+ auto cc = context<Args...>(ctx);
+ auto aa = reinterpret_cast<value*>(args);
+ auto f = reinterpret_cast<Func*>(sqlite3_user_data(ctx));
+
+ boost::span<value, Extent> vals{aa, static_cast<std::size_t>(len)};
+
+ execute_context_function(
+ ctx, f, cc, vals);
+ }, nullptr, nullptr, nullptr);
+}
+
+template<typename Func, typename ... Args, std::size_t Extent>
+auto create_scalar_function_impl(sqlite3 * db,
+ cstring_ref name,
+ Func * func, int flags,
+ std::tuple<context<Args...>, boost::span<value, Extent>> * ,
+ std::true_type /* void return */,
+ std::true_type /* is pointer */) -> int
+{
+ return sqlite3_create_function_v2(
+ db,
+ name.c_str(),
+ (Extent == boost::dynamic_extent) ? -1 : static_cast<int>(Extent),
+ SQLITE_UTF8 | flags,
+ reinterpret_cast<void*>(func),
+ +[](sqlite3_context* ctx, int len, sqlite3_value** args)
+ {
+ auto cc = context<Args...>(ctx);
+ auto aa = reinterpret_cast<value*>(args);
+ auto f = *reinterpret_cast<Func*>(sqlite3_user_data(ctx));
+ boost::span<value, Extent> vals{aa, static_cast<std::size_t>(len)};
+ execute_context_function(
+ ctx,
+ [&]()
+ {
+ f(cc, vals);
+ return result<void>();
+ });
+
+ }, nullptr, nullptr, nullptr);
+}
+
+template<typename Func>
+auto create_scalar_function(sqlite3 * db,
+ cstring_ref name,
+ Func && func,
+ int flags)
+ -> decltype(create_scalar_function_impl(
+ db, name, std::forward<Func>(func), flags,
+ static_cast<callable_traits::args_t<Func>*>(nullptr),
+ callable_traits::has_void_return<Func>{},
+ std::is_pointer<typename std::decay<Func>::type>{}
+ ))
+{
+ return create_scalar_function_impl(db, name, std::forward<Func>(func), flags,
+ static_cast<callable_traits::args_t<Func>*>(nullptr),
+ callable_traits::has_void_return<Func>{},
+ std::is_pointer<typename std::decay<Func>::type>{});
+}
+
+
+}
+
+BOOST_SQLITE_END_NAMESPACE
+
+#endif //BOOST_SQLITE_DETAIL_SCALAR_FUNCTION_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/detail/vtable.hpp b/subprojects/boost-sqlite/include/boost/sqlite/detail/vtable.hpp
new file mode 100644
index 0000000..f85f44c
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/detail/vtable.hpp
@@ -0,0 +1,611 @@
+// Copyright (c) 2023 Klemens D. Morgenstern
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#ifndef BOOST_SQLITE_DETAIL_VTABLE_HPP
+#define BOOST_SQLITE_DETAIL_VTABLE_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/detail/catch.hpp>
+#include <boost/sqlite/vtable.hpp>
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+namespace detail
+{
+struct vtab_impl
+{
+
+template<typename Module>
+static int connect(sqlite3 * db, void * pAux, int argc, const char * const * argv,
+ sqlite3_vtab **ppVTab, char** errMsg)
+{
+ using table_type = typename Module::table_type;
+ auto &impl = *static_cast<Module*>(pAux);
+ BOOST_SQLITE_TRY
+ {
+ result<table_type> rtab = impl.connect(
+ sqlite::connection(db, false),
+ argc, argv);
+
+ if (rtab.has_error())
+ return extract_error(*errMsg, rtab);
+
+ auto tab = make_unique<table_type>(std::move(*rtab));
+ tab->db_ = db;
+ auto code = sqlite3_declare_vtab(db, tab->declaration());
+ if (code != SQLITE_OK)
+ return code;
+
+ sqlite::vtab::module_config cfg{db};
+ auto r = tab->config(cfg);
+ if (r.has_error())
+ return extract_error(*errMsg, r);
+
+ *ppVTab = tab.release();
+
+ return SQLITE_OK;
+ }
+ BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(*errMsg)
+}
+
+
+template<typename Module>
+static int create(sqlite3 * db, void * pAux, int argc, const char * const * argv,
+ sqlite3_vtab **ppVTab, char** errMsg)
+{
+ using table_type = typename Module::table_type;
+ auto &impl = *static_cast<Module*>(pAux);
+ BOOST_SQLITE_TRY
+ {
+ result<table_type> rtab = impl.create(
+ sqlite::connection(db, false),
+ argc, argv);
+
+ if (rtab.has_error())
+ return extract_error(*errMsg, rtab);
+
+ auto tab = make_unique<table_type>(std::move(*rtab));
+ tab->db_ = db;
+
+ auto code = sqlite3_declare_vtab(db, tab->declaration());
+ if (code != SQLITE_OK)
+ return code;
+
+ sqlite::vtab::module_config mc{db};
+ auto r = tab->config(mc);
+ if (r.has_error())
+ return extract_error(*errMsg, r);
+
+ *ppVTab = tab.release();
+
+ return SQLITE_OK;
+ }
+ BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(*errMsg)
+}
+
+template<typename Table>
+static int disconnect(sqlite3_vtab * tab)
+{
+ BOOST_SQLITE_TRY
+ {
+ auto tb = static_cast<Table*>(tab);
+ tb->~Table();
+ sqlite3_free(tb);
+ return SQLITE_OK;
+ }
+ BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(tab->zErrMsg);
+}
+
+template<typename Table>
+static int destroy(sqlite3_vtab * tab)
+{
+ BOOST_SQLITE_TRY
+ {
+ auto tb = static_cast<Table*>(tab);
+ auto res = tb->destroy();
+ tb->~Table();
+ sqlite3_free(tb);
+ if (res.has_error())
+ return std::move(res).error().code;
+ return SQLITE_OK;
+ }
+ BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(tab->zErrMsg);
+}
+
+template<typename Module, typename Table>
+static void assign_create(sqlite3_module & md, const Module &,
+ const sqlite::vtab::eponymous_module<Table> & base)
+{
+ md.xConnect = md.xCreate = &connect<Module>;
+ md.xDisconnect = md.xDestroy = &disconnect<Table>;
+ if (base.eponymous_only())
+ md.xCreate = nullptr;
+}
+
+template<typename Module, typename Table>
+static void assign_create(sqlite3_module & md, const Module &,
+ const sqlite::vtab::module<Table> &)
+{
+ md.xConnect = &connect<Module>;
+ md.xDisconnect = &disconnect<Table>;
+ md.xCreate = &create<Module>;
+ md.xDestroy = &destroy<Table>;
+}
+
+template<typename Table>
+static int open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor)
+{
+ auto tab = static_cast<Table *>(pVTab);
+
+ BOOST_SQLITE_TRY
+ {
+ auto res = tab->open();
+ if (res.has_error())
+ return extract_error(pVTab->zErrMsg, res);
+ *ppCursor = new (memory_tag{}) typename Table::cursor_type(std::move(*res));
+ return SQLITE_OK;
+ }
+ BOOST_SQLITE_CATCH_AND_RETURN();
+}
+
+template<typename Cursor>
+static int close(sqlite3_vtab_cursor * cursor)
+{
+ auto p = static_cast<Cursor *>(cursor);
+
+ BOOST_SQLITE_TRY
+ {
+ p->~Cursor();
+ }
+ BOOST_SQLITE_CATCH_AND_RETURN();
+
+ sqlite3_free(p);
+ return SQLITE_OK;
+
+}
+
+template<typename Table>
+static int best_index(sqlite3_vtab *pVTab, sqlite3_index_info* info)
+{
+ BOOST_SQLITE_TRY
+ {
+ auto tb = static_cast<Table*>(pVTab);
+
+ sqlite::vtab::index_info ii(tb->db_, info);
+ auto r = tb->best_index(ii);
+ if (r.has_error())
+ return extract_error(pVTab->zErrMsg, r);
+
+ return SQLITE_OK;
+ }
+ BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg);
+}
+
+
+template<typename Cursor>
+static int filter(sqlite3_vtab_cursor* pCursor,
+ int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv)
+{
+ BOOST_SQLITE_TRY
+ {
+ auto cr = static_cast<Cursor*>(pCursor);
+
+ auto r = cr->filter(idxNum, idxStr,
+ boost::span<value>{reinterpret_cast<value*>(argv),
+ static_cast<std::size_t>(argc)});
+ if (r.has_error())
+ return extract_error(pCursor->pVtab->zErrMsg, r);
+
+ return SQLITE_OK;
+ }
+ BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pCursor->pVtab->zErrMsg);
+}
+
+
+template<typename Cursor>
+static int next(sqlite3_vtab_cursor* pCursor)
+{
+ BOOST_SQLITE_TRY
+ {
+ auto cr = static_cast<Cursor*>(pCursor);
+
+ auto r = cr->next();
+ if (r.has_error())
+ return extract_error(pCursor->pVtab->zErrMsg, r);
+
+ return SQLITE_OK;
+ }
+ BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pCursor->pVtab->zErrMsg);
+}
+
+
+template<typename Cursor>
+static int eof(sqlite3_vtab_cursor* pCursor)
+{
+ return static_cast<Cursor*>(pCursor)->eof() ? 1 : 0;
+}
+
+template<typename Cursor>
+static auto column(sqlite3_vtab_cursor* pCursor,
+ sqlite3_context * ctx, int idx)
+ -> typename std::enable_if<!std::is_void<typename Cursor::column_type>::value, int>::type
+{
+#if SQLITE_VERSION_NUMBER >= 3032000
+ bool no_change = sqlite3_vtab_nochange(ctx) != 0;
+#else
+ bool no_change = false;
+#endif
+ auto cr = static_cast<Cursor*>(pCursor);
+ execute_context_function(
+ ctx,
+ [&]{
+ return cr->column(idx, no_change);
+ });
+
+ return SQLITE_OK;
+}
+
+
+template<typename Cursor>
+static auto column(sqlite3_vtab_cursor* pCursor,
+ sqlite3_context * ctx, int idx)
+ -> typename std::enable_if<std::is_void<typename Cursor::column_type>::value, int>::type
+{
+#if SQLITE_VERSION_NUMBER >= 3032000
+ bool no_change = sqlite3_vtab_nochange(ctx) != 0;
+#else
+ bool no_change = false;
+#endif
+ auto cr = static_cast<Cursor*>(pCursor);
+ BOOST_SQLITE_TRY
+ {
+ cr->column(context<>{ctx}, idx, no_change);
+ }
+ BOOST_SQLITE_CATCH_RESULT(ctx);
+ return SQLITE_OK;
+}
+
+template<typename Cursor>
+static int row_id(sqlite3_vtab_cursor* pCursor, sqlite3_int64 *pRowid)
+{
+ BOOST_SQLITE_TRY
+ {
+ auto cr = static_cast<Cursor*>(pCursor);
+
+ auto r = cr->row_id();
+ if (r.has_error())
+ return extract_error(pCursor->pVtab->zErrMsg, r);
+ *pRowid = *r;
+ return SQLITE_OK;
+ }
+ BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pCursor->pVtab->zErrMsg);
+}
+
+template<typename Table>
+static int update(sqlite3_vtab * pVTab, int argc, sqlite3_value ** argv, sqlite3_int64 * pRowid)
+{
+ using table_type = Table;
+ BOOST_SQLITE_TRY
+ {
+ auto & mod = *static_cast<table_type *>(pVTab);
+ auto db = mod.db_;
+ if (argc == 1 && sqlite3_value_type(argv[0]) != SQLITE_NULL)
+ {
+ auto r = mod.delete_(sqlite::value(*argv));
+ if (r.has_error())
+ return extract_error(pVTab->zErrMsg, r);
+ }
+ else if (argc > 1 && sqlite3_value_type(argv[0]) == SQLITE_NULL)
+ {
+ auto r = mod.insert(value{argv[1]},
+ boost::span<value>{reinterpret_cast<value *>(argv + 2),
+ static_cast<std::size_t>(argc - 2)},
+ sqlite3_vtab_on_conflict(db));
+ if (r.has_error())
+ return extract_error(pVTab->zErrMsg, r);
+ *pRowid = r.value();
+ }
+ else if (argc > 1 && sqlite3_value_type(argv[0]) != SQLITE_NULL)
+ {
+ auto r = mod.update(sqlite::value(*argv), value{argv[1]}, // ID
+ boost::span<value>{reinterpret_cast<value *>(argv + 2),
+ static_cast<std::size_t>(argc - 2)},
+ sqlite3_vtab_on_conflict(db));
+
+ if (r.has_error())
+ return extract_error(pVTab->zErrMsg, r);
+ *pRowid = r.value();
+ }
+ else
+ {
+ pVTab->zErrMsg = sqlite3_mprintf("Misuse of update api");
+ return SQLITE_MISUSE;
+ }
+
+ }
+ BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg)
+ return SQLITE_OK;
+}
+
+template<typename Module>
+static void assign_update(sqlite3_module & md, const Module &,
+ std::true_type /* modifiable */)
+{
+ md.xUpdate = &update<typename Module::table_type>;
+}
+
+template<typename Module>
+static void assign_update(sqlite3_module & /*md*/, const Module &,
+ std::false_type /* modifiable */)
+{
+}
+
+template<typename Table>
+static int begin(sqlite3_vtab* pVTab)
+{
+ BOOST_SQLITE_TRY
+ {
+ auto cr = static_cast<Table*>(pVTab);
+
+ auto r = cr->begin();
+ if (r.has_error())
+ return extract_error(pVTab->zErrMsg, r);
+
+ return SQLITE_OK;
+ }
+ BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg);
+}
+
+template<typename Table>
+static int sync(sqlite3_vtab* pVTab)
+{
+ BOOST_SQLITE_TRY
+ {
+ auto cr = static_cast<Table*>(pVTab);
+
+ auto r = cr->sync();
+ if (r.has_error())
+ return extract_error(pVTab->zErrMsg, r);
+
+ return SQLITE_OK;
+ }
+ BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg);
+}
+
+template<typename Table>
+static int commit(sqlite3_vtab* pVTab)
+{
+ BOOST_SQLITE_TRY
+ {
+ auto cr = static_cast<Table*>(pVTab);
+
+ auto r = cr->commit();
+ if (r.has_error())
+ return extract_error(pVTab->zErrMsg, r);
+
+ return SQLITE_OK;
+ }
+ BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg);
+}
+
+template<typename Table>
+static int rollback(sqlite3_vtab* pVTab)
+{
+ BOOST_SQLITE_TRY
+ {
+ auto cr = static_cast<Table*>(pVTab);
+
+ auto r = cr->rollback();
+ if (r.has_error())
+ return extract_error(pVTab->zErrMsg, r);
+
+ return SQLITE_OK;
+ }
+ BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg);
+}
+
+template<typename Module>
+static void assign_transaction(sqlite3_module & /*md*/, const Module &,
+ std::false_type /* modifiable */)
+{
+}
+
+template<typename Module>
+static void assign_transaction(sqlite3_module & md, const Module &,
+ std::true_type /* modifiable */)
+{
+ md.xBegin = &begin <typename Module::table_type>;
+ md.xSync = &sync <typename Module::table_type>;
+ md.xCommit = &commit <typename Module::table_type>;
+ md.xRollback = &rollback<typename Module::table_type>;
+}
+
+template<typename Table>
+static int find_function(sqlite3_vtab *pVtab, int nArg, const char *zName,
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
+ void **ppArg)
+{
+ BOOST_SQLITE_TRY
+ {
+ auto cr = static_cast<Table*>(pVtab);
+
+ auto r = cr->find_function(
+ nArg, zName, sqlite::vtab::function_setter(pxFunc, ppArg));
+ if (r.has_error())
+ return extract_error(pVtab->zErrMsg, r);
+
+ return SQLITE_OK;
+ }
+ BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVtab->zErrMsg);
+}
+
+template<typename Module>
+static void assign_find_function(sqlite3_module & /*md*/, const Module &,
+ std::false_type /* overloadable */)
+{
+}
+
+template<typename Module>
+static void assign_find_function(sqlite3_module & md, const Module &,
+ std::true_type /* overloadable */)
+{
+ md.xFindFunction = &find_function<typename Module::table_type>;
+}
+
+template<typename Table>
+static int rename(sqlite3_vtab* pVTab, const char * name)
+{
+ BOOST_SQLITE_TRY
+ {
+ auto cr = static_cast<Table*>(pVTab);
+
+ auto r = cr->rename(name);
+ if (r.has_error())
+ return extract_error(pVTab->zErrMsg, r);
+
+ return SQLITE_OK;
+ }
+ BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg);
+}
+
+template<typename Module>
+static void assign_rename(sqlite3_module & /*md*/, const Module &,
+ std::false_type /* renamable */)
+{
+}
+
+template<typename Module>
+static void assign_rename(sqlite3_module & md, const Module &,
+ std::true_type /* renamable */)
+{
+ md.xRename = &rename<typename Module::table_type>;
+}
+#if SQLITE_VERSION_NUMBER >= 3007007
+
+template<typename Table>
+static int savepoint(sqlite3_vtab* pVTab, int i)
+{
+ BOOST_SQLITE_TRY
+ {
+ auto cr = static_cast<Table*>(pVTab);
+
+ auto r = cr->savepoint(i);
+ if (r.has_error())
+ return extract_error(pVTab->zErrMsg, r);
+
+ return SQLITE_OK;
+ }
+ BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg);
+}
+
+template<typename Table>
+static int release(sqlite3_vtab* pVTab, int i)
+{
+ BOOST_SQLITE_TRY
+ {
+ auto cr = static_cast<Table*>(pVTab);
+
+ auto r = cr->release(i);
+ if (r.has_error())
+ return extract_error(pVTab->zErrMsg, r);
+
+ return SQLITE_OK;
+ }
+ BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg);
+}
+
+template<typename Table>
+static int rollback_to(sqlite3_vtab* pVTab, int i)
+{
+ BOOST_SQLITE_TRY
+ {
+ auto cr = static_cast<Table*>(pVTab);
+
+ auto r = cr->rollback_to(i);
+ if (r.has_error())
+ return extract_error(pVTab->zErrMsg, r);
+
+ return SQLITE_OK;
+ }
+ BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg);
+}
+
+template<typename Module>
+static void assign_recursive_transaction(sqlite3_module & /*md*/, const Module &,
+ std::false_type /* recursive_transaction */)
+{
+}
+
+template<typename Module>
+static void assign_recursive_transaction(sqlite3_module & md, const Module &,
+ std::true_type /* recursive_transaction */)
+{
+ md.xSavepoint = &savepoint <typename Module::table_type>;
+ md.xRelease = &release <typename Module::table_type>;
+ md.xRollbackTo = &rollback_to<typename Module::table_type>;
+}
+
+#endif
+
+#if SQLITE_VERSION_NUMBER >= 3026000
+
+template<typename Table>
+static void assign_shadow_name(sqlite3_module & /*md*/, const sqlite::vtab::module<Table> &) {}
+
+template<typename Table>
+static void assign_shadow_name(sqlite3_module & /*md*/, const sqlite::vtab::eponymous_module<Table> &) {}
+
+template<typename Module,
+ bool (*Func)(const char *) = &Module::shadow_name>
+static void assign_shadow_name(sqlite3_module & md, const Module & mod)
+{
+ md.xShadowName = +[](const char * name){return Func(name) != 0;};
+}
+
+#endif
+
+};
+
+template<typename Module>
+const sqlite3_module make_module(const Module & mod)
+{
+ sqlite3_module md;
+ std::memset(&md, 0, sizeof(sqlite3_module));
+
+#if SQLITE_VERSION_NUMBER < 3007007
+ md.iVersion = 1;
+#elif SQLITE_VERSION_NUMBER < 3026000
+ md.iVersion = 2;
+#else
+ md.iVersion = 3;
+#endif
+ using table_type = typename Module::table_type;
+ using cursor_type = typename table_type::cursor_type;
+ vtab_impl::assign_create(md, mod, mod);
+ md.xBestIndex = &vtab_impl::best_index<table_type>;
+ md.xOpen = &vtab_impl::open <table_type>;
+ md.xClose = &vtab_impl::close <cursor_type>;
+ md.xFilter = &vtab_impl::filter <cursor_type>;
+ md.xNext = &vtab_impl::next <cursor_type>;
+ md.xEof = &vtab_impl::eof <cursor_type>;
+ md.xColumn = &vtab_impl::column <cursor_type>;
+ md.xRowid = &vtab_impl::row_id <cursor_type>;
+ vtab_impl::assign_update (md, mod, std::is_base_of<sqlite::vtab::modifiable, table_type>{});
+ vtab_impl::assign_transaction (md, mod, std::is_base_of<sqlite::vtab::transaction, table_type>{});
+ vtab_impl::assign_find_function(md, mod, std::is_base_of<sqlite::vtab::overload_functions, table_type>{});
+ vtab_impl::assign_rename (md, mod, std::is_base_of<sqlite::vtab::renamable, table_type>{});
+#if SQLITE_VERSION_NUMBER >= 3007007
+ vtab_impl::assign_recursive_transaction(md, mod, std::is_base_of<sqlite::vtab::recursive_transaction, table_type>{});
+#endif
+#if SQLITE_VERSION_NUMBER >= 3026000
+ vtab_impl::assign_shadow_name(md, mod);
+#endif
+ return md;
+}
+
+}
+
+BOOST_SQLITE_END_NAMESPACE
+
+#endif //BOOST_SQLITE_DETAIL_VTABLE_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/detail/window_function.hpp b/subprojects/boost-sqlite/include/boost/sqlite/detail/window_function.hpp
new file mode 100644
index 0000000..df8475c
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/detail/window_function.hpp
@@ -0,0 +1,238 @@
+// Copyright (c) 2023 Klemens D. Morgenstern
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#ifndef BOOST_SQLITE_IMPL_WINDOW_FUNCTION_HPP
+#define BOOST_SQLITE_IMPL_WINDOW_FUNCTION_HPP
+#if SQLITE_VERSION_NUMBER >= 3025000
+
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/cstring_ref.hpp>
+#include <boost/sqlite/memory.hpp>
+#include <boost/sqlite/result.hpp>
+#include <boost/sqlite/value.hpp>
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+namespace detail
+{
+
+template<typename Func>
+struct window_function_maker
+{
+ void * mem;
+
+ template<typename ... Args>
+ Func* operator()(Args && ... args)
+ {
+ return new (mem) Func(std::forward<Args>(args)...);
+ }
+};
+
+template<typename Func, typename Args>
+int create_window_function(sqlite3 * db, cstring_ref name, Args && args, int flags,
+ std::true_type /* is void */)
+{
+ using args_type = callable_traits::args_t<decltype(&Func::step)>;
+ using span_type = typename std::tuple_element<1U, args_type>::type;
+ using func_type = typename std::decay<Func>::type;
+ using func_args_type = typename std::decay<Args>::type;
+
+ return sqlite3_create_window_function(
+ db, name.c_str(),
+ span_type::extent == boost::dynamic_extent ? -1 : static_cast<int>(span_type::extent),
+ SQLITE_UTF8 | flags,
+ new (memory_tag{}) func_args_type(std::forward<Args>(args)),
+ +[](sqlite3_context* ctx, int len, sqlite3_value** args) noexcept //xStep
+ {
+ auto aa = reinterpret_cast<value*>(args);
+ auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx));
+ auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0));
+
+ execute_context_function(
+ ctx,
+ [&]() -> result<void>
+ {
+ if (c == nullptr)
+ {
+ auto p = sqlite3_aggregate_context(ctx, sizeof(func_type));
+ if (!p)
+ return error(SQLITE_NOMEM);
+ c = mp11::tuple_apply(window_function_maker<func_type>{p}, *fa);
+ }
+ c->step(span_type{aa, static_cast<std::size_t>(len)});
+ return {};
+ });
+
+ },
+ [](sqlite3_context* ctx) // xFinal
+ {
+ auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx));
+ auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0));
+
+ execute_context_function(
+ ctx,
+ [&]() -> result<decltype(c->value())>
+ {
+ if (c == nullptr)
+ {
+ auto p = sqlite3_aggregate_context(ctx, sizeof(func_type));
+ if (!p)
+ return error(SQLITE_NOMEM);
+ c = mp11::tuple_apply(window_function_maker<func_type>{p}, *fa);
+ }
+ struct reaper {void operator()(func_type * c) { c->~func_type();}};
+ std::unique_ptr<func_type, reaper> cl{c};
+
+ return c->value();
+ });
+ },
+ [](sqlite3_context* ctx) //xValue
+ {
+ auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx));
+ auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0));
+ execute_context_function(
+ ctx,
+ [&]() -> result<decltype(c->value())>
+ {
+ if (c == nullptr)
+ {
+ auto p = sqlite3_aggregate_context(ctx, sizeof(func_type));
+ if (!p)
+ return error(SQLITE_NOMEM);
+ c = mp11::tuple_apply(window_function_maker<func_type>{p}, *fa);
+ }
+ return c->value();
+ });
+
+ },
+ +[](sqlite3_context* ctx, int len, sqlite3_value** args) // xInverse
+ {
+ auto aa = reinterpret_cast<value*>(args);
+ auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx));
+ auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0));
+ execute_context_function(
+ ctx,
+ [&]() -> result<decltype(c->inverse(span_type{aa, static_cast<std::size_t>(len)}))>
+ {
+ if (c == nullptr)
+ {
+ auto p = sqlite3_aggregate_context(ctx, sizeof(func_type));
+ if (!p)
+ return error(SQLITE_NOMEM);
+ c = mp11::tuple_apply(window_function_maker<func_type>{p}, *fa);
+ }
+ c->inverse(span_type{aa, static_cast<std::size_t>(len)});
+ return {};
+ });
+
+ },
+ [](void * ptr) /* xDestroy */ { delete_(static_cast<func_type*>(ptr));}
+ );
+}
+
+template<typename Func, typename Args>
+int create_window_function(sqlite3 * db, cstring_ref name, Args && args, int flags,
+ std::false_type /* is void */)
+{
+ using args_type = callable_traits::args_t<decltype(&Func::step)>;
+ using span_type = typename std::tuple_element<1U, args_type>::type;
+ using func_type = typename std::decay<Func>::type;
+ using func_args_type = typename std::decay<Args>::type;
+
+ return sqlite3_create_window_function(
+ db, name.c_str(),
+ span_type::extent == boost::dynamic_extent ? -1 : static_cast<int>(span_type::extent),
+ SQLITE_UTF8 | flags,
+ new (memory_tag{}) func_args_type(std::forward<Args>(args)),
+ +[](sqlite3_context* ctx, int len, sqlite3_value** args) noexcept //xStep
+ {
+ auto aa = reinterpret_cast<value*>(args);
+ auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx));
+ auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0));
+
+ execute_context_function(
+ ctx,
+ [&]() -> result<decltype(c->step(span_type{aa, static_cast<std::size_t>(len)}))>
+ {
+ if (c == nullptr)
+ {
+ auto p = sqlite3_aggregate_context(ctx, sizeof(func_type));
+ if (!p)
+ return {system::in_place_error, error(SQLITE_NOMEM)};
+ c = mp11::tuple_apply(window_function_maker<func_type>{p}, *fa);
+ }
+ return c->step(span_type{aa, static_cast<std::size_t>(len)});
+ });
+
+ },
+ [](sqlite3_context* ctx) // xFinal
+ {
+ auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx));
+ auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0));
+ execute_context_function(
+ ctx,
+ [&]() -> result<decltype(c->value())>
+ {
+ if (c == nullptr)
+ {
+ auto p = sqlite3_aggregate_context(ctx, sizeof(func_type));
+ if (!p)
+ return {system::in_place_error, error(SQLITE_NOMEM)};
+ c = mp11::tuple_apply(window_function_maker<func_type>{p}, *fa);
+ }
+
+ struct reaper {void operator()(func_type * c) { c->~func_type();}};
+ std::unique_ptr<func_type, reaper> cl{c};
+ return c->value();
+ });
+ },
+ [](sqlite3_context* ctx) //xValue
+ {
+ auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx));
+ auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0));
+ execute_context_function(
+ ctx,
+ [&]() -> result<decltype(c->value())>
+ {
+ if (c == nullptr)
+ {
+ auto p = sqlite3_aggregate_context(ctx, sizeof(func_type));
+ if (!p)
+ return {system::in_place_error, error(SQLITE_NOMEM)};
+ c = mp11::tuple_apply(window_function_maker<func_type>{p}, *fa);
+ }
+ return c->value();
+ });
+
+ },
+ +[](sqlite3_context* ctx, int len, sqlite3_value** args) // xInverse
+ {
+ auto aa = reinterpret_cast<value*>(args);
+ auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx));
+ auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0));
+ execute_context_function(
+ ctx,
+ [&]() -> result<decltype(c->inverse(span_type{aa, static_cast<std::size_t>(len)}))>
+ {
+ if (c == nullptr)
+ {
+ auto p = sqlite3_aggregate_context(ctx, sizeof(func_type));
+ if (!p)
+ return {system::in_place_error, error(SQLITE_NOMEM)};
+ c = mp11::tuple_apply(window_function_maker<func_type>{p}, *fa);
+ }
+ return c->inverse(span_type{aa, static_cast<std::size_t>(len)});
+ });
+
+ },
+ [](void * ptr) /* xDestroy */ { delete_(static_cast<func_type*>(ptr));}
+ );
+}
+
+
+}
+
+BOOST_SQLITE_END_NAMESPACE
+#endif // SQLITE_VERSION
+#endif //BOOST_SQLITE_IMPL_WINDOW_FUNCTION_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/error.hpp b/subprojects/boost-sqlite/include/boost/sqlite/error.hpp
new file mode 100644
index 0000000..9fe6211
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/error.hpp
@@ -0,0 +1,178 @@
+//
+// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef BOOST_SQLITE_ERROR_HPP
+#define BOOST_SQLITE_ERROR_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/cstring_ref.hpp>
+#include <boost/sqlite/memory.hpp>
+#include <boost/system/error_code.hpp>
+#include <boost/system/error_category.hpp>
+#include <boost/system/result.hpp>
+
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+BOOST_SQLITE_DECL
+system::error_category & sqlite_category();
+
+
+/**
+ * \brief Additional information about error conditions stored in an sqlite-allocate string
+ * \ingroup reference
+ * \details Contains an error message describing what happened. Not all error
+ * conditions are able to generate this extended information - those that
+ * can't have an empty error message.
+ */
+struct error_info
+{
+ /// Default constructor.
+ error_info() = default;
+
+ /// Initialization constructor.
+ error_info(core::string_view msg) noexcept : msg_(new (memory_tag{}) char[msg.size() + 1u])
+ {
+ std::memcpy(msg_.get(), msg.data(), (std::min)(msg.size() + 1, capacity()));
+ }
+
+ /// set the message by copy
+ void set_message(core::string_view msg)
+ {
+ reserve(msg.size() + 1u);
+ std::memcpy(msg_.get(), msg.data(), (std::min)(msg.size() + 1, capacity()));
+ }
+ /// transfer ownership into the message
+ void reset(char * c = nullptr)
+ {
+ msg_.reset(c);
+ }
+
+ /// use sqlite_mprintf to generate a message
+#if defined(__GNUC__)
+ cstring_ref format(const char * fmt, ...) __attribute__((format (printf, 2, 3)))
+ {
+ va_list args;
+ va_start(args, fmt);
+ msg_.reset(sqlite3_vmprintf(fmt, args));
+ va_end(args);
+ return msg_.get();
+ }
+
+#endif
+ cstring_ref format(cstring_ref fmt, ...)
+ {
+ va_list args;
+ va_start(args, fmt);
+ msg_.reset(sqlite3_vmprintf(fmt.c_str(), args));
+ va_end(args);
+ return msg_.get();
+ }
+
+ /// use sqlite_snprintf to generate a message
+#if defined(__GNUC__)
+ cstring_ref snformat(const char * fmt, ...) __attribute__((format (printf, 2, 3)))
+ {
+ if (capacity() == 0)
+ return "";
+ va_list args;
+ va_start(args, fmt);
+ sqlite3_vsnprintf(static_cast<int>(capacity()), msg_.get(), fmt, args);
+ va_end(args);
+ return msg_.get();
+ }
+
+#endif
+ cstring_ref snformat(cstring_ref fmt, ...)
+ {
+ if (capacity() == 0)
+ return "";
+ va_list args;
+ va_start(args, fmt);
+ sqlite3_vsnprintf(static_cast<int>(capacity()), msg_.get(), fmt.c_str(), args);
+ va_end(args);
+ return msg_.get();
+ }
+ /// reserve data in the buffer i.e. allocate
+ void reserve(std::size_t sz)
+ {
+ if (msg_)
+ {
+ if (sqlite3_msize(msg_.get()) < sz)
+ msg_.reset(static_cast<char*>(sqlite3_realloc64(msg_.release(), sz)));
+ }
+ else
+ msg_.reset(static_cast<char*>(sqlite3_malloc64(sz)));
+ }
+
+ /// Get the allocated memory
+ std::size_t capacity() const {return msg_ ? sqlite3_msize(msg_.get()) : 0u;}
+
+ /// Gets the error message.
+ cstring_ref message() const noexcept { return msg_ ? msg_.get() : ""; }
+
+ char * release()
+ {
+ return msg_.release();
+ }
+ /// Restores the object to its initial state.
+ void clear() noexcept { if (msg_) *msg_ = '\0'; }
+
+
+ operator bool() const {return msg_.operator bool();}
+ private:
+ unique_ptr<char> msg_;
+};
+
+
+/**
+ * \brief An error containing both a code & optional message.
+ * \ingroup reference
+ * \details Contains an error .
+ */
+struct error
+{
+ /// The code of the error.
+ int code;
+ /// The additional information of the error
+ error_info info;
+
+ error(int code, error_info info) : code(code), info(std::move(info)) {}
+ explicit error(int code) : code(code) {}
+ error(int code, core::string_view info)
+ : code(code),
+ info(info) {}
+
+ error(system::error_code code, error_info info)
+ : code(code.category() == sqlite_category() ? code.value() : SQLITE_FAIL),
+ info(std::move(info)) {}
+
+ error(system::error_code code) : code(code.category() == sqlite_category() ? code.value() : SQLITE_FAIL)
+ {
+ if (code.category() == sqlite_category())
+ info = error_info{code.what()};
+ }
+ error() = default;
+ error(error && ) noexcept = default;
+};
+
+BOOST_NORETURN BOOST_SQLITE_DECL void throw_exception_from_error( error const & e, boost::source_location const & loc );
+
+template<typename T = void>
+using result = system::result<T, error>;
+
+template<typename T>
+struct is_result_type : std::false_type {};
+
+template<typename T>
+struct is_result_type<result<T>> : std::true_type {};
+
+
+BOOST_SQLITE_END_NAMESPACE
+
+
+#endif // BOOST_SQLITE_ERROR_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/extension.hpp b/subprojects/boost-sqlite/include/boost/sqlite/extension.hpp
new file mode 100644
index 0000000..32553fa
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/extension.hpp
@@ -0,0 +1,67 @@
+//
+// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef BOOST_SQLITE_EXTENSION_HPP
+#define BOOST_SQLITE_EXTENSION_HPP
+
+#define BOOST_SQLITE_COMPILE_EXTENSION 1
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/connection.hpp>
+#include <boost/sqlite/detail/catch.hpp>
+#include <boost/config.hpp>
+
+
+/** @brief Declare a sqlite module.
+ @ingroup reference
+
+ @param Name The name of the module
+ @param Conn The parameter name of the connection
+
+ This macro can be used to create an sqlite extension.
+
+ @note When defining BOOST_SQLITE_COMPILE_EXTENSION (was is done in extension.hpp)
+ sqlite will use an inline namespace to avoid symbol clashes.
+
+ @par Examples
+
+ @code{.cpp}
+
+ BOOST_SQLITE_EXTENSION(extension, conn)
+ {
+ 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");
+ });
+ }
+
+ @endcode{.cpp}
+
+ */
+#define BOOST_SQLITE_EXTENSION(Name, Conn) \
+void sqlite_##Name##_impl (boost::sqlite::connection Conn); \
+extern "C" BOOST_SYMBOL_EXPORT \
+int sqlite3_##Name##_init( \
+ sqlite3 *db, \
+ char **pzErrMsg, \
+ const sqlite3_api_routines *pApi) \
+{ \
+ using boost::sqlite::sqlite3_api; \
+ SQLITE_EXTENSION_INIT2(pApi); \
+ \
+ BOOST_SQLITE_TRY \
+ { \
+ sqlite_##Name##_impl(boost::sqlite::connection{db, false}); \
+ return SQLITE_OK; \
+ } \
+ BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(*pzErrMsg); \
+} \
+void sqlite_##Name##_impl(boost::sqlite::connection Conn)
+
+#endif //BOOST_SQLITE_EXTENSION_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/field.hpp b/subprojects/boost-sqlite/include/boost/sqlite/field.hpp
new file mode 100644
index 0000000..20fe4bd
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/field.hpp
@@ -0,0 +1,85 @@
+// Copyright (c) 2022 Klemens D. Morgenstern
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#ifndef BOOST_SQLITE_FIELD_HPP
+#define BOOST_SQLITE_FIELD_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/blob.hpp>
+#include <boost/sqlite/cstring_ref.hpp>
+#include <boost/sqlite/value.hpp>
+
+#include <boost/core/detail/string_view.hpp>
+
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+/** @brief A holder for a sqlite field, i.e. something returned from a query.
+ @ingroup reference
+ */
+struct field
+{
+ typedef sqlite_int64 int64;
+
+ /// The type of the value
+ value_type type() const
+ {
+ return static_cast<value_type>( sqlite3_column_type(stm_, col_));
+ }
+ /// Is the held value null
+ bool is_null() const
+ {
+ return type() == value_type::null;
+ }
+ /// Is the held value is not null
+ explicit operator bool () const
+ {
+ return type() != value_type::null;
+ }
+ /// Returns the value as an `int64`.
+ int64 get_int() const
+ {
+ return sqlite3_column_int64(stm_, col_);
+ }
+ /// Returns the value as an `double`.
+ double get_double() const
+ {
+ return sqlite3_column_double(stm_, col_);
+ }
+ /// Returns the value as text, i.e. a string_view. Note that this value may be invalidated`.
+ BOOST_SQLITE_DECL
+ cstring_ref get_text() const;
+ /// Returns the value as blob, i.e. raw memory. Note that this value may be invalidated`.
+ BOOST_SQLITE_DECL
+ blob_view get_blob() const;
+ /// Returns the field as a value.
+ value get_value() const
+ {
+ return value(sqlite3_column_value(stm_, col_));
+ }
+ /// Returns the name of the column.
+ cstring_ref column_name() const
+ {
+ return sqlite3_column_name(stm_, col_);
+ }
+ /// Returns the name of the table.
+ cstring_ref table_name() const
+ {
+ return sqlite3_column_table_name(stm_, col_);
+ }
+ /// Returns the name of the original data source.
+ cstring_ref column_origin_name() const
+ {
+ return sqlite3_column_origin_name(stm_, col_);
+ }
+
+ private:
+ friend struct row;
+ sqlite3_stmt * stm_;
+ int col_ = -1;
+};
+
+BOOST_SQLITE_END_NAMESPACE
+
+#endif //BOOST_SQLITE_FIELD_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/function.hpp b/subprojects/boost-sqlite/include/boost/sqlite/function.hpp
new file mode 100644
index 0000000..14060b6
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/function.hpp
@@ -0,0 +1,452 @@
+//
+// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef BOOST_SQLITE_FUNCTION_HPP
+#define BOOST_SQLITE_FUNCTION_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/detail/aggregate_function.hpp>
+#include <boost/sqlite/detail/scalar_function.hpp>
+#include <boost/sqlite/detail/window_function.hpp>
+#include <boost/sqlite/connection.hpp>
+#include <boost/sqlite/result.hpp>
+#include <boost/sqlite/value.hpp>
+#include <boost/sqlite/detail/exception.hpp>
+#include <boost/sqlite/cstring_ref.hpp>
+
+#include <boost/core/span.hpp>
+#include <boost/callable_traits/args.hpp>
+#include <boost/callable_traits/has_void_return.hpp>
+#include <boost/callable_traits/return_type.hpp>
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+enum function_flags
+{
+ deterministic = SQLITE_DETERMINISTIC,
+ directonly = SQLITE_DIRECTONLY,
+ subtype = SQLITE_SUBTYPE,
+ innocuous = SQLITE_INNOCUOUS,
+ result_subtype = SQLITE_RESULT_SUBTYPE,
+#if defined(SQLITE_SELFORDER1)
+ selforder1 = SQLITE_SELFORDER1,
+#else
+ selforder1 = 0
+#endif
+};
+
+
+/** @brief A context that can be passed into scalar functions.
+ @ingroup reference
+
+ @tparam Args The argument that can be stored in the context.
+
+ @code{.cpp}
+ extern sqlite::connection conn;
+
+ sqlite::create_scalar_function(
+ conn, "my_sum",
+ [](sqlite::context<std::size_t> ctx,
+ boost::span<sqlite::value, 1u> args) -> std::size_t
+ {
+ auto value = args[0].get_int();
+ auto p = ctx.get_if<0>();
+ if (p != nullptr) // increment the counter
+ return (*p) += value;
+ else // set the initial value
+ ctx.set<0>(value);
+ return value;
+ });
+ @endcode
+
+*/
+template<typename ... Args>
+struct context
+{
+ template<std::size_t N>
+ using element = mp11::mp_take_c<mp11::mp_list<Args...>, N>;
+
+ /// Set the value in the context at position `Idx`
+ template<std::size_t Idx>
+ void set(element<Idx> value)
+ {
+ sqlite3_set_auxdata(ctx_, Idx, *static_cast<void**>(&value),
+ new (memory_tag{}) element<Idx>(std::move(value)),
+ +[](void * ptr)
+ {
+ delete_(static_cast<element<Idx> *>(ptr));
+ });
+ }
+
+ /// Returns the value in the context at position `Idx`. Throws if the value isn't set.
+ template<std::size_t Idx>
+ auto get() -> element<Idx> &
+ {
+ using type = element<Idx> ;
+ auto p = static_cast<type*>(sqlite3_get_auxdata(ctx_, Idx));
+ if (p == nullptr)
+ detail::throw_invalid_argument("argument not set",
+ BOOST_CURRENT_LOCATION);
+ return *p;
+ }
+
+ /// Returns the value in the context at position `Idx`. Returns nullptr .value isn't set.
+ template<std::size_t Idx>
+ auto get_if() -> element<Idx> *
+ {
+ using type = element<Idx> ;
+ return static_cast<type*>(sqlite3_get_auxdata(ctx_, Idx));
+ }
+
+
+ explicit context(sqlite3_context * ctx) noexcept : ctx_(ctx) {}
+
+ /// Set the result through the context, instead of returning it.
+ template<typename T>
+ auto set_result(T && val)
+#if !defined(BOOST_SQLITE_GENERATING_DOCS)
+ -> decltype(detail::set_result(static_cast<sqlite3_context*>(nullptr), std::forward<T>(val)))
+#endif
+ {
+ detail::set_result(ctx_, std::forward<T>(val));
+ }
+ /// Set the an error through the context, instead of throwing it.
+ void set_error(cstring_ref message, int code = SQLITE_ERROR)
+ {
+ sqlite3_result_error(ctx_, message.c_str(), -1);
+ sqlite3_result_error_code(ctx_, code);
+ }
+ /// Returns the connection of the context.
+ connection get_connection() const
+ {
+ return connection{sqlite3_context_db_handle(ctx_), false};
+ }
+
+ private:
+ sqlite3_context * ctx_;
+};
+
+
+///@{
+/** @brief create a scalar function
+ @ingroup reference
+
+ @param conn The connection to add the function to.
+ @param name The name of the function
+ @param func The function to be added
+ @param ec The system::error_code
+
+ @throws `system::system_error` when the overload without `ec` is used.
+
+ `func` must take `context<Args...>` as the first and a `span<value, N>` as the second value.
+ If `N` is not `dynamic_extent` it will be used to deduce the number of arguments for the function.
+
+ @par Example
+
+ @code{.cpp}
+ extern sqlite::connection conn;
+
+ sqlite::create_function(
+ conn, "my_sum",
+ [](sqlite::context<std::size_t> ctx,
+ boost::span<sqlite::value, 1u> args) -> std::size_t
+ {
+ auto value = args[0].get_int();
+ auto p = ctx.get_if<0>();
+ if (p != nullptr) // increment the counter
+ return (*p) += value;
+ else // set the initial value
+ ctx.set<0>(value);
+ return value;
+ });
+ @endcode
+
+ */
+template<typename Func>
+auto create_scalar_function(
+ connection & conn,
+ cstring_ref name,
+ Func && func,
+ function_flags flags,
+ system::error_code & ec,
+ error_info & ei)
+#if !defined(BOOST_SQLITE_GENERATING_DOCS)
+ -> typename std::enable_if<
+ std::is_same<
+ decltype(
+ detail::create_scalar_function(
+ static_cast<sqlite3*>(nullptr), name,
+ std::declval<Func>(), flags)
+ ), int>::value>::type
+#endif
+{
+ auto res = detail::create_scalar_function(conn.handle(), name,
+ std::forward<Func>(func), static_cast<int>(flags));
+ if (res != 0)
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, res);
+ ei.set_message(sqlite3_errmsg(conn.handle()));
+ }
+}
+
+///@{
+/** @brief create a scalar function
+ @ingroup reference
+
+ @param conn The connection to add the function to.
+ @param name The name of the function
+ @param func The function to be added
+ @param ec The system::error_code
+
+ @throws `system::system_error` when the overload without `ec` is used.
+
+ `func` must take `context<Args...>` as the first and a `span<value, N>` as the second value.
+ If `N` is not `dynamic_extent` it will be used to deduce the number of arguments for the function.
+
+ @par Example
+
+ @code{.cpp}
+ extern sqlite::connection conn;
+
+ sqlite::create_function(
+ conn, "to_upper",
+ [](sqlite::context<> ctx,
+ boost::span<sqlite::value, 1u> args) -> std::string
+ {
+ std::string res;
+ auto txt = val[0].get_text();
+ res.resize(txt.size());
+ std::transform(txt.begin(), txt.end(), res.begin(),
+ [](char c){return std::toupper(c);});
+ return value;
+ });
+ @endcode
+
+ */
+template<typename Func>
+auto create_scalar_function(
+ connection & conn,
+ cstring_ref name,
+ Func && func,
+ function_flags flags = {})
+ -> typename std::enable_if<
+ std::is_same<
+ decltype(
+ detail::create_scalar_function(
+ static_cast<sqlite3*>(nullptr), name,
+ std::declval<Func>(), flags)
+ ), int>::value>::type
+{
+ system::error_code ec;
+ error_info ei;
+ create_scalar_function(conn, name, std::forward<Func>(func), flags, ec, ei);
+ if (ec)
+ detail::throw_error_code(ec, ei);
+}
+///@}
+
+
+///@{
+/** @brief create a aggregate function
+ @ingroup reference
+
+ @param conn The connection to add the function to.
+ @param name The name of the function
+ @param args
+ @param ec The system::error_code
+
+@tparam Func The function to be added
+
+ @throws `system::system_error` when the overload without `ec` is used.
+
+
+ `func` needs to be an object with two functions:
+
+ @code{.cpp}
+ void step(boost::span<sqlite::value, N> args);
+ T final();
+ @endcode
+
+
+
+ An aggregrate function will create a new `Func` for a new `aggregate` from the args tuple and call `step` for every step.
+ When the aggregation is done `final` is called and the result is returned to sqlite.
+
+ @par Example
+
+ @code{.cpp}
+ extern sqlite::connection conn;
+
+ struct aggregate_func
+ {
+ aggregate_func(std::size_t init) : counter(init) {}
+ std::size_t counter;
+ void step(, boost::span<sqlite::value, 1u> val)
+ {
+ counter += val[0].get_text().size();
+ }
+
+ std::size_t final()
+ {
+ return counter;
+ }
+ };
+
+ sqlite::create_function<aggregate_func>(
+ conn, "char_counter", std::make_tuple(42));
+
+ @endcode
+
+ */
+template<typename Func, typename Args = std::tuple<>>
+void create_aggregate_function(
+ connection & conn,
+ cstring_ref name,
+ Args && args,
+ function_flags flags,
+ system::error_code & ec,
+ error_info & ei)
+{
+ using func_type = typename std::decay<Func>::type;
+ auto res = detail::create_aggregate_function<Func>(
+ conn.handle(), name, std::forward<Args>(args), static_cast<int>(flags),
+ callable_traits::has_void_return<decltype(&func_type::step)>{}
+ );
+ if (res != 0)
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, res);
+ ei.set_message(sqlite3_errmsg(conn.handle()));
+ }
+}
+
+template<typename Func, typename Args = std::tuple<>>
+void create_aggregate_function(
+ connection & conn,
+ cstring_ref name,
+ Args && args= {},
+ function_flags flags = {})
+{
+ system::error_code ec;
+ error_info ei;
+ create_aggregate_function<Func>(conn, name, std::forward<Args>(args), flags, ec, ei);
+ if (ec)
+ detail::throw_error_code(ec, ei);
+}
+///@}
+
+#if SQLITE_VERSION_NUMBER >= 3025000
+
+///@{
+/** @brief create a aggregate window function
+ @ingroup reference
+
+ @param conn The connection to add the function to.
+ @param name The name of the function
+ @param args The arguments to construct Func from.
+ @param ec The system::error_code
+
+@tparam Func The function to be added
+
+ @throws `system::system_error` when the overload without `ec` is used.
+
+ `func` needs to be an object with three functions:
+
+ @code{.cpp}
+ void step(boost::span<sqlite::value, N> args);
+ void inverse(boost::span<sqlite::value, N> args);
+ T final();
+ @endcode
+
+ `State` can be any type and will get deduced together with `N`.
+ An aggregrate function will create a new `State` for a new `aggregate` and call `step` for every step.
+ When an element is removed from the window `inverse` is called.
+ When the aggregation is done `final` is called and the result is returned to sqlite.
+
+ @par Example
+
+ @code{.cpp}
+ extern sqlite::connection conn;
+
+ struct window_func
+ {
+ std::size_t counter;
+ void step(boost::span<sqlite::value, 1u> val)
+ {
+ counter += val[0].get_text().size();
+ }
+ void inverse(boost::span<sqlite::value, 1u> val)
+ {
+ counter -= val[0].get_text().size();
+ }
+
+ std::size_t final()
+ {
+ return counter;
+ }
+ };
+
+ sqlite::create_function(
+ conn, "win_char_counter",
+ aggregate_func{});
+ @endcode
+ */
+template<typename Func, typename Args = std::tuple<>>
+void create_window_function(
+ connection & conn,
+ cstring_ref name,
+ Args && args,
+ function_flags flags,
+ system::error_code & ec)
+{
+ using func_type = typename std::decay<Func>::type;
+ auto res = detail::create_window_function<Func>(
+ conn.handle(), name, std::forward<Args>(args), static_cast<int>(flags),
+ callable_traits::has_void_return<decltype(&func_type::step)>{});
+ if (res != 0)
+ BOOST_SQLITE_ASSIGN_EC(ec, res);
+}
+
+template<typename Func, typename Args = std::tuple<>>
+void create_window_function(
+ connection & conn,
+ cstring_ref name,
+ Args && args = {},
+ function_flags flags = {})
+{
+ system::error_code ec;
+ create_window_function<Func>(conn, name, std::forward<Args>(args), flags, ec);
+ if (ec)
+ detail::throw_error_code(ec);
+}
+
+///@}
+
+///@{
+/// Delete function
+
+inline void delete_function(connection & conn, cstring_ref name, int argc, system::error_code &ec)
+{
+ auto res = sqlite3_create_function_v2(conn.handle(), name.c_str(), argc, 0, nullptr, nullptr, nullptr, nullptr, nullptr);
+ if (res != 0)
+ BOOST_SQLITE_ASSIGN_EC(ec, res);
+
+}
+
+inline void delete_function(connection & conn, cstring_ref name, int argc = -1)
+{
+ system::error_code ec;
+ delete_function(conn, name, argc, ec);
+ if (ec)
+ detail::throw_error_code(ec);
+}
+
+///@}
+#endif
+
+BOOST_SQLITE_END_NAMESPACE
+
+#endif //BOOST_SQLITE_FUNCTION_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/hooks.hpp b/subprojects/boost-sqlite/include/boost/sqlite/hooks.hpp
new file mode 100644
index 0000000..50cb4fc
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/hooks.hpp
@@ -0,0 +1,370 @@
+//
+// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef BOOST_SQLITE_HOOKS_HPP
+#define BOOST_SQLITE_HOOKS_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/function.hpp>
+#include <boost/system/result.hpp>
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+
+#if defined(SQLITE_ENABLE_PREUPDATE_HOOK)
+/** The context for pre-update events
+
+ @note This is only available if sqlite was compiled with `SQLITE_ENABLE_PREUPDATE_HOOK` enabled.
+
+ */
+struct preupdate_context
+{
+ /// Returns the old value, i.e. the value before the update.
+ system::result<value> old(int column) const
+ {
+ sqlite3_value * val;
+ int res = sqlite3_preupdate_old(db_, column, &val);
+ if (res != 0)
+ BOOST_SQLITE_RETURN_EC(res);
+ return value(val);
+ }
+ /// The count of colums to be updated
+ int count() const { return sqlite3_preupdate_count(db_); }
+ /// The nesting depth of the update.
+ int depth() const { return sqlite3_preupdate_depth(db_); }
+ /// The new value to be written to column
+ system::result<value> new_(int column) const
+ {
+ sqlite3_value * val;
+ int res = sqlite3_preupdate_new(db_, column, &val);
+ if (res != 0)
+ BOOST_SQLITE_RETURN_EC(res);
+ return value(val);
+ }
+
+ /// @brief Query the status of blob access, e.g. when using @ref blob_handle
+ /// @see https://www.sqlite.org/c3ref/preupdate_blobwrite.html
+ int blob_write() const { return sqlite3_preupdate_blobwrite(db_); }
+
+ explicit preupdate_context(sqlite3 * db) noexcept : db_(db) {}
+ private:
+ sqlite3 * db_;
+};
+
+#endif
+
+namespace detail
+{
+
+
+template<typename Func>
+bool commit_hook_impl(sqlite3 * db,
+ Func * func,
+ std::true_type)
+{
+ static_assert(noexcept(func()), "hook must be noexcept");
+ return sqlite3_commit_hook( db, func, [](void * data) { return (*static_cast<Func *>(data))() ? 1 : 0; }, nullptr) != nullptr;
+}
+
+
+template<typename Func>
+bool commit_hook_impl(sqlite3 * db,
+ Func && func,
+ std::false_type)
+{
+ static_assert(noexcept(func()), "hook must be noexcept");
+ return sqlite3_commit_hook(
+ db,
+ [](void * data) { return (*static_cast<Func *>(data))() ? 1 : 0; },
+ &func) != nullptr;
+}
+
+inline bool commit_hook(sqlite3 * db, std::nullptr_t, std::false_type)
+{
+ return sqlite3_commit_hook(db, nullptr, nullptr);
+}
+
+
+
+template<typename Func>
+bool commit_hook(sqlite3 * db,
+ Func && func)
+{
+ using func_type = typename std::decay<Func>::type;
+ return commit_hook_impl(db, std::forward<Func>(func), std::is_pointer<func_type>{});
+}
+
+
+
+template<typename Func>
+bool rollback_hook_impl(sqlite3 * db,
+ Func * func,
+ std::true_type)
+{
+ static_assert(noexcept(func()), "hook must be noexcept");
+ return sqlite3_rollback_hook( db, func, [](void * data) { (*static_cast<Func *>(data))(); }, nullptr) != nullptr;
+}
+
+
+template<typename Func>
+bool rollback_hook_impl(sqlite3 * db,
+ Func && func,
+ std::false_type)
+{
+ static_assert(noexcept(func()), "hook must be noexcept");
+ return sqlite3_rollback_hook(
+ db,
+ [](void * data) { (*static_cast<Func *>(data))(); },
+ &func) != nullptr;
+}
+
+inline bool rollback_hook_impl(sqlite3 * db, std::nullptr_t, std::false_type)
+{
+ return sqlite3_rollback_hook(db, nullptr, nullptr);
+}
+
+template<typename Func>
+bool rollback_hook(sqlite3 * db,
+ Func && func)
+{
+ using func_type = typename std::decay<Func>::type;
+ return rollback_hook_impl(db, std::forward<Func>(func), std::is_pointer<func_type>{});
+}
+
+#if defined(SQLITE_ENABLE_PREUPDATE_HOOK)
+
+template<typename Func>
+bool preupdate_hook_impl(sqlite3 * db,
+ Func * func,
+ std::true_type)
+{
+ static_assert(noexcept(func(preupdate_context(nullptr), SQLITE_SELECT, "", "", 0, 0)), "hook must be noexcept");
+ return sqlite3_preupdate_hook(
+ db, func,
+ [](void * data,
+ sqlite3* db,
+ int op,
+ const char * db_name,
+ const char * table_name,
+ sqlite_int64 key1,
+ sqlite_int64 key2)
+ {
+ (*static_cast<Func *>(data))(preupdate_context(db), op, db_name, table_name, key1, key2);
+ }, nullptr) != nullptr;
+}
+
+
+template<typename Func>
+bool preupdate_hook_impl(sqlite3 * db,
+ Func & func,
+ std::false_type)
+{
+ static_assert(noexcept(func(preupdate_context(nullptr), SQLITE_SELECT, "", "", 0, 0)),
+ "hooks but be noexcept");
+ using func_type = typename std::decay<Func>::type;
+
+ return sqlite3_preupdate_hook(
+ db,
+ [](void * data,
+ sqlite3* db,
+ int op,
+ const char * db_name,
+ const char * table_name,
+ sqlite_int64 key1,
+ sqlite_int64 key2)
+ {
+ (*static_cast<Func *>(data))(preupdate_context(db), op, db_name, table_name, key1, key2);
+ }, &func) != nullptr;
+}
+
+inline bool preupdate_hook_impl(sqlite3 * db, std::nullptr_t, std::false_type)
+{
+ return sqlite3_preupdate_hook(db, nullptr, nullptr);
+}
+
+template<typename Func>
+bool preupdate_hook(sqlite3 * db,
+ Func && func)
+{
+ using func_type = typename std::decay<Func>::type;
+ return preupdate_hook_impl(db, std::forward<Func>(func), std::is_pointer<func_type>{});
+}
+
+#endif
+
+template<typename Func>
+bool update_hook_impl(sqlite3 * db,
+ Func * func,
+ std::true_type)
+{
+ static_assert(noexcept(func(SQLITE_SELECT, "", "", 0)), "hook must be noexcept");
+ return sqlite3_update_hook(
+ db, func,
+ [](void * data,
+ sqlite3*,
+ int op,
+ const char * db,
+ const char * name,
+ sqlite_int64 key)
+ {
+ (*static_cast<Func *>(data))(op, db, name, key);
+ }, nullptr) != nullptr;
+}
+
+
+template<typename Func>
+bool update_hook_impl(sqlite3 * db,
+ Func & func,
+ std::false_type)
+{
+ static_assert(noexcept(func(SQLITE_SELECT, "", "", 0)), "hook must be noexcept");
+ using func_type = typename std::decay<Func>::type;
+
+ return sqlite3_update_hook(
+ db,
+ [](void * data,
+ int op,
+ const char * db,
+ const char * name,
+ sqlite_int64 key)
+ {
+ (*static_cast<func_type*>(data))(op, db, name, key);
+ }, &func) != nullptr;
+}
+
+inline
+bool update_hook_impl(sqlite3 * db,
+ std::nullptr_t,
+ std::false_type)
+{
+ return sqlite3_update_hook(db, nullptr, nullptr);
+}
+
+template<typename Func>
+bool update_hook(sqlite3 * db,
+ Func && func)
+{
+ using func_type = typename std::decay<Func>::type;
+ return update_hook_impl(db, std::forward<Func>(func), std::is_pointer<func_type>{});
+}
+
+
+}
+
+/**
+ @brief Install a commit hook
+ @ingroup reference
+
+ @see [related sqlite documentation](https://www.sqlite.org/c3ref/commit_hook.html)
+
+ The commit hook gets called before a commit gets performed.
+ If `func` returns true, the commit goes, otherwise it gets rolled back.
+
+ @note If the function is not a free function pointer, this function will *NOT* take ownership.
+
+ @note If `func` is a `nullptr` the hook gets reset.
+
+ @param conn The database connection to install the hook in
+ @param func The hook function
+ @return true if an hook has been replaced.
+ */
+template<typename Func>
+bool commit_hook(connection & conn, Func && func)
+{
+ return detail::commit_hook(conn.handle(), std::forward<Func>(func));
+}
+
+/**
+ @brief Install a rollback hook
+ @ingroup reference
+
+ @see [related sqlite documentation](https://www.sqlite.org/c3ref/commit_hook.html)
+
+ The rollback hook gets called when a rollback gets performed.
+
+ @note If the function is not a free function pointer, this function will *NOT* take ownership.
+
+ @note If `func` is a `nullptr` the hook gets reset.
+
+ @param conn The database connection to install the hook in
+ @param func The hook function
+ @return true if an hook has been replaced.
+ */
+template<typename Func>
+bool rollback_hook(connection & conn, Func && func)
+{
+ return detail::rollback_hook(conn.handle(), std::forward<Func>(func));
+}
+
+#if defined(SQLITE_ENABLE_PREUPDATE_HOOK)
+/** A hook for pre-update events.
+
+ @note This is only available if sqlite was compiled with `SQLITE_ENABLE_PREUPDATE_HOOK` enabled.
+ @ingroup reference
+
+ @see [related sqlite documentation](https://sqlite.org/c3ref/preupdate_count.html)
+
+ The function will get called
+
+ @note If the function is not a free function pointer, this function will *NOT* take ownership.
+
+ @note If `func` is a `nullptr` the hook gets reset.
+
+ @param conn The database connection to install the hook in
+ @param func The hook function
+ @return true if an hook has been replaced.
+
+ The signature of the handler is as following (it must noexcept):
+
+ @code{.cpp}
+ void preupdate_hook(sqlite::preupdate_context ctx,
+ int op,
+ const char * db_name,
+ const char * table_name,
+ sqlite3_int64 current_key,
+ sqlite3_int64 new_key);
+ @endcode
+
+
+
+*/
+template<typename Func>
+bool preupdate_hook(connection & conn, Func && func)
+{
+ return detail::preupdate_hook(conn.handle(), std::forward<Func>(func));
+}
+
+#endif
+
+/**
+ @brief Install an update hook
+ @ingroup reference
+
+ @see [related sqlite documentation](https://www.sqlite.org/c3ref/update_hook.html)
+
+ The update hook gets called when an update was performed.
+
+ @note If the function is not a free function pointer, this function will *NOT* take ownership.
+
+ The signature of the function is `void(int op, core::string_view db, core::string_view table, sqlite3_int64 id)`.
+ `op` is either `SQLITE_INSERT`, `SQLITE_DELETE` and `SQLITE_UPDATE`.
+
+ @note If `func` is a `nullptr` the hook gets reset.
+
+ @param conn The database connection to install the hook in
+ @param func The hook function
+ @return true if an hook has been replaced.
+ */
+template<typename Func>
+bool update_hook(connection & conn, Func && func)
+{
+ return detail::update_hook(conn.handle(), std::forward<Func>(func));
+}
+
+BOOST_SQLITE_END_NAMESPACE
+
+#endif //BOOST_SQLITE_HOOKS_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/json.hpp b/subprojects/boost-sqlite/include/boost/sqlite/json.hpp
new file mode 100644
index 0000000..3409e56
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/json.hpp
@@ -0,0 +1,140 @@
+//
+// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef BOOST_SQLITE_JSON_HPP
+#define BOOST_SQLITE_JSON_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/field.hpp>
+#include <boost/sqlite/resultset.hpp>
+#include <boost/sqlite/value.hpp>
+#include <boost/json/parse.hpp>
+#include <boost/json/serializer.hpp>
+#include <boost/json/storage_ptr.hpp>
+#include <boost/json/value_from.hpp>
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+struct resultset;
+struct field;
+struct value;
+
+/// @brief The subtype value used by the sqlite json extension. See the [sqlite reference](https://www.sqlite.org/json1.html)
+constexpr int json_subtype = static_cast<int>('J');
+
+inline void tag_invoke(const struct set_result_tag &, sqlite3_context * ctx, const json::value & value)
+{
+ json::serializer ser;
+ ser.reset(&value);
+
+ sqlite3_int64 len = 4096;
+ unique_ptr<char> c{static_cast<char*>(sqlite3_malloc64(len))};
+
+ len = sqlite3_msize(c.get());
+ auto v = ser.read(c.get(), len);
+
+ while (!ser.done())
+ {
+ auto l = v.size();
+ len *= 2;
+ c.reset(static_cast<char*>(sqlite3_realloc(c.release(), len)));
+ v = ser.read(c.get() + l, len);
+ }
+
+ sqlite3_result_text(ctx, c.release(), v.size(), sqlite3_free);
+ sqlite3_result_subtype(ctx, json_subtype);
+}
+
+///@{
+/// @brief Check if the value or field is a json. @ingroup reference
+inline bool is_json(const value & v) { return v.type() == value_type::text && v.subtype() == json_subtype; }
+inline bool is_json(const field & f) { return f.type() == value_type::text && f.get_value().subtype() == json_subtype; }
+///@}
+
+///@{
+/// @brief Convert the value or field to a json. @ingroup reference
+inline json::value as_json(const value & v, json::storage_ptr ptr = {})
+{
+ return json::parse(v.get_text(), ptr);
+}
+inline json::value as_json(const field & f, json::storage_ptr ptr = {})
+{
+ return json::parse(f.get_text(), ptr);
+}
+///@}
+
+
+inline void tag_invoke( const json::value_from_tag &, json::value& val, const value & f)
+{
+ switch (f.type())
+ {
+ case value_type::integer:
+ val.emplace_int64() = f.get_int();
+ break;
+ case value_type::floating:
+ val.emplace_double() = f.get_double();
+ break;
+ case value_type::text:
+ {
+ auto txt = f.get_text();
+ if (f.subtype() == json_subtype)
+ val = json::parse(txt, val.storage());
+ else
+ val.emplace_string() = txt;
+ }
+ break;
+ case value_type::blob:
+ throw_exception(std::invalid_argument("cannot convert blob to json"));
+ case value_type::null:
+ default:
+ val.emplace_null();
+ }
+}
+
+inline void tag_invoke( const json::value_from_tag &, json::value& val, const field & f)
+{
+ switch (f.type())
+ {
+ case value_type::integer:
+ val.emplace_int64() = f.get_int();
+ break;
+ case value_type::floating:
+ val.emplace_double() = f.get_double();
+ break;
+ case value_type::text:
+ {
+ auto txt = f.get_text();
+ if (f.get_value().subtype() == json_subtype)
+ val = json::parse(txt, val.storage());
+ else
+ val.emplace_string() = txt;
+ }
+ break;
+ case value_type::blob:
+ throw_exception(std::invalid_argument("cannot convert blob to json"));
+ case value_type::null:
+ default:
+ val.emplace_null();
+ }
+}
+
+inline void tag_invoke( const json::value_from_tag &, json::value& val, resultset && rs)
+{
+ auto & obj = val.emplace_array();
+
+ for (auto r : rs)
+ {
+ auto & row = obj.emplace_back(json::object(obj.storage())).get_object();
+ for (auto c : r)
+ row[c.column_name()] = json::value_from(c, row.storage());
+ }
+}
+
+BOOST_SQLITE_END_NAMESPACE
+
+
+#endif //BOOST_SQLITE_JSON_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/memory.hpp b/subprojects/boost-sqlite/include/boost/sqlite/memory.hpp
new file mode 100644
index 0000000..e136a0e
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/memory.hpp
@@ -0,0 +1,105 @@
+// Copyright (c) 2023 Klemens D. Morgenstern
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#ifndef BOOST_SQLITE_MEMORY_HPP
+#define BOOST_SQLITE_MEMORY_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+
+#include <memory>
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+/// A tag to allow `operator new`
+/// @ingroup reference
+struct memory_tag {};
+BOOST_SQLITE_END_NAMESPACE
+
+
+inline void *operator new ( std::size_t size, boost::sqlite::memory_tag) noexcept
+{
+ using namespace boost::sqlite;
+ return sqlite3_malloc64(size);
+}
+inline void *operator new[]( std::size_t size, boost::sqlite::memory_tag) noexcept
+{
+ using namespace boost::sqlite;
+ return sqlite3_malloc64(size);
+}
+
+inline void operator delete ( void* ptr, boost::sqlite::memory_tag) noexcept
+{
+ using namespace boost::sqlite;
+ return sqlite3_free(ptr);
+}
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+template<typename T>
+void delete_(T * t)
+{
+ struct scoped_free
+ {
+ void * p;
+ ~scoped_free()
+ {
+ sqlite3_free(p);
+ }
+ };
+ scoped_free _{t};
+ t->~T();
+}
+
+namespace detail
+{
+
+template<typename T>
+struct deleter
+{
+ void operator()(T* t)
+ {
+ delete_(t);
+ }
+};
+
+template<typename T>
+struct deleter<T[]>
+{
+ static_assert(std::is_trivially_destructible<T>::value, "T[] needs to be trivially destructible");
+ void operator()(T* t)
+ {
+ sqlite3_free(t);
+ }
+};
+
+template<>
+struct deleter<void>
+{
+ void operator()(void* t)
+ {
+ sqlite3_free(t);
+ }
+};
+}
+
+template<typename T>
+using unique_ptr = std::unique_ptr<T, detail::deleter<T>>;
+
+template<typename T>
+inline std::size_t msize(const unique_ptr<T> & ptr)
+{
+ return sqlite3_msize(ptr.get());
+}
+
+template<typename T, typename ... Args>
+unique_ptr<T> make_unique(Args && ... args)
+{
+ unique_ptr<void> up{sqlite3_malloc64(sizeof(T))};
+ unique_ptr<T> res{new (up.get()) T(std::forward<Args>(args)...)};
+ up.release();
+ return res;
+}
+
+BOOST_SQLITE_END_NAMESPACE
+
+#endif //BOOST_SQLITE_MEMORY_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/meta_data.hpp b/subprojects/boost-sqlite/include/boost/sqlite/meta_data.hpp
new file mode 100644
index 0000000..00f6527
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/meta_data.hpp
@@ -0,0 +1,58 @@
+//
+// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef BOOST_SQLITE_META_DATA_HPP
+#define BOOST_SQLITE_META_DATA_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/connection.hpp>
+#include <boost/sqlite/cstring_ref.hpp>
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+
+struct connection ;
+
+/// The metadata of a column
+struct column_meta_data
+{
+ /// Data type fo the column
+ cstring_ref data_type;
+ /// Name of default collation sequence
+ cstring_ref collation;
+ /// true if column has a NOT NULL constraint
+ bool not_null;
+ /// true if column is part of the PRIMARY KEY
+ bool primary_key;
+ /// true if column is AUTOINCREMENT
+ bool auto_increment;
+};
+
+///@{
+/// get the meta-data of one colum
+BOOST_SQLITE_DECL
+column_meta_data table_column_meta_data(connection & conn,
+ cstring_ref db_name, cstring_ref table_name, cstring_ref column_name,
+ system::error_code & ec, error_info &ei);
+BOOST_SQLITE_DECL
+column_meta_data table_column_meta_data(connection & conn,
+ cstring_ref table_name, cstring_ref column_name,
+ system::error_code & ec, error_info &ei);
+
+BOOST_SQLITE_DECL
+column_meta_data table_column_meta_data(connection & conn,
+ cstring_ref db_name, cstring_ref table_name, cstring_ref column_name);
+BOOST_SQLITE_DECL
+column_meta_data table_column_meta_data(connection & conn,
+ cstring_ref table_name, cstring_ref column_name);
+///@}
+
+///
+BOOST_SQLITE_END_NAMESPACE
+
+
+#endif //BOOST_SQLITE_META_DATA_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/mutex.hpp b/subprojects/boost-sqlite/include/boost/sqlite/mutex.hpp
new file mode 100644
index 0000000..63caf0e
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/mutex.hpp
@@ -0,0 +1,55 @@
+//
+// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef BOOST_SQLITE_MUTEX_HPP
+#define BOOST_SQLITE_MUTEX_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+#include <memory>
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+/// A mutex class that maybe a noop depending on the mode sqlite3 was compiled as.
+struct mutex
+{
+ bool try_lock()
+ {
+ if (!impl_)
+ return false;
+ return sqlite3_mutex_try(impl_.get()) == SQLITE_OK;
+ }
+ void lock() { sqlite3_mutex_enter(impl_.get()); }
+ void unlock() { sqlite3_mutex_leave(impl_.get()); }
+
+ mutex() : impl_(sqlite3_mutex_alloc(SQLITE_MUTEX_FAST)) {}
+ mutex(mutex && ) = delete;
+ private:
+ struct deleter_ {void operator()(sqlite3_mutex *mtx) {sqlite3_mutex_free(mtx);}};
+ std::unique_ptr<sqlite3_mutex, deleter_> impl_;
+};
+
+/// A recursive mutex class that maybe a noop depending on the mode sqlite3 was compiled as.
+struct recursive_mutex
+{
+ bool try_lock()
+ {
+ if (!impl_)
+ return false;
+ return sqlite3_mutex_try(impl_.get()) == SQLITE_OK;
+ }
+ void lock() { sqlite3_mutex_enter(impl_.get()); }
+ void unlock() { sqlite3_mutex_leave(impl_.get()); }
+
+ recursive_mutex() : impl_(sqlite3_mutex_alloc(SQLITE_MUTEX_RECURSIVE)) {}
+ recursive_mutex(recursive_mutex && ) = delete;
+ private:
+ struct deleter_ {void operator()(sqlite3_mutex *mtx) {sqlite3_mutex_free(mtx);}};
+ std::unique_ptr<sqlite3_mutex, deleter_> impl_;
+};
+
+BOOST_SQLITE_END_NAMESPACE
+
+#endif //BOOST_SQLITE_MUTEX_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/result.hpp b/subprojects/boost-sqlite/include/boost/sqlite/result.hpp
new file mode 100644
index 0000000..b609eb4
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/result.hpp
@@ -0,0 +1,147 @@
+//
+// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef BOOST_SQLITE_RESULT_HPP
+#define BOOST_SQLITE_RESULT_HPP
+
+#include <boost/sqlite/blob.hpp>
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/value.hpp>
+
+#include <boost/variant2/variant.hpp>
+
+#include <type_traits>
+
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+
+struct set_result_tag {};
+
+
+inline void tag_invoke(set_result_tag, sqlite3_context * ctx, blob b)
+{
+ auto sz = b.size();
+ sqlite3_result_blob(ctx, std::move(b).release(), sz, &operator delete);
+}
+
+
+inline void tag_invoke(set_result_tag, sqlite3_context * ctx, zero_blob zb)
+{
+ sqlite3_result_zeroblob64(ctx, static_cast<sqlite3_uint64>(zb));
+}
+
+inline void tag_invoke(set_result_tag, sqlite3_context * ctx, double dbl) { sqlite3_result_double(ctx, dbl); }
+
+inline void tag_invoke(set_result_tag, sqlite3_context * ctx, sqlite3_int64 value)
+{
+ sqlite3_result_int64(ctx, static_cast<sqlite3_int64>(value));
+}
+
+template<typename = std::enable_if_t<!std::is_same<std::int64_t, sqlite3_int64>::value>>
+inline void tag_invoke(set_result_tag, sqlite3_context * ctx, std::int64_t value)
+
+{
+ sqlite3_result_int64(ctx, static_cast<sqlite3_int64>(value));
+}
+
+inline void tag_invoke(set_result_tag, sqlite3_context * ctx, std::nullptr_t) { sqlite3_result_null(ctx); }
+inline void tag_invoke(set_result_tag, sqlite3_context * ctx, string_view str)
+{
+ sqlite3_result_text(ctx, str.data(), str.size(), SQLITE_TRANSIENT);
+}
+template<typename String>
+inline auto tag_invoke(set_result_tag, sqlite3_context * ctx, String && str)
+ -> typename std::enable_if<std::is_convertible<String, string_view>::value>::type
+{
+ return tag_invoke(set_result_tag{}, ctx, string_view(str));
+}
+
+
+inline void tag_invoke(set_result_tag, sqlite3_context * , variant2::monostate) { }
+inline void tag_invoke(set_result_tag, sqlite3_context * ctx, const value & val)
+{
+ sqlite3_result_value(ctx, val.handle());
+}
+
+struct set_variant_result
+{
+ sqlite3_context * ctx;
+ template<typename T>
+ void operator()(T && val)
+ {
+ tag_invoke(set_result_tag{}, ctx, std::forward<T>(val));
+ }
+};
+
+template<typename ... Args>
+inline void tag_invoke(set_result_tag, sqlite3_context * ctx, const variant2::variant<Args...> & var)
+{
+ visit(set_variant_result{ctx}, var);
+}
+
+template<typename T>
+inline void tag_invoke(set_result_tag, sqlite3_context * ctx, std::unique_ptr<T> ptr)
+{
+ sqlite3_result_pointer(ctx, ptr.release(), typeid(T).name(), +[](void * ptr){delete static_cast<T*>(ptr);});
+}
+
+template<typename T>
+inline void tag_invoke(set_result_tag, sqlite3_context * ctx, std::unique_ptr<T, void(*)(T*)> ptr)
+{
+ sqlite3_result_pointer(ctx, ptr.release(), typeid(T).name(), static_cast<void(*)(void*)>(ptr.get_deleter()));
+}
+
+template<typename T, typename Deleter>
+inline auto tag_invoke(set_result_tag, sqlite3_context * ctx, std::unique_ptr<T, Deleter> ptr)
+ -> typename std::enable_if<std::is_empty<Deleter>::value &&
+ std::is_default_constructible<Deleter>::value>::type
+{
+ sqlite3_result_pointer(ctx, ptr.release(), typeid(T).name(), +[](void * ptr){Deleter()(static_cast<T*>(ptr));});
+}
+
+inline void tag_invoke(set_result_tag, sqlite3_context * ctx, error err)
+{
+ if (err.info)
+ sqlite3_result_error(ctx, err.info.message().c_str(), -1);
+ sqlite3_result_error_code(ctx, err.code);
+}
+
+
+template<typename T>
+inline void tag_invoke(set_result_tag tag, sqlite3_context * ctx, result<T> res)
+{
+ if (res.has_value())
+ tag_invoke(tag, ctx, std::move(res).value());
+ else
+ tag_invoke(tag, ctx, std::move(res).error());
+}
+
+inline void tag_invoke(set_result_tag tag, sqlite3_context * ctx, result<void> res)
+{
+ if (res.has_error())
+ tag_invoke(tag, ctx, std::move(res).error());
+}
+
+
+namespace detail
+{
+
+template<typename Value>
+inline auto set_result(sqlite3_context * ctx, Value && value)
+ -> decltype(tag_invoke(set_result_tag{}, ctx, std::forward<Value>(value)))
+{
+ tag_invoke(set_result_tag{}, ctx, std::forward<Value>(value));
+}
+
+
+}
+
+
+BOOST_SQLITE_END_NAMESPACE
+
+#endif //BOOST_SQLITE_RESULT_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/resultset.hpp b/subprojects/boost-sqlite/include/boost/sqlite/resultset.hpp
new file mode 100644
index 0000000..7c514f0
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/resultset.hpp
@@ -0,0 +1,150 @@
+// Copyright (c) 2022 Klemens D. Morgenstern
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#ifndef BOOST_SQLITE_RESULTSET_HPP
+#define BOOST_SQLITE_RESULTSET_HPP
+
+#include <memory>
+#include <boost/sqlite/row.hpp>
+#include <boost/describe/members.hpp>
+
+#include <boost/system/result.hpp>
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+struct connection ;
+/**
+ @brief Representation of a result from a database.
+ @ingroup reference
+
+ If is a forward-range with output iterators.
+
+ @par Example
+
+ @code{.cpp}
+
+ extern sqlite::connection conn;
+
+ sqlite::resultset rs = conn.query("select * from users;");
+
+ do
+ {
+ handle_row(r.current());
+ }
+ while (rs.read_next()) // read it line by line
+
+
+ @endcode
+
+*/
+struct resultset
+{
+ /// Returns the current row.
+ row current() const &
+ {
+ row r;
+ r.stm_ = impl_.get();
+ return r;
+ }
+ /// Checks if the last row has been reached.
+ bool done() const {return done_;}
+
+ ///@{
+ /// Read the next row. Returns false if there's nothing more to read.
+ BOOST_SQLITE_DECL bool read_next(system::error_code & ec, error_info & ei);
+ BOOST_SQLITE_DECL bool read_next();
+ ///@}
+
+ ///
+ std::size_t column_count() const
+ {
+ return sqlite3_column_count(impl_.get());
+ }
+ /// Returns the name of the column idx.
+ cstring_ref column_name(std::size_t idx) const
+ {
+ return sqlite3_column_name(impl_.get(), static_cast<int>(idx));
+ }
+ /// Returns the name of the source table for column idx.
+ cstring_ref table_name(std::size_t idx) const
+ {
+ return sqlite3_column_table_name(impl_.get(), static_cast<int>(idx));
+ }
+ /// Returns the origin name of the column for column idx.
+ cstring_ref column_origin_name(std::size_t idx) const
+ {
+ return sqlite3_column_origin_name(impl_.get(), static_cast<int>(idx));
+ }
+
+ /// The input iterator can be used to read every row in a for-loop
+ struct iterator
+ {
+ using value_type = value;
+ using difference_type = int;
+ using reference = value&;
+ using iterator_category = std::forward_iterator_tag;
+
+ iterator() {}
+ explicit iterator(sqlite3_stmt * stmt, bool sentinel) : sentinel_(sentinel )
+ {
+ row_.stm_ = stmt;
+ }
+
+ bool operator!=(iterator rhs) const
+ {
+ return sentinel_ != rhs.sentinel_;
+ }
+
+ row &operator*() { return row_; }
+ row *operator->() { return &row_; }
+
+ BOOST_SQLITE_DECL
+ iterator operator++();
+
+ iterator operator++(int)
+ {
+ auto l = *this;
+ ++(*this);
+ return l;
+ }
+
+ private:
+ row row_;
+ bool sentinel_ = true;
+ };
+
+ /// Return an input iterator to the currently unread row
+ iterator begin() { return iterator(impl_.get(), done_);}
+ /// Sentinel iterator.
+ iterator end() { return iterator(impl_.get(), true); }
+
+ private:
+ friend struct connection;
+ friend struct statement;
+
+ struct deleter_
+ {
+ constexpr deleter_() noexcept {}
+ bool delete_ = true;
+ void operator()(sqlite3_stmt * sm)
+ {
+ if (sqlite3_data_count(sm) > 0)
+ while ( sqlite3_step(sm) == SQLITE_ROW);
+ if (delete_)
+ sqlite3_finalize(sm);
+ else
+ {
+ sqlite3_clear_bindings(sm);
+ sqlite3_reset(sm);
+ }
+
+ }
+ };
+ std::unique_ptr<sqlite3_stmt, deleter_> impl_;
+ bool done_ = false;
+};
+
+BOOST_SQLITE_END_NAMESPACE
+
+
+#endif //BOOST_SQLITE_RESULTSET_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/row.hpp b/subprojects/boost-sqlite/include/boost/sqlite/row.hpp
new file mode 100644
index 0000000..6893f96
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/row.hpp
@@ -0,0 +1,167 @@
+// Copyright (c) 2022 Klemens D. Morgenstern
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#ifndef BOOST_SQLITE_ROW_HPP
+#define BOOST_SQLITE_ROW_HPP
+
+#include <boost/sqlite/field.hpp>
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+/** @brief Representation of a row in a database.
+ @ingroup reference
+
+ Is a random-access range.
+
+ All values that are obtained by view are valid until the next row is read.
+
+ */
+struct row
+{
+ /// The size of the row
+ std::size_t size() const
+ {
+ return sqlite3_column_count(stm_);
+ }
+ /// Returns the field at `idx`, @throws std::out_of_range
+ BOOST_SQLITE_DECL
+ field at(std::size_t idx) const;
+ /// Returns the field at `idx`.
+ field operator[](std::size_t idx) const
+ {
+ field f;
+ f.stm_ = stm_;
+ f.col_ = static_cast<int>(idx);
+ return f;
+ }
+ /// Random access iterator used to iterate over the columns.
+ struct const_iterator
+ {
+ using difference_type = int;
+ using reference = field&;
+ using iterator_category = std::random_access_iterator_tag;
+
+ const_iterator & operator++()
+ {
+ f_.col_++;
+ return *this;
+ }
+
+ const_iterator operator++(int)
+ {
+ auto last = *this;
+ ++(*this);
+ return last;
+ }
+
+ const_iterator & operator--()
+ {
+ f_.col_--;
+ return *this;
+ }
+
+ const_iterator operator--(int)
+ {
+ auto last = *this;
+ --(*this);
+ return last;
+ }
+
+ field operator[](int i) const
+ {
+ auto f = f_;
+ f.col_ += i;
+ return f;
+ }
+
+ const_iterator operator+(int i) const
+ {
+ auto r = *this;
+ r.f_.col_ += i;
+ return r;
+ }
+
+ const_iterator operator-(int i) const
+ {
+ auto r = *this;
+ r.f_.col_ -= i;
+ return r;
+ }
+
+ const_iterator & operator+=(int i)
+ {
+ f_.col_ += i;
+ return *this;
+ }
+
+ const_iterator & operator-=(int i)
+ {
+ f_.col_ -= i;
+ return *this;
+ }
+
+ const field & operator*() const
+ {
+ return f_;
+ }
+
+ const field * operator->() const
+ {
+ return &f_;
+ }
+
+ bool operator==(const const_iterator& other) const
+ {
+ return f_.col_ == other.f_.col_
+ && f_.stm_ == other.f_.stm_;
+ }
+
+ bool operator!=(const const_iterator& other) const
+ {
+ return f_.col_ != other.f_.col_
+ || f_.stm_ != other.f_.stm_;
+ }
+
+ bool operator<(const const_iterator& other) const
+ {
+ return f_.col_ < other.f_.col_
+ && f_.stm_ < other.f_.stm_;
+ }
+
+ bool operator>(const const_iterator& other) const
+ {
+ return f_.col_ > other.f_.col_
+ && f_.stm_ > other.f_.stm_;
+ }
+
+ const_iterator() = default;
+ private:
+ friend struct row;
+ field f_;
+ };
+ /// Returns the begin of the column-range.
+ const_iterator begin() const
+ {
+ const_iterator ci;
+ ci.f_.col_ = 0;
+ ci.f_.stm_ = stm_;
+ return ci;
+ }
+ /// Returns the end of the column-range.
+ const_iterator end() const
+ {
+ const_iterator ci;
+ ci.f_.col_ = sqlite3_column_count(stm_);
+ ci.f_.stm_ = stm_;
+ return ci;
+ }
+ private:
+ friend struct resultset;
+ sqlite3_stmt * stm_;
+
+};
+
+BOOST_SQLITE_END_NAMESPACE
+
+#endif //BOOST_SQLITE_ROW_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/statement.hpp b/subprojects/boost-sqlite/include/boost/sqlite/statement.hpp
new file mode 100644
index 0000000..b77bbe8
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/statement.hpp
@@ -0,0 +1,651 @@
+// Copyright (c) 2022 Klemens D. Morgenstern
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#ifndef BOOST_SQLITE_STATEMENT_HPP
+#define BOOST_SQLITE_STATEMENT_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/detail/exception.hpp>
+#include <boost/sqlite/blob.hpp>
+#include <boost/sqlite/resultset.hpp>
+
+#include <boost/mp11/algorithm.hpp>
+#include <boost/core/ignore_unused.hpp>
+#include <boost/variant2/variant.hpp>
+
+
+#include <tuple>
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+struct connection;
+template<typename, bool>
+struct static_resultset;
+
+/// @brief A reference to a value to temporary bind for an execute statement. Most values are captures by reference.
+/// @ingroup reference
+struct param_ref
+{
+ /// Default construct a parameter, gives `null`.
+ param_ref() = default;
+ /// Bind null
+ param_ref(variant2::monostate) : impl_{variant2::in_place_type_t<variant2::monostate>{}} {}
+ /// Bind null
+ param_ref(std::nullptr_t) : impl_{variant2::in_place_type_t<variant2::monostate>{}} {}
+ /// Bind an integer.
+ template<typename I,
+ typename = typename std::enable_if<std::is_integral<I>::value>::type>
+ param_ref(I value)
+ {
+ BOOST_IF_CONSTEXPR ((sizeof(I) == sizeof(int) && std::is_unsigned<I>::value)
+ || (sizeof(I) > sizeof(int)))
+ impl_.emplace<sqlite3_int64>(static_cast<sqlite3_int64>(value));
+ else
+ impl_.emplace<int>(static_cast<int>(value));
+ }
+ /// Bind a blob.
+ param_ref(blob_view blob) : impl_(blob) { }
+ /// Bind a string.
+ param_ref(string_view text) : impl_(text) { }
+
+ template<typename StringLike>
+ param_ref(StringLike && text,
+ typename std::enable_if<std::is_constructible<string_view, StringLike>::value>::type * = nullptr)
+ : impl_(variant2::in_place_type_t<string_view>{}, text) {}
+
+ template<typename BlobLike>
+ param_ref(BlobLike && text,
+ typename std::enable_if<
+ !std::is_constructible<string_view, BlobLike>::value
+ && std::is_constructible<blob_view, BlobLike>::value>::type * = nullptr)
+ : impl_(variant2::in_place_type_t<blob_view>{}, text) {}
+
+ /// Bind a floating point value.
+ param_ref(double value) : impl_(value) { }
+ /// Bind a zero_blob value, i.e. a blob that initialized by zero.
+ param_ref(zero_blob zb) : impl_(zb) { }
+
+#if SQLITE_VERSION_NUMBER >= 3020000
+ /// Bind pointer value to the parameter. @see https://www.sqlite.org/bindptr.html
+ template<typename T>
+ param_ref(std::unique_ptr<T> ptr)
+ : impl_(variant2::in_place_index_t<7>{},
+ std::unique_ptr<void, void(*)(void*)>(
+ static_cast<void*>(ptr.release()),
+ +[](void * ptr){delete static_cast<T*>(ptr);}),
+ typeid(T).name())
+ {
+ }
+
+ /// Bind pointer value with a function as deleter to the parameter. @see https://www.sqlite.org/bindptr.html
+ template<typename T>
+ param_ref(std::unique_ptr<T, void(*)(T*)> ptr)
+ : impl_(variant2::in_place_index_t<7>{},
+ std::unique_ptr<void, void(*)(void*)>(
+ static_cast<void*>(ptr.release()),
+ +[](void * ptr){delete static_cast<T*>(ptr);}),
+ typeid(T).name())
+ {
+ }
+
+ /// @brief Bind pointer value with a function custom deleter to the parameter.
+ /// The deleter needs to be default constructible. @see https://www.sqlite.org/bindptr.html
+ template<typename T, typename Deleter>
+ param_ref(std::unique_ptr<T, Deleter> ptr,
+ typename std::enable_if<std::is_empty<Deleter>::value &&
+ std::is_default_constructible<Deleter>::value, int>::type * = nullptr)
+ : impl_(variant2::in_place_index_t<7>{},
+ std::unique_ptr<void, void(*)(void*)>(
+ static_cast<void*>(ptr.release()),
+ +[](void * ptr){delete static_cast<T*>(ptr);}),
+ typeid(T).name())
+ {
+ }
+#endif
+
+ /// Apply the param_ref to a statement.
+ int apply(sqlite3_stmt * stmt, int c) const
+ {
+ return variant2::visit(visitor{stmt, c}, impl_);
+ }
+
+ private:
+ struct make_visitor
+ {
+ template<typename T>
+ auto operator()(T&& t) const -> typename std::enable_if<std::is_constructible<param_ref, T&&>::value, param_ref>::type
+ {
+ return param_ref(std::forward<T>(t));
+ }
+ };
+
+ public:
+ /// Construct param_ref from a variant
+ template<typename T>
+ param_ref(T && t,
+ decltype(variant2::visit(make_visitor(), std::forward<T>(t))) * = nullptr)
+ : param_ref(variant2::visit(make_visitor(), std::forward<T>(t)))
+ {}
+ private:
+
+ struct visitor
+ {
+ sqlite3_stmt * stmt;
+ int col;
+
+ int operator()(variant2::monostate )
+ {
+ return sqlite3_bind_null(stmt, col);
+ }
+ int operator()(int i )
+ {
+ return sqlite3_bind_int(stmt, col, i);
+ }
+ int operator()(sqlite3_int64 i64 )
+ {
+ return sqlite3_bind_int64(stmt, col, i64);
+ }
+
+ int operator()(blob_view blob)
+ {
+ if (blob.size() > static_cast<std::size_t>(std::numeric_limits<int>::max()))
+ return sqlite3_bind_blob64(stmt, col, blob.data(), blob.size(), SQLITE_STATIC);
+ else
+ return sqlite3_bind_blob(stmt, col, blob.data(), static_cast<int>(blob.size()), SQLITE_STATIC);
+ }
+
+ int operator()(string_view text)
+ {
+ if (text.size() > std::numeric_limits<int>::max())
+ return sqlite3_bind_text64(stmt, col, text.data(), text.size(), SQLITE_STATIC, SQLITE_UTF8);
+ else
+ return sqlite3_bind_text(stmt, col, text.data(), static_cast<int>(text.size()), SQLITE_STATIC);
+ }
+ int operator()(double value)
+ {
+ return sqlite3_bind_double(stmt, col, value);
+ }
+ int operator()(zero_blob zb)
+ {
+ if (static_cast<std::size_t>(zb) > static_cast<std::size_t>(std::numeric_limits<int>::max()))
+ return sqlite3_bind_zeroblob64(stmt, col, static_cast<sqlite3_uint64>(zb));
+ else
+ return sqlite3_bind_zeroblob(stmt, col, static_cast<int>(zb));
+ }
+#if SQLITE_VERSION_NUMBER >= 3020000
+ int operator()(std::pair<std::unique_ptr<void, void(*)(void*)>, const char*> & p)
+ {
+ auto d =p.first.get_deleter();
+ return sqlite3_bind_pointer(stmt, col, p.first.release(), p.second, d);
+ }
+#endif
+ };
+
+ mutable // so we can use it with
+ variant2::variant<variant2::monostate, int, sqlite3_int64,
+ blob_view, string_view, double, zero_blob
+#if SQLITE_VERSION_NUMBER >= 3020000
+ , std::pair<std::unique_ptr<void, void(*)(void*)>, const char*>
+#endif
+ > impl_;
+};
+
+
+/** @brief A statement used for a prepared-statement.
+ @ingroup reference
+
+ */
+struct statement
+{
+ ///@{
+ /** @brief execute the prepared statement once.
+
+ @param params The arguments to be passed to the prepared statement. This can be a map or a vector of param_ref.
+ @param ec The system::error_code used to deliver errors for the exception less overload.
+ @param info The error_info used to deliver errors for the exception less overload.
+ @return The resultset of the query.
+
+ @code{.cpp}
+ extern sqlite::connection conn;
+ statement st = conn.prepare("select id from users where name = $1;");
+ resultset q = std::move(st).execute(std::make_tuple("peter"));
+ @endcode
+
+ */
+ template <typename ArgRange = std::initializer_list<param_ref>>
+ resultset execute(
+ ArgRange && params,
+ system::error_code& ec,
+ error_info& info) &&
+ {
+ bind_impl(std::forward<ArgRange>(params), ec, info);
+ resultset rs;
+ rs.impl_.reset(impl_.release());
+ if (!ec)
+ rs.read_next(ec, info);
+ return rs;
+ }
+
+ template <typename ArgRange = std::initializer_list<param_ref>>
+ resultset execute(ArgRange && params) &&
+ {
+ system::error_code ec;
+ error_info ei;
+ auto tmp = std::move(*this).execute(std::forward<ArgRange>(params), ec, ei);
+ if (ec)
+ detail::throw_error_code(ec, ei);
+ return tmp;
+ }
+
+ resultset execute(
+ std::initializer_list<std::pair<string_view, param_ref>> params,
+ system::error_code& ec,
+ error_info& info) &&
+ {
+ bind_impl(std::move(params), ec, info);
+ resultset rs;
+ rs.impl_.reset(impl_.release());
+ if (!ec)
+ rs.read_next(ec, info);
+ return rs;
+ }
+
+ resultset execute(std::initializer_list<std::pair<string_view, param_ref>> params) &&
+ {
+ system::error_code ec;
+ error_info ei;
+ auto tmp = std::move(*this).execute(std::move(params), ec, ei);
+ if (ec)
+ detail::throw_error_code(ec, ei);
+ return tmp;
+ }
+
+ template<typename T, bool Strict = false, typename ArgRange = std::initializer_list<param_ref>>
+ static_resultset<T, Strict> execute(
+ ArgRange && params,
+ system::error_code & ec,
+ error_info & ei) &&
+ {
+ static_resultset<T, Strict> tmp = std::move(*this).execute(std::forward<ArgRange>(params), ec, ei);
+ if (ec)
+ return {};
+ tmp.check_columns_(ec, ei);
+ if (ec)
+ return {};
+
+ return tmp;
+ }
+
+ template<typename T, bool Strict = false, typename ArgRange = std::initializer_list<param_ref>>
+ static_resultset<T, Strict> execute(ArgRange && params) &&
+ {
+ system::error_code ec;
+ error_info ei;
+ auto tmp = std::move(*this).execute<T>(std::forward<ArgRange>(params), ec, ei);
+ if (ec)
+ throw_exception(system::system_error(ec, ei.message()));
+ return tmp;
+ }
+
+ template<typename T, bool Strict = false>
+ static_resultset<T, Strict> execute(
+ std::initializer_list<std::pair<string_view, param_ref>> params,
+ system::error_code & ec,
+ error_info & ei) &&
+ {
+ static_resultset<T, Strict> tmp = std::move(*this).execute(std::move(params), ec, ei);
+ if (ec)
+ return {};
+ tmp.check_columns_(ec, ei);
+ if (ec)
+ return {};
+
+ return tmp;
+ }
+
+ template<typename T, bool Strict = false>
+ static_resultset<T, Strict> execute(std::initializer_list<std::pair<string_view, param_ref>> params) &&
+ {
+ system::error_code ec;
+ error_info ei;
+ auto tmp = std::move(*this).execute<T, Strict>(std::move(params), ec, ei);
+ if (ec)
+ throw_exception(system::system_error(ec, ei.message()));
+ return tmp;
+ }
+
+ ///@}
+
+ ///@{
+ /** @brief execute the prepared statement and reset it afterwards.
+
+ @warning The handle is shared between the statement & resultset. The statemens need to be kept alive.
+
+ @param params The arguments to be passed to the prepared statement. This can be a map, a vector or a stuple of param_ref.
+ @param ec The system::error_code used to deliver errors for the exception less overload.
+ @param info The error_info used to deliver errors for the exception less overload.
+ @return The resultset of the query.
+
+ @code{.cpp}
+ extern sqlite::connection conn;
+ statement st = conn.prepare("select id from users where name = $1;");
+ resultset q = std::move(st).execute(std::make_tuple("peter"));
+ @endcode
+
+
+
+ */
+ template <typename ArgRange = std::initializer_list<param_ref>>
+ resultset execute(
+ ArgRange && params,
+ system::error_code& ec,
+ error_info& info) &
+ {
+ bind_impl(std::forward<ArgRange>(params), ec, info);
+ resultset rs;
+ rs.impl_.get_deleter().delete_ = false;
+ rs.impl_.reset(impl_.get());
+ if (!ec)
+ rs.read_next(ec, info);
+ return rs;
+ }
+
+
+ template <typename ArgRange = std::initializer_list<param_ref>>
+ resultset execute(ArgRange && params) &
+ {
+ system::error_code ec;
+ error_info ei;
+ auto tmp = execute(std::forward<ArgRange>(params), ec, ei);
+ if (ec)
+ detail::throw_error_code(ec, ei);
+ return tmp;
+ }
+
+
+ resultset execute(
+ std::initializer_list<std::pair<string_view, param_ref>> params,
+ system::error_code& ec,
+ error_info& info) &
+ {
+ bind_impl(std::move(params), ec, info);
+ resultset rs;
+ rs.impl_.get_deleter().delete_ = false;
+ rs.impl_.reset(impl_.get());
+ if (!ec)
+ rs.read_next(ec, info);
+ return rs;
+ }
+
+ resultset execute(std::initializer_list<std::pair<string_view, param_ref>> params) &
+ {
+ system::error_code ec;
+ error_info ei;
+ auto tmp = execute(std::move(params), ec, ei);
+ if (ec)
+ detail::throw_error_code(ec, ei);
+ return tmp;
+ }
+
+ template<typename T, bool Strict = false, typename ArgRange = std::initializer_list<param_ref>>
+ static_resultset<T, Strict> execute(
+ ArgRange && params,
+ system::error_code & ec,
+ error_info & ei) &
+ {
+ static_resultset<T, Strict> tmp = execute(std::forward<ArgRange>(params), ec, ei);
+ if (ec)
+ return {};
+ tmp.check_columns_(ec, ei);
+ if (ec)
+ return {};
+
+ return tmp;
+ }
+
+ template<typename T, bool Strict = false, typename ArgRange = std::initializer_list<param_ref>>
+ static_resultset<T, Strict> execute(ArgRange && params) &
+ {
+ system::error_code ec;
+ error_info ei;
+ auto tmp = execute<T, Strict>(std::forward<ArgRange>(params), ec, ei);
+ if (ec)
+ throw_exception(system::system_error(ec, ei.message()));
+ return tmp;
+ }
+
+ template<typename T, bool Strict = false>
+ static_resultset<T, Strict> execute(
+ std::initializer_list<std::pair<string_view, param_ref>> params,
+ system::error_code & ec,
+ error_info & ei) &
+ {
+ static_resultset<T, Strict> tmp = execute(std::move(params), ec, ei);
+ if (ec)
+ return {};
+ tmp.check_columns_(ec, ei);
+ if (ec)
+ return {};
+
+ return tmp;
+ }
+
+ template<typename T, bool Strict = false>
+ static_resultset<T, Strict> execute(std::initializer_list<std::pair<string_view, param_ref>> params) &
+ {
+ system::error_code ec;
+ error_info ei;
+ auto tmp = execute<T, Strict>(std::move(params), ec, ei);
+ if (ec)
+ throw_exception(system::system_error(ec, ei.message()));
+ return tmp;
+ }
+
+ ///@}
+
+
+ /// Returns the sql used to construct the prepared statement.
+ core::string_view sql()
+ {
+ return sqlite3_sql(impl_.get());
+ }
+
+#if SQLITE_VERSION_NUMBER >= 3014000
+ /// Returns the expanded sql used to construct the prepared statement.
+ core::string_view expanded_sql()
+ {
+ return sqlite3_expanded_sql(impl_.get());
+ }
+#endif
+
+ /// Returns the expanded sql used to construct the prepared statement.
+#ifdef SQLITE_ENABLE_NORMALIZE
+ core::string_view normalized_sql()
+ {
+ return sqlite3_normalized_sql(impl_.get());
+ }
+#endif
+
+ /// Returns the declared type of the column
+ core::string_view declared_type(int id) const
+ {
+ return sqlite3_column_decltype(impl_.get(), id);
+ }
+
+ private:
+
+ template<typename ... Args>
+ void bind_impl(std::tuple<Args...> && vec,
+ system::error_code & ec,
+ error_info & ei)
+ {
+ const auto sz = sqlite3_bind_parameter_count(impl_.get());
+ if (sizeof...(Args) < static_cast<std::size_t>(sz))
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_ERROR);
+ ei.format("To few parameters provided. Needed %d got %ld",
+ sz, sizeof...(Args));
+ return;
+ }
+
+ int i = 1, ar = SQLITE_OK;
+ mp11::tuple_for_each(std::move(vec),
+ [&](param_ref pr)
+ {
+ if (ar == SQLITE_OK)
+ ar = pr.apply(impl_.get(), i++);
+ });
+ if (ar != SQLITE_OK)
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, ar);
+ ei.set_message(sqlite3_errmsg(sqlite3_db_handle(impl_.get())));
+ return;
+ }
+ }
+
+
+ template<typename ... Args>
+ void bind_impl(const std::tuple<Args...> & vec,
+ system::error_code & ec,
+ error_info & ei)
+ {
+ const auto sz = sqlite3_bind_parameter_count(impl_.get());
+ if (static_cast<int>(sizeof...(Args)) < sz)
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_ERROR);
+ ei.format("To few parameters provided. Needed %d got %ld",
+ sz, sizeof...(Args));
+ return;
+ }
+
+ int i = 1, ar = SQLITE_OK;
+ mp11::tuple_for_each(std::move(vec),
+ [&](param_ref pr)
+ {
+ if (ar == SQLITE_OK)
+ ar = pr.apply(impl_.get(), i++);
+ });
+ if (ar != SQLITE_OK)
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, ar);
+ ei.set_message(sqlite3_errmsg(sqlite3_db_handle(impl_.get())));
+ return;
+ }
+ }
+
+ template<typename ParamVector>
+ void bind_impl(ParamVector && vec, system::error_code & ec, error_info & ei,
+ typename std::enable_if<std::is_convertible<
+ typename std::decay<ParamVector>::type::value_type, param_ref>::value>::type * = nullptr)
+ {
+ const auto sz = sqlite3_bind_parameter_count(impl_.get());
+ if (vec.size() < static_cast<std::size_t>(sz))
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_ERROR);
+ ei.format("To few parameters provided. Needed %d got %ld",
+ sz, vec.size());
+ }
+ int i = 1;
+ for (const param_ref & pr : std::forward<ParamVector>(vec))
+ {
+ int ar = pr.apply(impl_.get(), i++);
+ if (ar != SQLITE_OK)
+ {
+
+ BOOST_SQLITE_ASSIGN_EC(ec, ar);
+ ei.set_message(sqlite3_errmsg(sqlite3_db_handle(impl_.get())));
+ return;
+ }
+ }
+ }
+
+ template<typename ParamMap>
+ void bind_impl(ParamMap && vec, system::error_code & ec, error_info & ei,
+ typename std::enable_if<
+ std::is_convertible<typename std::decay<ParamMap>::type::key_type, string_view>::value &&
+ std::is_convertible<typename std::decay<ParamMap>::type::mapped_type, param_ref>::value
+ >::type * = nullptr)
+ {
+ for (auto i = 1; i <= sqlite3_bind_parameter_count(impl_.get()); i ++)
+ {
+ auto c = sqlite3_bind_parameter_name(impl_.get(), i);
+ if (c == nullptr)
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISUSE);
+ ei.set_message("Parameter maps require all parameters to be named.");
+ return ;
+ }
+ auto itr = vec.find(c+1);
+ if (itr == vec.end())
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISUSE);
+ ei.format("Can't find value for key '%s'", c+1);
+ return ;
+ }
+ int ar = SQLITE_OK;
+ if (std::is_rvalue_reference<ParamMap&&>::value)
+ ar = param_ref(std::move(itr->second)).apply(impl_.get(), i);
+ else
+ ar = param_ref(itr->second).apply(impl_.get(), i);
+
+ if (ar != SQLITE_OK)
+ {
+
+ BOOST_SQLITE_ASSIGN_EC(ec, ar);
+ ei.set_message(sqlite3_errmsg(sqlite3_db_handle(impl_.get())));
+ return;
+ }
+ }
+ }
+
+ void bind_impl(std::initializer_list<std::pair<string_view, param_ref>> params,
+ system::error_code & ec, error_info & ei)
+ {
+ for (auto i = 1; i <= sqlite3_bind_parameter_count(impl_.get()); i ++)
+ {
+ auto c = sqlite3_bind_parameter_name(impl_.get(), i);
+ if (c == nullptr)
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISUSE);
+ ei.set_message("Parameter maps require all parameters to be named.");
+ return ;
+ }
+
+ auto itr = std::find_if(params.begin(), params.end(),
+ [&](const std::pair<string_view, param_ref> & p)
+ {
+ return p.first == (c+1);
+ });
+ if (itr == params.end())
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISUSE);
+ ei.format("Can't find value for key '%s'", c+1);
+ return ;
+ }
+ auto ar = itr->second.apply(impl_.get(), i);
+ if (ar != SQLITE_OK)
+ {
+
+ BOOST_SQLITE_ASSIGN_EC(ec, ar);
+ ei.set_message(sqlite3_errmsg(sqlite3_db_handle(impl_.get())));
+ return;
+ }
+ }
+ }
+
+
+ friend
+ struct connection;
+ struct deleter_
+ {
+ void operator()(sqlite3_stmt * sm)
+ {
+ sqlite3_finalize(sm);
+ }
+ };
+ std::unique_ptr<sqlite3_stmt, deleter_> impl_;
+};
+
+BOOST_SQLITE_END_NAMESPACE
+
+#endif //BOOST_SQLITE_STATEMENT_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/static_resultset.hpp b/subprojects/boost-sqlite/include/boost/sqlite/static_resultset.hpp
new file mode 100644
index 0000000..fd6c359
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/static_resultset.hpp
@@ -0,0 +1,497 @@
+//
+// Copyright (c) 2024 Klemens Morgenstern (klemens.morgenstern@gmx.net)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef BOOST_SQLITE_STATIC_RESULTSET_HPP
+#define BOOST_SQLITE_STATIC_RESULTSET_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/resultset.hpp>
+#include <boost/sqlite/connection.hpp>
+
+#include <boost/describe/members.hpp>
+
+#include <array>
+#include <cstdint>
+
+#if __cplusplus >= 202002L
+#include <boost/pfr/core.hpp>
+#include <boost/pfr/core_name.hpp>
+#include <boost/pfr/traits.hpp>
+#endif
+
+
+namespace boost { template<typename> class optional;}
+
+#if __cplusplus >= 201702L
+#include <optional>
+#endif
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+namespace detail
+{
+
+inline void convert_field(sqlite_int64 & target, const field & f) {target = f.get_int();}
+
+template<typename = std::enable_if_t<!std::is_same<std::int64_t, sqlite_int64>::value>>
+inline void convert_field(std::int64_t & target, const field & f)
+{
+ target = static_cast<std::int64_t>(f.get_int());
+}
+
+inline void convert_field(double & target, const field & f) {target = f.get_double();}
+
+
+template<typename Traits = std::char_traits<char>, typename Allocator = std::allocator<char>>
+inline void convert_field(std::basic_string<char, Traits, Allocator> & target, const field & f)
+{
+ auto t = f.get_text();
+ target.assign(t.begin(), t.end());
+}
+
+inline void convert_field(string_view & target, const field & f) {target = f.get_text();}
+inline void convert_field(blob & target, const field & f) {target = blob(f.get_blob());}
+inline void convert_field(blob_view & target, const field & f) {target = f.get_blob();}
+
+#if __cplusplus >= 201702L
+template<typename T>
+inline void convert_field(std::optional<T> & target, const field & f)
+{
+ if (f.is_null())
+ target.reset();
+ else
+ convert_field(target.emplace(), f);
+}
+#endif
+
+template<typename T>
+inline void convert_field(boost::optional<T> & target, const field & f)
+{
+ if (f.is_null())
+ target.reset();
+ else
+ return convert_field(target.emplace_back(), f);
+}
+
+template<typename T>
+inline constexpr bool field_type_is_nullable(const T& ) {return false;}
+#if __cplusplus >= 201702L
+template<typename T>
+inline bool field_type_is_nullable(const std::optional<T> &) { return true; }
+#endif
+template<typename T>
+inline bool field_type_is_nullable(const boost::optional<T> &) { return true; }
+
+inline value_type required_field_type(const sqlite3_int64 &) {return value_type::integer;}
+
+template<typename = std::enable_if_t<!std::is_same<std::int64_t, sqlite_int64>::value>>
+inline value_type required_field_type(const std::int64_t &) {return value_type::integer;}
+
+template<typename Allocator, typename Traits>
+inline value_type required_field_type(const std::basic_string<char, Allocator, Traits> & )
+{
+ return value_type::text;
+}
+
+inline value_type required_field_type(const string_view &) {return value_type::text;}
+inline value_type required_field_type(const blob &) {return value_type::blob;}
+inline value_type required_field_type(const blob_view &) {return value_type::blob;}
+
+
+template<typename ... Args>
+void check_columns(const std::tuple<Args...> *, const resultset & r,
+ system::error_code &ec, error_info & ei)
+{
+ if (r.column_count() != sizeof...(Args))
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
+ ei.format("Tuple size doesn't match column count [%ld != %ld]", sizeof...(Args), r.column_count());
+ }
+}
+
+template<bool Strict, typename ... Args>
+void convert_row(std::tuple<Args...> & res, const row & r, system::error_code ec, error_info & ei)
+{
+ std::size_t idx = 0u;
+
+ mp11::tuple_for_each(
+ res,
+ [&](auto & v)
+ {
+ const auto i = idx++;
+ const auto & f = r[i];
+ BOOST_IF_CONSTEXPR (Strict)
+ {
+ if (!ec) // only check if we don't have an error yet.
+ {
+ if (f.is_null() && !field_type_is_nullable(v))
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_CONSTRAINT_NOTNULL);
+ ei.format("unexpected null in column %d", i);
+ }
+ else if (f.type() != required_field_type(v))
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_CONSTRAINT_DATATYPE);
+ ei.format("unexpected type [%s] in column %d, expected [%s]",
+ value_type_name(f.type()), i, value_type_name(required_field_type(v)));
+ }
+ }
+ }
+ else
+ boost::ignore_unused(ec, ei);
+
+ detail::convert_field(v, f);
+ });
+}
+
+#if defined(BOOST_DESCRIBE_CXX14)
+
+template<typename T, typename = typename std::enable_if<describe::has_describe_members<T>::value>::type>
+void check_columns(const T *, const resultset & r,
+ system::error_code &ec, error_info & ei)
+{
+ using mems = boost::describe::describe_members<T, describe::mod_public>;
+ constexpr std::size_t sz = mp11::mp_size<mems>();
+ if (r.column_count() != sz)
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
+ ei.format("Describe size doesn't match column count [%ld != %ld]", sz, r.column_count());
+ }
+
+ // columns can be duplicated!
+ std::array<bool, sz> found;
+ std::fill(found.begin(), found.end(), false);
+
+ for (std::size_t i = 0ul; i < r.column_count(); i++)
+ {
+ bool cfound = false;
+ boost::mp11::mp_for_each<mp11::mp_iota_c<sz>>(
+ [&](auto sz)
+ {
+ auto d = mp11::mp_at_c<mems, sz>();
+ if (d.name == r.column_name(i))
+ {
+ found[sz] = true;
+ cfound = true;
+ }
+ });
+
+ if (!cfound)
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
+ ei.format("Column '%s' not found in described struct.", r.column_name(i).c_str());
+ break;
+ }
+ }
+
+ if (ec)
+ return;
+
+
+ auto itr = std::find(found.begin(), found.end(), false);
+ if (itr != found.end())
+ {
+ mp11::mp_with_index<sz>(
+ std::distance(found.begin(), itr),
+ [&](auto sz)
+ {
+ auto d = mp11::mp_at_c<mems, sz>();
+ BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
+ ei.format("Described field '%s' not found in resultset struct.", d.name);
+ });
+ }
+}
+
+template<bool Strict, typename T,
+ typename = typename std::enable_if<describe::has_describe_members<T>::value>::type>
+void convert_row(T & res, const row & r, system::error_code ec, error_info & ei)
+{
+ for (auto && f: r)
+ {
+ boost::mp11::mp_for_each<boost::describe::describe_members<T, describe::mod_public> >(
+ [&](auto D)
+ {
+ if (D.name == f.column_name())
+ {
+ auto & r = res.*D.pointer;
+ BOOST_IF_CONSTEXPR(Strict)
+ {
+ if (!ec)
+ {
+ if (f.is_null() && !field_type_is_nullable(r))
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_CONSTRAINT_NOTNULL);
+ ei.format("unexpected null in column %s", D.name);
+ }
+ else if (f.type() != required_field_type(r))
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_CONSTRAINT_DATATYPE);
+ ei.format("unexpected type [%s] in column %s, expected [%s]",
+ value_type_name(f.type()), D.name, value_type_name(required_field_type(r)));
+ }
+ }
+ }
+
+ detail::convert_field(r, f);
+ }
+ });
+ }
+}
+
+#endif
+
+#if __cplusplus >= 202002L
+
+template<typename T>
+ requires (std::is_aggregate_v<T> && !describe::has_describe_members<T>::value)
+void check_columns(const T *, const resultset & r,
+ system::error_code &ec, error_info & ei)
+{
+ constexpr std::size_t sz = pfr::tuple_size_v<T>;
+ if (r.column_count() != sz)
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
+ ei.format("Describe size doesn't match column count [%ld != %ld]", sz, r.column_count());
+ }
+
+ // columns can be duplicated!
+ std::array<bool, sz> found;
+ std::fill(found.begin(), found.end(), false);
+
+ for (std::size_t i = 0ul; i < r.column_count(); i++)
+ {
+ bool cfound = false;
+ boost::mp11::mp_for_each<mp11::mp_iota_c<sz>>(
+ [&](auto sz)
+ {
+ if (pfr::get_name<sz, T>() == r.column_name(i))
+ {
+ found[sz] = true;
+ cfound = true;
+ }
+ });
+
+ if (!cfound)
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
+ ei.format("Column %s not found in struct.", r.column_name(i).c_str());
+ break;
+ }
+ }
+
+ if (ec)
+ return;
+
+
+ auto itr = std::find(found.begin(), found.end(), false);
+ if (itr != found.end())
+ {
+ mp11::mp_with_index<sz>(
+ std::distance(found.begin(), itr),
+ [&](auto sz)
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISMATCH);
+ auto nm = pfr::get_name<sz, T>();
+ ei.format("PFR field %.*s not found in resultset struct.", static_cast<int>(nm.size()), nm.data());
+ });
+ }
+}
+
+template<bool Strict, typename T>
+ requires (std::is_aggregate_v<T> && !describe::has_describe_members<T>::value)
+void convert_row(T & res, const row & r, system::error_code ec, error_info & ei)
+{
+ for (auto && f: r)
+ {
+ boost::mp11::mp_for_each<mp11::mp_iota_c<pfr::tuple_size_v<T>>>(
+ [&](auto D)
+ {
+ if (pfr::get_name<D, T>() == f.column_name().c_str())
+ {
+ auto & r = pfr::get<D()>(res);
+ BOOST_IF_CONSTEXPR(Strict)
+ {
+ if (!ec)
+ {
+ if (f.is_null() && !field_type_is_nullable(r))
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_CONSTRAINT_NOTNULL);
+ ei.format("unexpected null in column %s", D.name);
+ }
+ else if (f.type() != required_field_type(r))
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_CONSTRAINT_DATATYPE);
+ ei.format("unexpected type [%s] in column %s, expected [%s]",
+ value_type_name(f.type()), D.name, value_type_name(required_field_type(r)));
+ }
+ }
+ }
+ detail::convert_field(r, f);
+ }
+ });
+ }
+}
+
+#endif
+
+}
+
+/**
+ @brief A typed resultset using a tuple or a described struct.
+ @ingroup reference
+ @tparam T The static type of the query.
+ @tparam Strict Disables implicit conversions.
+
+ If is a forward-range with output iterators.
+
+ @par Example
+
+ @code{.cpp}
+
+ extern sqlite::connection conn;
+ struct user { std::string first_name; std::string last_name; };
+ BOOST_DESCRIBE_STRUCT(user, (), (first_name, last_name));
+
+ sqlite::resultset rs = conn.query("select first_name, last_name from users;");
+
+ do
+ {
+ user usr = r.current();
+ handle_row(u);
+ }
+ while (rs.read_next()) // read it line by line
+
+ @endcode
+
+*/
+template<typename T, bool Strict >
+struct static_resultset
+{
+ /// Returns the current row.
+ T current() const &
+ {
+ system::error_code ec;
+ error_info ei;
+ auto tmp = current(ec, ei);
+ if (ec)
+ throw_exception(system::system_error(ec, ei.message()));
+ return tmp;
+ }
+
+ /// Returns the current row.
+ T current(system::error_code & ec, error_info & ei) const &
+ {
+ T res;
+ detail::convert_row<Strict>(res, result_.current(), ec, ei);
+ return res;
+ }
+
+ /// Checks if the last row has been reached.
+ bool done() const {return result_.done();}
+
+ ///@{
+ /// Read the next row. Returns false if there's nothing more to read.
+ BOOST_SQLITE_DECL bool read_next(system::error_code & ec, error_info & ei) { return result_.read_next(ec, ei); }
+ BOOST_SQLITE_DECL bool read_next() { return result_.read_next(); }
+ ///@}
+
+ ///
+ std::size_t column_count() const { return result_.column_count(); }
+ /// Returns the name of the column idx.
+ core::string_view column_name(std::size_t idx) const { return result_.column_name(idx); }
+
+ /// Returns the name of the source table for column idx.
+ core::string_view table_name(std::size_t idx) const { return result_.table_name(idx);}
+ /// Returns the origin name of the column for column idx.
+ core::string_view column_origin_name(std::size_t idx) const { return result_.column_origin_name(idx);}
+
+ static_resultset() = default;
+ static_resultset(resultset && result) : result_(std::move(result)) { }
+
+
+ static_resultset(static_resultset<T, false> && rhs) : result_(std::move(rhs.result_)) {}
+
+ /// The input iterator can be used to read every row in a for-loop
+ struct iterator
+ {
+ using value_type = T;
+ using difference_type = int;
+ using reference = T&;
+ using iterator_category = std::forward_iterator_tag;
+
+ iterator()
+ {
+
+ }
+ explicit iterator(resultset::iterator itr) : itr_(itr)
+ {
+ system::error_code ec;
+ error_info ei;
+ if (itr->size() > 0ul)
+ detail::convert_row<Strict>(value_, *itr, ec, ei);
+ if (ec)
+ throw_exception(system::system_error(ec, ei.message()));
+ }
+
+ bool operator!=(iterator rhs) const
+ {
+ return itr_ != rhs.itr_;
+ }
+
+ value_type &operator*() { return value_; }
+ value_type *operator->() { return &value_; }
+
+ iterator& operator++()
+ {
+ ++itr_;
+
+ system::error_code ec;
+ error_info ei;
+ if (itr_->size() > 0ul)
+ detail::convert_row<Strict>(value_, *itr_, ec, ei);
+ if (ec)
+ throw_exception(system::system_error(ec, ei.message()));
+
+ return *this;
+ }
+ iterator operator++(int)
+ {
+ auto l = *this;
+ ++(*this);
+ return l;
+ }
+ private:
+ resultset::iterator itr_;
+ value_type value_;
+ };
+
+ /// Return an input iterator to the currently unread row
+ iterator begin() { return iterator(result_.begin());}
+ /// Sentinel iterator.
+ iterator end() { return iterator(result_.end()); }
+
+
+
+ static_resultset<T, true> strict() &&
+ {
+ return {std::move(result_)};
+ }
+ private:
+
+ friend struct static_resultset<T, true>;
+ friend struct connection;
+ friend struct statement;
+ resultset result_;
+ void check_columns_( system::error_code & ec, error_info & ei)
+ {
+ detail::check_columns(static_cast<T*>(nullptr), result_, ec, ei);
+ }
+};
+
+BOOST_SQLITE_END_NAMESPACE
+
+#endif //BOOST_SQLITE_STATIC_RESULTSET_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/string.hpp b/subprojects/boost-sqlite/include/boost/sqlite/string.hpp
new file mode 100644
index 0000000..5a81978
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/string.hpp
@@ -0,0 +1,49 @@
+// Copyright (c) 2023 Klemens D. Morgenstern
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#ifndef BOOST_SQLITE_STRING_HPP
+#define BOOST_SQLITE_STRING_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/cstring_ref.hpp>
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+inline
+bool like(
+ cstring_ref lhs,
+ cstring_ref rhs,
+ char escape = '\0')
+{
+ return sqlite3_strlike(lhs.c_str(), rhs.c_str(), escape) != 0;
+}
+
+inline
+bool glob(
+ cstring_ref lhs,
+ cstring_ref rhs)
+{
+ return sqlite3_strglob(lhs.c_str(), rhs.c_str()) != 0;
+}
+
+inline
+int icmp(
+ cstring_ref lhs,
+ cstring_ref rhs)
+{
+ return sqlite3_stricmp(lhs.c_str(), rhs.c_str());
+}
+
+inline
+int icmp(
+ core::string_view lhs,
+ core::string_view rhs,
+ std::size_t n)
+{
+ return sqlite3_strnicmp(lhs.data(), rhs.data(), static_cast<int>(n));
+}
+
+BOOST_SQLITE_END_NAMESPACE
+
+#endif //BOOST_SQLITE_STRING_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/transaction.hpp b/subprojects/boost-sqlite/include/boost/sqlite/transaction.hpp
new file mode 100644
index 0000000..b90bdbf
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/transaction.hpp
@@ -0,0 +1,198 @@
+//
+// Copyright (c) 2024 Klemens Morgenstern (klemens.morgenstern@gmx.net)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef BOOST_SQLITE_TRANSACTION_HPP
+#define BOOST_SQLITE_TRANSACTION_HPP
+
+#include <boost/sqlite/connection.hpp>
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+/**
+ * @brief A simple transaction guard implementing RAAI for transactions
+ * @ingroup reference
+ *
+ * @par Example
+ * @code{.cpp}
+ * sqlite::connection conn;
+ * conn.connect("./my-database.db");
+ *
+ * sqlite::transaction t{conn};
+ * conn.prepare("insert into log (text) values ($1)").execute(std::make_tuple("booting up"));
+ * t.commit();
+ * @endcode
+ */
+struct transaction
+{
+ /// The mode of the transaction
+ enum behaviour {deferred, immediate, exclusive};
+ /// A tag to use, to adopt an already initiated transaction.
+ constexpr static struct adopt_transaction_t {} adopt_transaction{};
+
+
+ /// Create transaction guard on an existing transaction
+ transaction(connection & conn, adopt_transaction_t) : conn_(conn), completed_(false)
+ {
+ }
+
+
+ /// Create transaction guard and initiate a transaction
+ transaction(connection & conn) : conn_(conn)
+ {
+ conn.execute("BEGIN");
+ completed_ = false;
+ }
+
+ /// Create transaction guard and initiate a transaction with the defined behaviour
+ transaction(connection & conn, behaviour b) : conn_(conn)
+ {
+ switch (b)
+ {
+ case deferred: conn.execute("BEGIN DEFERRED"); break;
+ case immediate: conn.execute("BEGIN IMMEDIATE"); break;
+ case exclusive: conn.execute("BEGIN EXCLUSIVE"); break;
+ }
+ completed_ = false;
+ }
+
+ // see https://www.sqlite.org/lang_transaction.html re noexcept
+ /// rollback the transaction if not committed.
+ ~transaction() noexcept(SQLITE_VERSION_NUMBER >= 3007011)
+ {
+ if (!completed_)
+ conn_.execute("ROLLBACK");
+ }
+
+ ///@{
+ /// Commit the transaction.
+ void commit()
+ {
+ conn_.execute("COMMIT");
+ completed_ = true;
+ }
+
+ void commit(system::error_code & ec, error_info & ei)
+ {
+ conn_.execute("COMMIT", ec, ei);
+ completed_ = true;
+ }
+ ///@}
+
+ ///@{
+ /// Rollback the transaction explicitly.
+ void rollback()
+ {
+ conn_.execute("ROLLBACK");
+ completed_ = true;
+ }
+
+ void rollback(system::error_code & ec, error_info & ei)
+ {
+ conn_.execute("ROLLBACK", ec, ei);
+ completed_ = true;
+ }
+ ///@}
+
+ private:
+ connection & conn_;
+ bool completed_ = true;
+};
+
+/**
+ * @brief A simple transaction guard implementing RAAI for savepoints. Savepoints can be used recursively.
+ * @ingroup reference
+ *
+ * @par Example
+ * @code{.cpp}
+ * sqlite::connection conn;
+ * conn.connect("./my-database.db");
+ *
+ * sqlite::savepoint t{conn, "my-savepoint};
+ * conn.prepare("insert into log (text) values ($1)").execute(std::make_tuple("booting up"));
+ * t.commit();
+ * @endcode
+*/
+struct savepoint
+{
+ /// A tag to use, to adopt an already initiated transaction.
+ constexpr static transaction::adopt_transaction_t adopt_transaction{};
+
+ /// Create savepoint guard on an existing savepoint
+ savepoint(connection & conn, std::string name, transaction::adopt_transaction_t)
+ : conn_(conn), name_(std::move(name))
+ {
+ }
+
+ /// Create transaction guard and initiate it
+ savepoint(connection & conn, std::string name) : conn_(conn), name_(std::move(name))
+ {
+ conn.execute("SAVEPOINT " + name_);
+ completed_ = false;
+ }
+
+
+ /// rollback to the savepoint if not committed.
+ ~savepoint() noexcept(SQLITE_VERSION_NUMBER >= 3007011)
+ {
+ if (!completed_)
+ conn_.execute("ROLLBACK TO " + name_);
+ }
+
+ ///@{
+ /// Commit/Release the transaction.
+ void commit()
+ {
+ conn_.execute("RELEASE " + name_);
+ completed_ = true;
+ }
+
+ void commit(system::error_code & ec, error_info & ei)
+ {
+ conn_.execute("RELEASE " + name_, ec, ei);
+ completed_ = true;
+ }
+
+ void release()
+ {
+ conn_.execute("RELEASE " + name_);
+ completed_ = true;
+ }
+
+ void release(system::error_code & ec, error_info & ei)
+ {
+ conn_.execute("RELEASE " + name_, ec, ei);
+ completed_ = true;
+ }
+ ///@}
+
+ ///@{
+ /// Rollback the transaction explicitly.
+ void rollback()
+ {
+ conn_.execute("ROLLBACK TO" + name_);
+ completed_ = true;
+ }
+
+ void rollback(system::error_code & ec, error_info & ei)
+ {
+ conn_.execute("ROLLBACK TO " + name_, ec, ei);
+ completed_ = true;
+ }
+ ///@}
+
+ /// The name of the savepoint.
+ const std::string & name() const {return name_;}
+ private:
+ connection & conn_;
+ std::string name_;
+ bool completed_ = true;
+};
+
+
+BOOST_SQLITE_END_NAMESPACE
+
+#endif //BOOST_SQLITE_TRANSACTION_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/value.hpp b/subprojects/boost-sqlite/include/boost/sqlite/value.hpp
new file mode 100644
index 0000000..16dc1c1
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/value.hpp
@@ -0,0 +1,130 @@
+// Copyright (c) 2022 Klemens D. Morgenstern
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#ifndef BOOST_SQLITE_VALUE_HPP
+#define BOOST_SQLITE_VALUE_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/blob.hpp>
+#include <boost/sqlite/cstring_ref.hpp>
+
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+
+/** @brief The type of a value
+ @ingroup reference
+
+ [related sqlite documentation](https://www.sqlite.org/datatype3.html)
+*/
+enum class value_type
+{
+ /// An integral value
+ integer = SQLITE_INTEGER,
+ /// A floating piont value
+ floating = SQLITE_FLOAT,
+ /// A textual value
+ text = SQLITE_TEXT,
+ /// A binary value
+ blob = SQLITE_BLOB,
+ /// No value
+ null = SQLITE_NULL,
+};
+
+inline
+const char * value_type_name(value_type vt)
+{
+ switch (vt)
+ {
+ case value_type::text: return "text";
+ case value_type::blob: return "blob";
+ case value_type::floating: return "floating";
+ case value_type::integer: return "integer";
+ case value_type::null: return "null";
+ default: return "unknown";
+ }
+}
+
+
+/** @brief A holder for a sqlite values used for internal APIs
+ @ingroup reference
+
+ */
+struct value
+{
+ // The value for integers in the database
+ typedef sqlite3_int64 int64 ;
+
+ /// The type of the value
+ value_type type() const
+ {
+ return static_cast<value_type>(sqlite3_value_type(value_));
+ }
+ /// The subtype of the value, see
+ int subtype() const
+ {
+ return sqlite3_value_subtype(value_);
+ }
+ /// Is the held value null
+ bool is_null() const
+ {
+ return type() == value_type::null;
+ }
+ /// Is the held value is not null
+ explicit operator bool () const
+ {
+ return type() != value_type::null;
+ }
+ /// Returns the value as an `integer`.
+ int64 get_int() const
+ {
+ return sqlite3_value_int64(value_);
+ }
+ /// Returns the value as an `double`.
+ double get_double() const
+ {
+ return sqlite3_value_double(value_);
+ }
+ /// Returns the value as text, i.e. a string_view. Note that this value may be invalidated`.
+ BOOST_SQLITE_DECL
+ cstring_ref get_text() const;
+ /// Returns the value as blob, i.e. raw memory. Note that this value may be invalidated`.
+ BOOST_SQLITE_DECL
+ blob_view get_blob() const;
+
+ /// Best numeric datatype of the value
+ value_type numeric_type() const{return static_cast<value_type>(sqlite3_value_numeric_type(value_));}
+
+#if SQLITE_VERSION_NUMBER >= 3032000
+ /// True if the column is unchanged in an UPDATE against a virtual table.
+ bool nochange() const {return 0 != sqlite3_value_nochange(value_);}
+#endif
+
+#if SQLITE_VERSION_NUMBER >= 3031000
+ /// True if value originated from a bound parameter
+ bool from_bind() const {return 0 != sqlite3_value_frombind(value_);}
+#endif
+ /// Construct value from a handle.
+ explicit value(sqlite3_value * value_) noexcept : value_(value_) {}
+
+ /// The handle of the value.
+ using handle_type = sqlite3_value *;
+ /// Returns the handle.
+ handle_type handle() const {return value_;}
+ handle_type & handle() {return value_;}
+
+#if SQLITE_VERSION_NUMBER >= 3020000
+ /// Get a value that was passed through the pointer interface.
+ /// A value can be set as a pointer by binding/returning a unique_ptr.
+ template<typename T>
+ T * get_pointer() {return static_cast<T*>(sqlite3_value_pointer(value_, typeid(T).name()));}
+#endif
+ private:
+ sqlite3_value * value_ = nullptr;
+};
+
+static_assert(sizeof(value) == sizeof(sqlite3_value*), "value must be same as sqlite3_value* pointer");
+
+BOOST_SQLITE_END_NAMESPACE
+
+#endif //BOOST_SQLITE_VALUE_HPP
diff --git a/subprojects/boost-sqlite/include/boost/sqlite/vtable.hpp b/subprojects/boost-sqlite/include/boost/sqlite/vtable.hpp
new file mode 100644
index 0000000..a984f4d
--- /dev/null
+++ b/subprojects/boost-sqlite/include/boost/sqlite/vtable.hpp
@@ -0,0 +1,615 @@
+//
+// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef BOOST_SQLITE_VTABLE_HPP
+#define BOOST_SQLITE_VTABLE_HPP
+
+#include <boost/sqlite/detail/config.hpp>
+#include <boost/sqlite/detail/catch.hpp>
+#include <boost/sqlite/function.hpp>
+
+#include <boost/core/span.hpp>
+#include <boost/core/demangle.hpp>
+
+#include <bitset>
+
+BOOST_SQLITE_BEGIN_NAMESPACE
+namespace detail
+{
+struct vtab_impl;
+}
+
+
+namespace vtab
+{
+
+/// Helper type to set a function through the xFindFunction callback
+struct function_setter
+{
+ /** Set the function
+ *
+ * @tparam Func The function type (either a lambda by ref or a pointer by copy)
+ * @param func The function to be used
+ *
+ * The function can either take a single argument, a `span<sqlite::value, N>`
+ * for scalar functions,
+ * or a `context<Args...>` as first, and the span as second for aggegrate functions.
+ *
+ */
+ template<typename Func>
+ void set(Func & func)
+ {
+ set_impl(func, static_cast<callable_traits::args_t<Func>*>(nullptr));
+ }
+
+ template<typename ... Args, std::size_t Extent>
+ void set(void(* ptr)(context<Args...>, span<value, Extent>)) noexcept
+ {
+ *ppArg_ = reinterpret_cast<void*>(ptr);
+ *pxFunc_ =
+ +[](sqlite3_context* ctx, int len, sqlite3_value** args)
+ {
+ auto cc = context<Args...>(ctx);
+ auto aa = reinterpret_cast<value*>(args);
+ auto &f = *reinterpret_cast<void(*)(context<Args...>, span<value, Extent>)>(sqlite3_user_data(ctx));
+ detail::execute_context_function(ctx, f, cc, boost::span<value, Extent>{aa, static_cast<std::size_t>(len)});
+ };
+ }
+
+ template<typename T, typename ... Args, std::size_t Extent>
+ void set(T(* ptr)(context<Args...>, span<value, Extent>))
+ {
+ *ppArg_ = reinterpret_cast<void*>(ptr);
+ *pxFunc_ =
+ +[](sqlite3_context* ctx, int len, sqlite3_value** args) noexcept
+ {
+ auto cc = context<Args...>(ctx);
+ auto aa = reinterpret_cast<value*>(args);
+ auto &f = *reinterpret_cast<T(*)(context<Args...>, span<value, Extent>)>(sqlite3_user_data(ctx));
+ detail::execute_context_function(ctx, f, cc, boost::span<value, Extent>{aa, static_cast<std::size_t>(len)});
+ };
+ }
+
+ template<std::size_t Extent>
+ void set(void(* ptr)(span<value, Extent>))
+ {
+ *ppArg_ = reinterpret_cast<void*>(ptr);
+ *pxFunc_ =
+ +[](sqlite3_context* ctx, int len, sqlite3_value** args) noexcept
+ {
+ auto aa = reinterpret_cast<value*>(args);
+ auto &f = *reinterpret_cast<void(*)(span<value, Extent>)>(sqlite3_user_data(ctx));
+ detail::execute_context_function(ctx, f, boost::span<value, Extent>{aa, static_cast<std::size_t>(len)});
+ };
+ }
+
+ template<typename T, std::size_t Extent>
+ void set(T(* ptr)(span<value, Extent>))
+ {
+ *ppArg_ = reinterpret_cast<void*>(ptr);
+ *pxFunc_ =
+ +[](sqlite3_context* ctx, int len, sqlite3_value** args) noexcept
+ {
+ auto aa = reinterpret_cast<value*>(args);
+ auto &f = *reinterpret_cast<T(*)(span<value, Extent>)>(sqlite3_user_data(ctx));
+ detail::execute_context_function(ctx, f, boost::span<value, Extent>{aa, static_cast<std::size_t>(len)});
+ };
+ }
+
+ explicit function_setter(void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
+ void **ppArg) : pxFunc_(pxFunc), ppArg_(ppArg) {}
+ private:
+
+ template<typename Func, typename ... Args, std::size_t Extent>
+ void set_impl(Func & func, std::tuple<context<Args...>, span<value, Extent>>)
+ {
+ *ppArg_ = &func;
+ *pxFunc_ =
+ +[](sqlite3_context* ctx, int len, sqlite3_value** args) noexcept
+ {
+ auto cc = context<Args...>(ctx);
+ auto aa = reinterpret_cast<value*>(args);
+ auto &f = *reinterpret_cast<Func*>(sqlite3_user_data(ctx));
+ detail::execute_context_function(ctx, f, cc, boost::span<value, Extent>{aa, static_cast<std::size_t>(len)});
+ };
+ }
+ template<typename Func, std::size_t Extent>
+ void set_impl(Func & func, std::tuple<span<value, Extent>> * )
+ {
+ *ppArg_ = &func;
+ *pxFunc_ =
+ +[](sqlite3_context* ctx, int len, sqlite3_value** args) noexcept
+ {
+ auto aa = reinterpret_cast<value*>(args);
+ auto &f = *reinterpret_cast<Func *>(sqlite3_user_data(ctx));
+ detail::execute_context_function(ctx, f, boost::span<value, Extent>{aa, static_cast<std::size_t>(len)});
+ };
+ }
+
+ void (**pxFunc_)(sqlite3_context*,int,sqlite3_value**);
+ void **ppArg_;
+};
+
+#if SQLITE_VERSION_NUMBER >= 3038000
+/** @brief Utility function that can be used in `xFilter` for the `in` operator.
+ @see https://www.sqlite.org/capi3ref.html#sqlite3_vtab_in_first
+ @ingroup reference
+ @Note requires sqlite version >= 3.38
+*/
+struct in
+{
+ struct iterator
+ {
+ iterator() = default;
+ explicit iterator(sqlite3_value * value ) : value_(value)
+ {
+ if (value == nullptr)
+ return ;
+ auto res = sqlite3_vtab_in_first(value, &out_.handle());
+ if (res != SQLITE_OK)
+ {
+ system::error_code ec;
+ BOOST_SQLITE_ASSIGN_EC(ec, res);
+ detail::throw_error_code(ec, ec.location());
+ }
+ }
+
+ iterator & operator++()
+ {
+ auto res = sqlite3_vtab_in_next(value_, &out_.handle());
+ if (res != SQLITE_OK)
+ {
+ system::error_code ec;
+ BOOST_SQLITE_ASSIGN_EC(ec, res);
+ detail::throw_error_code(ec, ec.location());
+ }
+ return *this;
+ }
+
+ iterator operator++(int)
+ {
+ auto last = *this;
+ ++(*this);
+ return last;
+ }
+
+
+ const value & operator*() const
+ {
+ return out_;
+ }
+
+ const value * operator->() const
+ {
+ return &out_;
+ }
+
+ bool operator==(const iterator& other) const
+ {
+ return value_ == other.value_
+ && out_.handle() == other.out_.handle();
+ }
+
+ bool operator!=(const iterator& other) const
+ {
+ return value_ != other.value_
+ || out_.handle() != other.out_.handle();
+ }
+
+
+ private:
+ sqlite3_value * value_{nullptr};
+ value out_{nullptr};
+ };
+
+ /// Returns a forward iterator to the `in` sequence for an `in` constraint pointing to the begin.
+ iterator begin() {return iterator(out_);}
+ /// Returns a forward iterator to the `in` sequence for an `in` constraint pointing to the end.
+ iterator end() {return iterator();}
+
+ explicit in(sqlite3_value * out) : out_(out) {}
+ explicit in(sqlite::value out) : out_(out.handle()) {}
+ private:
+ sqlite3_value * out_{nullptr};
+};
+
+#endif
+
+/// index info used by the find_index function
+/// @ingroup reference
+/// @see [sqlite reference](https://www.sqlite.org/vtab.html#xbestindex)
+struct index_info
+{
+ /// Returns constraints of the index.
+ BOOST_ATTRIBUTE_NODISCARD
+ span<const sqlite3_index_info::sqlite3_index_constraint> constraints() const
+ {
+ return {info_->aConstraint, static_cast<std::size_t>(info_->nConstraint)};
+ }
+
+ /// Returns ordering of the index.
+ BOOST_ATTRIBUTE_NODISCARD
+ span<const sqlite3_index_info::sqlite3_index_orderby> order_by() const
+ {
+ return {info_->aOrderBy, static_cast<std::size_t>(info_->nOrderBy)};
+ }
+
+ BOOST_ATTRIBUTE_NODISCARD
+ span<sqlite3_index_info::sqlite3_index_constraint_usage> usage()
+ {
+ return {info_->aConstraintUsage, static_cast<std::size_t>(info_->nConstraint)};
+ }
+
+ BOOST_ATTRIBUTE_NODISCARD
+ sqlite3_index_info::sqlite3_index_constraint_usage & usage_of(
+ const sqlite3_index_info::sqlite3_index_constraint & info)
+ {
+ auto dist = std::distance(constraints().begin(), &info);
+ auto itr = usage().begin() + dist;
+ BOOST_ASSERT(itr < usage().end());
+ return *itr;
+ }
+
+#if SQLITE_VERSION_NUMBER >= 3022000
+
+ /// Receive the collation for the contrainst of the position.
+ const char * collation(std::size_t idx) const
+ {
+ return sqlite3_vtab_collation(info_, static_cast<int>(idx));
+ }
+#endif
+
+ int on_conflict() const {return sqlite3_vtab_on_conflict(db_);}
+
+#if SQLITE_VERSION_NUMBER >= 3038000
+ /// Returns true if the constraint is
+ bool distinct() const {return sqlite3_vtab_distinct(info_);}
+
+ value * rhs_value(std::size_t idx) const
+ {
+ value * v = nullptr;
+ if (sqlite3_vtab_rhs_value(
+ info_, static_cast<int>(idx),
+ reinterpret_cast<sqlite3_value**>(v)) == SQLITE_OK)
+ return v;
+ else
+ return nullptr;
+ }
+#endif
+
+ void set_already_ordered() { info_->orderByConsumed = 1; }
+ void set_estimated_cost(double cost) { info_->estimatedCost = cost; }
+#if SQLITE_VERSION_NUMBER >= 3008200
+ void set_estimated_rows(sqlite3_int64 rows) { info_->estimatedRows = rows; }
+#endif
+#if SQLITE_VERSION_NUMBER >= 3009000
+ void set_index_scan_flags(int flags) { info_->idxFlags = flags; }
+#endif
+#if SQLITE_VERSION_NUMBER >= 3010000
+ std::bitset<64u> columns_used()
+ {
+ return std::bitset<64u>(info_->colUsed);
+ }
+#endif
+
+ void set_index(int value) { info_->idxNum = value; }
+ void set_index_string(char * str,
+ bool take_ownership = true)
+ {
+ info_->idxStr = str;
+ info_->needToFreeIdxStr = take_ownership ? 1 : 0;
+ }
+
+ sqlite3_index_info * info() const { return info_; }
+ sqlite3 * db() const { return db_; }
+
+ private:
+ explicit index_info(sqlite3 * db, sqlite3_index_info * info) : db_(db), info_(info) {}
+ sqlite3 * db_;
+ sqlite3_index_info * info_{nullptr};
+ friend struct detail::vtab_impl;
+};
+
+
+struct module_config
+{
+#if SQLITE_VERSION_NUMBER >= 3031000
+ /// @brief Can be used to set SQLITE_VTAB_INNOCUOUS
+ void set_innocuous()
+ {
+ sqlite3_vtab_config(db_, SQLITE_VTAB_INNOCUOUS);
+ }
+ /// @brief Can be used to set SQLITE_VTAB_DIRECTONLY
+ void set_directonly() {sqlite3_vtab_config(db_, SQLITE_VTAB_DIRECTONLY);}
+
+#endif
+
+ /// @brief Can be used to set SQLITE_VTAB_CONSTRAINT_SUPPORT
+ void set_constraint_support(bool enabled = false)
+ {
+ sqlite3_vtab_config(db_, SQLITE_VTAB_CONSTRAINT_SUPPORT, enabled ? 1 : 0);
+ }
+
+ private:
+ explicit module_config(sqlite3 *db) : db_(db) {}
+ friend struct detail::vtab_impl;
+ sqlite3 *db_;
+};
+
+template<typename Table>
+struct module
+{
+ using table_type = Table;
+
+ /// @brief Creates the instance
+ /// The instance_type gets used & managed by value, OR a pointer to a class that inherits sqlite3_vtab.
+ /// instance_type must have a member `declaration` that returns a `const char *` for the declaration.
+ BOOST_SQLITE_VIRTUAL result<table_type> create(sqlite::connection db, int argc, const char * const argv[]) BOOST_SQLITE_PURE;
+
+ /// @brief Create a table
+ /// The table_type gets used & managed by value, OR a pointer to a class that inherits sqlite3_vtab.
+ /// table_type must have a member `declaration` that returns a `const char *` for the declaration.
+ BOOST_SQLITE_VIRTUAL result<table_type> connect(sqlite::connection db, int argc, const char * const argv[]) BOOST_SQLITE_PURE;
+};
+
+template<typename Table>
+struct eponymous_module
+{
+ using table_type = Table;
+
+ /// @brief Creates the instance
+ /// The instance_type gets used & managed by value, OR a pointer to a class that inherits sqlite3_vtab.
+ /// instance_type must have a member `declaration` that returns a `const char *` for the declaration.
+ BOOST_SQLITE_VIRTUAL result<table_type> connect(sqlite::connection db, int argc, const char * const argv[]) BOOST_SQLITE_PURE;
+
+ eponymous_module(bool eponymous_only = false) : eponymous_only_(eponymous_only) {}
+
+ bool eponymous_only() const {return eponymous_only_;}
+ protected:
+ bool eponymous_only_{false};
+
+};
+
+template<typename ColumnType>
+struct cursor;
+
+/// The basis for vtable
+template<typename Cursor>
+struct table : protected sqlite3_vtab
+{
+ using cursor_type = Cursor;
+
+ BOOST_SQLITE_VIRTUAL result<void> config(module_config &) {return {};}
+
+ /// The Table declaration to be used with sqlite3_declare_vtab
+ BOOST_SQLITE_VIRTUAL const char *declaration() BOOST_SQLITE_PURE;
+
+ /// Destroy the storage = this function needs to be present for non eponymous tables
+ BOOST_SQLITE_VIRTUAL result<void> destroy() { return {}; }
+
+ /// Tell sqlite how to communicate with the table.
+ /// Optional, this library will fill in a default function that leaves comparisons to sqlite.
+ BOOST_SQLITE_VIRTUAL result<void> best_index(index_info & /*info*/) {return {system::in_place_error, SQLITE_OK};}
+
+ /// @brief Start a search on the table.
+ /// The cursor_type gets used & managed by value, OR a pointer to a class that inherits sqlite3_vtab_cursor.
+ BOOST_SQLITE_VIRTUAL result<cursor_type> open() BOOST_SQLITE_PURE;
+
+ /// Get the connection of the vtable
+ sqlite::connection connection() const {return sqlite::connection{db_, false};}
+
+ table(const sqlite::connection & conn) : db_(conn.handle()) {}
+ table(sqlite3 * db = nullptr) : db_(db) {}
+ private:
+ template<typename ColumnType>
+ friend struct cursor;
+ friend struct detail::vtab_impl;
+
+ sqlite3 * db_{nullptr};
+};
+
+/// Cursor needs the following member. @ingroup reference
+template<typename ColumnType = void>
+struct cursor : protected sqlite3_vtab_cursor
+{
+ using column_type = ColumnType;
+
+ /// @brief Apply a filter to the cursor. Required when best_index is implemented.
+ BOOST_SQLITE_VIRTUAL result<void> filter(
+ int /*index*/, const char * /*index_data*/,
+ boost::span<sqlite::value> /*values*/)
+ {
+ return {system::in_place_error, SQLITE_OK};
+ }
+
+ /// @brief Returns the next row.
+ BOOST_SQLITE_VIRTUAL result<void> next() BOOST_SQLITE_PURE;
+
+ /// @brief Check if the cursor is and the end
+ BOOST_SQLITE_VIRTUAL bool eof() BOOST_SQLITE_PURE;
+
+ /// @brief Returns the result of a value. It will use the set_result functionality to create a an sqlite function.
+ /// see [vtab_no_change](https://www.sqlite.org/c3ref/vtab_nochange.html)
+ BOOST_SQLITE_VIRTUAL result<column_type> column(int idx, bool no_change) BOOST_SQLITE_PURE;
+ /// @brief Returns the id of the current row
+ BOOST_SQLITE_VIRTUAL result<sqlite3_int64> row_id() BOOST_SQLITE_PURE;
+
+ /// Get the table the cursor is pointing to.
+ vtab::table<cursor> & table() { return *static_cast< vtab::table<cursor>*>(this->pVtab);}
+ const vtab::table<cursor> & table() const { return *static_cast<const vtab::table<cursor>*>(this->pVtab);}
+
+ friend struct detail::vtab_impl;
+};
+
+/// Cursor needs the following member. @ingroup reference
+template<>
+struct cursor<void> : protected sqlite3_vtab_cursor
+{
+ using column_type = void;
+
+ /// @brief Apply a filter to the cursor. Required when best_index is implemented.
+ BOOST_SQLITE_VIRTUAL result<void> filter(
+ int /*index*/, const char * /*index_data*/,
+ boost::span<sqlite::value> /*values*/)
+ {
+ return {system::in_place_error, SQLITE_OK};
+ }
+
+ /// @brief Returns the next row.
+ BOOST_SQLITE_VIRTUAL result<void> next() BOOST_SQLITE_PURE;
+
+ /// @brief Check if the cursor is and the end
+ BOOST_SQLITE_VIRTUAL bool eof() BOOST_SQLITE_PURE;
+
+ /// @brief Returns the result of a value. It will use the set_result functionality to create a an sqlite function.
+ /// see [vtab_no_change](https://www.sqlite.org/c3ref/vtab_nochange.html)
+ BOOST_SQLITE_VIRTUAL void column(context<> ctx, int idx, bool no_change) BOOST_SQLITE_PURE;
+ /// @brief Returns the id of the current row
+ BOOST_SQLITE_VIRTUAL result<sqlite3_int64> row_id() BOOST_SQLITE_PURE;
+
+ /// Get the table the cursor is pointing to.
+ vtab::table<cursor> & table() { return *static_cast< vtab::table<cursor>*>(this->pVtab);}
+ const vtab::table<cursor> & table() const { return *static_cast<const vtab::table<cursor>*>(this->pVtab);}
+
+ friend struct detail::vtab_impl;
+};
+
+
+/// Group of functions for modifications. @ingroup reference
+struct modifiable
+{
+ BOOST_SQLITE_VIRTUAL result<void> delete_(sqlite::value key) BOOST_SQLITE_PURE;
+ /// Insert a new row
+ BOOST_SQLITE_VIRTUAL result<sqlite_int64> insert(sqlite::value key, span<sqlite::value> values, int on_conflict) BOOST_SQLITE_PURE;
+ /// Update the row
+ BOOST_SQLITE_VIRTUAL result<sqlite_int64> update(sqlite::value old_key, sqlite::value new_key, span<sqlite::value> values, int on_conflict) BOOST_SQLITE_PURE;
+};
+
+/// Group of functions to support transactions. @ingroup reference
+struct transaction
+{
+ /// Begin a tranasction
+ BOOST_SQLITE_VIRTUAL result<void> begin() BOOST_SQLITE_PURE;
+ /// synchronize the state
+ BOOST_SQLITE_VIRTUAL result<void> sync() BOOST_SQLITE_PURE;
+ /// commit the transaction
+ BOOST_SQLITE_VIRTUAL result<void> commit() BOOST_SQLITE_PURE;
+ /// rollback the transaction
+ BOOST_SQLITE_VIRTUAL result<void> rollback() BOOST_SQLITE_PURE;
+};
+
+/// Fucntion to enable function overriding See `xFindFunction`.
+struct overload_functions
+{
+ /// @see https://www.sqlite.org/vtab.html#xfindfunction
+ BOOST_SQLITE_VIRTUAL result<void> find_function(
+ function_setter fs,
+ int arg, const char * name) BOOST_SQLITE_PURE;
+};
+
+/// Make the vtable renamable @ingroup reference
+struct renamable
+{
+ /// Function to rename the table. Optional
+ BOOST_SQLITE_VIRTUAL result<void> rename(const char * new_name) BOOST_SQLITE_PURE;
+
+};
+
+#if SQLITE_VERSION_NUMBER >= 3007007
+/// Support for recursive transactions @ingroup reference
+struct recursive_transaction
+{
+ /// Save the current state with to `i`
+ BOOST_SQLITE_VIRTUAL result<void> savepoint(int i) BOOST_SQLITE_PURE;
+ /// Release all saves states down to `i`
+ BOOST_SQLITE_VIRTUAL result<void> release(int i) BOOST_SQLITE_PURE;
+ /// Roll the transaction back to `i`.
+ BOOST_SQLITE_VIRTUAL result<void> rollback_to(int i) BOOST_SQLITE_PURE;
+};
+#endif
+
+}
+
+
+namespace detail
+{
+
+template<typename Module>
+const sqlite3_module make_module(const Module & mod);
+
+}
+
+////@{
+/** @brief Register a vtable
+ @ingroup reference
+ @see Sqlite vtab reference [vtab](https://www.sqlite.org/vtab.html)
+
+ @param conn The connection to install the vtable into
+ @param name The name for the vtable
+ @param module The module to install as a vtable. See @ref vtab_module_prototype for the structure required
+ @param ec The system::error_code used to deliver errors for the exception less overload.
+ @param info The error_info used to deliver errors for the exception less overload.
+
+ @param The requirements for `module`.
+
+ @tparam T The implementation type of the module. It must inherit
+
+ @returns A reference to the module as stored in the database. It's lifetime is managed by the database.
+
+
+*/
+template<typename T>
+auto create_module(connection & conn,
+ cstring_ref name,
+ T && module,
+ system::error_code & ec,
+ error_info & ei) -> typename std::decay<T>::type &
+{
+ using module_type = typename std::decay<T>::type;
+ static const sqlite3_module mod = detail::make_module(module);
+
+ std::unique_ptr<module_type> p{new (memory_tag{}) module_type(std::forward<T>(module))};
+ auto pp = p.get();
+
+ int res = sqlite3_create_module_v2(
+ conn.handle(), name.c_str(),
+ &mod, p.release(),
+ +[](void * ptr)
+ {
+ static_cast<module_type*>(ptr)->~module_type();
+ sqlite3_free(ptr);
+ });
+
+ if (res != SQLITE_OK)
+ {
+ BOOST_SQLITE_ASSIGN_EC(ec, res);
+ ei.set_message(sqlite3_errmsg(conn.handle()));
+ }
+ return *pp;
+}
+
+template<typename T>
+auto create_module(connection & conn,
+ const char * name,
+ T && module) -> typename std::decay<T>::type &
+{
+ system::error_code ec;
+ error_info ei;
+ T & ref = create_module(conn, name, std::forward<T>(module), ec, ei);
+ if (ec)
+ detail::throw_error_code(ec, ei);
+ return ref;
+}
+///@}
+
+
+BOOST_SQLITE_END_NAMESPACE
+
+#include <boost/sqlite/detail/vtable.hpp>
+
+#endif //BOOST_SQLITE_VTABLE_HPP
+