Skip to content

Commit

Permalink
Merge pull request #361 from fnc12/feature/dynamic-order-by
Browse files Browse the repository at this point in the history
added dynamic order by
  • Loading branch information
fnc12 authored Aug 10, 2019
2 parents d74604e + 67e5f2c commit ac860bc
Show file tree
Hide file tree
Showing 10 changed files with 547 additions and 261 deletions.
49 changes: 49 additions & 0 deletions dev/conditions.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <string> // std::string
#include <type_traits> // std::enable_if, std::is_same
#include <vector> // std::vector

#include "collate_argument.h"
#include "constraints.h"
Expand Down Expand Up @@ -502,6 +503,49 @@ namespace sqlite_orm {
multi_order_by_t(args_type &&args_) : args(std::move(args_)) {}
};

/**
* S - storage class
*/
template<class S>
struct dynamic_order_by_t : order_by_string {
using storage_type = S;

struct entry_t : order_by_base {
std::string name;

entry_t(decltype(name) name_, int asc_desc, std::string collate_argument) :
order_by_base{asc_desc, move(collate_argument)},
name(move(name_))
{}
};

using const_iterator = typename std::vector<entry_t>::const_iterator;

dynamic_order_by_t(const storage_type &storage_): storage(storage_) {}

template<class O>
void push_back(order_by_t<O> order_by) {
auto columnName = this->storage.string_from_expression(order_by.o, true);
entries.emplace_back(move(columnName), order_by.asc_desc, move(order_by._collate_argument));
}

const_iterator begin() const {
return this->entries.begin();
}

const_iterator end() const {
return this->entries.end();
}

void clear() {
this->entries.clear();
}

protected:
std::vector<entry_t> entries;
const storage_type &storage;
};

