Skip to content

Commit 9389efd

Browse files
committed
Merge branch 'dev' into claude
2 parents ca39c99 + f16e63b commit 9389efd

66 files changed

Lines changed: 1258 additions & 780 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

dev/constraints.h

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -496,18 +496,56 @@ namespace sqlite_orm::internal {
496496
template<class T>
497497
struct is_generated_always : polyfill::bool_constant<is_generated_always_v<T>> {};
498498

499-
/**
500-
* PRIMARY KEY INSERTABLE traits.
499+
// Custom type:
500+
// It is the programmer's responsibility to ensure data integrity in the value range of the custom type
501+
// and in purview of SQLite using a 64-bit signed integer.
502+
template<class F, class SFINAE = void>
503+
inline constexpr bool is_rowid_alias_capable_v = std::is_base_of<integer_printer, type_printer<F>>::value;
504+
505+
// For 64-bit signed integer type: capable
506+
template<class F>
507+
inline constexpr bool
508+
is_rowid_alias_capable_v<F,
509+
std::enable_if_t<std::is_integral<F>::value &&
510+
(sizeof(F) == sizeof(sqlite_int64) &&
511+
std::is_signed<F>::value == std::is_signed<sqlite_int64>::value)>> =
512+
true;
513+
514+
// Design decision for integral types other than 64-bit signed integer:
515+
// It is the programmer's responsibility to ensure data integrity in the value range of the integral type
516+
// and in purview of SQLite using a 64-bit signed integer.
517+
template<class F>
518+
inline constexpr bool
519+
is_rowid_alias_capable_v<F,
520+
std::enable_if_t<std::is_integral<F>::value &&
521+
(sizeof(F) != sizeof(sqlite_int64) ||
522+
std::is_signed<F>::value != std::is_signed<sqlite_int64>::value)>> =
523+
true;
524+
525+
// For non-integer types: unsuitable
526+
template<class F>
527+
inline constexpr bool
528+
is_rowid_alias_capable_v<F, std::enable_if_t<!std::is_base_of<integer_printer, type_printer<F>>::value>> =
529+
false;
530+
531+
/**
532+
* COLUMN PRIMARY KEY INSERTABLE traits.
533+
*
534+
* A column primary key is considered implicitly insertable if:
535+
* - it is an INTEGER PRIMARY KEY (and thus an alias for the "rowid" key),
536+
* - or has a default value.
537+
*
538+
* In terms of C++ types, this means that the field type must be capable of representing a 64-bit signed integer,
539+
* or the column is declared with a DEFAULT constraint.
540+
*
541+
* Implementation note: using a struct template in favor of a template alias so that the stack leading to a deprecation message is shorter.
501542
*/
502543
template<typename Column>
503-
struct is_primary_key_insertable
504-
: polyfill::disjunction<mpl::invoke_t<mpl::disjunction<check_if_has_template<primary_key_with_autoincrement>,
505-
check_if_has_template<default_t>>,
506-
constraints_type_t<Column>>,
507-
std::is_base_of<integer_printer, type_printer<field_type_t<Column>>>> {
508-
509-
static_assert(tuple_has<constraints_type_t<Column>, is_primary_key>::value, "an unexpected type was passed");
510-
};
544+
struct is_pkcol_implicitly_insertable
545+
: mpl::invoke_t<
546+
mpl::disjunction<mpl::always<polyfill::bool_constant<is_rowid_alias_capable_v<field_type_t<Column>>>>,
547+
check_if_has_template<default_t>>,
548+
constraints_type_t<Column>> {};
511549

512550
template<class T>
513551
using is_column_constraint = mpl::invoke_t<mpl::disjunction<check_if<std::is_base_of, primary_key_t<>>,
@@ -586,7 +624,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm {
586624
return {{}};
587625
}
588626

589-
#if SQLITE_VERSION_NUMBER >= 3009000
627+
#if SQLITE_VERSION_NUMBER >= 3009000 || defined(SQLITE_ORM_ENABLE_FTS5)
590628
/**
591629
* UNINDEXED column constraint builder function. Used in FTS virtual tables.
592630
*

dev/core_functions.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2155,6 +2155,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm {
21552155
}
21562156
}
21572157

2158+
#if SQLITE_VERSION_NUMBER >= 3009000 || defined(SQLITE_ORM_ENABLE_FTS5)
21582159
struct fts5;
21592160

21602161
/**
@@ -2219,4 +2220,5 @@ SQLITE_ORM_EXPORT namespace sqlite_orm {
22192220
return {std::move(x), std::move(y), std::move(z)};
22202221
}
22212222
#endif
2223+
#endif
22222224
}

dev/cte_column_names_collector.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,9 @@ namespace sqlite_orm::internal {
157157
};
158158

159159
template<typename Ctx, typename E, typename ExplicitColRefs, satisfies_is_specialization_of<E, select_t> = true>
160-
std::vector<std::string>
161-
collect_cte_column_names(const E& sel, const ExplicitColRefs& explicitColRefs, const Ctx& context) {
160+
std::vector<std::string> collect_cte_column_names(const E& sel,
161+
[[maybe_unused]] const ExplicitColRefs& explicitColRefs,
162+
const Ctx& context) {
162163
// 1. determine column names from subselect
163164
std::vector<std::string> columnNames = get_cte_column_names(sel.col, context);
164165

dev/schema/column.h

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
#pragma once
22

3+
#include <sqlite3.h> // sqlite_int64
34
#ifndef SQLITE_ORM_IMPORT_STD_MODULE
45
#include <tuple> // std::tuple
56
#include <string> // std::string
67
#include <memory> // std::unique_ptr
7-
#include <type_traits> // std::is_same, std::is_member_object_pointer
8+
#include <type_traits> // std::enable_if, std::is_same, std::is_member_object_pointer, std::is_signed
89
#include <utility> // std::move
910
#endif
1011

1112
#include "../functional/cxx_type_traits_polyfill.h"
1213
#include "../tuple_helper/tuple_traits.h"
1314
#include "../tuple_helper/tuple_filter.h"
14-
#include "../type_traits.h"
1515
#include "../member_traits/member_traits.h"
16+
#include "../type_traits.h"
1617
#include "../type_is_nullable.h"
1718
#include "../constraints.h"
1819

@@ -74,13 +75,21 @@ namespace sqlite_orm::internal {
7475
constraints_type constraints;
7576

7677
/**
77-
* Checks whether contraints contain specified type.
78+
* Checks whether constraints contain specified type.
7879
*/
7980
template<template<class...> class Trait>
8081
constexpr static bool is() {
8182
return tuple_has<constraints_type, Trait>::value;
8283
}
8384

85+
/**
86+
* Checks whether constraints contain specified class template.
87+
*/
88+
template<template<class...> class Primary>
89+
constexpr static bool is_template() {
90+
return tuple_has_template<constraints_type, Primary>::value;
91+
}
92+
8493
/**
8594
* Simplified interface for `DEFAULT` constraint
8695
* @return string representation of default value if it exists otherwise nullptr
@@ -161,19 +170,65 @@ namespace sqlite_orm::internal {
161170
field_type_t,
162171
filter_tuple_sequence_t<Elements, mpl::disjunction_fn<is_column, is_hidden_column>::template fn>>;
163172

164-
#if SQLITE_VERSION_NUMBER >= 3031000
173+
// Custom type:
174+
// It is the programmer's responsibility to ensure data integrity in the value range of the custom type
175+
// and in purview of SQLite using a 64-bit signed integer.
176+
template<class F, class SFINAE = void>
177+
struct check_pkcol {
178+
static constexpr void validate_column_primary_key_with_autoincrement() {}
179+
};
180+
181+
// For integer types: further checks
182+
template<class F>
183+
struct check_pkcol<F, std::enable_if_t<std::is_integral<F>::value>> {
184+
// For 64-bit signed integer type: valid
185+
template<class X = F,
186+
std::enable_if_t<sizeof(X) == sizeof(sqlite_int64) &&
187+
std::is_signed<X>::value == std::is_signed<sqlite_int64>::value,
188+
bool> = true>
189+
static constexpr void validate_column_primary_key_with_autoincrement() {}
190+
191+
// Design decision for integral types other than 64-bit signed integer:
192+
// It is the programmer's responsibility to ensure data integrity in the value range of the integral type
193+
// and in purview of SQLite using a 64-bit signed integer.
194+
template<class X = F,
195+
std::enable_if_t<sizeof(X) != sizeof(sqlite_int64) ||
196+
std::is_signed<X>::value != std::is_signed<sqlite_int64>::value,
197+
bool> = true>
198+
static constexpr void validate_column_primary_key_with_autoincrement() {}
199+
};
200+
201+
// For non-integer types: static_assert failure
202+
template<class F>
203+
struct check_pkcol<F, std::enable_if_t<!std::is_base_of<integer_printer, type_printer<F>>::value>> {
204+
static constexpr void validate_column_primary_key_with_autoincrement() {
205+
static_assert(polyfill::always_false_v<F>,
206+
R"(AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY as an alias for the "rowid" key)");
207+
}
208+
};
209+
210+
template<class G, class... Op>
211+
constexpr void validate_column_definition() {
212+
using constraints_type = std::tuple<Op...>;
213+
214+
static_assert(polyfill::conjunction_v<is_column_constraint<Op>...>, "Incorrect column constraints");
215+
216+
if constexpr (tuple_has_template<constraints_type, primary_key_with_autoincrement>::value) {
217+
check_pkcol<member_field_type_t<G>>::validate_column_primary_key_with_autoincrement();
218+
}
219+
}
220+
165221
/**
166222
* Factory function for a column definition from a member object pointer for hidden virtual table columns.
167223
*/
168224
template<class M, class... Op, satisfies<std::is_member_object_pointer, M> = true>
169225
hidden_column<M, empty_setter, Op...> make_hidden_column(std::string name, M memberPointer, Op... constraints) {
170-
static_assert(polyfill::conjunction_v<is_column_constraint<Op>...>, "Incorrect constraints pack");
226+
static_assert(polyfill::conjunction_v<is_column_constraint<Op>...>, "Incorrect column constraints");
171227

172228
// attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`,
173229
// as this will lead to UB with Clang on MinGW!
174230
return {std::move(name), memberPointer, {}, std::tuple<Op...>{std::move(constraints)...}};
175231
}
176-
#endif
177232
}
178233

