VSQLite++ 0.3
Loading...
Searching...
No Matches
function.hpp
Go to the documentation of this file.
1/*##############################################################################
2 VSQLite++ - virtuosic bytes SQLite3 C++ wrapper
3
4 Copyright (c) 2006-2024 Vinzenz Feenstra
5 and contributors
6 All rights reserved.
7
8 Redistribution and use in source and binary forms, with or without modification,
9 are permitted provided that the following conditions are met:
10
11 * Redistributions of source code must retain the above copyright notice,
12 this list of conditions and the following disclaimer.
13 * Redistributions in binary form must reproduce the above copyright notice,
14 this list of conditions and the following disclaimer in the documentation
15 and/or other materials provided with the distribution.
16 * Neither the name of virtuosic bytes nor the names of its contributors may
17 be used to endorse or promote products derived from this software without
18 specific prior written permission.
19
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 POSSIBILITY OF SUCH DAMAGE.
31
32##############################################################################*/
33#ifndef GUARD_SQLITE_FUNCTION_HPP_INCLUDED
34#define GUARD_SQLITE_FUNCTION_HPP_INCLUDED
35
36#include <concepts>
37#include <cstddef>
38#include <exception>
39#include <format>
40#include <functional>
41#include <limits>
42#include <memory>
43#include <optional>
44#include <span>
45#include <string>
46#include <string_view>
47#include <tuple>
48#include <type_traits>
49#include <utility>
50#include <vector>
51
54
55#include <sqlite3.h>
56
64namespace sqlite {
65inline namespace v2 {
66
69 int arity = -1;
70 int text_representation = SQLITE_UTF8;
71 bool deterministic = false;
72 bool direct_only = false;
73 bool innocuous = false;
74 };
75
76 struct null_type;
77
78 namespace detail {
79 template <typename Callable> struct function_holder {
80 std::decay_t<Callable> callable;
81 };
82
83 template <typename Callable, typename Enable = void> struct callable_traits;
84
85 template <typename R, typename... Args> struct callable_traits<R(Args...), void> {
86 using return_type = R;
87 using arguments_tuple = std::tuple<Args...>;
88 static constexpr std::size_t arity = sizeof...(Args);
89
90 template <std::size_t Index>
91 using argument = std::tuple_element_t<Index, arguments_tuple>;
92 };
93
94 template <typename R, typename... Args>
95 struct callable_traits<R (*)(Args...), void> : callable_traits<R(Args...)> {};
96
97 template <typename R, typename... Args>
98 struct callable_traits<R (&)(Args...), void> : callable_traits<R(Args...)> {};
99
100 template <typename Class, typename R, typename... Args>
101 struct callable_traits<R (Class::*)(Args...), void> : callable_traits<R(Args...)> {};
102
103 template <typename Class, typename R, typename... Args>
104 struct callable_traits<R (Class::*)(Args...) const, void> : callable_traits<R(Args...)> {};
105
106#if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510
107 template <typename Class, typename R, typename... Args>
108 struct callable_traits<R (Class::*)(Args...) noexcept, void> : callable_traits<R(Args...)> {
109 };
110
111 template <typename Class, typename R, typename... Args>
112 struct callable_traits<R (Class::*)(Args...) const noexcept, void>
113 : callable_traits<R(Args...)> {};
114
115 template <typename R, typename... Args>
116 struct callable_traits<R (*)(Args...) noexcept, void> : callable_traits<R(Args...)> {};
117
118 template <typename R, typename... Args>
119 struct callable_traits<R (&)(Args...) noexcept, void> : callable_traits<R(Args...)> {};
120#endif
121
122 template <typename Callable>
123 struct callable_traits<Callable, std::void_t<decltype(&Callable::operator())>>
124 : callable_traits<decltype(&Callable::operator())> {};
125
126 template <typename Callable>
127 struct callable_traits<Callable const,
128 std::enable_if_t<!std::is_function_v<Callable>, void>>
129 : callable_traits<Callable> {};
130
131 template <typename Callable>
132 struct callable_traits<Callable volatile,
133 std::enable_if_t<!std::is_function_v<Callable>, void>>
134 : callable_traits<Callable> {};
135
136 template <typename Callable>
137 struct callable_traits<Callable const volatile,
138 std::enable_if_t<!std::is_function_v<Callable>, void>>
139 : callable_traits<Callable> {};
140
141 template <typename Callable>
143 Callable &,
144 std::enable_if_t<!std::is_function_v<std::remove_reference_t<Callable>>, void>>
146
147 template <typename Callable>
149 Callable &&,
150 std::enable_if_t<!std::is_function_v<std::remove_reference_t<Callable>>, void>>
152
153 template <typename T> using decay_t = std::remove_cvref_t<T>;
154
155 template <typename T> struct is_optional : std::false_type {};
156
157 template <typename U> struct is_optional<std::optional<U>> : std::true_type {};
158
160 throw database_exception(
161 "NULL passed to SQL function argument but callable parameter is not nullable.");
162 }
163
164 template <typename T, typename Enable = void> struct argument_converter;
165
166 template <typename T>
167 requires(std::is_integral_v<decay_t<T>> && !std::is_same_v<decay_t<T>, bool>)
169 static decay_t<T> convert(sqlite3_value *value) {
170 if (sqlite3_value_type(value) == SQLITE_NULL) {
172 }
173 return static_cast<decay_t<T>>(sqlite3_value_int64(value));
174 }
175 };
176
177 template <> struct argument_converter<bool> {
178 static bool convert(sqlite3_value *value) {
179 if (sqlite3_value_type(value) == SQLITE_NULL) {
181 }
182 return sqlite3_value_int(value) != 0;
183 }
184 };
185
186 template <typename T>
187 requires std::is_floating_point_v<decay_t<T>>
188 struct argument_converter<T> {
189 static decay_t<T> convert(sqlite3_value *value) {
190 if (sqlite3_value_type(value) == SQLITE_NULL) {
192 }
193 return static_cast<decay_t<T>>(sqlite3_value_double(value));
194 }
195 };
196
197 template <> struct argument_converter<std::string_view> {
198 static std::string_view convert(sqlite3_value *value) {
199 if (sqlite3_value_type(value) == SQLITE_NULL) {
201 }
202 auto const len = sqlite3_value_bytes(value);
203 auto const *text = reinterpret_cast<char const *>(sqlite3_value_text(value));
204 if (!text) {
205 return std::string_view();
206 }
207 return std::string_view(text, static_cast<std::size_t>(len));
208 }
209 };
210
211 template <typename Text>
212 requires(!std::is_same_v<decay_t<Text>, std::string_view> &&
213 std::constructible_from<decay_t<Text>, std::string_view>)
215 static decay_t<Text> convert(sqlite3_value *value) {
217 }
218 };
219
220 template <> struct argument_converter<std::span<const unsigned char>> {
221 static std::span<const unsigned char> convert(sqlite3_value *value) {
222 if (sqlite3_value_type(value) == SQLITE_NULL) {
224 }
225 auto const len = sqlite3_value_bytes(value);
226 auto const *data = static_cast<unsigned char const *>(sqlite3_value_blob(value));
227 return std::span<const unsigned char>(data, static_cast<std::size_t>(len));
228 }
229 };
230
231 template <> struct argument_converter<std::span<const std::byte>> {
232 static std::span<const std::byte> convert(sqlite3_value *value) {
234 auto ptr = reinterpret_cast<std::byte const *>(bytes.data());
235 return std::span<const std::byte>(ptr, bytes.size());
236 }
237 };
238
239 template <typename Vector>
240 requires(std::is_same_v<decay_t<Vector>, std::vector<unsigned char>> ||
241 std::is_same_v<decay_t<Vector>, std::vector<std::byte>>)
243 static decay_t<Vector> convert(sqlite3_value *value) {
244 auto span = argument_converter<std::conditional_t<
245 std::is_same_v<decay_t<Vector>, std::vector<unsigned char>>,
246 std::span<const unsigned char>, std::span<const std::byte>>>::convert(value);
247 return decay_t<Vector>(span.begin(), span.end());
248 }
249 };
250
251 template <> struct argument_converter<sqlite3_value *> {
252 static sqlite3_value *convert(sqlite3_value *value) {
253 return value;
254 }
255 };
256
257 template <> struct argument_converter<sqlite3_value const *> {
258 static sqlite3_value const *convert(sqlite3_value *value) {
259 return value;
260 }
261 };
262
263 template <typename T> struct argument_converter<std::optional<T>> {
264 static_assert(!is_optional<decay_t<T>>::value,
265 "Nested std::optional values are not supported.");
266 static std::optional<T> convert(sqlite3_value *value) {
267 if (sqlite3_value_type(value) == SQLITE_NULL) {
268 return std::nullopt;
269 }
270 return argument_converter<T>::convert(value);
271 }
272 };
273
274 template <typename T, typename Enable = void> struct result_writer;
275
276 template <> struct result_writer<void> {
277 static void apply(sqlite3_context *ctx, void const *) {
278 sqlite3_result_null(ctx);
279 }
280 };
281
282 template <> struct result_writer<null_type> {
283 static void apply(sqlite3_context *ctx, null_type const &) {
284 sqlite3_result_null(ctx);
285 }
286 };
287
288 template <> struct result_writer<std::nullptr_t> {
289 static void apply(sqlite3_context *ctx, std::nullptr_t) {
290 sqlite3_result_null(ctx);
291 }
292 };
293
294 template <typename T>
295 requires(std::is_integral_v<decay_t<T>> && !std::is_same_v<decay_t<T>, bool>)
297 static void apply(sqlite3_context *ctx, T value) {
298 sqlite3_result_int64(ctx, static_cast<sqlite3_int64>(value));
299 }
300 };
301
302 template <> struct result_writer<bool> {
303 static void apply(sqlite3_context *ctx, bool value) {
304 sqlite3_result_int(ctx, value ? 1 : 0);
305 }
306 };
307
308 template <typename T>
309 requires std::is_floating_point_v<decay_t<T>>
310 struct result_writer<T> {
311 static void apply(sqlite3_context *ctx, T value) {
312 sqlite3_result_double(ctx, static_cast<double>(value));
313 }
314 };
315
316 inline void result_text(sqlite3_context *ctx, std::string_view view) {
317 sqlite3_result_text(ctx, view.data(), static_cast<int>(view.size()), SQLITE_TRANSIENT);
318 }
319
320 template <> struct result_writer<std::string_view> {
321 static void apply(sqlite3_context *ctx, std::string_view value) {
322 result_text(ctx, value);
323 }
324 };
325
326 template <typename Text>
327 requires(!std::is_same_v<decay_t<Text>, std::string_view> &&
328 std::constructible_from<std::string_view, decay_t<Text>>)
330 static void apply(sqlite3_context *ctx, Text const &value) {
331 result_text(ctx, std::string_view(value));
332 }
333 };
334
335 template <> struct result_writer<std::span<const unsigned char>> {
336 static void apply(sqlite3_context *ctx, std::span<const unsigned char> value) {
337 sqlite3_result_blob(ctx, value.data(), static_cast<int>(value.size()),
338 SQLITE_TRANSIENT);
339 }
340 };
341
342 template <> struct result_writer<std::span<const std::byte>> {
343 static void apply(sqlite3_context *ctx, std::span<const std::byte> value) {
344 auto ptr = reinterpret_cast<unsigned char const *>(value.data());
345 sqlite3_result_blob(ctx, ptr, static_cast<int>(value.size()), SQLITE_TRANSIENT);
346 }
347 };
348
349 template <typename Vector>
350 requires(std::is_same_v<decay_t<Vector>, std::vector<unsigned char>> ||
351 std::is_same_v<decay_t<Vector>, std::vector<std::byte>>)
353 static void apply(sqlite3_context *ctx, Vector const &value) {
354 sqlite3_result_blob(ctx, reinterpret_cast<unsigned char const *>(value.data()),
355 static_cast<int>(value.size()), SQLITE_TRANSIENT);
356 }
357 };
358
359 template <typename T> struct result_writer<std::optional<T>> {
360 static void apply(sqlite3_context *ctx, std::optional<T> const &value) {
361 if (!value) {
362 sqlite3_result_null(ctx);
363 return;
364 }
365 result_writer<T>::apply(ctx, *value);
366 }
367 };
368
369 template <typename Callable, std::size_t... Index>
371 sqlite3_value **argv, std::index_sequence<Index...>) {
372 using traits = callable_traits<Callable>;
373 if constexpr (sizeof...(Index) == 0) {
374 return std::invoke(holder.callable);
375 } else {
376 return std::invoke(
377 holder.callable,
378 argument_converter<decay_t<typename traits::template argument<Index>>>::convert(
379 argv[Index])...);
380 }
381 }
382
383 template <typename Callable> void destroy_holder(void *data) {
384 delete static_cast<function_holder<Callable> *>(data);
385 }
386
387 template <typename Callable>
388 void function_entry(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
389 using traits = callable_traits<Callable>;
390 auto *holder = static_cast<function_holder<Callable> *>(sqlite3_user_data(ctx));
391 if (!holder) {
392 sqlite3_result_error(ctx, "SQL function metadata missing.", -1);
393 return;
394 }
395 if (argc != static_cast<int>(traits::arity)) {
396 sqlite3_result_error(ctx, "Unexpected number of arguments for SQL function.", -1);
397 return;
398 }
399 try {
400 if constexpr (std::is_void_v<typename traits::return_type>) {
402 std::make_index_sequence<traits::arity>{});
403 sqlite3_result_null(ctx);
404 } else {
406 *holder, argv, std::make_index_sequence<traits::arity>{});
408 }
409 } catch (database_exception const &ex) {
410 sqlite3_result_error(ctx, ex.what(), -1);
411 } catch (std::exception const &ex) {
412 sqlite3_result_error(ctx, ex.what(), -1);
413 } catch (...) {
414 sqlite3_result_error(ctx, "Unhandled exception in SQL function.", -1);
415 }
416 }
417
418 inline int compose_text_rep(function_options const &options) {
419 int rep = options.text_representation;
420#ifdef SQLITE_DETERMINISTIC
421 if (options.deterministic) {
422 rep |= SQLITE_DETERMINISTIC;
423 }
424#endif
425#ifdef SQLITE_DIRECTONLY
426 if (options.direct_only) {
427 rep |= SQLITE_DIRECTONLY;
428 }
429#endif
430#ifdef SQLITE_INNOCUOUS
431 if (options.innocuous) {
432 rep |= SQLITE_INNOCUOUS;
433 }
434#endif
435 return rep;
436 }
437
438 inline std::string make_function_error(std::string_view name) {
439 if (name.empty()) {
440 return "Failed to register SQL function.";
441 }
442 return std::format("Failed to register SQL function '{}'.", name);
443 }
444 } // namespace detail
445
446 template <typename Callable>
447 void create_function(connection &con, std::string_view name, Callable &&callable,
448 function_options options = {}) {
449 using callable_t = std::decay_t<Callable>;
450 using traits = detail::callable_traits<callable_t>;
451 static_assert(traits::arity <= static_cast<std::size_t>(std::numeric_limits<int>::max()),
452 "SQL functions cannot expose more than INT_MAX parameters.");
453
454 constexpr auto max_arity = static_cast<int>(traits::arity);
455 int arity = options.arity;
456 if (arity < 0) {
457 arity = max_arity;
458 }
459 if (arity != max_arity) {
460 throw database_exception("Explicit arity override does not match callable signature.");
461 }
462
463 auto holder =
464 std::make_unique<detail::function_holder<callable_t>>(std::forward<Callable>(callable));
465
467 auto handle = private_accessor::get_handle(con);
468 std::string name_buffer(name);
469 auto text_rep = detail::compose_text_rep(options);
470
471 int rc = sqlite3_create_function_v2(handle, name_buffer.c_str(), arity, text_rep,
473 nullptr, nullptr, &detail::destroy_holder<callable_t>);
474
475 if (rc != SQLITE_OK) {
476 auto err = sqlite3_errmsg(handle);
477 throw database_exception_code(err ? err : detail::make_function_error(name), rc);
478 }
479
480 holder.release();
481 }
482
483} // namespace v2
484} // namespace sqlite
485
486#endif // GUARD_SQLITE_FUNCTION_HPP_INCLUDED
Exception hierarchy mirroring common SQLite failure categories.
void throw_null_argument_error()
Definition function.hpp:159
std::remove_cvref_t< T > decay_t
void destroy_holder(void *data)
Definition function.hpp:383
int compose_text_rep(function_options const &options)
Definition function.hpp:418
void function_entry(sqlite3_context *ctx, int argc, sqlite3_value **argv)
Definition function.hpp:388
decltype(auto) invoke_with_arguments(function_holder< Callable > &holder, sqlite3_value **argv, std::index_sequence< Index... >)
Definition function.hpp:370
void result_text(sqlite3_context *ctx, std::string_view view)
Definition function.hpp:316
std::string make_function_error(std::string_view name)
Definition function.hpp:438
void create_function(connection &con, std::string_view name, Callable &&callable, function_options options={})
Definition function.hpp:447
connection is used to open, close, attach and detach a database. Further it has to be passed to all c...
Exception that carries the original SQLite error code and optional SQL snippet.
Generic runtime failure raised for most SQLite errors.
static decay_t< T > convert(sqlite3_value *value)
Definition function.hpp:169
static decay_t< Text > convert(sqlite3_value *value)
Definition function.hpp:215
static decay_t< Vector > convert(sqlite3_value *value)
Definition function.hpp:243
static bool convert(sqlite3_value *value)
Definition function.hpp:178
static sqlite3_value * convert(sqlite3_value *value)
Definition function.hpp:252
static sqlite3_value const * convert(sqlite3_value *value)
Definition function.hpp:258
static std::optional< T > convert(sqlite3_value *value)
Definition function.hpp:266
static std::span< const std::byte > convert(sqlite3_value *value)
Definition function.hpp:232
static std::span< const unsigned char > convert(sqlite3_value *value)
Definition function.hpp:221
static std::string_view convert(sqlite3_value *value)
Definition function.hpp:198
std::tuple_element_t< Index, arguments_tuple > argument
Definition function.hpp:91
std::decay_t< Callable > callable
Definition function.hpp:80
static void apply(sqlite3_context *ctx, T value)
Definition function.hpp:297
static void apply(sqlite3_context *ctx, Text const &value)
Definition function.hpp:330
static void apply(sqlite3_context *ctx, Vector const &value)
Definition function.hpp:353
static void apply(sqlite3_context *ctx, bool value)
Definition function.hpp:303
static void apply(sqlite3_context *ctx, null_type const &)
Definition function.hpp:283
static void apply(sqlite3_context *ctx, std::nullptr_t)
Definition function.hpp:289
static void apply(sqlite3_context *ctx, std::optional< T > const &value)
Definition function.hpp:360
static void apply(sqlite3_context *ctx, std::span< const std::byte > value)
Definition function.hpp:343
static void apply(sqlite3_context *ctx, std::span< const unsigned char > value)
Definition function.hpp:336
static void apply(sqlite3_context *ctx, std::string_view value)
Definition function.hpp:321
static void apply(sqlite3_context *ctx, void const *)
Definition function.hpp:277
Options passed alongside a callable when registering it with sqlite3_create_function_v2.
Definition function.hpp:68
null_type is an empty type used to represent NULL values
Definition command.hpp:70
static void acccess_check(connection &m_con)
static struct sqlite3 * get_handle(connection &m_con)
Forward-only cursor over the rows produced by a prepared statement.
Definition result.hpp:75
view is used to create views. In SQLite a view can only be queried. INSERT, DELETE and UPDATE will fa...
Definition view.hpp:50