From 027220324ec238c9f84c6bcd152dc05675614b94 Mon Sep 17 00:00:00 2001 From: Daniel Frey Date: Sat, 23 Nov 2024 01:04:55 +0100 Subject: [PATCH] Use concepts for parameter types --- include/tao/pq/connection.hpp | 3 +- include/tao/pq/connection_pool.hpp | 3 +- include/tao/pq/parameter.hpp | 58 ++++++++++++++++------------- include/tao/pq/parameter_traits.hpp | 40 ++++++-------------- include/tao/pq/table_writer.hpp | 3 +- include/tao/pq/transaction.hpp | 4 +- src/test/pq/connection.cpp | 6 +++ 7 files changed, 58 insertions(+), 59 deletions(-) diff --git a/include/tao/pq/connection.hpp b/include/tao/pq/connection.hpp index dbfa7f2..cff3dca 100644 --- a/include/tao/pq/connection.hpp +++ b/include/tao/pq/connection.hpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -149,7 +150,7 @@ namespace tao::pq void prepare( const std::string& name, const std::string& statement ); void deallocate( const std::string_view name ); - template< typename... As > + template< parameter_type... As > auto execute( const internal::zsv statement, As&&... as ) { return direct()->execute( statement, std::forward< As >( as )... ); diff --git a/include/tao/pq/connection_pool.hpp b/include/tao/pq/connection_pool.hpp index c68ac28..657519a 100644 --- a/include/tao/pq/connection_pool.hpp +++ b/include/tao/pq/connection_pool.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -69,7 +70,7 @@ namespace tao::pq [[nodiscard]] auto connection() -> std::shared_ptr< connection >; - template< typename... As > + template< parameter_type... As > auto execute( const internal::zsv statement, As&&... as ) { return connection()->direct()->execute( statement, std::forward< As >( as )... ); diff --git a/include/tao/pq/parameter.hpp b/include/tao/pq/parameter.hpp index ad9ea6e..39bdfdb 100644 --- a/include/tao/pq/parameter.hpp +++ b/include/tao/pq/parameter.hpp @@ -20,6 +20,34 @@ namespace tao::pq class transaction; template< std::size_t Max = 16 > + class parameter; + + namespace internal + { + template< typename A > + inline constexpr bool is_parameter = false; + + template< std::size_t Max > + inline constexpr bool is_parameter< parameter< Max > > = true; + + template< typename... As > + inline constexpr bool contains_parameter = ( is_parameter< std::decay_t< As > > || ... ); + + template< typename... As > + inline constexpr std::size_t parameter_size = ( parameter_size< std::decay_t< As > > + ... + 0 ); + + template< typename A > + inline constexpr std::size_t parameter_size< A > = parameter_traits< A >::columns; + + template< std::size_t Max > + inline constexpr std::size_t parameter_size< parameter< Max > > = Max; + + } // namespace internal + + template< typename T > + concept parameter_type = parameter_type_direct< T > || internal::is_parameter< std::decay_t< T > >; + + template< std::size_t Max > class parameter { private: @@ -80,7 +108,7 @@ namespace tao::pq ( ( m_formats[ m_size + Is ] = t.template format< Is >() ), ... ); } - template< typename A > + template< parameter_type_direct A > void bind_impl( A&& a ) { using D = std::decay_t< A&& >; @@ -128,7 +156,7 @@ namespace tao::pq void bind_impl( parameter< N >&& p ) = delete; // NOLINT(modernize-use-equals-delete) public: - template< typename... As > + template< parameter_type... As > explicit parameter( As&&... as ) noexcept( noexcept( std::declval< parameter >().bind( std::forward< As >( as )... ) ) ) { parameter::bind( std::forward< As >( as )... ); @@ -158,13 +186,13 @@ namespace tao::pq void operator=( const parameter& ) = delete; void operator=( parameter&& ) = delete; - template< typename... As > + template< parameter_type... As > void bind( As&&... as ) noexcept( sizeof...( As ) == 0 ) { ( parameter::bind_impl( std::forward< As >( as ) ), ... ); } - template< typename... As > + template< parameter_type... As > void reset( As&&... as ) noexcept( noexcept( std::declval< parameter >().bind( std::forward< As >( as )... ) ) ) { for( std::size_t i = 0; i != m_pos; ++i ) { @@ -176,28 +204,6 @@ namespace tao::pq } }; - namespace internal - { - template< typename A > - inline constexpr bool is_parameter = false; - - template< std::size_t Max > - inline constexpr bool is_parameter< parameter< Max > > = true; - - template< typename... As > - inline constexpr bool contains_parameter = ( is_parameter< std::decay_t< As > > || ... ); - - template< typename... As > - inline constexpr std::size_t parameter_size = ( parameter_size< std::decay_t< As > > + ... + 0 ); - - template< typename A > - inline constexpr std::size_t parameter_size< A > = parameter_traits< A >::columns; - - template< std::size_t Max > - inline constexpr std::size_t parameter_size< parameter< Max > > = Max; - - } // namespace internal - } // namespace tao::pq #endif diff --git a/include/tao/pq/parameter_traits.hpp b/include/tao/pq/parameter_traits.hpp index 94ea085..26188c4 100644 --- a/include/tao/pq/parameter_traits.hpp +++ b/include/tao/pq/parameter_traits.hpp @@ -13,12 +13,12 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include @@ -64,34 +64,18 @@ namespace tao::pq } // namespace internal template< typename T > - struct parameter_traits - { - static_assert( internal::dependent_false< T >, "data type T not registered as taopq parameter type" ); - - explicit parameter_traits( const T& /*unused*/ ) noexcept; - - static constexpr std::size_t columns = 1; - static constexpr bool self_contained = true; - - template< std::size_t I > - [[nodiscard]] static auto type() noexcept -> oid; - - template< std::size_t I > - [[nodiscard]] static auto value() noexcept -> const char*; + struct parameter_traits; - template< std::size_t I > - [[nodiscard]] static auto length() noexcept -> int; - - template< std::size_t I > - [[nodiscard]] static auto format() noexcept -> int; - - // for arrays - template< std::size_t I > - static void element( std::string& data ); - - // for table_writer - template< std::size_t I > - static void copy_to( std::string& data ); + template< typename T > + concept parameter_type_direct = requires( const parameter_traits< std::decay_t< T > >& t, std::string& s ) { + { parameter_traits< std::decay_t< T > >::columns } -> std::same_as< const std::size_t& >; + { parameter_traits< std::decay_t< T > >::self_contained } -> std::same_as< const bool& >; + { t.template type< 0 >() } -> std::same_as< oid >; + { t.template value< 0 >() } -> std::same_as< const char* >; + { t.template length< 0 >() } -> std::same_as< int >; + { t.template format< 0 >() } -> std::same_as< int >; + // TODO: { t.template element< 0 >( s ) } -> std::same_as< void >; + { t.template copy_to< 0 >( s ) } -> std::same_as< void >; }; template<> diff --git a/include/tao/pq/table_writer.hpp b/include/tao/pq/table_writer.hpp index 5c2d33e..6f79dca 100644 --- a/include/tao/pq/table_writer.hpp +++ b/include/tao/pq/table_writer.hpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -91,7 +92,7 @@ namespace tao::pq void insert_raw( const std::string_view data ); - template< typename... As > + template< parameter_type... As > void insert( As&&... as ) { static_assert( sizeof...( As ) >= 1, "calling tao::pq::table_writer::insert() requires at least one argument" ); diff --git a/include/tao/pq/transaction.hpp b/include/tao/pq/transaction.hpp index 54d65e5..45b59df 100644 --- a/include/tao/pq/transaction.hpp +++ b/include/tao/pq/transaction.hpp @@ -121,7 +121,7 @@ namespace tao::pq [[nodiscard]] auto subtransaction() -> std::shared_ptr< transaction >; - template< typename... As > + template< parameter_type... As > void send( const internal::zsv statement, As&&... as ) { if constexpr( sizeof...( As ) == 0 ) { @@ -148,7 +148,7 @@ namespace tao::pq [[nodiscard]] auto get_result( const std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now() ) -> result; - template< typename... As > + template< parameter_type... As > auto execute( const internal::zsv statement, As&&... as ) { const auto start = std::chrono::steady_clock::now(); diff --git a/src/test/pq/connection.cpp b/src/test/pq/connection.cpp index f67e71d..2fe3b1a 100644 --- a/src/test/pq/connection.cpp +++ b/src/test/pq/connection.cpp @@ -67,6 +67,9 @@ namespace // identifier, so you can always make sure that they can not be confused. connection->execute( "drop_table" ); + // statements must consume all parameters + TEST_THROWS( connection->execute( "drop_table", 42 ) ); + // a statement which is not a query does not return "affected rows" TEST_THROWS( connection->execute( "drop_table" ).rows_affected() ); @@ -126,6 +129,9 @@ namespace // read data TEST_ASSERT( connection->execute( "SELECT b FROM tao_connection_test WHERE a = 1" )[ 0 ][ 0 ].get() == std::string( "42" ) ); + TEST_THROWS( connection->execute( "SELECT $1" ) ); + TEST_THROWS( connection->execute( "SELECT $1", "One", "Two" ) ); + TEST_THROWS( connection->execute( "SELECT $1", "" ).as< tao::pq::binary >() ); TEST_THROWS( connection->execute( "SELECT $1", "\\" ).as< tao::pq::binary >() ); TEST_THROWS( connection->execute( "SELECT $1", "\\xa" ).as< tao::pq::binary >() );