179234
SQLITE_ORM_EXPORT namespace sqlite_orm {
@@ -183,7 +238,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm {
183238
template<class M, class... Op, internal::satisfies<std::is_member_object_pointer, M> = true>
184239
internal::column_t<M, internal::empty_setter, Op...>
185240
make_column(std::string name, M memberPointer, Op... constraints) {
186-
static_assert(polyfill::conjunction_v<internal::is_column_constraint<Op>...>, "Incorrect constraints pack");
241+
internal::validate_column_definition<M, Op...>();
187242

188243
// attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`,
189244
// as this will lead to UB with Clang on MinGW!
@@ -201,7 +256,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm {
201256
internal::column_t<G, S, Op...> make_column(std::string name, S setter, G getter, Op... constraints) {
202257
static_assert(std::is_same<internal::setter_field_type_t<S>, internal::getter_field_type_t<G>>::value,
203258
"Getter and setter must get and set same data type");
204-
static_assert(polyfill::conjunction_v<internal::is_column_constraint<Op>...>, "Incorrect constraints pack");
259+
internal::validate_column_definition<G, Op...>();
205260

206261
// attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`,
207262
// as this will lead to UB with Clang on MinGW!
@@ -219,7 +274,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm {
219274
internal::column_t<G, S, Op...> make_column(std::string name, G getter, S setter, Op... constraints) {
220275
static_assert(std::is_same<internal::setter_field_type_t<S>, internal::getter_field_type_t<G>>::value,
221276
"Getter and setter must get and set same data type");
222-
static_assert(polyfill::conjunction_v<internal::is_column_constraint<Op>...>, "Incorrect constraints pack");
277+
internal::validate_column_definition<G, Op...>();
223278

224279
// attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`,
225280
// as this will lead to UB with Clang on MinGW!

dev/schema/table.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ namespace sqlite_orm::internal {
128128
constexpr size_t nTablePrimaryKeyColumns =
129129
nested_tuple_size_for_t<columns_tuple_t, elements_type, pk_index_sequence>::value;
130130

131-
static_assert(nTablePrimaryKeyColumns > 0, "Tabel primary key definition must contain one column");
131+
static_assert(nTablePrimaryKeyColumns > 0, "Table primary key definition must contain one column");
132132
}
133133
}
134134
}

dev/schema/table_base.h

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ namespace sqlite_orm::internal {
5555
}
5656

5757
/*
58-
* Returns the number of columns having the specified constraint trait.
58+
* Returns the number of columns not having the specified constraint trait.
5959
*/
6060
template<template<class...> class Trait>
6161
static constexpr int count_of_columns_excluding() {
@@ -179,42 +179,48 @@ namespace sqlite_orm::internal {
179179
};
180180

181181
template<class... Cs, class G, class S>
182-
bool table_primary_key_contains(const insertable_table_definition<Cs...>& definition,
183-
const column_field<G, S>& column) {
182+
bool table_primary_key_contains([[maybe_unused]] const insertable_table_definition<Cs...>& definition,
183+
[[maybe_unused]] const column_field<G, S>& column) {
184184
bool res = false;
185-
definition.visit_table_primary_key([&column, &res](auto& primaryKey) {
186-
using colrefs_tuple = decltype(primaryKey.columns);
187-
using same_type_index_sequence =
188-
filter_tuple_sequence_t<colrefs_tuple,
189-
check_if_is_type<member_field_type_t<G>>::template fn,
190-
member_field_type_t>;
191-
iterate_tuple(primaryKey.columns, same_type_index_sequence{}, [&res, &column](auto& memberPointer) {
192-
if (compare_fields(memberPointer, column.member_pointer) ||
193-
compare_fields(memberPointer, column.setter)) {
194-
res = true;
195-
}
185+
// note: though `visit_table_primary_key()` does no work if a column primary key exists, we try to save the compiler some work with this check up front
186+
if constexpr (/*bool hasNoColumnPK =*/!insertable_table_definition<Cs...>::template count_of_columns_with<
187+
is_primary_key>()) {
188+
definition.visit_table_primary_key([&column, &res](auto& primaryKey) {
189+
using colrefs_tuple = decltype(primaryKey.columns);
190+
using same_type_index_sequence =
191+
filter_tuple_sequence_t<colrefs_tuple,
192+
check_if_is_type<member_field_type_t<G>>::template fn,
193+
member_field_type_t>;
194+
iterate_tuple(primaryKey.columns, same_type_index_sequence{}, [&res, &column](auto& memberPointer) {
195+
if (compare_fields(memberPointer, column.member_pointer) ||
196+
compare_fields(memberPointer, column.setter)) {
197+
res = true;
198+
}
199+
});
196200
});
197-
});
201+
}
198202
return res;
199203
}
200204

201205
template<class... Cs, class G, class S>
202-
bool is_single_table_primary_key(const insertable_table_definition<Cs...>& definition,
203-
const column_field<G, S>& column) {
206+
bool is_single_table_primary_key([[maybe_unused]] const insertable_table_definition<Cs...>& definition,
207+
[[maybe_unused]] const column_field<G, S>& column) {
204208
bool res = false;
205-
definition.visit_table_primary_key([&column, &res](auto& primaryKey) {
206-
// note: use `decltype(primaryKey)` instead of `decltype(primaryKey.columns)` otherwise msvc 141 chokes on the `if constexpr` below
207-
using colrefs_tuple = columns_tuple_t<polyfill::remove_cvref_t<decltype(primaryKey)>>;
208-
if constexpr (std::tuple_size<colrefs_tuple>::value != 1) {
209-
return;
210-
} else {
211-
auto& memberPointer = std::get<0>(primaryKey.columns);
212-
if (compare_fields(memberPointer, column.member_pointer) ||
213-
compare_fields(memberPointer, column.setter)) {
214-
res = true;
209+
// note: though `visit_table_primary_key()` does no work if a column primary key exists, we try to save the compiler some work with this check up front
210+
if constexpr (/*bool hasNoColumnPK =*/!insertable_table_definition<Cs...>::template count_of_columns_with<
211+
is_primary_key>()) {
212+
definition.visit_table_primary_key([&column, &res](auto& primaryKey) {
213+
// note: use `decltype(primaryKey)` instead of `decltype(primaryKey.columns)` otherwise msvc 141 chokes on the `if constexpr` below
214+
using colrefs_tuple = columns_tuple_t<polyfill::remove_cvref_t<decltype(primaryKey)>>;
215+
if constexpr (std::tuple_size<colrefs_tuple>::value == 1) {
216+
auto& memberPointer = std::get<0>(primaryKey.columns);
217+
if (compare_fields(memberPointer, column.member_pointer) ||
218+
compare_fields(memberPointer, column.setter)) {
219+
res = true;
220+
}
215221
}
216-
}
217-
});
222+
});
223+
}
218224
return res;
219225
}
220226

0 commit comments

Comments
 (0)