Skip to content

Commit 4ba4cea

Browse files
authored
Merge pull request #1124 from FireDaemon/CTEs
2 parents 9baa19d + 312aef2 commit 4ba4cea

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+5542
-378
lines changed

appveyor.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ for:
107107
install:
108108
- |-
109109
cd C:\Tools\vcpkg
110-
git fetch --tags && git checkout 2024.01.12
110+
git fetch --tags && git checkout 2024.03.25
111111
cd %APPVEYOR_BUILD_FOLDER%
112112
C:\Tools\vcpkg\bootstrap-vcpkg.bat -disableMetrics
113113
C:\Tools\vcpkg\vcpkg integrate install
@@ -140,7 +140,7 @@ for:
140140
install:
141141
- |-
142142
pushd $HOME/vcpkg
143-
git fetch --tags && git checkout 2024.01.12
143+
git fetch --tags && git checkout 2024.03.25
144144
popd
145145
$HOME/vcpkg/bootstrap-vcpkg.sh -disableMetrics
146146
$HOME/vcpkg/vcpkg integrate install --overlay-triplets=vcpkg/triplets
@@ -168,7 +168,7 @@ for:
168168
# using custom vcpkg triplets for building and linking dynamic dependent libraries
169169
install:
170170
- |-
171-
git clone --depth 1 --branch 2024.01.12 https://github.com/microsoft/vcpkg.git $HOME/vcpkg
171+
git clone --depth 1 --branch 2024.03.25 https://github.com/microsoft/vcpkg.git $HOME/vcpkg
172172
$HOME/vcpkg/bootstrap-vcpkg.sh -disableMetrics
173173
$HOME/vcpkg/vcpkg integrate install --overlay-triplets=vcpkg/triplets
174174
vcpkg install sqlite3[core,dbstat,math,json1,fts5,soundex] catch2 --overlay-triplets=vcpkg/triplets

dev/alias.h

+131-7
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@
44
#include <utility> // std::make_index_sequence, std::move
55
#include <string> // std::string
66
#include <sstream> // std::stringstream
7+
#ifdef SQLITE_ORM_WITH_CTE
8+
#include <array>
9+
#endif
710

811
#include "functional/cxx_type_traits_polyfill.h"
912
#include "functional/cstring_literal.h"
1013
#include "type_traits.h"
1114
#include "alias_traits.h"
1215
#include "table_type_of.h"
1316
#include "tags.h"
17+
#include "column_pointer.h"
1418

