Skip to content

Commit

Permalink
Merge branch 'multi-compound' into CTEs
Browse files Browse the repository at this point in the history
  • Loading branch information
trueqbit committed Oct 24, 2023
2 parents 2bbf149 + 7fa9582 commit 60954f2
Show file tree
Hide file tree
Showing 33 changed files with 1,347 additions and 1,042 deletions.
9 changes: 4 additions & 5 deletions dev/ast_iterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,9 @@ namespace sqlite_orm {
}
};

template<class L, class R, class... Ds>
struct ast_iterator<binary_operator<L, R, Ds...>, void> {
using node_type = binary_operator<L, R, Ds...>;
template<class T>
struct ast_iterator<T, match_if<is_binary_operator, T>> {
using node_type = T;

template<class C>
void operator()(const node_type& node, C& lambda) const {
Expand Down Expand Up @@ -264,8 +264,7 @@ namespace sqlite_orm {

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

Expand Down
2 changes: 1 addition & 1 deletion dev/column_names_getter.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ namespace sqlite_orm {
const Ctx& context) {
if(definedOrder) {
auto& table = pick_table<mapped_type_proxy_t<T>>(context.db_objects);
collectedExpressions.reserve(collectedExpressions.size() + table.count_columns_amount());
collectedExpressions.reserve(collectedExpressions.size() + table.template count_of<is_column>());
table.for_each_column([qualified = !context.skip_table_name,
&tableName = table.name,
&collectedExpressions](const column_identifier& column) {
Expand Down
10 changes: 6 additions & 4 deletions dev/column_result.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
#include <functional> // std::reference_wrapper

#include "functional/cxx_universal.h"
#include "functional/mpl.h"
#include "tuple_helper/tuple_traits.h"
#include "tuple_helper/tuple_fy.h"
#include "tuple_helper/tuple_filter.h"
#include "tuple_helper/same_or_void.h"
#include "type_traits.h"
#include "member_traits/member_traits.h"
#include "mapped_type_proxy.h"
Expand Down Expand Up @@ -248,11 +250,11 @@ namespace sqlite_orm {

template<class DBOs, class T>
struct column_result_t<DBOs, T, match_if<is_compound_operator, T>> {
using left_result = column_result_of_t<DBOs, typename T::left_type>;
using right_result = column_result_of_t<DBOs, typename T::right_type>;
static_assert(std::is_same<left_result, right_result>::value,
using column_result_fn = mpl::bind_front_fn<column_result_of_t, DBOs>;
using types_tuple = transform_tuple_t<typename T::expressions_tuple, column_result_fn::template fn>;
using type = std::tuple_element_t<0, types_tuple>;
static_assert(!std::is_same<void, same_or_void_of_t<types_tuple>>::value,
"Compound subselect queries must return same types");
using type = left_result;
};

template<class DBOs, class T>
Expand Down
71 changes: 37 additions & 34 deletions dev/constraints.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,15 @@ namespace sqlite_orm {
};

template<class T>
struct primary_key_with_autoincrement {
struct primary_key_with_autoincrement : T {
using primary_key_type = T;

primary_key_type primary_key;

primary_key_with_autoincrement(primary_key_type primary_key_) : primary_key(primary_key_) {}
const primary_key_type& as_base() const {
return *this;
}
#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED
primary_key_with_autoincrement(primary_key_type primary_key) : primary_key_type{primary_key} {}
#endif
};

/**
Expand All @@ -65,7 +68,7 @@ namespace sqlite_orm {

columns_tuple columns;

primary_key_t(decltype(columns) columns) : columns(std::move(columns)) {}
primary_key_t(columns_tuple columns) : columns(std::move(columns)) {}

self asc() const {
auto res = *this;
Expand Down Expand Up @@ -291,12 +294,12 @@ namespace sqlite_orm {
/**
* Holds obect type of all referenced columns.
*/
using target_type = typename same_or_void<table_type_of_t<Rs>...>::type;
using target_type = same_or_void_t<table_type_of_t<Rs>...>;

/**
* Holds obect type of all source columns.
*/
using source_type = typename same_or_void<table_type_of_t<Cs>...>::type;
using source_type = same_or_void_t<table_type_of_t<Cs>...>;

columns_type columns;
references_type references;
Expand Down Expand Up @@ -379,6 +382,7 @@ namespace sqlite_orm {
expression_type expression;
};

#if SQLITE_VERSION_NUMBER >= 3031000
struct basic_generated_always {
enum class storage_type {
not_specified,
Expand Down Expand Up @@ -411,6 +415,7 @@ namespace sqlite_orm {
return {std::move(this->expression), this->full, storage_type::stored};
}
};
#endif

struct null_t {};

Expand All @@ -420,28 +425,32 @@ namespace sqlite_orm {
namespace internal {

template<class T>
SQLITE_ORM_INLINE_VAR constexpr bool is_foreign_key_v = polyfill::is_specialization_of_v<T, foreign_key_t>;
SQLITE_ORM_INLINE_VAR constexpr bool is_foreign_key_v =
#if SQLITE_VERSION_NUMBER >= 3006019
polyfill::is_specialization_of_v<T, foreign_key_t>;
#else
false;
#endif

template<class T>
using is_foreign_key = polyfill::bool_constant<is_foreign_key_v<T>>;

template<class T>
struct is_primary_key : std::false_type {};

template<class... Cs>
struct is_primary_key<primary_key_t<Cs...>> : std::true_type {};

template<class T>
struct is_primary_key<primary_key_with_autoincrement<T>> : std::true_type {};
SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = std::is_base_of<primary_key_base, T>::value;

template<class T>
SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = is_primary_key<T>::value;
using is_primary_key = polyfill::bool_constant<is_primary_key_v<T>>;

template<class T>
using is_generated_always = polyfill::is_specialization_of<T, generated_always_t>;
SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v =
#if SQLITE_VERSION_NUMBER >= 3031000
polyfill::is_specialization_of_v<T, generated_always_t>;
#else
false;
#endif

template<class T>
SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = is_generated_always<T>::value;
using is_generated_always = polyfill::bool_constant<is_generated_always_v<T>>;

/**
* PRIMARY KEY INSERTABLE traits.
Expand All @@ -458,22 +467,16 @@ namespace sqlite_orm {
};

template<class T>
using is_constraint =
mpl::instantiate<mpl::disjunction<check_if<is_primary_key>,
check_if<is_foreign_key>,
check_if_is_type<null_t>,
check_if_is_type<not_null_t>,
check_if_is_template<unique_t>,
check_if_is_template<default_t>,
check_if_is_template<check_t>,
check_if_is_template<primary_key_with_autoincrement>,
check_if_is_type<collate_constraint_t>,
#if SQLITE_VERSION_NUMBER >= 3031000
check_if<is_generated_always>,
#endif
// dummy tail because of SQLITE_VERSION_NUMBER checks above
mpl::always<std::false_type>>,
T>;
using is_constraint = mpl::instantiate<mpl::disjunction<check_if<is_primary_key>,
check_if<is_foreign_key>,
check_if_is_type<null_t>,
check_if_is_type<not_null_t>,
check_if_is_template<unique_t>,
check_if_is_template<default_t>,
check_if_is_template<check_t>,
check_if_is_type<collate_constraint_t>,
check_if<is_generated_always>>,
T>;
}

#if SQLITE_VERSION_NUMBER >= 3031000
Expand Down
2 changes: 1 addition & 1 deletion dev/cte_column_names_collector.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ namespace sqlite_orm {
auto& table = pick_table<T>(context.db_objects);

std::vector<std::string> columnNames;
columnNames.reserve(size_t(table.count_columns_amount()));
columnNames.reserve(size_t(table.template count_of<is_column>()));

table.for_each_column([&columnNames](const column_identifier& column) {
columnNames.push_back(column.name);
Expand Down
6 changes: 3 additions & 3 deletions dev/cte_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ namespace sqlite_orm {
* Because CTEs can recursively refer to themselves in a compound statement, parsing
* the whole compound statement would lead to compiler errors if a column_pointer<>
* can't be resolved. Therefore, at the time of building a CTE table, we are only
* interested in the column results of the left expression.
* interested in the column results of the left-most select expression.
*/
template<class Select>
decltype(auto) get_cte_driving_subselect(const Select& subSelect);
Expand All @@ -115,11 +115,11 @@ namespace sqlite_orm {
}

/**
* Return left expression of compound statement.
* Return left-most select expression of compound statement.
*/
template<class Compound, class... Args, std::enable_if_t<is_compound_operator_v<Compound>, bool> = true>
decltype(auto) get_cte_driving_subselect(const select_t<Compound, Args...>& subSelect) {
return subSelect.col.left;
return std::get<0>(subSelect.col.compound);
}

/**
Expand Down
46 changes: 42 additions & 4 deletions dev/functional/mpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
* such as filtering columns by constraints having various traits.
* Hence it contains only a very small subset of a full MPL.
*
* Two key concepts are critical to understanding:
* Three key concepts are critical to understanding:
* 1. A 'metafunction' is a class template that represents a function invocable at compile-time.
* 2. A 'metafunction class' is a certain form of metafunction representation that enables higher-order metaprogramming.
* E.g. `template<class T> struct x { using type = int; };`
* 2. A 'metafunction operation' is an alias template that represents a nested template expression, whose instantiation yields a type.
* E.g. `template<class T> using alias_op_t = typename x<T>::type`
* 3. A 'metafunction class' is a certain form of metafunction representation that enables higher-order metaprogramming.
* More precisely, it's a class with a nested metafunction called "fn"
* Correspondingly, a metafunction class invocation is defined as invocation of its nested "fn" metafunction.
* 3. A 'metafunction operation' is an alias template that represents a function whose instantiation already yields a type.
*
* Conventions:
* - "Fn" is the name for a metafunction template template parameter.
Expand All @@ -22,7 +24,7 @@
* - "higher order" denotes a metafunction that operates on another metafunction (i.e. takes it as an argument).
*/

#include <type_traits> // std::false_type, std::true_type
#include <type_traits> // std::enable_if, std::is_same

#include "cxx_universal.h"
#include "cxx_type_traits_polyfill.h"
Expand Down Expand Up @@ -54,6 +56,16 @@ namespace sqlite_orm {
struct is_metafunction_class : polyfill::bool_constant<is_metafunction_class_v<T>> {};
#endif

/*
* Given suitable template arguments, detect whether a template is an alias template.
*
* @note It exploits the fact that the `is_specialization_of` trait is only true for class templates
* but always false for alias templates.
*/
template<template<class...> class Template, class... Args>
SQLITE_ORM_INLINE_VAR constexpr bool is_alias_template_v =
!polyfill::is_specialization_of_v<Template<Args...>, Template>;

/*
* Invoke metafunction.
*/
Expand Down Expand Up @@ -82,6 +94,32 @@ namespace sqlite_orm {
using invoke_op_t = typename wrap_op<Op, Args...>::type;
#endif

template<class AlwaysVoid, template<class...> class F, class... Args>
struct invoke_meta;

template<template<class...> class Op, class... Args>
struct invoke_meta<std::enable_if_t<is_alias_template_v<Op, Args...>>, Op, Args...> {
using type = Op<Args...>;
};

template<template<class...> class Fn, class... Args>
struct invoke_meta<std::enable_if_t<!is_alias_template_v<Fn, Args...>>, Fn, Args...> {
using type = typename Fn<Args...>::type;
};

/*
* Invoke metafunction or metafunction operation.
*
* @attention If using an alias template, be aware that it isn't recognizable whether an alias template is
* a. an alias of a class template (e.g. `template<class T> using aliased = x<T>`) or
* b. an alias of a nested template expression yielding a result (e.g. `template<class T> alias_op_t = typename x<T>::type`)
* Therefore, one cannot use `invoke_meta_t` with an alias of a class template (a.), or
* in other words, `invoke_meta_t` expects an alias of a nested template expression (b.).
* The alternative in that case is to use `invoke_fn_t`.
*/
template<template<class...> class F, class... Args>
using invoke_meta_t = typename invoke_meta<void, F, Args...>::type;

/*
* Invoke metafunction class by invoking its nested metafunction.
*/
Expand Down
2 changes: 1 addition & 1 deletion dev/implementations/storage_definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ namespace sqlite_orm {
const Table& table,
const std::vector<const table_xinfo*>& columnsToIgnore) const { // must ignore generated columns
std::vector<std::reference_wrapper<const std::string>> columnNames;
columnNames.reserve(table.count_columns_amount());
columnNames.reserve(table.template count_of<is_column>());
table.for_each_column([&columnNames, &columnsToIgnore](const column_identifier& column) {
auto& columnName = column.name;
#if __cpp_lib_ranges >= 201911L
Expand Down
2 changes: 1 addition & 1 deletion dev/implementations/table_definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace sqlite_orm {
column.is_not_null(),
std::move(dft),
column.template is<is_primary_key>(),
column.is_generated());
column.template is<is_generated_always>());
});
auto compositeKeyColumnNames = this->composite_key_columns_names();
for(size_t i = 0; i < compositeKeyColumnNames.size(); ++i) {
Expand Down
Loading

0 comments on commit 60954f2

Please sign in to comment.