Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions dev/ast/cross_join.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include "../functional/cxx_type_traits_polyfill.h"

namespace sqlite_orm {
namespace internal {

/**
* CROSS JOIN holder.
* T is joined type which represents any mapped table.
*/
template<class T>
struct cross_join_t {
using type = T;
};
}
}

SQLITE_ORM_EXPORT namespace sqlite_orm {

/**
* CROSS JOIN function. Usage:
* `cross_join<User>();`
*/
template<class T>
internal::cross_join_t<T> cross_join() {
return {};
}
}
37 changes: 9 additions & 28 deletions dev/conditions.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@
#include "tags.h"
#include "type_printer.h"
#include "literal.h"
#include "ast/cross_join.h"

namespace sqlite_orm {

namespace internal {

/**
* Collated something
*/
Expand Down Expand Up @@ -595,33 +595,12 @@ namespace sqlite_orm {
glob_t(arg_t arg_, pattern_t pattern_) : arg(std::move(arg_)), pattern(std::move(pattern_)) {}
};

struct cross_join_string {
operator std::string() const {
return "CROSS JOIN";
}
};

/**
* CROSS JOIN holder.
* T is joined type which represents any mapped table.
*/
template<class T>
struct cross_join_t : cross_join_string {
using type = T;
};

struct natural_join_string {
operator std::string() const {
return "NATURAL JOIN";
}
};

/**
* NATURAL JOIN holder.
* T is joined type which represents any mapped table.
*/
template<class T>
struct natural_join_t : natural_join_string {
struct natural_join_t {
using type = T;
};

Expand Down Expand Up @@ -750,6 +729,13 @@ namespace sqlite_orm {

template<class T>
using is_constrained_join = polyfill::is_detected<on_type_t, T>;

template<class T>
using is_any_join = mpl::invoke_t<mpl::disjunction<check_if<is_constrained_join>,
check_if_is_template<cross_join_t>,
check_if_is_template<natural_join_t>>,
T>;

}
}

Expand Down Expand Up @@ -907,11 +893,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm {
return {std::move(t)};
}

template<class T>
internal::cross_join_t<T> cross_join() {
return {};
}

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
template<orm_refers_to_recordset auto alias>
auto cross_join() {
Expand Down
33 changes: 24 additions & 9 deletions dev/statement_serializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -1946,17 +1946,24 @@ namespace sqlite_orm {
using conditions_tuple = typename statement_type::conditions_type;
constexpr bool hasExplicitFrom = tuple_has<conditions_tuple, is_from>::value;
if constexpr (!hasExplicitFrom) {
using joins_index_sequence = filter_tuple_sequence_t<conditions_tuple, is_any_join>;

auto tableNames = collect_table_names(sel, context);
using joins_index_sequence = filter_tuple_sequence_t<conditions_tuple, is_constrained_join>;
// deduplicate table names of constrained join statements
iterate_tuple(sel.conditions, joins_index_sequence{}, [&tableNames, &context](auto& join) {
using original_join_type = typename std::remove_reference_t<decltype(join)>::type;
using cross_join_type = mapped_type_proxy_t<original_join_type>;
std::pair<const std::string&, std::string> tableNameWithAlias{
lookup_table_name<cross_join_type>(context.db_objects),
alias_extractor<original_join_type>::as_alias()};
tableNames.erase(tableNameWithAlias);
using join_type = mapped_type_proxy_t<original_join_type>;

const auto& tableName = lookup_table_name<join_type>(context.db_objects);
auto it = std::find_if(tableNames.begin(), tableNames.end(), [&tableName](const auto& pair) {
return pair.first == tableName;
});
if (it == tableNames.end()) {
return;
}
tableNames.erase(it);
});

if (!tableNames.empty() && !is_compound_operator<T>::value) {
ss << " FROM " << streaming_identifiers(tableNames);
}
Expand Down Expand Up @@ -2290,11 +2297,19 @@ namespace sqlite_orm {
using statement_type = Join;

template<class Ctx>
SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& join,
SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& /*join*/,
const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP {
std::stringstream ss;
ss << static_cast<std::string>(join) << " "
<< streaming_identifier(lookup_table_name<type_t<Join>>(context.db_objects));
if constexpr (polyfill::is_specialization_of<statement_type, cross_join_t>::value) {
ss << "CROSS JOIN";
} else if constexpr (polyfill::is_specialization_of<statement_type, natural_join_t>::value) {
ss << "NATURAL JOIN";
} else {
static_assert(polyfill::always_false_v<statement_type>);
}
ss << " "
<< streaming_identifier(
lookup_table_name<mapped_type_proxy_t<type_t<statement_type>>>(context.db_objects));
return ss.str();
}
};
Expand Down
98 changes: 61 additions & 37 deletions include/sqlite_orm/sqlite_orm.h
Original file line number Diff line number Diff line change
Expand Up @@ -5197,10 +5197,38 @@ namespace sqlite_orm {
}
}

namespace sqlite_orm {
// #include "ast/cross_join.h"
// #include "../functional/cxx_type_traits_polyfill.h"

namespace sqlite_orm {
namespace internal {

/**
* CROSS JOIN holder.
* T is joined type which represents any mapped table.
*/
template<class T>
struct cross_join_t {
using type = T;
};
}
}

SQLITE_ORM_EXPORT namespace sqlite_orm {

/**
* CROSS JOIN function. Usage:
* `cross_join<User>();`
*/
template<class T>
internal::cross_join_t<T> cross_join() {
return {};
}
}

namespace sqlite_orm {

namespace internal {
/**
* Collated something
*/
Expand Down Expand Up @@ -5765,33 +5793,12 @@ namespace sqlite_orm {
glob_t(arg_t arg_, pattern_t pattern_) : arg(std::move(arg_)), pattern(std::move(pattern_)) {}
};

struct cross_join_string {
operator std::string() const {
return "CROSS JOIN";
}
};

/**
* CROSS JOIN holder.
* T is joined type which represents any mapped table.
*/
template<class T>
struct cross_join_t : cross_join_string {
using type = T;
};

struct natural_join_string {
operator std::string() const {
return "NATURAL JOIN";
}
};

/**
* NATURAL JOIN holder.
* T is joined type which represents any mapped table.
*/
template<class T>
struct natural_join_t : natural_join_string {
struct natural_join_t {
using type = T;
};

Expand Down Expand Up @@ -5920,6 +5927,13 @@ namespace sqlite_orm {

template<class T>
using is_constrained_join = polyfill::is_detected<on_type_t, T>;

template<class T>
using is_any_join = mpl::invoke_t<mpl::disjunction<check_if<is_constrained_join>,
check_if_is_template<cross_join_t>,
check_if_is_template<natural_join_t>>,
T>;

}
}

Expand Down Expand Up @@ -6077,11 +6091,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm {
return {std::move(t)};
}

template<class T>
internal::cross_join_t<T> cross_join() {
return {};
}

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
template<orm_refers_to_recordset auto alias>
auto cross_join() {
Expand Down Expand Up @@ -22358,17 +22367,24 @@ namespace sqlite_orm {
using conditions_tuple = typename statement_type::conditions_type;
constexpr bool hasExplicitFrom = tuple_has<conditions_tuple, is_from>::value;
if constexpr (!hasExplicitFrom) {
using joins_index_sequence = filter_tuple_sequence_t<conditions_tuple, is_any_join>;

auto tableNames = collect_table_names(sel, context);
using joins_index_sequence = filter_tuple_sequence_t<conditions_tuple, is_constrained_join>;
// deduplicate table names of constrained join statements
iterate_tuple(sel.conditions, joins_index_sequence{}, [&tableNames, &context](auto& join) {
using original_join_type = typename std::remove_reference_t<decltype(join)>::type;
using cross_join_type = mapped_type_proxy_t<original_join_type>;
std::pair<const std::string&, std::string> tableNameWithAlias{
lookup_table_name<cross_join_type>(context.db_objects),
alias_extractor<original_join_type>::as_alias()};
tableNames.erase(tableNameWithAlias);
using join_type = mapped_type_proxy_t<original_join_type>;

const auto& tableName = lookup_table_name<join_type>(context.db_objects);
auto it = std::find_if(tableNames.begin(), tableNames.end(), [&tableName](const auto& pair) {
return pair.first == tableName;
});
if (it == tableNames.end()) {
return;
}
tableNames.erase(it);
});

if (!tableNames.empty() && !is_compound_operator<T>::value) {
ss << " FROM " << streaming_identifiers(tableNames);
}
Expand Down Expand Up @@ -22702,11 +22718,19 @@ namespace sqlite_orm {
using statement_type = Join;

template<class Ctx>
SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& join,
SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& /*join*/,
const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP {
std::stringstream ss;
ss << static_cast<std::string>(join) << " "
<< streaming_identifier(lookup_table_name<type_t<Join>>(context.db_objects));
if constexpr (polyfill::is_specialization_of<statement_type, cross_join_t>::value) {
ss << "CROSS JOIN";
} else if constexpr (polyfill::is_specialization_of<statement_type, natural_join_t>::value) {
ss << "NATURAL JOIN";
} else {
static_assert(polyfill::always_false_v<statement_type>);
}
ss << " "
<< streaming_identifier(
lookup_table_name<mapped_type_proxy_t<type_t<statement_type>>>(context.db_objects));
return ss.str();
}
};
Expand Down
29 changes: 29 additions & 0 deletions tests/statement_serializer_tests/ast/cross_join.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include <sqlite_orm/sqlite_orm.h>
#include <catch2/catch_all.hpp>

using namespace sqlite_orm;

TEST_CASE("cross_join") {
using internal::serialize;

struct User {
int id = 0;
std::string name;
};
auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name));
using db_objects_t = internal::db_objects_tuple<decltype(table)>;
auto dbObjects = db_objects_t{table};
using context_t = internal::serializer_context<db_objects_t>;
context_t context{dbObjects};
std::string value;
SECTION("straight") {
auto node = cross_join<User>();
value = serialize(node, context);
}
SECTION("alias") {
using user_s = alias_s<User>;
auto node = cross_join<user_s>();
value = serialize(node, context);
}
REQUIRE(value == R"(CROSS JOIN "users")");
}
29 changes: 29 additions & 0 deletions tests/statement_serializer_tests/ast/natural_join.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include <sqlite_orm/sqlite_orm.h>
#include <catch2/catch_all.hpp>

using namespace sqlite_orm;

TEST_CASE("natural_join") {
using internal::serialize;

struct User {
int id = 0;
std::string name;
};
auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name));
using db_objects_t = internal::db_objects_tuple<decltype(table)>;
auto dbObjects = db_objects_t{table};
using context_t = internal::serializer_context<db_objects_t>;
context_t context{dbObjects};
std::string value;
SECTION("straight") {
auto node = natural_join<User>();
value = serialize(node, context);
}
SECTION("alias") {
using user_s = alias_s<User>;
auto node = natural_join<user_s>();
value = serialize(node, context);
}
REQUIRE(value == R"(NATURAL JOIN "users")");
}
Loading