diff --git a/include/tao/pq/connection.hpp b/include/tao/pq/connection.hpp index 09b7ebe..88b8cb0 100644 --- a/include/tao/pq/connection.hpp +++ b/include/tao/pq/connection.hpp @@ -87,13 +87,15 @@ namespace tao::pq void cancel(); [[nodiscard]] auto get_result( const std::chrono::steady_clock::time_point end ) -> std::unique_ptr< PGresult, decltype( &PQclear ) >; + [[nodiscard]] auto get_fatal_error( const std::chrono::steady_clock::time_point end ) -> std::unique_ptr< PGresult, decltype( &PQclear ) >; + void consume_empty_result( const std::chrono::steady_clock::time_point end ); + [[nodiscard]] auto get_copy_data( char*& buffer, const std::chrono::steady_clock::time_point end ) -> std::size_t; [[nodiscard]] auto get_copy_data( char*& buffer ) -> std::size_t; void put_copy_data( const char* buffer, const std::size_t size ); void put_copy_end( const char* error_message = nullptr ); - void clear_results( const std::chrono::steady_clock::time_point end ); void clear_copy_data( const std::chrono::steady_clock::time_point end ); // pass-key idiom diff --git a/src/lib/pq/connection.cpp b/src/lib/pq/connection.cpp index 68d8ae1..3475b02 100644 --- a/src/lib/pq/connection.cpp +++ b/src/lib/pq/connection.cpp @@ -301,6 +301,29 @@ namespace tao::pq return result; } + auto connection::get_fatal_error( const std::chrono::steady_clock::time_point end ) -> std::unique_ptr< PGresult, decltype( &PQclear ) > + { + auto result = connection::get_result( end ); + if( !result ) { + throw std::runtime_error( "unable to obtain result" ); + } + + const auto status = PQresultStatus( result.get() ); + if( status != PGRES_FATAL_ERROR ) { + throw std::runtime_error( std::format( "unexpected result status: {}", PQresStatus( status ) ) ); + } + + return result; + } + + void connection::consume_empty_result( const std::chrono::steady_clock::time_point end ) + { + if( const auto result = connection::get_result( end ) ) { + const auto status = PQresultStatus( result.get() ); + throw std::runtime_error( std::format( "unexpected result status: {}", PQresStatus( status ) ) ); + } + } + auto connection::get_copy_data( char*& buffer, const std::chrono::steady_clock::time_point end ) -> std::size_t { while( true ) { @@ -378,12 +401,6 @@ namespace tao::pq } } - void connection::clear_results( const std::chrono::steady_clock::time_point end ) - { - while( connection::get_result( end ) ) { - } - } - void connection::clear_copy_data( const std::chrono::steady_clock::time_point end ) { char* ptr; @@ -540,7 +557,7 @@ namespace tao::pq const auto result = connection::get_result( end ); switch( PQresultStatus( result.get() ) ) { case PGRES_COMMAND_OK: - connection::clear_results( end ); + connection::consume_empty_result( end ); break; case PGRES_TUPLES_OK: @@ -550,7 +567,7 @@ namespace tao::pq TAO_PQ_INTERNAL_UNREACHABLE; // LCOV_EXCL_LINE default: - connection::clear_results( end ); + connection::consume_empty_result( end ); internal::throw_sqlstate( result.get() ); } diff --git a/src/lib/pq/table_reader.cpp b/src/lib/pq/table_reader.cpp index bf0aeaa..c4647ae 100644 --- a/src/lib/pq/table_reader.cpp +++ b/src/lib/pq/table_reader.cpp @@ -36,15 +36,15 @@ namespace tao::pq case PGRES_COMMAND_OK: case PGRES_TUPLES_OK: - m_transaction->connection()->clear_results( end ); + m_transaction->connection()->consume_empty_result( end ); throw std::runtime_error( "expected COPY TO statement" ); case PGRES_EMPTY_QUERY: - m_transaction->connection()->clear_results( end ); + m_transaction->connection()->consume_empty_result( end ); throw std::runtime_error( "unexpected empty query" ); default: - m_transaction->connection()->clear_results( end ); + m_transaction->connection()->consume_empty_result( end ); internal::throw_sqlstate( result.get() ); } } diff --git a/src/lib/pq/table_writer.cpp b/src/lib/pq/table_writer.cpp index 3ca2775..9a2b976 100644 --- a/src/lib/pq/table_writer.cpp +++ b/src/lib/pq/table_writer.cpp @@ -33,7 +33,7 @@ namespace tao::pq void table_writer::check_result() { const auto end = m_transaction->connection()->timeout_end(); - auto result = m_transaction->connection()->get_result( end ); + const auto result = m_transaction->connection()->get_result( end ); switch( PQresultStatus( result.get() ) ) { case PGRES_COPY_IN: break; @@ -41,20 +41,21 @@ namespace tao::pq case PGRES_COPY_OUT: m_transaction->connection()->cancel(); m_transaction->connection()->clear_copy_data( end ); - m_transaction->connection()->clear_results( end ); + std::ignore = m_transaction->connection()->get_fatal_error( end ); + m_transaction->connection()->consume_empty_result( end ); throw std::runtime_error( "unexpected COPY TO statement" ); case PGRES_COMMAND_OK: case PGRES_TUPLES_OK: - m_transaction->connection()->clear_results( end ); + m_transaction->connection()->consume_empty_result( end ); throw std::runtime_error( "expected COPY FROM statement" ); case PGRES_EMPTY_QUERY: - m_transaction->connection()->clear_results( end ); + m_transaction->connection()->consume_empty_result( end ); throw std::runtime_error( "unexpected empty query" ); default: - m_transaction->connection()->clear_results( end ); + m_transaction->connection()->consume_empty_result( end ); internal::throw_sqlstate( result.get() ); } } diff --git a/src/lib/pq/transaction.cpp b/src/lib/pq/transaction.cpp index bea2997..a5d7728 100644 --- a/src/lib/pq/transaction.cpp +++ b/src/lib/pq/transaction.cpp @@ -173,12 +173,14 @@ namespace tao::pq switch( PQresultStatus( result.get() ) ) { case PGRES_COPY_IN: m_connection->put_copy_end( "unexpected COPY FROM statement" ); + result = m_connection->get_fatal_error( end ); break; case PGRES_COPY_OUT: m_connection->cancel(); m_connection->clear_copy_data( end ); - m_connection->clear_results( end ); + std::ignore = m_connection->get_fatal_error( end ); + m_connection->consume_empty_result( end ); throw std::runtime_error( "unexpected COPY TO statement" ); case PGRES_SINGLE_TUPLE: @@ -190,10 +192,7 @@ namespace tao::pq default:; } - while( auto next = m_connection->get_result( end ) ) { - result = std::move( next ); - } - + m_connection->consume_empty_result( end ); return pq::result( result.release() ); }