Skip to content

Commit e92fe74

Browse files
committed
Labeled bindables and named parameters
1 parent f131446 commit e92fe74

17 files changed

+886
-108
lines changed

dev/ast/labeled_bindable.h

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#pragma once
2+
#ifndef SQLITE_ORM_IMPORT_STD_MODULE
3+
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
4+
#include <utility>
5+
#endif
6+
#endif
7+
8+
#include "../functional/cxx_type_traits_polyfill.h"
9+
#include "../functional/cstring_literal.h"
10+
#include "../statement_binding_traits.h"
11+
12+
namespace sqlite_orm::internal {
13+
template<class T>
14+
using access_bindable_t = polyfill::detected_or_t<T, type_t, T>;
15+
}
16+
17+
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
18+
namespace sqlite_orm::internal {
19+
template<class Moniker, class T>
20+
struct labeled_bindable {
21+
using name_constant_type = Moniker;
22+
using type = T;
23+
24+
type value;
25+
};
26+
27+
template<class Moniker>
28+
struct bindable_label : Moniker {
29+
using name_constant_type = Moniker;
30+
};
31+
32+
// note: not in use because AST iteration walks through to the value leaf
33+
template<class Moniker, class T>
34+
T& access_bindable(const labeled_bindable<Moniker, T>& t) = delete;
35+
36+
template<class T>
37+
using name_constant_type_t = typename T::name_constant_type;
38+
39+
template<class T>
40+
using name_constant_type_or_none_t = polyfill::detected_t<name_constant_type_t, T>;
41+
}
42+
43+
SQLITE_ORM_EXPORT namespace sqlite_orm {
44+
// Intentionally place operators for types classified as arithmetic or general operator arguments in the internal namespace
45+
// to facilitate ADL (Argument Dependent Lookup)
46+
namespace internal {
47+
/*
48+
Associates a bindable value with a moniker.
49+
*/
50+
template<class T, class Moniker>
51+
requires is_bindable_v<T>
52+
constexpr labeled_bindable<Moniker, T> operator>>=(T bindable, const bindable_label<Moniker>&) {
53+
return {std::move(bindable)};
54+
}
55+
56+
/*
57+
Associates a referenced bindable value with a moniker.
58+
*/
59+
template<class T, class Moniker>
60+
requires is_bindable_v<T>
61+
constexpr labeled_bindable<Moniker, T> operator>>=(std::reference_wrapper<T> bindable,
62+
const bindable_label<Moniker>&) {
63+
return {bindable};
64+
}
65+
}
66+
67+
inline namespace literals {
68+
/*
69+
* Make a label for a bindable from a string literal.
70+
* E.g. "myparam"_bindable
71+
*/
72+
template<internal::cstring_literal name>
73+
[[nodiscard]] consteval auto operator"" _bindable() {
74+
using name_constant_type = std::integral_constant<decltype(name), name>;
75+
return internal::bindable_label<name_constant_type>{};
76+
}
77+
}
78+
79+
/** @short Specifies that a type is an integral constant C-string usable as a label for a bindable.
80+
*/
81+
template<class T>
82+
concept orm_bindable_label = polyfill::is_specialization_of_v<T, internal::bindable_label>;
83+
}
84+
#endif

dev/ast/named_parameter.h

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#pragma once
2+
#pragma once
3+
#ifndef SQLITE_ORM_IMPORT_STD_MODULE
4+
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
5+
#include <algorithm>
6+
#include <utility>
7+
#include <memory>
8+
#endif
9+
#endif
10+
11+
#include "../functional/cxx_type_traits_polyfill.h"
12+
#include "../functional/cstring_literal.h"
13+
#include "../statement_binding_traits.h"
14+
15+
namespace sqlite_orm::internal {
16+
template<class T>
17+
using access_bindable_t = polyfill::detected_or_t<T, type_t, T>;
18+
19+
template<class T>
20+
T& access_bindable(T& t) {
21+
return t;
22+
}
23+
}
24+
25+
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
26+
namespace sqlite_orm::internal {
27+
/*
28+
* @note Named SQL parameters can be used multiple times in an SQL statement, which is the reason that we use shared_ptr here.
29+
*/
30+
template<class Moniker, class T>
31+
struct named_bindable {
32+
using name_constant_type = Moniker;
33+
using type = T;
34+
35+
std::shared_ptr<type> value;
36+
37+
template<class U = type>
38+
void operator=(U&& other) {
39+
*value = std::forward<U>(other);
40+
}
41+
};
42+
43+
template<class Moniker>
44+
struct parameter_moniker : Moniker {
45+
using name_constant_type = Moniker;
46+
47+
/*
48+
* Create a named SQL parameter argument of type T.
49+
*/
50+
template<class T, class... Args>
51+
[[nodiscard]] constexpr named_bindable<Moniker, T> create(Args&&... args) const {
52+
return {std::make_shared<T>(std::forward<Args>(args)...)};
53+
}
54+
};
55+
56+
template<class Moniker, class T>
57+
T& access_bindable(const named_bindable<Moniker, T>& bindable) {
58+
return *bindable.value;
59+
}
60+
61+
template<class T>
62+
using name_constant_type_t = typename T::name_constant_type;
63+
64+
template<class T>
65+
using name_constant_type_or_none_t = polyfill::detected_t<name_constant_type_t, T>;
66+
}
67+
68+
SQLITE_ORM_EXPORT namespace sqlite_orm {
69+
inline namespace literals {
70+
/*
71+
* Make a moniker for a named parameter from a string literal.
72+
* E.g. "myparam"_param
73+
*/
74+
template<internal::cstring_literal moniker>
75+
[[nodiscard]] consteval auto operator"" _param() {
76+
static_assert(moniker.cstr[0] == ':' || moniker.cstr[0] == '@');
77+
using name_constant_type = std::integral_constant<decltype(moniker), moniker>;
78+
return internal::parameter_moniker<name_constant_type>{};
79+
}
80+
}
81+
82+
/** @short Specifies that a type is an integral constant C-string usable for a named parameter.
83+
*/
84+
template<class T>
85+
concept orm_parameter_moniker = polyfill::is_specialization_of_v<T, internal::parameter_moniker>;
86+
}
87+
#endif

