1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
//
// 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)
//
#ifndef BOOST_SQLITE_COLLATION_HPP
#define BOOST_SQLITE_COLLATION_HPP
#include <boost/sqlite/connection.hpp>
#include <boost/sqlite/detail/exception.hpp>
BOOST_SQLITE_BEGIN_NAMESPACE
///@{
/** Define a custom collation function
@ingroup reference
@param conn A connection to the database in which to install the collation.
@param name The name of the collation.
@param func The function
The function must be callable with two `string_view` and return an int, indicating the comparison results.
@par Example
@code{.cpp}
// a case insensitive string omparison, e.g. from boost.urls
int ci_compare(string_view s0, string_view s1) noexcept;
extern sqlite::connection conn;
// Register the collation
sqlite::create_collation(conn, "iequal", &ci_compare);
// use the collation to get by name, case insensitively
conn.query("select first_name, last_name from people where first_name = 'Klemens' collate iequal;");
// order by names case insensitively
conn.query("select * from people order by last_name collate iequal asc;");
@endcode
*/
template<typename Func>
void create_collation(
connection & conn,
cstring_ref name,
Func && func,
typename std::enable_if<
std::is_convertible<
decltype(func(string_view(), string_view())),
int>::value,
system::error_code>::type & ec)
{
using func_type = typename std::decay<Func>::type;
unique_ptr<func_type> f{new (memory_tag{}) func_type(std::forward<Func>(func))};
if (f == nullptr)
{
BOOST_SQLITE_ASSIGN_EC(ec, SQLITE_NOMEM);
return;
}
auto res = sqlite3_create_collation_v2(
conn.handle(),
name.c_str(),
SQLITE_UTF8,
f.get(),
+[](void * data, int len_l, const void * str_l, int len_r, const void * str_r) -> int
{
string_view l(static_cast<const char*>(str_l), len_l);
string_view r(static_cast<const char*>(str_r), len_r);
auto & impl = (*static_cast<func_type*>(data));
static_assert(noexcept(impl(l, r)),
"Collation function must be noexcept");
return impl(l, r);
},
+[](void * p) { delete_(static_cast<func_type*>(p)); }
);
if (res != SQLITE_OK)
BOOST_SQLITE_ASSIGN_EC(ec, res);
else
f.release();
}
template<typename Func>
auto create_collation(
connection & conn,
cstring_ref name,
Func && func)
#if !defined(BOOST_SQLITE_GENERATING_DOCS)
-> typename std::enable_if<
std::is_convertible<
decltype(func(string_view(), string_view())),
int>::value>::type
#endif
{
system::error_code ec;
create_collation(conn, name, std::forward<Func>(func), ec);
if (ec)
detail::throw_error_code(ec, BOOST_CURRENT_LOCATION);
}
inline void delete_collation(
connection & conn,
cstring_ref name,
system::error_code & ec)
{
auto res = sqlite3_create_collation_v2(
conn.handle(),
name.c_str(),
SQLITE_UTF8,
nullptr, nullptr, nullptr);
if (res != SQLITE_OK)
{
BOOST_SQLITE_ASSIGN_EC(ec, res);
}
}
inline auto delete_collation(
connection & conn,
cstring_ref name)
{
system::error_code ec;
delete_collation(conn, name, ec);
if (ec)
detail::throw_error_code(ec, BOOST_CURRENT_LOCATION);
}
/// @}
BOOST_SQLITE_END_NAMESPACE
#endif //BOOST_SQLITE_COLLATION_HPP
|