Skip to content

Commit 0810262

Browse files
committed
Experimental parameter<> support for #71
1 parent f25b9e2 commit 0810262

File tree

4 files changed

+191
-0
lines changed

4 files changed

+191
-0
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ set(taopq_INCLUDE_FILES
4646
${taopq_INCLUDE_DIRS}/tao/pq/notification.hpp
4747
${taopq_INCLUDE_DIRS}/tao/pq/null.hpp
4848
${taopq_INCLUDE_DIRS}/tao/pq/oid.hpp
49+
${taopq_INCLUDE_DIRS}/tao/pq/parameter.hpp
4950
${taopq_INCLUDE_DIRS}/tao/pq/parameter_traits.hpp
5051
${taopq_INCLUDE_DIRS}/tao/pq/parameter_traits_aggregate.hpp
5152
${taopq_INCLUDE_DIRS}/tao/pq/parameter_traits_array.hpp

include/tao/pq/parameter.hpp

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Copyright (c) 2023 Daniel Frey and Dr. Colin Hirsch
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4+
5+
#ifndef TAO_PQ_PARAMETER_HPP
6+
#define TAO_PQ_PARAMETER_HPP
7+
8+
#include <cassert>
9+
#include <cstddef>
10+
#include <memory>
11+
#include <stdexcept>
12+
#include <type_traits>
13+
#include <utility>
14+
15+
#include <tao/pq/oid.hpp>
16+
#include <tao/pq/parameter_traits.hpp>
17+
18+
namespace tao::pq
19+
{
20+
class transaction;
21+
22+
// NOTE: for now, this is experimental and might change or vanish at any time!
23+
// TODO: naming?
24+
template< std::size_t Max = 16 >
25+
class parameter
26+
{
27+
private:
28+
struct binder
29+
{
30+
virtual ~binder() = default;
31+
};
32+
33+
template< typename T >
34+
class traits_binder : public binder
35+
{
36+
private:
37+
const parameter_traits< T > m_traits;
38+
39+
public:
40+
explicit traits_binder( const T& t ) noexcept( noexcept( parameter_traits< T >( t ) ) )
41+
: m_traits( t )
42+
{}
43+
44+
template< std::size_t... Is >
45+
void fill( Oid* types, const char** values, int* lengths, int* formats, std::index_sequence< Is... > /*unused*/ ) const // TODO: noexcept( ... )?
46+
{
47+
( ( types[ Is ] = static_cast< Oid >( m_traits.template type< Is >() ) ), ... );
48+
( ( values[ Is ] = m_traits.template value< Is >() ), ... );
49+
( ( lengths[ Is ] = m_traits.template length< Is >() ), ... );
50+
( ( formats[ Is ] = m_traits.template format< Is >() ), ... );
51+
}
52+
};
53+
54+
std::size_t m_pos = 0;
55+
std::unique_ptr< binder > m_binder[ Max ];
56+
57+
std::size_t m_size = 0;
58+
Oid m_types[ Max ];
59+
const char* m_values[ Max ];
60+
int m_lengths[ Max ];
61+
int m_formats[ Max ];
62+
63+
friend class transaction;
64+
65+
template< typename A >
66+
void bind_impl( const A& a ) // TODO: protect against binding temporaries!
67+
{
68+
constexpr auto columns = parameter_traits< std::decay_t< const A& > >::columns;
69+
if( m_size + columns > Max ) {
70+
throw std::length_error( "too many parameters!" );
71+
}
72+
73+
auto* bptr = new traits_binder< std::decay_t< const A& > >( a );
74+
m_binder[ m_pos++ ].reset( bptr );
75+
76+
bptr->fill( &m_types[ m_size ], &m_values[ m_size ], &m_lengths[ m_size ], &m_formats[ m_size ], std::make_index_sequence< columns >() );
77+
m_size += columns;
78+
}
79+
80+
public:
81+
// NOTE: arguments must remain VALID and UNMODIFIED until this object is destroyed or reset.
82+
template< typename... As >
83+
void bind( As&&... as )
84+
{
85+
( bind_impl( std::forward< As >( as ) ), ... );
86+
}
87+
88+
void reset() noexcept
89+
{
90+
for( std::size_t i = 0; i < m_pos; ++i ) {
91+
m_binder[ i ].reset();
92+
}
93+
m_pos = 0;
94+
m_size = 0;
95+
}
96+
};
97+
98+
} // namespace tao::pq
99+
100+
#endif

include/tao/pq/transaction.hpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <tao/pq/internal/gen.hpp>
2020
#include <tao/pq/internal/zsv.hpp>
2121
#include <tao/pq/oid.hpp>
22+
#include <tao/pq/parameter.hpp>
2223
#include <tao/pq/parameter_traits.hpp>
2324
#include <tao/pq/result.hpp>
2425

@@ -104,6 +105,18 @@ namespace tao::pq
104105
}
105106
}
106107

