Skip to content

Custom data types #106

@matthijs

Description

@matthijs

I am looking into migrating to sqlpp23. I found one issue.

I am using a lot of strong types. Mapping enum fields in the database to enum fields in C++, strong typed id's, so that mistakes like: tableA_id == tableB_id cannot happen. Well, a small reproducible example says more then words:

#include <sqlpp23/mock_db/mock_db.h> #include <sqlpp23/sqlpp23.h> #include <sqlpp23/tests/core/make_test_connection.h> // Strong type struct TestId { int64_t value{0}; explicit TestId(int64_t v) : value(v) {} TestId() = default; bool operator==(const TestId&) const = default; }; // Table definition struct Test_ { struct Id { SQLPP_CREATE_NAME_TAG_FOR_SQL_AND_CPP(id, id); using data_type = TestId; // <-- strong type as data_type using has_default = std::false_type; }; struct Symbol { SQLPP_CREATE_NAME_TAG_FOR_SQL_AND_CPP(symbol, symbol); using data_type = ::sqlpp::text; using has_default = std::false_type; }; SQLPP_CREATE_NAME_TAG_FOR_SQL_AND_CPP(stock, stock); template<typename T> using _table_columns = sqlpp::table_columns<T, Id, Symbol>; using _required_insert_columns = sqlpp::detail::type_set< sqlpp::column_t<sqlpp::table_t<Test_>, Id>, sqlpp::column_t<sqlpp::table_t<Test_>, Symbol>>; }; using Test = ::sqlpp::table_t<Test_>; // Glue needed for sqlpp template <> struct sqlpp::data_type_of<TestId> { using type = sqlpp::integral; }; template <typename Context> auto to_sql_string(Context& ctx, const TestId& id) { return to_sql_string(ctx, id.value); } template <> struct sqlpp::result_data_type_of<TestId> { using type = TestId; // result row will hold a TestId directly }; // End glue int main() { auto db = sqlpp::mock_db::make_test_connection({}); const Test stock; for (const auto& row : db(select(all_of(stock)).from(stock))) { TestId id = row.id; (void)id; } return 0; }

Compiling with: clang++ -std=c++23 -I include -I tests/include test.cpp gave the following output:

In file included from test.cpp:1: In file included from include/sqlpp23/mock_db/mock_db.h:30: In file included from include/sqlpp23/mock_db/database/connection.h:32: include/sqlpp23/core/query/result_row.h:55:12: error: no matching member function for call to 'read_field' 55 | target.read_field(index, _field::operator()()); | ~~~~~~~^~~~~~~~~~ include/sqlpp23/core/query/result_row.h:72:36: note: in instantiation of function template specialization 'sqlpp::detail::result_field<0, sqlpp::field_spec_t<Test_::Id::_sqlpp_name_tag,  TestId>>::_read_field<sqlpp::mock_db::text_result_t>' requested here 72 | (result_field<Is, FieldSpecs>::_read_field(target), ...); | ^ include/sqlpp23/core/query/result_row.h:134:12: note: in instantiation of function template specialization 'sqlpp::detail::result_row_impl<std::integer_sequence<unsigned long, 0, 1>,  sqlpp::field_spec_t<Test_::Id::_sqlpp_name_tag, TestId>, sqlpp::field_spec_t<Test_::Symbol::_sqlpp_name_tag,  sqlpp::text>>::_read_fields<sqlpp::mock_db::text_result_t>' requested here 134 | _impl::_read_fields(target); | ^ include/sqlpp23/core/query/result_row.h:151:9: note: in instantiation of function template specialization 'sqlpp::result_row_t<sqlpp::field_spec_t<Test_::Id::_sqlpp_name_tag, TestId>,  sqlpp::field_spec_t<Test_::Symbol::_sqlpp_name_tag, sqlpp::text>>::_read_fields<sqlpp::mock_db::text_result_t>' requested here 151 | row._read_fields(target); | ^ include/sqlpp23/mock_db/text_result.h:92:42: note: in instantiation of function template specialization 'sqlpp::detail::result_row_bridge::read_fields<sqlpp::field_spec_t<Test_::Id::_sqlpp_name_tag, TestId>,  sqlpp::field_spec_t<Test_::Symbol::_sqlpp_name_tag, sqlpp::text>, sqlpp::mock_db::text_result_t>' requested here 92 | sqlpp::detail::result_row_bridge{}.read_fields(result_row, *this); | ^ include/sqlpp23/core/result.h:70:13: note: in instantiation of function template specialization 'sqlpp::mock_db::text_result_t::next<sqlpp::result_row_t<sqlpp::field_spec_t<Test_::Id::_sqlpp_name_tag, TestId>,  sqlpp::field_spec_t<Test_::Symbol::_sqlpp_name_tag, sqlpp::text>>>' requested here 70 | _result.next(_result_row); | ^ include/sqlpp23/core/clause/select_column_list.h:166:12: note: in instantiation of member function 'sqlpp::result_t<sqlpp::mock_db::text_result_t, sqlpp::result_row_t<sqlpp::field_spec_t<Test_::Id::_sqlpp_name_tag,  TestId>, sqlpp::field_spec_t<Test_::Symbol::_sqlpp_name_tag, sqlpp::text>>>::result_t' requested here 166 | return {statement_handler_t{}.select(std::forward<Statement>(self), db)}; | ^ include/sqlpp23/core/query/statement_handler.h:38:47: note: in instantiation of function template specialization 'sqlpp::select_result_methods_t<sqlpp::column_t<sqlpp::table_t<Test_>, Test_::Id>,  sqlpp::column_t<sqlpp::table_t<Test_>, Test_::Symbol>>::_run<const sqlpp::statement_t<sqlpp::select_t,  sqlpp::select_column_list_t<std::tuple<>, std::tuple<sqlpp::column_t<sqlpp::table_t<Test_>, Test_::Id>,  sqlpp::column_t<sqlpp::table_t<Test_>, Test_::Symbol>>>, sqlpp::from_t<sqlpp::table_t<Test_>>, sqlpp::no_where_t,  sqlpp::no_group_by_t, sqlpp::no_having_t, sqlpp::no_order_by_t, sqlpp::no_limit_t, sqlpp::no_offset_t,  sqlpp::no_union_t, sqlpp::no_for_update_t> &, sqlpp::mock_db::connection_base>' requested here 38 | return std::forward<Statement>(statement)._run(db); | ^ include/sqlpp23/mock_db/text_result.h:106:8: note: candidate function not viable: no known conversion from 'TestId' to 'bool &' for 2nd argument 106 | void read_field(size_t index, bool& value) { | ^ ~~~~~~~~~~~ include/sqlpp23/mock_db/text_result.h:111:8: note: candidate function not viable: no known conversion from 'TestId' to 'double &' for 2nd argument 111 | void read_field(size_t index, double& value) { | ^ ~~~~~~~~~~~~~ include/sqlpp23/mock_db/text_result.h:115:8: note: candidate function not viable: no known conversion from 'TestId' to 'int64_t &' (aka 'long &') for 2nd argument 115 | void read_field(size_t index, int64_t& value) { | ^ ~~~~~~~~~~~~~~ include/sqlpp23/mock_db/text_result.h:119:8: note: candidate function not viable: no known conversion from 'TestId' to 'uint64_t &' (aka 'unsigned long &') for 2nd argument 119 | void read_field(size_t index, uint64_t& value) { | ^ ~~~~~~~~~~~~~~~ include/sqlpp23/mock_db/text_result.h:123:8: note: candidate function not viable: no known conversion from 'TestId' to 'std::span<const uint8_t> &' (aka 'span<const unsigned char> &') for 2nd argument 123 | void read_field(size_t index, std::span<const uint8_t>& value) { | ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/sqlpp23/mock_db/text_result.h:129:8: note: candidate function not viable: no known conversion from 'TestId' to 'std::string_view &' (aka 'basic_string_view<char> &') for 2nd argument 129 | void read_field(size_t index, std::string_view& value) { | ^ ~~~~~~~~~~~~~~~~~~~~~~~ include/sqlpp23/mock_db/text_result.h:134:8: note: candidate function not viable: no known conversion from 'TestId' to 'std::chrono::sys_days &' (aka 'time_point<std::chrono::system_clock, duration<long, ratio<86400>>>  &') for 2nd argument 134 | void read_field(size_t index, std::chrono::sys_days& value) { | ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/sqlpp23/mock_db/text_result.h:162:8: note: candidate function not viable: no known conversion from 'TestId' to '::sqlpp::chrono::sys_microseconds &' (aka 'time_point<std::chrono::system_clock, duration<long,  ratio<1, 1000000>>> &') for 2nd argument 162 | void read_field(size_t index, ::sqlpp::chrono::sys_microseconds& value) { | ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/sqlpp23/mock_db/text_result.h:190:8: note: candidate function not viable: no known conversion from 'TestId' to '::std::chrono::microseconds &' (aka 'duration<long, ratio<1, 1000000>> &') for 2nd argument 190 | void read_field(size_t index, ::std::chrono::microseconds& value) { | ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/sqlpp23/mock_db/text_result.h:219:8: note: candidate template ignored: could not match 'std::optional<T>' against 'TestId' 219 | auto read_field(size_t index, std::optional<T>& value) -> void { | ^ 1 error generated.

As you can see in the example, I am implementing the to_sql_string function, I expected something like a from_sql_string to do the other way around.

With sqlpp11 I had implementation of different data_types which handled exactly this. Hopefully this functionality can be implemented in sqlpp23!

Thank you for the wonderful library!

Regards, Matthijs

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions