From efcea3a80da7c4479d5fe168435ecc9fd06bdc72 Mon Sep 17 00:00:00 2001 From: John Turner Date: Sun, 14 Sep 2025 00:16:10 -0400 Subject: Squashed 'subprojects/boost-sqlite/' content from commit 3378e35 git-subtree-dir: subprojects/boost-sqlite git-subtree-split: 3378e353705271e569cf4ba15c467b840a39798c --- include/boost/sqlite/detail/vtable.hpp | 611 +++++++++++++++++++++++++++++++++ 1 file changed, 611 insertions(+) create mode 100644 include/boost/sqlite/detail/vtable.hpp (limited to 'include/boost/sqlite/detail/vtable.hpp') diff --git a/include/boost/sqlite/detail/vtable.hpp b/include/boost/sqlite/detail/vtable.hpp new file mode 100644 index 0000000..f85f44c --- /dev/null +++ b/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 +#include +#include + +BOOST_SQLITE_BEGIN_NAMESPACE +namespace detail +{ +struct vtab_impl +{ + +template +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(pAux); + BOOST_SQLITE_TRY + { + result rtab = impl.connect( + sqlite::connection(db, false), + argc, argv); + + if (rtab.has_error()) + return extract_error(*errMsg, rtab); + + auto tab = make_unique(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 +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(pAux); + BOOST_SQLITE_TRY + { + result rtab = impl.create( + sqlite::connection(db, false), + argc, argv); + + if (rtab.has_error()) + return extract_error(*errMsg, rtab); + + auto tab = make_unique(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 +static int disconnect(sqlite3_vtab * tab) +{ + BOOST_SQLITE_TRY + { + auto tb = static_cast(tab); + tb->~Table(); + sqlite3_free(tb); + return SQLITE_OK; + } + BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(tab->zErrMsg); +} + +template +static int destroy(sqlite3_vtab * tab) +{ + BOOST_SQLITE_TRY + { + auto tb = static_cast(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 +static void assign_create(sqlite3_module & md, const Module &, + const sqlite::vtab::eponymous_module & base) +{ + md.xConnect = md.xCreate = &connect; + md.xDisconnect = md.xDestroy = &disconnect
; + if (base.eponymous_only()) + md.xCreate = nullptr; +} + +template +static void assign_create(sqlite3_module & md, const Module &, + const sqlite::vtab::module
&) +{ + md.xConnect = &connect; + md.xDisconnect = &disconnect
; + md.xCreate = &create; + md.xDestroy = &destroy
; +} + +template +static int open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) +{ + auto tab = static_cast
(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 +static int close(sqlite3_vtab_cursor * cursor) +{ + auto p = static_cast(cursor); + + BOOST_SQLITE_TRY + { + p->~Cursor(); + } + BOOST_SQLITE_CATCH_AND_RETURN(); + + sqlite3_free(p); + return SQLITE_OK; + +} + +template +static int best_index(sqlite3_vtab *pVTab, sqlite3_index_info* info) +{ + BOOST_SQLITE_TRY + { + auto tb = static_cast(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 +static int filter(sqlite3_vtab_cursor* pCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast(pCursor); + + auto r = cr->filter(idxNum, idxStr, + boost::span{reinterpret_cast(argv), + static_cast(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 +static int next(sqlite3_vtab_cursor* pCursor) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast(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 +static int eof(sqlite3_vtab_cursor* pCursor) +{ + return static_cast(pCursor)->eof() ? 1 : 0; +} + +template +static auto column(sqlite3_vtab_cursor* pCursor, + sqlite3_context * ctx, int idx) + -> typename std::enable_if::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(pCursor); + execute_context_function( + ctx, + [&]{ + return cr->column(idx, no_change); + }); + + return SQLITE_OK; +} + + +template +static auto column(sqlite3_vtab_cursor* pCursor, + sqlite3_context * ctx, int idx) + -> typename std::enable_if::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(pCursor); + BOOST_SQLITE_TRY + { + cr->column(context<>{ctx}, idx, no_change); + } + BOOST_SQLITE_CATCH_RESULT(ctx); + return SQLITE_OK; +} + +template +static int row_id(sqlite3_vtab_cursor* pCursor, sqlite3_int64 *pRowid) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast(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 +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(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{reinterpret_cast(argv + 2), + static_cast(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{reinterpret_cast(argv + 2), + static_cast(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 +static void assign_update(sqlite3_module & md, const Module &, + std::true_type /* modifiable */) +{ + md.xUpdate = &update; +} + +template +static void assign_update(sqlite3_module & /*md*/, const Module &, + std::false_type /* modifiable */) +{ +} + +template +static int begin(sqlite3_vtab* pVTab) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast(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 +static int sync(sqlite3_vtab* pVTab) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast(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 +static int commit(sqlite3_vtab* pVTab) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast(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 +static int rollback(sqlite3_vtab* pVTab) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast(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 +static void assign_transaction(sqlite3_module & /*md*/, const Module &, + std::false_type /* modifiable */) +{ +} + +template +static void assign_transaction(sqlite3_module & md, const Module &, + std::true_type /* modifiable */) +{ + md.xBegin = &begin ; + md.xSync = &sync ; + md.xCommit = &commit ; + md.xRollback = &rollback; +} + +template +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(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 +static void assign_find_function(sqlite3_module & /*md*/, const Module &, + std::false_type /* overloadable */) +{ +} + +template +static void assign_find_function(sqlite3_module & md, const Module &, + std::true_type /* overloadable */) +{ + md.xFindFunction = &find_function; +} + +template +static int rename(sqlite3_vtab* pVTab, const char * name) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast(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 +static void assign_rename(sqlite3_module & /*md*/, const Module &, + std::false_type /* renamable */) +{ +} + +template +static void assign_rename(sqlite3_module & md, const Module &, + std::true_type /* renamable */) +{ + md.xRename = &rename; +} +#if SQLITE_VERSION_NUMBER >= 3007007 + +template +static int savepoint(sqlite3_vtab* pVTab, int i) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast(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 +static int release(sqlite3_vtab* pVTab, int i) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast(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 +static int rollback_to(sqlite3_vtab* pVTab, int i) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast(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 +static void assign_recursive_transaction(sqlite3_module & /*md*/, const Module &, + std::false_type /* recursive_transaction */) +{ +} + +template +static void assign_recursive_transaction(sqlite3_module & md, const Module &, + std::true_type /* recursive_transaction */) +{ + md.xSavepoint = &savepoint ; + md.xRelease = &release ; + md.xRollbackTo = &rollback_to; +} + +#endif + +#if SQLITE_VERSION_NUMBER >= 3026000 + +template +static void assign_shadow_name(sqlite3_module & /*md*/, const sqlite::vtab::module
&) {} + +template +static void assign_shadow_name(sqlite3_module & /*md*/, const sqlite::vtab::eponymous_module
&) {} + +template +static void assign_shadow_name(sqlite3_module & md, const Module & mod) +{ + md.xShadowName = +[](const char * name){return Func(name) != 0;}; +} + +#endif + +}; + +template +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; + md.xOpen = &vtab_impl::open ; + md.xClose = &vtab_impl::close ; + md.xFilter = &vtab_impl::filter ; + md.xNext = &vtab_impl::next ; + md.xEof = &vtab_impl::eof ; + md.xColumn = &vtab_impl::column ; + md.xRowid = &vtab_impl::row_id ; + vtab_impl::assign_update (md, mod, std::is_base_of{}); + vtab_impl::assign_transaction (md, mod, std::is_base_of{}); + vtab_impl::assign_find_function(md, mod, std::is_base_of{}); + vtab_impl::assign_rename (md, mod, std::is_base_of{}); +#if SQLITE_VERSION_NUMBER >= 3007007 + vtab_impl::assign_recursive_transaction(md, mod, std::is_base_of{}); +#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 -- cgit v1.2.3