1519
namespace sqlite_orm {
1620

@@ -78,8 +82,8 @@ namespace sqlite_orm {
7882
/*
7983
* Encapsulates extracting the alias identifier of an alias.
8084
*
81-
* `extract()` always returns the alias identifier.
82-
* `as_alias()` is used in contexts where a table is aliased, and the alias identifier is returned.
85+
* `extract()` always returns the alias identifier or CTE moniker.
86+
* `as_alias()` is used in contexts where a recordset is aliased, and the alias identifier is returned.
8387
* `as_qualifier()` is used in contexts where a table is aliased, and the alias identifier is returned.
8488
*/
8589
template<class A>
@@ -96,6 +100,14 @@ namespace sqlite_orm {
96100
return alias_extractor::extract();
97101
}
98102

103+
#ifdef SQLITE_ORM_WITH_CTE
104+
// for CTE monikers -> empty
105+
template<class T = A, satisfies<std::is_same, polyfill::detected_t<type_t, T>, A> = true>
106+
static std::string as_alias() {
107+
return {};
108+
}
109+
#endif
110+
99111
// for regular table aliases -> alias identifier
100112
template<class T = A, satisfies<is_table_alias, T> = true>
101113
static std::string as_qualifier(const basic_table&) {
@@ -131,6 +143,8 @@ namespace sqlite_orm {
131143
using type = T;
132144

133145
alias_holder() = default;
146+
// CTE feature needs it to implicitly convert a column alias to an alias_holder; see `cte()` factory function
147+
alias_holder(const T&) noexcept {}
134148
};
135149

136150
template<class T>
@@ -144,8 +158,31 @@ namespace sqlite_orm {
144158
[[nodiscard]] consteval recordset_alias<T, A, X...> for_() const {
145159
return {};
146160
}
161+
162+
template<auto t>
163+
[[nodiscard]] consteval auto for_() const {
164+
using T = std::remove_const_t<decltype(t)>;
165+
return recordset_alias<T, A, X...>{};
166+
}
147167
};
148168
#endif
169+
170+
#ifdef SQLITE_ORM_WITH_CTE
171+
template<size_t n, char... C>
172+
SQLITE_ORM_CONSTEVAL auto n_to_colalias() {
173+
constexpr column_alias<'1' + n % 10, C...> colalias{};
174+
if constexpr(n > 10) {
175+
return n_to_colalias<n / 10, '1' + n % 10, C...>();
176+
} else {
177+
return colalias;
178+
}
179+
}
180+
181+
template<class T>
182+
inline constexpr bool is_builtin_numeric_column_alias_v = false;
183+
template<char... C>
184+
inline constexpr bool is_builtin_numeric_column_alias_v<column_alias<C...>> = ((C >= '0' && C <= '9') && ...);
185+
#endif
149186
}
150187

151188
/**
@@ -155,7 +192,12 @@ namespace sqlite_orm {
155192
* using als = alias_u<User>;
156193
* select(alias_column<als>(column<User>(&User::id)))
157194
*/
158-
template<class A, class C, std::enable_if_t<internal::is_table_alias<A>::value, bool> = true>
195+
template<class A,
196+
class C,
197+
std::enable_if_t<
198+
polyfill::conjunction<internal::is_table_alias<A>,
199+
polyfill::negation<internal::is_cte_moniker<internal::type_t<A>>>>::value,
200+
bool> = true>
159201
constexpr auto alias_column(C field) {
160202
using namespace ::sqlite_orm::internal;
161203
using aliased_type = type_t<A>;
@@ -173,7 +215,13 @@ namespace sqlite_orm {
173215
* using als = alias_u<User>;
174216
* select(alias_column<als>(&User::id))
175217
*/
176-
template<class A, class F, class O, std::enable_if_t<internal::is_table_alias<A>::value, bool> = true>
218+
template<class A,
219+
class F,
220+
class O,
221+
std::enable_if_t<
222+
polyfill::conjunction<internal::is_table_alias<A>,
223+
polyfill::negation<internal::is_cte_moniker<internal::type_t<A>>>>::value,
224+
bool> = true>
177225
constexpr auto alias_column(F O::*field) {
178226
using namespace ::sqlite_orm::internal;
179227
using aliased_type = type_t<A>;
@@ -195,6 +243,7 @@ namespace sqlite_orm {
195243
* select(alias_column<als>(&User::id))
196244
*/
197245
template<orm_table_alias auto als, class C>
246+
requires(!orm_cte_moniker<internal::auto_type_t<als>>)
198247
constexpr auto alias_column(C field) {
199248
using namespace ::sqlite_orm::internal;
200249
using A = decltype(als);
@@ -225,11 +274,60 @@ namespace sqlite_orm {
225274
* select(als->*&User::id)
226275
*/
227276
template<orm_table_alias A, class F>
277+
requires(!orm_cte_moniker<internal::type_t<A>>)
228278
constexpr auto operator->*(const A& /*tableAlias*/, F field) {
229279
return alias_column<A>(std::move(field));
230280
}
231281
#endif
232282

283+
#ifdef SQLITE_ORM_WITH_CTE
284+
/**
285+
* Create a column reference to an aliased CTE column.
286+
*/
287+
template<class A,
288+
class C,
289+
std::enable_if_t<
290+
polyfill::conjunction_v<internal::is_table_alias<A>, internal::is_cte_moniker<internal::type_t<A>>>,
291+
bool> = true>
292+
constexpr auto alias_column(C c) {
293+
using namespace internal;
294+
using cte_moniker_t = type_t<A>;
295+
296+
if constexpr(is_column_pointer_v<C>) {
297+
static_assert(std::is_same<table_type_of_t<C>, cte_moniker_t>::value,
298+
"Column pointer must match aliased CTE");
299+
return alias_column_t<A, C>{c};
300+
} else {
301+
auto cp = column<cte_moniker_t>(c);
302+
return alias_column_t<A, decltype(cp)>{std::move(cp)};
303+
}
304+
}
305+
306+
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
307+
/**
308+
* Create a column reference to an aliased CTE column.
309+
*
310+
* @note (internal) Intentionally place in the sqlite_orm namespace for ADL (Argument Dependent Lookup)
311+
* because recordset aliases are derived from `sqlite_orm::alias_tag`
312+
*/
313+
template<orm_table_alias A, class C>
314+
requires(orm_cte_moniker<internal::type_t<A>>)
315+
constexpr auto operator->*(const A& /*tableAlias*/, C c) {
316+
return alias_column<A>(std::move(c));
317+
}
318+
319+
/**
320+
* Create a column reference to an aliased CTE column.
321+
*/
322+
template<orm_table_alias auto als, class C>
323+
requires(orm_cte_moniker<internal::auto_type_t<als>>)
324+
constexpr auto alias_column(C c) {
325+
using A = std::remove_const_t<decltype(als)>;
326+
return alias_column<A>(std::move(c));
327+
}
328+
#endif
329+
#endif
330+
233331
/**
234332
* Alias a column expression.
235333
*/
@@ -247,17 +345,29 @@ namespace sqlite_orm {
247345
return internal::as_t<decltype(als), E>{std::move(expression)};
248346
}
249347

250-
/**
348+
/**
251349
* Alias a column expression.
252350
*/
253351
template<orm_column_alias A, class E>
254352
internal::as_t<A, E> operator>>=(E expression, const A&) {
255353
return {std::move(expression)};
256354
}
355+
#else
356+
/**
357+
* Alias a column expression.
358+
*/
359+
template<class A, class E, internal::satisfies<internal::is_column_alias, A> = true>
360+
internal::as_t<A, E> operator>>=(E expression, const A&) {
361+
return {std::move(expression)};
362+
}
257363
#endif
258364

259-
template<class A, internal::satisfies<internal::is_column_alias, A> = true>
260-
internal::alias_holder<A> get() {
365+
/**
366+
* Wrap a column alias in an alias holder.
367+
*/
368+
template<class T>
369+
internal::alias_holder<T> get() {
370+
static_assert(internal::is_column_alias_v<T>, "");
261371
return {};
262372
}
263373

@@ -362,4 +472,18 @@ namespace sqlite_orm {
362472
}
363473
}
364474
#endif
475+
476+
#ifdef SQLITE_ORM_WITH_CTE
477+
/**
478+
* column_alias<'1'[, ...]> from a numeric literal.
479+
* E.g. 1_colalias, 2_colalias
480+
*/
481+
template<char... Chars>
482+
[[nodiscard]] SQLITE_ORM_CONSTEVAL auto operator"" _colalias() {
483+
// numeric identifiers are used for automatically assigning implicit aliases to unaliased column expressions,
484+
// which start at "1".
485+
static_assert(std::array{Chars...}[0] > '0');
486+
return internal::column_alias<Chars...>{};
487+
}
488+
#endif
365489
}

dev/alias_traits.h

+26-3
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ namespace sqlite_orm {
4444
/** @short Alias of a concrete table, see `orm_table_alias`.
4545
*/
4646
template<class A>
47-
SQLITE_ORM_INLINE_VAR constexpr bool is_table_alias_v = polyfill::conjunction_v<
47+
SQLITE_ORM_INLINE_VAR constexpr bool is_table_alias_v = polyfill::conjunction<
4848
is_recordset_alias<A>,
49-
polyfill::negation<std::is_same<polyfill::detected_t<type_t, A>, std::remove_const_t<A>>>>;
49+
polyfill::negation<std::is_same<polyfill::detected_t<type_t, A>, std::remove_const_t<A>>>>::value;
5050

5151
template<class A>
5252
struct is_table_alias : polyfill::bool_constant<is_table_alias_v<A>> {};
@@ -68,6 +68,20 @@ namespace sqlite_orm {
6868
template<auto recordset>
6969
using decay_table_reference_t = typename decay_table_reference<decltype(recordset)>::type;
7070
#endif
71+
72+
/** @short Moniker of a CTE, see `orm_cte_moniker`.
73+
*/
74+
template<class A>
75+
SQLITE_ORM_INLINE_VAR constexpr bool is_cte_moniker_v =
76+
#ifdef SQLITE_ORM_WITH_CTE
77+
polyfill::conjunction_v<is_recordset_alias<A>,
78+
std::is_same<polyfill::detected_t<type_t, A>, std::remove_const_t<A>>>;
79+
#else
80+
false;
81+
#endif
82+
83+
template<class A>
84+
using is_cte_moniker = polyfill::bool_constant<is_cte_moniker_v<A>>;
7185
}
7286

7387
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
@@ -109,6 +123,15 @@ namespace sqlite_orm {
109123
template<class R>
110124
concept orm_table_reference = polyfill::is_specialization_of_v<std::remove_const_t<R>, internal::table_reference>;
111125

126+
/** @short Moniker of a CTE.
127+
*
128+
* A CTE moniker has the following traits:
129+
* - is derived from `alias_tag`.
130+
* - has a `type` typename, which refers to itself.
131+
*/
132+
template<class A>
133+
concept orm_cte_moniker = (orm_recordset_alias<A> && std::same_as<typename A::type, std::remove_const_t<A>>);
134+
112135
/** @short Specifies that a type refers to a mapped table (possibly aliased).
113136
*/
114137
template<class T>
@@ -122,6 +145,6 @@ namespace sqlite_orm {
122145
/** @short Specifies that a type is a mapped recordset (table reference).
123146
*/
124147
template<class T>
125-
concept orm_mapped_recordset = (orm_table_reference<T>);
148+
concept orm_mapped_recordset = (orm_table_reference<T> || orm_cte_moniker<T>);
126149
#endif
127150
}

dev/ast_iterator.h

+23
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,29 @@ namespace sqlite_orm {
226226
}
227227
};
228228

229+
#ifdef SQLITE_ORM_WITH_CTE
230+
template<class CTE>
231+
struct ast_iterator<CTE, match_specialization_of<CTE, common_table_expression>> {
232+
using node_type = CTE;
233+
234+
template<class L>
235+
void operator()(const node_type& c, L& lambda) const {
236+
iterate_ast(c.subselect, lambda);
237+
}
238+
};
239+
240+
template<class With>
241+
struct ast_iterator<With, match_specialization_of<With, with_t>> {
242+
using node_type = With;
243+
244+
template<class L>
245+
void operator()(const node_type& c, L& lambda) const {
246+
iterate_ast(c.cte, lambda);
247+
iterate_ast(c.expression, lambda);
248+
}
249+
};
250+
#endif
251+
229252
template<class T>
230253
struct ast_iterator<T, match_if<is_compound_operator, T>> {
231254
using node_type = T;

0 commit comments

Comments
 (0)