struct group_by_string {
operator std::string() const {
return "GROUP BY";
Expand Down Expand Up @@ -1110,6 +1154,11 @@ namespace sqlite_orm {
return {std::make_tuple(std::forward<Args>(args)...)};
}

template<class S>
conditions::dynamic_order_by_t<S> dynamic_order_by(const S &storage) {
return {storage};
}

template<class ...Args>
conditions::group_by_t<Args...> group_by(Args&& ...args) {
return {std::make_tuple(std::forward<Args>(args)...)};
Expand Down
214 changes: 114 additions & 100 deletions dev/storage.h

Large diffs are not rendered by default.

35 changes: 35 additions & 0 deletions dev/storage_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,41 @@ namespace sqlite_orm {
}
}

template<class S>
std::string process_order_by(const conditions::dynamic_order_by_t<S> &orderBy) const {
std::vector<std::string> expressions;
for(auto &entry : orderBy){
std::string entryString;
{
std::stringstream ss;
ss << entry.name << " ";
if(!entry._collate_argument.empty()){
ss << "COLLATE " << entry._collate_argument << " ";
}
switch(entry.asc_desc){
case 1:
ss << "ASC";
break;
case -1:
ss << "DESC";
break;
}
entryString = ss.str();
}
expressions.push_back(move(entryString));
};
std::stringstream ss;
ss << static_cast<std::string>(orderBy) << " ";
for(size_t i = 0; i < expressions.size(); ++i) {
ss << expressions[i];
if(i < expressions.size() - 1) {
ss << ", ";
}
}
ss << " ";
return ss.str();
}

static int collate_callback(void *arg, int leftLen, const void *lhs, int rightLen, const void *rhs) {
auto &f = *(collating_function*)arg;
return f(leftLen, lhs, rightLen, rhs);
Expand Down
26 changes: 13 additions & 13 deletions dev/storage_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,23 +186,23 @@ namespace sqlite_orm {
* `column_name` has SFINAE check for type equality but `column_name_simple` has not.
*/
template<class O, class F>
std::string column_name_simple(F O::*m) {
std::string column_name_simple(F O::*m) const {
return this->table.find_column_name(m);
}

/**
* Same thing as above for getter.
*/
template<class T, typename std::enable_if<is_getter<T>::value>::type>
std::string column_name_simple(T g) {
std::string column_name_simple(T g) const {
return this->table.find_column_name(g);
}

/**
* Same thing as above for setter.
*/
template<class T, typename std::enable_if<is_setter<T>::value>::type>
std::string column_name_simple(T s) {
std::string column_name_simple(T s) const {
return this->table.find_column_name(s);
}

Expand All @@ -211,15 +211,15 @@ namespace sqlite_orm {
* skip inequal type O.
*/
template<class O, class F, class HH = typename H::object_type>
std::string column_name(F O::*m, typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) {
std::string column_name(F O::*m, typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) const {
return this->table.find_column_name(m);
}

/**
* Opposite version of function defined above. Just calls same function in superclass.
*/
template<class O, class F, class HH = typename H::object_type>
std::string column_name(F O::*m, typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) {
std::string column_name(F O::*m, typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) const {
return this->super::column_name(m);
}

Expand All @@ -228,15 +228,15 @@ namespace sqlite_orm {
* skip inequal type O.
*/
template<class O, class F, class HH = typename H::object_type>
std::string column_name(const F& (O::*g)() const, typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) {
std::string column_name(const F& (O::*g)() const, typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) const {
return this->table.find_column_name(g);
}

/**
* Opposite version of function defined above. Just calls same function in superclass.
*/
template<class O, class F, class HH = typename H::object_type>
std::string column_name(const F& (O::*g)() const, typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) {
std::string column_name(const F& (O::*g)() const, typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) const {
return this->super::column_name(g);
}

Expand All @@ -245,35 +245,35 @@ namespace sqlite_orm {
* skip inequal type O.
*/
template<class O, class F, class HH = typename H::object_type>
std::string column_name(void (O::*s)(F), typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) {
std::string column_name(void (O::*s)(F), typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) const {
return this->table.find_column_name(s);
}

/**
* Opposite version of function defined above. Just calls same function in superclass.
*/
template<class O, class F, class HH = typename H::object_type>
std::string column_name(void (O::*s)(F), typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) {
std::string column_name(void (O::*s)(F), typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) const {
return this->super::column_name(s);
}

template<class T, class F, class HH = typename H::object_type>
std::string column_name(const column_pointer<T, F> &c, typename std::enable_if<std::is_same<T, HH>::value>::type * = nullptr) {
std::string column_name(const column_pointer<T, F> &c, typename std::enable_if<std::is_same<T, HH>::value>::type * = nullptr) const {
return this->column_name_simple(c.field);
}

template<class T, class F, class HH = typename H::object_type>
std::string column_name(const column_pointer<T, F> &c, typename std::enable_if<!std::is_same<T, HH>::value>::type * = nullptr) {
std::string column_name(const column_pointer<T, F> &c, typename std::enable_if<!std::is_same<T, HH>::value>::type * = nullptr) const {
return this->super::column_name(c);
}

template<class O, class HH = typename H::object_type>
auto& get_impl(typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) {
auto& get_impl(typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) const {
return *this;
}

template<class O, class HH = typename H::object_type>
auto& get_impl(typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) {
auto& get_impl(typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) const {
return this->super::template get_impl<O>();
}

Expand Down
26 changes: 13 additions & 13 deletions dev/table.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ namespace sqlite_orm {
* Function used to get field value from object by mapped member pointer/setter/getter
*/
template<class F, class C>
const F* get_object_field_pointer(const object_type &obj, C c) {
const F* get_object_field_pointer(const object_type &obj, C c) const {
const F *res = nullptr;
this->for_each_column_with_field_type<F>([&res, &c, &obj](auto &col){
using column_type = typename std::remove_reference<decltype(col)>::type;
Expand Down Expand Up @@ -90,7 +90,7 @@ namespace sqlite_orm {
/**
* @return vector of column names of table.
*/
std::vector<std::string> column_names() {
std::vector<std::string> column_names() const {
std::vector<std::string> res;
this->for_each_column([&res](auto &c){
res.push_back(c.name);
Expand All @@ -102,22 +102,22 @@ namespace sqlite_orm {
* Calls **l** with every primary key dedicated constraint
*/
template<class L>
void for_each_primary_key(const L &l) {
void for_each_primary_key(const L &l) const {
iterate_tuple(this->columns, [&l](auto &column){
using column_type = typename std::decay<decltype(column)>::type;
static_if<internal::is_primary_key<column_type>{}>(l)(column);
});
}

std::vector<std::string> composite_key_columns_names() {
std::vector<std::string> composite_key_columns_names() const {
std::vector<std::string> res;
this->for_each_primary_key([this, &res](auto &c){
res = this->composite_key_columns_names(c);
});
return res;
}

std::vector<std::string> primary_key_column_names() {
std::vector<std::string> primary_key_column_names() const {
std::vector<std::string> res;
this->for_each_column_with<constraints::primary_key_t<>>([&res](auto &c){
res.push_back(c.name);
Expand All @@ -129,7 +129,7 @@ namespace sqlite_orm {
}

template<class ...Args>
std::vector<std::string> composite_key_columns_names(const constraints::primary_key_t<Args...> &pk) {
std::vector<std::string> composite_key_columns_names(const constraints::primary_key_t<Args...> &pk) const {
std::vector<std::string> res;
using pk_columns_tuple = decltype(pk.columns);
res.reserve(std::tuple_size<pk_columns_tuple>::value);
Expand All @@ -147,7 +147,7 @@ namespace sqlite_orm {
class F,
class O,
typename = typename std::enable_if<std::is_member_pointer<F O::*>::value && !std::is_member_function_pointer<F O::*>::value>::type>
std::string find_column_name(F O::*m) {
std::string find_column_name(F O::*m) const {
std::string res;
this->template for_each_column_with_field_type<F>([&res, m](auto c) {
if(c.member_pointer == m) {
Expand All @@ -162,7 +162,7 @@ namespace sqlite_orm {
* @return column name or empty string if nothing found.
*/
template<class G>
std::string find_column_name(G getter, typename std::enable_if<is_getter<G>::value>::type * = nullptr) {
std::string find_column_name(G getter, typename std::enable_if<is_getter<G>::value>::type * = nullptr) const {
std::string res;
using field_type = typename getter_traits<G>::field_type;
this->template for_each_column_with_field_type<field_type>([&res, getter](auto c) {
Expand All @@ -178,7 +178,7 @@ namespace sqlite_orm {
* @return column name or empty string if nothing found.
*/
template<class S>
std::string find_column_name(S setter, typename std::enable_if<is_setter<S>::value>::type * = nullptr) {
std::string find_column_name(S setter, typename std::enable_if<is_setter<S>::value>::type * = nullptr) const {
std::string res;
using field_type = typename setter_traits<S>::field_type;
this->template for_each_column_with_field_type<field_type>([&res, setter](auto c) {
Expand Down Expand Up @@ -211,20 +211,20 @@ namespace sqlite_orm {
* @param l Lambda to be called per column itself. Must have signature like this [] (auto col) -> void {}
*/
template<class L>
void for_each_column(const L &l) {
void for_each_column(const L &l) const {
iterate_tuple(this->columns, [&l](auto &column){
using column_type = typename std::decay<decltype(column)>::type;
static_if<internal::is_column<column_type>{}>(l)(column);
});
}

template<class L>
void for_each_column_with_constraints(const L &l) {
void for_each_column_with_constraints(const L &l) const {
iterate_tuple(this->columns, l);
}

template<class F, class L>
void for_each_column_with_field_type(const L &l) {
void for_each_column_with_field_type(const L &l) const {
iterate_tuple(this->columns, [&l](auto &column){
using column_type = typename std::decay<decltype(column)>::type;
static_if<std::is_same<F, typename column_type::field_type>{}>(l)(column);
Expand All @@ -238,7 +238,7 @@ namespace sqlite_orm {
* @param l Lambda to be called per column itself. Must have signature like this [] (auto col) -> void {}
*/
template<class Op, class L>
void for_each_column_with(const L &l) {
void for_each_column_with(const L &l) const {
using tuple_helper::tuple_contains_type;
iterate_tuple(this->columns, [&l](auto &column){
using column_type = typename std::decay<decltype(column)>::type;
Expand Down
Loading

0 comments on commit ac860bc

Please sign in to comment.