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
1519namespace 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}
0 commit comments