diff options
Diffstat (limited to 'subprojects/boost-sqlite/example/url.cpp')
-rw-r--r-- | subprojects/boost-sqlite/example/url.cpp | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/subprojects/boost-sqlite/example/url.cpp b/subprojects/boost-sqlite/example/url.cpp new file mode 100644 index 0000000..f5ed4f9 --- /dev/null +++ b/subprojects/boost-sqlite/example/url.cpp @@ -0,0 +1,315 @@ +// +// 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) +// + +#include <boost/sqlite/extension.hpp> +#include <boost/sqlite/vtable.hpp> +#include <boost/system/result.hpp> +#include <boost/url.hpp> + +using namespace boost; + +// tag::subtype[] +constexpr int pct_subtype = static_cast<int>('U'); + +void tag_invoke(sqlite::set_result_tag, sqlite3_context * ctx, urls::pct_string_view value) +{ + using boost::sqlite::ext::sqlite3_api; + // we're using the sqlite API here directly, because we need to set a different subtype + sqlite3_result_text(ctx, value.data(), value.size(), nullptr); + sqlite3_result_subtype(ctx, pct_subtype); +} + + +void tag_invoke(sqlite::set_result_tag, sqlite3_context * ctx, const urls::segments_encoded_view & value) +{ + using boost::sqlite::ext::sqlite3_api; + // we're using the sqlite API here directly, because we need to set a different subtype + sqlite3_result_text(ctx, value.buffer().data(), value.buffer().size(), nullptr); + sqlite3_result_subtype(ctx, pct_subtype); +} +// end::subtype[] + +struct url_cursor final + : sqlite::vtab::cursor< + variant2::variant<variant2::monostate, core::string_view, urls::pct_string_view> + > +{ + url_cursor(urls::url_view view) : view(view ) {} + + urls::url_view view; + bool done{false}; + + sqlite::result<void> next() { done = true; return {};} + + sqlite::result<sqlite3_int64> row_id() {return static_cast<sqlite3_int64>(0);} + sqlite::result<column_type> column(int i, bool /*nochange*/) + { + switch (i) + { + case 0: return view.scheme(); + case 1: return view.encoded_user(); + case 2: return view.encoded_password(); + case 3: return view.encoded_host(); + case 4: return view.port(); + case 5: return view.encoded_path(); + case 6: return view.encoded_query(); + case 7: return view.encoded_fragment(); + case 8: return view.buffer(); + default: + return variant2::monostate{}; + } + } + sqlite::result<void> filter(int /*idx*/, const char * /*idxStr*/, span<sqlite::value> values) + { + if (values.size() > 0u) + view = urls::parse_uri(values[0].get_text()).value(); + return {}; + } + + bool eof() noexcept {return done || view.empty();} +}; + +struct url_wrapper final : sqlite::vtab::table<url_cursor> +{ + urls::url value; + const char * declaration() override + { + return R"( + create table url( + scheme text, + user text, + password text, + host text, + port text, + path text, + query text, + fragment text, + url text hidden);)"; + } + + sqlite::result<url_cursor> open() override + { + return url_cursor{value}; + } + + sqlite::result<void> best_index(sqlite::vtab::index_info & info) override + { + for (const auto & constraint : info.constraints()) + { + if (constraint.iColumn == 8 && constraint.usable) + { + if (constraint.op != SQLITE_INDEX_CONSTRAINT_EQ) + return {SQLITE_MISUSE, + sqlite::error_info("query only support equality constraints")}; + info.usage_of(constraint).argvIndex = 1; + info.set_index(1); + } + } + + return {}; + } +}; + + +struct url_module final : sqlite::vtab::eponymous_module<url_wrapper> +{ + sqlite::result<url_wrapper> connect(sqlite::connection /*db*/, + int /*argc*/, const char * const */*argv*/) + { + return url_wrapper{}; + } +}; + +struct segements_cursor final : sqlite::vtab::cursor< + variant2::variant<variant2::monostate, std::int64_t, core::string_view, urls::segments_encoded_view>> +{ + segements_cursor(urls::segments_encoded_view view) : view(view) {} + urls::segments_encoded_view view; + urls::segments_encoded_view::const_iterator itr{view.begin()}; + + sqlite::result<void> next() override { itr++; return {};} + + sqlite::result<sqlite3_int64> row_id() override {return std::distance(view.begin(), itr);} + sqlite::result<column_type> column(int i, bool /*nochange*/) override + { + //nochange = true; + switch (i) + { + case 0: return std::distance(view.begin(), itr); + case 1: return *itr; + case 2: return view; + default: + return variant2::monostate{}; + } + } + sqlite::result<void> filter(int /*idx*/, const char * /*idxStr*/, + span<sqlite::value> values) override + { + if (values.size() > 0u) + view = urls::segments_encoded_view(values[0].get_text()); + itr = view.begin(); + return {}; + } + bool eof() noexcept override {return itr == view.end();} +}; + +struct segment_wrapper final : sqlite::vtab::table<segements_cursor> +{ + urls::segments_encoded_view value; + const char * declaration() override + { + return R"( + create table segments( + idx integer, + segment text, + segments text hidden);)"; + } + + + + sqlite::result<segements_cursor> open() override + { + return segements_cursor{value}; + } + + sqlite::result<void> best_index(sqlite::vtab::index_info & info) override + { + for (auto & constraint : info.constraints()) + { + if (constraint.iColumn == 2 + && constraint.usable) + { + if (constraint.op != SQLITE_INDEX_CONSTRAINT_EQ) + return {SQLITE_OK, sqlite::error_info("segments only support equality constraints")}; + info.usage_of(constraint).argvIndex = 1; + info.set_index(1); + } + } + + return {}; + } +}; + + +struct segments_module final : sqlite::vtab::eponymous_module<segment_wrapper> +{ + sqlite::result<segment_wrapper> connect(sqlite::connection /*conn*/, + int /*argc*/, const char * const */*argv*/) + { + return segment_wrapper{}; + } +}; + +// tag::query_cursor[] +struct query_cursor final : sqlite::vtab::cursor< + variant2::variant<variant2::monostate, std::int64_t, core::string_view, urls::pct_string_view> + > +{ + urls::params_encoded_view view; + urls::params_encoded_view::const_iterator itr{view.begin()}; + + sqlite::result<void> next() override { itr++; return {};} + + sqlite::result<sqlite3_int64> row_id() override {return std::distance(view.begin(), itr);} + sqlite::result<column_type> column(int i, bool /*nochange*/) override // <1> + { + //nochange = true; + switch (i) + { + case 0: return std::distance(view.begin(), itr); + case 1: return itr->key; + case 2: + if (!itr->has_value) + return variant2::monostate{}; + else + return itr->value; + case 3: return view.buffer(); + default: + return variant2::monostate{}; + } + } + sqlite::result<void> filter(int /*idx*/, const char * /*idxStr*/, + span<sqlite::value> values) override + { + if (values.size() > 0u) // <2> + view = urls::params_encoded_view(values[0].get_text()); + itr = view.begin(); + + return {}; + } + bool eof() noexcept override {return itr == view.end();} +}; +// end::query_cursor[] + +// tag::query_boiler_plate[] +struct query_wrapper final : sqlite::vtab::table<query_cursor> +{ + const char * declaration() override + { + return R"( + create table queries( + idx integer, + name text, + value text, + query_string text hidden);)"; // <1> + } + + sqlite::result<query_cursor> open() override + { + return query_cursor{}; + } + + sqlite::result<void> best_index(sqlite::vtab::index_info & info) override + { + for (auto & constraint : info.constraints()) + { + if (constraint.iColumn == 3 + && constraint.usable) + { + if (constraint.op != SQLITE_INDEX_CONSTRAINT_EQ) // <2> + return sqlite::error{SQLITE_OK, "query only support equality constraints"}; + + info.usage_of(constraint).argvIndex = 1; + info.set_index(1); + } + } + return {}; + } +}; + +struct query_module final : sqlite::vtab::eponymous_module<query_wrapper> +{ + sqlite::result<query_wrapper> connect(sqlite::connection /*conn*/, + int /*argc*/, const char * const */*argv*/) + { + return query_wrapper{}; + } +}; +// end::query_boiler_plate[] + +BOOST_SQLITE_EXTENSION(url, conn) +{ + sqlite::create_module(conn, "url", url_module{}); + sqlite::create_module(conn, "segments", segments_module()); + + // tag::query_boiler_plate[] + sqlite::create_module(conn, "query", query_module()); + // end::query_boiler_plate[] + sqlite::create_scalar_function( + conn, "pct_decode", + +[](boost::sqlite::context<> , boost::span<boost::sqlite::value, 1u> s) + { + return urls::pct_string_view(s[0].get_text()).decode(); + }); + + sqlite::create_scalar_function( + conn, "pct_encode", + +[](boost::sqlite::context<> , boost::span<boost::sqlite::value, 1u> s) + { + return urls::encode(s[0].get_text(), urls::pchars); + }); +}
\ No newline at end of file |