boost_sqlite 1
A sqlite C++ library
Loading...
Searching...
No Matches
statement.hpp
1// Copyright (c) 2022 Klemens D. Morgenstern
2//
3// Distributed under the Boost Software License, Version 1.0. (See accompanying
4// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5#ifndef BOOST_SQLITE_STATEMENT_HPP
6#define BOOST_SQLITE_STATEMENT_HPP
7
8#include <boost/sqlite/detail/config.hpp>
9#include <boost/sqlite/detail/exception.hpp>
10#include <boost/sqlite/blob.hpp>
11#include <boost/sqlite/resultset.hpp>
12
13#include <boost/mp11/algorithm.hpp>
14#include <boost/core/ignore_unused.hpp>
15#include <boost/variant2/variant.hpp>
16
17
18#include <tuple>
19
20BOOST_SQLITE_BEGIN_NAMESPACE
21struct connection;
22template<typename>
23struct static_resultset;
24
28{
30 param_ref() = default;
32 param_ref(variant2::monostate) : impl_{variant2::in_place_type_t<variant2::monostate>{}} {}
34 param_ref(std::nullptr_t) : impl_{variant2::in_place_type_t<variant2::monostate>{}} {}
36 template<typename I,
37 typename = typename std::enable_if<std::is_integral<I>::value>::type>
39 {
40 BOOST_IF_CONSTEXPR ((sizeof(I) == sizeof(int) && std::is_unsigned<I>::value)
41 || (sizeof(I) > sizeof(int)))
42 impl_.emplace<sqlite3_int64>(static_cast<sqlite3_int64>(value));
43 else
44 impl_.emplace<int>(static_cast<int>(value));
45 }
49 param_ref(string_view text) : impl_(text) { }
50
51 template<typename StringLike>
52 param_ref(StringLike && text,
53 typename std::enable_if<std::is_constructible<string_view, StringLike>::value>::type * = nullptr)
54 : impl_(variant2::in_place_type_t<string_view>{}, text) {}
55
56 template<typename BlobLike>
57 param_ref(BlobLike && text,
58 typename std::enable_if<
59 !std::is_constructible<string_view, BlobLike>::value
60 && std::is_constructible<blob_view, BlobLike>::value>::type * = nullptr)
61 : impl_(variant2::in_place_type_t<blob_view>{}, text) {}
62
64 param_ref(double value) : impl_(value) { }
66 param_ref(zero_blob zb) : impl_(zb) { }
67
68#if SQLITE_VERSION_NUMBER >= 3020000
70 template<typename T>
71 param_ref(std::unique_ptr<T> ptr)
72 : impl_(variant2::in_place_index_t<7>{},
73 std::unique_ptr<void, void(*)(void*)>(
74 static_cast<void*>(ptr.release()),
75 +[](void * ptr){delete static_cast<T*>(ptr);}),
76 typeid(T).name())
77 {
78 }
79
81 template<typename T>
82 param_ref(std::unique_ptr<T, void(*)(T*)> ptr)
83 : impl_(variant2::in_place_index_t<7>{},
84 std::unique_ptr<void, void(*)(void*)>(
85 static_cast<void*>(ptr.release()),
86 +[](void * ptr){delete static_cast<T*>(ptr);}),
87 typeid(T).name())
88 {
89 }
90
93 template<typename T, typename Deleter>
94 param_ref(std::unique_ptr<T, Deleter> ptr,
95 typename std::enable_if<std::is_empty<Deleter>::value &&
96 std::is_default_constructible<Deleter>::value, int>::type * = nullptr)
97 : impl_(variant2::in_place_index_t<7>{},
98 std::unique_ptr<void, void(*)(void*)>(
99 static_cast<void*>(ptr.release()),
100 +[](void * ptr){delete static_cast<T*>(ptr);}),
101 typeid(T).name())
102 {
103 }
104#endif
105
107 int apply(sqlite3_stmt * stmt, int c) const
108 {
109 return variant2::visit(visitor{stmt, c}, impl_);
110 }
111
112 private:
113 struct make_visitor
114 {
115 template<typename T>
116 auto operator()(T&& t) const -> typename std::enable_if<std::is_constructible<param_ref, T&&>::value, param_ref>::type
117 {
118 return param_ref(std::forward<T>(t));
119 }
120 };
121
122 public:
124 template<typename T>
125 param_ref(T && t,
126 decltype(variant2::visit(make_visitor(), std::forward<T>(t))) * = nullptr)
127 : param_ref(variant2::visit(make_visitor(), std::forward<T>(t)))
128 {}
129 private:
130
131 struct visitor
132 {
133 sqlite3_stmt * stmt;
134 int col;
135
136 int operator()(variant2::monostate )
137 {
138 return sqlite3_bind_null(stmt, col);
139 }
140 int operator()(int i )
141 {
142 return sqlite3_bind_int(stmt, col, i);
143 }
144 int operator()(sqlite3_int64 i64 )
145 {
146 return sqlite3_bind_int64(stmt, col, i64);
147 }
148
149 int operator()(blob_view blob)
150 {
151 if (blob.size() > static_cast<std::size_t>(std::numeric_limits<int>::max()))
152 return sqlite3_bind_blob64(stmt, col, blob.data(), blob.size(), SQLITE_STATIC);
153 else
154 return sqlite3_bind_blob(stmt, col, blob.data(), blob.size(), SQLITE_STATIC);
155 }
156
157 int operator()(string_view text)
158 {
159 if (text.size() > std::numeric_limits<int>::max())
160 return sqlite3_bind_text64(stmt, col, text.data(), text.size(), SQLITE_STATIC, SQLITE_UTF8);
161 else
162 return sqlite3_bind_text(stmt, col, text.data(), text.size(), SQLITE_STATIC);
163 }
164 int operator()(double value)
165 {
166 return sqlite3_bind_double(stmt, col, value);
167 }
168 int operator()(zero_blob zb)
169 {
170 if (static_cast<std::size_t>(zb) > static_cast<std::size_t>(std::numeric_limits<int>::max()))
171 return sqlite3_bind_zeroblob64(stmt, col, static_cast<sqlite3_uint64>(zb));
172 else
173 return sqlite3_bind_zeroblob(stmt, col, static_cast<int>(zb));
174 }
175#if SQLITE_VERSION_NUMBER >= 3020000
176 int operator()(std::pair<std::unique_ptr<void, void(*)(void*)>, const char*> & p)
177 {
178 auto d =p.first.get_deleter();
179 return sqlite3_bind_pointer(stmt, col, p.first.release(), p.second, d);
180 }
181#endif
182 };
183
184 mutable // so we can use it with
185 variant2::variant<variant2::monostate, int, sqlite3_int64,
186 blob_view, string_view, double, zero_blob
187#if SQLITE_VERSION_NUMBER >= 3020000
188 , std::pair<std::unique_ptr<void, void(*)(void*)>, const char*>
189#endif
190 > impl_;
191};
192
193
199{
201
215 template <typename ArgRange = std::initializer_list<param_ref>>
217 ArgRange && params,
218 system::error_code& ec,
219 error_info& info) &&
220 {
221 bind_impl(std::forward<ArgRange>(params), ec, info);
222 resultset rs;
223 rs.impl_.reset(impl_.release());
224 if (!ec)
225 rs.read_next(ec, info);
226 return rs;
227 }
228
229 template <typename ArgRange = std::initializer_list<param_ref>>
230 resultset execute(ArgRange && params) &&
231 {
232 system::error_code ec;
233 error_info ei;
234 auto tmp = std::move(*this).execute(std::forward<ArgRange>(params), ec, ei);
235 if (ec)
236 detail::throw_error_code(ec, ei);
237 return tmp;
238 }
239
241 std::initializer_list<std::pair<string_view, param_ref>> params,
242 system::error_code& ec,
243 error_info& info) &&
244 {
245 bind_impl(std::move(params), ec, info);
246 resultset rs;
247 rs.impl_.reset(impl_.release());
248 if (!ec)
249 rs.read_next(ec, info);
250 return rs;
251 }
252
253 resultset execute(std::initializer_list<std::pair<string_view, param_ref>> params) &&
254 {
255 system::error_code ec;
256 error_info ei;
257 auto tmp = std::move(*this).execute(std::move(params), ec, ei);
258 if (ec)
259 detail::throw_error_code(ec, ei);
260 return tmp;
261 }
262
263 template<typename T, typename ArgRange = std::initializer_list<param_ref>>
264 static_resultset<T> execute(
265 ArgRange && params,
266 system::error_code & ec,
267 error_info & ei) &&
268 {
269 static_resultset<T> tmp = std::move(*this).execute(std::forward<ArgRange>(params), ec, ei);
270 if (ec)
271 return {};
272 tmp.check_columns_(ec, ei);
273 if (ec)
274 return {};
275
276 return tmp;
277 }
278
279 template<typename T, typename ArgRange = std::initializer_list<param_ref>>
280 static_resultset<T> execute(ArgRange && params) &&
281 {
282 system::error_code ec;
283 error_info ei;
284 auto tmp = std::move(*this).execute<T>(std::forward<ArgRange>(params), ec, ei);
285 if (ec)
286 throw_exception(system::system_error(ec, ei.message()));
287 return tmp;
288 }
289
290 template<typename T>
291 static_resultset<T> execute(
292 std::initializer_list<std::pair<string_view, param_ref>> params,
293 system::error_code & ec,
294 error_info & ei) &&
295 {
296 static_resultset<T> tmp = std::move(*this).execute(std::move(params), ec, ei);
297 if (ec)
298 return {};
299 tmp.check_columns_(ec, ei);
300 if (ec)
301 return {};
302
303 return tmp;
304 }
305
306 template<typename T>
307 static_resultset<T> execute(std::initializer_list<std::pair<string_view, param_ref>> params) &&
308 {
309 system::error_code ec;
310 error_info ei;
311 auto tmp = std::move(*this).execute<T>(std::move(params), ec, ei);
312 if (ec)
313 throw_exception(system::system_error(ec, ei.message()));
314 return tmp;
315 }
316
318
320
338 template <typename ArgRange = std::initializer_list<param_ref>>
340 ArgRange && params,
341 system::error_code& ec,
342 error_info& info) &
343 {
344 bind_impl(std::forward<ArgRange>(params), ec, info);
345 resultset rs;
346 rs.impl_.get_deleter().delete_ = false;
347 rs.impl_.reset(impl_.get());
348 if (!ec)
349 rs.read_next(ec, info);
350 return rs;
351 }
352
353
354 template <typename ArgRange = std::initializer_list<param_ref>>
355 resultset execute(ArgRange && params) &
356 {
357 system::error_code ec;
358 error_info ei;
359 auto tmp = execute(std::forward<ArgRange>(params), ec, ei);
360 if (ec)
361 detail::throw_error_code(ec, ei);
362 return tmp;
363 }
364
365
367 std::initializer_list<std::pair<string_view, param_ref>> params,
368 system::error_code& ec,
369 error_info& info) &
370 {
371 bind_impl(std::move(params), ec, info);
372 resultset rs;
373 rs.impl_.get_deleter().delete_ = false;
374 rs.impl_.reset(impl_.get());
375 if (!ec)
376 rs.read_next(ec, info);
377 return rs;
378 }
379
380 resultset execute(std::initializer_list<std::pair<string_view, param_ref>> params) &
381 {
382 system::error_code ec;
383 error_info ei;
384 auto tmp = execute(std::move(params), ec, ei);
385 if (ec)
386 detail::throw_error_code(ec, ei);
387 return tmp;
388 }
389
390 template<typename T, typename ArgRange = std::initializer_list<param_ref>>
391 static_resultset<T> execute(
392 ArgRange && params,
393 system::error_code & ec,
394 error_info & ei) &
395 {
396 static_resultset<T> tmp = execute(std::forward<ArgRange>(params), ec, ei);
397 if (ec)
398 return {};
399 tmp.check_columns_(ec, ei);
400 if (ec)
401 return {};
402
403 return tmp;
404 }
405
406 template<typename T, typename ArgRange = std::initializer_list<param_ref>>
407 static_resultset<T> execute(ArgRange && params) &
408 {
409 system::error_code ec;
410 error_info ei;
411 auto tmp = execute<T>(std::forward<ArgRange>(params), ec, ei);
412 if (ec)
413 throw_exception(system::system_error(ec, ei.message()));
414 return tmp;
415 }
416
417 template<typename T>
418 static_resultset<T> execute(
419 std::initializer_list<std::pair<string_view, param_ref>> params,
420 system::error_code & ec,
421 error_info & ei) &
422 {
423 static_resultset<T> tmp = execute(std::move(params), ec, ei);
424 if (ec)
425 return {};
426 tmp.check_columns_(ec, ei);
427 if (ec)
428 return {};
429
430 return tmp;
431 }
432
433 template<typename T>
434 static_resultset<T> execute(std::initializer_list<std::pair<string_view, param_ref>> params) &
435 {
436 system::error_code ec;
437 error_info ei;
438 auto tmp = execute<T>(std::move(params), ec, ei);
439 if (ec)
440 throw_exception(system::system_error(ec, ei.message()));
441 return tmp;
442 }
443
445
446
448 core::string_view sql()
449 {
450 return sqlite3_sql(impl_.get());
451 }
452
453#if SQLITE_VERSION_NUMBER >= 3014000
455 core::string_view expanded_sql()
456 {
457 return sqlite3_expanded_sql(impl_.get());
458 }
459#endif
460
462#ifdef SQLITE_ENABLE_NORMALIZE
463 core::string_view normalized_sql()
464 {
465 return sqlite3_normalized_sql(impl_.get());
466 }
467#endif
468
470 core::string_view declared_type(int id) const
471 {
472 return sqlite3_column_decltype(impl_.get(), id);
473 }
474
475 private:
476
477 template<typename ... Args>
478 void bind_impl(std::tuple<Args...> && vec,
479 system::error_code & ec,
480 error_info & ei)
481 {
482 const auto sz = sqlite3_bind_parameter_count(impl_.get());
483 if (sizeof...(Args) < static_cast<std::size_t>(sz))
484 {
485 BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_ERROR);
486 ei.format("To few parameters provided. Needed %ld got %ld",
487 sz, sizeof...(Args));
488 return;
489 }
490
491 int i = 1, ar = SQLITE_OK;
492 mp11::tuple_for_each(std::move(vec),
493 [&](param_ref pr)
494 {
495 if (ar == SQLITE_OK)
496 ar = pr.apply(impl_.get(), i++);
497 });
498 if (ar != SQLITE_OK)
499 {
500 BOOST_SQLITE_ASSIGN_EC(ec, ar);
501 ei.set_message(sqlite3_errmsg(sqlite3_db_handle(impl_.get())));
502 return;
503 }
504 }
505
506
507 template<typename ... Args>
508 void bind_impl(const std::tuple<Args...> & vec,
509 system::error_code & ec,
510 error_info & ei)
511 {
512 const auto sz = sqlite3_bind_parameter_count(impl_.get());
513 if (static_cast<int>(sizeof...(Args)) < sz)
514 {
515 BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_ERROR);
516 ei.format("To few parameters provided. Needed %ld got %ld",
517 sz, sizeof...(Args));
518 return;
519 }
520
521 int i = 1, ar = SQLITE_OK;
522 mp11::tuple_for_each(std::move(vec),
523 [&](param_ref pr)
524 {
525 if (ar == SQLITE_OK)
526 ar = pr.apply(impl_.get(), i++);
527 });
528 if (ar != SQLITE_OK)
529 {
530 BOOST_SQLITE_ASSIGN_EC(ec, ar);
531 ei.set_message(sqlite3_errmsg(sqlite3_db_handle(impl_.get())));
532 return;
533 }
534 }
535
536 template<typename ParamVector>
537 void bind_impl(ParamVector && vec, system::error_code & ec, error_info & ei,
538 typename std::enable_if<std::is_convertible<
539 typename std::decay<ParamVector>::type::value_type, param_ref>::value>::type * = nullptr)
540 {
541 const auto sz = sqlite3_bind_parameter_count(impl_.get());
542 if (vec.size() < static_cast<std::size_t>(sz))
543 {
544 BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_ERROR);
545 ei.format("To few parameters provided. Needed %ld got %ld",
546 sz, vec.size());
547 }
548 int i = 1;
549 for (const param_ref & pr : std::forward<ParamVector>(vec))
550 {
551 int ar = pr.apply(impl_.get(), i++);
552 if (ar != SQLITE_OK)
553 {
554
555 BOOST_SQLITE_ASSIGN_EC(ec, ar);
556 ei.set_message(sqlite3_errmsg(sqlite3_db_handle(impl_.get())));
557 return;
558 }
559 }
560 }
561
562 template<typename ParamMap>
563 void bind_impl(ParamMap && vec, system::error_code & ec, error_info & ei,
564 typename std::enable_if<
565 std::is_convertible<typename std::decay<ParamMap>::type::key_type, string_view>::value &&
566 std::is_convertible<typename std::decay<ParamMap>::type::mapped_type, param_ref>::value
567 >::type * = nullptr)
568 {
569 for (auto i = 1; i <= sqlite3_bind_parameter_count(impl_.get()); i ++)
570 {
571 auto c = sqlite3_bind_parameter_name(impl_.get(), i);
572 if (c == nullptr)
573 {
574 BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISUSE);
575 ei.set_message("Parameter maps require all parameters to be named.");
576 return ;
577 }
578 auto itr = vec.find(c+1);
579 if (itr == vec.end())
580 {
581 BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISUSE);
582 ei.format("Can't find value for key '%s'", c+1);
583 return ;
584 }
585 int ar = SQLITE_OK;
586 if (std::is_rvalue_reference<ParamMap&&>::value)
587 ar = param_ref(std::move(itr->second)).apply(impl_.get(), i);
588 else
589 ar = param_ref(itr->second).apply(impl_.get(), i);
590
591 if (ar != SQLITE_OK)
592 {
593
594 BOOST_SQLITE_ASSIGN_EC(ec, ar);
595 ei.set_message(sqlite3_errmsg(sqlite3_db_handle(impl_.get())));
596 return;
597 }
598 }
599 }
600
601 void bind_impl(std::initializer_list<std::pair<string_view, param_ref>> params,
602 system::error_code & ec, error_info & ei)
603 {
604 for (auto i = 1; i <= sqlite3_bind_parameter_count(impl_.get()); i ++)
605 {
606 auto c = sqlite3_bind_parameter_name(impl_.get(), i);
607 if (c == nullptr)
608 {
609 BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISUSE);
610 ei.set_message("Parameter maps require all parameters to be named.");
611 return ;
612 }
613
614 auto itr = std::find_if(params.begin(), params.end(),
615 [&](const std::pair<string_view, param_ref> & p)
616 {
617 return p.first == (c+1);
618 });
619 if (itr == params.end())
620 {
621 BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_MISUSE);
622 ei.format("Can't find value for key '%s'", c+1);
623 return ;
624 }
625 auto ar = itr->second.apply(impl_.get(), i);
626 if (ar != SQLITE_OK)
627 {
628
629 BOOST_SQLITE_ASSIGN_EC(ec, ar);
630 ei.set_message(sqlite3_errmsg(sqlite3_db_handle(impl_.get())));
631 return;
632 }
633 }
634 }
635
636
637 friend
638 struct connection;
639 struct deleter_
640 {
641 void operator()(sqlite3_stmt * sm)
642 {
643 sqlite3_finalize(sm);
644 }
645 };
646 std::unique_ptr<sqlite3_stmt, deleter_> impl_;
647};
648
649BOOST_SQLITE_END_NAMESPACE
650
651#endif //BOOST_SQLITE_STATEMENT_HPP
zero_blob
Helper type to pass a blob full of zeroes without allocating extra memory.
Definition blob.hpp:44
@ text
A textual value.
@ blob
A binary value.
a view to a binary large object
Definition blob.hpp:21
An object that owns a binary large object.
Definition blob.hpp:48
Additional information about error conditions stored in an sqlite-allocate string.
Definition error.hpp:33
void set_message(core::string_view msg)
set the message by copy
Definition error.hpp:44
A reference to a value to temporary bind for an execute statement. Most values are captures by refere...
Definition statement.hpp:28
param_ref(zero_blob zb)
Bind a zero_blob value, i.e. a blob that initialized by zero.
Definition statement.hpp:66
param_ref(std::unique_ptr< T > ptr)
Bind pointer value to the parameter.
Definition statement.hpp:71
param_ref(T &&t, decltype(variant2::visit(make_visitor(), std::forward< T >(t))) *=nullptr)
Construct param_ref from a variant.
param_ref(std::nullptr_t)
Bind null.
Definition statement.hpp:34
param_ref(double value)
Bind a floating point value.
Definition statement.hpp:64
param_ref(blob_view blob)
Bind a blob.
Definition statement.hpp:47
int apply(sqlite3_stmt *stmt, int c) const
Apply the param_ref to a statement.
param_ref(string_view text)
Bind a string.
Definition statement.hpp:49
param_ref()=default
Default construct a parameter, gives null.
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)
Bind pointer value with a function custom deleter to the parameter. The deleter needs to be default c...
Definition statement.hpp:94
param_ref(std::unique_ptr< T, void(*)(T *)> ptr)
Bind pointer value with a function as deleter to the parameter.
Definition statement.hpp:82
param_ref(I value)
Bind an integer.
Definition statement.hpp:38
param_ref(variant2::monostate)
Bind null.
Definition statement.hpp:32
Representation of a result from a database.
Definition resultset.hpp:41
A statement used for a prepared-statement.
resultset execute(ArgRange &&params, system::error_code &ec, error_info &info) &&
execute the prepared statement once.
static_resultset< T > execute(std::initializer_list< std::pair< string_view, param_ref > > params) &&
execute the prepared statement once.
core::string_view sql()
Returns the sql used to construct the prepared statement.
resultset execute(ArgRange &&params) &
execute the prepared statement and reset it afterwards.
resultset execute(ArgRange &&params) &&
execute the prepared statement once.
static_resultset< T > execute(ArgRange &&params, system::error_code &ec, error_info &ei) &
execute the prepared statement and reset it afterwards.
static_resultset< T > execute(std::initializer_list< std::pair< string_view, param_ref > > params, system::error_code &ec, error_info &ei) &
execute the prepared statement and reset it afterwards.
resultset execute(std::initializer_list< std::pair< string_view, param_ref > > params, system::error_code &ec, error_info &info) &
execute the prepared statement and reset it afterwards.
resultset execute(std::initializer_list< std::pair< string_view, param_ref > > params, system::error_code &ec, error_info &info) &&
execute the prepared statement once.
static_resultset< T > execute(ArgRange &&params, system::error_code &ec, error_info &ei) &&
execute the prepared statement once.
static_resultset< T > execute(std::initializer_list< std::pair< string_view, param_ref > > params) &
execute the prepared statement and reset it afterwards.
resultset execute(std::initializer_list< std::pair< string_view, param_ref > > params) &&
execute the prepared statement once.
core::string_view expanded_sql()
Returns the expanded sql used to construct the prepared statement.
core::string_view declared_type(int id) const
Returns the expanded sql used to construct the prepared statement.
static_resultset< T > execute(ArgRange &&params) &&
execute the prepared statement once.
resultset execute(std::initializer_list< std::pair< string_view, param_ref > > params) &
execute the prepared statement and reset it afterwards.
static_resultset< T > execute(ArgRange &&params) &
execute the prepared statement and reset it afterwards.
static_resultset< T > execute(std::initializer_list< std::pair< string_view, param_ref > > params, system::error_code &ec, error_info &ei) &&
execute the prepared statement once.
resultset execute(ArgRange &&params, system::error_code &ec, error_info &info) &
execute the prepared statement and reset it afterwards.
A holder for a sqlite values used for internal APIs.
Definition value.hpp:39