Skip to content

Commit

Permalink
Merge pull request #1278 from fnc12/feature/objects_inside_columns
Browse files Browse the repository at this point in the history
Support for selecting multiple objects and unmapped but named structs
  • Loading branch information
trueqbit authored May 5, 2024
2 parents 50cb629 + 8c13c97 commit ed5653e
Show file tree
Hide file tree
Showing 27 changed files with 988 additions and 321 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.03.25
git fetch --tags && git checkout 2024.04.26
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.03.25
git fetch --tags && git checkout 2024.04.26
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.03.25 https://github.com/microsoft/vcpkg.git $HOME/vcpkg
git clone --depth 1 --branch 2024.04.26 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
29 changes: 2 additions & 27 deletions dev/alias_traits.h
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
#pragma once

#include <type_traits> // std::remove_const, std::is_base_of, std::is_same, std::type_identity
#include <type_traits> // std::is_base_of, std::is_same
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
#include <concepts>
#endif

#include "functional/cxx_universal.h"
#include "functional/cxx_type_traits_polyfill.h"
#include "type_traits.h"
#include "table_reference.h"

namespace sqlite_orm {

Expand Down Expand Up @@ -51,24 +52,6 @@ namespace sqlite_orm {
template<class A>
struct is_table_alias : polyfill::bool_constant<is_table_alias_v<A>> {};

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
/*
* Identity wrapper around a mapped object, facilitating uniform column pointer expressions.
*/
template<class O>
struct table_reference : std::type_identity<O> {};

template<class RecordSet>
struct decay_table_reference : std::remove_const<RecordSet> {};
template<class O>
struct decay_table_reference<table_reference<O>> : std::type_identity<O> {};
template<class O>
struct decay_table_reference<const table_reference<O>> : std::type_identity<O> {};

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>
Expand Down Expand Up @@ -115,14 +98,6 @@ namespace sqlite_orm {
template<class A>
concept orm_table_alias = (orm_recordset_alias<A> && !std::same_as<typename A::type, std::remove_const_t<A>>);

/** @short Specifies that a type is a reference of a concrete table, especially of a derived class.
*
* A concrete table reference has the following traits:
* - specialization of `table_reference`, whose `type` typename references a mapped object.
*/
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:
Expand Down
6 changes: 3 additions & 3 deletions dev/ast_iterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,9 @@ namespace sqlite_orm {
}
};

template<class... Args>
struct ast_iterator<columns_t<Args...>, void> {
using node_type = columns_t<Args...>;
template<class C>
struct ast_iterator<C, std::enable_if_t<polyfill::disjunction<is_columns<C>, is_struct<C>>::value>> {
using node_type = C;

template<class L>
void operator()(const node_type& cols, L& lambda) const {
Expand Down
9 changes: 9 additions & 0 deletions dev/column_expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,15 @@ namespace sqlite_orm {
using type = std::tuple<column_expression_of_t<DBOs, std::decay_t<Args>>...>;
};

/**
* Resolve multiple columns.
* struct_t<T, C...> -> tuple<ColExpr...>
*/
template<class DBOs, class T, class... Args>
struct column_expression_type<DBOs, struct_t<T, Args...>, void> {
using type = std::tuple<column_expression_of_t<DBOs, std::decay_t<Args>>...>;
};

/**
* Resolve column(s) of subselect.
* select_t<E, Args...> -> ColExpr, tuple<ColExpr....>
Expand Down
14 changes: 14 additions & 0 deletions dev/column_names_getter.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,20 @@ namespace sqlite_orm {
return this->collectedExpressions;
}

template<class T, class... Args, class Ctx>
std::vector<std::string>& operator()(const struct_t<T, Args...>& cols, const Ctx& context) {
this->collectedExpressions.reserve(this->collectedExpressions.size() + cols.count);
iterate_tuple(cols.columns, [this, &context](auto& colExpr) {
(*this)(colExpr, context);
});
// note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order
if(tuple_has_template<typename struct_t<T, Args...>::columns_type, asterisk_t>::value &&
this->collectedExpressions.capacity() > this->collectedExpressions.size()) {
this->collectedExpressions.shrink_to_fit();
}
return this->collectedExpressions;
}

std::vector<std::string> collectedExpressions;
};

Expand Down
1 change: 1 addition & 0 deletions dev/column_pointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "functional/cxx_core_features.h"
#include "functional/cxx_type_traits_polyfill.h"
#include "type_traits.h"
#include "table_reference.h"
#include "alias_traits.h"
#include "tags.h"

Expand Down
8 changes: 7 additions & 1 deletion dev/column_result.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "select_constraints.h"
#include "operators.h"
#include "rowid.h"
#include "column_result_proxy.h"
#include "alias.h"
#include "cte_types.h"
#include "storage_traits.h"
Expand Down Expand Up @@ -246,6 +247,11 @@ namespace sqlite_orm {
struct column_result_t<DBOs, columns_t<Args...>, void>
: conc_tuple<tuplify_t<column_result_of_t<DBOs, std::decay_t<Args>>>...> {};

template<class DBOs, class T, class... Args>
struct column_result_t<DBOs, struct_t<T, Args...>, void> {
using type = structure<T, tuple_cat_t<tuplify_t<column_result_of_t<DBOs, std::decay_t<Args>>>...>>;
};

template<class DBOs, class T, class... Args>
struct column_result_t<DBOs, select_t<T, Args...>> : column_result_t<DBOs, T> {};

Expand Down Expand Up @@ -297,7 +303,7 @@ namespace sqlite_orm {

template<class DBOs, class T>
struct column_result_t<DBOs, object_t<T>, void> {
using type = T;
using type = table_reference<T>;
};

template<class DBOs, class T, class E>
Expand Down
41 changes: 41 additions & 0 deletions dev/column_result_proxy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#pragma once

#include "type_traits.h"
#include "table_reference.h"

namespace sqlite_orm {
namespace internal {

/*
* Holder for the type of an unmapped aggregate/structure/object to be constructed ad-hoc from column results.
* `T` must be constructible using direct-list-initialization.
*/
template<class T, class ColResults>
struct structure {
using type = T;
};
}
}

namespace sqlite_orm {
namespace internal {

template<class T, class SFINAE = void>
struct column_result_proxy : std::remove_const<T> {};

/*
* Unwrap `table_reference`
*/
template<class P>
struct column_result_proxy<P, match_if<is_table_reference, P>> : decay_table_ref<P> {};

/*
* Unwrap `structure`
*/
template<class P>
struct column_result_proxy<P, match_specialization_of<P, structure>> : P {};

template<class T>
using column_result_proxy_t = typename column_result_proxy<T>::type;
}
}
11 changes: 6 additions & 5 deletions dev/conditions.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "serializer_context.h"
#include "serialize_result_type.h"
#include "tags.h"
#include "table_reference.h"
#include "alias_traits.h"
#include "expression.h"
#include "column_pointer.h"
Expand Down Expand Up @@ -837,7 +838,7 @@ namespace sqlite_orm {
*/
template<orm_refers_to_recordset auto... recordsets>
auto from() {
return from<internal::decay_table_reference_t<recordsets>...>();
return from<internal::auto_decay_table_ref_t<recordsets>...>();
}
#endif

Expand Down Expand Up @@ -991,7 +992,7 @@ namespace sqlite_orm {
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
template<orm_refers_to_recordset auto alias, class On>
auto left_join(On on) {
return left_join<internal::decay_table_reference_t<alias>, On>(std::move(on));
return left_join<internal::auto_decay_table_ref_t<alias>, On>(std::move(on));
}
#endif

Expand All @@ -1003,7 +1004,7 @@ namespace sqlite_orm {
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
template<orm_refers_to_recordset auto alias, class On>
auto join(On on) {
return join<internal::decay_table_reference_t<alias>, On>(std::move(on));
return join<internal::auto_decay_table_ref_t<alias>, On>(std::move(on));
}
#endif

Expand All @@ -1015,7 +1016,7 @@ namespace sqlite_orm {
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
template<orm_refers_to_recordset auto alias, class On>
auto left_outer_join(On on) {
return left_outer_join<internal::decay_table_reference_t<alias>, On>(std::move(on));
return left_outer_join<internal::auto_decay_table_ref_t<alias>, On>(std::move(on));
}
#endif

Expand All @@ -1027,7 +1028,7 @@ namespace sqlite_orm {
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
template<orm_refers_to_recordset auto alias, class On>
auto inner_join(On on) {
return inner_join<internal::decay_table_reference_t<alias>, On>(std::move(on));
return inner_join<internal::auto_decay_table_ref_t<alias>, On>(std::move(on));
}
#endif

Expand Down
3 changes: 2 additions & 1 deletion dev/core_functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "serialize_result_type.h"
#include "operators.h"
#include "tags.h"
#include "table_reference.h"
#include "ast/into.h"

namespace sqlite_orm {
Expand Down Expand Up @@ -1853,7 +1854,7 @@ namespace sqlite_orm {
*/
template<orm_refers_to_recordset auto mapped>
auto count() {
return count<internal::decay_table_reference_t<mapped>>();
return count<internal::auto_decay_table_ref_t<mapped>>();
}
#endif

Expand Down
6 changes: 6 additions & 0 deletions dev/cte_column_names_collector.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ namespace sqlite_orm {
static_assert(polyfill::always_false_v<Object>, "Selecting an object in a subselect is not allowed.");
};

// No CTE for object expressions.
template<class Object>
struct cte_column_names_collector<Object, match_if<is_struct, Object>> {
static_assert(polyfill::always_false_v<Object>, "Repacking columns in a subselect is not allowed.");
};

template<class Columns>
struct cte_column_names_collector<Columns, match_specialization_of<Columns, columns_t>> {
using expression_type = Columns;
Expand Down
1 change: 1 addition & 0 deletions dev/mapped_type_proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <type_traits> // std::remove_const

#include "type_traits.h"
#include "table_reference.h"
#include "alias_traits.h"

namespace sqlite_orm {
Expand Down
3 changes: 3 additions & 0 deletions dev/node_tuple.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ namespace sqlite_orm {
template<class... Args>
struct node_tuple<columns_t<Args...>, void> : node_tuple_for<Args...> {};

template<class T, class... Args>
struct node_tuple<struct_t<T, Args...>, void> : node_tuple_for<Args...> {};

template<class L, class A>
struct node_tuple<dynamic_in_t<L, A>, void> : node_tuple_for<L, A> {};

Expand Down
45 changes: 40 additions & 5 deletions dev/object_from_column_builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,26 @@

#include <sqlite3.h>
#include <type_traits> // std::is_member_object_pointer
#include <utility> // std::move

#include "functional/static_magic.h"
#include "member_traits/member_traits.h"
#include "table_reference.h"
#include "row_extractor.h"
#include "schema/column.h"
#include "storage_lookup.h"

namespace sqlite_orm {

namespace internal {

struct object_from_column_builder_base {
sqlite3_stmt* stmt = nullptr;
int index = 0;
int columnIndex = -1;

#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED
object_from_column_builder_base(sqlite3_stmt* stmt) : stmt{stmt} {}
object_from_column_builder_base(sqlite3_stmt* stmt, int columnIndex = -1) :
stmt{stmt}, columnIndex{columnIndex} {}
#endif
};

Expand All @@ -28,13 +34,13 @@ namespace sqlite_orm {

object_type& object;

object_from_column_builder(object_type& object_, sqlite3_stmt* stmt_) :
object_from_column_builder_base{stmt_}, object(object_) {}
object_from_column_builder(object_type& object_, sqlite3_stmt* stmt_, int nextColumnIndex = 0) :
object_from_column_builder_base{stmt_, nextColumnIndex - 1}, object(object_) {}

template<class G, class S>
void operator()(const column_field<G, S>& column) {
const auto rowExtractor = row_value_extractor<member_field_type_t<G>>();
auto value = rowExtractor.extract(this->stmt, this->index++);
auto value = rowExtractor.extract(this->stmt, ++this->columnIndex);
static_if<std::is_member_object_pointer<G>::value>(
[&value, &object = this->object](const auto& column) {
object.*column.member_pointer = std::move(value);
Expand All @@ -44,5 +50,34 @@ namespace sqlite_orm {
})(column);
}
};

/**
* Specialization for a table reference.
*
* This plays together with `column_result_of_t`, which returns `object_t<O>` as `table_referenece<O>`
*/
template<class O, class DBOs>
struct struct_extractor<table_reference<O>, DBOs> {
const DBOs& db_objects;

O extract(const char* columnText) const = delete;

// note: expects to be called only from the top level, and therefore discards the index
O extract(sqlite3_stmt* stmt, int&& /*nextColumnIndex*/ = 0) const {
int columnIndex = 0;
return this->extract(stmt, columnIndex);
}

O extract(sqlite3_stmt* stmt, int& columnIndex) const {
O obj;
object_from_column_builder<O> builder{obj, stmt, columnIndex};
auto& table = pick_table<O>(this->db_objects);
table.for_each_column(builder);
columnIndex = builder.columnIndex;
return obj;
}

O extract(sqlite3_value* value) const = delete;
};
}
}
Loading

0 comments on commit ed5653e

Please sign in to comment.