108+
template< std::size_t Max >
109+
void send( const internal::zsv statement, const parameter< Max >& as )
110+
{
111+
send_params( statement, as.m_size, as.m_types, as.m_values, as.m_lengths, as.m_formats );
112+
}
113+
114+
template< std::size_t Max >
115+
void send( const internal::zsv statement, parameter< Max >& as )
116+
{
117+
send( statement, const_cast< const parameter< Max >& >( as ) );
118+
}
119+
107120
[[nodiscard]] auto get_result( const std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now() ) -> result;
108121

109122
template< typename... As >

src/test/pq/parameter.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright (c) 2023 Daniel Frey and Dr. Colin Hirsch
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4+
5+
#include "../getenv.hpp"
6+
#include "../macros.hpp"
7+
8+
#include <iostream>
9+
#include <tao/pq.hpp>
10+
11+
void run()
12+
{
13+
// overwrite the default with an environment variable if needed
14+
const auto connection_string = tao::pq::internal::getenv( "TAOPQ_TEST_DATABASE", "dbname=template1" );
15+
16+
// open a connection to the database
17+
const auto conn = tao::pq::connection::create( connection_string );
18+
19+
// execute statements
20+
conn->execute( "DROP TABLE IF EXISTS tao_parameter" );
21+
conn->execute( "CREATE TABLE tao_parameter ( name TEXT PRIMARY KEY, age INTEGER NOT NULL )" );
22+
23+
// prepare statements
24+
conn->prepare( "insert_user", "INSERT INTO tao_parameter ( name, age ) VALUES ( $1, $2 )" );
25+
26+
{
27+
// begin transaction
28+
const auto tr = conn->transaction();
29+
30+
// execute previously prepared statements
31+
{
32+
tao::pq::parameter p;
33+
p.bind( "Daniel", 42 );
34+
tr->execute( "insert_user", p );
35+
p.reset();
36+
p.bind( "Tom" );
37+
p.bind( 41 );
38+
tr->execute( "insert_user", p );
39+
}
40+
41+
{
42+
tao::pq::parameter< 2 > p;
43+
p.bind( "Jerry" );
44+
p.bind( 29 );
45+
tr->execute( "insert_user", p );
46+
}
47+
48+
// commit transaction
49+
tr->commit();
50+
}
51+
52+
// query data
53+
const auto users = conn->execute( "SELECT name, age FROM tao_parameter WHERE age >= $1", 40 );
54+
55+
// iterate and convert results
56+
for( const auto& row : users ) {
57+
std::cout << row[ "name" ].as< std::string >() << " is "
58+
<< row[ "age" ].as< unsigned >() << " years old.\n";
59+
}
60+
}
61+
62+
auto main() -> int
63+
{
64+
try {
65+
run();
66+
}
67+
// LCOV_EXCL_START
68+
catch( const std::exception& e ) {
69+
std::cerr << "exception: " << e.what() << std::endl;
70+
throw;
71+
}
72+
catch( ... ) {
73+
std::cerr << "unknown exception" << std::endl;
74+
throw;
75+
}
76+
// LCOV_EXCL_STOP
77+
}

0 commit comments

Comments
 (0)