dev/ast_iterator.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,18 @@ namespace sqlite_orm {
514514
}
515515
};
516516

517+
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
518+
template<class T>
519+
struct ast_iterator<T, match_specialization_of<T, labeled_bindable>> {
520+
using node_type = T;
521+
522+
template<class L>
523+
SQLITE_ORM_STATIC_CALLOP void operator()(const node_type& param, L& lambda) SQLITE_ORM_OR_CONST_CALLOP {
524+
iterate_ast(param.value, lambda);
525+
}
526+
};
527+
#endif
528+
517529
template<class F, class... CallArgs>
518530
struct ast_iterator<function_call<F, CallArgs...>, void> {
519531
using node_type = function_call<F, CallArgs...>;

dev/column_result.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include "cte_types.h"
2626
#include "storage_traits.h"
2727
#include "function.h"
28+
#include "ast/labeled_bindable.h"
29+
#include "ast/named_parameter.h"
2830
#include "ast/special_keywords.h"
2931
#include "ast/cast.h"
3032

@@ -132,6 +134,18 @@ namespace sqlite_orm {
132134
using type = std::nullptr_t;
133135
};
134136

137+
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
138+
template<class DBOs, class T>
139+
struct column_result_t<DBOs, T, match_specialization_of<T, labeled_bindable>> {
140+
using type = typename T::type;
141+
};
142+
143+
template<class DBOs, class T>
144+
struct column_result_t<DBOs, T, match_specialization_of<T, named_bindable>> {
145+
using type = typename T::type;
146+
};
147+
#endif
148+
135149
template<class DBOs, class T>
136150
struct column_result_t<DBOs, count_asterisk_t<T>, void> {
137151
using type = int;

dev/get_prepared_statement.h

Lines changed: 85 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#endif
77

88
#include "functional/cxx_type_traits_polyfill.h"
9+
#include "ast/labeled_bindable.h"
10+
#include "ast/named_parameter.h"
911
#include "type_traits.h"
1012
#include "prepared_statement.h"
1113
#include "ast_iterator.h"
@@ -126,19 +128,22 @@ SQLITE_ORM_EXPORT namespace sqlite_orm {
126128

127129
template<int N, class T>
128130
const auto& get(const internal::prepared_statement_t<T>& statement) {
131+
using namespace ::sqlite_orm::internal;
129132
using statement_type = polyfill::remove_cvref_t<decltype(statement)>;
130-
using expression_type = internal::expression_type_t<statement_type>;
131-
using node_tuple = internal::node_tuple_t<expression_type>;
132-
using bind_tuple = internal::bindable_filter_t<node_tuple>;
133-
using result_type = std::tuple_element_t<static_cast<size_t>(N), bind_tuple>;
133+
using expression_type = expression_type_t<statement_type>;
134+
using node_tuple = node_tuple_t<expression_type>;
135+
using bind_tuple = bindable_filter_t<node_tuple>;
136+
using bound_type = std::tuple_element_t<static_cast<size_t>(N), bind_tuple>;
137+
using result_type = access_bindable_t<bound_type>;
138+
134139
const result_type* result = nullptr;
135-
internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable {
136-
using node_type = polyfill::remove_cvref_t<decltype(node)>;
137-
if constexpr (internal::is_bindable<node_type>::value) {
140+
iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable {
141+
using leaf_type = polyfill::remove_cvref_t<decltype(node)>;
142+
if constexpr (is_sql_parameter<leaf_type>::value) {
138143
++index;
139-
if constexpr (std::is_same<result_type, node_type>::value) {
144+
if constexpr (std::is_same<result_type, access_bindable_t<leaf_type>>::value) {
140145
if (index == N) {
141-
result = &node;
146+
result = &access_bindable(node);
142147
}
143148
}
144149
}
@@ -148,24 +153,86 @@ SQLITE_ORM_EXPORT namespace sqlite_orm {
148153

149154
template<int N, class T>
150155
auto& get(internal::prepared_statement_t<T>& statement) {
156+
using namespace ::sqlite_orm::internal;
151157
using statement_type = std::remove_reference_t<decltype(statement)>;
152-
using expression_type = internal::expression_type_t<statement_type>;
153-
using node_tuple = internal::node_tuple_t<expression_type>;
154-
using bind_tuple = internal::bindable_filter_t<node_tuple>;
155-
using result_type = std::tuple_element_t<static_cast<size_t>(N), bind_tuple>;
158+
using expression_type = expression_type_t<statement_type>;
159+
using node_tuple = node_tuple_t<expression_type>;
160+
using bind_tuple = bindable_filter_t<node_tuple>;
161+
using bound_type = std::tuple_element_t<static_cast<size_t>(N), bind_tuple>;
162+
using result_type = access_bindable_t<bound_type>;
163+
156164
result_type* result = nullptr;
165+
iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable {
166+
using leaf_type = polyfill::remove_cvref_t<decltype(node)>;
167+
if constexpr (is_sql_parameter<leaf_type>::value) {
168+
++index;
169+
if constexpr (std::is_same<result_type, access_bindable_t<leaf_type>>::value) {
170+
if (index == N) {
171+
result = const_cast<result_type*>(&access_bindable(node));
172+
}
173+
}
174+
}
175+
});
176+
return internal::get_ref(*result);
177+
}
178+
179+
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
180+
template<auto name, class T>
181+
requires (orm_parameter_moniker<decltype(name)> || orm_bindable_label<decltype(name)>)
182+
const auto& access(const internal::prepared_statement_t<T>& statement) {
183+
using namespace ::sqlite_orm::internal;
184+
using statement_type = std::remove_cvref_t<decltype(statement)>;
185+
using expression_type = expression_type_t<statement_type>;
186+
using node_tuple = node_tuple_t<expression_type>;
187+
using bind_tuple = bindable_filter_t<node_tuple>;
188+
using index_type =
189+
find_tuple_type<bind_tuple, name_constant_type_t<decltype(name)>, name_constant_type_or_none_t>;
190+
constexpr size_t N = index_type::value;
191+
static_assert(N < std::tuple_size_v<bind_tuple>, "No such named bindable found in prepared statement");
192+
using bound_type = std::tuple_element_t<N, bind_tuple>;
193+
using result_type = access_bindable_t<bound_type>;
194+
195+
const result_type* result = nullptr;
196+
iterate_ast(statement.expression, [&result, index = -1]<class leaf_type>(const leaf_type& node) mutable {
197+
if constexpr (is_sql_parameter<leaf_type>::value) {
198+
++index;
199+
if constexpr (std::is_same_v<result_type, access_bindable_t<leaf_type>>) {
200+
if (index == N) {
201+
result = &access_bindable(node);
202+
}
203+
}
204+
}
205+
});
206+
return internal::get_ref(*result);
207+
}
157208

158-
internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable {
159-
using node_type = polyfill::remove_cvref_t<decltype(node)>;
160-
if constexpr (internal::is_bindable<node_type>::value) {
209+
template<auto name, class T>
210+
requires (orm_parameter_moniker<decltype(name)> || orm_bindable_label<decltype(name)>)
211+
auto& access(internal::prepared_statement_t<T>& statement) {
212+
using namespace ::sqlite_orm::internal;
213+
using statement_type = std::remove_cvref_t<decltype(statement)>;
214+
using expression_type = expression_type_t<statement_type>;
215+
using node_tuple = node_tuple_t<expression_type>;
216+
using bind_tuple = bindable_filter_t<node_tuple>;
217+
using index_type =
218+
find_tuple_type<bind_tuple, name_constant_type_t<decltype(name)>, name_constant_type_or_none_t>;
219+
constexpr size_t N = index_type::value;
220+
static_assert(N < std::tuple_size_v<bind_tuple>, "No such named bindable found in prepared statement");
221+
using bound_type = std::tuple_element_t<N, bind_tuple>;
222+
using result_type = access_bindable_t<bound_type>;
223+
224+
result_type* result = nullptr;
225+
iterate_ast(statement.expression, [&result, index = -1]<class leaf_type>(const leaf_type& node) mutable {
226+
if constexpr (is_sql_parameter<leaf_type>::value) {
161227
++index;
162-
if constexpr (std::is_same<result_type, node_type>::value) {
228+
if constexpr (std::is_same_v<result_type, access_bindable_t<leaf_type>>) {
163229
if (index == N) {
164-
result = const_cast<result_type*>(&node);
230+
result = const_cast<result_type*>(&access_bindable(node));
165231
}
166232
}
167233
}
168234
});
169235
return internal::get_ref(*result);
170236
}
237+
#endif
171238
}

dev/node_tuple.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,6 @@ namespace sqlite_orm {
9898
template<char... C>
9999
struct node_tuple<column_alias<C...>, void> : node_tuple<void> {};
100100

101-
/**
102-
* Literal
103-
*/
104-
template<class T>
105-
struct node_tuple<literal_holder<T>, void> : node_tuple<void> {};
106-
107101
template<class E>
108102
struct node_tuple<order_by_t<E>, void> : node_tuple<E> {};
109103

0 commit comments

Comments
 (0)