Skip to content

Commit

Permalink
Merge pull request #1124 from FireDaemon/CTEs
Browse files Browse the repository at this point in the history
  • Loading branch information
trueqbit authored Apr 4, 2024
2 parents 9baa19d + 312aef2 commit 4ba4cea
Show file tree
Hide file tree
Showing 46 changed files with 5,542 additions and 378 deletions.
6 changes: 3 additions & 3 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ for:
install:
- |-
cd C:\Tools\vcpkg
git fetch --tags && git checkout 2024.01.12
git fetch --tags && git checkout 2024.03.25
cd %APPVEYOR_BUILD_FOLDER%
C:\Tools\vcpkg\bootstrap-vcpkg.bat -disableMetrics
C:\Tools\vcpkg\vcpkg integrate install
Expand Down Expand Up @@ -140,7 +140,7 @@ for:
install:
- |-
pushd $HOME/vcpkg
git fetch --tags && git checkout 2024.01.12
git fetch --tags && git checkout 2024.03.25
popd
$HOME/vcpkg/bootstrap-vcpkg.sh -disableMetrics
$HOME/vcpkg/vcpkg integrate install --overlay-triplets=vcpkg/triplets
Expand Down Expand Up @@ -168,7 +168,7 @@ for:
# using custom vcpkg triplets for building and linking dynamic dependent libraries
install:
- |-
git clone --depth 1 --branch 2024.01.12 https://github.com/microsoft/vcpkg.git $HOME/vcpkg
git clone --depth 1 --branch 2024.03.25 https://github.com/microsoft/vcpkg.git $HOME/vcpkg
$HOME/vcpkg/bootstrap-vcpkg.sh -disableMetrics
$HOME/vcpkg/vcpkg integrate install --overlay-triplets=vcpkg/triplets
vcpkg install sqlite3[core,dbstat,math,json1,fts5,soundex] catch2 --overlay-triplets=vcpkg/triplets
Expand Down
138 changes: 131 additions & 7 deletions dev/alias.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
#include <utility> // std::make_index_sequence, std::move
#include <string> // std::string
#include <sstream> // std::stringstream
#ifdef SQLITE_ORM_WITH_CTE
#include <array>
#endif

#include "functional/cxx_type_traits_polyfill.h"
#include "functional/cstring_literal.h"
#include "type_traits.h"
#include "alias_traits.h"
#include "table_type_of.h"
#include "tags.h"
#include "column_pointer.h"

