Skip to content

Commit

Permalink
Generalize the counting of columns
Browse files Browse the repository at this point in the history
  • Loading branch information
trueqbit committed Oct 24, 2023
1 parent db83d84 commit a2f37cd
Show file tree
Hide file tree
Showing 19 changed files with 429 additions and 331 deletions.
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.columns_count);
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
60 changes: 26 additions & 34 deletions dev/constraints.h
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,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 +412,7 @@ namespace sqlite_orm {
return {std::move(this->expression), this->full, storage_type::stored};
}
};
#endif

struct null_t {};

Expand All @@ -420,7 +422,12 @@ 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>>;
Expand All @@ -438,10 +445,15 @@ namespace sqlite_orm {
SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = is_primary_key<T>::value;

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,37 +470,17 @@ namespace sqlite_orm {
};

template<class T>
struct primary_key_colums_count_t;

template<class... Cs>
struct primary_key_colums_count_t<primary_key_t<Cs...>>
: std::integral_constant<int, static_cast<int>(sizeof...(Cs))> {};

template<class... Pks>
struct flatten_primry_keys_columns {
using columns_tuple = typename conc_tuple<typename Pks::columns_tuple...>::type;
};

template<class... Pks>
struct flatten_primry_keys_columns<std::tuple<Pks...>> : flatten_primry_keys_columns<Pks...> {};

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_template<primary_key_with_autoincrement>,
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/functional/index_sequence_util.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#pragma once

#include <utility> // std::index_sequence, std::make_index_sequence
#include <utility> // std::index_sequence

#include "../functional/cxx_universal.h"

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.columns_count);
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
16 changes: 1 addition & 15 deletions dev/schema/column.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,24 +76,13 @@ namespace sqlite_orm {
constraints_type constraints;

/**
* Checks whether contraints are of trait `Trait`
* Checks whether contraints contain specified type.
*/
template<template<class...> class Trait>
constexpr static bool is() {
return tuple_has<Trait, constraints_type>::value;
}

constexpr static bool is_primary_key =
mpl::invoke_t<check_if_tuple_has<internal::is_primary_key>, constraints_type>::value;

constexpr bool is_generated() const {
#if SQLITE_VERSION_NUMBER >= 3031000
return is<is_generated_always>();
#else
return false;
#endif
}

/**
* Simplified interface for `DEFAULT` constraint
* @return string representation of default value if it exists otherwise nullptr
Expand All @@ -118,9 +107,6 @@ namespace sqlite_orm {
template<class T>
SQLITE_ORM_INLINE_VAR constexpr bool is_column_v = polyfill::is_specialization_of_v<T, column_t>;

template<class T>
using is_primary_key_column = polyfill::bool_constant<T::is_primary_key>;

template<class T>
using is_column = polyfill::bool_constant<is_column_v<T>>;

Expand Down
55 changes: 26 additions & 29 deletions dev/schema/table.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "../tuple_helper/tuple_filter.h"
#include "../tuple_helper/tuple_traits.h"
#include "../tuple_helper/tuple_iteration.h"
#include "../tuple_helper/tuple_transformer.h"
#include "../member_traits/member_traits.h"
#include "../typed_comparator.h"
#include "../type_traits.h"
Expand All @@ -41,16 +42,8 @@ namespace sqlite_orm {
struct table_t : basic_table {
using object_type = O;
using elements_type = std::tuple<Cs...>;
using columns_tuple = filter_tuple_t<elements_type, is_column>;
using dedicated_primary_keys_tuple = filter_tuple_t<elements_type, is_primary_key>;
using dedicated_primary_keys_columns_tuple =
typename flatten_primry_keys_columns<dedicated_primary_keys_tuple>::columns_tuple;

static constexpr bool is_without_rowid_v = WithoutRowId;
static constexpr int columns_count = static_cast<int>(std::tuple_size<columns_tuple>::value);
static constexpr int primary_key_columns_count = count_tuple<columns_tuple, is_primary_key_column>::value;
static constexpr int dedicated_primary_key_columns_count =
static_cast<int>(std::tuple_size<dedicated_primary_keys_columns_tuple>::value);

using is_without_rowid = polyfill::bool_constant<is_without_rowid_v>;

Expand All @@ -65,15 +58,32 @@ namespace sqlite_orm {
return {this->name, this->elements};
}

/**
* Returns foreign keys count in table definition
/*
* Returns the number of elements of the specified type.
*/
static constexpr int foreign_keys_count =
#if SQLITE_VERSION_NUMBER >= 3006019
static_cast<int>(filter_tuple_sequence_t<elements_type, is_foreign_key>::size());
#else
0;
#endif
template<template<class...> class Trait>
static constexpr int count_of() {
using sequence_of = filter_tuple_sequence_t<elements_type, Trait>;
return int(sequence_of::size());
}

/*
* Returns the number of columns having the specified constraint trait.
*/
template<template<class...> class Trait>
static constexpr int count_of_columns_with() {
using filtered_index_sequence = col_index_sequence_with<elements_type, Trait>;
return int(filtered_index_sequence::size());
}

/*
* Returns the number of columns having the specified constraint trait.
*/
template<template<class...> class Trait>
static constexpr int count_of_columns_excluding() {
using excluded_col_index_sequence = col_index_sequence_excluding<elements_type, Trait>;
return int(excluded_col_index_sequence::size());
}

/**
* Function used to get field value from object by mapped member pointer/setter/getter.
Expand Down Expand Up @@ -195,19 +205,6 @@ namespace sqlite_orm {
return res;
}

/**
* Counts and returns amount of columns without GENERATED ALWAYS constraints. Skips table constraints.
*/
constexpr int non_generated_columns_count() const {
#if SQLITE_VERSION_NUMBER >= 3031000
using non_generated_col_index_sequence =
col_index_sequence_excluding<elements_type, is_generated_always>;
return int(non_generated_col_index_sequence::size());
#else
return this->columns_count;
#endif
}

/**
* Call passed lambda with all defined foreign keys.
* @param lambda Lambda called for each column. Function signature: `void(auto& column)`
Expand Down
2 changes: 1 addition & 1 deletion dev/statement_serializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -1430,7 +1430,7 @@ namespace sqlite_orm {
ss << "REPLACE INTO " << streaming_identifier(table.name) << " ("
<< streaming_non_generated_column_names(table) << ")";
const auto valuesCount = std::distance(rep.range.first, rep.range.second);
const auto columnsCount = table.non_generated_columns_count();
const auto columnsCount = table.template count_of_columns_excluding<is_generated_always>();
ss << " VALUES " << streaming_values_placeholders(columnsCount, valuesCount);
return ss.str();
}
Expand Down
20 changes: 15 additions & 5 deletions dev/storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "functional/mpl.h"
#include "tuple_helper/tuple_traits.h"
#include "tuple_helper/tuple_filter.h"
#include "tuple_helper/tuple_transformer.h"
#include "tuple_helper/tuple_iteration.h"
#include "type_traits.h"
#include "alias.h"
Expand Down Expand Up @@ -173,14 +174,23 @@ namespace sqlite_orm {

template<class O>
void assert_updatable_type() const {
#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED)
using Table = storage_pick_table_t<O, db_objects_type>;
constexpr int primaryKeyColumnsCount =
Table::primary_key_columns_count + Table::dedicated_primary_key_columns_count;
constexpr int nonPrimaryKeysColumnsCount = Table::columns_count - primaryKeyColumnsCount;
static_assert(primaryKeyColumnsCount > 0, "Type with no primary keys can't be updated");
using elements_type = elements_type_t<Table>;
using col_index_sequence = filter_tuple_sequence_t<elements_type, is_column>;
using pk_index_sequence = filter_tuple_sequence_t<elements_type, is_primary_key>;
using pkcol_index_sequence = col_index_sequence_with<elements_type, is_primary_key>;
constexpr size_t dedicatedPrimaryKeyColumnsCount =
nested_tuple_size_for_t<columns_tuple_t, elements_type, pk_index_sequence>::value;

constexpr size_t primaryKeyColumnsCount =
dedicatedPrimaryKeyColumnsCount + pkcol_index_sequence::size();
constexpr ptrdiff_t nonPrimaryKeysColumnsCount = col_index_sequence::size() - primaryKeyColumnsCount;
static_assert(primaryKeyColumnsCount > 0, "A table without primary keys cannot be updated");
static_assert(
nonPrimaryKeysColumnsCount > 0,
"Type with primary keys only can't be updated. You need at least 1 non-primary key column");
"A table with only primary keys cannot be updated. You need at least 1 non-primary key column");
#endif
}

template<class O,
Expand Down
2 changes: 1 addition & 1 deletion dev/storage_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace sqlite_orm {
int foreign_keys_count(const DBOs& dbObjects) {
int res = 0;
iterate_tuple<true>(dbObjects, tables_index_sequence<DBOs>{}, [&res](const auto& table) {
res += table.foreign_keys_count;
res += table.template count_of<is_foreign_key>();
});
return res;
}
Expand Down
2 changes: 1 addition & 1 deletion dev/tuple_helper/tuple_filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#include <type_traits> // std::integral_constant, std::index_sequence, std::conditional, std::declval
#include <tuple> // std::tuple

#include "../functional/cxx_universal.h"
#include "../functional/cxx_universal.h" // ::size_t
#include "../functional/index_sequence_util.h"

namespace sqlite_orm {
Expand Down
17 changes: 1 addition & 16 deletions dev/tuple_helper/tuple_iteration.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
#pragma once

#include <tuple> // std::tuple, std::get, std::tuple_element, std::tuple_size
#include <type_traits> // std::remove_reference, std::index_sequence, std::make_index_sequence
#include <type_traits> // std::remove_reference, std::index_sequence, std::make_index_sequence, std::forward, std::move
#include <utility> // std::forward, std::move

#include "../functional/cxx_universal.h" // ::size_t
#include "../functional/cxx_type_traits_polyfill.h"
#include "../functional/cxx_functional_polyfill.h"

namespace sqlite_orm {
namespace internal {
Expand Down Expand Up @@ -92,19 +90,6 @@ namespace sqlite_orm {
iterate_tuple<Tpl>(std::make_index_sequence<std::tuple_size<Tpl>::value>{}, std::forward<L>(lambda));
}

template<class R, class Tpl, size_t... Idx, class Projection = polyfill::identity>
R create_from_tuple(Tpl&& tpl, std::index_sequence<Idx...>, Projection project = {}) {
return R{polyfill::invoke(project, std::get<Idx>(std::forward<Tpl>(tpl)))...};
}

template<class R, class Tpl, class Projection = polyfill::identity>
R create_from_tuple(Tpl&& tpl, Projection project = {}) {
return create_from_tuple<R>(
std::forward<Tpl>(tpl),
std::make_index_sequence<std::tuple_size<std::remove_reference_t<Tpl>>::value>{},
std::forward<Projection>(project));
}

template<template<class...> class Base, class L>
struct lambda_as_template_base : L {
#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED
Expand Down
Loading

0 comments on commit a2f37cd

Please sign in to comment.