@@ -3975,19 +3975,26 @@ namespace sqlite_orm {
39753975 template<class T>
39763976 struct is_generated_always : polyfill::bool_constant<is_generated_always_v<T>> {};
39773977
3978- /**
3979- * PRIMARY KEY INSERTABLE traits.
3978+ /**
3979+ * COLUMN PRIMARY KEY INSERTABLE traits.
3980+ *
3981+ * A column primary key is considered implicitly insertable if:
3982+ * - it is an INTEGER PRIMARY KEY (and thus an alias for the "rowid" key),
3983+ * - or has a default value.
3984+ *
3985+ * Note that the restrictions on an alias for the "rowid" key are actually more narrow:
3986+ * it must be of 64-bit signed integer type (not any other integral arithmetic type),
3987+ * however due to sqlite_orm's current type mapping this is not enforced here.
39803988 */
39813989 template<typename Column>
3982- struct is_primary_key_insertable
3983- : polyfill::disjunction<
3984- mpl::invoke_t<mpl::disjunction<check_if_has_template<primary_key_with_autoincrement>,
3985- check_if_has_template<default_t>>,
3986- constraints_type_t<Column>>,
3987- std::is_base_of<integer_printer, type_printer<field_type_t<Column>>>> {
3990+ struct is_pkcol_implicitly_insertable
3991+ : mpl::invoke_t<
3992+ mpl::disjunction<mpl::always<std::is_base_of<integer_printer, type_printer<field_type_t<Column>>>>,
3993+ check_if_has_template<default_t>>,
3994+ constraints_type_t<Column>> {
39883995
3989- static_assert(tuple_has<constraints_type_t<Column>, is_primary_key>::value,
3990- "an unexpected type was passed" );
3996+ // internal programming error: column primary key required
3997+ static_assert(tuple_has<constraints_type_t<Column>, is_primary_key>::value );
39913998 };
39923999
39934000 template<class T>
@@ -8924,10 +8931,10 @@ SQLITE_ORM_EXPORT namespace sqlite_orm {
89248931
89258932// #include "../tuple_helper/tuple_filter.h"
89268933
8927- // #include "../type_traits.h"
8928-
89298934// #include "../member_traits/member_traits.h"
89308935
8936+ // #include "../type_traits.h"
8937+
89318938// #include "../type_is_nullable.h"
89328939
89338940#ifndef SQLITE_ORM_IMPORT_STD_MODULE
@@ -9122,13 +9129,32 @@ namespace sqlite_orm {
91229129 field_type_t,
91239130 filter_tuple_sequence_t<Elements, mpl::disjunction_fn<is_column, is_hidden_column>::template fn>>;
91249131
9132+ template<class G, class... Op>
9133+ constexpr void validate_column_definition() {
9134+ using constraints_type = std::tuple<Op...>;
9135+
9136+ static_assert(polyfill::conjunction_v<is_column_constraint<Op>...>, "Incorrect column constraints");
9137+
9138+ if constexpr (tuple_has<constraints_type, is_primary_key>::value) {
9139+ using field_type = member_field_type_t<G>;
9140+ using is_pkcol_correct = mpl::invoke_t<
9141+ mpl::disjunction<mpl::always<std::is_base_of<integer_printer, type_printer<field_type>>>,
9142+ mpl::not_<check_if_has_template<primary_key_with_autoincrement>>>,
9143+ constraints_type>;
9144+
9145+ static_assert(
9146+ is_pkcol_correct::value,
9147+ R"(AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY as an alias for the "rowid" key)");
9148+ }
9149+ }
9150+
91259151#if SQLITE_VERSION_NUMBER >= 3031000
91269152 /**
91279153 * Factory function for a column definition from a member object pointer for hidden virtual table columns.
91289154 */
91299155 template<class M, class... Op, satisfies<std::is_member_object_pointer, M> = true>
91309156 hidden_column<M, empty_setter, Op...> make_hidden_column(std::string name, M memberPointer, Op... constraints) {
9131- static_assert(polyfill::conjunction_v<is_column_constraint<Op>...>, "Incorrect constraints pack ");
9157+ static_assert(polyfill::conjunction_v<is_column_constraint<Op>...>, "Incorrect column constraints ");
91329158
91339159 // attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`,
91349160 // as this will lead to UB with Clang on MinGW!
@@ -9145,7 +9171,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm {
91459171 template<class M, class... Op, internal::satisfies<std::is_member_object_pointer, M> = true>
91469172 internal::column_t<M, internal::empty_setter, Op...>
91479173 make_column(std::string name, M memberPointer, Op... constraints) {
9148- static_assert(polyfill::conjunction_v< internal::is_column_constraint<Op> ...>, "Incorrect constraints pack" );
9174+ internal::validate_column_definition<M, Op ...>( );
91499175
91509176 // attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`,
91519177 // as this will lead to UB with Clang on MinGW!
@@ -9163,7 +9189,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm {
91639189 internal::column_t<G, S, Op...> make_column(std::string name, S setter, G getter, Op... constraints) {
91649190 static_assert(std::is_same<internal::setter_field_type_t<S>, internal::getter_field_type_t<G>>::value,
91659191 "Getter and setter must get and set same data type");
9166- static_assert(polyfill::conjunction_v< internal::is_column_constraint<Op> ...>, "Incorrect constraints pack" );
9192+ internal::validate_column_definition<G, Op ...>( );
91679193
91689194 // attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`,
91699195 // as this will lead to UB with Clang on MinGW!
@@ -9181,7 +9207,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm {
91819207 internal::column_t<G, S, Op...> make_column(std::string name, G getter, S setter, Op... constraints) {
91829208 static_assert(std::is_same<internal::setter_field_type_t<S>, internal::getter_field_type_t<G>>::value,
91839209 "Getter and setter must get and set same data type");
9184- static_assert(polyfill::conjunction_v< internal::is_column_constraint<Op> ...>, "Incorrect constraints pack" );
9210+ internal::validate_column_definition<G, Op ...>( );
91859211
91869212 // attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`,
91879213 // as this will lead to UB with Clang on MinGW!
@@ -12848,9 +12874,7 @@ namespace sqlite_orm::internal {
1284812874 definition.visit_table_primary_key([&column, &res](auto& primaryKey) {
1284912875 // note: use `decltype(primaryKey)` instead of `decltype(primaryKey.columns)` otherwise msvc 141 chokes on the `if constexpr` below
1285012876 using colrefs_tuple = columns_tuple_t<polyfill::remove_cvref_t<decltype(primaryKey)>>;
12851- if constexpr (std::tuple_size<colrefs_tuple>::value != 1) {
12852- return;
12853- } else {
12877+ if constexpr (std::tuple_size<colrefs_tuple>::value == 1) {
1285412878 auto& memberPointer = std::get<0>(primaryKey.columns);
1285512879 if (compare_fields(memberPointer, column.member_pointer) ||
1285612880 compare_fields(memberPointer, column.setter)) {
@@ -24450,12 +24474,13 @@ namespace sqlite_orm {
2445024474 using elements_type = elements_type_t<Table>;
2445124475 using pkcol_index_sequence = col_index_sequence_with<elements_type, is_primary_key>;
2445224476 static_assert(
24453- count_filtered_tuple<elements_type, is_primary_key_insertable, pkcol_index_sequence>::value <= 1,
24477+ count_filtered_tuple<elements_type, is_pkcol_implicitly_insertable, pkcol_index_sequence>::value <=
24478+ 1,
2445424479 "Attempting to execute 'insert' request into an noninsertable table was detected. "
2445524480 "Insertable table cannot contain > 1 primary keys. Please use 'replace' instead of "
2445624481 "'insert', or you can use 'insert' with explicit column listing.");
2445724482 static_assert(count_filtered_tuple<elements_type,
24458- check_if_not<is_primary_key_insertable >::template fn,
24483+ check_if_not<is_pkcol_implicitly_insertable >::template fn,
2445924484 pkcol_index_sequence>::value == 0,
2446024485 "Attempting to execute 'insert' request into an noninsertable table was detected. "
2446124486 "Insertable table cannot contain non-standard primary keys. Please use 'replace' instead "
0 commit comments