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
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_idcannot happen. Well, a small reproducible example says more then words:Compiling with:
clang++ -std=c++23 -I include -I tests/include test.cppgave the following output:As you can see in the example, I am implementing the
to_sql_stringfunction, I expected something like afrom_sql_stringto 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