diff --git a/NEWS b/NEWS index 198c053b8..8b77b46c8 100644 --- a/NEWS +++ b/NEWS @@ -31,9 +31,8 @@ - Support for `PQinitOpenSSL()`. (#678) - Slightly more helpful error for unsupported conversions. (#695) - Replace some C++ feature tests with C++20 feature macros. - - Give `stream_to` a move constructor. (#706) - - Support move in `stream_to`. (#706) ->>>>>>> 42a46214 (Support move assignment as well.) + - Support moving of `stream_to`. (#706) + - Incorporate `source_location` in exceptions. 7.7.4 - `transaction_base::for_each()` is now called `for_stream()`. (#580) - New `transaction_base::for_query()` is similar, but non-streaming. (#580) diff --git a/config/Makefile.in b/config/Makefile.in index ef0904008..bb5865a19 100644 --- a/config/Makefile.in +++ b/config/Makefile.in @@ -123,7 +123,7 @@ am__can_run_installinfo = \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in compile config.guess \ - config.sub install-sh ltmain.sh missing mkinstalldirs + config.sub depcomp install-sh ltmain.sh missing mkinstalldirs DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ diff --git a/include/pqxx/except.hxx b/include/pqxx/except.hxx index 267295b77..55947e57d 100644 --- a/include/pqxx/except.hxx +++ b/include/pqxx/except.hxx @@ -17,6 +17,10 @@ # error "Include libpqxx headers as , not ." #endif +#if pqxx_have_source_location +#include +#endif + #include #include @@ -42,7 +46,15 @@ namespace pqxx /// Run-time failure encountered by libpqxx, similar to std::runtime_error. struct PQXX_LIBEXPORT failure : std::runtime_error { +#if pqxx_have_source_location + explicit failure( + std::string const &, + std::source_location = std::source_location::current() + ); + std::source_location location; +#else explicit failure(std::string const &); +#endif }; @@ -57,11 +69,11 @@ struct PQXX_LIBEXPORT failure : std::runtime_error * signal harmless is to make your program ignore it: * * ```cxx - * #include + * #include * * int main() * { - * signal(SIGPIPE, SIG_IGN); + * std::signal(SIGPIPE, SIG_IGN); * // ... * } * ``` @@ -69,11 +81,15 @@ struct PQXX_LIBEXPORT failure : std::runtime_error struct PQXX_LIBEXPORT broken_connection : failure { broken_connection(); - explicit broken_connection(std::string const &); + explicit broken_connection(std::string const & +#if pqxx_have_source_location + , std::source_location = std::source_location::current() +#endif + ); }; -/// Exception class for micommunication with th server. +/// Exception class for micommunication with the server. /** This happens when the conversation between libpq and the server gets messed * up. There aren't many situations where this happens, but one known instance * is when you call a parameterised or prepared statement with th ewrong number @@ -84,15 +100,23 @@ struct PQXX_LIBEXPORT broken_connection : failure */ struct PQXX_LIBEXPORT protocol_violation : broken_connection { - explicit protocol_violation(std::string const &); + explicit protocol_violation(std::string const & +#if pqxx_have_source_location + , std::source_location = std::source_location::current() +#endif + ); }; /// The caller attempted to set a variable to null, which is not allowed. struct PQXX_LIBEXPORT variable_set_to_null : failure { - variable_set_to_null(); - explicit variable_set_to_null(std::string const &); + explicit variable_set_to_null( + std::string const & +#if pqxx_have_source_location + , std::source_location = std::source_location::current() +#endif + ); }; @@ -110,7 +134,11 @@ class PQXX_LIBEXPORT sql_error : public failure public: explicit sql_error( std::string const &whatarg = "", std::string const &Q = "", - char const sqlstate[] = nullptr); + char const sqlstate[] = nullptr +#if pqxx_have_source_location + , std::source_location = std::source_location::current() +#endif + ); virtual ~sql_error() noexcept override; /// The query whose execution triggered the exception @@ -130,7 +158,11 @@ public: */ struct PQXX_LIBEXPORT in_doubt_error : failure { - explicit in_doubt_error(std::string const &); + explicit in_doubt_error(std::string const & +#if pqxx_have_source_location + , std::source_location = std::source_location::current() +#endif + ); }; @@ -139,7 +171,11 @@ struct PQXX_LIBEXPORT transaction_rollback : sql_error { explicit transaction_rollback( std::string const &whatarg, std::string const &q = "", - char const sqlstate[] = nullptr); + char const sqlstate[] = nullptr +#if pqxx_have_source_location + , std::source_location = std::source_location::current() +#endif + ); }; @@ -156,7 +192,11 @@ struct PQXX_LIBEXPORT serialization_failure : transaction_rollback { explicit serialization_failure( std::string const &whatarg, std::string const &q, - char const sqlstate[] = nullptr); + char const sqlstate[] = nullptr +#if pqxx_have_source_location + , std::source_location = std::source_location::current() +#endif + ); }; @@ -165,7 +205,11 @@ struct PQXX_LIBEXPORT statement_completion_unknown : transaction_rollback { explicit statement_completion_unknown( std::string const &whatarg, std::string const &q, - char const sqlstate[] = nullptr); + char const sqlstate[] = nullptr +#if pqxx_have_source_location + , std::source_location = std::source_location::current() +#endif + ); }; @@ -174,7 +218,11 @@ struct PQXX_LIBEXPORT deadlock_detected : transaction_rollback { explicit deadlock_detected( std::string const &whatarg, std::string const &q, - char const sqlstate[] = nullptr); + char const sqlstate[] = nullptr +#if pqxx_have_source_location + , std::source_location = std::source_location::current() +#endif + ); }; @@ -188,151 +236,298 @@ struct PQXX_LIBEXPORT internal_error : std::logic_error /// Error in usage of libpqxx library, similar to std::logic_error struct PQXX_LIBEXPORT usage_error : std::logic_error { - explicit usage_error(std::string const &); + explicit usage_error(std::string const & +#if pqxx_have_source_location + , std::source_location = std::source_location::current() +#endif + ); + +#if pqxx_have_source_location + std::source_location location; +#endif }; /// Invalid argument passed to libpqxx, similar to std::invalid_argument struct PQXX_LIBEXPORT argument_error : std::invalid_argument { - explicit argument_error(std::string const &); + explicit argument_error(std::string const & +#if pqxx_have_source_location + , std::source_location = std::source_location::current() +#endif + ); + +#if pqxx_have_source_location + std::source_location location; +#endif }; /// Value conversion failed, e.g. when converting "Hello" to int. struct PQXX_LIBEXPORT conversion_error : std::domain_error { - explicit conversion_error(std::string const &); + explicit conversion_error(std::string const & +#if pqxx_have_source_location + , std::source_location = std::source_location::current() +#endif + ); + +#if pqxx_have_source_location + std::source_location location; +#endif }; /// Could not convert null value: target type does not support null. struct PQXX_LIBEXPORT unexpected_null : conversion_error { - explicit unexpected_null(std::string const &); + explicit unexpected_null(std::string const & +#if pqxx_have_source_location + , std::source_location = std::source_location::current() +#endif + ); }; /// Could not convert value to string: not enough buffer space. struct PQXX_LIBEXPORT conversion_overrun : conversion_error { - explicit conversion_overrun(std::string const &); + explicit conversion_overrun(std::string const & +#if pqxx_have_source_location + , std::source_location = std::source_location::current() +#endif + ); }; /// Something is out of range, similar to std::out_of_range struct PQXX_LIBEXPORT range_error : std::out_of_range { - explicit range_error(std::string const &); + explicit range_error(std::string const & +#if pqxx_have_source_location + , std::source_location = std::source_location::current() +#endif + ); + +#if pqxx_have_source_location + std::source_location location; +#endif }; /// Query returned an unexpected number of rows. struct PQXX_LIBEXPORT unexpected_rows : public range_error { +#if pqxx_have_source_location + explicit unexpected_rows( + std::string const &msg, + std::source_location loc = std::source_location::current() + ) : + range_error{msg, loc} {} +#else explicit unexpected_rows(std::string const &msg) : range_error{msg} {} +#endif }; /// Database feature not supported in current setup. struct PQXX_LIBEXPORT feature_not_supported : sql_error { +#if pqxx_have_source_location + explicit feature_not_supported( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : + sql_error{err, Q, sqlstate, loc} + {} +#else explicit feature_not_supported( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : sql_error{err, Q, sqlstate} {} +#endif }; /// Error in data provided to SQL statement. struct PQXX_LIBEXPORT data_exception : sql_error { +#if pqxx_have_source_location + explicit data_exception( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : + sql_error{err, Q, sqlstate, loc} + {} +#else explicit data_exception( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : sql_error{err, Q, sqlstate} {} +#endif }; struct PQXX_LIBEXPORT integrity_constraint_violation : sql_error { +#if pqxx_have_source_location + explicit integrity_constraint_violation( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : + sql_error{err, Q, sqlstate, loc} + {} +#else explicit integrity_constraint_violation( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : sql_error{err, Q, sqlstate} {} +#endif }; struct PQXX_LIBEXPORT restrict_violation : integrity_constraint_violation { +#if pqxx_have_source_location + explicit restrict_violation( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : + integrity_constraint_violation{err, Q, sqlstate, loc} + {} +#else explicit restrict_violation( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : integrity_constraint_violation{err, Q, sqlstate} {} +#endif }; struct PQXX_LIBEXPORT not_null_violation : integrity_constraint_violation { +#if pqxx_have_source_location + explicit not_null_violation( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : + integrity_constraint_violation{err, Q, sqlstate, loc} + {} +#else explicit not_null_violation( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : integrity_constraint_violation{err, Q, sqlstate} {} +#endif }; struct PQXX_LIBEXPORT foreign_key_violation : integrity_constraint_violation { +#if pqxx_have_source_location + explicit foreign_key_violation( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : + integrity_constraint_violation{err, Q, sqlstate, loc} + {} +#else explicit foreign_key_violation( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : integrity_constraint_violation{err, Q, sqlstate} {} +#endif }; struct PQXX_LIBEXPORT unique_violation : integrity_constraint_violation { +#if pqxx_have_source_location + explicit unique_violation( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : + integrity_constraint_violation{err, Q, sqlstate, loc} + {} +#else explicit unique_violation( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : integrity_constraint_violation{err, Q, sqlstate} {} +#endif }; struct PQXX_LIBEXPORT check_violation : integrity_constraint_violation { +#if pqxx_have_source_location + explicit check_violation( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : + integrity_constraint_violation{err, Q, sqlstate, loc} + {} +#else explicit check_violation( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : integrity_constraint_violation{err, Q, sqlstate} {} +#endif }; struct PQXX_LIBEXPORT invalid_cursor_state : sql_error { +#if pqxx_have_source_location + explicit invalid_cursor_state( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : + sql_error{err, Q, sqlstate, loc} + {} +#else explicit invalid_cursor_state( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : sql_error{err, Q, sqlstate} {} +#endif }; struct PQXX_LIBEXPORT invalid_sql_statement_name : sql_error { +#if pqxx_have_source_location + explicit invalid_sql_statement_name( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : + sql_error{err, Q, sqlstate, loc} + {} +#else explicit invalid_sql_statement_name( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : sql_error{err, Q, sqlstate} {} +#endif }; struct PQXX_LIBEXPORT invalid_cursor_name : sql_error { +#if pqxx_have_source_location + explicit invalid_cursor_name( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : + sql_error{err, Q, sqlstate, loc} + {} +#else explicit invalid_cursor_name( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : sql_error{err, Q, sqlstate} {} +#endif }; struct PQXX_LIBEXPORT syntax_error : sql_error @@ -340,82 +535,166 @@ struct PQXX_LIBEXPORT syntax_error : sql_error /// Approximate position in string where error occurred, or -1 if unknown. int const error_position; +#if pqxx_have_source_location + explicit syntax_error( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, int pos = -1, + std::source_location loc = std::source_location::current()) : + sql_error{err, Q, sqlstate, loc}, error_position{pos} + {} +#else explicit syntax_error( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr, int pos = -1) : sql_error{err, Q, sqlstate}, error_position{pos} {} +#endif }; struct PQXX_LIBEXPORT undefined_column : syntax_error { +#if pqxx_have_source_location + explicit undefined_column( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : +// TODO: Can we get the column? + syntax_error{err, Q, sqlstate, -1, loc} + {} +#else explicit undefined_column( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : syntax_error{err, Q, sqlstate} {} +#endif }; struct PQXX_LIBEXPORT undefined_function : syntax_error { +#if pqxx_have_source_location + explicit undefined_function( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : +// TODO: Can we get the column? + syntax_error{err, Q, sqlstate, -1, loc} + {} +#else explicit undefined_function( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : syntax_error{err, Q, sqlstate} {} +#endif }; struct PQXX_LIBEXPORT undefined_table : syntax_error { +#if pqxx_have_source_location + explicit undefined_table( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : +// TODO: Can we get the column? + syntax_error{err, Q, sqlstate, -1, loc} + {} +#else explicit undefined_table( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : syntax_error{err, Q, sqlstate} {} +#endif }; struct PQXX_LIBEXPORT insufficient_privilege : sql_error { +#if pqxx_have_source_location + explicit insufficient_privilege( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : + sql_error{err, Q, sqlstate, loc} + {} +#else explicit insufficient_privilege( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : sql_error{err, Q, sqlstate} {} +#endif }; /// Resource shortage on the server struct PQXX_LIBEXPORT insufficient_resources : sql_error { +#if pqxx_have_source_location + explicit insufficient_resources( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : + sql_error{err, Q, sqlstate, loc} + {} +#else explicit insufficient_resources( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : sql_error{err, Q, sqlstate} {} +#endif }; struct PQXX_LIBEXPORT disk_full : insufficient_resources { +#if pqxx_have_source_location + explicit disk_full( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : + insufficient_resources{err, Q, sqlstate, loc} + {} +#else explicit disk_full( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : insufficient_resources{err, Q, sqlstate} {} +#endif }; struct PQXX_LIBEXPORT out_of_memory : insufficient_resources { +#if pqxx_have_source_location + explicit out_of_memory( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : + insufficient_resources{err, Q, sqlstate, loc} + {} +#else explicit out_of_memory( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : insufficient_resources{err, Q, sqlstate} {} +#endif }; struct PQXX_LIBEXPORT too_many_connections : broken_connection { +#if pqxx_have_source_location + explicit too_many_connections( + std::string const &err, + std::source_location loc = std::source_location::current() + ) : + broken_connection{err, loc} + {} +#else explicit too_many_connections(std::string const &err) : broken_connection{err} {} +#endif }; /// PL/pgSQL error @@ -423,39 +702,75 @@ struct PQXX_LIBEXPORT too_many_connections : broken_connection */ struct PQXX_LIBEXPORT plpgsql_error : sql_error { +#if pqxx_have_source_location + explicit plpgsql_error( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : + sql_error{err, Q, sqlstate, loc} + {} +#else explicit plpgsql_error( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : sql_error{err, Q, sqlstate} {} +#endif }; /// Exception raised in PL/pgSQL procedure struct PQXX_LIBEXPORT plpgsql_raise : plpgsql_error { +#if pqxx_have_source_location + explicit plpgsql_raise( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : + plpgsql_error{err, Q, sqlstate, loc} + {} +#else explicit plpgsql_raise( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : plpgsql_error{err, Q, sqlstate} {} +#endif }; struct PQXX_LIBEXPORT plpgsql_no_data_found : plpgsql_error { +#if pqxx_have_source_location + explicit plpgsql_no_data_found( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : + plpgsql_error{err, Q, sqlstate, loc} + {} +#else explicit plpgsql_no_data_found( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : plpgsql_error{err, Q, sqlstate} {} +#endif }; struct PQXX_LIBEXPORT plpgsql_too_many_rows : plpgsql_error { +#if pqxx_have_source_location + explicit plpgsql_too_many_rows( + std::string const &err, std::string const &Q = "", + char const sqlstate[] = nullptr, + std::source_location loc = std::source_location::current()) : + plpgsql_error{err, Q, sqlstate, loc} + {} +#else explicit plpgsql_too_many_rows( std::string const &err, std::string const &Q = "", char const sqlstate[] = nullptr) : plpgsql_error{err, Q, sqlstate} {} +#endif }; /** diff --git a/include/pqxx/internal/cxx-features.hxx b/include/pqxx/internal/cxx-features.hxx index db0dde0f9..7e3e9733e 100644 --- a/include/pqxx/internal/cxx-features.hxx +++ b/include/pqxx/internal/cxx-features.hxx @@ -24,6 +24,12 @@ #define pqxx_have_multidim 0 #endif // pqxx_have_multidim +#if defined(__cpp_lib_source_location) && __cpp_lib_source_location +#define pqxx_have_source_location 1 +#else +#define pqxx_have_source_location 0 +#endif // __cpp_lib_source_location + #if defined(__cpp_lib_ssize) && __cpp_lib_ssize #define pqxx_have_ssize 1 #else diff --git a/include/pqxx/internal/header-pre.hxx b/include/pqxx/internal/header-pre.hxx index d247a79a5..595a53b45 100644 --- a/include/pqxx/internal/header-pre.hxx +++ b/include/pqxx/internal/header-pre.hxx @@ -17,6 +17,12 @@ * mistake, or contact the author. */ +#if __has_include() +#include +#endif + +#include "pqxx/internal/cxx-features.hxx" + // NO GUARD HERE! This part should be included every time this file is. #if defined(_MSC_VER) diff --git a/include/pqxx/result.hxx b/include/pqxx/result.hxx index a274d4f1f..59c61fbf3 100644 --- a/include/pqxx/result.hxx +++ b/include/pqxx/result.hxx @@ -23,7 +23,6 @@ #include #include "pqxx/except.hxx" -#include "pqxx/internal/cxx-features.hxx" #include "pqxx/types.hxx" #include "pqxx/util.hxx" #include "pqxx/zview.hxx" diff --git a/include/pqxx/util.hxx b/include/pqxx/util.hxx index a3687e777..e3d107d5b 100644 --- a/include/pqxx/util.hxx +++ b/include/pqxx/util.hxx @@ -32,12 +32,7 @@ #include #include -#if __has_include() -# include -#endif - #include "pqxx/except.hxx" -#include "pqxx/internal/cxx-features.hxx" #include "pqxx/types.hxx" #include "pqxx/version.hxx" diff --git a/src/blob.cxx b/src/blob.cxx index 1492d9107..9e16fdaa1 100644 --- a/src/blob.cxx +++ b/src/blob.cxx @@ -118,9 +118,8 @@ pqxx::blob::~blob() catch (std::exception const &e) { if (m_conn != nullptr) - PQXX_UNLIKELY - m_conn->process_notice(internal::concat( - "Failure while closing binary large object: ", e.what(), "\n")); + m_conn->process_notice(internal::concat( + "Failure while closing binary large object: ", e.what(), "\n")); } } diff --git a/src/except.cxx b/src/except.cxx index 90d5c5f66..737e3131c 100644 --- a/src/except.cxx +++ b/src/except.cxx @@ -15,9 +15,16 @@ #include "pqxx/internal/header-post.hxx" +#if pqxx_have_source_location +pqxx::failure::failure(std::string const &whatarg, std::source_location loc) : + std::runtime_error{whatarg}, + location{loc} +{} +#else pqxx::failure::failure(std::string const &whatarg) : std::runtime_error{whatarg} {} +#endif pqxx::broken_connection::broken_connection() : @@ -25,30 +32,63 @@ pqxx::broken_connection::broken_connection() : {} -pqxx::broken_connection::broken_connection(std::string const &whatarg) : - failure{whatarg} -{} - - -pqxx::protocol_violation::protocol_violation(std::string const &whatarg) : - broken_connection{whatarg} +pqxx::broken_connection::broken_connection( + std::string const &whatarg +#if pqxx_have_source_location + , std::source_location loc +#endif + ) : + failure{whatarg +#if pqxx_have_source_location + , loc +#endif + } {} -pqxx::variable_set_to_null::variable_set_to_null() : - variable_set_to_null{ - "Attempt to set a variable to null. This is not allowed."} +pqxx::protocol_violation::protocol_violation( + std::string const &whatarg +#if pqxx_have_source_location + , std::source_location loc +#endif +) : + broken_connection{whatarg +#if pqxx_have_source_location + , loc +#endif + } {} -pqxx::variable_set_to_null::variable_set_to_null(std::string const &whatarg) : - failure{whatarg} +pqxx::variable_set_to_null::variable_set_to_null( + std::string const &whatarg +#if pqxx_have_source_location + , std::source_location loc +#endif +) : + failure{ + whatarg +#if pqxx_have_source_location + , loc +#endif + } {} pqxx::sql_error::sql_error( - std::string const &whatarg, std::string const &Q, char const sqlstate[]) : - failure{whatarg}, m_query{Q}, m_sqlstate{sqlstate ? sqlstate : ""} + std::string const &whatarg, std::string const &Q, char const sqlstate[] +#if pqxx_have_source_location + , std::source_location loc +#endif +) : + failure{ + whatarg +#if pqxx_have_source_location + , loc +#endif + }, + m_query{Q}, + m_sqlstate{sqlstate ? sqlstate : ""} {} @@ -67,32 +107,78 @@ PQXX_PURE std::string const &pqxx::sql_error::sqlstate() const noexcept } -pqxx::in_doubt_error::in_doubt_error(std::string const &whatarg) : - failure{whatarg} +pqxx::in_doubt_error::in_doubt_error( + std::string const &whatarg +#if pqxx_have_source_location + , std::source_location loc +#endif +) : + failure{ + whatarg +#if pqxx_have_source_location + , loc +#endif + } {} pqxx::transaction_rollback::transaction_rollback( - std::string const &whatarg, std::string const &q, char const sqlstate[]) : - sql_error{whatarg, q, sqlstate} + std::string const &whatarg, std::string const &q, char const sqlstate[] +#if pqxx_have_source_location + , std::source_location loc +#endif + ) : + sql_error{ + whatarg, q, sqlstate +#if pqxx_have_source_location + , loc +#endif + } {} pqxx::serialization_failure::serialization_failure( - std::string const &whatarg, std::string const &q, char const sqlstate[]) : - transaction_rollback{whatarg, q, sqlstate} + std::string const &whatarg, std::string const &q, char const sqlstate[] +#if pqxx_have_source_location + , std::source_location loc +#endif +) : + transaction_rollback{ + whatarg, q, sqlstate +#if pqxx_have_source_location + , loc +#endif + } {} pqxx::statement_completion_unknown::statement_completion_unknown( - std::string const &whatarg, std::string const &q, char const sqlstate[]) : - transaction_rollback{whatarg, q, sqlstate} + std::string const &whatarg, std::string const &q, char const sqlstate[] +#if pqxx_have_source_location + , std::source_location loc +#endif +) : + transaction_rollback{ + whatarg, q, sqlstate +#if pqxx_have_source_location + , loc +#endif + } {} pqxx::deadlock_detected::deadlock_detected( - std::string const &whatarg, std::string const &q, char const sqlstate[]) : - transaction_rollback{whatarg, q, sqlstate} + std::string const &whatarg, std::string const &q, char const sqlstate[] +#if pqxx_have_source_location + , std::source_location loc +#endif +) : + transaction_rollback{ + whatarg, q, sqlstate +#if pqxx_have_source_location + , loc +#endif + } {} @@ -101,31 +187,83 @@ pqxx::internal_error::internal_error(std::string const &whatarg) : {} -pqxx::usage_error::usage_error(std::string const &whatarg) : +pqxx::usage_error::usage_error( + std::string const &whatarg +#if pqxx_have_source_location + , std::source_location loc +#endif +) : std::logic_error{whatarg} +#if pqxx_have_source_location + , location{loc} +#endif {} -pqxx::argument_error::argument_error(std::string const &whatarg) : +pqxx::argument_error::argument_error( + std::string const &whatarg +#if pqxx_have_source_location + , std::source_location loc +#endif +) : invalid_argument{whatarg} +#if pqxx_have_source_location + , location{loc} +#endif {} -pqxx::conversion_error::conversion_error(std::string const &whatarg) : +pqxx::conversion_error::conversion_error( + std::string const &whatarg +#if pqxx_have_source_location + , std::source_location loc +#endif +) : domain_error{whatarg} +#if pqxx_have_source_location + , location{loc} +#endif {} -pqxx::unexpected_null::unexpected_null(std::string const &whatarg) : - conversion_error{whatarg} +pqxx::unexpected_null::unexpected_null( + std::string const &whatarg +#if pqxx_have_source_location + , std::source_location loc +#endif +) : + conversion_error{ + whatarg +#if pqxx_have_source_location + , loc +#endif + } {} -pqxx::conversion_overrun::conversion_overrun(std::string const &whatarg) : - conversion_error{whatarg} +pqxx::conversion_overrun::conversion_overrun( + std::string const &whatarg +#if pqxx_have_source_location + , std::source_location loc +#endif +) : + conversion_error{ + whatarg +#if pqxx_have_source_location + , loc +#endif + } {} -pqxx::range_error::range_error(std::string const &whatarg) : +pqxx::range_error::range_error( + std::string const &whatarg +#if pqxx_have_source_location + , std::source_location loc +#endif +) : out_of_range{whatarg} +#if pqxx_have_source_location + , location{loc} +#endif {} diff --git a/src/transaction.cxx b/src/transaction.cxx index 8e5fc7ed5..439e6c5d1 100644 --- a/src/transaction.cxx +++ b/src/transaction.cxx @@ -78,7 +78,12 @@ void pqxx::internal::basic_transaction::do_commit() process_notice(msg); // Strip newline. It was only needed for process_notice(). msg.pop_back(); - throw in_doubt_error{std::move(msg)}; + throw in_doubt_error{ + std::move(msg) +#if pqxx_have_source_location + , e.location +#endif + }; } catch (std::exception const &e) { diff --git a/test/runner.cxx b/test/runner.cxx index 38c9849d8..dddc24896 100644 --- a/test/runner.cxx +++ b/test/runner.cxx @@ -23,10 +23,16 @@ inline std::string deref_field(pqxx::field const &f) namespace pqxx::test { +#if pqxx_have_source_location +test_failure::test_failure(std::string const &desc, std::source_location loc) : + std::logic_error{desc}, m_loc{loc} +{} +#else test_failure::test_failure( std::string const &ffile, int fline, std::string const &desc) : std::logic_error(desc), m_file(ffile), m_line(fline) {} +#endif test_failure::~test_failure() noexcept = default; @@ -39,19 +45,49 @@ inline void drop_table(transaction_base &t, std::string const &table) [[noreturn]] void -check_notreached(char const file[], int line, std::string desc) +check_notreached( +#if !pqxx_have_source_location + char const file[], int line, +#endif + std::string desc +#if pqxx_have_source_location + , std::source_location loc +#endif +) { - throw test_failure(file, line, desc); + throw test_failure{ +#if !pqxx_have_source_location + file, line, +#endif + desc +#if pqxx_have_source_location + , loc +#endif + }; } void check( - char const file[], int line, bool condition, char const text[], - std::string const &desc) +#if !pqxx_have_source_location + char const file[], int line, +#endif + bool condition, char const text[], + std::string const &desc +#if pqxx_have_source_location + , std::source_location loc +#endif +) { if (not condition) - throw test_failure( - file, line, desc + " (failed expression: " + text + ")"); + throw test_failure{ +#if !pqxx_have_source_location + file, line, +#endif + desc + " (failed expression: " + text + ")" +#if pqxx_have_source_location + , loc +#endif + }; } @@ -154,9 +190,10 @@ int main(int argc, char const *argv[]) } catch (pqxx::test::test_failure const &e) { - std::cerr << "Test failure in " + e.file() + " line " + - pqxx::to_string(e.line()) - << ": " << e.what() << std::endl; + std::cerr + << "Test failure in " << e.file() << " line " + << pqxx::to_string(e.line()) + << ": " << e.what() << std::endl; } catch (std::bad_alloc const &) { @@ -164,15 +201,30 @@ int main(int argc, char const *argv[]) } catch (pqxx::feature_not_supported const &e) { - std::cerr << "Not testing unsupported feature: " << e.what() - << std::endl; + std::cerr << "Not testing unsupported feature: " << e.what() << '\n'; +#if pqxx_have_source_location + std::string func{e.location.function_name()}; + std::cerr << "("; + std::cerr << e.location.file_name() << ':' << e.location.line(); + if (not func.empty()) + std::cerr << " in " << e.location.function_name(); + std::cerr << ")\n"; +#endif success = true; --test_count; } catch (pqxx::sql_error const &e) { - std::cerr << "SQL error: " << e.what() << std::endl - << "Query was: " << e.query() << std::endl; + std::cerr << "SQL error: " << e.what() << '\n'; +#if pqxx_have_source_location + std::string func{e.location.function_name()}; + std::cerr << "("; + std::cerr << e.location.file_name() << ':' << e.location.line(); + if (not func.empty()) + std::cerr << " in " << e.location.function_name(); + std::cerr << ")\n"; +#endif + std::cerr << "Query was: " << e.query() << std::endl; } catch (std::exception const &e) { diff --git a/test/test_helpers.hxx b/test/test_helpers.hxx index e88110e0f..87d88a00f 100644 --- a/test/test_helpers.hxx +++ b/test/test_helpers.hxx @@ -10,16 +10,32 @@ namespace test { class test_failure : public std::logic_error { - std::string const m_file; - int m_line; - public: +#if pqxx_have_source_location + test_failure( + std::string const &desc, + std::source_location loc=std::source_location::current()); +#else test_failure(std::string const &ffile, int fline, std::string const &desc); +#endif ~test_failure() noexcept override; +#if pqxx_have_source_location + constexpr char const *file() const noexcept { return m_loc.file_name(); } + constexpr auto line() const noexcept { return m_loc.line(); } +#else std::string const &file() const noexcept { return m_file; } int line() const noexcept { return m_line; } +#endif + +private: +#if pqxx_have_source_location + std::source_location m_loc; +#else + std::string const m_file; + int m_line; +#endif }; @@ -52,26 +68,62 @@ struct registrar // Unconditional test failure. +#if pqxx_have_source_location +#define PQXX_CHECK_NOTREACHED(desc) pqxx::test::check_notreached((desc)) +#else #define PQXX_CHECK_NOTREACHED(desc) \ pqxx::test::check_notreached(__FILE__, __LINE__, (desc)) +#endif [[noreturn]] void -check_notreached(char const file[], int line, std::string desc); +check_notreached( +#if !pqxx_have_source_location + char const file[], int line, +#endif + std::string desc +#if pqxx_have_source_location + , std::source_location loc=std::source_location::current() +#endif +); // Verify that a condition is met, similar to assert() +#if pqxx_have_source_location +#define PQXX_CHECK(condition, desc) \ + pqxx::test::check((condition), #condition, (desc)) +#else #define PQXX_CHECK(condition, desc) \ pqxx::test::check(__FILE__, __LINE__, (condition), #condition, (desc)) +#endif void check( - char const file[], int line, bool condition, char const text[], - std::string const &desc); +#if !pqxx_have_source_location + char const file[], int line, +#endif + bool condition, char const text[], + std::string const &desc +#if pqxx_have_source_location + , std::source_location loc=std::source_location::current() +#endif +); // Verify that variable has the expected value. +#if pqxx_have_source_location +#define PQXX_CHECK_EQUAL(actual, expected, desc) \ + pqxx::test::check_equal((actual), #actual, (expected), #expected, (desc)) +#else #define PQXX_CHECK_EQUAL(actual, expected, desc) \ pqxx::test::check_equal( \ __FILE__, __LINE__, (actual), #actual, (expected), #expected, (desc)) +#endif template inline void check_equal( - char const file[], int line, ACTUAL actual, char const actual_text[], - EXPECTED expected, char const expected_text[], std::string const &desc) +#if !pqxx_have_source_location + char const file[], int line, +#endif + ACTUAL actual, char const actual_text[], + EXPECTED expected, char const expected_text[], std::string const &desc +#if pqxx_have_source_location + , std::source_location loc=std::source_location::current() +#endif + ) { if (expected == actual) return; @@ -83,17 +135,33 @@ inline void check_equal( ", " "expected=" + to_string(expected) + ")"; +#if pqxx_have_source_location + throw test_failure{fulldesc, loc}; +#else throw test_failure(file, line, fulldesc); +#endif } // Verify that two values are not equal. +#if pqxx_have_source_location +#define PQXX_CHECK_NOT_EQUAL(value1, value2, desc) \ + pqxx::test::check_not_equal((value1), #value1, (value2), #value2, (desc)) +#else #define PQXX_CHECK_NOT_EQUAL(value1, value2, desc) \ pqxx::test::check_not_equal( \ __FILE__, __LINE__, (value1), #value1, (value2), #value2, (desc)) +#endif template inline void check_not_equal( - char const file[], int line, VALUE1 value1, char const text1[], - VALUE2 value2, char const text2[], std::string const &desc) +#if !pqxx_have_source_location + char const file[], int line, +#endif + VALUE1 value1, char const text1[], + VALUE2 value2, char const text2[], std::string const &desc +#if pqxx_have_source_location + , std::source_location loc=std::source_location::current() +#endif + ) { if (value1 != value2) return; @@ -101,10 +169,22 @@ inline void check_not_equal( ": " "both are " + to_string(value2) + ")"; - throw test_failure(file, line, fulldesc); +# if pqxx_have_source_location + throw test_failure{fulldesc, loc}; +#else + throw test_failure{file, line, fulldesc}; +#endif } +#if pqxx_have_source_location +// Verify that value1 is less than value2. +#define PQXX_CHECK_LESS(value1, value2, desc) \ + pqxx::test::check_less((value1), #value1, (value2), #value2, (desc)) +// Verify that value1 is greater than value2. +#define PQXX_CHECK_GREATER(value2, value1, desc) \ + pqxx::test::check_less((value1), #value1, (value2), #value2, (desc)) +#else // Verify that value1 is less than value2. #define PQXX_CHECK_LESS(value1, value2, desc) \ pqxx::test::check_less( \ @@ -113,10 +193,18 @@ inline void check_not_equal( #define PQXX_CHECK_GREATER(value2, value1, desc) \ pqxx::test::check_less( \ __FILE__, __LINE__, (value1), #value1, (value2), #value2, (desc)) +#endif template inline void check_less( - char const file[], int line, VALUE1 value1, char const text1[], - VALUE2 value2, char const text2[], std::string const &desc) +#if !pqxx_have_source_location + char const file[], int line, +#endif + VALUE1 value1, char const text1[], + VALUE2 value2, char const text2[], std::string const &desc +#if pqxx_have_source_location + , std::source_location loc=std::source_location::current() +#endif +) { if (value1 < value2) return; @@ -127,10 +215,22 @@ inline void check_less( ", " "\"upper\"=" + to_string(value2) + ")"; +#if pqxx_have_source_location + throw test_failure{fulldesc, loc}; +#else throw test_failure(file, line, fulldesc); +#endif } +#if pqxx_have_source_location +// Verify that value1 is less than or equal to value2. +#define PQXX_CHECK_LESS_EQUAL(value1, value2, desc) \ + pqxx::test::check_less_equal((value1), #value1, (value2), #value2, (desc)) +// Verify that value1 is greater than or equal to value2. +#define PQXX_CHECK_GREATER_EQUAL(value2, value1, desc) \ + pqxx::test::check_less_equal((value1), #value1, (value2), #value2, (desc)) +#else // Verify that value1 is less than or equal to value2. #define PQXX_CHECK_LESS_EQUAL(value1, value2, desc) \ pqxx::test::check_less_equal( \ @@ -139,10 +239,18 @@ inline void check_less( #define PQXX_CHECK_GREATER_EQUAL(value2, value1, desc) \ pqxx::test::check_less_equal( \ __FILE__, __LINE__, (value1), #value1, (value2), #value2, (desc)) +#endif template inline void check_less_equal( - char const file[], int line, VALUE1 value1, char const text1[], - VALUE2 value2, char const text2[], std::string const &desc) +#if !pqxx_have_source_location + char const file[], int line, +#endif + VALUE1 value1, char const text1[], + VALUE2 value2, char const text2[], std::string const &desc +#if pqxx_have_source_location + , std::source_location loc=std::source_location::current() +#endif +) { if (value1 <= value2) return; @@ -153,7 +261,11 @@ inline void check_less_equal( ", " "\"upper\"=" + to_string(value2) + ")"; +#if pqxx_have_source_location + throw test_failure{fulldesc, loc}; +#else throw test_failure(file, line, fulldesc); +#endif } @@ -245,15 +357,28 @@ inline void end_of_statement() {} } \ pqxx::test::internal::end_of_statement() +#if pqxx_have_source_location +#define PQXX_CHECK_BOUNDS(value, lower, upper, desc) \ + pqxx::test::check_bounds( \ + (value), #value, (lower), #lower, (upper), #upper, (desc)) +#else #define PQXX_CHECK_BOUNDS(value, lower, upper, desc) \ pqxx::test::check_bounds( \ __FILE__, __LINE__, (value), #value, (lower), #lower, (upper), #upper, \ (desc)) +#endif template inline void check_bounds( - char const file[], int line, VALUE value, char const text[], LOWER lower, +#if !pqxx_have_source_location + char const file[], int line, +#endif + VALUE value, char const text[], LOWER lower, char const lower_text[], UPPER upper, char const upper_text[], - std::string const &desc) + std::string const &desc +#if pqxx_have_source_location + , std::source_location loc=std::source_location::current() +#endif +) { std::string const range_check = std::string{lower_text} + " < " + upper_text, lower_check = @@ -261,14 +386,35 @@ inline void check_bounds( upper_check = std::string{text} + " < " + upper_text; pqxx::test::check( - file, line, lower < upper, range_check.c_str(), - desc + " (acceptable range is empty; value was " + text + ")"); +#if !pqxx_have_source_location + file, line, +#endif + lower < upper, range_check.c_str(), + desc + " (acceptable range is empty; value was " + text + ")" +#if pqxx_have_source_location + , loc +#endif + ); pqxx::test::check( - file, line, not(value < lower), lower_check.c_str(), - desc + " (" + text + " is below lower bound " + lower_text + ")"); +#if !pqxx_have_source_location + file, line, +#endif + not(value < lower), lower_check.c_str(), + desc + " (" + text + " is below lower bound " + lower_text + ")" +#if pqxx_have_source_location + , loc +#endif + ); pqxx::test::check( - file, line, value < upper, upper_check.c_str(), - desc + " (" + text + " is not below upper bound " + upper_text + ")"); +#if !pqxx_have_source_location + file, line, +#endif + value < upper, upper_check.c_str(), + desc + " (" + text + " is not below upper bound " + upper_text + ")" +#if pqxx_have_source_location + , loc +#endif + ); } diff --git a/test/unit/test_test_helpers.cxx b/test/unit/test_test_helpers.cxx index 89cde627e..c2c0c1a8b 100644 --- a/test/unit/test_test_helpers.cxx +++ b/test/unit/test_test_helpers.cxx @@ -19,8 +19,11 @@ void test_check_notreached() // This is what we expect. } if (not failed) - throw pqxx::test::test_failure( - __FILE__, __LINE__, "PQXX_CHECK_NOTREACHED is broken."); + throw pqxx::test::test_failure{ +#if !pqxx_have_source_location + __FILE__, __LINE__, +#endif + "PQXX_CHECK_NOTREACHED is broken."}; } @@ -51,9 +54,15 @@ void test_check_throws_exception() "PQXX_CHECK_THROWS_EXCEPTION did not catch std::exception."); // ...or any exception type derived from it. +#if pqxx_have_source_location PQXX_CHECK_THROWS_EXCEPTION( - throw pqxx::test::test_failure(__FILE__, __LINE__, "(expected)"), + throw pqxx::test::test_failure{"(expected)"}, + "PQXX_CHECK_THROWS_EXCEPTION() failed to catch expected exception."); +#else + PQXX_CHECK_THROWS_EXCEPTION( + throw (pqxx::test::test_failure{__FILE__, __LINE__, "(expected)"}), "PQXX_CHECK_THROWS_EXCEPTION() failed to catch expected exception."); +#endif // Any other type is an error. bool failed{true}; @@ -98,10 +107,17 @@ void test_check_throws_exception() // Test PQXX_CHECK_THROWS. void test_check_throws() { +#if pqxx_have_source_location + PQXX_CHECK_THROWS( + throw pqxx::test::test_failure{"(expected)"}, + pqxx::test::test_failure, + "PQXX_CHECK_THROWS() failed to catch expected exception."); +#else PQXX_CHECK_THROWS( throw pqxx::test::test_failure(__FILE__, __LINE__, "(expected)"), pqxx::test::test_failure, "PQXX_CHECK_THROWS() failed to catch expected exception."); +#endif // Even if it's not std::exception-derived. PQXX_CHECK_THROWS(throw 1, int, "(expected)");