diff --git a/include/sqlpp11/result.h b/include/sqlpp11/result.h index 109cdfed..024dfc85 100644 --- a/include/sqlpp11/result.h +++ b/include/sqlpp11/result.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015, Roland Bock + * Copyright (c) 2013-2017, Roland Bock, Aaron Bishop * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -38,6 +38,25 @@ namespace sqlpp using type = std::input_iterator_tag; }; + namespace detail + { + template + struct result_has_size : std::false_type {}; + + template + struct result_has_size().size())>> + : std::true_type {}; + + template + struct result_size_type { using type = void; }; + + template + struct result_size_type().size())>> + { + using type = decltype(std::declval().size()); + }; + } + template class result_t { @@ -139,6 +158,13 @@ namespace sqlpp { _result.next(_result_row); } + + template::type> + Size size() const + { + static_assert(detail::result_has_size::value, "Underlying connector does not support size()"); + return _result.size(); + } }; } // namespace sqlpp diff --git a/test_types/result_row.cpp b/test_types/result_row.cpp index 31858a91..9bfae81a 100644 --- a/test_types/result_row.cpp +++ b/test_types/result_row.cpp @@ -65,7 +65,7 @@ namespace static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); } { - const auto& x = db(select(bar.alpha, foo.delta, bar.gamma, seven) + const auto& x = db(select(bar.alpha, foo.delta, bar.gamma, seven) .from(bar.join(foo).on(foo.omega > bar.alpha)) .unconditionally()).front(); static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); @@ -73,6 +73,14 @@ namespace static_assert(not sqlpp::can_be_null_t::value, "right side of (inner) join cannot be null"); static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); } + { + MockSizeDb db2; + auto&& result = db2(select(bar.alpha, foo.delta, bar.gamma, seven) + .from(bar.join(foo).on(foo.omega > bar.alpha)) + .unconditionally()); + result.size(); + static_assert(std::is_same::value, "MockSizeDb size() isn't size_t"); + } // Inner join { diff --git a/tests/MockDb.h b/tests/MockDb.h index 8e5d5308..05271893 100644 --- a/tests/MockDb.h +++ b/tests/MockDb.h @@ -288,4 +288,206 @@ struct MockDbT : public sqlpp::connection using MockDb = MockDbT; using EnforceDb = MockDbT; + +struct MockSizeDb : public sqlpp::connection +{ + using _traits = MockDb::_traits; + + using _serializer_context_t = MockDb::_serializer_context_t; + + using _interpreter_context_t = _serializer_context_t; + + _serializer_context_t get_serializer_context() + { + return {}; + } + + template + static _serializer_context_t& _serialize_interpretable(const T& t, _serializer_context_t& context) + { + sqlpp::serialize(t, context); + return context; + } + + template + static _serializer_context_t& _interpret_interpretable(const T& t, _interpreter_context_t& context) + { + sqlpp::serialize(t, context); + return context; + } + + class result_t : public MockDb::result_t + { + public: + size_t size() const { return 0; } + }; + + // Directly executed statements start here + template + auto _run(const T& t, ::sqlpp::consistent_t) -> decltype(t._run(*this)) + { + return t._run(*this); + } + + template + auto _run(const T& t, Check) -> Check; + + template + auto operator()(const T& t) -> decltype(this->_run(t, sqlpp::run_check_t<_serializer_context_t, T>{})) + { + return _run(t, sqlpp::run_check_t<_serializer_context_t, T>{}); + } + + size_t execute(const std::string&) + { + return 0; + } + + template < + typename Statement, + typename Enable = typename std::enable_if::value, void>::type> + size_t execute(const Statement& x) + { + _serializer_context_t context; + ::sqlpp::serialize(x, context); + std::cout << "Running execute call with\n" << context.str() << std::endl; + return execute(context.str()); + } + + template + size_t insert(const Insert& x) + { + _serializer_context_t context; + ::sqlpp::serialize(x, context); + std::cout << "Running insert call with\n" << context.str() << std::endl; + return 0; + } + + template + size_t update(const Update& x) + { + _serializer_context_t context; + ::sqlpp::serialize(x, context); + std::cout << "Running update call with\n" << context.str() << std::endl; + return 0; + } + + template + size_t remove(const Remove& x) + { + _serializer_context_t context; + ::sqlpp::serialize(x, context); + std::cout << "Running remove call with\n" << context.str() << std::endl; + return 0; + } + + template + result_t select(const Select& x) + { + _serializer_context_t context; + ::sqlpp::serialize(x, context); + std::cout << "Running select call with\n" << context.str() << std::endl; + return {}; + } + + // Prepared statements start here + using _prepared_statement_t = std::nullptr_t; + + template + auto _prepare(const T& t, ::sqlpp::consistent_t) -> decltype(t._prepare(*this)) + { + return t._prepare(*this); + } + + template + auto _prepare(const T& t, Check) -> Check; + + template + auto prepare(const T& t) -> decltype(this->_prepare(t, sqlpp::prepare_check_t<_serializer_context_t, T>{})) + { + return _prepare(t, sqlpp::prepare_check_t<_serializer_context_t, T>{}); + } + + template + _prepared_statement_t prepare_execute(Statement& x) + { + _serializer_context_t context; + ::sqlpp::serialize(x, context); + std::cout << "Running prepare execute call with\n" << context.str() << std::endl; + return nullptr; + } + + template + _prepared_statement_t prepare_insert(Insert& x) + { + _serializer_context_t context; + ::sqlpp::serialize(x, context); + std::cout << "Running prepare insert call with\n" << context.str() << std::endl; + return nullptr; + } + + template + size_t run_prepared_execute(const PreparedExecute&) + { + return 0; + } + + template + size_t run_prepared_insert(const PreparedInsert&) + { + return 0; + } + + template + _prepared_statement_t prepare_select(Select& x) + { + _serializer_context_t context; + ::sqlpp::serialize(x, context); + std::cout << "Running prepare select call with\n" << context.str() << std::endl; + return nullptr; + } + + template + result_t run_prepared_select(PreparedSelect&) + { + return {}; + } + + auto attach(std::string name) -> ::sqlpp::schema_t + { + return {name}; + } + + void start_transaction() + { + _mock_data._last_isolation_level = _mock_data._default_isolation_level; + } + + void start_transaction(sqlpp::isolation_level level) + { + _mock_data._last_isolation_level = level; + } + + void set_default_isolation_level(sqlpp::isolation_level level) + { + _mock_data._default_isolation_level = level; + } + + sqlpp::isolation_level get_default_isolation_level() + { + return _mock_data._default_isolation_level; + } + + void rollback_transaction(bool) + {} + + void commit_transaction() + {} + + void report_rollback_failure(std::string) + {} + + // temporary data store to verify the expected results were produced + InternalMockData _mock_data; +}; #endif