namespace sqlite_orm {

Expand Down Expand Up @@ -78,8 +82,8 @@ namespace sqlite_orm {
/*
* Encapsulates extracting the alias identifier of an alias.
*
* `extract()` always returns the alias identifier.
* `as_alias()` is used in contexts where a table is aliased, and the alias identifier is returned.
* `extract()` always returns the alias identifier or CTE moniker.
* `as_alias()` is used in contexts where a recordset is aliased, and the alias identifier is returned.
* `as_qualifier()` is used in contexts where a table is aliased, and the alias identifier is returned.
*/
template<class A>
Expand All @@ -96,6 +100,14 @@ namespace sqlite_orm {
return alias_extractor::extract();
}

#ifdef SQLITE_ORM_WITH_CTE
// for CTE monikers -> empty
template<class T = A, satisfies<std::is_same, polyfill::detected_t<type_t, T>, A> = true>
static std::string as_alias() {
return {};
}
#endif

// for regular table aliases -> alias identifier
template<class T = A, satisfies<is_table_alias, T> = true>
static std::string as_qualifier(const basic_table&) {
Expand Down Expand Up @@ -131,6 +143,8 @@ namespace sqlite_orm {
using type = T;

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

template<class T>
Expand All @@ -144,8 +158,31 @@ namespace sqlite_orm {
[[nodiscard]] consteval recordset_alias<T, A, X...> for_() const {
return {};
}

template<auto t>
[[nodiscard]] consteval auto for_() const {
using T = std::remove_const_t<decltype(t)>;
return recordset_alias<T, A, X...>{};
}
};
#endif

#ifdef SQLITE_ORM_WITH_CTE
template<size_t n, char... C>
SQLITE_ORM_CONSTEVAL auto n_to_colalias() {
constexpr column_alias<'1' + n % 10, C...> colalias{};
if constexpr(n > 10) {
return n_to_colalias<n / 10, '1' + n % 10, C...>();
} else {
return colalias;
}
}

template<class T>
inline constexpr bool is_builtin_numeric_column_alias_v = false;
template<char... C>
inline constexpr bool is_builtin_numeric_column_alias_v<column_alias<C...>> = ((C >= '0' && C <= '9') && ...);
#endif
}

/**
Expand All @@ -155,7 +192,12 @@ namespace sqlite_orm {
* using als = alias_u<User>;
* select(alias_column<als>(column<User>(&User::id)))
*/
template<class A, class C, std::enable_if_t<internal::is_table_alias<A>::value, bool> = true>
template<class A,
class C,
std::enable_if_t<
polyfill::conjunction<internal::is_table_alias<A>,
polyfill::negation<internal::is_cte_moniker<internal::type_t<A>>>>::value,
bool> = true>
constexpr auto alias_column(C field) {
using namespace ::sqlite_orm::internal;
using aliased_type = type_t<A>;
Expand All @@ -173,7 +215,13 @@ namespace sqlite_orm {
* using als = alias_u<User>;
* select(alias_column<als>(&User::id))
*/
template<class A, class F, class O, std::enable_if_t<internal::is_table_alias<A>::value, bool> = true>
template<class A,
class F,
class O,
std::enable_if_t<
polyfill::conjunction<internal::is_table_alias<A>,
polyfill::negation<internal::is_cte_moniker<internal::type_t<A>>>>::value,
bool> = true>
constexpr auto alias_column(F O::*field) {
using namespace ::sqlite_orm::internal;
using aliased_type = type_t<A>;
Expand All @@ -195,6 +243,7 @@ namespace sqlite_orm {
* select(alias_column<als>(&User::id))
*/
template<orm_table_alias auto als, class C>
requires(!orm_cte_moniker<internal::auto_type_t<als>>)
constexpr auto alias_column(C field) {
using namespace ::sqlite_orm::internal;
using A = decltype(als);
Expand Down Expand Up @@ -225,11 +274,60 @@ namespace sqlite_orm {
* select(als->*&User::id)
*/
template<orm_table_alias A, class F>
requires(!orm_cte_moniker<internal::type_t<A>>)
constexpr auto operator->*(const A& /*tableAlias*/, F field) {
return alias_column<A>(std::move(field));
}
#endif

#ifdef SQLITE_ORM_WITH_CTE
/**
* Create a column reference to an aliased CTE column.
*/
template<class A,
class C,
std::enable_if_t<
polyfill::conjunction_v<internal::is_table_alias<A>, internal::is_cte_moniker<internal::type_t<A>>>,
bool> = true>
constexpr auto alias_column(C c) {
using namespace internal;
using cte_moniker_t = type_t<A>;

if constexpr(is_column_pointer_v<C>) {
static_assert(std::is_same<table_type_of_t<C>, cte_moniker_t>::value,
"Column pointer must match aliased CTE");
return alias_column_t<A, C>{c};
} else {
auto cp = column<cte_moniker_t>(c);
return alias_column_t<A, decltype(cp)>{std::move(cp)};
}
}

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
/**
* Create a column reference to an aliased CTE column.
*
* @note (internal) Intentionally place in the sqlite_orm namespace for ADL (Argument Dependent Lookup)
* because recordset aliases are derived from `sqlite_orm::alias_tag`
*/
template<orm_table_alias A, class C>
requires(orm_cte_moniker<internal::type_t<A>>)
constexpr auto operator->*(const A& /*tableAlias*/, C c) {
return alias_column<A>(std::move(c));
}

/**
* Create a column reference to an aliased CTE column.
*/
template<orm_table_alias auto als, class C>
requires(orm_cte_moniker<internal::auto_type_t<als>>)
constexpr auto alias_column(C c) {
using A = std::remove_const_t<decltype(als)>;
return alias_column<A>(std::move(c));
}
#endif
#endif

/**
* Alias a column expression.
*/
Expand All @@ -247,17 +345,29 @@ namespace sqlite_orm {
return internal::as_t<decltype(als), E>{std::move(expression)};
}

/**
/**
* Alias a column expression.
*/
template<orm_column_alias A, class E>
internal::as_t<A, E> operator>>=(E expression, const A&) {
return {std::move(expression)};
}
#else
/**
* Alias a column expression.
*/
template<class A, class E, internal::satisfies<internal::is_column_alias, A> = true>
internal::as_t<A, E> operator>>=(E expression, const A&) {
return {std::move(expression)};
}
#endif

template<class A, internal::satisfies<internal::is_column_alias, A> = true>
internal::alias_holder<A> get() {
/**
* Wrap a column alias in an alias holder.
*/
template<class T>
internal::alias_holder<T> get() {
static_assert(internal::is_column_alias_v<T>, "");
return {};
}

Expand Down Expand Up @@ -362,4 +472,18 @@ namespace sqlite_orm {
}
}
#endif

#ifdef SQLITE_ORM_WITH_CTE
/**
* column_alias<'1'[, ...]> from a numeric literal.
* E.g. 1_colalias, 2_colalias
*/
template<char... Chars>
[[nodiscard]] SQLITE_ORM_CONSTEVAL auto operator"" _colalias() {
// numeric identifiers are used for automatically assigning implicit aliases to unaliased column expressions,
// which start at "1".
static_assert(std::array{Chars...}[0] > '0');
return internal::column_alias<Chars...>{};
}
#endif
}
29 changes: 26 additions & 3 deletions dev/alias_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ namespace sqlite_orm {
/** @short Alias of a concrete table, see `orm_table_alias`.
*/
template<class A>
SQLITE_ORM_INLINE_VAR constexpr bool is_table_alias_v = polyfill::conjunction_v<
SQLITE_ORM_INLINE_VAR constexpr bool is_table_alias_v = polyfill::conjunction<
is_recordset_alias<A>,
polyfill::negation<std::is_same<polyfill::detected_t<type_t, A>, std::remove_const_t<A>>>>;
polyfill::negation<std::is_same<polyfill::detected_t<type_t, A>, std::remove_const_t<A>>>>::value;

template<class A>
struct is_table_alias : polyfill::bool_constant<is_table_alias_v<A>> {};
Expand All @@ -68,6 +68,20 @@ namespace sqlite_orm {
template<auto recordset>
using decay_table_reference_t = typename decay_table_reference<decltype(recordset)>::type;
#endif

/** @short Moniker of a CTE, see `orm_cte_moniker`.
*/
template<class A>
SQLITE_ORM_INLINE_VAR constexpr bool is_cte_moniker_v =
#ifdef SQLITE_ORM_WITH_CTE
polyfill::conjunction_v<is_recordset_alias<A>,
std::is_same<polyfill::detected_t<type_t, A>, std::remove_const_t<A>>>;
#else
false;
#endif

template<class A>
using is_cte_moniker = polyfill::bool_constant<is_cte_moniker_v<A>>;
}

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

/** @short Moniker of a CTE.
*
* A CTE moniker has the following traits:
* - is derived from `alias_tag`.
* - has a `type` typename, which refers to itself.
*/
template<class A>
concept orm_cte_moniker = (orm_recordset_alias<A> && std::same_as<typename A::type, std::remove_const_t<A>>);

/** @short Specifies that a type refers to a mapped table (possibly aliased).
*/
template<class T>
Expand All @@ -122,6 +145,6 @@ namespace sqlite_orm {
/** @short Specifies that a type is a mapped recordset (table reference).
*/
template<class T>
concept orm_mapped_recordset = (orm_table_reference<T>);
concept orm_mapped_recordset = (orm_table_reference<T> || orm_cte_moniker<T>);
#endif
}
23 changes: 23 additions & 0 deletions dev/ast_iterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,29 @@ namespace sqlite_orm {
}
};

#ifdef SQLITE_ORM_WITH_CTE
template<class CTE>
struct ast_iterator<CTE, match_specialization_of<CTE, common_table_expression>> {
using node_type = CTE;

template<class L>
void operator()(const node_type& c, L& lambda) const {
iterate_ast(c.subselect, lambda);
}
};

template<class With>
struct ast_iterator<With, match_specialization_of<With, with_t>> {
using node_type = With;

template<class L>
void operator()(const node_type& c, L& lambda) const {
iterate_ast(c.cte, lambda);
iterate_ast(c.expression, lambda);
}
};
#endif

template<class T>
struct ast_iterator<T, match_if<is_compound_operator, T>> {
using node_type = T;
Expand Down
Loading

0 comments on commit 4ba4cea

Please sign in to comment.