boost_sqlite 1
A sqlite C++ library
Loading...
Searching...
No Matches
vtable.hpp
1// Copyright (c) 2023 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_DETAIL_VTABLE_HPP
6#define BOOST_SQLITE_DETAIL_VTABLE_HPP
7
8#include <boost/sqlite/detail/config.hpp>
9#include <boost/sqlite/detail/catch.hpp>
10#include <boost/sqlite/vtable.hpp>
11
12BOOST_SQLITE_BEGIN_NAMESPACE
13namespace detail
14{
15struct vtab_impl
16{
17
18template<typename Module>
19static int connect(sqlite3 * db, void * pAux, int argc, const char * const * argv,
20 sqlite3_vtab **ppVTab, char** errMsg)
21{
22 using table_type = typename Module::table_type;
23 auto &impl = *static_cast<Module*>(pAux);
24 BOOST_SQLITE_TRY
25 {
26 result<table_type> rtab = impl.connect(
27 sqlite::connection(db, false),
28 argc, argv);
29
30 if (rtab.has_error())
31 return extract_error(*errMsg, rtab);
32
33 auto tab = make_unique<table_type>(std::move(*rtab));
34 tab->db_ = db;
35 auto code = sqlite3_declare_vtab(db, tab->declaration());
36 if (code != SQLITE_OK)
37 return code;
38
39 sqlite::vtab::module_config cfg{db};
40 auto r = tab->config(cfg);
41 if (r.has_error())
42 return extract_error(*errMsg, r);
43
44 *ppVTab = tab.release();
45
46 return SQLITE_OK;
47 }
48 BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(*errMsg)
49}
50
51
52template<typename Module>
53static int create(sqlite3 * db, void * pAux, int argc, const char * const * argv,
54 sqlite3_vtab **ppVTab, char** errMsg)
55{
56 using table_type = typename Module::table_type;
57 auto &impl = *static_cast<Module*>(pAux);
58 BOOST_SQLITE_TRY
59 {
60 result<table_type> rtab = impl.create(
61 sqlite::connection(db, false),
62 argc, argv);
63
64 if (rtab.has_error())
65 return extract_error(*errMsg, rtab);
66
67 auto tab = make_unique<table_type>(std::move(*rtab));
68 tab->db_ = db;
69
70 auto code = sqlite3_declare_vtab(db, tab->declaration());
71 if (code != SQLITE_OK)
72 return code;
73
74 sqlite::vtab::module_config mc{db};
75 auto r = tab->config(mc);
76 if (r.has_error())
77 return extract_error(*errMsg, r);
78
79 *ppVTab = tab.release();
80
81 return SQLITE_OK;
82 }
83 BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(*errMsg)
84}
85
86template<typename Table>
87static int disconnect(sqlite3_vtab * tab)
88{
89 BOOST_SQLITE_TRY
90 {
91 auto tb = static_cast<Table*>(tab);
92 tb->~Table();
93 sqlite3_free(tb);
94 return SQLITE_OK;
95 }
96 BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(tab->zErrMsg);
97}
98
99template<typename Table>
100static int destroy(sqlite3_vtab * tab)
101{
102 BOOST_SQLITE_TRY
103 {
104 auto tb = static_cast<Table*>(tab);
105 auto res = tb->destroy();
106 tb->~Table();
107 sqlite3_free(tb);
108 if (res.has_error())
109 return std::move(res).error().code;
110 return SQLITE_OK;
111 }
112 BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(tab->zErrMsg);
113}
114
115template<typename Module, typename Table>
116static void assign_create(sqlite3_module & md, const Module & mod,
117 const sqlite::vtab::eponymous_module<Table> & base)
118{
119 md.xConnect = md.xCreate = &connect<Module>;
120 md.xDisconnect = md.xDestroy = &disconnect<Table>;
121 if (base.eponymous_only())
122 md.xCreate = nullptr;
123}
124
125template<typename Module, typename Table>
126static void assign_create(sqlite3_module & md, const Module & mod,
127 const sqlite::vtab::module<Table> & base)
128{
129 md.xConnect = &connect<Module>;
130 md.xDisconnect = &disconnect<Table>;
131 md.xCreate = &create<Module>;
132 md.xDestroy = &destroy<Table>;
133}
134
135template<typename Table>
136static int open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor)
137{
138 auto tab = static_cast<Table *>(pVTab);
139
140 BOOST_SQLITE_TRY
141 {
142 auto res = tab->open();
143 if (res.has_error())
144 return extract_error(pVTab->zErrMsg, res);
145 *ppCursor = new (memory_tag{}) typename Table::cursor_type(std::move(*res));
146 return SQLITE_OK;
147 }
148 BOOST_SQLITE_CATCH_AND_RETURN();
149}
150
151template<typename Cursor>
152static int close(sqlite3_vtab_cursor * cursor)
153{
154 auto p = static_cast<Cursor *>(cursor);
155
156 BOOST_SQLITE_TRY
157 {
158 p->~Cursor();
159 }
160 BOOST_SQLITE_CATCH_AND_RETURN();
161
162 sqlite3_free(p);
163 return SQLITE_OK;
164
165}
166
167template<typename Table>
168static int best_index(sqlite3_vtab *pVTab, sqlite3_index_info* info)
169{
170 BOOST_SQLITE_TRY
171 {
172 auto tb = static_cast<Table*>(pVTab);
173
174 sqlite::vtab::index_info ii(tb->db_, info);
175 auto r = tb->best_index(ii);
176 if (r.has_error())
177 return extract_error(pVTab->zErrMsg, r);
178
179 return SQLITE_OK;
180 }
181 BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg);
182}
183
184
185template<typename Cursor>
186static int filter(sqlite3_vtab_cursor* pCursor,
187 int idxNum, const char *idxStr,
188 int argc, sqlite3_value **argv)
189{
190 BOOST_SQLITE_TRY
191 {
192 auto cr = static_cast<Cursor*>(pCursor);
193
194 auto r = cr->filter(idxNum, idxStr,
195 boost::span<value>{reinterpret_cast<value*>(argv),
196 static_cast<std::size_t>(argc)});
197 if (r.has_error())
198 return extract_error(pCursor->pVtab->zErrMsg, r);
199
200 return SQLITE_OK;
201 }
202 BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pCursor->pVtab->zErrMsg);
203}
204
205
206template<typename Cursor>
207static int next(sqlite3_vtab_cursor* pCursor)
208{
209 BOOST_SQLITE_TRY
210 {
211 auto cr = static_cast<Cursor*>(pCursor);
212
213 auto r = cr->next();
214 if (r.has_error())
215 return extract_error(pCursor->pVtab->zErrMsg, r);
216
217 return SQLITE_OK;
218 }
219 BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pCursor->pVtab->zErrMsg);
220}
221
222
223template<typename Cursor>
224static int eof(sqlite3_vtab_cursor* pCursor)
225{
226 return static_cast<Cursor*>(pCursor)->eof() ? 1 : 0;
227}
228
229template<typename Cursor>
230static auto column(sqlite3_vtab_cursor* pCursor,
231 sqlite3_context * ctx, int idx)
232 -> typename std::enable_if<!std::is_void<typename Cursor::column_type>::value, int>::type
233{
234#if SQLITE_VERSION_NUMBER >= 3032000
235 bool no_change = sqlite3_vtab_nochange(ctx) != 0;
236#else
237 bool no_change = false;
238#endif
239 auto cr = static_cast<Cursor*>(pCursor);
240 execute_context_function(
241 ctx,
242 [&]{
243 return cr->column(idx, no_change);
244 });
245
246 return SQLITE_OK;
247}
248
249
250template<typename Cursor>
251static auto column(sqlite3_vtab_cursor* pCursor,
252 sqlite3_context * ctx, int idx)
253 -> typename std::enable_if<std::is_void<typename Cursor::column_type>::value, int>::type
254{
255#if SQLITE_VERSION_NUMBER >= 3032000
256 bool no_change = sqlite3_vtab_nochange(ctx) != 0;
257#else
258 bool no_change = false;
259#endif
260 auto cr = static_cast<Cursor*>(pCursor);
261 BOOST_SQLITE_TRY
262 {
263 cr->column(context<>{ctx}, idx, no_change);
264 }
265 BOOST_SQLITE_CATCH_RESULT(ctx);
266 return SQLITE_OK;
267}
268
269template<typename Cursor>
270static int row_id(sqlite3_vtab_cursor* pCursor, sqlite3_int64 *pRowid)
271{
272 BOOST_SQLITE_TRY
273 {
274 auto cr = static_cast<Cursor*>(pCursor);
275
276 auto r = cr->row_id();
277 if (r.has_error())
278 return extract_error(pCursor->pVtab->zErrMsg, r);
279 *pRowid = *r;
280 return SQLITE_OK;
281 }
282 BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pCursor->pVtab->zErrMsg);
283}
284
285template<typename Table>
286static int update(sqlite3_vtab * pVTab, int argc, sqlite3_value ** argv, sqlite3_int64 * pRowid)
287{
288 using table_type = Table;
289 BOOST_SQLITE_TRY
290 {
291 auto & mod = *static_cast<table_type *>(pVTab);
292 auto db = mod.db_;
293 if (argc == 1 && sqlite3_value_type(argv[0]) != SQLITE_NULL)
294 {
295 auto r = mod.delete_(sqlite::value(*argv));
296 if (r.has_error())
297 return extract_error(pVTab->zErrMsg, r);
298 }
299 else if (argc > 1 && sqlite3_value_type(argv[0]) == SQLITE_NULL)
300 {
301 auto r = mod.insert(value{argv[1]},
302 boost::span<value>{reinterpret_cast<value *>(argv + 2),
303 static_cast<std::size_t>(argc - 2)},
304 sqlite3_vtab_on_conflict(db));
305 if (r.has_error())
306 return extract_error(pVTab->zErrMsg, r);
307 *pRowid = r.value();
308 }
309 else if (argc > 1 && sqlite3_value_type(argv[0]) != SQLITE_NULL)
310 {
311 auto r = mod.update(sqlite::value(*argv), value{argv[1]}, // ID
312 boost::span<value>{reinterpret_cast<value *>(argv + 2),
313 static_cast<std::size_t>(argc - 2)},
314 sqlite3_vtab_on_conflict(db));
315
316 if (r.has_error())
317 return extract_error(pVTab->zErrMsg, r);
318 *pRowid = r.value();
319 }
320 else
321 {
322 pVTab->zErrMsg = sqlite3_mprintf("Misuse of update api");
323 return SQLITE_MISUSE;
324 }
325
326 }
327 BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg)
328 return SQLITE_OK;
329}
330
331template<typename Module>
332static void assign_update(sqlite3_module & md, const Module & mod,
333 std::true_type /* modifiable */)
334{
335 md.xUpdate = &update<typename Module::table_type>;
336}
337
338template<typename Module>
339static void assign_update(sqlite3_module & md, const Module & mod,
340 std::false_type /* modifiable */)
341{
342}
343
344template<typename Table>
345static int begin(sqlite3_vtab* pVTab)
346{
347 BOOST_SQLITE_TRY
348 {
349 auto cr = static_cast<Table*>(pVTab);
350
351 auto r = cr->begin();
352 if (r.has_error())
353 return extract_error(pVTab->zErrMsg, r);
354
355 return SQLITE_OK;
356 }
357 BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg);
358}
359
360template<typename Table>
361static int sync(sqlite3_vtab* pVTab)
362{
363 BOOST_SQLITE_TRY
364 {
365 auto cr = static_cast<Table*>(pVTab);
366
367 auto r = cr->sync();
368 if (r.has_error())
369 return extract_error(pVTab->zErrMsg, r);
370
371 return SQLITE_OK;
372 }
373 BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg);
374}
375
376template<typename Table>
377static int commit(sqlite3_vtab* pVTab)
378{
379 BOOST_SQLITE_TRY
380 {
381 auto cr = static_cast<Table*>(pVTab);
382
383 auto r = cr->commit();
384 if (r.has_error())
385 return extract_error(pVTab->zErrMsg, r);
386
387 return SQLITE_OK;
388 }
389 BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg);
390}
391
392template<typename Table>
393static int rollback(sqlite3_vtab* pVTab)
394{
395 BOOST_SQLITE_TRY
396 {
397 auto cr = static_cast<Table*>(pVTab);
398
399 auto r = cr->rollback();
400 if (r.has_error())
401 return extract_error(pVTab->zErrMsg, r);
402
403 return SQLITE_OK;
404 }
405 BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg);
406}
407
408template<typename Module>
409static void assign_transaction(sqlite3_module & md, const Module & mod,
410 std::false_type /* modifiable */)
411{
412}
413
414template<typename Module>
415static void assign_transaction(sqlite3_module & md, const Module & mod,
416 std::true_type /* modifiable */)
417{
418 md.xBegin = &begin <typename Module::table_type>;
419 md.xSync = &sync <typename Module::table_type>;
420 md.xCommit = &commit <typename Module::table_type>;
421 md.xRollback = &rollback<typename Module::table_type>;
422}
423
424template<typename Table>
425static int find_function(sqlite3_vtab *pVtab, int nArg, const char *zName,
426 void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
427 void **ppArg)
428{
429 BOOST_SQLITE_TRY
430 {
431 auto cr = static_cast<Table*>(pVtab);
432
433 auto r = cr->find_function(
434 nArg, zName, sqlite::vtab::function_setter(pxFunc, ppArg));
435 if (r.has_error())
436 return extract_error(pVtab->zErrMsg, r);
437
438 return SQLITE_OK;
439 }
440 BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVtab->zErrMsg);
441}
442
443template<typename Module>
444static void assign_find_function(sqlite3_module & md, const Module & mod,
445 std::false_type /* overloadable */)
446{
447}
448
449template<typename Module>
450static void assign_find_function(sqlite3_module & md, const Module & mod,
451 std::true_type /* overloadable */)
452{
453 md.xFindFunction = &find_function<typename Module::table_type>;
454}
455
456template<typename Table>
457static int rename(sqlite3_vtab* pVTab, const char * name)
458{
459 BOOST_SQLITE_TRY
460 {
461 auto cr = static_cast<Table*>(pVTab);
462
463 auto r = cr->rename(name);
464 if (r.has_error())
465 return extract_error(pVTab->zErrMsg, r);
466
467 return SQLITE_OK;
468 }
469 BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg);
470}
471
472template<typename Module>
473static void assign_rename(sqlite3_module & md, const Module & mod,
474 std::false_type /* renamable */)
475{
476}
477
478template<typename Module>
479static void assign_rename(sqlite3_module & md, const Module & mod,
480 std::true_type /* renamable */)
481{
482 md.xRename = &rename<typename Module::table_type>;
483}
484#if SQLITE_VERSION_NUMBER >= 3007007
485
486template<typename Table>
487static int savepoint(sqlite3_vtab* pVTab, int i)
488{
489 BOOST_SQLITE_TRY
490 {
491 auto cr = static_cast<Table*>(pVTab);
492
493 auto r = cr->savepoint(i);
494 if (r.has_error())
495 return extract_error(pVTab->zErrMsg, r);
496
497 return SQLITE_OK;
498 }
499 BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg);
500}
501
502template<typename Table>
503static int release(sqlite3_vtab* pVTab, int i)
504{
505 BOOST_SQLITE_TRY
506 {
507 auto cr = static_cast<Table*>(pVTab);
508
509 auto r = cr->release(i);
510 if (r.has_error())
511 return extract_error(pVTab->zErrMsg, r);
512
513 return SQLITE_OK;
514 }
515 BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg);
516}
517
518template<typename Table>
519static int rollback_to(sqlite3_vtab* pVTab, int i)
520{
521 BOOST_SQLITE_TRY
522 {
523 auto cr = static_cast<Table*>(pVTab);
524
525 auto r = cr->rollback_to(i);
526 if (r.has_error())
527 return extract_error(pVTab->zErrMsg, r);
528
529 return SQLITE_OK;
530 }
531 BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg);
532}
533
534template<typename Module>
535static void assign_recursive_transaction(sqlite3_module & md, const Module & mod,
536 std::false_type /* recursive_transaction */)
537{
538}
539
540template<typename Module>
541static void assign_recursive_transaction(sqlite3_module & md, const Module & mod,
542 std::true_type /* recursive_transaction */)
543{
544 md.xSavepoint = &savepoint <typename Module::table_type>;
545 md.xRelease = &release <typename Module::table_type>;
546 md.xRollbackTo = &rollback_to<typename Module::table_type>;
547}
548
549#endif
550
551#if SQLITE_VERSION_NUMBER >= 3026000
552
553template<typename Table>
554static void assign_shadow_name(sqlite3_module & md, const sqlite::vtab::module<Table> &) {}
555
556template<typename Table>
557static void assign_shadow_name(sqlite3_module & md, const sqlite::vtab::eponymous_module<Table> &) {}
558
559template<typename Module,
560 bool (*Func)(const char *) = &Module::shadow_name>
561static void assign_shadow_name(sqlite3_module & md, const Module & mod)
562{
563 md.xShadowName = +[](const char * name){return Func(name) != 0;};
564}
565
566#endif
567
568};
569
570template<typename Module>
571const sqlite3_module make_module(const Module & mod)
572{
573 sqlite3_module md;
574 std::memset(&md, 0, sizeof(sqlite3_module));
575
576#if SQLITE_VERSION_NUMBER < 3007007
577 md.iVersion = 1;
578#elif SQLITE_VERSION_NUMBER < 3026000
579 md.iVersion = 2;
580#else
581 md.iVersion = 3;
582#endif
583 using table_type = typename Module::table_type;
584 using cursor_type = typename table_type::cursor_type;
585 vtab_impl::assign_create(md, mod, mod);
586 md.xBestIndex = &vtab_impl::best_index<table_type>;
587 md.xOpen = &vtab_impl::open <table_type>;
588 md.xClose = &vtab_impl::close <cursor_type>;
589 md.xFilter = &vtab_impl::filter <cursor_type>;
590 md.xNext = &vtab_impl::next <cursor_type>;
591 md.xEof = &vtab_impl::eof <cursor_type>;
592 md.xColumn = &vtab_impl::column <cursor_type>;
593 md.xRowid = &vtab_impl::row_id <cursor_type>;
594 vtab_impl::assign_update (md, mod, std::is_base_of<sqlite::vtab::modifiable, table_type>{});
595 vtab_impl::assign_transaction (md, mod, std::is_base_of<sqlite::vtab::transaction, table_type>{});
596 vtab_impl::assign_find_function(md, mod, std::is_base_of<sqlite::vtab::overload_functions, table_type>{});
597 vtab_impl::assign_rename (md, mod, std::is_base_of<sqlite::vtab::renamable, table_type>{});
598#if SQLITE_VERSION_NUMBER >= 3007007
599 vtab_impl::assign_recursive_transaction(md, mod, std::is_base_of<sqlite::vtab::recursive_transaction, table_type>{});
600#endif
601#if SQLITE_VERSION_NUMBER >= 3026000
602 vtab_impl::assign_shadow_name(md, mod);
603#endif
604 return md;
605}
606
607}
608
609BOOST_SQLITE_END_NAMESPACE
610
611#endif //BOOST_SQLITE_DETAIL_VTABLE_HPP