Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Operators for alias columns, alias columns from column pointers #1237

Merged
merged 12 commits into from
Oct 22, 2023
Merged
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 2023.06.20
git fetch --tags && git checkout 2023.10.19
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 2023.06.20
git fetch --tags && git checkout 2023.10.19
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 2023.06.20 https://github.com/microsoft/vcpkg.git $HOME/vcpkg
git clone --depth 1 --branch 2023.10.19 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] catch2 --overlay-triplets=vcpkg/triplets
Expand Down
84 changes: 70 additions & 14 deletions dev/alias.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "functional/cxx_type_traits_polyfill.h"
#include "type_traits.h"
#include "alias_traits.h"
#include "table_type_of.h"
#include "tags.h"

namespace sqlite_orm {
Expand Down Expand Up @@ -67,6 +68,10 @@ namespace sqlite_orm {
column_type column;
};

template<class T>
SQLITE_ORM_INLINE_VAR constexpr bool
is_operator_argument_v<T, std::enable_if_t<polyfill::is_specialization_of_v<T, alias_column_t>>> = true;

struct basic_table;

/*
Expand Down Expand Up @@ -166,30 +171,81 @@ namespace sqlite_orm {
}

/**
* @return column with table alias attached. Place it instead of a column statement in case you need to specify a
* column with table alias prefix like 'a.column'.
* Using a column pointer, create a column reference to an aliased table column.
*
* Example:
* 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_v<A>, bool> = true>
internal::alias_column_t<A, C> alias_column(C c) {
using aliased_type = internal::type_t<A>;
static_assert(std::is_same<polyfill::detected_t<internal::table_type_of_t, C>, aliased_type>::value,
"Column must be from aliased table");
return {c};
constexpr auto alias_column(C field) {
using namespace ::sqlite_orm::internal;
using aliased_type = type_t<A>;
static_assert(is_field_of_v<C, aliased_type>, "Column must be from aliased table");

return alias_column_t<A, C>{std::move(field)};
}

/**
* Using an object member field, create a column reference to an aliased table column.
*
* @note The object member pointer can be from a derived class without explicitly forming a column pointer.
*
* Example:
* 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_v<A>, bool> = true>
constexpr auto alias_column(F O::*field) {
using namespace ::sqlite_orm::internal;
using aliased_type = type_t<A>;
static_assert(is_field_of_v<F O::*, aliased_type>, "Column must be from aliased table");

using C1 =
std::conditional_t<std::is_same<O, aliased_type>::value, F O::*, column_pointer<aliased_type, F O::*>>;
return alias_column_t<A, C1>{{field}};
}

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
/**
* Create a column reference to an aliased table column.
*
* @note An object member pointer can be from a derived class without explicitly forming a column pointer.
*
* Example:
* constexpr auto als = "u"_alias.for_<User>();
* select(alias_column<als>(&User::id))
*/
template<orm_table_alias auto als, class C>
auto alias_column(C c) {
constexpr auto alias_column(C field) {
using namespace ::sqlite_orm::internal;
using A = std::remove_const_t<decltype(als)>;
using aliased_type = internal::type_t<A>;
static_assert(std::is_same_v<polyfill::detected_t<internal::table_type_of_t, C>, aliased_type>,
"Column must be from aliased table");
return internal::alias_column_t<A, decltype(c)>{c};
using aliased_type = type_t<A>;
static_assert(is_field_of_v<C, aliased_type>, "Column must be from aliased table");

if constexpr(is_column_pointer_v<C>) {
return alias_column_t<A, C>{std::move(field)};
} else if constexpr(std::is_same_v<member_object_type_t<C>, aliased_type>) {
return alias_column_t<A, C>{field};
} else {
// wrap in column_pointer
using C1 = column_pointer<aliased_type, C>;
return alias_column_t<A, C1>{{field}};
}
}

/**
* Create a column reference to an aliased table column.
*
* @note An object member pointer can be from a derived class without explicitly forming a column pointer.
*
* Example:
* constexpr auto als = "u"_alias.for_<User>();
* select(als->*&User::id)
*/
template<orm_table_alias A, class F>
constexpr auto operator->*(const A&, F field) {
return internal::alias_column_t<A, decltype(field)>{field};
constexpr auto operator->*(const A& /*tableAlias*/, F field) {
return alias_column<A>(std::move(field));
}
#endif

Expand Down
7 changes: 3 additions & 4 deletions dev/column_pointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ namespace sqlite_orm {
*/
template<class T, class F>
struct column_pointer {
using self = column_pointer<T, F>;
using type = T;
using field_type = F;

Expand All @@ -36,8 +35,8 @@ namespace sqlite_orm {
* struct MyType : BaseType { ... };
* storage.select(column<MyType>(&BaseType::id));
*/
template<class T, class F>
internal::column_pointer<T, F> column(F f) {
return {std::move(f)};
template<class Object, class F, internal::satisfies_not<internal::is_recordset_alias, Object> = true>
constexpr internal::column_pointer<Object, F> column(F field) {
return {std::move(field)};
}
}
4 changes: 0 additions & 4 deletions dev/functional/cxx_core_features.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@
#define SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED
#endif

#if __cpp_inline_variables >= 201606L
#define SQLITE_ORM_INLINE_VARIABLES_SUPPORTED
#endif

#if __cpp_if_constexpr >= 201606L
#define SQLITE_ORM_IF_CONSTEXPR_SUPPORTED
#endif
Expand Down
56 changes: 29 additions & 27 deletions dev/schema/table.h
Original file line number Diff line number Diff line change
Expand Up @@ -277,30 +277,32 @@ namespace sqlite_orm {

module_details_type module_details;

#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED
virtual_table_t(std::string name, module_details_type module_details) :
basic_table{std::move(name)}, module_details{std::move(module_details)} {}
#endif

/**
* Call passed lambda with columns not having the specified constraint trait `OpTrait`.
* @param lambda Lambda called for each column.
/**
* Call passed lambda with columns not having the specified constraint trait `OpTrait`.
trueqbit marked this conversation as resolved.
Show resolved Hide resolved
* @param lambda Lambda called for each column.
*/
template<template<class...> class OpTraitFn, class L>
void for_each_column_excluding(L&& lambda) const {
this->module_details.template for_each_column_excluding<OpTraitFn>(lambda);
}

/**
* Call passed lambda with columns not having the specified constraint trait `OpTrait`.
* @param lambda Lambda called for each column.
/**
* Call passed lambda with columns not having the specified constraint trait `OpTrait`.
* @param lambda Lambda called for each column.
*/
template<class OpTraitFnCls, class L, satisfies<mpl::is_metafunction_class, OpTraitFnCls> = true>
void for_each_column_excluding(L&& lambda) const {
this->module_details.template for_each_column_excluding<OpTraitFnCls>(lambda);
}

/**
* Call passed lambda with all defined columns.
* @param lambda Lambda called for each column. Function signature: `void(auto& column)`
/**
* Call passed lambda with all defined columns.
* @param lambda Lambda called for each column. Function signature: `void(auto& column)`
*/
template<class L>
void for_each_column(L&& lambda) const {
Expand All @@ -323,27 +325,27 @@ namespace sqlite_orm {

using_fts5_t(columns_type columns) : columns(std::move(columns)) {}

/**
* Call passed lambda with columns not having the specified constraint trait `OpTrait`.
* @param lambda Lambda called for each column.
/**
* Call passed lambda with columns not having the specified constraint trait `OpTrait`.
* @param lambda Lambda called for each column.
*/
template<template<class...> class OpTraitFn, class L>
void for_each_column_excluding(L&& lambda) const {
iterate_tuple(this->columns, col_index_sequence_excluding<columns_type, OpTraitFn>{}, lambda);
}

/**
* Call passed lambda with columns not having the specified constraint trait `OpTrait`.
* @param lambda Lambda called for each column.
/**
* Call passed lambda with columns not having the specified constraint trait `OpTrait`.
* @param lambda Lambda called for each column.
*/
template<class OpTraitFnCls, class L, satisfies<mpl::is_metafunction_class, OpTraitFnCls> = true>
void for_each_column_excluding(L&& lambda) const {
this->for_each_column_excluding<OpTraitFnCls::template fn>(lambda);
}

/**
* Call passed lambda with all defined columns.
* @param lambda Lambda called for each column. Function signature: `void(auto& column)`
/**
* Call passed lambda with all defined columns.
* @param lambda Lambda called for each column. Function signature: `void(auto& column)`
*/
template<class L>
void for_each_column(L&& lambda) const {
Expand Down Expand Up @@ -388,21 +390,21 @@ namespace sqlite_orm {
SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward<Cs>(columns)...)});
}

/**
* Factory function for a table definition.
*
* The mapped object type is determined implicitly from the first column definition.
/**
* Factory function for a table definition.
*
* The mapped object type is determined implicitly from the first column definition.
*/
template<class... Cs, class T = typename std::tuple_element_t<0, std::tuple<Cs...>>::object_type>
internal::table_t<T, false, Cs...> make_table(std::string name, Cs... args) {
SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(
return {std::move(name), std::make_tuple<Cs...>(std::forward<Cs>(args)...)});
}

/**
* Factory function for a table definition.
*
* The mapped object type is explicitly specified.
/**
* Factory function for a table definition.
*
* The mapped object type is explicitly specified.
*/
template<class T, class... Cs>
internal::table_t<T, false, Cs...> make_table(std::string name, Cs... args) {
Expand All @@ -412,6 +414,6 @@ namespace sqlite_orm {

template<class M>
internal::virtual_table_t<M> make_virtual_table(std::string name, M module_details) {
return internal::virtual_table_t<M>(std::move(name), std::move(module_details));
SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::move(name), std::move(module_details)});
}
}
6 changes: 3 additions & 3 deletions dev/statement_serializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ namespace sqlite_orm {
using statement_type = current_time_t;

template<class Ctx>
std::string operator()(const statement_type& statement, const Ctx& context) {
std::string operator()(const statement_type& /*statement*/, const Ctx& /*context*/) {
trueqbit marked this conversation as resolved.
Show resolved Hide resolved
return "CURRENT_TIME";
}
};
Expand All @@ -173,7 +173,7 @@ namespace sqlite_orm {
using statement_type = current_date_t;

template<class Ctx>
std::string operator()(const statement_type& statement, const Ctx& context) {
std::string operator()(const statement_type& /*statement*/, const Ctx& /*context*/) {
return "CURRENT_DATE";
}
};
Expand All @@ -183,7 +183,7 @@ namespace sqlite_orm {
using statement_type = current_timestamp_t;

template<class Ctx>
std::string operator()(const statement_type& statement, const Ctx& context) {
std::string operator()(const statement_type& /*statement*/, const Ctx& /*context*/) {
return "CURRENT_TIMESTAMP";
}
};
Expand Down
27 changes: 24 additions & 3 deletions dev/table_type_of.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#pragma once
#include <type_traits> // std::declval
#include "functional/cxx_type_traits_polyfill.h"

namespace sqlite_orm {

Expand Down Expand Up @@ -33,11 +35,30 @@ namespace sqlite_orm {
};

template<class C>
struct table_type_of<indexed_column_t<C>> {
using type = typename table_type_of<C>::type;
};
struct table_type_of<indexed_column_t<C>> : table_type_of<C> {};

template<class T>
using table_type_of_t = typename table_type_of<T>::type;

/*
* This trait can be used to check whether the object type of a member pointer or column pointer matches the target type.
*
* One use case is the ability to create column reference to an aliased table column of a derived object field without explicitly using a column pointer.
* E.g.
* regular: `alias_column<alias_d<Derived>>(column<Derived>(&Base::field))`
* short: `alias_column<alias_d<Derived>>(&Base::field)`
*/
template<class F, class T, class SFINAE = void>
SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v = false;

/*
* `true` if a pointer-to-member operator is a valid expression for an object of type `T` and a member pointer of type `F O::*`.
*/
template<class F, class O, class T>
SQLITE_ORM_INLINE_VAR constexpr bool
is_field_of_v<F O::*, T, polyfill::void_t<decltype(std::declval<T>().*std::declval<F O::*>())>> = true;

template<class F, class T>
SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v<column_pointer<T, F>, T, void> = true;
}
}
Loading